Log in

View Full Version : imports are easy to fix


deroko
October 23rd, 2007, 10:50
Well I read post at openrce posted by blabberer in X-Code thread here at woodmann (http://www.woodmann.com/forum/showthread.php?t=10635) and saw interesting discussion between Rolf Rolles (waz up man :devil and Kris Kaspersky debating which way of generic fixing is better. Kris votes for adding loader, but Rolf likes clean import to have good disasm in IDA. Infact I had this same problem a few months ago, it was easy to locate all APIs and add loader, who cares, app works, well it works and you don't have to analyse it as long as registration scheme is located in the wraper, but what if registration is located in program itself and you have to analyse it? It would be mess and boring task in IDA not seeing real APIs Basically that small discussion was inspiration to write this blog post

Look at this messy fixed import. It looks like, for example, armadillo shuffled IAT or maybe ActiveMARK v6.xx fixed IAT. But take a closer
look where are APIs located:

Code:
00BBC368 >7C80A447 kernel32.QueryPerformanceCounter
00BBC36C >7E450702 USER32.MessageBoxA

kernel32.QueryPerformanceCounter = BBC364
user32.MessageBoxA = BBC368

You might wonder how did I get this to work? Using API loader? No!!!

What I have used here is 1API = 1 image import descriptor concept which I use to fix imports always. Basically this is how it works:

Code:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

According to PE specification OriginalFirstThunk points to API names, but if it is 0 then FirstThunk is pointing to API names, also FirstThunk is used to know where to store API addresses.


So what we shall do to make this concept work?

1. Build OriginalFirstThunk such that it only points to one API name
2. place RVA in FirstThunk where we want API loaded
3. When import table is built simply walk firstthunk and set it to
specific value != 0 as windows loader will deny loading image if
address to which FirstThunk is pointing is 0. Weird?! I didn't find
anything in PE specification about this behaviour

Lets see some code snipets from this library which you may find in src of my GenericUnpacker and import engine at http://deroko.phearless.org

First we define some struct which will hold our imports:

Code:
import_struct struct
is_address dd ? <--- RVA where we want API address to be
writen by windows loader
is_dlllen dd ? <--- len of dll name including terminating 0
is_apilen dd ? <--- len of API string including terminating 0
or 4 if API is imported by ordinal
is_dllname db 256 dup(0) <--- dllname (ASCII)
is_apiname db 256 dup(0) <--- API name or ordinal | 0x80000000
import_struct ends

Now it's very simply to fix IAT:

By number of this structs we know how many IMAGE_IMPORT_DESCRIPTORs we will have, but
also tnhx to lengths stored in structs we can estimate size of newly built IAT including
APIs aswell.

OriginalFirstThunk will always point to API name or ordinal and also will take one more
DWORD for 0 to tell windows loader that there are no more APIs to be loaded.


EBX = memory used for IMAGE_IMPORT_DESCRIPTOR
EDI = pointer where we write dll, api names or ordinals and is calculated
as EBX + (number_of_import_structs + 1) * sizeof (IMAGE_IMPORT_DESCRIPTOR)

Code:
sub edx, size import_struct

__cycle_main_loop: add edx, size import_struct
cmp [edx.is_address], 0
je __done_building

;copy dll name
mov esi, edi
sub esi, new_iat_mem
add esi, iat_sec_rva

mov [ebx.id_name], esi
lea esi, [edx.is_dllname]
mov ecx, [edx.is_dlllen]
rep movsb

;write original first thunk
mov esi, eax
sub esi, new_iat_mem
add esi, iat_sec_rva

mov [ebx.id_originalfirstthunk], esi

mov esi, dword ptr[edx.is_apiname]
test esi, 80000000h
jz __api_has_name
mov [eax], esi
jmp __firstthunk

__api_has_name:
mov esi, edi
sub esi, new_iat_mem
add esi, iat_sec_rva
mov [eax], esi

lea esi, [edx.is_apiname]
add edi, 2
mov ecx, [edx.is_apilen]
rep movsb

__firstthunk:
mov esi, [edx.is_address]
mov [ebx.id_firstthunk], esi

add ebx, size import_directory
add eax, 8
jmp __cycle_main_loop

When this small loop is done, all we have to do is to walk all first_thunks and set them
to != 0 so win loader can actually load this import table.

With this concept you don't have to write sorting algos to get nice and clean IAT, who
cares where are APIs located. In my, aspr2.3 dumper for example, I scan original code
for APIs that are not stolen, then I go for stolen APIs and write them into array
and I don't care if I have something like this:

MessageBoxA
GetModuleHandleA
RegOpenKeyExA

etc...

After this is done, then I go for APIs located in poly oep, or other stolen procedures
which are located in markers. At this point IAT looks like nightmare and importrec nor
any other public known engine can fix this IAT... But 1API = 1IDD concept fixes this
without a problem!!!

Think about it when writing unpacker and protection developers think too when
you try to make "hard" IAT to stop importrec, you will lose one way or another
Remember that not many people are using it lately

blabberer
October 23rd, 2007, 12:19
heya nice entry there sometimes i think i should reaally rebuild some fscked up import table im atleast happy my little post inspired you to write a blog

Maximus
October 23rd, 2007, 12:33
not very sure, but it might happen that an error arise when the loader tries to make address-0 memory writeable? Since the IAT can be written even if it's read-only.

----
mmh... yep, looks like it is

deroko
October 23rd, 2007, 12:48
Actually it's strange there is code like this somewhere in ntdll loader:

Code:

FirstThunk = import->FirstThunk + imagebase;
if (*FirstThunk == 0)
bad_IAT();


that's why I simply set it to != 0 value later on when IAT is added to dump

Maximus
October 23rd, 2007, 12:51
aaah, i found the code for unprotectable memory only then!
will dig more

dELTA
October 24th, 2007, 10:22
Nice trick deroko, thanks for sharing as always.