View on GitHub

poxyblog

poxyran's blog

Unpacking Malware Series - Fixing the (tiny) IAT redirection in Dharma ransomware

Very long time ago, like two years, I worte about unpacking Dharma ransomware. At that time, I put on my TODO list to write about the little IAT redirection the unpacked sample was implementing. Nothing hard nor complex but I wanted to write about it, just for fun.

Tools

In case you want to follow this analysis, you’ll need the following tools:

Studying the IAT redirection

Once you get the first layer removed, if you look at the import table functions, you’ll see that there are just a few imported functions:

Tha’s because most addresses of the important functions used by the ransomware are calculated dynamically. That’s not new, you probably know the LoadLibrary/GetProcAddress combination, right?. If you trace a little bit the code, at some point, you’ll find something like this code construction:

PUSH FUNCTION_PARAM
CALL TRAMPOLINE

TRAMPOLINE:
MOV EAX, DWORD PTR:DS[FUNC_ADDR]
JMP FIXED_JMP_EAX

FIXED_JMP_EAX:
JMP EAX

As you can see, the parameter for the function is pushed onto the stack and then a trampoline code is called instead of the real function. This little stub of code is in charge of getting the address of the imported function from a fixed address in the .data section and move it to the EAX register. Then, a JMP EAX instruction is executed to invoke the real function code. By using these trampolines, the ransomware author adds some complexity to the code in order to avoid its analysis. Specially, the static analysis code because as we don’t have the symbols of the functions there, we don’t know for sure where the function calls go. Dynamic analysis get also a little bit more complicated because this way you won’t see the reference to the function in the disassembly window. This case if very easy to fix but keep in mind that it could be quite more complex.

The real IAT is built on a fixed region inside the .data section. The function in charge of doing this is 406440:

