Tutorial IAT hook Import Address Table Hooking Explained

Hexui Undetected CSGO Cheats Sinkicheat PUBG Cheat

NTvalk

Hacker
Meme Tier VIP
Jul 6, 2013
499
3,108
8
Hello, I was looking for a good alternative for MS-Detours so i started making an IAT hook. I will add a library in the end with an example.

IAT hooking has pros and cons:
Cons:
- The method you are hooking must be imported from another module, you can't just hook a certain address in memory. This is not optimal for directx hooks, since you will only find createdevice (you can use that to get the device tho) but for Opengl and such this is handy.
Pros:
- Less detectable, you can make this into a fully external hook, that should be undetected for any antivirus/cheat because it also doesn't use any malicious calls.

Before we jump in the Import Address Table you first need to know a bit background information, I'll start with the PE format. The Portable Executable (PE) format is a file format for executables, object code, DLLs, FON Font files, and others used in 32-bit and 64-bit versions of Windows operating systems. The PE format is a data structure that encapsulates the information necessary for the Windows OS loader to manage the wrapped executable code. This includes dynamic library references for linking, API export and import tables, resource management data and thread-local storage (TLS) data.

One section of note is the import address table (IAT), which is used as a lookup table when the application is calling a function in a different module. It can be in the form of both import by ordinal and import by name. Because a compiled program cannot know the memory location of the libraries it depends upon, an indirect jump is required whenever an API call is made. As the dynamic linker loads modules and joins them together, it writes actual addresses into the IAT slots, so that they point to the memory locations of the corresponding library functions. Though this adds an extra jump over the cost of an intra-module call resulting in a performance penalty, it provides a key benefit: The number of memory pages that need to be copy-on-write changed by the loader is minimized, saving memory and disk I/O time. If the compiler knows ahead of time that a call will be inter-module (via a dllimport attribute) it can produce more optimized code that simply results in an indirect call opcode.

pe_imptbl_headers.jpg
(you can zoom in)
More information on the PE header: msdn

This will be the procedure for internal (dll must be injected in target process) hooking:
- Retrieve DOS/NT Headers
- loop through the import descriptors
And thats it!

For external hooking you must do the following:
- Retrieve PEB base address with ntqueryprocessinformation
- From the PEB we get the image header (DOS/NT etc) and the .text section
- Loop through import descriptors
- Get the offset for our function
- Read the IAT
- Patch the IAT

Externally hooking the IAT is kinda overkill for a game hack tho, it's too much work if you want to hook multiple functions. I decided to explain it a bit anyway just for the lulz.

So first we get a handle to our main module:
C++:
int ip = 0;
if (module == 0)
	module = GetModuleHandle(0);
then we retrieve the headers (warning:Whoever wrote the header file for the PE format is certainly a believer in long, descriptive names, along with deeply nested structures and macros. When coding with WINNT.H, it's not uncommon to have mind blowing expressions):
C++:
// get the DOS header 
PIMAGE_DOS_HEADER pImgDosHeaders = (PIMAGE_DOS_HEADER)module;
// get the NT header from the dos header
PIMAGE_NT_HEADERS pImgNTHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pImgDosHeaders + pImgDosHeaders->e_lfanew); 
// get the import_descriptor from the NT header (its all relative so we keep adding (LPBYTE)pImgDosHeaders
PIMAGE_IMPORT_DESCRIPTOR pImgImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 
// the size also from the NT header
int size = (int)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size);
// check if the DOS header is a valid dos header
	if (pImgDosHeaders->e_magic != IMAGE_DOS_SIGNATURE)
		printf("e_magic is no valid DOS signature\n");
Now we basicly have enough information to start making the loops to the function pointer, note that every DLL has its own IMAGE_IMPORT_DESCRIPTOR that's why we loop through all of them:
C++:
	for (IMAGE_IMPORT_DESCRIPTOR* iid = pImgImportDesc; iid->Name != NULL; iid++){}
And inside this loop, we loop through the functions, if you add an int to the firsthunk you get to the next thunk and so on.
C++:
	for (int funcIdx = 0; *(funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module)) != NULL; funcIdx++){}
Now if you look in the import_desciptor structure you can see the name is on firsthunk +2 so
C++:
char* name = (*(funcIdx + (SIZE_T*)(iid->OriginalFirstThunk + (SIZE_T)module)) + 2
when we have the name we can compare it with our target and patch the address :)
the function will look like this:

