Last active
October 4, 2023 00:47
-
-
Save rad9800/ccfbf5f085aff2218699d92d354fe91e to your computer and use it in GitHub Desktop.
Using macros and constexpr to make API hashing a bit more friendly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <winternl.h> | |
#pragma comment(linker, "/ENTRY:entry") | |
// Define hashing algorithm to use | |
#define HASHALGO HashStringDjb2 | |
// Define how large you'd like cache to be | |
#define CACHE 50 | |
#pragma region hashes | |
// https://github.com/vxunderground/VX-API/blob/main/VX-API/MalwareStrings.h | |
#pragma region HashStringDjb2 | |
constexpr DWORD HashStringDjb2(const char* String) | |
{ | |
ULONG Hash = 5381; | |
INT c = 0; | |
while ((c = *String++)) { | |
Hash = ((Hash << 5) + Hash) + c; | |
} | |
return Hash; | |
} | |
constexpr DWORD HashStringDjb2(const wchar_t* String) | |
{ | |
ULONG Hash = 5381; | |
INT c = 0; | |
while ((c = *String++)) { | |
Hash = ((Hash << 5) + Hash) + c; | |
} | |
return Hash; | |
} | |
#pragma endregion | |
#pragma region HashStringFowlerNollVoVariant1a | |
constexpr ULONG HashStringFowlerNollVoVariant1a(const char* String) | |
{ | |
ULONG Hash = 0x811c9dc5; | |
while (*String) | |
{ | |
Hash ^= (UCHAR)*String++; | |
Hash *= 0x01000193; | |
} | |
return Hash; | |
} | |
constexpr ULONG HashStringFowlerNollVoVariant1a(const wchar_t* String) | |
{ | |
ULONG Hash = 0x811c9dc5; | |
while (*String) | |
{ | |
Hash ^= (UCHAR)*String++; | |
Hash *= 0x01000193; | |
} | |
return Hash; | |
} | |
#pragma endregion | |
#pragma endregion | |
#pragma region macros | |
#define TOKENIZE( x ) #x | |
#define CONCAT( x , y) x##y | |
#define hash( VAL ) constexpr auto CONCAT( hash, VAL ) = HASHALGO( TOKENIZE( VAL ) ); | |
#define dllhash(DLL, VAL ) constexpr auto CONCAT( hash, DLL ) = HASHALGO( VAL ); | |
#define hashFunc( FUNCNAME , RETTYPE, ...) \ | |
hash( FUNCNAME ) typedef RETTYPE( WINAPI* CONCAT( type, FUNCNAME ) )( __VA_ARGS__ ); | |
#define API( DLL, FUNCNAME ) ( ( CONCAT( type, FUNCNAME ))GetProcAddrH( CONCAT( hash, DLL ) ,\ | |
CONCAT( hash,FUNCNAME ) ) ) | |
#pragma region dlls | |
dllhash(KERNEL32, L"KERNEL32.DLL") | |
dllhash(NTDLL, L"ntdll.dll") | |
dllhash(KERNELBASE, L"KERNELBASE.dll") | |
dllhash(UCRTBASE, L"ucrtbase.dll") | |
#pragma endregion | |
#pragma region functions | |
// Example functions funcName , rettype , parameters | |
//hashFunc(NtWriteVirtualMemory, NTSTATUS, HANDLE, PVOID, PVOID, ULONG, PULONG) | |
hashFunc(VirtualProtect, BOOL, LPVOID, SIZE_T, DWORD, PDWORD) | |
hashFunc(VirtualAlloc, LPVOID, LPVOID, SIZE_T, DWORD, DWORD); | |
hashFunc(CreateProcessA, BOOL, LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); | |
hashFunc(LoadLibraryA, HMODULE, LPCSTR); | |
hashFunc(LoadLibraryW, HMODULE, LPCWSTR); | |
hashFunc(CloseHandle, BOOL, HANDLE); | |
#pragma endregion | |
#pragma endregion | |
#pragma function(memset) | |
void* memset(void* dest, int c, size_t count); | |
__forceinline char Upper(char c); | |
void* GetProcAddrH(UINT moduleHash, UINT funcHash); | |
void InitModules(); | |
struct HashStruct | |
{ | |
UINT Hash; | |
PVOID addr; | |
}; | |
HashStruct ModuleHashes[] = | |
{ | |
{ hashNTDLL , nullptr }, | |
{ hashKERNEL32 , nullptr }, | |
{ hashKERNEL32 , nullptr }, | |
{ hashKERNELBASE , nullptr }, | |
{ hashUCRTBASE , nullptr }, | |
}; | |
#ifdef CACHE | |
HashStruct HashCache[CACHE]; | |
DWORD hashPointer; | |
#endif | |
int entry() | |
{ | |
InitModules(); | |
STARTUPINFOA si = { sizeof(si) }; | |
PROCESS_INFORMATION pi; | |
API(KERNEL32, CreateProcessA)(nullptr, (LPSTR)R"(c:\windows\system32\calc.exe)", nullptr, nullptr, 0, 0, nullptr, nullptr, &si, &pi); | |
API(KERNEL32, CloseHandle)(pi.hProcess); | |
API(KERNEL32, CloseHandle)(pi.hThread); | |
return 0; | |
} | |
void* GetProcAddrH(UINT moduleHash, UINT funcHash) | |
{ | |
void* base = nullptr; | |
for (auto i : ModuleHashes) { | |
if (i.Hash == moduleHash) { | |
base = i.addr; | |
} | |
} | |
if (base == nullptr) { | |
return nullptr; | |
} | |
#ifdef CACHE | |
for (auto i = 0; i < CACHE; i++) | |
{ | |
if (funcHash == HashCache[i].Hash) { | |
return HashCache[i].addr; | |
} | |
} | |
#endif | |
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)base; | |
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PBYTE)base + dos->e_lfanew); | |
PIMAGE_EXPORT_DIRECTORY exports = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)base + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); | |
if (exports->AddressOfNames != 0) | |
{ | |
auto ordinals = (PWORD)((PBYTE)base + exports->AddressOfNameOrdinals); | |
auto names = (PDWORD)((PBYTE)base + exports->AddressOfNames); | |
auto functions = (PDWORD)((PBYTE)base + exports->AddressOfFunctions); | |
for (auto i = 0; i < exports->NumberOfNames; i++) { | |
auto name = (LPSTR)((PBYTE)base + names[i]); | |
if (HASHALGO(name) == funcHash) { | |
auto function = ((PBYTE)base + functions[ordinals[i]]); | |
#ifdef CACHE | |
// Cache result | |
HashCache[hashPointer % CACHE].addr = function; | |
HashCache[hashPointer % CACHE].Hash = funcHash; | |
hashPointer = (hashPointer + 1) % CACHE; | |
#endif | |
return function; | |
} | |
} | |
} | |
return nullptr; | |
} | |
void InitModules() | |
{ | |
PEB* peb = NtCurrentTeb()->ProcessEnvironmentBlock; | |
LIST_ENTRY* head = &peb->Ldr->InMemoryOrderModuleList; | |
LIST_ENTRY* next = head->Flink; | |
while (next != head) | |
{ | |
LDR_DATA_TABLE_ENTRY* entry = (LDR_DATA_TABLE_ENTRY*)((PBYTE)next - offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks)); | |
UNICODE_STRING* fullname = &entry->FullDllName; | |
UNICODE_STRING* basename = (UNICODE_STRING*)((PBYTE)fullname + sizeof(UNICODE_STRING)); | |
char name[64]; | |
if (basename->Length < sizeof(name) - 1) | |
{ | |
int i = 0; | |
while (basename->Buffer[i] && i < sizeof(name) - 1) | |
{ | |
name[i] = Upper((char)basename->Buffer[i]); // can never be sure so uppercase | |
i++; | |
} | |
name[i] = 0; | |
UINT hash = HASHALGO(name); | |
for (auto& i : ModuleHashes) { | |
if (i.Hash == hash) { | |
i.addr = entry->DllBase; | |
} | |
} | |
} | |
next = next->Flink; | |
} | |
#ifdef CACHE | |
RtlSecureZeroMemory(HashCache, sizeof(HashCache)); | |
hashPointer = 0; | |
#endif | |
} | |
char Upper(char c) | |
{ | |
if (c >= 'a' && c <= 'z') { | |
return c - 'a' + 'A'; | |
} | |
return c; | |
} | |
void* memset(void* dest, int c, size_t count) | |
{ | |
char* bytes = (char*)dest; | |
while (count--) | |
{ | |
*bytes++ = (char)c; | |
} | |
return dest; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment