理解了如何通过调试事件输出当前进程中寄存器信息,那么实现加载DLL模块也会变得很容易实现,加载DLL模块主要使用LOAD_DLL_DEBUG_EVENT
这个通知事件,该事件可检测进程加载的模块信息,一旦有新模块被加载或装入那么则会触发一个通知事件,利用该方法并配合磁盘路径获取函数则可很容易的实现进程模块加载的监控。
获取加载的动态链接库DLL的详细信息,具体实现细节如下:
- 首先,代码通过
GetFileSize
函数获取目标DLL
文件的大小,如果大小为0,则立即退出函数。 - 然后,代码调用
CreateFileMapping
和MapViewOfFile
函数创建了一个内存映射对象,该映射对象可以让代码访问DLL
文件的内容。 - 随后,代码调用
GetMappedFileName
函数获取该内存映射对象关联的DLL
文件的路径,其中需要使用QueryDosDevice
查询函数来确认磁盘符号对应的真实文件名称, 如果找到对应的真实文件名称,则可以更新原始路径为真实路径,这里用到了字符串操作函数_tcslen、 _tcsnicmp以及_tcsncpy等。 - 最后,再调用
UnmapViewOfFile
和CloseHandle
函数清理资源,并将相关的信息输出到控制台上,包括基址、名称、大小和路径等信息。
有了这段获取DLL完整路径的程序片段,那么实现这个功能将变得很容易,我们看看一下OnDllLoaded
中是如何针对DLL进程处理的,实现代码片段如下所示;
void OnDllLoaded(const LOAD_DLL_DEBUG_INFO* pDebug)
{
//printf("基址: 0x%-8X --> ", pDebug->lpBaseOfDll);
BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH + 1];
HANDLE hFileMap;
// 获取文件大小
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(pDebug->hFile, &dwFileSizeHi);
//printf("长度: %9d --> ", dwFileSizeLo);
if (dwFileSizeLo == 0 && dwFileSizeHi == 0)
{
return;
}
// 创建内存映射
hFileMap = CreateFileMapping(pDebug->hFile, 0, PAGE_READONLY, 0, 1, 0);
if (hFileMap)
{
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (pMem)
{
// 获取当前映射名称
if (GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH))
{
TCHAR szTemp[4096] = { 0 };
// 得到当前所有磁盘字符串
if (GetLogicalDriveStrings(4096 - 1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
*szDrive = *p;
if (QueryDosDevice(szDrive, szName, 4096))
{
UINT uNameLen = _tcslen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _tcsnicmp(pszFilename, szName, uNameLen) == 0;
if (bFound)
{
TCHAR szTempFile[MAX_PATH];
_stprintf(szTempFile, TEXT("%s%s"), szDrive, pszFilename + uNameLen);
_tcsncpy(pszFilename, szTempFile, MAX_PATH);
}
}
}
while (*p++);
} while (!bFound && *p);
}
}
bSuccess = TRUE;
UnmapViewOfFile(pMem);
}
CloseHandle(hFileMap);
}
printf("基址: 0x%08X \t 相对名称: %-30s \t 大小: %-9d \t 路径: %s \n",
pDebug->lpBaseOfDll, GetBaseName(pszFilename), dwFileSizeLo, pszFilename);
}
上述程序被运行后,则可输出被加载进程的所有模块信息,其中包括了模块基址,相对名称,模块大小,模块完整路径等,输出效果如下图所示;
本文作者: 王瑞
本文链接: https://www.lyshark.com/post/52d14ea1.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!