C++:
void** ninehook::IATfind(const char* function, HMODULE module){
	int ip = 0;
	if (module == 0)
		module = GetModuleHandle(0);
	PIMAGE_DOS_HEADER pImgDosHeaders = (PIMAGE_DOS_HEADER)module;
	PIMAGE_NT_HEADERS pImgNTHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)pImgDosHeaders + pImgDosHeaders->e_lfanew); 
	PIMAGE_IMPORT_DESCRIPTOR pImgImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
	int size = (int)((LPBYTE)pImgDosHeaders + pImgNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size);

	if (pImgDosHeaders->e_magic != IMAGE_DOS_SIGNATURE)
		printf("e_magic is no valid DOS signature\n");

	for (IMAGE_IMPORT_DESCRIPTOR* iid = pImgImportDesc; iid->Name != NULL; iid++){
		for (int funcIdx = 0; *(funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module)) != NULL; funcIdx++){
			char* modFuncName = (char*)(*(funcIdx + (SIZE_T*)(iid->OriginalFirstThunk + (SIZE_T)module)) + (SIZE_T)module + 2);
			if (!_stricmp(function, modFuncName))
				return funcIdx + (LPVOID*)(iid->FirstThunk + (SIZE_T)module);
		}
	}
	return 0;
}
And that's it! now we can just patch it:
C++:
DWORD oldrights, newrights = PAGE_READWRITE;
		VirtualProtect(funcptr, sizeof(LPVOID), newrights, &oldrights);
		oldfunctionptr = *funcptr;
		*funcptr = newfunction;
		VirtualProtect(funcptr, sizeof(LPVOID), oldrights, &newrights);
To make it external its a bit harder because we are only allowed to use read/write process memory.
Make sure you open the process with
C++:
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_OPERATION
rights.
I will not explain everything in detail here.

This is the way you can use NtQueryInformationProcess to get the PEB, the PEB structure contains information we need for the DOS header
msdn said:
Contains process information.
C++:
PROCESS_BASIC_INFORMATION* ninehook::FindRemotePEB(HANDLE hProcess){
	HMODULE hNTDLL = LoadLibraryA("ntdll");
	if (!hNTDLL)
		return 0;

	FARPROC fpNtQueryInformationProcess = GetProcAddress(hNTDLL,"NtQueryInformationProcess");
	if (!fpNtQueryInformationProcess)
		return 0;

	NtQueryInformationProcess_t ntQueryInformationProcess =
		(NtQueryInformationProcess_t)fpNtQueryInformationProcess;
	PROCESS_BASIC_INFORMATION* pBasicInfo = new PROCESS_BASIC_INFORMATION();
	DWORD dwReturnLength = 0;

	ntQueryInformationProcess(hProcess,
		0,
		pBasicInfo,
		sizeof(PROCESS_BASIC_INFORMATION),
		&dwReturnLength);

	if (pBasicInfo->PebBaseAddress == 0)
		printf("address of peb is zero\n");


	return pBasicInfo;
}


PEB* ninehook::ReadRemotePEB(HANDLE hProcess){
	PROCESS_BASIC_INFORMATION* procbscInfo;
	procbscInfo = FindRemotePEB(hProcess);
	DWORD dwPEBAddress = procbscInfo->PebBaseAddress;
	PEB* pPEB = new PEB();

	BOOL bSuccess = ReadProcessMemory(hProcess,
		(LPCVOID)dwPEBAddress,
		pPEB,
		sizeof(PEB),
		0);

	if (!bSuccess)
		return 0;

	return pPEB;
}
This is all pretty straightforward, get the PROCESS_BASIC_INFORMATION and from there a pointer to the PEB.
the PEB contains the imagebaseaddress, which we use to get the DOS header:

