MalwareTech Challenge - shellcode2.exe

Jun 24, 2019 • mtchallenge,tools,re

I have been teaching myself to reverse engineer binary programs so that I can use these skills to reverse engineer malware. I have been learning assembly code, and playing with new tools such as ghidra and radare2/cutter.

I found that @MalwareTech had some great binary analysis challenges on his blog and decided to check them out.

This write up covers the fifth challenge shellcode2.exe: ‘https://www.malwaretech.com/challenges-shellcode2’

Lets open this binary in cutter and analyze it with radare2. Lets take a look at the entry function:

/ (fcn) entry0 342
|   entry0 ();
|           ; var LPVOID s1 @ ebp-0xc0
|           ; var LPCSTR lpText @ ebp-0xbc
|           ; var int32_t var_b8h @ ebp-0xb8
|           ; var int32_t var_28h @ ebp-0x28
|           ; var int32_t var_27h @ ebp-0x27
|           ; var int32_t var_26h @ ebp-0x26
|           ; var int32_t var_25h @ ebp-0x25
|           ; var int32_t var_24h @ ebp-0x24
|           ; var int32_t var_23h @ ebp-0x23
|           ; var int32_t var_22h @ ebp-0x22
|           ; var int32_t var_21h @ ebp-0x21
|           ; var int32_t var_20h @ ebp-0x20
|           ; var int32_t var_1fh @ ebp-0x1f
|           ; var int32_t var_1eh @ ebp-0x1e
|           ; var int32_t var_1dh @ ebp-0x1d
|           ; var int32_t var_1ch @ ebp-0x1c
|           ; var int32_t var_1bh @ ebp-0x1b
|           ; var int32_t var_1ah @ ebp-0x1a
|           ; var int32_t var_19h @ ebp-0x19
|           ; var int32_t var_18h @ ebp-0x18
|           ; var int32_t var_17h @ ebp-0x17
|           ; var int32_t var_16h @ ebp-0x16
|           ; var int32_t var_15h @ ebp-0x15
|           ; var int32_t var_14h @ ebp-0x14
|           ; var int32_t var_13h @ ebp-0x13
|           ; var int32_t var_12h @ ebp-0x12
|           ; var int32_t var_11h @ ebp-0x11
|           ; var int32_t var_10h @ ebp-0x10
|           ; var int32_t var_fh @ ebp-0xf
|           ; var int32_t var_eh @ ebp-0xe
|           ; var int32_t var_dh @ ebp-0xd
|           ; var int32_t var_ch @ ebp-0xc
|           ; var int32_t var_bh @ ebp-0xb
|           ; var int32_t var_ah @ ebp-0xa
|           ; var int32_t var_9h @ ebp-0x9
|           ; var int32_t var_8h @ ebp-0x8
|           ; var int32_t var_7h @ ebp-0x7
|           ; var int32_t var_6h @ ebp-0x6
|           ; var int32_t var_5h @ ebp-0x5
|           ; var LPVOID var_4h @ ebp-0x4
|           0x00402270      push ebp
|           0x00402271      mov  ebp, esp
|           0x00402273      sub  esp, 0xc0
|           0x00402279      lea  ecx, [var_b8h]
|           0x0040227f      call sym.shellcode2.exe___0MD5__QAE_XZ
|           0x00402284      mov  byte [var_28h], 0x12 ; 18
|           0x00402288      mov  byte [var_27h], 0x24 ; '$' ; 36
|           0x0040228c      mov  byte [var_26h], 0x28 ; '(' ; 40
|           0x00402290      mov  byte [var_25h], 0x34 ; '4' ; 52
|           0x00402294      mov  byte [var_24h], 0x5b ; '[' ; 91
|           0x00402298      mov  byte [var_23h], 0x23 ; '#' ; 35
|           0x0040229c      mov  byte [var_22h], 0x26 ; '&' ; 38
|           0x004022a0      mov  byte [var_21h], 0x20 ; 32
|           0x004022a4      mov  byte [var_20h], 0x35 ; '5' ; 53
|           0x004022a8      mov  byte [var_1fh], 0x37 ; '7' ; 55
|           0x004022ac      mov  byte [var_1eh], 0x4c ; 'L' ; 76
|           0x004022b0      mov  byte [var_1dh], 0x28 ; '(' ; 40
|           0x004022b4      mov  byte [var_1ch], 0x76 ; 'v' ; 118
|           0x004022b8      mov  byte [var_1bh], 0x26 ; '&' ; 38
|           0x004022bc      mov  byte [var_1ah], 0x33 ; '3' ; 51
|           0x004022c0      mov  byte [var_19h], 0x37 ; '7' ; 55
|           0x004022c4      mov  byte [var_18h], 0x3a ; ':' ; 58
|           0x004022c8      mov  byte [var_17h], 0x27 ; ''' ; 39
|           0x004022cc      mov  byte [var_16h], 0x3d ; '=' ; 61
|           0x004022d0      mov  byte [var_15h], 0x6e ; 'n' ; 110
|           0x004022d4      mov  byte [var_14h], 0x25 ; '%' ; 37
|           0x004022d8      mov  byte [var_13h], 0x48 ; 'H' ; 72
|           0x004022dc      mov  byte [var_12h], 0x6f ; 'o' ; 111
|           0x004022e0      mov  byte [var_11h], 0x3c ; '<' ; 60
|           0x004022e4      mov  byte [var_10h], 0x58 ; 'X' ; 88
|           0x004022e8      mov  byte [var_fh], 0x3a ; ':' ; 58
|           0x004022ec      mov  byte [var_eh], 0x68 ; 'h' ; 104
|           0x004022f0      mov  byte [var_dh], 0x2c ; ',' ; 44
|           0x004022f4      mov  byte [var_ch], 0x43 ; 'C' ; 67
|           0x004022f8      mov  byte [var_bh], 0x73 ; 's' ; 115
|           0x004022fc      mov  byte [var_ah], 0x10 ; 16
|           0x00402300      mov  byte [var_9h], 0xe ; 14
|           0x00402304      mov  byte [var_8h], 0x10 ; 16
|           0x00402308      mov  byte [var_7h], 0x6b ; 'k' ; 107
|           0x0040230c      mov  byte [var_6h], 0x10 ; 16
|           0x00402310      mov  byte [var_5h], 0x6f ; 'o' ; 111
|           0x00402314      push 0x10 ; 16
|           0x00402316      push 0 ; DWORD dwFlags
|           0x00402318      call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; 0x403010 ; ",1" ; HANDLE GetProcessHeap(void)
|           0x0040231e      push eax ; HANDLE hHeap
|           0x0040231f      call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; 0x40300c ; " 1" ; LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
|           0x00402325      mov  dword [var_4h], eax
|           0x00402328      mov  eax, dword [var_4h]
|           0x0040232b      mov  ecx, dword sym.imp.KERNEL32.dll_LoadLibraryA ; [0x403008:4]=0x3110 reloc.KERNEL32.dll_LoadLibraryA
|           0x00402331      mov  dword [eax], ecx
|           0x00402333      mov  edx, dword [var_4h]
|           0x00402336      mov  eax, dword sym.imp.KERNEL32.dll_GetProcAddress ; [0x403004:4]=0x30fe reloc.KERNEL32.dll_GetProcAddress
|           0x0040233b      mov  dword [edx + 4], eax
|           0x0040233e      mov  ecx, dword [var_4h]
|           0x00402341      lea  edx, [var_28h]
|           0x00402344      mov  dword [ecx + 8], edx
|           0x00402347      mov  eax, dword [var_4h]
|           0x0040234a      mov  dword [eax + 0xc], 0x24 ; '$' ; [0x24:4]=-1 ; 36
|           0x00402351      push 0x40 ; '@' ; 64 ; DWORD flProtect
|           0x00402353      push 0x1000 ; DWORD flAllocationType
|           0x00402358      push 0x248 ; 584 ; SIZE_T dwSize
|           0x0040235d      push 0 ; LPVOID lpAddress
|           0x0040235f      call dword [sym.imp.KERNEL32.dll_VirtualAlloc] ; 0x403000 ; LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|           0x00402365      mov  dword [s1], eax
|           0x0040236b      push 0x248 ; 584 ; size_t n
|           0x00402370      push 0x404040 ; '@@@' ; "U\x89\xe5\x81\xec\xbc\x01" ; const void *s2
|           0x00402375      mov  ecx, dword [s1]
|           0x0040237b      push ecx ; void *s1
|           0x0040237c      call sub.ntdll.dll_memcpy ; void *memcpy(void *s1, const void *s2, size_t n)
|           0x00402381      add  esp, 0xc
|           0x00402384      push dword [var_4h]
|           0x00402387      call dword [s1]
|           0x0040238d      lea  edx, [var_28h]
|           0x00402390      push edx
|           0x00402391      lea  ecx, [var_b8h]
|           0x00402397      call sym.shellcode2.exe__digestString_MD5__QAEPADPAD_Z
|           0x0040239c      mov  dword [lpText], eax
|           0x004023a2      push 0x30 ; '0' ; 48 ; UINT uType
|           0x004023a4      push str.We_ve_been_compromised ; 0x403038 ; "We've been compromised!" ; LPCSTR lpCaption
|           0x004023a9      mov  eax, dword [lpText]
|           0x004023af      push eax ; LPCSTR lpText
|           0x004023b0      push 0 ; HWND hWnd
|           0x004023b2      call dword [sym.imp.USER32.dll_MessageBoxA] ; 0x40301c ; "~1" ; int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
|           0x004023b8      push 0 ; UINT uExitCode
|           0x004023ba      call dword [sym.imp.KERNEL32.dll_ExitProcess] ; 0x403014 ; void ExitProcess(UINT uExitCode)
|           0x004023c0      xor  eax, eax
|           0x004023c2      mov  esp, ebp
|           0x004023c4      pop  ebp
\           0x004023c5      ret

Right away it looks like we are dealing with quite a few variables. Lets look at the meat and potatoes:

|           0x00402314      push 0x10 ; 16
|           0x00402316      push 0 ; DWORD dwFlags
|           0x00402318      call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; 0x403010 ; ",1" ; HANDLE GetProcessHeap(void)
|           0x0040231e      push eax ; HANDLE hHeap
|           0x0040231f      call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; 0x40300c ; " 1" ; LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
|           0x00402325      mov  dword [var_4h], eax
|           0x00402328      mov  eax, dword [var_4h]
|           0x0040232b      mov  ecx, dword sym.imp.KERNEL32.dll_LoadLibraryA ; [0x403008:4]=0x3110 reloc.KERNEL32.dll_LoadLibraryA
|           0x00402331      mov  dword [eax], ecx
|           0x00402333      mov  edx, dword [var_4h]
|           0x00402336      mov  eax, dword sym.imp.KERNEL32.dll_GetProcAddress ; [0x403004:4]=0x30fe reloc.KERNEL32.dll_GetProcAddress
|           0x0040233b      mov  dword [edx + 4], eax
|           0x0040233e      mov  ecx, dword [var_4h]
|           0x00402341      lea  edx, [var_28h]
|           0x00402344      mov  dword [ecx + 8], edx
|           0x00402347      mov  eax, dword [var_4h]
|           0x0040234a      mov  dword [eax + 0xc], 0x24 ; '$' ; [0x24:4]=-1 ; 36
|           0x00402351      push 0x40 ; '@' ; 64 ; DWORD flProtect
|           0x00402353      push 0x1000 ; DWORD flAllocationType
|           0x00402358      push 0x248 ; 584 ; SIZE_T dwSize
|           0x0040235d      push 0 ; LPVOID lpAddress
|           0x0040235f      call dword [sym.imp.KERNEL32.dll_VirtualAlloc] ; 0x403000 ; LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|           0x00402365      mov  dword [s1], eax
|           0x0040236b      push 0x248 ; 584 ; size_t n
|           0x00402370      push 0x404040 ; '@@@' ; "U\x89\xe5\x81\xec\xbc\x01" ; const void *s2
|           0x00402375      mov  ecx, dword [s1]
|           0x0040237b      push ecx ; void *s1
|           0x0040237c      call sub.ntdll.dll_memcpy ; void *memcpy(void *s1, const void *s2, size_t n)
|           0x00402381      add  esp, 0xc
|           0x00402384      push dword [var_4h]
|           0x00402387      call dword [s1]

Right away a lot of this looks very familiar to the last challenge. We are grabbing the heap and allocating a piece of it:

|           0x00402314      push 0x10 ; 16
|           0x00402316      push 0 ; DWORD dwFlags
|           0x00402318      call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; 0x403010 ; ",1" ; HANDLE GetProcessHeap(void)
|           0x0040231e      push eax ; HANDLE hHeap
|           0x0040231f      call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; 0x40300c ; " 1" ; LPVOID HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)
|           0x00402325      mov  dword [var_4h], eax
|           0x00402328      mov  eax, dword [var_4h]

The heap is then taken from the eax register and stored in a dword var_4h and then put back into eax.

|           0x0040232b      mov  ecx, dword sym.imp.KERNEL32.dll_LoadLibraryA ; [0x403008:4]=0x3110 reloc.KERNEL32.dll_LoadLibraryA
|           0x00402331      mov  dword [eax], ecx
|           0x00402333      mov  edx, dword [var_4h]
|           0x00402336      mov  eax, dword sym.imp.KERNEL32.dll_GetProcAddress ; [0x403004:4]=0x30fe reloc.KERNEL32.dll_GetProcAddress
|           0x0040233b      mov  dword [edx + 4], eax
|           0x0040233e      mov  ecx, dword [var_4h]
|           0x00402341      lea  edx, [var_28h]
|           0x00402344      mov  dword [ecx + 8], edx
|           0x00402347      mov  eax, dword [var_4h]
|           0x0040234a      mov  dword [eax + 0xc], 0x24 ; '$' ; [0x24:4]=-1 ; 36

It then looks like we are storing some functions as dwords and moving them to registers. This looks to be getting our functions setup in our heap.

After all of this we are allocating page space, copying our shellcode over, and then executing it.

|           0x00402351      push 0x40 ; '@' ; 64 ; DWORD flProtect
|           0x00402353      push 0x1000 ; DWORD flAllocationType
|           0x00402358      push 0x248 ; 584 ; SIZE_T dwSize
|           0x0040235d      push 0 ; LPVOID lpAddress
|           0x0040235f      call dword [sym.imp.KERNEL32.dll_VirtualAlloc] ; 0x403000 ; LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
|           0x00402365      mov  dword [s1], eax
|           0x0040236b      push 0x248 ; 584 ; size_t n
|           0x00402370      push 0x404040 ; '@@@' ; "U\x89\xe5\x81\xec\xbc\x01" ; const void *s2
|           0x00402375      mov  ecx, dword [s1]
|           0x0040237b      push ecx ; void *s1
|           0x0040237c      call sub.ntdll.dll_memcpy ; void *memcpy(void *s1, const void *s2, size_t n)
|           0x00402381      add  esp, 0xc
|           0x00402384      push dword [var_4h]
|           0x00402387      call dword [s1]

It looks like our shellcode is 584 bytes starting at 0x404040. Cutter has a neat feature where you can copy in a byte array and it will parse it as shellcode:

You can also just click on the offset in the original code and view the dissassembly there. Lets look at the disassembly:

    0x00404040      push ebp
    0x00404041      mov  ebp, esp
    0x00404043      sub  esp, 0x1bc
    0x00404049      mov  byte [ebp - 0x58], 0x6d ; 'm' ; 109
    0x0040404d      mov  byte [ebp - 0x57], 0x73 ; 's' ; 115
    0x00404051      mov  byte [ebp - 0x56], 0x76 ; 'v' ; 118
    0x00404055      mov  byte [ebp - 0x55], 0x63 ; 'c' ; 99
    0x00404059      mov  byte [ebp - 0x54], 0x72 ; 'r' ; 114
    0x0040405d      mov  byte [ebp - 0x53], 0x74 ; 't' ; 116
    0x00404061      mov  byte [ebp - 0x52], 0x2e ; '.' ; 46
    0x00404065      mov  byte [ebp - 0x51], 0x64 ; 'd' ; 100
    0x00404069      mov  byte [ebp - 0x50], 0x6c ; 'l' ; 108
    0x0040406d      mov  byte [ebp - 0x4f], 0x6c ; 'l' ; 108
    0x00404071      mov  byte [ebp - 0x4e], 0
    0x00404075      mov  byte [ebp - 0xa0], 0x6b ; 'k' ; 107
    0x0040407c      mov  byte [ebp - 0x9f], 0x65 ; 'e' ; 101
    0x00404083      mov  byte [ebp - 0x9e], 0x72 ; 'r' ; 114
    0x0040408a      mov  byte [ebp - 0x9d], 0x6e ; 'n' ; 110
    0x00404091      mov  byte [ebp - 0x9c], 0x65 ; 'e' ; 101
    0x00404098      mov  byte [ebp - 0x9b], 0x6c ; 'l' ; 108
    0x0040409f      mov  byte [ebp - 0x9a], 0x33 ; '3' ; 51
    0x004040a6      mov  byte [ebp - 0x99], 0x32 ; '2' ; 50
    0x004040ad      mov  byte [ebp - 0x98], 0x2e ; '.' ; 46
    0x004040b4      mov  byte [ebp - 0x97], 0x64 ; 'd' ; 100
    0x004040bb      mov  byte [ebp - 0x96], 0x6c ; 'l' ; 108
    0x004040c2      mov  byte [ebp - 0x95], 0x6c ; 'l' ; 108
    0x004040c9      mov  byte [ebp - 0x94], 0
    0x004040d0      mov  byte [ebp - 0x1b8], 0x66 ; 'f' ; 102
    0x004040d7      mov  byte [ebp - 0x1b7], 0x6f ; 'o' ; 111
    0x004040de      mov  byte [ebp - 0x1b6], 0x70 ; 'p' ; 112
    0x004040e5      mov  byte [ebp - 0x1b5], 0x65 ; 'e' ; 101
    0x004040ec      mov  byte [ebp - 0x1b4], 0x6e ; 'n' ; 110
    0x004040f3      mov  byte [ebp - 0x1b3], 0
    0x004040fa      mov  byte [ebp - 0x4c], 0x66 ; 'f' ; 102
    0x004040fe      mov  byte [ebp - 0x4b], 0x72 ; 'r' ; 114
    0x00404102      mov  byte [ebp - 0x4a], 0x65 ; 'e' ; 101
    0x00404106      mov  byte [ebp - 0x49], 0x61 ; 'a' ; 97
    0x0040410a      mov  byte [ebp - 0x48], 0x64 ; 'd' ; 100
    0x0040410e      mov  byte [ebp - 0x47], 0
    0x00404112      mov  byte [ebp - 0xc], 0x66 ; 'f' ; 102
    0x00404116      mov  byte [ebp - 0xb], 0x73 ; 's' ; 115
    0x0040411a      mov  byte [ebp - 0xa], 0x65 ; 'e' ; 101
    0x0040411e      mov  byte [ebp - 9], 0x65 ; 'e' ; 101
    0x00404122      mov  byte [ebp - 8], 0x6b ; 'k' ; 107
    0x00404126      mov  byte [ebp - 7], 0
    0x0040412a      mov  byte [ebp - 0x60], 0x66 ; 'f' ; 102
    0x0040412e      mov  byte [ebp - 0x5f], 0x63 ; 'c' ; 99
    0x00404132      mov  byte [ebp - 0x5e], 0x6c ; 'l' ; 108
    0x00404136      mov  byte [ebp - 0x5d], 0x6f ; 'o' ; 111
    0x0040413a      mov  byte [ebp - 0x5c], 0x73 ; 's' ; 115
    0x0040413e      mov  byte [ebp - 0x5b], 0x65 ; 'e' ; 101
    0x00404142      mov  byte [ebp - 0x5a], 0
    0x00404146      mov  byte [ebp - 0x78], 0x47 ; 'G' ; 71
    0x0040414a      mov  byte [ebp - 0x77], 0x65 ; 'e' ; 101
    0x0040414e      mov  byte [ebp - 0x76], 0x74 ; 't' ; 116
    0x00404152      mov  byte [ebp - 0x75], 0x4d ; 'M' ; 77
    0x00404156      mov  byte [ebp - 0x74], 0x6f ; 'o' ; 111
    0x0040415a      mov  byte [ebp - 0x73], 0x64 ; 'd' ; 100
    0x0040415e      mov  byte [ebp - 0x72], 0x75 ; 'u' ; 117
    0x00404162      mov  byte [ebp - 0x71], 0x6c ; 'l' ; 108
    0x00404166      mov  byte [ebp - 0x70], 0x65 ; 'e' ; 101
    0x0040416a      mov  byte [ebp - 0x6f], 0x46 ; 'F' ; 70
    0x0040416e      mov  byte [ebp - 0x6e], 0x69 ; 'i' ; 105
    0x00404172      mov  byte [ebp - 0x6d], 0x6c ; 'l' ; 108
    0x00404176      mov  byte [ebp - 0x6c], 0x65 ; 'e' ; 101
    0x0040417a      mov  byte [ebp - 0x6b], 0x4e ; 'N' ; 78
    0x0040417e      mov  byte [ebp - 0x6a], 0x61 ; 'a' ; 97
    0x00404182      mov  byte [ebp - 0x69], 0x6d ; 'm' ; 109
    0x00404186      mov  byte [ebp - 0x68], 0x65 ; 'e' ; 101
    0x0040418a      mov  byte [ebp - 0x67], 0x41 ; 'A' ; 65
    0x0040418e      mov  byte [ebp - 0x66], 0
    0x00404192      mov  byte [ebp - 0x7c], 0x72 ; 'r' ; 114
    0x00404196      mov  byte [ebp - 0x7b], 0x62 ; 'b' ; 98
    0x0040419a      mov  byte [ebp - 0x7a], 0
    0x0040419e      mov  eax, dword [ebp + 8] ; [0x8:4]=-1 ; 8
    0x004041a1      mov  ecx, dword [eax]
    0x004041a3      mov  dword [ebp - 4], ecx
    0x004041a6      mov  ecx, dword [eax + 4] ; [0x4:4]=-1 ; 4
    0x004041a9      mov  dword [ebp - 0x44], ecx
    0x004041ac      lea  ecx, [ebp - 0x58]
    0x004041af      push ecx
    0x004041b0      call dword [ebp - 4]
    0x004041b3      mov  dword [ebp - 0x3c], eax
    0x004041b6      lea  edx, [ebp - 0xa0]
    0x004041bc      push edx
    0x004041bd      call dword [ebp - 4]
    0x004041c0      mov  dword [ebp - 0x84], eax
    0x004041c6      lea  eax, [ebp - 0x78]
    0x004041c9      push eax
    0x004041ca      mov  ecx, dword [ebp - 0x84]
    0x004041d0      push ecx
    0x004041d1      call dword [ebp - 0x44]
    0x004041d4      mov  dword [ebp - 0x10], eax
    0x004041d7      lea  edx, [ebp - 0x1b8]
    0x004041dd      push edx
    0x004041de      mov  eax, dword [ebp - 0x3c]
    0x004041e1      push eax
    0x004041e2      call dword [ebp - 0x44]
    0x004041e5      mov  dword [ebp - 0x80], eax
    0x004041e8      lea  ecx, [ebp - 0xc]
    0x004041eb      push ecx
    0x004041ec      mov  edx, dword [ebp - 0x3c]
    0x004041ef      push edx
    0x004041f0      call dword [ebp - 0x44]
    0x004041f3      mov  dword [ebp - 0xa4], eax
    0x004041f9      lea  eax, [ebp - 0x4c]
    0x004041fc      push eax
    0x004041fd      mov  ecx, dword [ebp - 0x3c]
    0x00404200      push ecx
    0x00404201      call dword [ebp - 0x44]
    0x00404204      mov  dword [ebp - 0x90], eax
    0x0040420a      lea  edx, [ebp - 0x60]
    0x0040420d      push edx
    0x0040420e      mov  eax, dword [ebp - 0x3c]
    0x00404211      push eax
    0x00404212      call dword [ebp - 0x44]
    0x00404215      mov  dword [ebp - 0x64], eax
    0x00404218      push 0x104 ; 260
    0x0040421d      lea  ecx, [ebp - 0x1b0]
    0x00404223      push ecx
    0x00404224      push 0
    0x00404226      call dword [ebp - 0x10]
    0x00404229      lea  edx, [ebp - 0x7c]
    0x0040422c      push edx
    0x0040422d      lea  eax, [ebp - 0x1b0]
    0x00404233      push eax
    0x00404234      call dword [ebp - 0x80]
    0x00404237      add  esp, 8
    0x0040423a      mov  dword [ebp - 0x40], eax
    0x0040423d      push 0
    0x0040423f      push 0x4e ; 'N' ; 78
    0x00404241      mov  ecx, dword [ebp - 0x40]
    0x00404244      push ecx
    0x00404245      call dword [ebp - 0xa4]
    0x0040424b      add  esp, 0xc
    0x0040424e      mov  edx, dword [ebp - 0x40]
    0x00404251      push edx
    0x00404252      push 1 ; 1
    0x00404254      push 0x26 ; '&' ; 38
    0x00404256      lea  eax, [ebp - 0x38]
    0x00404259      push eax
    0x0040425a      call dword [ebp - 0x90]
    0x00404260      add  esp, 0x10
    0x00404263      mov  ecx, dword [ebp - 0x40]
    0x00404266      push ecx
    0x00404267      call dword [ebp - 0x64]
    0x0040426a      add  esp, 4
    0x0040426d      mov  edx, dword [ebp + 8] ; [0x8:4]=-1 ; 8
    0x00404270      mov  ecx, dword [edx + 0xc] ; [0xc:4]=-1 ; 12
    0x00404273      mov  edi, dword [edx + 8] ; [0x8:4]=-1 ; 8
    0x00404274          .string "z\b1\xd2\x8aD" ; len=7 ;-- "z\b1ҊD":
:   0x0040427b      enter 0x430, 0x17 ; 1072
:   0x0040427f      inc  edx
:   0x00404280      cmp  edx, ecx
`=< 0x00404282      jne  0x404278
    0x00404284      mov  esp, ebp
    0x00404286      pop  ebp
    0x00404287      ret

The first thing we see is a large stack string being setup: Lets copy 0x00404049 to 0x00404196 to a file and run some commandline-fu on it to make it a bit easier on the eyes:

cat stack_string.txt | cut -d';' -f2 | sed -e 's/ //g' -e 's/\'//g' -e 's/^   //g' -e 's/^0x00404.*/ /g' | tr '\n' '\0'

msvcrt.dll kernel32.dll fopen fread fseek fclose GetModuleFileNameA rb

Much better. It looks like our stack string contains 2 dll names (msvcrt.dll, kernel32.dll) some basic c++ file commands (fopen, fread, fseek, fclose) and a function (GetModuleFileNameA). The last 2 characters “rb” are most likely parameters we are gonna pass to fopen which will tell it readonly and present the file as a binary object. I think its safe to assume we are going to be opening a file, grabbing some bytes, and creating our flag from that. Lets pull up the docs. Besides the functions and dlls above lets also get the docs for the two functions we saved as dwords earlier.

GetModuleFileNameA

Retrieves the fully qualified path for the file that contains the specified module. The module must have been loaded by the current process.

DWORD GetModuleFileNameA(
  HMODULE hModule,
  LPSTR   lpFilename,
  DWORD   nSize
);

LoadLibraryA

Loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded.

HMODULE LoadLibraryA(
  LPCSTR lpLibFileName
);

GetProcAddress

Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).

FARPROC GetProcAddress(
  HMODULE hModule,
  LPCSTR  lpProcName
);

msvcrt.dll

MSVCRT.DLL is the C standard library for the Visual C++ (MSVC) compiler from version 4.2 to 6.0. It provides programs compiled by these versions of MSVC with most of the standard C library functions. These include string manipulation, memory allocation, C-style input/output calls, and others.

kernel32.dll

KERNEL32.DLL exposes to applications most of the Win32 base APIs, such as memory management, input/output (I/O) operations, process and thread creation, and synchronization functions. Many of these are implemented within KERNEL32.DLL by calling corresponding functions in the native API, exposed by NTDLL.DLL.

fopen

Opens a file indicated by filename and returns a file stream associated with that file. mode is used to determine the file access mode.

std::FILE* fopen( const char* filename, const char* mode );

fread

Reads up to count objects into the array buffer from the given input stream stream as if by calling std::fgetc size times for each object, and storing the results, in the order obtained, into the successive positions of buffer, which is reinterpreted as an array of unsigned char. The file position indicator for the stream is advanced by the number of characters read.

std::size_t fread( void* buffer, std::size_t size, std::size_t count, std::FILE* stream );

fseek

Sets the file position indicator for the file stream stream. If the stream is open in binary mode, the new position is exactly offset bytes measured from the beginning of the file if origin is SEEK_SET, from the current file position if origin is SEEK_CUR, and from the end of the file if origin is SEEK_END. Binary streams are not required to support SEEK_END, in particular if additional null bytes are output.

int fseek( std::FILE* stream, long offset, int origin );

fclose

Closes the given file stream. Any unwritten buffered data are flushed to the OS. Any unread buffered data are discarded.

int fclose( std::FILE* stream );

Lets list these out with their pointers for easy reference later:

ebp 0x58   msvcrt.dll
ebp 0xa0   kernel32.dll
ebp 0x1b8  fopen
ebp 0x4c   fread
ebp 0xc    fseek
ebp 0x60   fclose
ebp 0x78   GetModuleFileNameA
ebp 0x7c   rb

Alright, so we have a pretty good guess as to what this shellcode is going to do. Lets examine the rest of it and see if we are right:

    0x0040419e      mov  eax, dword [ebp + 8] ; [0x8:4]=-1 ; 8
    0x004041a1      mov  ecx, dword [eax]
    0x004041a3      mov  dword [ebp - 4], ecx
    0x004041a6      mov  ecx, dword [eax + 4] ; [0x4:4]=-1 ; 4
    0x004041a9      mov  dword [ebp - 0x44], ecx
    0x004041ac      lea  ecx, [ebp - 0x58]
    0x004041af      push ecx
    0x004041b0      call dword [ebp - 4]
    0x004041b3      mov  dword [ebp - 0x3c], eax

We are calling LoadLibraryA after pushing msvcrt.dll to the stck. We are then saving the return to ebp 0x3c. Lets update our reference:

ebp 0x58           msvcrt.dll
ebp 0xa0           kernel32.dll
ebp 0x1b8          fopen
ebp 0x4c           fread
ebp 0xc            fseek
ebp 0x60           fclose
ebp 0x78           GetModuleFileNameA
ebp 0x7c           rb
dword [ebp - 4]    LoadLibraryA
dword [ebp - 0x44] GetProcAddress
dword [ebp - 0x3c] msvcrt.dll module handle
    0x004041b6      lea  edx, [ebp - 0xa0]
    0x004041bc      push edx
    0x004041bd      call dword [ebp - 4]
    0x004041c0      mov  dword [ebp - 0x84], eax

Next we call LoadLibraryA after pushing kernel32.dll to the stack. We are then saving the return to ebp 0x84. Lets update our reference and move on.

ebp 0x58           msvcrt.dll
ebp 0xa0           kernel32.dll
ebp 0x1b8          fopen
ebp 0x4c           fread
ebp 0xc            fseek
ebp 0x60           fclose
ebp 0x78           GetModuleFileNameA
ebp 0x7c           rb
dword [ebp - 4]    LoadLibraryA
dword [ebp - 0x44] GetProcAddress
dword [ebp - 0x3c] msvcrt.dll module handle
dword [ebp - 0x84] kernel32.dll module handle
    0x004041c6      lea  eax, [ebp - 0x78]
    0x004041c9      push eax
    0x004041ca      mov  ecx, dword [ebp - 0x84]
    0x004041d0      push ecx
    0x004041d1      call dword [ebp - 0x44]
    0x004041d4      mov  dword [ebp - 0x10], eax

Next we are pushing GetModuleFileNameA, and then kernel32.dll handle to the stack. We are then calling GetProcAddress(kernel32.dll, GetModuleFileNameA). This returns the address of GetModuleFileNameA and we are saving that in ebp 0x10.

ebp 0x58           msvcrt.dll
ebp 0xa0           kernel32.dll
ebp 0x1b8          fopen
ebp 0x4c           fread
ebp 0xc            fseek
ebp 0x60           fclose
ebp 0x78           GetModuleFileNameA
ebp 0x7c           rb
dword [ebp - 4]    LoadLibraryA
dword [ebp - 0x44] GetProcAddress
dword [ebp - 0x3c] msvcrt.dll module handle
dword [ebp - 0x84] kernel32.dll module handle
dword [ebp - 0x10] GetModuleFileNameA address
    0x004041d7      lea  edx, [ebp - 0x1b8]
    0x004041dd      push edx
    0x004041de      mov  eax, dword [ebp - 0x3c]
    0x004041e1      push eax
    0x004041e2      call dword [ebp - 0x44]
    0x004041e5      mov  dword [ebp - 0x80], eax

Next up it looks like we are pushing fopen, and msvcrt.dll handle to the stack. Then we are calling GetProcAddress on them. The return address for fopen is saved as ebp 0x80.

ebp 0x58           msvcrt.dll
ebp 0xa0           kernel32.dll
ebp 0x1b8          fopen
ebp 0x4c           fread
ebp 0xc            fseek
ebp 0x60           fclose
ebp 0x78           GetModuleFileNameA
ebp 0x7c           rb
dword [ebp - 4]    LoadLibraryA
dword [ebp - 0x44] GetProcAddress
dword [ebp - 0x3c] msvcrt.dll module handle
dword [ebp - 0x84] kernel32.dll module handle
dword [ebp - 0x10] GetModuleFileNameA address
dword [ebp - 0x80] fopen address
    0x004041e8      lea  ecx, [ebp - 0xc]
    0x004041eb      push ecx
    0x004041ec      mov  edx, dword [ebp - 0x3c]
    0x004041ef      push edx
    0x004041f0      call dword [ebp - 0x44]
    0x004041f3      mov  dword [ebp - 0xa4], eax

Here we are getting the address to fseek as ebp 0xa4.

    0x004041f9      lea  eax, [ebp - 0x4c]
    0x004041fc      push eax
    0x004041fd      mov  ecx, dword [ebp - 0x3c]
    0x00404200      push ecx
    0x00404201      call dword [ebp - 0x44]
    0x00404204      mov  dword [ebp - 0x90], eax

Here we are getting the address to fread as ebp 0x90.

    0x0040420a      lea  edx, [ebp - 0x60]
    0x0040420d      push edx
    0x0040420e      mov  eax, dword [ebp - 0x3c]
    0x00404211      push eax
    0x00404212      call dword [ebp - 0x44]
    0x00404215      mov  dword [ebp - 0x64], eax

Here we are getting the address to fclose as ebp 0x64. Lets update these and go on to the next call.

ebp 0x58           msvcrt.dll
ebp 0xa0           kernel32.dll
ebp 0x1b8          fopen
ebp 0x4c           fread
ebp 0xc            fseek
ebp 0x60           fclose
ebp 0x78           GetModuleFileNameA
ebp 0x7c           rb
dword [ebp - 4]    LoadLibraryA
dword [ebp - 0x44] GetProcAddress
dword [ebp - 0x3c] msvcrt.dll module handle
dword [ebp - 0x84] kernel32.dll module handle
dword [ebp - 0x10] GetModuleFileNameA address
dword [ebp - 0x80] fopen address
dword [ebp - 0xa4] fseek address
dword [ebp - 0x90] fread address
dword [ebp - 0x64] fclose address
    0x00404218      push 0x104 ; 260
    0x0040421d      lea  ecx, [ebp - 0x1b0]
    0x00404223      push ecx
    0x00404224      push 0
    0x00404226      call dword [ebp - 0x10]

Here we are calling GetModuleFileNameA(0, ebp 0x1b0, 260)

DWORD GetModuleFileNameA(
  HMODULE hModule,
  LPSTR   lpFilename,
  DWORD   nSize
);

We are sending an hModule of 0:

A handle to the loaded module whose path is being requested. If this parameter is NULL, GetModuleFileName retrieves the path of the executable file of the current process.

lpFilename is listed in the docs as follows

A pointer to a buffer that receives the fully qualified path of the module. If the length of the path is less than the size that the nSize parameter specifies, the function succeeds and the path is returned as a null-terminated string.

So ebp 0x1b0 is the fully qualified path to the running process.

ebp 0x58            msvcrt.dll
ebp 0xa0            kernel32.dll
ebp 0x1b8           fopen
ebp 0x4c            fread
ebp 0xc             fseek
ebp 0x60            fclose
ebp 0x78            GetModuleFileNameA
ebp 0x7c            rb
dword [ebp - 4]     LoadLibraryA
dword [ebp - 0x44]  GetProcAddress
dword [ebp - 0x3c]  msvcrt.dll module handle
dword [ebp - 0x84]  kernel32.dll module handle
dword [ebp - 0x10]  GetModuleFileNameA address
dword [ebp - 0x80]  fopen address
dword [ebp - 0xa4]  fseek address
dword [ebp - 0x90]  fread address
dword [ebp - 0x64]  fclose address
dword [ebp - 0x1b0] current process path
    0x00404229      lea  edx, [ebp - 0x7c]
    0x0040422c      push edx
    0x0040422d      lea  eax, [ebp - 0x1b0]
    0x00404233      push eax
    0x00404234      call dword [ebp - 0x80]
    0x00404237      add  esp, 8
    0x0040423a      mov  dword [ebp - 0x40], eax
    0x0040423d      push 0
    0x0040423f      push 0x4e ; 'N' ; 78
    0x00404241      mov  ecx, dword [ebp - 0x40]
    0x00404244      push ecx
    0x00404245      call dword [ebp - 0xa4]
    0x0040424b      add  esp, 0xc
    0x0040424e      mov  edx, dword [ebp - 0x40]
    0x00404251      push edx
    0x00404252      push 1 ; 1
    0x00404254      push 0x26 ; '&' ; 38
    0x00404256      lea  eax, [ebp - 0x38]
    0x00404259      push eax
    0x0040425a      call dword [ebp - 0x90]
    0x00404260      add  esp, 0x10
    0x00404263      mov  ecx, dword [ebp - 0x40]
    0x00404266      push ecx
    0x00404267      call dword [ebp - 0x64]

Now we are opening the current process to read only, as a binary file. We are then calling fseek(filestream,0x4e,0). Looking at the docs this means we have moved the pointer in the file to offset 0x4e. Next we are calling fread(ebp 0x38, 0x26, 1, filestream). The docs let us know that we are reading out 1 object that is 38 bytes. After this we are closing the filestream.

Lets open the binary up in a hex editor and grab 38 bytes starting at 0x4e:

00000040  0e 1f ba 0e 00 b4 09 cd  21 b8 01 4c cd 21 54 68  |........!..L.!Th|
00000050  69 73 20 70 72 6f 67 72  61 6d 20 63 61 6e 6e 6f  |is program canno|
00000060  74 20 62 65 20 72 75 6e  20 69 6e 20 44 4f 53 20  |t be run in DOS |
00000070  6d 6f 64 65 2e 0d 0d 0a  24 00 00 00 00 00 00 00  |mode....$.......|

We get a string back from the header. “This program cannot be run in DOS mode.”

    0x0040426a      add  esp, 4
    0x0040426d      mov  edx, dword [ebp + 8] ; [0x8:4]=-1 ; 8
    0x00404270      mov  ecx, dword [edx + 0xc] ; [0xc:4]=-1 ; 12
    0x00404273      mov  edi, dword [edx + 8] ; [0x8:4]=-1 ; 8
    0x00404274          .string "z\b1\xd2\x8aD" ; len=7 ;-- "z\b1ҊD":
:   0x0040427b      enter 0x430, 0x17 ; 1072
:   0x0040427f      inc  edx
:   0x00404280      cmp  edx, ecx
`=< 0x00404282      jne  0x404278
    0x00404284      mov  esp, ebp
    0x00404286      pop  ebp
    0x00404287      ret

It looks like we are moving our string to edx and the first 36 bytes to ecx. Something looks broken in this disassembly view, im not sure if I failed when importing it, or if radare2 just wasn’t able to handle it. Lets open Ghidra to take a look at the last few lines:

        0040426d 8b 55 08        MOV        EDX,dword ptr [EBP + 0x8]
        00404270 8b 4a 0c        MOV        ECX,dword ptr [EDX + 0xc]
        00404273 8b 7a 08        MOV        EDI,dword ptr [EDX + 0x8]
        00404276 31 d2           XOR        EDX,EDX

That is more of what I would expect. We are taking the header we read and XORing it with something. There was a stack string at the begining of the binary that I completely ignored. Lets grab it and see if thats what we need.

|           0x00402284      mov byte [var_28h], 0x12 ; 18
|           0x00402288      mov byte [var_27h], 0x24 ; '$' ; 36
|           0x0040228c      mov byte [var_26h], 0x28 ; '(' ; 40
|           0x00402290      mov byte [var_25h], 0x34 ; '4' ; 52
|           0x00402294      mov byte [var_24h], 0x5b ; '[' ; 91
|           0x00402298      mov byte [var_23h], 0x23 ; '#' ; 35
|           0x0040229c      mov byte [var_22h], 0x26 ; '&' ; 38
|           0x004022a0      mov byte [var_21h], 0x20 ; 32
|           0x004022a4      mov byte [var_20h], 0x35 ; '5' ; 53
|           0x004022a8      mov byte [var_1fh], 0x37 ; '7' ; 55
|           0x004022ac      mov byte [var_1eh], 0x4c ; 'L' ; 76
|           0x004022b0      mov byte [var_1dh], 0x28 ; '(' ; 40
|           0x004022b4      mov byte [var_1ch], 0x76 ; 'v' ; 118
|           0x004022b8      mov byte [var_1bh], 0x26 ; '&' ; 38
|           0x004022bc      mov byte [var_1ah], 0x33 ; '3' ; 51
|           0x004022c0      mov byte [var_19h], 0x37 ; '7' ; 55
|           0x004022c4      mov byte [var_18h], 0x3a ; ':' ; 58
|           0x004022c8      mov byte [var_17h], 0x27 ; ''' ; 39
|           0x004022cc      mov byte [var_16h], 0x3d ; '=' ; 61
|           0x004022d0      mov byte [var_15h], 0x6e ; 'n' ; 110
|           0x004022d4      mov byte [var_14h], 0x25 ; '%' ; 37
|           0x004022d8      mov byte [var_13h], 0x48 ; 'H' ; 72
|           0x004022dc      mov byte [var_12h], 0x6f ; 'o' ; 111
|           0x004022e0      mov byte [var_11h], 0x3c ; '<' ; 60
|           0x004022e4      mov byte [var_10h], 0x58 ; 'X' ; 88
|           0x004022e8      mov byte [var_fh], 0x3a ; ':' ; 58
|           0x004022ec      mov byte [var_eh], 0x68 ; 'h' ; 104
|           0x004022f0      mov byte [var_dh], 0x2c ; ',' ; 44
|           0x004022f4      mov byte [var_ch], 0x43 ; 'C' ; 67
|           0x004022f8      mov byte [var_bh], 0x73 ; 's' ; 115
|           0x004022fc      mov byte [var_ah], 0x10 ; 16
|           0x00402300      mov byte [var_9h], 0xe ; 14
|           0x00402304      mov byte [var_8h], 0x10 ; 16
|           0x00402308      mov byte [var_7h], 0x6b ; 'k' ; 107
|           0x0040230c      mov byte [var_6h], 0x10 ; 16
|           0x00402310      mov byte [var_5h], 0x6f ; 'o' ; 111

