Making a quick post on some obfuscation techniques I've seen. This will also be a multi part post, but I won't number them because who knows when it would end :P
first, the malware I got this from imports only a couple of seemingly harmless api, one of them being GetCommandLine. So we dereference it's address into eax and call a routine to get the base address for kernel32.dll like so:
- mov eax, ds:GetCommandLineA ; cpy the addr of GetCmdLine into eax
- push eax ; push addr of GetCommandLine
- call GetStartAddrFor_Kernel32 ; emulate GetProcAddress
So now we have address of GetCommandLine in kernel32.dll so we take that address and zero out the last 3 nibbles by doing an and with 0xFFFFF000h
GetStartAddrFor_Kernel32;
- mov ebp, esp
- sub esp, 8
- mov eax, [ebp+addrGetCmdLine] ; cpy GetCommandLine's address to eax
- xor ecx, ecx
- ; zero the last 3 nibbles of the address, its now 0xXXXXX000
- and eax, 0FFFFF000h
- and ecx, 0Fh
- mov [ebp+addrGetCmdLine], eax ; replace with the new address
Then we enter a loop, which takes the address, dereferences a word from the address into edx, and compares it with 0x5A4Dh, which is 'MZ'
- loc_401106: ; CODE XREF: GetStartAddrFor_Kernel32+5Aj
- mov edx, 1
- test edx, edx
- jz short loc_40114C
- mov eax, [ebp+addrGetCmdLine]
- mov [ebp+var_8], eax ; cpy the address to a tmp variable
- mov ecx, [ebp+var_8]
- movzx edx, word ptr [ecx] ; dereference a word from that address into edx
- cmp edx, 5A4Dh ; does edx == 'MZ'?
- jnz short loc_40113F
If we found 'MZ', next check for 'PE' by adding 0x3c to the address, which should point to the PE header structure
- mov eax, [ebp+var_8] ; if 'MZ' then set EAX
- mov ecx, [ebp+var_8] ; and ECX to be the start address for kernel32
- add ecx, [eax+3Ch] ; add 3ch to ecx, it now points to the PE header struct
- mov [ebp+var_4], ecx ; cpy PE struct addr to a variable
- mov edx, [ebp+var_4] ; and to edx as well
- ; make sure we're at the right place, 45h 50h == 'PE'
- cmp dword ptr [edx], 4550h
- jnz short loc_40113F ; if we're at the right spot
- mov eax, [ebp+var_8] ; cpy start addr of kernel32 into eax again
- jmp short loc_40114C ; and exit
If we didn't find 'MZ' then subtract 0x1000h from the address and loop until we find it.
- loc_40113F: ; CODE XREF: GetStartAddrFor_Kernel32+31j
- mov eax, [ebp+addrGetCmdLine]
- sub eax, 1000h ; subtract 4KB from eax
- mov [ebp+addrGetCmdLine], eax
- jmp short loc_401106
Once found, return
- loc_40114C: ; CODE XREF: GetStartAddrFor_Kernel32+1Dj
- mov esp, ebp
- pop ebp
- retn
Then when we return, do some checking for the OS, on a WinXP box GetCommandLine's address points to a mov, while a Win7 box points to a jump
- add esp, 4 ; stack adjust
- mov kernel32addr, eax ; cpy addr of Kernel32 to a variable
- mov ecx, ds:GetCommandLineA ; cpy GetCmdLine's addr to ecx
- movzx edx, byte ptr [ecx] ; deref a byte to edx
- cmp edx, 0A1h ; if its a mov [WinXP], jump
- jz short loc_40126A ; cpy kernel32 addr to edx
- mov eax, ds:GetCommandLineA
- movzx ecx, byte ptr [eax] ; deref a byte of what's there to ecx
- cmp ecx, 0EBh ; if its a jmp [win7] then jump
- jz short loc_40126A ; cpy kernel32 addr to edx
So, a quick wrap-up of what the above really did:
We got the address to an exported API from kernel32.dll, then we using that address we resolved the base address of kernel32. Once we have that, we check to see what OS we're running on. In a later post I'll show how this piece walks through the PE file to find the exports directory, and starts resolving address for other API by walking through it.
darel