phywhispererusb icon indicating copy to clipboard operation
phywhispererusb copied to clipboard

Trigger do not work on custom database descriptor packets

Open h0rac opened this issue 5 years ago • 13 comments

Hi

I have following issue with phywhisperer, I am not able to trigger on dev.ctrl_transfer(bmRequestType=0x80, bRequest=0x06, wValue=0x0001, wIndex=0x0000, data_or_wLength=0x1F, timeout=5)

My trigger is set to phy.set_pattern(pattern=[0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x1F])

I can trigger on any original packets received on USB bus by phywisperer but not on packets I want to inject using pyusb

When I inject packet I can see it with logic analyser Saleae, but cannot trigger on it using phywisperer

My physical setup: HOST/CONTROL PC connected to OS X, TARGET to USB key

try:
    phy = pw.Usb()
    phy.con(program_fpga=True)
    print(phy.get_fpga_buildtime())
    phy.load_bitstream("phywhisperer_top.bit")
    phy.set_usb_mode('auto')
    phy.set_pattern(pattern=[0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x1F])
    phy.set_capture_size(8000)
    while True:
        phy.set_power_source("host")  
        # time.sleep(0.5)  
        dev = usb.core.find(idVendor=2414, idProduct=2132)
        dev.set_configuration()
        if dev is None:
            raise ValueError('Our device is not connected')
        # print("USB Dev:", dev)
        phy.set_trigger(num_triggers=1, delays=[453], widths=[80], enable=True)
        phy.arm()
        dev.ctrl_transfer(bmRequestType=0x80, bRequest=0x06, wValue=0x0001, wIndex=0x0000, data_or_wLength=0x1F, timeout=15)
        time.sleep(0.5)
        phy.set_power_source("off")
        time.sleep(0.1)
        # arm:
        phy.set_power_source("host")    
        time.sleep(0.1)
        # phy.wait_disarmed()
        print("done")
        phy.addpattern = True
.....

h0rac avatar Jan 09 '20 14:01 h0rac