Now that we have this string lets grab the first 36 out of the header string We saw that ecx was being set to the first 36 bytes of the header string, and our xor key is 36 bytes so that should work out. Lets see if we can get our flag with Python:

In [1]: header = [0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 
   ...:           0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 
   ...:           0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 
   ...:           0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 
   ...:           0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 
   ...:           0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f]

In [2]: xorkey = [0x12, 0x24, 0x28, 0x34, 0x5b, 0x23, 
   ...:           0x26, 0x20, 0x35, 0x37, 0x4c, 0x28, 
   ...:           0x76, 0x26, 0x33, 0x37, 0x3a, 0x27, 
   ...:           0x3d, 0x6e, 0x25, 0x48, 0x6f, 0x3c, 
   ...:           0x58, 0x3a, 0x68, 0x2c, 0x43, 0x73, 
   ...:           0x10, 0x0e, 0x10, 0x6b, 0x10, 0x6f]

In [3]: flag = '' 

In [18]: for i in range(36): 
    ...:     flag = flag + chr(header[i] ^ xorkey[i]) 
    ...:                                                                                      

In [19]: print(flag)                       
FLAG{STORE-EVERYTHING-ON-THE-STACK}

Flag 5 found. There are a few more challenges from MalwareTech, if I end up doing them I will be sure to post about it here. Other than that there are a few other CTF style reversing challenges I may look into. I also think its time to fire up the lab and document some actual malware reversing.