zoukankan      html  css  js  c++  java
  • 学习自建调试体系(二)

    这一部分的代码的作用主要是用于枚举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);		//说明函数1
    	if (!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(			//说明函数2
    			hProcess,
    			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(								//说明函数3
    			hProcess,
    			dw64ModAddress,
    			NULL,
    			EnumSymCallBack,  								//说明函数4
    			NULL))
    		{
    			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.

  • 相关阅读:
    数值分析实验之常微分方程的数值解法(MATLAB实现)
    Hadoop 伪分布模式安装
    Word 如何标齐选项
    在windows上安装elasticsearch7.6
    《转载》python/人工智能/Tensorflow/自然语言处理/计算机视觉/机器学习学习资源分享
    使用fastai训练的一个性别识别模型
    小程序录音、上传、转格式、语音识别爬坑记(微信版本:6.6.7及以上)
    centos 7.0 下安装FFmpeg软件 过程
    用python实现matlib的 生成高斯模糊核
    opencv如何在jupyter notebook中显示图片
  • 原文地址:https://www.cnblogs.com/Lnju/p/5413868.html
Copyright © 2011-2022 走看看