All programs loaded through Windows API are automatically loaded with ntdll.dll (Core system functions), kernel32.dll (Windows "kernel"), and KERNELBASE.DLL (Another Windows "kernel"). These libraries can actually be accessed without having to load them through an import table, with the power of Windows API (Sometimes it shines, sometimes it just flat out sucks... Mostly the second part).
Basically we're going to create an importer by getting
LoadLibraryExA from KERNELBASE.DLL (It doesn't have LoadLibraryA).
First step is to get the base address of either Kernel32.dll or KERNELBASE.DLL. We can get KERNELBASE.DLL with the Windows API.
Code:
GetKERNELBASE:
mov eax,[fs:0x30]
mov eax,[eax+0x0c]
mov eax,[eax+0x1c]
mov eax,[eax]
mov eax,[eax+0x08]
ret
This function gets the base address of KERNELBASE.DLL in your application through a series of Windows API pointers.
Next we need to create our own
GetProcAddress function.
Code:
MyGetProcAddress:
push ebp
mov ebp,esp
push ebx
push ecx
push esi
push edi
mov dword eax,[ebp+8]
mov [.modb],eax ;Get module base address
mov dword eax,[ebp+12] ;String to cmp
mov [.fname],eax
mov eax,[.modb]
add eax,60
mov eax,[eax] ;get e_lfanew
add eax,[.modb] ;PE Header
add eax,0x78 ;now points to export directory address
mov eax,[eax] ;... and get that address
add eax,[.modb]
mov [.ied],eax
mov ebx,[eax+24] ;get the number of names
mov [.non],ebx
mov ebx,[eax+28] ;get the addresses of functions
mov [.aof],ebx
mov ebx,[eax+32] ;get the addresses of names
mov [.aon],ebx
mov ebx,[eax+36] ;get the addresses of name ordinals
mov [.aono],ebx
xor ecx,ecx
.loop:
mov dword esi,[.fname]
cmp dword ecx,[.non]
jnl .break
mov dword ebx,[.aon]
add dword ebx,[.modb]
mov eax,4
mul ecx
add ebx,eax
mov dword ebx,[ebx]
add ebx,[.modb]
mov edi,ebx
.chkstr:
cmpsb
jne .continue
cmp byte [esi-1],0
jne .chkstr
cmp byte [edi-1],0 ;checking edi isn't necessary, but it's here for safety anyway
jne .chkstr
mov dword ebx,[.aono]
add dword ebx,[.modb]
mov eax,2
mul ecx
add ebx,eax
mov word bx,[ebx]
mov [.ordinal],bx
mov ebx,[.aof]
add dword ebx,[.modb]
mov eax,4
mul [.ordinal]
add ebx,eax
mov eax,[ebx]
add eax,[.modb]
pop edi
pop esi
pop ecx
pop ebx
mov esp,ebp
pop ebp
ret 8
.continue:
inc ecx
jmp .loop
.break:
popa
mov esp,ebp
pop ebp
ret 8
.modb dd 0
.ordinal dw 0
.fname dd 0
.ied dd 0
.non dd 0
.aon dd 0
.aono dd 0
.aof dd 0
There is a lot of code and I don't feel like explaining it all, but I can explain
how to create your own GetProcAddress function (in not too much detail). WARNING: If you don't have a working knowledge of Windows headers, then you will not understand the following steps.
Now you can just call the code like so:
Code:
call GetKERNELBASE ;puts base address of KERNELBASE.DLL into eax
push <ProcString>
push <BaseModuleAddress>
call MyGetProcAddress ;puts address of process <ProcString> from <BaseModuleAddress> into eax
Here is a full example script:
Code:
format PE GUI
entry main
section '.text' code writable readable executable
k32:
dd 0
.LoadLibraryEx dd 0
u32:
dd 0
.MessageBox dd 0
Strings:
.u32 db 'user32.dll',0
.MessageBoxA db 'MessageBoxA',0
.LoadLibraryExA db 'LoadLibraryExA',0
.cap db 'Caption',0
.msg db 'No IATs needed!',0
main:
call GetKERNELBASE
mov [k32],eax
push Strings.LoadLibraryExA
push dword [k32]
call MyGetProcAddress
cmp eax,0
je .break
mov [k32.LoadLibraryEx],eax
push 0
push 0
push Strings.u32
call [k32.LoadLibraryEx]
mov [u32],eax
push Strings.MessageBoxA
push dword [u32]
call MyGetProcAddress
mov [u32.MessageBox],eax
push 0
push Strings.cap
push Strings.msg
push 0
call [u32.MessageBox]
.break: ;Couldn't find LoadLibraryExA
ret
GetKERNELBASE:
mov eax,[fs:0x30]
mov eax,[eax+0x0c]
mov eax,[eax+0x1c]
mov eax,[eax]
mov eax,[eax+0x08]
ret
MyGetProcAddress:
push ebp
mov ebp,esp
push ebx
push ecx
push esi
push edi
mov dword eax,[ebp+8]
mov [.modb],eax ;Get module base address
mov dword eax,[ebp+12] ;String to cmp
mov [.fname],eax
mov eax,[.modb]
add eax,60
mov eax,[eax] ;get e_lfanew
add eax,[.modb] ;PE Header
add eax,0x78 ;now points to export directory address
mov eax,[eax] ;... and get that address
add eax,[.modb]
mov [.ied],eax
mov ebx,[eax+24] ;get the number of names
mov [.non],ebx
mov ebx,[eax+28] ;get the addresses of functions
mov [.aof],ebx
mov ebx,[eax+32] ;get the addresses of names
mov [.aon],ebx
mov ebx,[eax+36] ;get the addresses of name ordinals
mov [.aono],ebx
xor ecx,ecx
.loop:
mov dword esi,[.fname]
cmp dword ecx,[.non]
jnl .break
mov dword ebx,[.aon]
add dword ebx,[.modb]
mov eax,4
mul ecx
add ebx,eax
mov dword ebx,[ebx]
add ebx,[.modb]
mov edi,ebx
.chkstr:
cmpsb
jne .continue
cmp byte [esi-1],0
jne .chkstr
cmp byte [edi-1],0 ;checking edi isn't necessary, but it's here for safety anyway
jne .chkstr
mov dword ebx,[.aono]
add dword ebx,[.modb]
mov eax,2
mul ecx
add ebx,eax
mov word bx,[ebx]
mov [.ordinal],bx
mov ebx,[.aof]
add dword ebx,[.modb]
mov eax,4
mul [.ordinal]
add ebx,eax
mov eax,[ebx]
add eax,[.modb]
pop edi
pop esi
pop ecx
pop ebx
mov esp,ebp
pop ebp
ret 8
.continue:
inc ecx
jmp .loop
.break:
popa
mov esp,ebp
pop ebp
ret 8
.modb dd 0
.ordinal dw 0
.fname dd 0
.ied dd 0
.non dd 0
.aon dd 0
.aono dd 0
.aof dd 0