What does phy.get_usb_mode() return? (if it's "auto" then the target speed hasn't been detected properly, which means that the phy won't be programmed correctly).

It's also possible that things get out of whack because you're possibly re-arming PW in the while loop without having captured anything. If you must have a while loop around the PW arming, you should call reset_fpga() before re-arming (you'll also need to re-program all PW parameters).

Finally I would try relaxing the match pattern to see what PW is seeing.

jpcrypt avatar Jan 09 '20 15:01 jpcrypt

Hi thanks for answer

phy.get_usb_mode() return auto and then FS if I check in loop. When I set it to FS manually it triggers but only for short pattern like [0x00, 0x1F] entire packet trigger is not working.

h0rac avatar Jan 09 '20 15:01 h0rac

Can you try the following:

phy = pw.Usb()
phy.con(program_fpga=True)
print(phy.get_fpga_buildtime())
phy.reset_fpga()
phy.set_usb_mode('FS')
#phy.set_pattern(pattern=[0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x1F])
phy.set_pattern(pattern=[0x80, 0x06])
phy.set_capture_size(8000)

phy.set_power_source("host")  
# time.sleep(0.5)  
dev = usb.core.find(idVendor=2414, idProduct=2132)
dev.set_configuration()
if dev is None:
    raise ValueError('Our device is not connected')

phy.arm()
# make sure we haven't seen a match yet:
assert phy.armed() == True

dev.ctrl_transfer(bmRequestType=0x80, bRequest=0x06, wValue=0x0001, wIndex=0x0000, data_or_wLength=0x1F, timeout=15)
phy.wait_disarmed()
print("done")
phy.addpattern = True
raw = phy.read_capture_data()
...

Assuming this runs through, look for your pattern in raw.

jpcrypt avatar Jan 09 '20 16:01 jpcrypt

you didn't; set trigger ? - so it not work, but this one works.. up to [0x00,0x00,0xFF,0xFF]

try:
    phy = pw.Usb()
    phy.con()
    phy.set_usb_mode('FS')
    phy.set_pattern(pattern=[0xFF, 0xFF])
    phy.set_capture_size(8000)
    while True:
        phy.set_power_source("host")  
        time.sleep(0.25)
        dev = usb.core.find(idVendor=2414, idProduct=2132)
        dev.set_configuration()
        if dev is None:
            dev = usb.core.find(idVendor=2414, idProduct=2132)
            dev.set_configuration()
        # print("USB Dev:", dev)
        # print(ret)
        phy.set_trigger(num_triggers=2, delays=[0,453], widths=[80, 80], enable=True)
        phy.arm()
        assert phy.armed() == True
        resp = dev.ctrl_transfer(bmRequestType=0x80, bRequest=0x06, wValue=0x0001, wIndex=0x0000, data_or_wLength=0xFFFF, timeout=15)
        print(list(resp))
        phy.set_power_source("off")
        # time.sleep(0.1)
        # arm:
        phy.set_power_source("host")    
        # time.sleep(0.1)
        phy.wait_disarmed()
        print("done")
        phy.addpattern = True
        raw = phy.read_capture_data()
        # if(phy.armed() == False):

h0rac avatar Jan 09 '20 17:01 h0rac

Just an update, I haven't been able to set up a target with pyusb yet to investigate this further; I will try to do that sometime next week. However I did check that I have no trouble with matching on a similar pattern -- in my case, [0x80, 0x06, 0x01, 0x03, 0x09, 0x04, 0xFF]. So I still suspect that the issue doesn't lie with the PW pattern matching logic. FYI, we verified random patterns and masks up to 64 bytes long in simulation.

jpcrypt avatar Jan 12 '20 03:01 jpcrypt

That was my mistake pattern should be [0x80, 0x06, 0x01, 0x00, 0x00, 0x00, 0x1F] instead [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x1F]

h0rac avatar Jan 12 '20 16:01 h0rac

Ah, so do you mean that it works and the issue can be closed?

jpcrypt avatar Jan 12 '20 21:01 jpcrypt

Looks like, whatever pyusb sends its reversed in pattern

h0rac avatar Jan 14 '20 12:01 h0rac

Is this a documentation issue? I mean it sounds like this is due to byte/bit ordering that USB uses being not an obvious mapping.

Did the PW-USB feedback show you the correct byte order? Just trying to figure out what all to document! I'm thinking at minimum we could make a small helper macro/function that maps from a USB request to the expected pattern over-the-wire (which would be a very very common usage)

colinoflynn avatar Jan 14 '20 13:01 colinoflynn

This is what I want to test also on other stacks, on OS X it is weird that I can't set using pyusb Device/index as 0x0100/0x00 it simply do not send that correctly. What pyusb is doing is bit shift index | << 8 descriptor_type. The only request I can send as valid is

dev.ctrl_transfer(bmRequestType=0x80, bRequest=0x06, wValue=0x0101, wIndex=0x0000, data_or_wLength=0xFFFF)

Then on phywhisperer I need to catch as pattern phy.set_pattern(pattern=[0x80,0x06,0x01,0x01,0x00,0x00,0xFF,0xFF])

Other example to show byte swap:

resp = dev.ctrl_transfer(bmRequestType=0x80, bRequest=0x06, wValue=0x1234, wIndex=0x0000, data_or_wLength=0xFFFF)]

phy.set_pattern(pattern=[0x80,0x06,0x34,0x12,0x00,0x00,0xFF,0xFF])

Screenshot 2020-01-14 at 14 19 38

h0rac avatar Jan 14 '20 13:01 h0rac

The byte ordering of USB is LSB first IIRC - so when it sends the 16-bit values, they go over the wire as 0x34, x012. This is the "unexpected" part since most people are used to MSB first! Having a helper function would make it more clear how the mapping works.

On the device/index - basically you are limited by the USB stack to sending only "Valid" things. The other trying to try is the wLength value. I've seen that on some stacks you can set it to 0xFFFF, on other stacks it gets limited (0x0400 is maximum I've seen on a few). It seems to be something added at some point in libusb, so you just get USB errors.

It's worth considering making a dedicated "usb host". GreatFET can basically be used as this too (Facedancer is basically designed for this). But I've also just used a raspberry pi that you can compile your specific version of libusb on...

colinoflynn avatar Jan 14 '20 13:01 colinoflynn

Which version you compiled for Raspberry Pi ? nice hint. I am above on real project testing some USB dongle. I see I can get up to 71 bytes back when glitching and using OS X as. host but.. I also see bunch of errors on Saleae when triggering on crafted packet

You also connect Control PC and Host USB ports to Raspberry PI ? or use Control PC as some Laptop OSX/WIndows/Linux and only Host USB is connected to Raspberry from which you only craft and Send USB packets. On Control PC you use Python API of Phywhisperer to trigger and so on

h0rac avatar Jan 14 '20 13:01 h0rac

So I'm not positive on control PC - I always did that from regular laptop. In theory (?) it should work but I never tried, so might be some problem with libusb on r-pi. I was using older verions of everything (older kernel).

I actually ended up just dedicating a computer to serve as my control instead (old laptop), so I didn't spend long on the r-pi idea! But In theory it would be nicer/smaller/easier to use r-pi. The laptop was something older - I was playing with Ubuntu 14 & a Windows 7 with an old (~Python 2.7) install.

If you try to send a wLength of 0xFFFF and it gives an error, but the packet does work with a wLength of something smaller (0x00FF) then you know the host is giving you the problem. The end device will ignore a larger wLength, so if you get an error with a wLength of 0xFFFF that means that you'll never get it working I think.

Sorry I don't have exact versions! But if you try a few VMs or something it should be helpful? I ended up doing bare metal as was concerned that the VM-->Host interface would also be giving me trouble. In the end I did get the 0xFFFF to work however - when I get a chance I'll try it on a few systems around to see if any "just work"

colinoflynn avatar Jan 14 '20 17:01 colinoflynn

closing since no further updates

jpcrypt avatar Mar 20 '24 16:03 jpcrypt