C++:
BYTE* lpBuffer = new BYTE[BUFFER_SIZE];
	PIMAGE_THUNK_DATA32 pIAT, pILT;
	DWORD dwOffset,dwThunkArrayLen = BUFFER_SIZE / sizeof(IMAGE_THUNK_DATA32);
	PIMAGE_IMPORT_DESCRIPTOR pImportDescriptors;

	BOOL bSuccess = ReadProcessMemory(hProcess,
		lpImageBaseAddress,
		lpBuffer,
		BUFFER_SIZE,
		0);

	if (!bSuccess){
		printf("Read process memory error\n");
		return 0;
	}
	PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER)lpBuffer;

	LOADED_IMAGE* pImage = new LOADED_IMAGE();
	pImage->FileHeader = (PIMAGE_NT_HEADERS32)(lpBuffer + pDOSHeader->e_lfanew);
	pImage->NumberOfSections = pImage->FileHeader->FileHeader.NumberOfSections;
	pImage->Sections =(PIMAGE_SECTION_HEADER)(lpBuffer + pDOSHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32));

	/* read .text */
	PIMAGE_SECTION_HEADER pImportTextHeader;
	for (DWORD i = 0; i < pImage->NumberOfSections; i++){
		PIMAGE_SECTION_HEADER pHeader = &pImage->Sections[i];
		if (!_stricmp((char*)pHeader->Name, ".text")){
			pImportTextHeader = pHeader;
			break;
		}
}
This is same as we did with internal, but now we also read the .text section
Now we use readprocessmemory() to go through the headers, we read the importdescriptors and then we find the index to our function
C++:
/* read image descriptor */
	IMAGE_DATA_DIRECTORY importDirectory = pImage->FileHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 
	pImportDescriptors = new IMAGE_IMPORT_DESCRIPTOR[importDirectory.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR)];

	bSuccess = ReadProcessMemory(hProcess,
		(LPCVOID)((DWORD)lpImageBaseAddress + importDirectory.VirtualAddress),
		pImportDescriptors,
		importDirectory.Size,
		0);

	if (!bSuccess || !pImportDescriptors)
		return 0;

	for (DWORD i = 0; i < BUFFER_SIZE; i++){
		IMAGE_IMPORT_DESCRIPTOR idescriptor = pImportDescriptors[i];
		char* pName = new char[BUFFER_SIZE];

		bSuccess = ReadProcessMemory(hProcess,
			(LPCVOID)((DWORD)lpImageBaseAddress + idescriptor.Name),
			pName,
			BUFFER_SIZE,
			0);

		if (!bSuccess)
			return 0;
		//printf("DLL : %s\n",pName);
		if (!_stricmp(pName, pDLLname)){
			pILT = new IMAGE_THUNK_DATA32[dwThunkArrayLen];
			bSuccess = ReadProcessMemory(hProcess,
				(LPCVOID)((DWORD)lpImageBaseAddress +
				idescriptor.OriginalFirstThunk),
				pILT,
				BUFFER_SIZE,0);
			if (!bSuccess)
				return 0;
			
			/* Get the offset */
			for (dwOffset = 0; dwOffset < dwThunkArrayLen; dwOffset++){
				BYTE* lpImportNameBuffer = new BYTE[BUFFER_SIZE];
				ReadProcessMemory(hProcess,
					(LPCVOID)((DWORD)lpImageBaseAddress + pILT->u1.AddressOfData),
					lpImportNameBuffer,
					BUFFER_SIZE,
					0);
				PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)lpImportNameBuffer;
				if (!strcmp((char*)pImportByName->Name, pFunctionName))
					break;
			}
			/* Read the import table */
			ReadProcessMemory(hProcess,
				(LPCVOID)((DWORD)lpImageBaseAddress +
				idescriptor.FirstThunk),
				pIAT,
				BUFFER_SIZE,0);

			if (!pIAT){
				printf("Error reading Address Table\n");
				return FALSE;}
                        // pointer to the old function
			oldfunctionptr = (void*)pIAT[dwOffset].u1.AddressOfData;
As i said earlier i will not go through all the code but if you take the time to read it, it will probably make sense to you.

Now you need to add a handler (Assembly code converted to hex, so we can use it easier) to the text section and from there patch the IAT.

C++:
BYTE* pHandlerBuffer = new BYTE[dwHandlerSize];

			memcpy(pHandlerBuffer, pHandler, dwHandlerSize);

			BOOL bSuccess = PatchDWORD
			(
				pHandlerBuffer, 
				dwHandlerSize, 
				0xDEADBEEF, 
				dwOriginalAddress
			);

			if (!bSuccess)
			{
				printf("Error patching import address into handler");
				return FALSE;
			}

			DWORD dwHandlerAddress = (DWORD)pImportImageBase + 
				pImportTextHeader->VirtualAddress + 
				pImportTextHeader->SizeOfRawData - 
				dwHandlerSize;

			// Write handler to text section
			bSuccess = WriteProcessMemory
			(
				hProcess,
				(LPVOID)dwHandlerAddress, 
				pHandlerBuffer, 
				dwHandlerSize, 
				0
			);

			if (!bSuccess)
			{
				printf("Error writing process memory");
				return FALSE;
			}

			printf("Handler address: 0x%p\r\n", dwHandlerAddress);

			LPVOID pAddress = (LPVOID)((DWORD)pPEB->ImageBaseAddress + 
				descriptor.FirstThunk + (dwOffset * sizeof(IMAGE_THUNK_DATA32)));

			// Write IAT
			bSuccess = WriteProcessMemory
			(
				hProcess,
				pAddress,
				&dwHandlerAddress, 
				4, 
				0
			);

			if (!bSuccess)
			{
				printf("Error writing process memory");
				return FALSE;
			}	

			return TRUE;
		}
