Blog

Useful tidbits related to software development, that I think might be of use or interest to everyone else (or to me when I forget what I did!)

Finding the address of GetProcAddress

June 29, 2005

When working within the confines of a compiled PE file (.exe) you often want to call external functions stored in DLLs. If the DLL/function was imported when the exe was compiled then it will be referenced in the imports table; DLLs/functions in the imports table will be loaded by the Windows loader when the PE is executed, you can then use the IAT (import address table) to get the address to call these functions. However, most of the time the DLL and/or function you want to use was not originally imported by the PE file and will therefore not have an entry in the IAT. You can load a DLL manually by calling LoadLibrary and GetProcAddress in your assembly code, passing in the name of the DLL and the name of the function you wish to load respectively. This method relies on the exe at least having the two imports: Kernel32.LoadLibrary Kernel32.GetProcAddress Without these two imports, you have to do something a little more elaborate. The method I will outline relies on the exe having only one import: Kernel32.GetModuleHandle Which most Win32 PE files will import. This function returns HMODULE, which is actually the base address of the loaded DLL. (If the file you are working on doesn't import any of these functions, you can try using the standard Kernel32.dll base address of 0xBFF70000 or use some other technique, such as reading the PEB, TEB or using SEH, try Googling.) Assuming you now have the base address of Kernel32.dll you can read the PE headers of the image to find the exported GetProcAddress. Once you have GetProcAddress, you can use it to find LoadLibrary and you then have the two functions you need to load any number of DLLs/functions.
mov eax,myExe.&"Kernel32.dll"                       ;Address of ASCII "Kernel32.dll" - already in imports
push eax
mov eax,<jmp.&Kernel32.GetModuleHandleA>            ;IAT Thunk
call eax
push eax                                            ;hMod
add eax,dword ptr ds:[eax+3c]                       ;eax+e_lfanew
mov ebx,dword ptr ds:[eax+78]                       ;PE+78h IMAGE_DIRECTORY_ENTRY_EXPORT
mov eax,dword ptr ss:[esp]                          ;hMod
add eax,ebx                                         ;VA to IMAGE_EXPORT_DIRECTORY
pop edx                                             ;save hMod
push eax                                            ;save Exports Section address
push edx                                            ;hMod on top
mov ebx,dword ptr ds:[eax+20]                       ;Read IMAGE_EXPORT_DIRECTORY+20h (AddressOfNames[])
pop eax                                             ;hMod
xor ecx,ecx                                         ;reset counter
:label1
inc ecx                                             ;count++
push ebx                                            ;save RVA AddressOfNames[]
push eax                                            ;save hMod
add eax,ebx                                         ;VA AddressOfNames[count]
mov ebx,dword ptr ds:[eax]                          ;RVA FunctionName
mov eax,dword ptr ss:[esp]                          ;hMod
add eax,ebx                                         ;VA of FunctionName
mov esi,eax
pop eax                                             ;hMod
pop ebx                                             ;RVA AddressOfNames[]
mov edi,myExe.&"GetProcA"                           ;ASCII "GetProcA" - Put this in a cave
cmps dword ptr ds:[esi],dword ptr es:[edi]          ;(DWORD cmp) FunctionName = "GetP"
je short myExe.&label2                              ;match, check next half
add ebx,4                                           ;else, next RVA (4byte RVAs)
jmp short myExe.&label1                             ;loop
:label2
add ebx,4                                           ;next RVA (4byte RVAs)
cmps dword ptr ds:[esi],dword ptr es:[edi]          ;(2nd DWORD cmp) FunctionName = "rocA"
jnz short myExe.&label1                             ;if not, loop again
pop edx                                             ;VA Exports Section
mov ebx,dword ptr ds:[edx+1c]                       ;read IMAGE_EXPORT_DIRECTORY+1Ch (AddressOfFunctions[])
push eax                                            ;hMod
mov eax,ecx                                         ;ordinal+1
dec eax                                             ;ordinal
mov ecx,4                                           ;4byte RVAs
mul ecx                                             ;ordinal*4
add ebx,eax                                         ;move ordinal*4 bytes in AddressOfFunctions[]
pop eax                                             ;hMod
push eax                                            ;kernel32.77E60000
add eax,ebx                                         ;RVA FunctionAddress
pop ebx                                             ;hMod
push ebx                                            ;save for later - kernel base
add ebx,dword ptr ds:[eax]                          ;VA FunctionAddress
push ebx                                            ;entry GetProcAddress
The above code snippet leaves you with the VA of GetProcAddress on the stack (and in EBX). You can the go on to find LoadLibrary..
push myExe.&"LoadLibraryA"                      ;Address of ASCII "LoadLibraryA" - in a cave
push dword ptr ss:[esp+8]                       ;hMod Kernel32 - still on the stack from above
call ebx                                        ;call GetProcAddress - returns address to EAX
push eax                                        ;Save to stack
You have now loaded GetProcAddress and LoadLibrary and have them both on the stack ready for use.

x86 Assembly Bubble Sort

January 09, 2005

The following example shows how you can use a bubble sort in Assembly language to sort some numbers:
.386
.model flat,stdcall

option casemap:none

.data
example_data db 1,3,4,5,2,5,7,4,6,0
num_of_elements db 10

.code
start:
    mov eax, dword ptr[num_of_elements] ;whatever the programmer entered
    dec eax                             ;less one (since 10 elements = 0-9)
    mov dword ptr[num_of_elements], eax ;save the new value

    lea eax, example_data               ;point eax to start addr
    xor ebx, ebx                        ;reset (data reg 1)
    xor edx, edx                        ;reset (data reg 2)
    xor ecx, ecx                        ;reset counter

stillsort:
    mov bl, byte ptr[eax]               ;get 1 byte
    mov dl, byte ptr[eax+1]             ;and the byte to its right
    cmp bl, dl                          ;compare the 2
    jg notdone                          ;if byte 2 &gt; byte 1, not sorted, go sort
    push eax                            ;save where we are
    push ecx                            ;save counter
    lea eax, example_data               ;go back to start (for test)
    xor ecx, ecx                        ;reset counter
    jmp test_sorted                     ;go test the whole list

notdone:
    mov byte ptr[eax], dl               ;put byte 2 in byte 1 position
    mov byte ptr[eax+1], bl             ;put byte 1 in byte 2 position
    inc eax                             ;go to next byte
    inc ecx                             ;count
    cmp ecx, dword ptr[num_of_elements] ;10 elements (0-9)
    jnz stillsort                       ;still sorting (no reset)
    lea eax, example_data               ;did all 10 elements, go again from start
    xor ecx, ecx                        ;reset counter
    jmp stillsort                       ;back to sort code

test_sorted:
    mov bl, byte ptr[eax]               ;get 1 byte
    mov dl, byte ptr[eax+1]             ;and the byte to its right
    cmp bl, dl                          ;compare
    jg nope                             ;if byte 2 &gt; byte 1 the whole list isnt sorted
    inc eax                             ;try next byte
    inc ecx                             ;count
    cmp ecx, dword ptr[num_of_elements] ;10 elements (0-9)
    jz done                             ;all 10 elements are sorted
    jmp test_sorted                     ;or loop

nope:
    pop ecx                             ;get the back the old count
    pop eax                             ;back to the last byte we were on
    inc eax                             ;but 1 more now
    inc ecx                             ;increase counter
    cmp ecx, dword ptr[num_of_elements] ;10 elements (0-9)
    jnz stillsort                       ;sorting
    lea eax, example_data               ;it was the last element, back to start
    xor ecx, ecx                        ;reset counter
    jmp stillsort                       ;sorting

done:
    pop ecx                             ;clear stack
    pop eax                             ;clear stack
    xor eax, eax                        ;exit code 0
    ret
end start