Bridge them all

Rating: 2 votes, 2.00 average.
Today I’m going to tell you something about the last malware I checked (MD5 0C17E03F41289E47EEB5D0F3F1F48C9C).

The exe file imports few functions only, but the malware calls a lot of APIs. The author uses a special trick to call an API function, he creates a sort of bridge between the first instruction and the rest of the code of the function itself. The first instruction is executed directly from the stack, then a jmp instruction (the bridge) will lead you to the second instruction (and the rest of the code) of the function. I think I’ve already seen the trick somewhere but unfortunately I don’t remember where… maybe a specific packer or just something similar, I don’t know. If you have seen this trick before just drop me a comment, thx!

The malware is packed, but inside the unpacked file there’s something strange:

The exe is full of calls to NULL value. I’m pretty sure that it’s not an error occorred during the unpacking process; there’s something at the beginning of the exe able to fix the addresses. I started my analysis from the first lines of the unpacked file.

The Import Table is really small but spying inside the strings window I found a lot of common API strings. There’s a big list of functions, and they are divided into some groups; one group containing kernel32 functions, another one with user32 and so on. Working a little with some cross references I got the point I was looking for. It’s time to describe how the malware changes all the “call NULL” instructions.

First of all the malware gains access to kernel32 base address:

4013D5    mov  edi, large fs:30h  ; PEB
4013DC    mov  edi, [edi+0Ch]     ; PEB+0x00c   Ldr : Ptr32 _PEB_LDR_DATA
4013DF    mov  edi, [edi+0Ch]     ; +0x00c InLoadOrderModuleList : _LIST_ENTRY
4013E2    jmp  short loc_401404

4013E4 check_current_module:
4013E4    mov  eax, edi    ; eax points to current _LDR_MODULE structure
4013E6    add  eax, 2Ch
4013E9    push [ebp+arg_0]        ; unicode "kernel32.dll"
4013EC    push dword ptr [eax+4]  ; current module name inside InLoadOrderModuleList
4013EF    call Compare_UNICODE_Strings
4013F4    or   eax, eax
4013F6    jnz  short strings_are_not_equal
4013F8    mov  eax, edi           ; LDR_MODULE of the module I was looking for
4013FA    mov  eax, [eax+18h]     ; He gets the BaseAddress!!!
4013FD    pop  edi
4013FE    leave
4013FF    retn 4

401402 strings_are_not_equal:
401402    mov  edi, [edi]        ; jump to next module structure
401404 loc_401404:
401404    cmp  dword ptr [edi+18h], 0          ; Is BaseAddress 0?
401408    jnz  short check_current_module
40140A    xor  eax, eax
40140C    pop  edi
40140D    leave
40140E    retn 4
Quite common way, but quite uncommon inside a malware… at least from my not so experienced perspective. Anyway, once it has the right BaseAddress tha malware starts bridge-ing all the necessary functions.

It’s everything inside this call. It takes four parameters, we can ignore the first one pushed into the stack. What about the others?
- eax represents the BaseAddress of a module, in this case ntdll
- 404040 points to a sequence of strings, in this case the first one is “RtlZeroMemory”
- 406000 represents an address inside the malware
The procedure is called each time the malware needs to bridge a group of functions, all of them belong to a specific module. In this specific case it works with ntdll’s functions. The list of the functions starts from 0×404040 address:

The routine contains a loop running until all the functions inside the current group are not all bridged. Here is how the first function is bridged (the sub routine starts at 0×401411):
1. It takes VirtualAlloc starting address via Kernel32’s ExportTable
2. It gets the number of bytes of the first instruction of VirtualAlloc. On my XP machine VirtualAlloc starts with a two byte length instruction “MOV EDI, EDI”
3. It subtracts 7 from ESP value. 7 is obtained adding 5 (a fixed value) to the number of bytes of VirtualAlloc first instruction (2+5=7)
4. It copies the first two bytes of VirtualAlloc’s code inside the word pointed by the new stack pointer value
5. If the length of the first instruction is one byte only the malware checks for a possible active breakpoint comparing the byte with 0xCC value; quite useless check at this point…
6. It sets the byte pointed by ESP+2 to 0xE9
7. It fills the final 4 of 7 bytes obtaining:

You have the first instruction of VirtualAlloc at 0×12FDF5, then the jump instruction will lead you directly at the second instruction of VirtualAlloc. Now you understand why it decreases ESP value by 7, two bytes for the first instruction and 5 for the jump. Starting from 0×12FDFC you have the old untouched stack.
8. It gets the length of the first instruction of the function to bridge. In this specific case the name of the function is RtlZeroMemory and the length is 1
9. It adds 5 to the obtained value
10. It calls VirtualAlloc passing trought the stack. It calls 0×12FDF5, and it allocs 6 bytes. 6 is the value that comes from point #9 (5+1=6)
11. It copies the first instruction of RtlZeroMemory inside the allocated memory space
12. An anti breakpoint check occours this time because the first instruction is one byte only
13. It sets the second byte inside the allocated memory space to 0xE9 (again, a jmp instruction)
14. It fills the rest of the bytes:

Here is the bridge! It’s like what happened to VirtualAlloc, the allocated memory space contains the first instruction of RtlZeroMemory and a jump to the rest of the code
15. It restores the original stack pointer value simply adding 7 to the current ESP value
16. It returns the starting address of the allocated memory space (in this case 0×320000)

To sum up, the routine bridges the function and returns a memory address. The address will be saved starting from 0×406000 which is another parameter passed to the routine. If you don’t remind all the parameters you can take a look some lines above.
So, starting from 0×406000 you’ll have a series of dwords, each one containing a pointer to a memory allocated space; these are the values used to replace all the NULL calls. Now I finally know why after the unpacking process I still had a series of “call NULL” instructions.

Is it really necessary to bridge everything?
Yes if the author wants to fool an automatic analysis. I don’t know if the trick works or not, but it’s the only reasonable thing I can think of.
On the other hand, he can’t stop a complete human analysis because once you know how it works it’s pretty easy to convert all the “call NULL” instructions into the right ones; a simple idc script will solve the puzzle.

It’s a nice piece of malware to analyse btw, it has some interesting routines inside!

Submit "Bridge them all" to Digg Submit "Bridge them all" to Submit "Bridge them all" to StumbleUpon Submit "Bridge them all" to Google