导语:我们该如何获得没有被Windows NT内核所导出的函数和结构体的虚拟地址呢?
获得没有被Windows NT内核所导出的函数和结构体的虚拟地址
众所周知,许多函数和结构体都没有被Windows NT内核所导出,例如PsGetNextProcess函数、KeServiceDescriptorTable等等。那么,我们该如何获得这些函数和结构体的虚拟地址呢?
为此,我们可以通过模式匹配来查找函数内部的特定函数或结构体,但是这种查找方式并不可靠,因为微软一旦对这些函数或结构体进行了修改,原来的模式匹配算法就失效了。
那么,使用Microsoft的Debug Help Library怎么样呢?
我们可以利用它来访问可执行文件(如%systemroot%/system32/ntoskrnl.exe)的符号调试信息,提取所需函数/结构体的RVA并添加到nt的地址。
比如,可以使用Psapi的EnumDeviceDrivers获取ntoskrnl.exe的地址,使用SymFromName获取函数/结构体的符号信息。
如果目标系统没有任何含有调试信息的可执行文件,如symchk.exe,SymSrv.dll等。那么,要获取包含ntoskrnl.exe调试信息的.pdb文件,则需要使用symchk.exe手动下载:
我们可以在Windows 10的C:\Program Files (x86)\Windows Kits\10\Debuggers\x64目录下找到symchk.exe,它会用到SymbolCheck.dll、SymSrv.dll和DbgHelp.dll。
将所有必要的文件嵌入到一个可执行文件,并在运行时将其提取出来是一个不错的选择。我们需要用到下列可执行文件:DbgHelp.dll、SymbolCheck.dll、symchk.exe和SymSrv.dll
示例源代码如下所示:
// Get ntoskrn.exe address at run-time VOID* krnlAddr; // First element of the array is base address of ntoskrnl.exe/nt DWORD retLen; if (!EnumDeviceDrivers(&krnlAddr, sizeof(krnlAddr), &retLen)) return GetLastError(); HANDLE procHandle = GetCurrentProcess(); if (!SymInitialize(procHandle, nullptr, FALSE)) { auto er = GetLastError(); printf_s("SymInitialize error: %d\n", er); return er; } ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; STARTUPINFO sa{ sizeof(STARTUPINFO)}; PROCESS_INFORMATION pi{}; TCHAR execSrv[] = L"./symchk.exe /v c:\\Windows\\System32\\ntoskrnl.exe /oc ."; if (!CreateProcess(nullptr, execSrv, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &sa, &pi)) { return GetLastError(); } WaitForSingleObject(pi.hProcess, INFINITE); pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; CHAR symName[MAX_SYM_NAME] = "KeServiceDescriptorTable"; // change it auto baseAddr = SymLoadModuleEx(procHandle, nullptr, "%systemroot%\\system32\\ntoskrnl.exe", nullptr, 0, 0, nullptr, 0); if (!SymFromName(procHandle, symName, pSymbol)) { auto er = GetLastError(); if (er == ERROR_MOD_NOT_FOUND) puts("The specified module could not be found\n"); printf_s("SymFromName error: %d\n", er); return er; } auto symAddr = (pSymbol->Address - baseAddr) + (DWORD64)krnlAddr; printf_s("Virtual address of %s: 0x%llx\n", symName, symAddr); SymCleanup(procHandle);
本方法的优点:
· 在正确的环境下,我们能得到准确的信息
· 跨平台
本方法的缺点:
· 我们需要用到用户模式进程
· 我们需要互联网连接
· 由于包含多个可执行文件,用户模式应用程序占用空间比较大
感谢您阅读本文…