Reverse Engineering a Samsung Scanner Button, page 4
Pages: 1 2 3 4 5 6 7 8
Before we proceed, we should talk about URBs and Endpoints. An 'URB' is a USB Request Block. If you want to fully understand the packet structure of an URB, that link and several other resources on the Internet are available. Discussing URBs in detail is beyond the scope of this article. Fortunately for us, we don't require this in-depth knowledge of URBs in order to accomplish our task. We only need to understand the way that "usbmon" represents the calls going from the Linux kernel to the device, and packets from the device coming back to the Linux Kernel.
Endpoints are the points in USB terminology that we can talk to, sort of like sockets. As represented by usbmon, they are either listening for a request 'S = Submission', or they are sending back data after a request 'C = Callback'. The response will come from the same endpoint as the request.
The types of Endpoints and associated bits of information (according to usbmon) that we need to understand are as follows:
Endpoint Types:
Control Transfer Interrupt Transfer Isochronous Transfer Bulk Transfer
Event-Type: S=submission, C=callback, E=submission error
URB-Type and direction:
Ci or Co Control input or Control output Zi or Zo Isochronous input or Isochronous output Ii or Io Interrupt input or Interrupt output Bi or Bo Bulk input or Bulk output
URB-Status: s=setup and 0=acknowledged
Each line in the capture file that we have created via usbmon represents an URB. We will use
a simple query
(first line) and response (second line) to show an example of the structure produced by usbmon (color coded):
ffff880023078a80 2959991785 S Ci:1:003:0 s c1 23 0100 0100 0008 8 <
ffff880023078a80 2959991966 C Ci:1:003:0 0 8 = 4e4f5453 50505254
Now let's break this down:
Submission(Query): Internal-Mem URB address/tag Time Stamp Event-Type URB-Transfer-Type/direction Bus Dev Endpoint ffff880023078a80 2959991785 S Ci: 1: 003: 0 URB-Status bRequestType bRequest wValue wIndex wLength wLength s c1 23 0100 0100 0008 8 <
Callback(Response): Internal-Mem URB address/tag Time Stamp Event-Type URB-Transfer-Type/direction Bus Dev Endpoint ffff880023078a80 2959991966 C Ci: 1: 003: 0 URB-Status Data-Length Data-Tag Data-Words (In this case, 4x2=8 bytes) 0 8 = 4e4f5453 50505254
In the Submission, the first value for wLength is a hex value, and the second is an int value.
Note the colors used to separate the values.
Step 5: Look at the verbose output of 'lsusb'
What we need to determine now are the Endpoints of interest. This command will tell us what we need to know. It may be helpful to send the output of this command to a text file for examination:
[root@server5 ~]# lsusb -v > lsusb.txt
In my case, the target device in my text file looked like this (points of interest in red):
Bus 001 Device 003: ID 04e8:344f Samsung Electronics Co., Ltd Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x04e8 Samsung Electronics Co., Ltd idProduct 0x344f bcdDevice 1.00 iManufacturer 1 Samsung Electronics Co., Ltd. iProduct 2 SCX-3400 Series iSerial 3 Z8BNB8KDCE00DQW bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 55 bNumInterfaces 2 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 2mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 255 Vendor Specific Class bInterfaceSubClass 255 Vendor Specific Subclass bInterfaceProtocol 255 Vendor Specific Protocol iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x04 EP 4 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 10 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 10 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 7 Printer bInterfaceSubClass 1 Printer bInterfaceProtocol 2 Bidirectional iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 10 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0200 1x 512 bytes bInterval 10 Device Qualifier (for other device speed): bLength 10 bDescriptorType 6 bcdUSB 2.00 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 bNumConfigurations 1 Device Status: 0x0001 Self Powered
We notice something interesting here... in most devices that I have seen, such as a scanner, there will normally be control endpoints or interrupt endpoints and they will be the ones receiving control messages. However, Samsung (or whoever built the guts of this thing) chose to manufacture this device with only bulk data endpoints. What this means to us is that control messages and bulk data streams will use the same type of endpoint. We also see that the capabilities for all 4 endpoints on this device are identical.
The messages that we are interested in are control messages, such as the requests to start a scan or stop a scan. When we press the scan button, what happens is, a request to query if this hardware address (button) has changed it's value is sent from the application via the device driver. Let's be more clear...
Remember that USB is a host initiated protocol. The device cannot inform us if the button has been pressed, so we will have to ask. The code that we will write will behave the same way as the Windows driver... our code will ask every X number of seconds if the button has been pressed, and we will ask that question exactly the way that the driver does.
So in order for us to know if the button has been pressed, we will have to:
1. Create a buffer of a given length.
2. Send a given command to a given endpoint on the device along with our buffer.
3. The endpoint that we query with our command will fill that buffer with an answer.
4. The buffer will be returned to us by the device endpoint and we can examine it's contents.