MalwareTech Challenge - strings3.exe
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!