ZaiRoN
October 15th, 2007, 22:38
This post is a little bit long, for a better reading you can download the pdf version here: http://www.box.net/shared/static/nexbjpy0p6.pdf
Roaming around Symantec web pages I stumbled on a review of a trojan named Ascesso. The malware does a lot of things that are described inside the “Technical details” section. I decided to give it a try just because it’s interesting to read what a malware does, but it’s much more funny when you play with it! In this blog entry I won’t talk about what the malware does, but I’ll write something about the way I used to obtain a readable dead list inside Ida. One of the next blog entry could be focused on the analysis of the malware, don’t know.
The file I’m going to analyze is named asc3550.sys, md5=BBEB49A32417226CABED3E104A9C94B5.
The malware is crypted, I think it’s a home made protection. If you load the file in Ida you are not able to see too much, almost all the code has been crypted. To view the uncrypted malware you have two options: you can run the driver dumping the memory image using a ring0 dumper (i.e. softice+icedump), or you can use Ida trying to convert the initial output into something really closer to the original driver. Generally, I like to work on a simple dump of the packed/crypted file, but with a driver I prefer to work on a perfect file. With a simple dump of the image you’ll have to deal with instructions like:
Code:
It’s hard to say what’s going on when you have unknown addresses in front of you. I think you’ll prefer to look at something like:
Code:
This is what I’m going to do.
Initial decryption
When you load the file in Ida the decryption routine is the only visible code:
Code:
These are the starting instructions. Reading through various forums I had the impression that most of the people have some problems trying to anaylize this kind of snippets. It’s pretty obvious what the snippet does, it’s a simple decryption routine consisting in a sub operation. The main problem is: which part of code will be decrypted? Everything depends on value stored inside edx register, which is obtained by the instructions inside the call 400242:
Code:
How to know the exact value pointed by [esp+8]? Taking in mind that the file I’m analyzing is a .sys file you can get the value inside the stack with a simple deducting reasoning.
The first driver instruction is the one at 400240, but who brings me there? I mean, there should be an instruction which is executed before the one at 400240. The instruction is a call and it’s somewhere inside IopLoadDriver function (in ntoskrnl.exe):
Code:
[edi+2C] points to DriverInit, 400240 in this particular case. Look at the last parameter (pDriverObject), it’s really important. You have to concentrate on the stack only, when you are at the first driver instruction you’ll have something like:
Code:
Here’s how the stack looks like when you are inside the “call sub_400242″, at address 400242: Code:
Now it’s pretty easy to retrieve the starting value of edx: Code:
Just a little more step and you’ll have the value we are searching for. Look a the definition of the DRIVER_OBJECT structure, taken from ntddk.h:
Code:
DRIVER_OBJECT+0Ch is DriverStart, the memory address that points to the first byte of the driver. I don’t have the possibility to know the exact value stored inside DriverStart field, but for a static analysis you can suppose that the address is the ImageBase: 0×400000. It comes out that the edx value we want is: 0×400000+0×9540 = 0×409540
It’s time to decrypt the bytes using this simple idc script: Code:
I inserted the Message function just because I wanted to see the first non-decrypted address: 0×409A9C; you can remove the function, if you prefer.
Ok, now that the decryption is complete I have to look at the decrypted code. The last instruction of the initial decryption routine will bring me directly at a fresh decrypted instruction, which is the next instruction to be executed? Code:
The decrypted code is a small routine, all the other bytes of the file are still crypted; maybe I have to deal with some more layers. The decrypted code contains an initializations part, some calls end a final jmp instruction.
Initialization
It’s pretty easy to understand the initialization code. I won’t attach any snippet,it’s only a sequence of mov/add/lea instructions used to retrieve/store some special values that are used later.
At the end of the initialization part there’s a piece of code which is used to move 0×44 bytes: Code:
Do you know what 400240 is? It’s the entry point of the file. It’s something to remember for the next decryption script.
Sections decryption
The first interesting piece of code is located inside a call at 0×4095C1, before studying the call I prefer to take a look at the parameters: Code:
I can’t get any clue from the parameters, I can’t say nothing else without spying the code. After a little investigation over the previous instructions I discover what the parameters are:
- ebx = DriverStart
- eax = 0×409A38
- 1
- dword ptr [ebp+401BECh] = 0×9540
- dword ptr [ebp+401BF8h] = 0×8FA0
Just wait some minutes and I’ll tell you everything about these parameters.
As you can see I renamed the call, the name suggests the task performed by the call: all the sections are decrypted. The call contains a loop which is executed until all the sections are not decrypted; it starts from the last section. At each round, it firstly gets the pointer to the IMAGE_SECTION_HEADER of the current section (he needs to know where the section starts/ends) and then decrypt it. The decryption algo is really simple: Code:
Another simple decryption. This time a xor instruction between the crypted dword and a fixed value, it’s 0×003CF5B5 and it’s located at address 409A38.
Look at the comments I inserted in the code, don’t you have any doubt about it? The decryption routine resides in .reloc section, is it possible to decrypt all the bytes in .reloc section? Well, no because if you decrypt every single byte you’ll decrypt the decryption routine generating a bsod. That’s why the call needs the first two parameters (0×9540 and 0×8FA0). The values are taken from .reloc IMAGE_SECTION_HEADER. One of them, 0×8FA0, is the Raw Offset of of the section. To calculate the number of bytes to decrypt the malware uses 0×9540 substracting 0×8FA0 from it, obtaining 0×5A0. These are the bytes to decrypt. The rest of the bytes belong to the decryption routine I’m checking and they are untouchables.
Exploring the decryption routine I found a strange piece of code I’m not able to totally understand: Code:
Three things:
1. it checks for ‘rsrc’. This is a common name generally used for resource type section, if the file contains this particular section the decryption won’t be executed over that section. The driver doesn’t have a .rsrc section for sure, why the author needs to add this check?
2. do you remember the 5 parameters passed to the function? One of them is the value 1. At 40995A it checks that value. The condition is satisfied and the conditional jump occours. This function is called one time only, which is the purpose of this check?
3. it really needs two parameters (0×9540, 0×8FA0) and a sub instruction? It only needs 0×5A0 and a different implementation algorithm.
I don’t have an answer for the first two things, but I think the author ripped this code. Why? It’s a behaviour of some packers, they leave resource section untouched. Moreover, there’s another check for [ebp+Fixed_Value_1]: Code:
Call_4099A7 is almost identical to Call Decrypt_current_section, they only differ by a pushad, nothing more. Hm, who adds an useless bugged function in his code? I might be wrong but ‘rip’ is the first thing I can think of, what do you think?
Anyway, here is the script I used to decrypt the entire file:
Code:
The decryption ends here. Now…
Relocation: to fix or not to fix, that is the question
When the file is totally decrypted the loader has to fix some things: relocations, some pe fields and something else. The function able to fix relocation is located at 40962F, essentially the algorithm is:
http://zairon.files.wordpress.com/2007/10/reloc_procedure.JPG
This diagram should be enough for you to understand the algo. Taking in mind I’m going to reconstruct a fully readable file under IDA, is it necessary to change the relocated addresses? The answer is no; as you can see from the picture above relocation is not applied when DriverStart is equal to ImageBase, I’ll do the same.
Fix PE fields
After relocation there’s a little function, it changes some fields in the PE header. Code:
Some minor changes to PE header. It changes rva and size relative to IMAGE_DIRECTORY_ENTRY_EXPORT and IMAGE_DIRECTORY_ENTRY_BASERELOC.
Again, there’s a strange thing in this piece of code. It zero-es PE+78 and PE+7C while they are already zero…
Change FirstThunk field
The last part of the loader, the function starts at 4096B7. The algorithm fixes every single FirstThunk element overwriting the old value with the starting memory address of the relative function. Code:
The external loop scans every single IMAGE_IMPORT_DESCRIPTOR structure, and the internal function changes every single FirstThunk.
To understand the algo I’ll show you a little example explaining how to fix one element, the first function (KfAcquireSpinLock) of the first dll (Hal.dll). This is the IMAGE_IMPORT_DESCRIPTOR: Code:
FirstThunk array starts at rva 45C0 and it contains 2 elements: Code:
The first two dwords are relative to the two Hal.dll’s imported functions, and the 3° dword represents the null terminator dword. I’m interested in the first function, _IMAGE_IMPORT_BY_NAME is located at rva 0×8A34: Code:
At this point the malware uses MmGetSystemRoutineAddress to retrieve the function’s address, that’s why MmGetSystemRoutineAddress is one of the functions imported by the original encrypted file. Once it has the address of the function it stores the memory address at 4045C0.
There’s a little exception in this system regarding the function from ndis module, the malware doesn’t use MmGet.. function but it takes the addresses directly from the running module. Why? I’m sure you can easily find out why by yourself.
Now, to obtain a readable dead list I have to write a simple idc script able to convert:
Code:
to the more readable instruction:
Code:
Here is the script: Code:
That’s all, now you can explore the malware.
Roaming around Symantec web pages I stumbled on a review of a trojan named Ascesso. The malware does a lot of things that are described inside the “Technical details” section. I decided to give it a try just because it’s interesting to read what a malware does, but it’s much more funny when you play with it! In this blog entry I won’t talk about what the malware does, but I’ll write something about the way I used to obtain a readable dead list inside Ida. One of the next blog entry could be focused on the analysis of the malware, don’t know.
The file I’m going to analyze is named asc3550.sys, md5=BBEB49A32417226CABED3E104A9C94B5.
The malware is crypted, I think it’s a home made protection. If you load the file in Ida you are not able to see too much, almost all the code has been crypted. To view the uncrypted malware you have two options: you can run the driver dumping the memory image using a ring0 dumper (i.e. softice+icedump), or you can use Ida trying to convert the initial output into something really closer to the original driver. Generally, I like to work on a simple dump of the packed/crypted file, but with a driver I prefer to work on a perfect file. With a simple dump of the image you’ll have to deal with instructions like:
Code:
Code:
.text:0040381B call dword ptr ds:0F77B3664h
.text:004028F9 mov dword ptr [edi], 0F77AF000h
.text:0040294E movzx eax, byte ptr ds:0F77B36D2h
It’s hard to say what’s going on when you have unknown addresses in front of you. I think you’ll prefer to look at something like:
Code:
Code:
.text:0040381B call ds:_ExAllocatePoolWithTag
.text:004028F9 mov dword ptr [edi], offset __ImageBase
.text:0040294E movzx eax, ds:byte_4046D2
This is what I’m going to do.
Initial decryption
When you load the file in Ida the decryption routine is the only visible code:
Code:
Code:
00400240 000 jmp short loc_400257 ; Entry point
00400242 sub_400242 proc near
00400242
00400242 arg_4= dword ptr 8
00400242
00400242 000 lea edx, [esp+arg_4]
00400246 000 mov edx, [edx]
00400248 000 mov edx, [edx+0Ch]
0040024B 000 add edx, 9540h
00400251 000 mov eax, 9AEDh
00400256 000 retn
00400256 sub_400242 endp
00400257 loc_400257:
00400257 000 call sub_400242
0040025C 000 pusha
0040025D 020 push 55Ch
00400262 024 pop ecx
00400263 Decrypt_1:
00400263 020 mov eax, [edx] ; Current dword to decrypt
00400265 020 sub eax, 0FA598390h ; Decryption: sub operation
0040026A 020 mov [edx], eax ; Save the decrypted dword
0040026C 020 lea edx, [edx+4] ; Next dword to decrypt
0040026F 020 sub ecx, 4
00400272 020 test ecx, ecx
00400274 020 jnz short Decrypt_1
00400276 020 popa
00400277 000 mov ecx, 9B29h
0040027C 000 add edx, 2
0040027F 000 add edx, 6
00400282 000 jmp edx
These are the starting instructions. Reading through various forums I had the impression that most of the people have some problems trying to anaylize this kind of snippets. It’s pretty obvious what the snippet does, it’s a simple decryption routine consisting in a sub operation. The main problem is: which part of code will be decrypted? Everything depends on value stored inside edx register, which is obtained by the instructions inside the call 400242:
Code:
Code:
00400242 lea edx, [esp+8] ; stack value
00400246 mov edx, [edx] ; edx is an address !?!
00400248 mov edx, [edx+0Ch] ; edx points to a structure !?!
0040024B add edx, 9540h ; add operation
How to know the exact value pointed by [esp+8]? Taking in mind that the file I’m analyzing is a .sys file you can get the value inside the stack with a simple deducting reasoning.
The first driver instruction is the one at 400240, but who brings me there? I mean, there should be an instruction which is executed before the one at 400240. The instruction is a call and it’s somewhere inside IopLoadDriver function (in ntoskrnl.exe):
Code:
Code:
PAGE:004DCFE2 020 push [ebp+68h+PreviousMode] ; PUNICODE_STRING RegistryPath
PAGE:004DCFE5 024 push edi ; PDRIVER_OBJECT pDriverObject
PAGE:004DCFE6 028 call dword ptr [edi+2Ch] ; Call DriverEntry
[edi+2C] points to DriverInit, 400240 in this particular case. Look at the last parameter (pDriverObject), it’s really important. You have to concentrate on the stack only, when you are at the first driver instruction you’ll have something like:
Code:
Code:
esp+00h: IopLoadDriver_return_address
esp+04h: pDriverObject
esp+08h: RegistryPath
esp+0Ch: …
Here’s how the stack looks like when you are inside the “call sub_400242″, at address 400242: Code:
Code:
esp+00h: call_400242_return_address
esp+04h: IopLoadDriver_return_address
esp+08h: pDriverObject
esp+0Ch: RegistryPath
esp+10h: …
Now it’s pretty easy to retrieve the starting value of edx: Code:
Code:
00400242 lea edx, [esp+8] ; edx = pDriverObject
00400246 mov edx, [edx] ; edx points to the first byte of DRIVER_OBJECT structure
00400248 mov edx, [edx+0Ch] ; edx = DRIVER_OBJECT+0Ch
0040024B add edx, 9540h ; edx = edx + 0×9540
Just a little more step and you’ll have the value we are searching for. Look a the definition of the DRIVER_OBJECT structure, taken from ntddk.h:
Code:
Code:
typedef struct _DRIVER_OBJECT {
CSHORT Type; // +0×00
CSHORT Size; // +0×02
PDEVICE_OBJECT DeviceObject; // +0×04
ULONG Flags; // +0×08
PVOID DriverStart; // +0×0C
ULONG DriverSize; // +0×10
PVOID DriverSection; // +0×14
PDRIVER_EXTENSION DriverExtension; // +0×18
UNICODE_STRING DriverName; // +0×1C
PUNICODE_STRING HardwareDatabase; // +0×24
PFAST_IO_DISPATCH FastIoDispatch; // +0×28
PDRIVER_INITIALIZE DriverInit; // +0×2C
PDRIVER_STARTIO DriverStartIo; // +0×30
PDRIVER_UNLOAD DriverUnload; // +0×34
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // +0×38
} DRIVER_OBJECT;
DRIVER_OBJECT+0Ch is DriverStart, the memory address that points to the first byte of the driver. I don’t have the possibility to know the exact value stored inside DriverStart field, but for a static analysis you can suppose that the address is the ImageBase: 0×400000. It comes out that the edx value we want is: 0×400000+0×9540 = 0×409540
It’s time to decrypt the bytes using this simple idc script: Code:
Code:
#include
static main()
{
auto CurrentAddress, i;
CurrentAddress = 0×409540;
for(i=0×55C;i>0;i=i-4)
{
PatchDword(CurrentAddress, (Dword(CurrentAddress) - 0xFA598390));
CurrentAddress = CurrentAddress + 4;
}
Message(”\nDecryption done, last address: %X”, CurrentAddress);
}
I inserted the Message function just because I wanted to see the first non-decrypted address: 0×409A9C; you can remove the function, if you prefer.
Ok, now that the decryption is complete I have to look at the decrypted code. The last instruction of the initial decryption routine will bring me directly at a fresh decrypted instruction, which is the next instruction to be executed? Code:
Code:
00400276 popa ; edx = 0×409540
00400277 mov ecx, 9B29h ; ecx = 0×9B29
0040027C add edx, 2 ; edx = 0×409540 + 2 = 0×409542
0040027F add edx, 6 ; edx = 0×409542 + 6 = 0×409548
00400282 jmp edx ; jmp 0×409548
The decrypted code is a small routine, all the other bytes of the file are still crypted; maybe I have to deal with some more layers. The decrypted code contains an initializations part, some calls end a final jmp instruction.
Initialization
It’s pretty easy to understand the initialization code. I won’t attach any snippet,it’s only a sequence of mov/add/lea instructions used to retrieve/store some special values that are used later.
At the end of the initialization part there’s a piece of code which is used to move 0×44 bytes: Code:
Code:
.reloc:00409592 add edi, [ebp+401BF4h] ; edi = 400240
.reloc:00409598 lea esi, [ebp+401D52h] ; esi = 409A48
.reloc:0040959E mov ecx, 44h
.reloc:004095A3 rep movsb
Do you know what 400240 is? It’s the entry point of the file. It’s something to remember for the next decryption script.
Sections decryption
The first interesting piece of code is located inside a call at 0×4095C1, before studying the call I prefer to take a look at the parameters: Code:
Code:
.reloc:004095B1 push dword ptr [ebp+401BF8h] ; ebp+401BF8 and ebp+401BEC were setted
.reloc:004095B7 push dword ptr [ebp+401BECh] ; up in the initialization part
.reloc:004095BD push 1
.reloc:004095BF push eax
.reloc:004095C0 push ebx
.reloc:004095C1 call Decrypt_First_5_Sections
I can’t get any clue from the parameters, I can’t say nothing else without spying the code. After a little investigation over the previous instructions I discover what the parameters are:
- ebx = DriverStart
- eax = 0×409A38
- 1
- dword ptr [ebp+401BECh] = 0×9540
- dword ptr [ebp+401BF8h] = 0×8FA0
Just wait some minutes and I’ll tell you everything about these parameters.
As you can see I renamed the call, the name suggests the task performed by the call: all the sections are decrypted. The call contains a loop which is executed until all the sections are not decrypted; it starts from the last section. At each round, it firstly gets the pointer to the IMAGE_SECTION_HEADER of the current section (he needs to know where the section starts/ends) and then decrypt it. The decryption algo is really simple: Code:
Code:
004099F8 Decrypt_Section_Bytes: ; esi points to the beginning of the current section
004099F8 lodsd ; Get the current dword
004099F9 xor eax, ebx ; Decryption consists of a xor operation
004099FB stosd ; Store the decrypted dword
004099FC sub ecx, 4
004099FF test ecx, ecx ; Decrypt every single byte of the section
00409A01 jnz short Decrypt_Section_Bytes
Another simple decryption. This time a xor instruction between the crypted dword and a fixed value, it’s 0×003CF5B5 and it’s located at address 409A38.
Look at the comments I inserted in the code, don’t you have any doubt about it? The decryption routine resides in .reloc section, is it possible to decrypt all the bytes in .reloc section? Well, no because if you decrypt every single byte you’ll decrypt the decryption routine generating a bsod. That’s why the call needs the first two parameters (0×9540 and 0×8FA0). The values are taken from .reloc IMAGE_SECTION_HEADER. One of them, 0×8FA0, is the Raw Offset of of the section. To calculate the number of bytes to decrypt the malware uses 0×9540 substracting 0×8FA0 from it, obtaining 0×5A0. These are the bytes to decrypt. The rest of the bytes belong to the decryption routine I’m checking and they are untouchables.
Exploring the decryption routine I found a strange piece of code I’m not able to totally understand: Code:
Code:
0040994C lea ebx, [eax] ; ebx -> IMAGE_SECTION_HEADER current section
0040994E inc ebx ; ebx -> 2° byte of the section’s name
0040994F cmp dword ptr [ebx], ‘crsr’
00409955 jz short Skip_Decrypt_Section
00409957 mov ebx, [eax+0Ch] ; ebx = current_section.SizeOfRawData
0040995A cmp [ebp+Fixed_Value_1], 1 ; [ebp+Fixed_Value_1] is the pushed value: 1
0040995E jz short loc_409963 ; Jump everytime
00409960 mov ebx, [eax+14h] ; ebx = current_section.PointerToRelocations
00409963 loc_409963:
00409963 mov ecx, [eax+10h] ; ecx = current_section.PointerToRawData
00409966 test ecx, ecx
…
00409978 sub eax, ebx ; eax = 0×9540 – 0×8FA0 = 0×5A0
Three things:
1. it checks for ‘rsrc’. This is a common name generally used for resource type section, if the file contains this particular section the decryption won’t be executed over that section. The driver doesn’t have a .rsrc section for sure, why the author needs to add this check?
2. do you remember the 5 parameters passed to the function? One of them is the value 1. At 40995A it checks that value. The condition is satisfied and the conditional jump occours. This function is called one time only, which is the purpose of this check?
3. it really needs two parameters (0×9540, 0×8FA0) and a sub instruction? It only needs 0×5A0 and a different implementation algorithm.
I don’t have an answer for the first two things, but I think the author ripped this code. Why? It’s a behaviour of some packers, they leave resource section untouched. Moreover, there’s another check for [ebp+Fixed_Value_1]: Code:
Code:
0040997F cmp [ebp+Fixed_Value_1], 1
00409983 jz short loc_409991 ; Jump…
00409985 push [ebp+Address_409A38]
00409988 push ecx
00409989 push ebx
0040998A call sub_4099A7 ; Pretty similar to Decrypt_current_section
0040998F jmp short Skip_Decrypt_Section
00409991 loc_409991:
00409991 push [ebp+Address_409A38]
00409994 push ecx
00409995 push ebx
00409996 call Decrypt_current_section
Call_4099A7 is almost identical to Call Decrypt_current_section, they only differ by a pushad, nothing more. Hm, who adds an useless bugged function in his code? I might be wrong but ‘rip’ is the first thing I can think of, what do you think?
Anyway, here is the script I used to decrypt the entire file:
Code:
Code:
#include
static main()
{
auto CurrentAddress;
// I need to copy some bytes from 0×409A48 to 0×400240, do you
// remember the last piece of code in the initialization?
CurrentAddress = 0×409A48;
while (CurrentAddress < 0×409A8C)
{
PatchDword(CurrentAddress-0×9808, Dword(CurrentAddress));
CurrentAddress = CurrentAddress + 4;
}
// Now I can decrypt the code
CurrentAddress = 0×400240;
while (CurrentAddress < 0×409540)
{
PatchDword(CurrentAddress, (Dword(CurrentAddress) ^ 0×003CF5B5));
CurrentAddress = CurrentAddress + 4;
}
Message(”\nDecryption complete!”;
}
The decryption ends here. Now…
Relocation: to fix or not to fix, that is the question
When the file is totally decrypted the loader has to fix some things: relocations, some pe fields and something else. The function able to fix relocation is located at 40962F, essentially the algorithm is:
http://zairon.files.wordpress.com/2007/10/reloc_procedure.JPG
This diagram should be enough for you to understand the algo. Taking in mind I’m going to reconstruct a fully readable file under IDA, is it necessary to change the relocated addresses? The answer is no; as you can see from the picture above relocation is not applied when DriverStart is equal to ImageBase, I’ll do the same.
Fix PE fields
After relocation there’s a little function, it changes some fields in the PE header. Code:
Code:
004095F9 Fix_Some_PE_Fields proc near
004095F9 pusha
004095FA mov esi, [ebp+401BF4h] ; esi = DriverStart
00409600 add esi, [esi+3Ch] ; DriverStart+3C = file offset of the PE header
00409603 mov eax, [ebp+401C04h] ; eax = 0
00409609 mov [esi+78h], eax ; PE+78 = IMAGE_DIRECTORY_ENTRY_EXPORT
0040960C mov eax, [ebp+401C08h]
00409612 mov [esi+7Ch], eax ; PE+7C = 0
00409615 mov eax, [ebp+401BE0h] ; eax = 0×8FA0
0040961B mov [esi+0A0h], eax ; PE+A0 = IMAGE_DIRECTORY_ENTRY_BASERELOC
00409621 mov eax, [ebp+401BE4h]
00409627 mov [esi+0A4h], eax ; PE+0xA4 = 0×308
0040962D popa
0040962E retn
0040962E Fix_Some_PE_Fields endp
Some minor changes to PE header. It changes rva and size relative to IMAGE_DIRECTORY_ENTRY_EXPORT and IMAGE_DIRECTORY_ENTRY_BASERELOC.
Again, there’s a strange thing in this piece of code. It zero-es PE+78 and PE+7C while they are already zero…
Change FirstThunk field
The last part of the loader, the function starts at 4096B7. The algorithm fixes every single FirstThunk element overwriting the old value with the starting memory address of the relative function. Code:
Code:
004096C4 Check_Current__IMAGE_IMPORT_DESCRIPTOR: ; esi -> current _IMAGE_IMPORT_DESCRIPTOR
004096C4 mov eax, [esi+0Ch] ; eax = _IMAGE_IMPORT_DESCRIPTOR.Name
004096C7 test eax, eax
004096C9 jz short loc_4096D5
004096CB call Fix_Current_FirstThunk_Array
004096D0 add esi, 14h ; Jump on the next _IMAGE_IMPORT_DESCRIPTOR
004096D3 jmp short Check_Current__IMAGE_IMPORT_DESCRIPTOR
The external loop scans every single IMAGE_IMPORT_DESCRIPTOR structure, and the internal function changes every single FirstThunk.
To understand the algo I’ll show you a little example explaining how to fix one element, the first function (KfAcquireSpinLock) of the first dll (Hal.dll). This is the IMAGE_IMPORT_DESCRIPTOR: Code:
Code:
OriginalFirstThunk: 24 89 00 00
TimeDateStamp: 00 00 00 00
ForwarderChain: 00 00 00 00
Name: 34 8D 00 00
FirstThunk: C0 45 00 00
FirstThunk array starts at rva 45C0 and it contains 2 elements: Code:
Code:
4045C0: 34 8A 00 00 CC 8C 00 00 00 00 00 00
The first two dwords are relative to the two Hal.dll’s imported functions, and the 3° dword represents the null terminator dword. I’m interested in the first function, _IMAGE_IMPORT_BY_NAME is located at rva 0×8A34: Code:
Code:
INIT:00408A34 dw 6
INIT:00408A36 aKfacquirespinlock_0 db ‘KfAcquireSpinLock’,0
At this point the malware uses MmGetSystemRoutineAddress to retrieve the function’s address, that’s why MmGetSystemRoutineAddress is one of the functions imported by the original encrypted file. Once it has the address of the function it stores the memory address at 4045C0.
There’s a little exception in this system regarding the function from ndis module, the malware doesn’t use MmGet.. function but it takes the addresses directly from the running module. Why? I’m sure you can easily find out why by yourself.
Now, to obtain a readable dead list I have to write a simple idc script able to convert:
Code:
Code:
.text:0040048D call ds:dword_4045C0
to the more readable instruction:
Code:
Code:
.text:0040048D call ds:_KfAcquireSpinLock
Here is the script: Code:
Code:
#include
static main()
{
auto CurrentAddress, _dword;
CurrentAddress = 0×4045C0;
while (CurrentAddress < 0×4046CC)
{
// Do I need to fix this dword?
if (Byte(CurrentAddress) != 0×00)
{
// Get the pointer to the name of the API, patch and rename the address
_dword = Dword(CurrentAddress)+0×400002;
PatchDword(CurrentAddress, _dword);
MakeNameEx(CurrentAddress, “_”+GetString(_dword, -1, ASCSTR_C), SN_AUTO);
}
CurrentAddress = CurrentAddress + 4;
}
Message(”\nGame over…”;
}
That’s all, now you can explore the malware.