MalwareTech Challenge - strings3.exe

Jun 22, 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 third challenge strings3.exe: ‘https://www.malwaretech.com/strings3’

Lets open this binary in cutter and analyze it with radare2. Once open lets navigate to the entry fucntion:

Already this challenge looks a bit more challenging than the previous ones. Lets take a look at the assembly:

/ (fcn) entry0 179
|   entry0 ();
|           ; var LPSTR lpBuffer @ ebp-0x4a0
|           ; var void *s @ ebp-0x49f
|           ; var LPCSTR lpText @ ebp-0x9c
|           ; var int32_t var_98h @ ebp-0x98
|           ; var HRSRC var_8h @ ebp-0x8
|           ; var UINT uID @ ebp-0x4
|           0x00402290      push ebp
|           0x00402291      mov ebp, esp
|           0x00402293      sub esp, 0x4a0
|           0x00402299      lea ecx, [var_98h]
|           0x0040229f      call sym.plaintext3.exe___0MD5__QAE_XZ
|           0x004022a4      mov byte [lpBuffer], 0
|           0x004022ab      push 0x3ff ; 1023 ; size_t n
|           0x004022b0      push 0 ; int c
|           0x004022b2      lea eax, [s]
|           0x004022b8      push eax ; void *s
|           0x004022b9      call sub.ntdll.dll_memset ; void *memset(void *s, int c, size_t n)
|           0x004022be      add esp, 0xc
|           0x004022c1      mov dword [uID], 0
|           0x004022c8      push 6 ; 6 ; LPCSTR lpType
|           0x004022ca      push str.rc.rc ; 0x403028 ; "rc.rc" ; LPCSTR lpName
|           0x004022cf      push 0 ; HMODULE hModule
|           0x004022d1      call dword [sym.imp.KERNEL32.dll_FindResourceA] ; 0x403000 ; HRSRC FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType)
|           0x004022d7      mov dword [var_8h], eax
|           0x004022da      mov eax, 1
|           0x004022df      shl eax, 8
|           0x004022e2      xor edx, edx
|           0x004022e4      inc edx
|           0x004022e5      shl edx, 4
|           0x004022e8      or eax, edx
|           0x004022ea      mov dword [uID], eax
|           0x004022ed      push 0x3ff ; 1023 ; int cchBufferMax
|           0x004022f2      lea ecx, [lpBuffer]
|           0x004022f8      push ecx ; LPSTR lpBuffer
|           0x004022f9      mov edx, dword [uID]
|           0x004022fc      push edx ; UINT uID
|           0x004022fd      push 0 ; HINSTANCE hInstance
|           0x004022ff      call dword [sym.imp.USER32.dll_LoadStringA] ; 0x40300c ; "*1" ; int LoadStringA(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int cchBufferMax)
|           0x00402305      lea eax, [lpBuffer]
|           0x0040230b      push eax
|           0x0040230c      lea ecx, [var_98h]
|           0x00402312      call sym.plaintext3.exe__digestString_MD5__QAEPADPAD_Z
|           0x00402317      mov dword [lpText], eax
|           0x0040231d      push 0x30 ; '0' ; 48 ; UINT uType
|           0x0040231f      push str.We_ve_been_compromised ; 0x403030 ; "We've been compromised!" ; LPCSTR lpCaption
|           0x00402324      mov ecx, dword [lpText]
|           0x0040232a      push ecx ; LPCSTR lpText
|           0x0040232b      push 0 ; HWND hWnd
|           0x0040232d      call dword [sym.imp.USER32.dll_MessageBoxA] ; 0x403010 ; int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
|           0x00402333      push 0 ; UINT uExitCode
|           0x00402335      call dword [sym.imp.KERNEL32.dll_ExitProcess] ; 0x403004 ; void ExitProcess(UINT uExitCode)
|           0x0040233b      xor eax, eax
|           0x0040233d      mov esp, ebp
|           0x0040233f      pop ebp
\           0x00402340      ret 0x10

Here we see several variables being referenced then stack being initialized. After this we are loding in the addresses of the functions we will be calling. Looking through the next couple of lines we come across:

