API Hashing
In this documentation, we will discuss API Hashing technique.
What is Portable Executable?
First of all, I think it is necessary to understand the PE (Portable Executable) structure because this technique is all about this structure.
PE is a file format for executable files, object codes, DLLs and others used in 32 and 64 bit versions of Windows operating systems and UEFI environments. PE contains the data structures needed for the Windows OS loader to manage the code to be executed.
A PE file consists of a set of headers and sections that tell the dynamic linker how to map the executable file into file memory and execute it. These headers and sections allow the operating system to load and run the file correctly.

Some important headers in the PE structure are as follows:
- DOS Header (IMAGE_DOS_HEADER): This header contains the minimum information necessary for the executable to be recognized by older MS-DOS operating systems.
- DOS Stub: This contains the minimum information required for an executable file to be recognized by the DOS operating system. This is used to determine whether the file is executable or not.
- PE Header (IMAGE_NT_HEADERS): This header contains important information specific to the PE format. It contains the file size, entry point address, optional DLLs and other information necessary for the operating system to load and run the file correctly.
- File Header (IMAGE_FILE_HEADER): This header contains general information about the file. The file’s architecture, file type, partitions and other information can be found here.
- Optional Header (IMAGE_OPTIONAL_HEADER): This header contains the information needed to load and run the file. The file’s architecture, file type, partitions and other information can be found here.
What are EAT and IAT?
EAT (Export Address Table) and IAT (Import Address Table) are two important tables found in PE files. These tables contain the addresses and names of functions used by the operating system during file execution.
- EAT (Export Address Table): EAT is a table containing the addresses and names of the functions that a PE file exports. This table helps the dynamic linker to determine which functions can be used when loading and running the executable. Keep this in mind especially because we will operate on this table in the future.
- IAT (Import Address Table): IAT is a table containing the addresses and names of functions that a PE file imports from other PE files. This table also helps the dynamic linker to determine which functions should be imported when loading and running the executable.
What is API Hashing?
API Hashing is a technique that converts the name or other identifying characteristics of an API function into a hash value. This hash value is used to represent the identity of the function. It can be used to hide the names and addresses of APIs.
For example, let’s say we compile and run a project that includes the MessageBoxA function. Since we are using MessageBoxA in our project, during compilation the compiler will add the address of the MessageBoxA API to the Import Address Table (IAT). This means that when we run the program, the MessageBoxA API will be accessible from this address.
However, what can we do if we want to hide the address of MessageBoxA? This is where API hashing comes in. A person analyzing the malware can easily see the APIs used in the malware in the IAT. Therefore, if we give different names to these APIs in our project and then hash these different names and add them to the IAT, the analyst cannot easily see these APIs. Basically, this is the API hashing method: Hiding the relevant API by renaming and hashing it and then adding it to the IAT table.
#include <stdio.h>
#include <Windows.h>
DWORD CalculateHash(char* data) {
DWORD hash = 0x99;
for (size_t i = 0; i < sizeof(data); i++) {
hash += (hash * 0xab10f29f + data[i]) & 0xffffff;
}
return hash;
}
int main() {
DWORD hash_value = CalculateHash("CreateThread");
printf("CreateThread: 0x%00x\n", hash_value);
getchar();
return 0;
}
The above example is a simple piece of code that calculates the hash value of a given API. First, the name of the API is taken and sent to the CalculateHash function to calculate it.
Inside the function, we created a DWORD variable named hash and holding a value of 0x99. We used this value as the starting point of the calculation.
We then started a loop. In this loop, the current hash value is multiplied by the constant value 0xab10f29f. This helps to increase the contribution of each character to the hash value. The value of the current character is added to the multiplied value. Finally, the result is bitwise AND with the value 0xffffff. This ensures that the hash value remains a 32-bit integer. This is a simple project and we will use this function in the future.
Finding the Base Address
Now, we will move on to finding the address of the API. Step by step, using WinDbg, we will learn how to sort the process modules and APIs and how to see the PE (Portable Executable) structure. Then, we will apply this information to the C project.
First, we will examine the PEB structure of a running process, looking at the base addresses of the modules of the process. Specifically, a PEB is a structure that contains the runtime information of a process. It contains the base addresses of the process’s modules, heap and stack information, environmental variables and other information.
In 64-bit processes, the PEB is located at offset 0x60 from the Thread Environment Block block. TEB is a structure containing runtime information of the executing thread. The TEB structure is marked by the GS segment register. Therefore, the PEB structure can be accessed through the GS segment register at GS:0x60.
Now, let’s run cmd.exe and start the analysis using WinDbg:
First, we can start by examining the structure of TEB with the dt command:

