目录
- 获取模块信息
- NtQuerySystemInformation
- RtlMoveMemory
- 枚举符号
- SymInitialize
- SymLoadModule64
- SymEnumSymbolsW
- 回调函数EnumSymbolsCallback
这一部分的代码的作用主要是用于枚举NT内核模块的符号文件,很多都是常规写法(同一个套路),来做个总结,以后肯定是会再见到的。
获取模块信息
/*获取模块信息到SysModInfo结构
*/
BOOL DbgGetSysModuleInfo(PSYSTEM_MODULE SysModInfo)
{ULONG ulInfoLen = 0;NTSTATUS Status;SYSTEM_MODULE_INFORMATION* pStLoaderInfo = NULL;if (SysModInfo == NULL){return FALSE;}// 套路1// 2次调用NtQuerySystemInforamtion获取系统模块信息。// 首次调用获取模块信息长度,作为第二次调用的参数// 再次调用才是真正的获取信息// NtQuerySystemInformation下文有详细解释NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &ulInfoLen);if (!ulInfoLen){return STATUS_UNSUCCESSFUL;}pStLoaderInfo = (SYSTEM_MODULE_INFORMATION*)malloc(ulInfoLen + sizeof(ULONG));//真正获取系统模块信息Status = NtQuerySystemInformation(SystemModuleInformation, (PVOID)pStLoaderInfo,ulInfoLen, &ulInfoLen);if (!NT_SUCCESS(Status)){if (pStLoaderInfo){free(pStLoaderInfo);}return Status;}/*typedef struct _SYSTEM_MODULE_INFORMATION{ULONG uCount; //模块个数SYSTEM_MODULE aSM[]; //模块数组}*/if (pStLoaderInfo->aSM == NULL){return STATUS_UNSUCCESSFUL;}//pStLoaderInfo->aSM[0] 第一个模块就是NT内核模块//驱动级的内存复制 RtlMoveMemory(SysModInfo, &pStLoaderInfo->aSM[0], sizeof(SYSTEM_MODULE));if (pStLoaderInfo){free(pStLoaderInfo);}return TRUE;
}
NtQuerySystemInformation
NtQuerySystemInformation用于获取多种系统信息中的其中之一。
NTSTATUS
NtQuerySystemInformation(IN SYSTEMINFOCLASS SystemInformationClass, //指定获取哪种系统信息,枚举值OUT PVOID pSystemInformation, //buffer指针,用于接收请求的信息IN ULONG uSystemInformationLength, //buffer的长度OUT PULONG puReturnLength OPTIONAL //函数实际返回的系统信息占用的长度);
RtlMoveMemory
//内存复制VOID RtlMoveMemory(_Out_ VOID UNALIGNED *Destination,_In_ const VOID UNALIGNED *Source,_In_ SIZE_T Length);
枚举符号
枚举符号的过程感觉像是实现windbg从上游符号服务器获得符号文件的过程。很多地方能用到,IDA有时候也要符号文件,感觉也是很实用的套路。
DbgObjEnumSymbols(VOID)
{CHAR FilePath[512] = { 0 };CHAR Ntpath[512] = { 0 };CHAR pSymPath[512] = { 0 };CHAR SymFileName[512] = { 0 };wchar_t str[256] = { 0 };wchar_t SzOldEnvVar[512] = { 0 };SYSTEM_MODULE SysModInfo;HANDLE hProcess;HANDLE hFile;BOOL bRet = TRUE;DWORD dwFileSize = 0;//文件大小////------------------------------------获得NT模块的信息//if (!DbgGetSysModuleInfo(&SysModInfo)) {return FALSE;}BaseOfDll = SysModInfo.Base;////----------------------------------获取系统内核文件路径//GetSystemDirectoryA(Ntpath, sizeof(Ntpath));strcat_s(Ntpath, sizeof(Ntpath), "\\");strcat_s(Ntpath, sizeof(Ntpath), SysModInfo.ImageName + SysModInfo.ModuleNameOffset);OutputDebugStringA(Ntpath);////-----------------------------------获取当前程序绝对路径//DbgGetModuleFilePathA(FilePath, sizeof(FilePath));sprintf_s(pSymPath, sizeof(pSymPath), "srv*%ssymbols*http://msdl.microsoft.com/download/symbols", FilePath); OutputDebugStringA(pSymPath);////------------------------------------ 设置符号选项//SymSetOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME);////------------------------------------初始化符号库//hProcess = GetCurrentProcess();bRet = SymInitialize(hProcess, pSymPath, TRUE); //说明函数1if (!bRet){swprintf_s(str, SizeOf(str), _T("SymInitialize bRet:%d"), bRet);OutputDebugString(str);return FALSE;}OutputDebugString(_T("初始化符号库成功!"));////---------------------------------得到内核文件大小dwFileSize//hFile = CreateFileA(Ntpath,GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);if (INVALID_HANDLE_VALUE == hFile){swprintf_s(str, SizeOf(str), _T("CreateFile hFile:%d"), hFile);OutputDebugString(str);return FALSE;}if (INVALID_FILE_SIZE == (dwFileSize = GetFileSize(hFile, NULL))){swprintf_s(str, SizeOf(str), _T("GetFileSize dwFileSize:%d"), dwFileSize);OutputDebugString(str);return FALSE;}CloseHandle(hFile);__try{////调用SymLoadModule函数载入对应符号库-----------------------------------------------//DWORD64 dw64ModAddress = SymLoadModule64( //说明函数2hProcess,NULL,Ntpath,NULL,(DWORD64)SysModInfo.Base,dwFileSize);if (dw64ModAddress == 0){swprintf_s(str, SizeOf(str), _T("SymLoadModule64 dw64ModAddress:%ld"), dw64ModAddress);OutputDebugString(str);return FALSE;}////使用SymEnumSymbols函数枚举模块中的符号////学习点4/*枚举符号,它既可以枚举全局的符号,也可以枚举当前作用域内的符号BOOL WINAPI SymEnumSymbols(_In_ HANDLE hProcess, // 符号处理器的标识符,之前传递给SymInitialize函数_In_ ULONG64 BaseOfDll, // 指定模块的基地址_In_opt_ PCTSTR Mask, // 字符串,只有名称与该字符串匹配的符号才会被枚举,在字符串中允许使用通配符*和?_In_ PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, // 对于每个被枚举的符号都会调用该函数_In_opt_ const PVOID UserContext //用于传递给EnumSymbolsCallback函数的UserContext参数);*/OutputDebugString(_T("使用SymEnumSymbols函数枚举模块中的符号!"));if (!SymEnumSymbolsW( //说明函数3hProcess,dw64ModAddress,NULL,EnumSymCallBack, //说明函数4NULL)){return FALSE;}SymUnloadModule64(hProcess, dw64ModAddress);SymCleanup(hProcess);bRet = TRUE;}__except (EXCEPTION_EXECUTE_HANDLER) {bRet = FALSE;}return bRet;
}
SymInitialize
初始化进程所对应的符号处理程序
BOOL
IMAGEAPI
SymInitialize(__in HANDLE hProcess, //标识调用者的句柄,这个值必须是唯一且非0的数字,但是不需要是一个真正的进程句柄。如果用了进程句柄,那么一定保证句柄值的正确性。如果程序是一个调试器,传递进程句柄要传被调试进程的句柄,而不要传GetCurrentProcess返回值。__in_opt PCSTR UserSearchPath, //用来搜索符号文件的路径,用;分隔__in BOOL fInvadeProcess //如果为真,枚举进程被加载的模块,对每个模块调用SymLoadModule64函数);
对于第三个参数如果为False,那么SymInitialize只是创建一个符号处理器,不加载任何模块的调试符号,此时需要我们自己调用SymLoadModule64函数来加载模块;如果为TRUE,SymInitialize会遍历进程的所有模块,并加载其调试符号,所以在这种情况下hProcess必须是一个有效的进程句柄。
SymLoadModule64
装载指定模块的符号表
DWORD64
IMAGEAPI
SymLoadModule64(__in HANDLE hProcess, //保证和SymInitialize参数1相同__in_opt HANDLE hFile, __in_opt PCSTR ImageName, //文件路径__in_opt PCSTR ModuleName, //可选参数__in DWORD64 BaseOfDll, //模块基地址__in DWORD SizeOfDll //文件大小);
参数二 传递NULL说明不使用文件句柄。一个文件句柄。MSDN说第二个参数多被调试器程序使用,调试器可以传递从debugging event中获取到的句柄值。
SymEnumSymbolsW
BOOL
IMAGEAPI
SymEnumSymbolsW(__in HANDLE hProcess, //和之前一样,不多说__in ULONG64 BaseOfDll, //SymLoadModule64的返回值__in_opt PCWSTR Mask, //字符串,只有名称与该字符串匹配的符号才会被枚举,在字符串中允许使用通配符*和?,我们代码这里传NULL__in PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,//对于每个被枚举的符号都会调用该函数__in_opt PVOID UserContext //用于EnumSymbolsCallback的参数,我们代码里传递的NULL);
回调函数EnumSymbolsCallback
typedef BOOL
(CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACKW)(__in PSYMBOL_INFOW pSymInfo, //指向PSYMBOL_INFO结构的指针,提供关于符号的信息__in ULONG SymbolSize, //符号大小__in_opt PVOID UserContext //可选参数);
返回值:
If the function returns TRUE, the enumeration will continue.
If the function returns FALSE, the enumeration will stop.