X-Git-Url: http://git.rot13.org/?p=goodfet;a=blobdiff_plain;f=client%2FGoodFETGlitch.py;h=e596d4eeb2788a98c28585928e21980c7acb2fcd;hp=b85c56802ca4eadc92e4196693082165e8ead897;hb=a9b0b85970a4f989779578f49c0f45d87848dc24;hpb=a7eacc6432e6be392bad9e53248a5b505fefd29e diff --git a/client/GoodFETGlitch.py b/client/GoodFETGlitch.py index b85c568..e596d4e 100644 --- a/client/GoodFETGlitch.py +++ b/client/GoodFETGlitch.py @@ -10,6 +10,9 @@ import sqlite3; from GoodFET import *; + +# After four million points, this kills 32-bit gnuplot. +# Dumping to a bitmap might be preferable. script_timevcc=""" plot "< sqlite3 glitch.db 'select time,vcc,glitchcount from glitches where count=0;'" \ with dots \ @@ -21,19 +24,98 @@ title "Success", \ with dots \ title "Exploited" """; +script_timevccrange=""" +plot "< sqlite3 glitch.db 'select time,vcc,glitchcount from glitches where count=0;'" \ +with dots \ +title "Scanned", \ +"< sqlite3 glitch.db 'select time,vcc,count from glitches where count>0;'" \ +with dots \ +title "Success", \ +"< sqlite3 glitch.db 'select time,max(vcc),count from glitches where count=0 group by time ;'" with lines title "Max", \ +"< sqlite3 glitch.db 'select time,min(vcc),count from glitches where count>0 group by time ;'" with lines title "Min" +"""; class GoodFETGlitch(GoodFET): def __init__(self, *args, **kargs): - print "Initializing GoodFET Glitcher." + print "# Initializing GoodFET Glitcher." #Database connection w/ 30 second timeout. self.db=sqlite3.connect("glitch.db",30000); - self.db.execute("create table if not exists glitches(time,vcc,gnd,trials,glitchcount,count,lock)"); + + #Training + self.db.execute("create table if not exists glitches(time,vcc,gnd,trials,glitchcount,count,lock);"); self.db.execute("create index if not exists glitchvcc on glitches(vcc);"); self.db.execute("create index if not exists glitchtime on glitches(time);"); + + #Exploitation record, to be built from the training table. + self.db.execute("create table if not exists exploits(time,vcc,gnd,trials,count);"); + self.db.execute("create index if not exists exploitvcc on exploits(vcc);"); + self.db.execute("create index if not exists exploittime on exploits(time);"); + self.client=0; def setup(self,arch="avr"): self.client=getClient(arch); + self.client.serInit(); #No timeout + + def glitchvoltages(self,time): + """Returns list of voltages to train at.""" + c=self.db.cursor(); + #c.execute("""select + # (select min(vcc) from glitches where time=? and count=1), + # (select max(vcc) from glitches where time=? and count=0);""", + # [time, time]); + c.execute("select min,max from glitchrange where time=? and max-min>0;",[time]); + rows=c.fetchall(); + for r in rows: + min=r[0]; + max=r[1]; + if(min==None or max==None): return []; + + spread=max-min; + return range(min,max,1); + #If we get here, there are no points. Return empty set. + return []; + def crunch(self): + """This builds tables for glitching voltage ranges from the training set.""" + print "Precomputing glitching ranges. This might take a long while."; + print "Times..."; + sys.stdout.flush(); + self.db.execute("drop table if exists glitchrange;"); + self.db.execute("create table glitchrange(time integer primary key asc,max,min);"); + self.db.commit(); + print "Calculating ranges..."; + sys.stdout.flush(); + + maxes={}; + mins={}; + + c=self.db.cursor(); + c.execute("select time,vcc,glitchcount,count from glitches;"); #Limit 10000 for testing. + progress=0; + for r in c: + progress=progress+1; + if progress % 1000000==0: print "%09i rows crunched." % progress; + t=r[0]; + v=r[1]; + glitchcount=r[2]; + count=r[3]; + # FIXME: Threse thresholds suck. + if count<2: + try: oldmax=maxes[t]; + except: oldmax=-1; + if v>oldmax: maxes[t]=v; + elif glitchcount<2: + try: oldmin=mins[t]; + except: oldmin=0x10000; + if v0;"); + print "time vcc gnd glitchcount count"; + for r in c: + print "%i %i %i %i %i" % r; + def rpoints(self): + c=self.db.cursor(); + c.execute("select time,vcc,gnd,glitchcount,count from glitches where lock=0 and glitchcount>0;"); + print "time vcc gnd glitchcount count"; + for r in c: + print "%i %i %i %i %i" % r; + #GnuPlot sucks for large sets. Switch to viewpoints soon. + # sqlite3 glitch.db "select time,vcc,count from glitches where count=0" | vp -l -d "|" -I + + def explore(self,times=None, trials=10): + """Exploration phase. Uses thresholds to find exploitable points.""" + gnd=0; + self.scansetup(1); #Lock the chip, place key in eeprom. + if times==None: + tstart=0; + tstop=self.client.glitchstarttime(); + times=range(tstart,tstop); + random.shuffle(times); + #self.crunch(); + count=0.0; + total=1.0*len(times); + c=self.db.cursor(); + c.execute("select time,min,max from glitchrange where max-min>0;"); + rows=c.fetchall(); + c.close(); + random.shuffle(rows); + print "Exploring %i times." % len(times); + mins={}; + maxes={}; + for r in rows: + t=r[0]; + mins[t]=r[1]; + maxes[t]=r[2]; + for t in times: + min=mins[t]; + max=maxes[t]; + voltages=range(min,max,1); + count=count+1.0; + print "%02.02f Exploring %04i points in t=%04i." % (count/total,len(voltages),t); + sys.stdout.flush(); + for vcc in voltages: + self.scanat(1,trials,vcc,gnd,t); def learn(self): - #Learning phase - trials=1; + """Learning phase. Finds thresholds at which the chip screws up.""" + trials=30; lock=0; #1 locks, 0 unlocked vstart=0; - vstop=0xfff; #Could be as high as 0xFFF + vstop=1024; #Could be as high as 0xFFF, but upper range is useless vstep=1; tstart=0; - tstop=-1; #<0 defaults to full range + tstop=self.client.glitchstarttime(); tstep=0x1; #Must be 1 - self.scan(lock,trials,vstart,vstop,tstart,tstop); - - def scan(self,lock,trials=1,vstart=0,vstop=0xfff,tstart=0,tstop=-1): + self.scan(lock,trials,range(vstart,vstop),range(tstart,tstop)); + print "Learning phase complete, begin to crunch."; + self.crunch(); + #print "Crunch phase complete, beginning to explore."; + #self.explore(); + + def scansetup(self,lock): client=self.client; - self.lock=lock; - client.serInit(); - if tstop<0: - tstop=client.glitchstarttime(); #Really long; only use for initial investigation. - print "-- Start takes %04i cycles." % tstop; + client.verbose=0; client.start(); client.erase(); + print "Scanning %s" % client.infostring(); + + #Kind of arbitrary. + #16 bit minimum. + self.secret=0xdead; + + + print "-- Setting secret"; + client.start(); + + #Flash the secret, to try and recover it later. + client.erase(); + print "-- Secret was %02x" % client.getsecret(); + client.setsecret(self.secret); + print "-- Secret set to %02x" % client.getsecret(); + sys.stdout.flush() + if(client.getsecret()!=self.secret): + print "Secret failed to set. Exiting for safety."; + sys.exit(); - self.secret=0x69; - - while(client.eeprompeek(0)!=self.secret): - print "-- Setting secret"; - client.start(); - - #Flash the secret to the first two bytes of CODE memory. - client.erase(); - client.eeprompoke(0,self.secret); - client.eeprompoke(1,self.secret); - sys.stdout.flush() - #Lock chip to unlock it later. if lock>0: client.lock(); - voltages=range(vstart,vstop,1); - times=range(tstart,tstop,1); - gnd=0; #TODO, glitch GND. - vcc=0xfff; + + def scan(self,lock,trials,voltages,times): + """Scan many voltages and times.""" + client=self.client; + self.scansetup(lock); + gnd=0; random.shuffle(voltages); #random.shuffle(times); @@ -123,22 +256,24 @@ class GoodFETGlitch(GoodFET): print "Exploring vcc=%i" % vcc; sys.stdout.flush(); for time in times: - self.scanat(trials,vcc,gnd,time) + self.scanat(lock,trials,vcc,gnd,time) sys.stdout.flush() self.db.commit(); else: print "Voltage %i already explored." % vcc; sys.stdout.flush(); + + def vccexplored(self,vcc): c=self.db.cursor(); c.execute("select vcc from glitches where vcc=? limit 1;",[vcc]); rows=c.fetchall(); for a in rows: return True; + c.close(); return False; - def scanat(self,trials,vcc,gnd,time): + def scanat(self,lock,trials,vcc,gnd,time): client=self.client; - db=self.db; client.glitchRate(time); client.glitchVoltages(gnd, vcc); #drop voltage target gcount=0; @@ -149,12 +284,12 @@ class GoodFETGlitch(GoodFET): client.glitchstart(); #Try to read *0, which is secret if read works. - a=client.eeprompeek(0x0); - if self.lock>0: #locked + a=client.getsecret(); + if lock>0: #locked if(a!=0 and a!=0xFF and a!=self.secret): gcount+=1; if(a==self.secret): - print "-- %04x: %02x HELL YEAH! " % (time, a); + print "-- %06i: %02x HELL YEAH! " % (time, a); scount+=1; else: #unlocked if(a!=self.secret): @@ -163,6 +298,13 @@ class GoodFETGlitch(GoodFET): scount+=1; #print "values (%i,%i,%i,%i,%i);" % ( # time,vcc,gnd,gcount,scount); - self.db.execute("insert into glitches(time,vcc,gnd,trials,glitchcount,count,lock)" + if(lock==0): + self.db.execute("insert into glitches(time,vcc,gnd,trials,glitchcount,count,lock)" "values (%i,%i,%i,%i,%i,%i,%i);" % ( - time,vcc,gnd,trials,gcount,scount,self.lock)); + time,vcc,gnd,trials,gcount,scount,lock)); + elif scount>0: + print "INSERTING AN EXPLOIT point, t=%i and vcc=%i" % (time,vcc); + self.db.execute("insert into exploits(time,vcc,gnd,trials,count)" + "values (%i,%i,%i,%i,%i);" % ( + time,vcc,gnd,trials,scount)); + self.db.commit(); #Don't leave a lock open.