The important section is +0x060 ProcessEnvironmentBlock : 0x000000fc4bacf000 PEB. We mentioned TEB and PEB above and here we see the address of PEB in TEB. Now let’s take a look at this structure with the address of this PEB:

Thus we have seen the PEB structure of the closely related process. The part that interests us here is +0x018 Ldr: 0x00007ffec5c153e0 PEB_LDR_DATA. This structure contains the list of loaded modules. Let’s take a look at this structure:

When we take a look at this structure, we see that there are three different lists. These lists represent the list of loaded modules. The structures inside these lists belong to the LDR_DATA_TABLE_ENTRY structure. This structure contains the information of the loaded module.
Before recognizing these lists, we need to understand the LIST_ENTRY structure:

- Flink The next structure that points to the corresponding structure.
- Blink: The previous structure pointing to the related structure.
Now let’s get to know the lists:
-
InLoadOrderModuleList: Represents the order in which modules are loaded.
-
InMemoryOrderModuleList: Represents the order in which modules are loaded into memory.
-
InInitializationOrderModuleList: Represents the initialization order of the modules.
Our main focus will be on the InLoaderOrderModuleList. We mentioned that this list represents the order in which the modules are loaded. Now we are going to operate on this list and browse the loaded modules one by one.
First, the address 0x000002c0f1e23690 in the InLoadOrderModuleList structure is the address of the first loaded module. Using this address we can browse the first loaded module:

We can see the detailed information of the first loaded module up close as can be seen in the photo. We can also see that the first loaded module is the module of the executable file with the name cmd.exe. Dllbase contains the base address of the module. BaseDllName is the name of the loaded module.
We can move on to the next loaded module to continue the research:

We see that the other loaded module is ntdll.dll. In this way we can examine all the installed modules one by one. Finally, let’s take a look at the other module:

When we take a look at the other loaded module, we see that it is kernel32.dll. Now we will turn our focus to our C project. We will pour what we have learned into the C project.
Since these structures are not defined in the IDE, we need to complete them manually. Now we can move on to coding:
#include "utils.h"
DWORD CalculateHash(char* data) {
DWORD hash = 0x99;
for (size_t i = 0; i < sizeof(data); i++) {
hash += (hash * 0xab10f29f + data[i]) & 0xffffff;
}
return hash;
}
DWORD CalcModuleHash(LDR_MODULE* ModuleLink) {
char* ModuleName[64];
size_t counter = 0x0;
while (ModuleLink->BaseDllName.Buffer[counter] && counter < sizeof(ModuleName) - 1) {
ModuleName[counter] = (char)ModuleLink->BaseDllName.Buffer[counter];
counter++;
}
ModuleName[counter++] = 0;
return CalculateHash((char*)CharLowerA(ModuleName));
}
HMODULE GetModuleBaseAddress(DWORD Hash) {
HMODULE ModuleBaseAddress = NULL;
INT_PTR PEB = __readgsqword(0x60);
INT_PTR FlinkOffset = 0x10;
INT_PTR PEB_LDR_DATA = *(INT_PTR*)(PEB + LDR);
INT_PTR FirstFlink = *(INT_PTR*)(PEB_LDR_DATA + FlinkOffset);
LDR_MODULE* LDR_DATA_TABLE_ENTRY = (LDR_MODULE*)FirstFlink;
do {
LDR_DATA_TABLE_ENTRY = (LDR_MODULE*)LDR_DATA_TABLE_ENTRY->InLoadOrderModuleList.Flink;
if (LDR_DATA_TABLE_ENTRY->BaseAddress != NULL) {
if (CalcModuleHash(LDR_DATA_TABLE_ENTRY) == Hash) {
break;
}
}
} while (FirstFlink != (INT_PTR)LDR_DATA_TABLE_ENTRY);
ModuleBaseAddress = (HMODULE)LDR_DATA_TABLE_ENTRY->BaseAddress;
return ModuleBaseAddress;
}
In this code, first the hash value of the API is calculated with the CalculateHash function. Then the CalcModuleHash function takes the name of a module and calculates its hash value. In the process, the module name is converted to lower case and a new hash value is calculated using the ASCII value of each character and the previous hash value.
Finally, the GetModuleBaseAddress function finds a module that matches a given hash value and returns its base address. For this, the process loops over the loaded module list and checks the hash value of each module by calculating it with CalcModuleHash. If a match is found, the starting address of the corresponding module is returned.
But we don’t need to use this piece of code. There are already APIs that do this.
Finding the API’s Address
After finding the Base Address, we need to find the address of the API. What we need for this is the IMAGE_EXPORT_DIRECTORY structure. This structure contains the address of the Export Address Table (EAT). Here are some of the important fields contained in this structure:
- NumberOfFunctions: Number of exported functions
- AddressOfFunctions: Address of the table with the addresses of the exported functions
- AddressOfNames: Address of the table with the names of the exported functions
Let’s go back to Windbg and start by finding the EAT table of ntdll:

