# Reversing the USB update process of a device

This post is a work in progress, so if you find it incomplete and not readable probably it's not finished yet. I prefer to publish a little before than leave a post to rust in my drafts.

I’m again at it: I have a device that I want to know how it works and I started to reverse it, this time without any particular reason if not curiosity.

What I couldn’t know was that I was entering a rabbit hole of biblic proportion and this post is only the tip of the iceberg.

In this post I want to describe without any particular order, how to reverse a C++ application and the USB protocol that it uses to update the firmware on the device. I don’t think this will be useful to anyone, let me know in case it has changed your life :)

I will use Ghidra and I will try to show how to do some specific steps and how I approach reversing in general.

## Context

The device is the EZCast bought on Aliexpress for like ten euro, I never used much since I have a Chromecast but out of curiosity I have opened it and soldered the serial on the two exposed pads and accessed the (root) shell.

It has an update process via USB by a Windows application that you can download from their site. The md5 of this application is 8cad508dddcbb11f67297e79609c2561 (I have seen two different versions during my experiments).

I never run it since I’m a Linux guy; some time ago I started playing with it and with a spell of mine I found out an interesting URL:

$strings MiraScreen/EZCastToolDriver/EZUpdate.exe | grep http ... https://www.iezvu.com/upgrade/ota_rx.php ...  right now I don’t remember how (probably I stumbled upon some json strings), but I found the right payload for a request $ curl -X POST -H "Content-type: application/json; charset=utf-8" -i https://www.iezvu.com/upgrade/ota_rx.php -d'{
"version":      1,
"vendor":       "ezcast",
"softap_ssid":  "000000-00000000",
"firmware_version":     "0"
}'
HTTP/2 200
server: nginx
date: Wed, 16 Aug 2017 16:03:02 GMT
content-type: text/html; charset=utf-8
vary: Accept-Encoding
x-powered-by: PHP/5.5.9-1ubuntu4.21

