Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 4046885

Who's Online

We have 5 guests online
Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Home arrow Articles - Programming arrow Assembly Programming arrow Locating API function offsets in memory
Locating API function offsets in memory
User Rating: / 9
PoorBest 
Written by Rheingold   
Side Story
Converting virtual offsets to raw offsets and vice versa

Converting raw offsets (the one in a file you see in a HexEditor) to virtual offsets (the one you see in a debugger) is very useful if you work with the PE header. This short essay will explain how to convert them. For this purpose you need to know some values you can read from the PE header of Win32 PE files (EXE/DLL). You need to know the ImageBase of the file and you have to perform some calculations with the section definitions of the PE header. Below you see an example of a PE header from the beginning of the file (where it is actually a MZ header until offset 0x80) until the section definitions end (offset 0x23F). The example is taken from my notepad.exe.

Read More >>



In the last issue of the Programming Journal I wrote about this prog which adds a password dialog to EXE files and how to add functions and DLLs to the import table of PE files. This essay deals with a completely different way to use all API functions you need in your packer/cryptor/whatever with no need to change the import table in any way. I'll show you how to search through the memory in order to locate Kernel32.dll and then parse the export section to get the API functions you want to use.

Download Source here  

 

When an EXE file is executed, it's not (very) much more than a sub-call of Kernel32.dll (mostly of CreateProcessA). This means that at the start [esp] must contain the return offset to somewhere inside the Kernel32.dll and that is our starting point. Considered that we know a valid offset inside Kernel32.dll there must be a way to locate the beginning of this DLL and then we can easily parse for the PE header, the Exports section and all APIs we desire to use. And obviously there is one. Since Kernel32.dll is mapped to a certain offset which is aligned to 1000h we can parse back and look at all offsets which are aligned to 1000h and check for a MZ header.

 

        mov     edx, [esp]                      ;; Offset of function in Kernel32.dll
xor dx, dx ;; Align to 1000h
@Label:
sub edx, 1000h ;; search next page
cmp word ptr [edx], 'ZM' ;; MZ header?
jne @Label ;; If not, then keep searching

At the end of this loop EDX will point to the MZ header and we can go for the PE header/Export table. We don't need any check if the offsets we check are valid/initialized or even SEH because we can't miss the MZ header and there are no invalid pages inside a (properly created) DLL. The next is locating the Export table. As the regular reader of Programming Journal knows in the meantime, there is a pointer to the PE header 3Ch bytes after the MZ header starts and from there we can get the starting offset of the Export table (PE header + 78h). I don't present the sourcecode for the simple parser I describe here, but already the "sophisticated" one I wrote. Sophisticated means here that it is a procedure which takes a pointer to a MZ header and a certain checksum as parameters and locates API functions. I'll come back to that checksum later. The parameters must be passed in EBX and EDX. Furthermore I need to declare 4 DWORD variables with obvious names: DLLStart, ExportStart and PEStart. In my code snippet they are located in the .data section but this is not needed. You can put them close to your code as long as you remember to enable write access on that section. Let's get into the code now.

 

;; EDX must hold the offset of the MZ header, EBX the checksum
FindAPI:
push    edx                             ;; Preserve the value in EDX
mov     DLLStart, edx                   ;; keep the offset for later use
mov     ecx, [edx+3Ch]                  ;; PE header
add     ecx, edx                        ;; Normalize offset
mov     PEStart, ecx                    ;; Keep PEStart for later use
mov     ecx, [ecx+78h]                  ;; Pointer to Export Table
add     ecx, edx                        ;; Normalize offset
mov     ExportStart, ecx                ;; Keep pointer to Export table for later use
mov     edx, [ecx+18h]                  ;; Number of Exports
mov     ecx, [ecx+20h]                  ;; Pointer To Pointer to Exports Names
add     ecx, DLLStart                   ;; Normalize offset
With those few lines we have a pointer to API name table of the Exports. Now we just have to parse all names and check if this is the one we need. This is where the checksum appears. An average API name is about 10 bytes long, if not longer, but if we use an unique signature to identify this API we can decrease the needed size quite fine. You already guessed it, that's what the checksum is for. The function contains a very simple algorithm which produces nearly unique (at least for our purposes) results and only a DWORD value is needed to store it. When the function now tries to locate the API function we need then it calculates a checksum over all API names and compares it with the one that was passed to the function in EBX. The inner loop calculates and compares the checksum, the outer loop loops through all API names and keeps counting how many API names were already checked (EDX) which is needed later.
@Loop:
xor     edi, edi                        ;; EDI will hold the checksum
mov     esi, [ecx]                      ;; Pointer To Export #n
add     esi, DLLStart                   ;; Normalize offset
xor     eax, eax
push    ecx
@Loop2:
lodsb                                   ;; Load char of API name into esi
mov     ecx, eax
add     edi, eax                        ;; checksum of API name - Part 1
rol     edi, cl                         ;; checksum of API name - Part 2
test    eax, eax                        ;; if byte was not the 0-byte...
jne     @Loop2                          ;; ...keep looping
pop     ecx
cmp     edi, ebx                        ;; Checksum found ?
je      @Found                          ;; API found
add     ecx, 4                          ;; Next pointer to API Name
dec     edx                             ;; Still APIs to go?
jne     @Loop                           ;; If so, jump
pop     edx                             ;; Clean the stack
xor     eax, eax                        ;; return 0 means "API not found"
ret
@Found:

