If you already know how the export/import scheme works in windows you can scroll to the bottom of the page for the source code involved.
Ever had problems hooking targets that use dynamic linking to call their functions? If you have, you probably used the round about way of hooking LoadLibrary() and GetProcAddress() to see when your target function is being loaded, and then replace the return address with that of your own.
However there's a more elegant way to hook functions that are imported through dynamic linking. You sill need a launcher and some research beforehand for a 100% reliable method, but that is a requisite for the alternative too.
So how do we pull this of?
It's very simple, when an application calls GetProcAddress() on a module, windows searches the export header of that module for the module name supplied in the call. The export header links the function names with their respective addresses(relative to the module base address). When windows finds the name in the export header it returns the corresponding address to the caller.
Since we are not going to hook this function, further internal explanation is not needed. What is important however is the step before calling GetProcAddress(), more specifically the call to GetModuleHandle().
GetModuleHandle() searches a struct called the peb for the module, the PEB is located somewhere in the address space of the calling process. In th PEB is a struct called the ldr. The ldr links the names of all the dlls loaded with their corresponding base addresses in the calling process. So when you load a library with LoadLibrary() it's address and name are recored in the ldr. When GetModuleHandle() searches the PEB there's no guarantee that the address returned corresponds to the name supplied. The PEB isn't even write protected so you can mess with it all you want.
The more absolute readers will probably know what's next. But I just love windows paint so I've painted you guys a picture
So this is what we are going to do:
We are going to change the address in the ldr corresponding to User32.dll. So when our target application calls GetModuleHandle() our base address will be returned instead of that of the real module.
To insure success in a real application you may want to call LoadLibrary() on the module you want to hook yourself. Since you can't hook an entry that isn't there, right? What's more if the library is already loaded LoadLibrary() proceeds as GetModuleHandle and returns the address found in the peb->ldr, which is perfect for us since we can insure that the program only uses our library!
To avoid race conditions however, you'll need to call LoadLibrary() before your target does. And the only way to do that is to inject your dll in a suspended process which created using a launcher.
There are only two more things left to talk about before I can show you the code, so hang in tight!
We've replaced our dll base address with that of user32.dll so all calls to LoadLibrary and GetModuleHandle will return our address. This is all very nice, but how will this help us??
Very simple, remember that GetProcAddres() is called on the base address of a module in memory? And that it returns the address of the function found in the export header?
If you do then the path is clear, if you don't I'll explain one more time below.
When your target calls GetProcAddress() on the address returned by a call to GetModuleHandle() windows will start looking for a function origionaly exported by User32.dll (let's say MessageBoxA). The only problem is that our dll does not export MessageBoxA, so GetProcAddress will fail and your target with it probably. However we can compile our dll to export a function named MessageBoxA, and we can chose what this function does. If we inject this new dll exporting MessageBoxA GetProcAddress() will not fail, and the address of our function is returned to the caller, instead of the address of the real MessageBoxA().
So to sum it all up:
Our target dynamically links to User32.dll importing MessageBoxA().
We can change the PEB address of User32.dll to that of our own.
When GetProcAddress() is called on our dll code of our choosing gets executed.
There are only 3 downsides to this hooking method
You'll need to dig into the target application to see which functions are dynamically linked, if you don't and the target calls a function you haven't exported GetProcAddress() fails, you can of course dissolve these issues by hooking GetProcAddress() but that defeats the purpose.
To avoid race conditions or failed hooks you'll need a launcher to insure that the peb is patched before the target application starts importing it's addresses.
While the peb is not a section which anti-cheat programs generally cover, virus scanners get really, really paranoid with programs modifying their PEB. This is because computer viruses often need to make use of the PEB to create offset independent code. So therefore an anti-virus program is likely to flag your program as a virus, just because it reads and changes entries in the ldr.
Now that's finally over, we can move to the S0uRc3 c0d3S!
Target.exe!main.cpp
new console project main.cpp:
Code:
#include <iostream>
#include <windows.h>
using namespace std;
int main(){
LoadLibrary("User32.dll");
printf("Waiting for keypress\n");
cin.ignore();
printf("Base address of User32.dll: 0x%.8x\n", GetModuleHandle("User32.dll"));
printf("Base address of MessageBoxA: 0x%.8x\n", GetProcAddress( GetModuleHandle("User32.dll"), "MessageBoxA" ) );
system("pause");
return 0;
}
New dll project: DllMain.cpp:
Code:
#include "Main.h"
#include <iostream>
void MainThread();
extern int PatchPeb(char* ModuleName, DWORD* NewBase);
CALLBACK DllMain( HANDLE hModule, DWORD fdwReason, LPVOID lpReserved ){
if( fdwReason == DLL_PROCESS_ATTACH){
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&MainThread, NULL, NULL, NULL);
return TRUE;
}
return TRUE;
}
void MainThread(){
printf("The result: 0x%.8x\n", LoadLibrary("User32.dll"));
PatchPeb("User32.dll", (DWORD*)GetModuleHandle("pebHook.dll") );
printf("The result: 0x%.8x\n", LoadLibrary("User32.dll"));
return;
}
In the same dll project: Main.h
Code:
#ifndef MAIN_H_INC
#define MAIN_H_INC
#include <windows.h>
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
}LDR_MODULE, *PLDR_MODULE;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
}PEB_LDR_DATA, *PPEB_LDR_DATA;
typedef struct _RTL_USER_PROCESS_PARAMETERS
{
PVOID NuLL;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PVOID PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;
#endif
in the same dll project: Patcher.cpp
Code:
#include "Main.h"
char* ANSI_STR(UNICODE_STRING wstr){
char* TempStr = new char[wstr.Length];
if( WideCharToMultiByte(CP_ACP, 0, wstr.Buffer, (UINT)wstr.Length, TempStr, MAX_PATH, NULL, NULL) == 0){
return "ERROR";
} else {
return TempStr;
}
}
int PatchPeb(char* ModuleName, DWORD* NewBase){
PLDR_MODULE LdMod;
unsigned long* ldr_addr;
PPEB_LDR_DATA ldr_data;
char* Str;
__asm{
mov eax, fs:[0x30]
add eax, 0xc
mov eax, [eax]
mov ldr_addr, eax
}
__try{
ldr_data = (PPEB_LDR_DATA)ldr_addr;
LdMod = (PLDR_MODULE)ldr_data->InLoadOrderModuleList.Flink;
if( LdMod == NULL){
return -1;
}
while( LdMod->BaseAddress != 0){
LdMod = (PLDR_MODULE)LdMod->InLoadOrderModuleList.Flink;
Str = ANSI_STR( LdMod->BaseDllName );
// printf("Does %s == %s?\n", Str, ModuleName);
if( strcmp( Str , ModuleName) == 0){
LdMod->BaseAddress = (PVOID)NewBase;
delete [] Str;
break;
} else {
delete [] Str;
}
}
__asm{
mov eax, fs:[0x30]
add eax, 0xc
mov eax, [eax]
mov ldr_addr, eax
}
ldr_data = (PPEB_LDR_DATA)ldr_addr;
LdMod = (PLDR_MODULE)ldr_data->InInitializationOrderModuleList.Flink;
if( ldr_data->Initialized == FALSE ){
while( LdMod->BaseAddress != 0){
LdMod = (PLDR_MODULE)LdMod->InInitializationOrderModuleList.Flink;
Str = ANSI_STR( LdMod->BaseDllName );
// printf("Does %s == %s?\n", Str, ModuleName);
if( strcmp( Str , ModuleName) == 0){
LdMod->BaseAddress = (PVOID)NewBase;
delete [] Str;
break;
} else {
delete [] Str;
}
}
__asm{
mov eax, fs:[0x30]
add eax, 0xc
mov eax, [eax]
mov ldr_addr, eax
}
ldr_data = (PPEB_LDR_DATA)ldr_addr;
LdMod = (PLDR_MODULE)ldr_data->InMemoryOrderModuleList.Flink;
while( LdMod->BaseAddress != 0){
LdMod = (PLDR_MODULE)LdMod->InMemoryOrderModuleList.Flink;
Str = ANSI_STR( LdMod->BaseDllName );
// printf("Does %s == %s?\n", Str, ModuleName);
if( strcmp( Str , ModuleName) == 0){
LdMod->BaseAddress = (PVOID)NewBase;
delete [] Str;
break;
} else {
delete [] Str;
}
}
}
} __except(EXCEPTION_EXECUTE_HANDLER){
return -2;
}
return 0;
}
In the same dll project: Exports.cpp
Code:
#define DllExport __declspec( dllexport )
extern "C" DllExport unsigned long MessageBoxA(unsigned long hwnd, char* Text, char* Title, unsigned long Mask){
return 0;
}
Example output:
Credits
While the code is mostly mine and copied from an old project, I got the idea from: Shearer and Dreg
For those interested in the original article I can only say: Phrack-65:10