|           0x004022c1      mov dword [uID], 0
|           0x004022c8      push 6 ; 6 ; LPCSTR lpType
|           0x004022ca      push str.rc.rc ; 0x403028 ; "rc.rc" ; LPCSTR lpName
|           0x004022cf      push 0 ; HMODULE hModule
|           0x004022d1      call dword [sym.imp.KERNEL32.dll_FindResourceA] ; 0x403000 ; HRSRC FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType)
|           0x004022d7      mov dword [var_8h], eax

So it looks like we are setting up a pointer to store the output of a function call. The function we are calling is FindResourceA. Now based on the name we can assume what this does but since i have no idea what I am doing lets do a quick google search. The first result was a ‘Microsoft Docs page’.

Determines the location of a resource with the specified type and name in the specified module. So our end result will be an address to a resource stored in var_8h.

Further down we come across another function call:

|           0x004022ea      mov dword [uID], eax
|           0x004022ed      push 0x3ff ; 1023 ; int cchBufferMax
|           0x004022f2      lea ecx, [lpBuffer]
|           0x004022f8      push ecx ; LPSTR lpBuffer
|           0x004022f9      mov edx, dword [uID]
|           0x004022fc      push edx ; UINT uID
|           0x004022fd      push 0 ; HINSTANCE hInstance
|           0x004022ff      call dword [sym.imp.USER32.dll_LoadStringA] ; 0x40300c ; "*1" ; int LoadStringA(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int cchBufferMax)
|           0x00402305      lea eax, [lpBuffer]

Lets figure out what ‘LoadStringA’ does exactly.

Loads a string resource from the executable file associated with a specified module and either copies the string into a buffer with a terminating null character or returns a read-only pointer to the string resource itself.

The rest of the entry function looks like what we have seen in the past challenges.

So it looks like our flag is in lpBuffer. Now we just need to figure out how to take a look at that. Looking at the docs the second argument given to LoadStringA is The identifier of the string to be loaded. which in our code is here 0x004022ea mov dword [uID], eax. So we need to know what resource to look at then we could use the string identifier to find our flag. Looking at just the assembly here there are a few lines here that stand out to me. eax is set to a value that is then moved to uID. Lets do some quick math.

|          0x004022da      mov eax, 1                 ; eax = 1
|          0x004022df      shl eax, 8                 ; eax = 1 << 8 = 256 = 0x100
|          0x004022e2      xor edx, edx               ; clears out edx for use
|          0x004022e4      inc edx                    ; edx = 1
|          0x004022e5      shl edx, 4                 ; edx = 1 << 4 = 16 = 0x10
|          0x004022e8      or eax, edx                ; eax = 256 | 16 = 272 = 0x110

So it looks like the string identifier is 272 (0x110). Lets see if we can find it.

Popping open the resources it doesnt look like we have a 272, or rather radare2 wasnt able to parse all the resource strings properly:

Lets open this up and see what ghidra shows us. Once pulling up the entry function i started editing the function signature and some of the variable names to reflect what we already know:

Well look at that, ghidra even populated the flag in the disassembly view, lets pretend we didnt see that and find it ourselves.

The decompiled code gives us a much better idea of what is happening:

int main(void)

{
  char *lpText;
  LPSTR local_4a4;
  MD5 local_9c [144];
  HRSRC local_c;
  UINT flag_resource_id;
  
  MD5(local_9c);
  local_4a4._0_1_ = 0;
  memset((void *)((int)&local_4a4 + 1),0,0x3ff);
  flag_resource_id = 0;
  local_c = FindResourceA((HMODULE)0x0,"rc.rc",(LPCSTR)0x6);
  flag_resource_id = 0x110;
  LoadStringA((HINSTANCE)0x0,0x110,(LPSTR)&local_4a4,0x3ff);
  lpText = digestString(local_9c,(char *)&local_4a4);
  MessageBoxA((HWND)0x0,lpText,"We\'ve been compromised!",0x30);
  ExitProcess(0);
  return 0;
}

So now we have the string identifier flag_resource_id = 0x110; which matches what we computed from the assembly code above. Lets check out our resources to see what we can find out. Clicking on .rsrc in the program tree to open the resources shows much more promise:

Scrolling through the resources we see that they ghidra has correctly parsed out our resource strings, and a bit of scrolling later we come across 272. Flag acquired!

This was definitely the toughest challenge thusfar. My methodolgy could use some optimizing, hopefully that comes as I become more familiar with things. More to come soon!