If you are kinda new to hooking or are not interested in doin things the hard way i suggest just sticking with the internal method through DLL injection.
Hope you enjoyed reading this.
 
Last edited:

gothie

Newbie
Sep 7, 2016
3
284
0
I need a code in Delphi! hooking table d'import LdrLoadDll api to prevent dll injection in my own application

any friends can help me?

You will be well rewarded!
 

Broihon

edgy 12 y/o
Escobar Tier VIP
Fleep Tier Donator
Dec 22, 2013
1,745
40,528
316
I need a code in Delphi! hooking table d'import LdrLoadDll api to prevent dll injection in my own application

any friends can help me?

You will be well rewarded!
Why do you want to IAT hook LdrLoadDll in your own process? Wouldn't it be easier to hook it directly in case someone uses LdrLoadDll directly instead of LoadLibraryA?
 

gothie

Newbie
Sep 7, 2016
3
284
0
hi Вroihon, I need a method to prevent any dll injected in my aplications and or how to unload this dll without crash my application.

"Dll Injection prevent and unload"
 

Broihon

edgy 12 y/o
Escobar Tier VIP
Fleep Tier Donator
Dec 22, 2013
1,745
40,528
316
hi Вroihon, I need a method to prevent any dll injected in my aplications and or how to unload this dll without crash my application.

"Dll Injection prevent and unload"
Yes, as I said you should hook LdrLoadDll directly (not as IAT hook). I don't know any Delphi so I can't really help you with that.
Also keep in mind that that doesn't help against manual mapping.
 

gothie

Newbie
Sep 7, 2016
3
284
0
Вroihon;44708 said:
Yes, as I said you should hook LdrLoadDll directly (not as IAT hook). I don't know any Delphi so I can't really help you with that.
Also keep in mind that that doesn't help against manual mapping.
OK! thank you for the attention! ;)
 

Broihon

edgy 12 y/o
Escobar Tier VIP
Fleep Tier Donator
Dec 22, 2013
1,745
40,528
316
No, Im saying youre looking at the issue wrong; GCNrd only does what you tell it to. If you tell it to break on all writes to address 80C68A68, then its going to, regardless of what you think might belong there.
Nice spambot
 

HACKEDHACKER

Mambda
Fleep Tier Donator
Nobleman
Mar 5, 2014
83
2,208
6
I know this is necroing but so technically, you could patch the address in the IAT to your function, and next time they try to look for that function, it will jump to our function instead. However, not all functions are in IAT, only the exported function in a DLL that is being loaded are in the IAT, correct?
 
Attention! Before you post:

Read the How to Ask Questions Guide
99% of questions are answered in the Beginner's Guide, do it before asking a question.

No Hack Requests. Post in the correct section.  Search the forum first. Read the rules.

How to make a good post:

  • Fill out the form correctly
  • Tell us the game name & coding language
  • Post everything we need to know to help you
  • Ask specific questions, be descriptive
  • Post errors, line numbers & screenshots
  • Post code snippets using code tags
  • If it's a large project, zip it up and attach it

If you do not comply, your post may be deleted.  We want to help, please make a good post and we will do our best to help you.

Community Mods