00406440  /$  55            PUSH EBP
00406441  |.  8BEC          MOV EBP,ESP
00406443  |.  83EC 0C       SUB ESP,0C
00406446  |.  8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]
00406449  |.  8945 F4       MOV DWORD PTR SS:[EBP-C],EAX
0040644C  |.  C745 F8 00000>MOV DWORD PTR SS:[EBP-8],0
00406453  |.  C745 FC 00000>MOV DWORD PTR SS:[EBP-4],0
0040645A  |>  8B4D F4       /MOV ECX,DWORD PTR SS:[EBP-C]
0040645D  |.  0FBE11        |MOVSX EDX,BYTE PTR DS:[ECX]
00406460  |.  85D2          |TEST EDX,EDX
00406462  |.  0F84 A7000000 |JE Dumped.0040650F
00406468  |.  8B45 F4       |MOV EAX,DWORD PTR SS:[EBP-C]
0040646B  |.  50            |PUSH EAX                                ; /FileName
0040646C  |.  FF15 0CA04000 |CALL DWORD PTR DS:[<&KERNEL32.LoadLibra>; \LoadLibraryA
00406472  |.  8945 FC       |MOV DWORD PTR SS:[EBP-4],EAX
00406475  |.  837D FC 00    |CMP DWORD PTR SS:[EBP-4],0
00406479  |.  0F84 89000000 |JE Dumped.00406508
0040647F  |>  8B4D F4       |/MOV ECX,DWORD PTR SS:[EBP-C]
00406482  |.  0FBE11        ||MOVSX EDX,BYTE PTR DS:[ECX]
00406485  |.  85D2          ||TEST EDX,EDX
00406487  |.  74 0B         ||JE SHORT Dumped.00406494
00406489  |.  8B45 F4       ||MOV EAX,DWORD PTR SS:[EBP-C]
0040648C  |.  83C0 01       ||ADD EAX,1
0040648F  |.  8945 F4       ||MOV DWORD PTR SS:[EBP-C],EAX
00406492  |.^ EB EB         |\JMP SHORT Dumped.0040647F
00406494  |>  8B4D F4       |MOV ECX,DWORD PTR SS:[EBP-C]
00406497  |.  83C1 01       |ADD ECX,1
0040649A  |.  894D F4       |MOV DWORD PTR SS:[EBP-C],ECX
0040649D  |>  8B55 F4       |/MOV EDX,DWORD PTR SS:[EBP-C]
004064A0  |.  0FBE02        ||MOVSX EAX,BYTE PTR DS:[EDX]
004064A3  |.  85C0          ||TEST EAX,EAX
004064A5  |.  74 41         ||JE SHORT Dumped.004064E8
004064A7  |.  8B4D F4       ||MOV ECX,DWORD PTR SS:[EBP-C]
004064AA  |.  51            ||PUSH ECX                               ; /ProcNameOrOrdinal = "SetHandleInformation"
004064AB  |.  8B55 FC       ||MOV EDX,DWORD PTR SS:[EBP-4]           ; |
004064AE  |.  52            ||PUSH EDX                               ; |hModule
004064AF  |.  FF15 08A04000 ||CALL DWORD PTR DS:[<&KERNEL32.GetProcA>; \GetProcAddress
004064B5  |.  8B4D F8       ||MOV ECX,DWORD PTR SS:[EBP-8]
004064B8  |.  89048D 303C42>||MOV DWORD PTR DS:[ECX*4+423C30],EAX
004064BF  |.  8B55 F8       ||MOV EDX,DWORD PTR SS:[EBP-8]
004064C2  |.  83C2 01       ||ADD EDX,1
004064C5  |.  8955 F8       ||MOV DWORD PTR SS:[EBP-8],EDX
004064C8  |>  8B45 F4       ||/MOV EAX,DWORD PTR SS:[EBP-C]
004064CB  |.  0FBE08        |||MOVSX ECX,BYTE PTR DS:[EAX]
004064CE  |.  85C9          |||TEST ECX,ECX
004064D0  |.  74 0B         |||JE SHORT Dumped.004064DD
004064D2  |.  8B55 F4       |||MOV EDX,DWORD PTR SS:[EBP-C]
004064D5  |.  83C2 01       |||ADD EDX,1
004064D8  |.  8955 F4       |||MOV DWORD PTR SS:[EBP-C],EDX
004064DB  |.^ EB EB         ||\JMP SHORT Dumped.004064C8
004064DD  |>  8B45 F4       ||MOV EAX,DWORD PTR SS:[EBP-C]
004064E0  |.  83C0 01       ||ADD EAX,1
004064E3  |.  8945 F4       ||MOV DWORD PTR SS:[EBP-C],EAX
004064E6  |.^ EB B5         |\JMP SHORT Dumped.0040649D
004064E8  |>  8B4D F4       |/MOV ECX,DWORD PTR SS:[EBP-C]
004064EB  |.  0FBE11        ||MOVSX EDX,BYTE PTR DS:[ECX]
004064EE  |.  85D2          ||TEST EDX,EDX
004064F0  |.  74 0B         ||JE SHORT Dumped.004064FD
004064F2  |.  8B45 F4       ||MOV EAX,DWORD PTR SS:[EBP-C]
004064F5  |.  83C0 01       ||ADD EAX,1
004064F8  |.  8945 F4       ||MOV DWORD PTR SS:[EBP-C],EAX
004064FB  |.^ EB EB         |\JMP SHORT Dumped.004064E8
004064FD  |>  8B4D F4       |MOV ECX,DWORD PTR SS:[EBP-C]
00406500  |.  83C1 01       |ADD ECX,1
00406503  |.  894D F4       |MOV DWORD PTR SS:[EBP-C],ECX
00406506  |.  EB 02         |JMP SHORT Dumped.0040650A
00406508  |>  EB 05         |JMP SHORT Dumped.0040650F
0040650A  |>^ E9 4BFFFFFF   \JMP Dumped.0040645A
0040650F  |>  8BE5          MOV ESP,EBP
00406511  |.  5D            POP EBP
00406512  \.  C3            RETN

This function just iterates over a list of strings stored at the beginning of the .data section with the libraries and function names to be loaded. Then, it calls LoadLibrary and GetProcAddress, respectively, in order to find the addresses of all the functions used by the ransomware. Finally, it just fills the new IAT region with the values returned by GetProcAddress.

The following picture shows the strings of libraries/function names:

The following picture shows the newly created IAT:

The address of the function that builds the new IAT can be found by simply finding one of the trampolines (by tracing the code) and putting a breakpoint on-write (hardware or memory breakpoint) on the address dereferenced in the MOV EAX, DWORD PTR:DS[FUNC_ADDR] instruction.

In the next section, you’ll see how to fix this trampolines.

Fixing the IAT redirection with Ollyscript

The following picture shows the place where all the trampolines reside:

As you can see, it starts with a JMP EAX and it follows with all the different trampolines. Each of them, correspond to a single Windows API call. Also, you can see that there is a kind of pattern for all this trampolines, right?. For the distracted reader, here’s the pattern I’m talking about:

00406530   $  A1 303C4200   MOV EAX,DWORD PTR DS:[423C30]
00406535   .^ E9 E6FFFFFF   JMP Dumped.00406520
0040653A      CC            INT3
0040653B      CC            INT3
0040653C      CC            INT3
0040653D      CC            INT3
0040653E      CC            INT3
0040653F      CC            INT3

We have a MOV, a JMP and six INT3 instructions.

What would happen if we directly let the ransomware fill the new IAT and then replace all the trampolines with the real function call or jump?. That’s exactly what I did.

I’ll put the code of my script, written in Ollyscript language and then I’ll explain it:

var currentaddr

log "Starting rebuilding" 

doit:
find eip, #A1????????E9????????#
cmp $RESULT, 0
je endscript
mov currentaddr, $RESULT
cmp currentaddr, 406980
ja endscript

mov eax, [currentaddr+1]
mov [currentaddr], #FF25#
mov [currentaddr+2], eax
mov [currentaddr+6], #90909090#

add currentaddr, 0A
mov eip, currentaddr

jmp doit

endscript:
ret

Ollyscript language is very similar to ASM but also include some auxiliary functions to make our life easy. You can program an Ollyscript program as you do it in MASM or NASM. Very easy to learn. I think the code is very descriptive but let’s see its logic a little bit.

The first thing I do is to use the find function in order to search for a specific byte pattern starting from the value stored in EIP. As we already know where all the trampolines are, I just do a NEW ORIGIN HERE in Ollydbg to set the start of the script near to the JMP EAX.

The byte pattern I’m looking for corresponds to the JMP and CALL instructions. I’m using some wildcards in order to ignore the bytes corresponding to instruction parameters.

The next thing is to check if the pattern was found, if that is the case then the variable $RESULT holds the address where the pattern was found, otherwise it is 0:

cmp $RESULT, 0
je endscript

If the pattern was found, I save the address returned and then I check if I reach the end of the trampoline’s region, which is 406980:

mov currentaddr, $RESULT
cmp currentaddr, 406980
ja endscript

If we are still on the trampoline’s region, I build a JMP DWORD PTR DS:[FUNC_1_ADDR]. Basically, I do the JMP instruction to take the address of the previous MOV instruction:

mov eax, [currentaddr+1]
mov [currentaddr], #FF25#
mov [currentaddr+2], eax
mov [currentaddr+6], #90909090#

In other words, I replace this pattern:

00406530   $  A1 303C4200   MOV EAX,DWORD PTR DS:[423C30]
00406535   .^ E9 E6FFFFFF   JMP Dumped.00406520
0040653A      CC            INT3
0040653B      CC            INT3
0040653C      CC            INT3
0040653D      CC            INT3
0040653E      CC            INT3
0040653F      CC            INT3

with this other pattern:

00406530   $  FF25 303C4200 JMP DWORD PTR DS:[423C30]
00406536      90            NOP
00406537      90            NOP
00406538      90            NOP
00406539      90            NOP

Finally, I just add the amount of bytes needed to reach the next trampoline to the current address, set EIP register with that address and start all over again.

At the end, once the new IAT is filled by the ransomware and the script is executed, you’ll get something like this:

Once the script is finished, you can just use Ollydump or any other similar tool to dump the resulting binary and IMPRec to create a new IAT in the dumped binary. Keep in mind that this new IAT won’t include the initial imported functions such as LoadLibraryA and GetProcAddress but as they are not so many (just 4 or 5 functions) you can include them manually.

You can find the .ose script here.