Everything's pretty simple so far and it will not get harder. In fact, we are pretty close to the end. We just have to get back to the beginning of the export directory and look at the offset table for offset of the API we need.

 

        mov     eax, DLLStart                   ;; Here we need...
mov     ecx, ExportStart                ;; the saved values again
mov     ebx, [ecx+18h]                  ;; Number of Exports
mov     ecx, [ecx+24h]                  ;; Pointer To Ordinals
add     ecx, eax                        ;; Normalize offset
sub     ebx, edx                        ;; EBX = Number of Ordinals - API we want
shl     ebx, 1                          ;; The ordinal table is made of WORD values - we have to multiply with 2
add     ecx, ebx                        ;; Pointer to the ordinal of the API
movzx   ebx, word ptr [ecx]             ;; Ordinal of our API
mov     ecx, ExportStart                ;; And the Export Start again
mov     ecx, [ecx+1Ch]                  ;; Offset Table
add     ecx, eax                        ;; Normalize offset
shl     ebx, 2                          ;; Expand to DWORD size
add     ecx, ebx                        ;; Point to offset where our API begins
add     eax, [ecx]                      ;; Add that offset to the beginning of the DLL
pop     edx                             ;; Clean the stack
ret

Now we have a working code snippet to locate all the APIs we want to use, even those which are not located in Kernel32.dll. To be sure that the DLLs you need are located in the EXE file's address space you can load them manually using LoadLibrary and as this API is located inside kernel32.dll which can be parsed easily using the return address of the EXE file everything's fine and you can use all APIs you want nevertheless which DLL and no matter if this DLL is loaded when the EXE is started. To be more precise: It obviously only works in DLL files which export by name but I wrote the code only for handling the most important system DLLs and they export by Name. Nevertheless for DLLs which export only by ordinal you can easily make changes to my code and search for the ordinals instead of a checksum.

Here's an example how to display a messagebox without using any directly imported API functions in Win9X. There is another problem for WinNT/Win2K (at least proven for Win2K and guessed for WinNT) which has nothing to do with my code, but with the fact that those operating systems don't run EXE files that have no import table. For compatibiliy reasons I use InitCommonControls in the example. Note that the example file's code section must have the write-enabled flag because I defined my variables in this section instead of the data section which is write-enabled by default.

 

.386
.model flat, stdcall
option casemap:none
include \masm32\include\comctl32.inc ;; for NT/2K compatibility
includelib \masm32\lib\comctl32.lib ;; for NT/2K compatibility
.code
DLLStart    dd 0                        ;; used in the FindAPI proc
KernelStart dd 0                        ;; used to store offset of Kernel32.dll
ExportStart dd 0                        ;; used in the FindAPI proc
PEStart     dd 0                        ;; used in the FindAPI proc
GetProcA    dd 0                        ;; offset of GetProcAddress
LoadLib     dd 0                        ;; offset of LoadLibraryA
user32      db "User32.dll",0           ;; DLL we need for the messagebox
text        db "Test, Test, 1,2,3",0    ;; MessageBox text
start:
invoke InitCommonControls               ;; for NT/2K compatibility
mov     edx, [esp]                      ;; Offset of function in Kernel32.dll
xor     dx, dx                          ;; Align to 1000h
@Label:
sub     edx, 1000h                      ;; search next page
cmp     word ptr [edx], 'ZM'            ;; MZ header?
jne     @Label                          ;; If not, then keep searching
mov     KernelStart, edx                ;; Save Kernel32.dll offset
mov     ebx, 0A216A185h                 ;; Checksum of LoadLibraryA
call    FindAPI                         ;; Get offset of LoadLibraryA
mov     LoadLib, eax                    ;; Save offset
push    OFFSET user32                   ;; "User32.dll"
call    LoadLib                         ;; LoadLibraryA
mov     ebx, 9A9C4525h                  ;; Checksum of MessageBoxA
mov     edx, eax                        ;; Offset of User32.dll MZ header
call    FindAPI                         ;; Get offset of MessageBoxA
push edx                                ;; Store for calling FreeLibrary
push    0
push    OFFSET text
push    OFFSET text
push    0
call    eax                             ;; Show MessageBox
mov     ebx, 0B36E72B3h                 ;; CheckSum of FreeLibrary
mov     edx, KernelStart                ;; API is inside Kernel32.dll
call    FindAPI                         ;; Get API
call    eax                             ;; Free User32.dll
ret                                     ;; exit program
;; EDX must hold the offset of the MZ header, EBX the checksum
FindAPI:
... ;; See code above
ret
end start
The messagebox is displayed and everything seems to work fine. The only question left is how to get the checksum of the API functions quickly? I wrote a small C++ program where you enter a string and the checksum is calculated.
#include <iostream>
#include <stdlib>
#include <conio>

void main()
{
char name[80];
unsigned int checksum = 0;
cout << "Enter the API name: ";
cin  >> name;
for (int i=0;i<strlen(name);i++)
{
checksum += name[i];
checksum = _lrotl(checksum, name[i]); // Rotate left
}
cout << "Checksum: " << hex << checksum;
getch();
}

That's all so far. As you probably saw is this code only useful for programs which attach to EXE files and execute first. When you are already somewhere in the EXE file you probably don't know if [ESP] still holds the return offset of the program or if you are already in a subcall or if parameters or temporary variables are pushed onto to the stack. Here it's still better to add the API/DLLs you need right to the Import Table as I explained last time.

This e-mail address is being protected from spam bots, you need JavaScript enabled to view it