mirror of
https://github.com/helpsystems/nanodump
synced 2026-06-08 14:38:00 +00:00
275 lines
7.3 KiB
C
275 lines
7.3 KiB
C
#include "dinvoke.h"
|
|
|
|
// used by spoof_callstack
|
|
PVOID find_dll_by_pointer(
|
|
IN PVOID address)
|
|
{
|
|
PND_PEB Peb = (PND_PEB)READ_MEMLOC(PEB_OFFSET);
|
|
PND_PEB_LDR_DATA Ldr = Peb->Ldr;
|
|
PVOID FirstEntry = &Ldr->InLoadOrderModuleList.Flink;
|
|
PND_LDR_DATA_TABLE_ENTRY Entry = (PND_LDR_DATA_TABLE_ENTRY)Ldr->InLoadOrderModuleList.Flink;
|
|
|
|
do
|
|
{
|
|
if ((ULONG_PTR)address >= (ULONG_PTR)Entry->DllBase &&
|
|
(ULONG_PTR)address < RVA(ULONG_PTR, Entry->DllBase, Entry->SizeOfImage))
|
|
return Entry->DllBase;
|
|
|
|
Entry = (PND_LDR_DATA_TABLE_ENTRY)Entry->InLoadOrderLinks.Flink;
|
|
} while (Entry != FirstEntry);
|
|
|
|
DPRINT_ERR("Could not find the target DLL from the pointer 0x%p", address);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Check that hLibrary is indeed a DLL and not something else
|
|
*/
|
|
BOOL is_dll(
|
|
IN HMODULE hLibrary)
|
|
{
|
|
PIMAGE_DOS_HEADER dos;
|
|
PIMAGE_NT_HEADERS nt;
|
|
|
|
if (!hLibrary)
|
|
return FALSE;
|
|
|
|
dos = (PIMAGE_DOS_HEADER)hLibrary;
|
|
|
|
// check the MZ magic bytes
|
|
if (dos->e_magic != MZ)
|
|
return FALSE;
|
|
|
|
nt = RVA(PIMAGE_NT_HEADERS, hLibrary, dos->e_lfanew);
|
|
|
|
// check the NT_HEADER signature
|
|
if (nt->Signature != IMAGE_NT_SIGNATURE)
|
|
return FALSE;
|
|
|
|
// check that it is a DLL and not a PE
|
|
USHORT Characteristics = nt->FileHeader.Characteristics;
|
|
if ((Characteristics & IMAGE_FILE_DLL) != IMAGE_FILE_DLL)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Look among all loaded DLLs for an export with certain function hash
|
|
*/
|
|
PVOID find_legacy_export(
|
|
IN HMODULE hOriginalLibrary,
|
|
IN DWORD fhash)
|
|
{
|
|
PVOID addr;
|
|
PND_PEB Peb = (PND_PEB)READ_MEMLOC(PEB_OFFSET);
|
|
PND_PEB_LDR_DATA Ldr = Peb->Ldr;
|
|
PVOID FirstEntry = &Ldr->InLoadOrderModuleList.Flink;
|
|
PND_LDR_DATA_TABLE_ENTRY Entry = (PND_LDR_DATA_TABLE_ENTRY)Ldr->InLoadOrderModuleList.Flink;
|
|
|
|
for (; Entry != FirstEntry; Entry = (PND_LDR_DATA_TABLE_ENTRY)Entry->InLoadOrderLinks.Flink)
|
|
{
|
|
// avoid looking in the DLL that brought us here
|
|
if (Entry->DllBase == hOriginalLibrary)
|
|
continue;
|
|
|
|
// check if this DLL has an export with the function hash we are looking for
|
|
addr = get_function_address(
|
|
Entry->DllBase,
|
|
fhash,
|
|
0);
|
|
if (!addr)
|
|
continue;
|
|
|
|
return addr;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Follow the reference and return the real address of the function
|
|
*/
|
|
PVOID resolve_reference(
|
|
IN HMODULE hOriginalLibrary,
|
|
IN PVOID addr)
|
|
{
|
|
HANDLE hLibrary;
|
|
PVOID new_addr;
|
|
LPCSTR api;
|
|
|
|
// addr points to a string like: NewLibrary.NewFunctionName
|
|
api = &strrchr(addr, '.')[1];
|
|
DWORD dll_length = (DWORD)((ULONG_PTR)api - (ULONG_PTR)addr);
|
|
char dll[MAX_PATH + 1] = {0};
|
|
strncpy(dll, (LPCSTR)addr, dll_length);
|
|
strncat(dll, "dll", MAX_PATH);
|
|
wchar_t wc_dll[MAX_PATH] = {0};
|
|
mbstowcs(wc_dll, dll, MAX_PATH);
|
|
|
|
// try to find the library NewLibrary
|
|
hLibrary = get_library_address(wc_dll, FALSE);
|
|
if (!hLibrary)
|
|
{
|
|
// the library is not loaded, meaning it is a legacy DLL
|
|
new_addr = find_legacy_export(
|
|
hOriginalLibrary,
|
|
SW2_HashSyscall(api));
|
|
|
|
return new_addr;
|
|
}
|
|
|
|
// get the address of NewFunction in NewLibrary
|
|
new_addr = get_function_address(
|
|
hLibrary,
|
|
SW2_HashSyscall(api),
|
|
0);
|
|
|
|
return new_addr;
|
|
}
|
|
|
|
/*
|
|
* Find an export in a DLL
|
|
*/
|
|
PVOID get_function_address(
|
|
IN HMODULE hLibrary,
|
|
IN DWORD fhash,
|
|
IN WORD ordinal)
|
|
{
|
|
PIMAGE_DOS_HEADER dos;
|
|
PIMAGE_NT_HEADERS nt;
|
|
PIMAGE_DATA_DIRECTORY data;
|
|
PIMAGE_EXPORT_DIRECTORY exp;
|
|
DWORD exp_size;
|
|
PDWORD adr;
|
|
PDWORD sym;
|
|
PWORD ord;
|
|
LPCSTR api;
|
|
PVOID addr;
|
|
|
|
if (!is_dll(hLibrary))
|
|
return NULL;
|
|
|
|
dos = (PIMAGE_DOS_HEADER)hLibrary;
|
|
nt = RVA(PIMAGE_NT_HEADERS, hLibrary, dos->e_lfanew);
|
|
data = (PIMAGE_DATA_DIRECTORY)nt->OptionalHeader.DataDirectory;
|
|
|
|
if (!data->Size || !data->VirtualAddress)
|
|
return NULL;
|
|
|
|
exp = RVA(PIMAGE_EXPORT_DIRECTORY, hLibrary, data->VirtualAddress);
|
|
exp_size = data[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
|
|
|
|
adr = RVA(PDWORD, hLibrary, exp->AddressOfFunctions);
|
|
sym = RVA(PDWORD, hLibrary, exp->AddressOfNames);
|
|
ord = RVA(PWORD, hLibrary, exp->AddressOfNameOrdinals);
|
|
|
|
addr = NULL;
|
|
if (fhash)
|
|
{
|
|
// iterate over all the exports
|
|
for (DWORD i = 0; i < exp->NumberOfNames; i++)
|
|
{
|
|
api = RVA(LPCSTR, hLibrary, sym[i]);
|
|
//addr = RVA(PVOID, hLibrary, adr[ord[i]]);
|
|
//DPRINT("%lx -> %s -> 0x%llx", fhash, api, (ULONG_PTR)addr-(ULONG_PTR)hLibrary);
|
|
if (fhash == SW2_HashSyscall(api))
|
|
{
|
|
addr = RVA(PVOID, hLibrary, adr[ord[i]]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
addr = RVA(PVOID, hLibrary, adr[ordinal - exp->Base]);
|
|
}
|
|
if (!addr)
|
|
return NULL;
|
|
|
|
// check if addr is a pointer to another function in another DLL
|
|
if ((ULONG_PTR)addr >= (ULONG_PTR)exp &&
|
|
(ULONG_PTR)addr < RVA(ULONG_PTR, exp, exp_size))
|
|
{
|
|
// the function seems to be defined somewhere else
|
|
addr = resolve_reference(
|
|
hLibrary,
|
|
addr);
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* Get the base address of a DLL
|
|
*/
|
|
HANDLE get_library_address(
|
|
IN LPWSTR lib_path,
|
|
IN BOOL DoLoad)
|
|
{
|
|
PND_PEB Peb = (PND_PEB)READ_MEMLOC(PEB_OFFSET);
|
|
PND_PEB_LDR_DATA Ldr = Peb->Ldr;
|
|
PVOID FirstEntry = &Ldr->InLoadOrderModuleList.Flink;
|
|
PND_LDR_DATA_TABLE_ENTRY Entry = (PND_LDR_DATA_TABLE_ENTRY)Ldr->InLoadOrderModuleList.Flink;
|
|
BOOL is_full_path = wcsrchr(lib_path, '\\') ? TRUE : FALSE;
|
|
|
|
do
|
|
{
|
|
if (is_full_path)
|
|
{
|
|
// the dll name was provided
|
|
if (!_wcsicmp(lib_path, Entry->FullDllName.Buffer))
|
|
return Entry->DllBase;
|
|
}
|
|
else
|
|
{
|
|
// the full path was provided
|
|
if (!_wcsicmp(lib_path, Entry->BaseDllName.Buffer))
|
|
return Entry->DllBase;
|
|
}
|
|
|
|
Entry = (PND_LDR_DATA_TABLE_ENTRY)Entry->InLoadOrderLinks.Flink;
|
|
} while (Entry != FirstEntry);
|
|
|
|
if (!DoLoad)
|
|
return NULL;
|
|
|
|
// the library is not currently loaded
|
|
// get the address of LdrLoadDll
|
|
LdrLoadDll_t LdrLoadDll = (LdrLoadDll_t)(ULONG_PTR)get_function_address(
|
|
get_library_address(NTDLL_DLL, FALSE),
|
|
LdrLoadDll_SW2_HASH,
|
|
0);
|
|
if (!LdrLoadDll)
|
|
{
|
|
api_not_found("LdrLoadDll");
|
|
return NULL;
|
|
}
|
|
|
|
// create a UNICODE_STRING with the library name
|
|
UNICODE_STRING ModuleFileName = { 0 };
|
|
ModuleFileName.Buffer = lib_path;
|
|
ModuleFileName.Length = (USHORT)wcsnlen(ModuleFileName.Buffer, MAX_PATH);
|
|
ModuleFileName.Length *= 2;
|
|
ModuleFileName.MaximumLength = ModuleFileName.Length + 2;
|
|
|
|
// load the library
|
|
HANDLE hLibrary = NULL;
|
|
NTSTATUS status = LdrLoadDll(
|
|
NULL,
|
|
0,
|
|
&ModuleFileName,
|
|
&hLibrary);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
DPRINT_ERR(
|
|
"Failed to load %ls, status: 0x%lx\n",
|
|
lib_path,
|
|
status);
|
|
return NULL;
|
|
}
|
|
DPRINT("Loaded %ls at 0x%p", lib_path, hLibrary);
|
|
|
|
return hLibrary;
|
|
}
|