Nothing can be simpler that calling API functions by absolute addresses. Having chosen a function (let it be the GetCurrentThreadId function exported by kernel32.dll), process it with the dumpbin utility supplied as part of practically any compiler. Having recognized the Relative Virtual Address (RVA) of the required function, it is necessary to add it to the base load address reported by dumpbin . As a result, the absolute address of the function will be obtained.
The complete session of working with the dumpbin utility appears as shown in Listing 11.3. Here, the absolute address of the GetCurrentThreadId function is determined by adding its RVA ( 76A1h ) to the base load address of the module ( 77E80000h ).
>dumpbin.exe /EXPORTS KERNEL32.DLL > KERNEL32.TXT >type KERNEL32.TXT MORE ordinal hint RVA name 270 10D 00007DD2 GetCurrentProcessId 271 10E 000076AB GetCurrentThread 272 10F 000076A1 GetCurrentThreadld 273 110 00017CE2 GetDateFormatA 274 111 00019E18 GetDateFormatW >dumpbin.exe /HEADERS KERNEL32.DLL > KERNEL32.TXT >type KERNEL32.TXT MORE OPTIONAL HEADER VALUES 10B Magic # 5.12 Linker version 5D800 Size of code 56400 Size of initialized data 0 Size of uninitialized data 871D RVA of entry point 1000 Base of code 5A000 Base of data 77E80000 Image base 1000 Section alignment 200 File alignment ...
On my machine, the absolute address of the GetCurrentThreadId function is equal to 77E876A1h ; however, in different versions of Windows NT it certainly will be different. Regardless, the call to this function easily fits within two lines of code (or 1 bytes), as shown in Listing 11.4.
00000000: B8A1867E07 MOV EAX, 0077E86A1 00000005: FFDO CALL EAX
Now try to call the connect function exported by ws2_32.dll. Process ws2_32.dll with dumpbin and Wait! Who promised that this DLL would be loaded into the memory? Furthermore, even if it happens to be loaded, no one can guarantee that the base address written in its header matches the actual base load address. After all, DLLs are numerous , and if this address is already occupied by another module, the operating system will load this library into another memory region.
Only two DLLs are guaranteed to be present in the address space of any process, and they always are loaded at the same addresses (the base load address of these DLLs is constant for a given operating system version). These are kernel32.dll and ntdlLdH. Functions exported by other libraries must be called as shown in Listing 11.5.
h = LoadLibraryA("ws2_32.DLL") ; if (h != 0) __error__; zzz = GetProcAddress(h, "connect");
Thus, the task of calling an arbitrary function is reduced to searching addresses of the LoadLibraryA and GetProcAddress functions.