$curl -X POST -H "Content-type: application/json; charset=utf-8" -i https://www.iezvu.com/upgrade/ota_rx.php -d'{ "version": 1, "vendor": "mirawire_8252n", "mac_address": "authorshxj", "softap_ssid": "000000-00000000", "firmware_version": "0" }' HTTP/2 200 server: nginx date: Wed, 16 Aug 2017 16:14:38 GMT content-type: text/html; charset=utf-8 vary: Accept-Encoding x-powered-by: PHP/5.5.9-1ubuntu4.21 {"ota_conf_file":"http://cdn.iezvu.com/upgrade/mirawire_8252n_8M/mirawire_8252n_8M-16285000.conf","ota_fw_file":"http://cdn.iezvu.com/upgrade/mirawire_8252n_8M/mirawire_8252n_8M-16285000.gz","ota_enforce":false}  and downloaded the firmware update at the url indicated in the field ota_fw_file. Analyzing the firmware opened a can of worms, since now I wanted to know how the upgrade process works. ## MFC C++ First of all, the application is using VC6 (the magic in the FuncInfo struct is 0x19930520). I opened ghidra and imported the binary: obviously I had no idea of what I was doing so the initial phase was to start jumping around the calls tree: my first thing that I do when reversing something is to rename functions, variables etc… with labels that indicate something about them, if not just to remember that is something I have already seen. It can seem something boring and useless but I assure you that the brain is very good at spotting patterns (maybe too much) and this helps a lot during the reversing process. However, at some point, walking up to the call tree you arrive to a function that is not called from no one and you are like “uhm, how is this possible?”; for example the function at 0x0040a050 (that by the way is the function at the end calls the one the uses the url described above). Maybe the address is indicated somewhere, or the code is not disassembled yet; using Ghidra you can look for direct reference using the menu Search > For Direct References and found that at 0x004e02a4 there is 4 bytes value corresponding at that address. Right clicking on the address in the listing window and choosing Data > pointer from the menu you can inform Ghidra that the address stores a pointer to a function (you can also define a shortcut for data type you use frequently, I suggest you one for dword). Continuing to define as dword the values next to it you can see there is a pattern As I said previously, it is all about recognizing patterns, reversing patterns, and in this case if you search for something related to reversing MFC you can find an old post of Quequero where he describes the structure of this kind of application, in particular this struct struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT_PTR nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) };  that corresponds perfectly with what we are looking for. This struct is used to describe “callback” from element of the GUi of the program represented by the MFC class. At the end the layout in memory of a MFC class is the following + CRuntimeClass <------------------------------. + Message Map data <----------------------------------------. + ptr to MFC42.DLL::<super class>::messageMap() | | + ptr to AFX_MSGMAP_ENTRY array -. | | + AFX_MSGMAP_ENTRY array <------' | | + 0th element | | + 1th element | | ... | | + last element (all elements are NULL) | | + MFC class' vtable | | + GetRuntimeClass() (returns a pointer to) ---------' | + Destructor() | + null() | + null() | + null() | + OnCmdMsg() | ... | + GetTypeLib() | + GetMessageMap() (returns a pointer to) --------------------' ...  and in particular GetRuntimeClass() gives us the class this vtable belongs to; instead if you want the constructor you need to look at the function referencing the vtable: All the destructors have a structure like CDialog * __thiscall FUN_0040a4d0(void *this,byte param_1) { FUN_0040a4f0((CDialog *)this); if ((param_1 & 1) != 0) { operator_delete(this); } return (CDialog *)this; }  Some of these tables have as the first function something that ghidra doesn’t recognize as a GetRuntimeClass():  ************************************************************** * FUNCTION * ************************************************************** undefined * * __stdcall FUN_004192b0(void) undefined * * EAX:4 <RETURN> FUN_004192b0 XREF[1]: 004e0d40(*) 004192b0 0 b8 90 0c MOV EAX=>PTR_s_CPage_ECDkey_004e0c90,PTR_s_CPage_E = 0055fcf4 4e 00 004192b5 0 c3 RET  but in reality this is a custom object so the function returns the CRuntimeStructure for that object: char * m_lpszClassName dword m_nObjectSize dword * m_pBaseClass dword * m_pfnCreateObject dword * m_pfnGetBaseClass dword m_wSchema  Remember: you can follow the GetRuntimeClass() into the original library and find out the size of the class so to have an idea of how much space a class is going to occupy in memory (and in particular it’s very useful for local variables in the stack).  PTR_s_CPage_ECDkey_004e0c90 XREF[2]: FUN_004192b0:004192b0(*), FUN_004192b0:004192b0(*) 004e0c90 f4 fc 55 00 addr s_CPage_ECDkey_0055fcf4 = "CPage_ECDkey" 004e0c94 6c 41 00 00 ddw 416Ch 004e0c98 ff ff 00 00 ddw FFFFh 004e0c9c 50 92 41 00 addr FUN_00419250 004e0ca0 b0 3f 42 00 addr FUN_00423fb0 004e0ca4 00 00 00 00 ddw 0h  obviously also the getMessageMap() method is custom. You can read on the official documentation or an article about CString internals in VC6 bad enough this application uses mfc42.dll that is a library with API not completly “forward” compatible with the one existing today. You can organize smartly the functon into class moving the function in the classes with the mouse once that you put the function into the right hierarchy, __thiscall set the ecx register to the right type (be aware that the struct connected to the class must have the same name, it’s obvious you know, until you name one Whatever and the other WhateverClass). ### Subclasses The classes that are used in the application are Name Description CPageUpdateClass CDialogUpdateClass explicit CDialog( UINT nIDTemplate, CWnd* pParentWnd = NULL); UINT GetDlgItemTextA( HWND hDlg, int nIDDlgItem, LPSTR lpString, int cchMax ); BOOL SetDlgItemTextA( HWND hDlg, int nIDDlgItem, LPCSTR lpString );  after doModal() there is initDialog() The general organization of the vtable is the following GetRuntimeClass ??? nullsub nullsub nullsub OnCmdMsg OnFinalRelease IsInvokeAllowed GetDispatchIID GetTypeInfoCount GetTypeLibCache GetTypeLib GetMessageMap GetCommandMap GetDispatchMap GetConnectionMap GetInterfaceMap GetEventSinkMap OnCreateAggregates GetInterfaceHook GetExtraConnectionPoints GetConnectionHook PreSubclassWindow Create DestroyWindow PreCreateWindow CalcWindowRect OnToolHitTest GetScrollBarCtrl WinHelpA ContinueModal EndModalLoop OnCommand OnNotify GetSuperWndProcAddr ??? _function_shared CPage_ECDkey::FUN_00401fa0 PreTranslateMessage OnAmbientProperty WindowProc OnWndMsg DefWindowProcA PostNcDestroy OnChildNotify CheckAutoCenter IsFrameWnd SetOccDialogInfo DoModal OnInitDialog OnSetFont OnOK OnCancel PreInitDialog  ## Windows quirks For someone like me, coming from linux, some aspects of the runtime of Windows are very puzzling ### Libraries resolution This is the most WTF of all: the external call to a function placed in a library is identified by an id so you cannot know the name of the function called if you don’t have the library and in my case I have some API that are mismatched from the one recognnized by ghidra (it’s obvious since in every place where those functions are used the stack goes banana). Maybe I’m missing something here but how in the world someone could think that using an id was a good idea? ### Calling conventions Internally this application uses all the possible families of calling convention that I list here Name Arguments Stack cleaning Return value cdecl passed on the stack in reverse order by the caller eax stdcall passed on the stack in reverse order callee eax fastcall passed via registers by the caller eax thiscall used with class’s methods, ecx contains the this pointer and the arguments are on the stack on the reverse order eax If ghidra mis-recognize the calling convention of a function you will see something strange happening to the local variables. ### Resources Inside a Windows executable is possible to use a particular section (named .rsrc) for storing resources, like icon, images etc… and load it during the execution with appropriate calls, like FindResourceA(hModule,(LPCSTR)((uint)rsrc_id & 0xffff),type). This can be useful to “connect the dots” to particular functionality: in the application exists the function loadRsrc() that takes a parameter identifying the resource and set a particular field of the class to the resource: you can see that is possible to directly see what icon corresponds to what resource from ghidra Strange that from the symbol tree pane I see the PNGs and them have the xref to the function but the call to loadRsrc() doesn’t have the xref back (if not a = <PNG-Image> comment in the listing window that doesn’t jump to the resource). It’s possible also to reconstruct some custom dialog using the resource id passed as argument to the constructor of CDialog() ## Internal state There is a global variable used to handle the internal state of the GUI at 0x00594e78 and it’s processed mainly at 0x00409a20 by a function named by me setState(); this function is pretty interesting since allows to know what state corresponds to what number via the messages that presents to the user and so you can create a wonderful enum :) Also there are pieces of the interfaces that are set, like the PNGs etc… ## Firmware downloading and parsing  int __cdecl download(CDialogUpdateClass *this) { int iVar1; setState(this,DOWNLOADING_CONF); iVar1 = download_conf(this); if (iVar1 == 0) { setState(this,RETRY_SERVER); return 1; } setState(this,UPGRADE); return 1; }   /* Here get the response from the server with FW and CONF urls, but also downloads the conf. */ int __fastcall download_conf(CDialogUpdateClass *this) { char *response; int iVar1; response = (char *)client_do_request(s_https://www.iezvu.com/upgrade/ot_0055e0ec, (char *)&this->json); if (response == NULL) { return 0; } /* here seems that the function takes like three args but internally doesn't use the last two */ parse_response(response,(char *)&this->ota_conf_file,(char *)&this->ota_fw_file); free(response); iVar1 = downloading_at((char *)&this->ota_conf_file,(char *)&this->Upgrade.con_path); if (iVar1 != 0) { return 0; } getServerVersionFromConfFile(this); return 1; }  The core of what interest us is at 0x00408d50:  int __cdecl update(CDialogUpdateClass *this) { int downloadStatus; downloadStatus = download_firmware(this); if (downloadStatus != 0) { setState(this,DOWNLOAD_FIRMWARE_FAILED); return 0; } firmware_open_and_parse(this); return 1; }  00408d80 download_firmware() void __fastcall download_firmware(CDialogUpdateClass *this) { setState(this,DOWNLOADING_FIRMWARE); downloading_at((char *)&this->ota_fw_file,(char *)&this->Upgrade.tmp_path); return; }  After downloading the file from the remote server and saving it in Upgrade.tmp (this path is set by the routine that starts at 0x0040882c) the application parses it. If we take a look at the first 200 bytes of the firmware we have some hints that is structured into different sections: 00000000: 4163 7469 6f6e 7346 6972 6d77 6172 6500 ActionsFirmware. 00000010: 5570 6461 7465 5665 723a 7631 2e33 3100 UpdateVer:v1.31. 00000020: 4348 4543 4b53 554d 0000 0000 0000 0000 CHECKSUM........ 00000030: 4000 0000 c0fd b801 0000 0000 b59b c093 @............... 00000040: 434f 4d50 5245 5353 0000 0000 0000 0000 COMPRESS........ 00000050: 0000 0000 0000 0000 00b2 4100 00de 9d00 ..........A..... 00000060: 4144 4543 6164 6675 7300 0000 0000 0000 ADECadfus....... 00000070: 0004 0000 0020 0000 0000 04b4 0000 0000 ..... .......... 00000080: 4144 4655 6164 6675 7300 0000 0000 0000 ADFUadfus....... 00000090: 0024 0000 1826 0000 0000 00a0 0000 0000 .$...&..........
000000a0: 4857 5343 6877 7363 0000 0000 0000 0000  HWSChwsc........
000000b0: 004c 0000 102a 0000 0000 01a0 0000 0000  .L...*..........
000000c0: 4636 3438 6677 7363 0000 0000 0000 0000  F648fwsc........
000000d0: 0078 0000 2091 0100 0080 01a0 0000 0000  .x.. ...........
000000e0: 4636 3438 6d62 7265 6300 0000 0000 0000  F648mbrec.......
000000f0: 000a 0200 001a 0000 0000 01a0 0040 04b4  .............@..
00000100: 4636 3438 6272 6563 0000 0000 0000 0000  F648brec........
00000110: 0024 0200 0000 0200 0000 0000 1000 0000  .$.............. 00000120: 4649 524d 0000 0000 9879 f68a e33c 4541 FIRM.....y...<EA 00000130: 0024 0400 0000 9e00 00de 9d00 c304 2400 .$............. 00000140: 4c49 4e55 5800 0000 0000 0000 0000 0000 LINUX........... 00000150: 0100 0000 0000 e000 0300 0000 0000 0000 ................ 00000160: 726f 6f74 6673 0000 0000 0000 0000 0000 rootfs.......... 00000170: 002a 2800 0000 0004 0000 0004 8300 0100 .*(............. 00000180: 7573 6572 3100 0000 0000 0000 0000 0000 user1........... 00000190: 00f0 8b01 0000 8000 0000 8000 8300 0100 ................ 000001a0: 7672 616d 0000 0000 0000 0000 0000 0000 vram............ 000001b0: 00a4 b801 005a 0000 0000 1000 0b00 0000 .....Z.......... 000001c0: 7265 7365 7276 6500 0000 0000 0000 0000 reserve......... 000001d0: 0000 0000 0000 0000 0000 0002 0000 0000 ................ 000001e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 000001f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................  it seems that the header is composed of header sections of 32 bytes each, let’s see if we are able to understand the structure of them. The function firmware_open_and_parse() located at 0x00408b00 then opens the downloaded firmware using a global CFile instance located at 0x00593858 that will be used from other parts of the application to act on the firmware itself or on other kind of files. The next step parses finally the firmware at 0x0040e7a0: first of all the first 16 bytes must contain the header of the firmware with the string ActionsFirmware A interesting part is the handling of the CHECKSUM section: if there isn’t such section there is some code to decrypt, however if there is a section with that name then read some values and calculate the checksum  00000020 43 48 45 43 4b char[16] "CHECKSUM" checksum 53 55 4d 00 00 00 00 00 00 00 00000030 40 00 00 00 ddw 40h start 00000034 c0 ed 66 00 ddw 66EDC0h size 00000038 00 00 00 00 ddw 0h encriptedFlag 0000003c 5a fa ad 12 ddw 12ADFA5Ah checksum  the function is at 0x00407510 int __cdecl checksum(void *buffer,uint size) { int ctr; byte bVar1; uint nDword; dword *ptrBuffer; int iVar2; dword tmp; ctr = 0; nDword = size >> 2; ptrBuffer = (dword *)buffer; while (nDword != 0) { tmp = *ptrBuffer; ptrBuffer = ptrBuffer + 1; ctr = ctr + tmp; nDword = nDword - 1; } if (ptrBuffer < (dword *)(size + (int)buffer)) { iVar2 = 0; do { bVar1 = (byte)iVar2; iVar2 = iVar2 + 8; ctr = ctr + ((uint)*(byte *)ptrBuffer << (bVar1 & 0x1f)); ptrBuffer = (dword *)((int)ptrBuffer + 1); } while (ptrBuffer < (dword *)(size + (int)buffer)); } return ctr; }  After that check for the sections named LINUX and FIRM, with the last one mandatory. 00000120 46 49 52 4d section_t 00 00 00 00 9a 0b f0 dc 00000120 46 49 52 4d 00 char[16] "FIRM" name 00 00 00 9a 0b f0 dc a1 74 bf 00000130 00 de 01 00 ddw 1DE00h start_address 00000134 00 00 17 00 ddw 170000h ??? 00000138 00 ce 16 00 ddw 16CE00h size 0000013c df c6 16 00 ddw 16C6DFh unk1  00000160 72 6f 6f 74 section_t 66 73 00 00 00 00 00 00 00000160 72 6f 6f 74 66 char[16] "rootfs" name 73 00 00 00 00 00 00 00 00 00 00000170 00 a6 18 00 ddw 18A600h start_address 00000174 00 50 4e 00 ddw 4E5000h length 00000178 00 50 4e 00 ddw 4E5000h unk0 0000017c 83 00 01 00 ddw 10083h unk1 00000180 76 72 61 6d section_t 00 00 00 00 00 00 00 00 00000180 76 72 61 6d 00 char[16] "vram" name 00 00 00 00 00 00 00 00 00 00 00000190 00 a0 66 00 ddw 66A000h start_address 00000194 00 4e 00 00 ddw 4E00h length 00000198 00 00 08 00 ddw 80000h unk0 0000019c 0b 00 00 00 ddw Bh unk1  The LINUX parsing is done at 0x00417ae0 and it’s the more puzzling piece because some values don’t make sense: this is an example 00000140 4c 49 4e 55 section_t 58 00 00 00 00 00 00 00 00000140 4c 49 4e 55 58 char[16] "LINUX" name 00 00 00 00 00 00 00 00 00 00 00000150 01 00 00 00 ddw 1h ????? 00000154 00 00 20 00 ddw 200000h ????? 00000158 02 00 00 00 ddw 2h n_subsections 0000015c 00 00 00 00 ddw 0h ????  at 0x14 and 0x1c there is something used elsewhere. The interesting fact is that is possible to have a reserve section that doesn’t seem to indicate actual data in the OTA but some metadata. The dword at offset 0x18 indicates the number of subsections to read: the name of the subsections doesn’t imply anything. The FIRM instead at 0x0040ef10; at 0x00406e80 is manipulating the first 0x80 bytes copied in memory At this point the smart reader could ask about the other sections, like ADECadfus, ADFUadfus etc… that part will be investigated in the next post where I’ll take a look more in detail in the internal working(?) of this family of chips. ### Intermezzo: stack_adjust This is particular function that I encountered during my trip in the assembly land  004dbf50 0 51 PUSH ECX <-- it's going to use ECX 004dbf51 004 3d 00 10 00 00 CMP EAX,0x1000 <-- EAX must be passed as argument 004dbf56 004 8d 4c 24 08 LEA ECX,[ESP + 0x8] <-- ECX = addr of 1st arg .----- 004dbf5a 004 72 14 JC LAB_004dbf70 | LAB_004dbf5c | .--> 004dbf5c 004 81 e9 00 10 SUB ECX,0x1000 | | 00 00 | | 004dbf62 004 2d 00 10 00 00 SUB EAX,0x1000 | | 004dbf67 004 85 01 TEST dword ptr [ECX],EAX <--- here it's make an AND between | | 004dbf69 004 3d 00 10 00 00 CMP EAX,0x1000 | '--- 004dbf6e 004 73 ec JNC LAB_004dbf5c | LAB_004dbf70 '----> 004dbf70 004 2b c8 SUB ECX,EAX <--- ECX points to addr 1st arg - EAX 004dbf72 004 8b c4 MOV EAX,ESP <--- EAX now points to the stack frame 004dbf74 004 85 01 TEST dword ptr [ECX],EAX <--- USELESS??? 004dbf76 004 8b e1 MOV ESP,ECX <--- now use ECX as stack pointer (ghidra goes banana) 004dbf78 - ? - 8b 08 MOV ECX,dword ptr [EAX] <--- restore ECX 004dbf7a - ? - 8b 40 04 MOV EAX,dword ptr [EAX + 0x4] <---. 004dbf7d - ? - 50 PUSH EAX <-----------------------'-- restore the return address so that 004dbf7e - ? - c3 RET <------------------------------------------------------' we jump back to the caller  probably is a “dynamic” allocation routine that uses the stack: it moves the stack pointer EAX bytes below: indeed at the end of each function that uses this method there is a ADD ESP, <offset> that restore the correct frame for the caller. Since this function messup the stack, every function that uses it makes ghidra lose tracking of the local variables after the call. The best way to deal with it is to set stack depth change to minus the offset plus four (I don’t know why… probably there is a disalignment between the listing and decompilation windows) seems that ghidra can handle this with alloca_probe ## GZIP An interesting part is where the code gunzip the FIRM section at 0x00414d50: it doesn’t seem to be depending from parameters as I thought initially (I have firmwares from other Actions’ devices that don’t compress that part). It’s the first time I recognize the format following the specification. ## AWK Function at 0x004111d0 does some magic with awk to parse %s -v BS=\ "{if(NF==BS){$NF=NULL;line=line$0;}else{print line $0;line=NULL;}}" %s |%s -F# -v SP=" " "$1{gsub(/\t/,SP,$1);gsub(/rd_size=__FIX_ME_ON_PACK__/,\"rd_size=0x%08x\",$1);print \$1}" > %s


## Partitions

At 0x00416660 the application builds what seems to be the Native MBR using the information extracted during the parsing of the LINUX portion of the firmware

## USB communication protocol

The mechanism that the application uses to update the firmware is by a custom USB protocol on top of the mass storage; the core of it is the Command Block Wrapper (CBW) a packet of 31 bytes (yeah, 31) having the following organization:

  .----.----.----.----.
| U    S    B    C  |
|----|----|----|----|
|        tag        |
|----|----|----|----|
|   transferLength  |
|----'----'----'----'
| Fl | LU | CL |    |
|----|----|----|....|
|                   |
|....|....|....|....|
|                   |
|....|....|....|....|
|         |         |
|....|....|....|....'
|              |
'....|....|....'


the “custom” part is inside CBWCB.

struct cmd_block_t {
byte cmd;
dword arg0;
dword arg1;
short subCmd;
short subCmd2;
};

struct CBW_t {
byte[4] signature;
dword tag;
dword transferLength;
byte flags;
byte LUN;
byte cmdLength;
struct cmd_block_t cmdBlock;
};


The typical code is the following

  CBW_t localCBW;

/* first it copies the 31 bytes of the pre-filled packet */
int counter = 7;
dword* cbw = (dword *)&GLOBAL_CBW_PACKET;
dword* ref2localCBW = (dword *)&localCBW;
while (counter != 0) {
counter = counter + -1;
*ref2localCBW = *cbw;
cbw = cbw + 1;
ref2localCBW = ref2localCBW + 1;
}
/* this strange assignment exists because there are the three remaining bytes*/
*(undefined2 *)ref2localCBW = *(undefined2 *)cbw;
*(undefined *)((int)ref2localCBW + 2) = *(undefined *)((int)cbw + 2);

/* then it fills the values needed for the wanted function */
localCBW.transferLength = transferLength;
localCBW.cmdBlock.arg0 = arg0;
localCBW.cmdBlock.arg1 = arg1;

/* it then sends the packet to the device */
retValue = USB_Write(&localCBW,0x1f,usbIndex);

/*