X-Git-Url: http://git.rot13.org/?p=goodfet;a=blobdiff_plain;f=client%2Fgoodfet.maxusbmass;h=8c12923b83d46fef57168c1b6e9662571ac2f2dc;hp=950d67d85530b491b7c69c6acb7d9e299dfc9f94;hb=0c1cae929512565296940bf6322087a73b2cecd0;hpb=8baf3779e15f289e442e2ee56495c5600606955a diff --git a/client/goodfet.maxusbmass b/client/goodfet.maxusbmass index 950d67d..8c12923 100755 --- a/client/goodfet.maxusbmass +++ b/client/goodfet.maxusbmass @@ -8,11 +8,87 @@ import sys; import binascii; import array; import time; +import warnings from GoodFETMAXUSB import *; -class GoodFETMAXUSBMass(GoodFETMAXUSB): +warnings.warn( +"""The libraries upon which this program depends will soon be deprecated in +favor of the USB*.py libraries. See facedancer-umass.py (forthcoming) for an +example of this program written using the new libraries.""" +) + +# This constant is kinda complicated and very ugly. The idea is that +# if we take too long in any given transaction, the host will abort. +# How many blocks we can send depends upon timeouts on both sides, +# with (at least in Linux) the behavior that aborting early causes the +# disk to reset with only warning and no real errors. Somewhere +# there's a way to provide this constant to the host, in which case +# stalling and waiting for a reset will no longer be necessary. + +MAXBLOCKSPERTRANSFER=128 + +def zeros(length): + """Returns a list of zeroes of the specified length.""" + l=range(0,length); + for foo in l: + l[foo]=0; + return l; + +class GoodFETMAXUSBMass(GoodFETMAXUSBDevice): """This emulates a USB Mass Storage device.""" + + #Too much data to watch everything. + usbverbose=False; + + def getSectorData(self,lba): + """Overload this to return data from a given 512-byte sector.""" + print "You forgot to overload getSectorData(). Returning something neighborly."; + sector=[ + 0xE9, 0x86, 0x00, 0x0A, 0x47, 0x6F, 0x6F, 0x64, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x30, 0x2E, 0x30, + 0x31, 0x0A, 0x0D, 0x62, 0x79, 0x20, 0x54, 0x72, 0x61, 0x76, 0x69, 0x73, 0x20, 0x47, 0x6F, 0x6F, + 0x64, 0x73, 0x70, 0x65, 0x65, 0x64, 0x0A, 0x0A, 0x0D, 0x00, 0x59, 0x6F, 0x75, 0x20, 0x68, 0x61, + 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x65, 0x61, 0x74, 0x65, 0x6E, 0x20, 0x62, 0x79, + 0x20, 0x61, 0x20, 0x67, 0x72, 0x75, 0x65, 0x2E, 0x20, 0x20, 0x53, 0x6F, 0x72, 0x72, 0x79, 0x2E, + 0x0A, 0x0D, 0x00, 0x31, 0x29, 0x20, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x6B, 0x65, + 0x72, 0x6E, 0x65, 0x6C, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x2E, 0x0A, + 0x0D, 0x00, 0x32, 0x29, 0x20, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x6B, + 0x65, 0x72, 0x6E, 0x65, 0x6C, 0x2E, 0x0A, 0x0D, 0x00, 0xBE, 0x03, 0x7C, 0xE8, 0x41, 0x00, 0xE8, + 0x7B, 0x00, 0x31, 0xC0, 0x30, 0xD2, 0xCD, 0x13, 0x0F, 0x82, 0xE8, 0x00, 0xBE, 0x53, 0x7C, 0xE8, + 0x2E, 0x00, 0xB8, 0xE0, 0x07, 0x8E, 0xC0, 0x31, 0xDB, 0xB8, 0x10, 0x02, 0xB5, 0x00, 0xB1, 0x02, + 0xB6, 0x00, 0xB2, 0x00, 0xCD, 0x13, 0x0F, 0x82, 0xCA, 0x00, 0xB8, 0x00, 0x7E, 0x89, 0xC6, 0xE8, + 0x7C, 0x00, 0xBE, 0x72, 0x7C, 0xE8, 0x08, 0x00, 0xEA, 0x00, 0x00, 0xE0, 0x07, 0xE8, 0xB4, 0x00, + 0xAC, 0x3C, 0x00, 0x74, 0x06, 0xB4, 0x0E, 0xCD, 0x10, 0xEB, 0xF5, 0xC3, 0x30, 0x78, 0x00, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x20, + 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2E, 0x0A, 0x0D, 0x00, 0x53, 0x65, 0x67, 0x6D, + 0x65, 0x6E, 0x74, 0x73, 0x3A, 0x20, 0x00, 0x2C, 0x20, 0x00, 0x0A, 0x0D, 0x00, 0xBE, 0xDC, 0x7C, + 0xE8, 0xBD, 0xFF, 0xE8, 0x63, 0x00, 0xE8, 0x07, 0x00, 0xBE, 0xDF, 0x7C, 0xE8, 0xB1, 0xFF, 0xC3, + 0x89, 0xC3, 0xC1, 0xE8, 0x0C, 0xE8, 0x39, 0x00, 0x89, 0xD8, 0xC1, 0xE8, 0x08, 0xE8, 0x31, 0x00, + 0x89, 0xD8, 0xC1, 0xE8, 0x04, 0xE8, 0x29, 0x00, 0x89, 0xD8, 0xE8, 0x24, 0x00, 0xC3, 0x31, 0xC9, + 0xAD, 0xE8, 0xDC, 0xFF, 0xE8, 0x2C, 0x00, 0x83, 0xC1, 0x02, 0x81, 0xF9, 0x00, 0x02, 0x75, 0xF0, + 0xC3, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x50, 0x56, 0x83, 0xE0, 0x0F, 0x05, 0x51, 0x7D, 0x89, 0xC6, 0xAC, 0xB4, 0x0E, 0xCD, 0x10, + 0x5E, 0x58, 0xC3, 0xB8, 0x20, 0x0E, 0xCD, 0x10, 0xC3, 0x31, 0xC0, 0xCD, 0x12, 0x72, 0x05, 0x85, + 0xC0, 0x74, 0x01, 0xC3, 0xBE, 0x2A, 0x7C, 0xE8, 0x46, 0xFF, 0xEB, 0xFE, 0xEA, 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA + ]; + return sector; + def putSectorData(self,lba,block): + """Overload this to write data to a given 512-byte sector.""" + print "You forgot to overload putSectorData(). Ignoring sector write."; + return; + + def getSectorCount(self): + """Returns the number of viable Logical Block Addresses.""" + print "You forgot to overload getSectorCount(). Guessing 0x200."; + return 0x200; + def massinit(self): """Initialize a USB Mass Storage device.""" self.usb_disconnect(); @@ -22,8 +98,9 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): def massrun(self): """Main loop of the USB Mass Storage emulator.""" - print "Starting a Mass Storage device. This doesn't work yet."; + print "Starting a Mass Storage device."; while 1: + sys.stdout.flush(); self.service_irqs(); def do_SETUP(self): """Handle USB Enumeration""" @@ -37,7 +114,6 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): if setuptype==0x00: self.std_request(SUD); elif setuptype==0x20: - print "XXXXXXXXXX Got a class request." self.class_request(SUD); elif setuptype==0x40: self.vendor_request(SUD); @@ -46,7 +122,20 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): self.STALL_EP0(SUD); def class_request(self,SUD): """Handle a class request.""" - print "Stalling a class request."; + requesttype=ord(SUD[bmRequestType]); + request=ord(SUD[bRequest]); + if requesttype==0xA1 and request==0xFE: + print "Reporting 0 as the maximum LUN."; + #This is a Get Max LUN request. + #Return 1-byte maximum Logical Unit Number + self.wreg(rEP0FIFO,0x00); # Just one LUN. + self.wregAS(rEP0BC,1); # ARM and fire! + return; #Don't stall. + if requesttype==0x21 and request==0xff: + print "Received BBB reset." + self.wregAS(rEP0BC,0); # ARM and fire! + return; #Don't stall. + print "Stalling an unknown class request: %s" % self.setup2str(SUD); self.STALL_EP0(SUD); def vendor_request(self,SUD): """Handle a vendor request.""" @@ -64,9 +153,11 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): elif setuptype==SR_SET_ADDRESS: self.rregAS(rFNADDR); elif setuptype==SR_GET_INTERFACE: self.get_interface(SUD); else: - print "Stalling Unknown standard setup request type %02x" % setuptype; - self.STALL_EP0(SUD); - + #print "Stalling Unknown standard setup request type %02x" % setuptype; + #self.STALL_EP0(SUD); + print "Accepting unknown standard setup request type %02x" % setuptype; + self.wregAS(rEP0BC,0); + def get_interface(self,SUD): """Handles a setup request for SR_GET_INTERFACE.""" if ord(SUD[wIndexL]==0): @@ -76,7 +167,6 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): self.STALL_EP0(SUD); -#0403:6001 #Device Descriptor DD=[ @@ -85,7 +175,9 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x81, 0x07, #Sandisk 0x50, 0x51, #SDCZ2 Cruzer Mini Flash Drive (thin) - 0x00, 0x03, 0x00, 0x00, 0x00, 0x01 + 0x00, 0x03, + 0x01, 0x02, 0x03, #Strings + 0x01 ]; @@ -101,10 +193,10 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): 0x06, #SCSI 0x50, 0x00, + #OUT EP1 + 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00, #IN EP3 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, - #OUT EP1 - 0x07, 0x05, 0x01, 0x02, 0x40, 0x00, 0x00 ]; strDesc=[ @@ -123,38 +215,6 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): "\x14\x03S\x00/\x00N\x00 \x003\x004\x002\x000\x00E\x00" ]; - def send_descriptor(self,SUD): - """Send the USB descriptors based upon the setup data.""" - desclen=0; - reqlen=ord(SUD[wLengthL])+256*ord(SUD[wLengthH]); #16-bit length - desctype=ord(SUD[wValueH]); - - if desctype==GD_DEVICE: - desclen=self.DD[0]; - ddata=self.DD; - elif desctype==GD_CONFIGURATION: - desclen=self.CD[2]; - ddata=self.CD; - elif desctype==GD_STRING: - desclen=self.strDesc[ord(SUD[wValueL])][0]; - ddata=self.strDesc[ord(SUD[wValueL])]; - - #TODO Configuration, String, Hid, and Report - - if desclen>0: - sendlen=min(reqlen,desclen); - self.writebytes(rEP0FIFO,ddata); - self.wregAS(rEP0BC,sendlen); - else: - print "Stalling in send_descriptor() for lack of handler for %02x." % desctype; - self.STALL_EP0(SUD); - def set_configuration(self,SUD): - """Set the configuration.""" - bmSUSPIE=0x10; - configval=ord(SUD[wValueL]); - if(configval>0): - self.SETBIT(rUSBIEN,bmSUSPIE); - self.rregAS(rFNADDR); def get_status(self,SUD): """Get the USB Setup Status.""" testbyte=ord(SUD[bmRequestType]) @@ -177,52 +237,268 @@ class GoodFETMAXUSBMass(GoodFETMAXUSB): self.wreg(rEP0FIFO,0x00); #Second byte is always zero. self.wregAS(rEP0BC,2); else: + print "Stalling unknown status."; self.STALL_EP0(SUD); else: + print "Stalling unknown status."; self.STALL_EP0(SUD); - def service_irqs(self): - """Handle USB interrupt events.""" - - epirq=self.rreg(rEPIRQ); - usbirq=self.rreg(rUSBIRQ); - - #Are we being asked for setup data? - if(epirq&bmSUDAVIRQ): #Setup Data Requested - self.wreg(rEPIRQ,bmSUDAVIRQ); #Clear the bit - self.do_SETUP(); - elif(epirq&bmIN3BAVIRQ): #EN3-IN packet - self.do_IN3(); - #self.wreg(rEPIRQ,bmIN3BAVIRQ); #Clear the bit - elif(epirq&bmOUT1DAVIRQ): #OUT1-OUT packet - self.do_OUT1(); - self.wregAS(rEPIRQ,bmOUT1DAVIRQ); #Clear the bit *AFTER* servicing. - #else: - # self.do_IN3(); - - typestring="GoodFET emulates Mass properly, if you can read this!\n"; - typepos=0; def do_IN3(self): """Handle IN3 input event.""" - #Don't bother clearing interrupt flag, that's done by sending the reply. - #self.type_IN3(); - print "Got an input event, no idea what to do about it."; - self.wreg(rEP3INFIFO,0x01); #Modem Status - self.wreg(rEP3INFIFO,0x00); #Line Status - self.wreg(rEP3INFIFO,0x00); - self.wregAS(rEP3INBC,3); + # Do nothing here, as it'll be taken care of elsewhere. The + # interrupt just means that the buffer is empty, not that we + # are expected to fill it. + def do_OUT1(self): """Handle an OUT1 output event.""" - print "Got an output event, printing the result."; l=self.rreg(rEP1OUTBC); - frame=self.readbytesAS(rEP1OUTFIFO,l); - print "Mass OUT: %s" % frame[1:len(frame)]; - #self.type_IN3(); + frame=self.readbytes(rEP1OUTFIFO,l); + self.handleCBW(frame); + + lastCBW=""; + def handleCBW(self,cbw): + """Handles an incoming Command Block Wrapper. See USB Mass + Storage Class for details.""" + + if len(cbw)!=31: + print "Invalid CBW length of %i bytes. Aborting." % len(cbw); + return; + sig=cbw[0:4]; + if sig!="USBC": + print "Invalid CBW signature: %s. Should be USBC; aborting." % sig; + return; + self.lastCBW=cbw; + + dtlen=ord(cbw[8])+(ord(cbw[9])<<8)+(ord(cbw[10])<<16)+(ord(cbw[11])<<24); + flags=ord(cbw[12]); + dtdir=flags&0x80; # 0x80 for dev->host, 0x00 for host->dev + lun=ord(cbw[13])&0x0F; # Should be zero, as we only reported one LUN. + cblen=ord(cbw[14])&0x1F; + cb=cbw[15:31]; + self.handleCB(cb,cblen,dtlen,dtdir); + + def handleCB(self,cb,cblen,dtlen,dtdir): + """Handles a command block, then replies with a CSW.""" + if self.usbverbose: + print "Got command block, type 0x%02x requesting 0x%02x bytes" % ( + ord(cb[0]), dtlen); + verb=ord(cb[0]); + status=00; #good, set to 1 for bad. + if verb==0x00: # Test Unit Ready + # Send nothing, just the success code. + status=0; + elif verb==0x03: # Request Sense + print "Responding to Request Sense. Needed for Macs."; + response=[0x70, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, + 0,0,0,0,0]; + #status=1; + self.writebytes(rEP3INFIFO, + response); + self.wreg(rEP3INBC,len(response)); + elif verb==0x12: #Inquiry + #print "Responding to CB inquiry."; + response=[ + 0x00, # 00 for Direct, 1F for "no floppy" + 0x80, # make 0x80 for removable media, 0x00 for fixed + 0x00, # Version + 0x01, # Response Data Format + 0x1f, #Additional length. + 0x00, 0x00, 0x00, + #Manufacturer + ord('G'),ord('o'),ord('o'),ord('d'),ord('F'),ord('E'),ord('T'),0x20, + #Device name + ord('G'),ord('o'),ord('o'),ord('d'),ord('F'),ord('E'),ord('T'),0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + ord('0'),ord('.'),ord('0'),ord('1')] + #print "Sending %i byte reply to %i byte query." % ( + # len(response),dtlen); + while len(response)>24)&0xFF, (lastlba>>16)&0xFF, (lastlba>>8)&0xFF, lastlba&0xFF, # Last LBA + 0x00,0x00,0x02,0x00 # Block length of 512 bytes. + ]; + self.writebytes(rEP3INFIFO, + response); + self.wregAS(rEP3INBC, + len(response)); + elif verb==0x28: #READ SECTOR + cbw=self.lastCBW; + baselba=( + ord(cbw[20]) | + (ord(cbw[19])<<8) | + (ord(cbw[18])<<16) | + (ord(cbw[17])<<24) + ); + count=dtlen/512; + print "Fetching %i blocks starting at LBA %i." % (count,baselba); + if count>MAXBLOCKSPERTRANSFER: + count=0; + #status=1; #Fail if we're asked to read more than 32 blocks. + #Now we need to stall EP3. It's not acceptable to just forget to transmit. + self.wreg(rEPSTALLS,0x10); + return; + for i in range(0,count): + data=self.getSectorData(baselba+i); + self.fifo_ep3in_tx(data); + + # for j in range(0,8): + # #print "Sending block fragment %i,%i" % (i,j); + # #Transmit each 64-byte block fragment, then wait for next. + # while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ): pass; + # response=data[j*64:j*64+64]; + # self.writebytes(rEP3INFIFO, + # response); + # self.wregAS(rEP3INBC, + # 64); + #sys.exit(); + elif verb==0x2A: #WRITE SECTOR + print "Haven't implemented WRITE SECTOR. This will end badly."; + #sys.exit(); + elif verb==0x1B: #EJECT/RETRACT DISK + print "Haven't implemented SCSI Start Stop Unit Command (1B)"; + print "https://en.wikipedia.org/wiki/SCSI_Start_Stop_Unit_Command"; + #sys.exit(); + else: + print "ERROR: Unknown SCSI command block verb %02x." % verb; + status=0x02; #Command Failed + if dtlen>0: + print "Sending %i bytes of dummy data here." % dtlen; + self.fifo_ep3in_tx(zeros(dtlen)); + #sys.exit(1); + cbw=self.lastCBW; + + #Now we need to send the CSW. + csw=[ + #Standard prefix. + ord('U'),ord('S'),ord('B'),ord('S'), + #CBW key; must be the same as the one we're replying to. + ord(cbw[4]),ord(cbw[5]),ord(cbw[6]),ord(cbw[7]), + #CSW Data Residue, probably oughtn't be zeroed. + 0,0,0,0, + #Status byte: 00 for good, 01 for bad. + status]; + self.writebytes(rEP3INFIFO, + csw); + self.wregAS(rEP3INBC,len(csw)); + + + return; + +class GoodFETMAXUSBMassFile(GoodFETMAXUSBMass): + """This emulates a USB Mass Storage Device, providing a file as + its image. Writes are not yet supported, and this is very slow. + Performance hacks will come after the code stabilizes.""" + + datafile=None; + datafilelen=None; + def openImage(self,filename): + """Opens an image for use. Call this *before* massinit().""" + self.datafile=open(filename,"rb"); + print "Opened an image with %i blocks." % self.getSectorCount(); + def putSectorData(self,lba,block): + """Writes a 512-byte sector to the lba address.""" + print "Writes aren't yet supported."; + return; + + def getSectorData(self,lba): + """Returns data from a 512-byte sector.""" + toret=""; + + #Seek to the appropraite block. + #print "Seeking to position %i"% (lba*512) + self.datafile.seek(lba*512,0); + pos=self.datafile.tell(); + if pos!=lba*512: + print "SEEK ERROR: Seeked to %i (lba=%i), but now I'm at %i (lba=%i)" % ( + lba*512,lba, + pos,pos/512); + #sys.exit(); + + #Dump the data out, assuming no blocking and filling with nonsense. + toret=self.datafile.read(512); + if len(toret)<512: + print "Holy hell, I only have %i bytes of 512." % len(toret); + + toretbytes=range(0,len(toret)); + for b in range(0,len(toret)): toretbytes[b]=ord(toret[b]); + return toretbytes; + + def getSectorCount(self): + """Returns the number of viable Logical Block Addresses.""" + + # Python must have a better way to read a file length, but for + # now we just read the whole damned thing and then throw it + # away. With present performance, we can't read anything + # large enough for this to be a problem. + if self.datafilelen==None: + self.datafile.seek(0); + self.datafilelen=len(self.datafile.read()); + if self.datafilelen%512!=0: + print "ERROR: Image does not have an integer number of blocks!" + print "%i \% 512 == %i" % (self.datafilelen, + self.datafilelen%512); + sys.exit(); + return self.datafilelen/512-1; + +if(len(sys.argv)==1): + print "Usage: %s disk.img\n" % sys.argv[0]; + sys.exit(); + #Initialize FET and set baud rate -client=GoodFETMAXUSBMass(); +client=GoodFETMAXUSBMassFile(); client.serInit() +client.openImage(sys.argv[1]); client.MAXUSBsetup(); client.massinit();