We can see that the address where the ntdll is installed is 00007ffec5a90000. Now let’s access the IMAGE_DOS_HEADER structure from this address:

After finding the address of the IMAGE_DOS_HEADER structure, we will reach the IMAGE_NT_HEADERS structure.
To find the address of this structure, we can obtain the hex value of e_lfanew of the IMAGE_DOS_HEADER structure by adding it to the address of ntdll. Thus we can reach the IMAGE_NT_HEADERS structure:

Then we will access IMAGE_OPTIONAL_HEADER64’a (0x18):
0:004> dt _IMAGE_OPTIONAL_HEADER64 00007ffe`c5a90000+0xe0+0x18
ntdll!_IMAGE_OPTIONAL_HEADER64
+0x000 Magic : 0x20b
+0x002 MajorLinkerVersion : 0xe ''
+0x003 MinorLinkerVersion : 0x1e ''
+0x004 SizeOfCode : 0x130000
+0x008 SizeOfInitializedData : 0xe5000
+0x00c SizeOfUninitializedData : 0
+0x010 AddressOfEntryPoint : 0
+0x014 BaseOfCode : 0x1000
+0x018 ImageBase : 0x00007ff9`373b0000
+0x020 SectionAlignment : 0x1000
+0x024 FileAlignment : 0x1000
+0x028 MajorOperatingSystemVersion : 0xa
+0x02a MinorOperatingSystemVersion : 0
+0x02c MajorImageVersion : 0xa
+0x02e MinorImageVersion : 0
+0x030 MajorSubsystemVersion : 0xa
+0x032 MinorSubsystemVersion : 0
+0x034 Win32VersionValue : 0
+0x038 SizeOfImage : 0x216000
+0x03c SizeOfHeaders : 0x1000
+0x040 CheckSum : 0x21eed5
+0x044 Subsystem : 3
+0x046 DllCharacteristics : 0x4160
+0x048 SizeOfStackReserve : 0x40000
+0x050 SizeOfStackCommit : 0x1000
+0x058 SizeOfHeapReserve : 0x100000
+0x060 SizeOfHeapCommit : 0x1000
+0x068 LoaderFlags : 0
+0x06c NumberOfRvaAndSizes : 0x10
+0x070 DataDirectory : [16] _IMAGE_DATA_DIRECTORY
The important offset is0x70, the IMAGE_DATA_DIRECTORY structure. This structure holds important information for the Import Address Table, such as the RVA (Relative Virtual Address) of the Export Address Table.
The IMAGE_EXPORT_DIRECTORY structure is as follows:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, * PIMAGE_EXPORT_DIRECTORY;
Now, let’s add the following code to our C project to find the address of the API:
PDWORD getFunctionAddressByHash(char* library, DWORD hash)
{
PDWORD functionAddress = (PDWORD)0;
HMODULE libraryBase = LoadLibraryA(library);
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)libraryBase;
PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)libraryBase + dosHeader->e_lfanew);
DWORD_PTR exportDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)libraryBase + exportDirectoryRVA);
// Export edilmiş fonksiyonlarla ilgili bilgiler için RVA'ları al
PDWORD addresOfFunctionsRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfFunctions);
PDWORD addressOfNamesRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNames);
PWORD addressOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNameOrdinals);
for (DWORD i = 0; i < imageExportDirectory->NumberOfFunctions; i++)
{
DWORD functionNameRVA = addressOfNamesRVA[i];
DWORD_PTR functionNameVA = (DWORD_PTR)libraryBase + functionNameRVA;
char* functionName = (char*)functionNameVA;
DWORD_PTR functionAddressRVA = 0;
// Alınan Export fonksiyonun hash değerini hesapla
DWORD functionNameHash = getHashFromString(functionName);
// CreateThread bulunursa Adresini ekrana bastır ve main'e dön
if (functionNameHash == hash)
{
functionAddressRVA = addresOfFunctionsRVA[addressOfNameOrdinalsRVA[i]];
functionAddress = (PDWORD)((DWORD_PTR)libraryBase + functionAddressRVA);
printf("%s : 0x%x : %p\n", functionName, functionNameHash, functionAddress);
return functionAddress;
}
}
}
In the code, the getFunctionAddressByHash function finds and returns the address of an API that matches a given hash value. To do this, it loads the given library name and gets the address of the Export Address Table using the IMAGE_EXPORT_DIRECTORY structure.
Then, using the addresses in this structure, the addresses of the table containing the addresses of the exported functions and the table containing the names are retrieved. It then loops over these tables and calculates the name and hash value of each function. When a match is found, the address of the corresponding function is returned.
Full code:
#include <stdio.h>
#include <string.h>
#include <Windows.h>
typedef struct HANDLE(NTAPI* MyCreateThread)(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
DWORD getHashFromString(char* string)
{
size_t stringLength = strnlen_s(string, 50);
DWORD hash = 0x35;
for (size_t i = 0; i < stringLength; i++)
{
hash += (hash * 0xab10f29f + string[i]) & 0xffffff;
}
return hash;
}
PDWORD getFunctionAddressByHash(char* library, DWORD hash)
{
PDWORD functionAddress = (PDWORD)0;
HMODULE libraryBase = LoadLibraryA(library);
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)libraryBase;
PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)libraryBase + dosHeader->e_lfanew);
DWORD_PTR exportDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)libraryBase + exportDirectoryRVA);
// Export edilmiş fonksiyonlarla ilgili bilgiler için RVA'ları al
PDWORD addresOfFunctionsRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfFunctions);
PDWORD addressOfNamesRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNames);
PWORD addressOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNameOrdinals);
for (DWORD i = 0; i < imageExportDirectory->NumberOfFunctions; i++)
{
DWORD functionNameRVA = addressOfNamesRVA[i];
DWORD_PTR functionNameVA = (DWORD_PTR)libraryBase + functionNameRVA;
char* functionName = (char*)functionNameVA;
DWORD_PTR functionAddressRVA = 0;
// Alınan Export fonksiyonun hash değerini hesapla
DWORD functionNameHash = getHashFromString(functionName);
// CreateThread bulunursa Adresini ekrana bastır ve main'e dön
if (functionNameHash == hash)
{
functionAddressRVA = addresOfFunctionsRVA[addressOfNameOrdinalsRVA[i]];
functionAddress = (PDWORD)((DWORD_PTR)libraryBase + functionAddressRVA);
printf("%s : 0x%x : %p\n", functionName, functionNameHash, functionAddress);
return functionAddress;
}
}
}
int main()
{
DWORD hash = getHashFromString("CreateThread");
PDWORD functionAddress = getFunctionAddressByHash((char*)"kernel32", hash);
DWORD TID = 0;
HANDLE th = CreateThread(NULL, 0, NULL, NULL, 0, &TID);
if (th == NULL) {
printf("Failed to create thread\n");
return -1;
}
printf("Thread created successfully\n");
CloseHandle(th);
return 0;
}
When we look at the result, we see that the API was successfully executed:
