Skip to content

Instantly share code, notes, and snippets.

@rad9800
Last active October 4, 2023 00:47
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save rad9800/ccfbf5f085aff2218699d92d354fe91e to your computer and use it in GitHub Desktop.
Save rad9800/ccfbf5f085aff2218699d92d354fe91e to your computer and use it in GitHub Desktop.
Using macros and constexpr to make API hashing a bit more friendly
#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