10.2 调试事件获取DLL装载

调试,事件,获取,dll,装载 · 浏览次数 : 14

小编点评

**实现进程模块加载的步骤:** 1. 获取目标 DLL 文件的大小。 2. 创建内存映射对象,并将其映射到进程内存。 3. 获取内存映射对象关联的 DLL 文件路径。 4. 清理内存资源,关闭文件映射。 5. 输出加载的模块信息,包括基址、名称、大小和路径等。 **代码片段:** ```c++ void OnDllLoaded(const LOAD_DLL_DEBUG_INFO* pDebug) { // 打印加载模块的信息 printf("基址: 0x%-8X --> ", pDebug->lpBaseOfDll); // 获取模块名称 TCHAR pszFilename[MAX_PATH + 1]; if (GetMappedFileName(GetCurrentProcess(), pDebug->lpBaseOfDll, pszFilename, MAX_PATH)) { // 获取当前所有磁盘字符串 // ... } // 打印模块大小 printf("大小: %-9d --> ", pDebug->dwFileSizeLo); } ``` **输出效果:** ``` 基址: 0x0804E50 --> 相对名称: - -模块大小: 1234 --> 路径: D:\path\to\module.dll ``` **注意:** * `GetMappedFileName()` 函数需要包含字符串操作库。 * `QueryDosDevice()` 函数需要包含 Windows 公共头文件。

正文

理解了如何通过调试事件输出当前进程中寄存器信息,那么实现加载DLL模块也会变得很容易实现,加载DLL模块主要使用LOAD_DLL_DEBUG_EVENT这个通知事件,该事件可检测进程加载的模块信息,一旦有新模块被加载或装入那么则会触发一个通知事件,利用该方法并配合磁盘路径获取函数则可很容易的实现进程模块加载的监控。

获取加载的动态链接库DLL的详细信息,具体实现细节如下:

  • 首先,代码通过GetFileSize函数获取目标DLL文件的大小,如果大小为0,则立即退出函数。
  • 然后,代码调用CreateFileMappingMapViewOfFile函数创建了一个内存映射对象,该映射对象可以让代码访问DLL文件的内容。
  • 随后,代码调用GetMappedFileName函数获取该内存映射对象关联的DLL文件的路径,其中需要使用QueryDosDevice查询函数来确认磁盘符号对应的真实文件名称, 如果找到对应的真实文件名称,则可以更新原始路径为真实路径,这里用到了字符串操作函数_tcslen、 _tcsnicmp以及_tcsncpy等。
  • 最后,再调用UnmapViewOfFileCloseHandle函数清理资源,并将相关的信息输出到控制台上,包括基址、名称、大小和路径等信息。

有了这段获取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 许可协议。转载请注明出处!

与10.2 调试事件获取DLL装载相似的内容:

10.2 调试事件获取DLL装载

理解了如何通过调试事件输出当前进程中寄存器信息,那么实现加载DLL模块也会变得很容易实现,加载DLL模块主要使用`LOAD_DLL_DEBUG_EVENT`这个通知事件,该事件可检测进程加载的模块信息,一旦有新模块被加载或装入那么则会触发一个通知事件,利用该方法并配合磁盘路径获取函数则可很容易的实现进程模块加载的监控。

操作系统开发:BIOS/MBR基础与调试

这里在实验之前需要下载 Bochs-win32-2.6.11 作者使用的是Linux版本的,在Linux写代码不太舒服,所以最好在Windows上做实验,下载好虚拟机以后还需要下载Nasm汇编器,以及GCC编译器,为了能够使用DD命令实现磁盘拷贝,这里你可以安装windows 10 下面的子系统Ub

测试员最佳跳槽频率是多少?进来看看你是不是符合

最近笔者刷到一则消息,一位测试员在某乎上分享,从月薪5K到如今的20K,他总共跳了10次槽,其中还经历过两次劳动申诉,拿到了大几万的赔偿,被同事们称为“职场碰瓷人”。 虽说这种依靠跳槽式的挣钱法相当奇葩,但不得不说,跳槽成为了职场上越来越常见的现象。在智联招聘调查数据中我们看到,93.2%的白领有跳

广告内容定向分级,保护未成年人身心健康

随着移动智能终端的普及,未成年人首次触网的年龄不断降低。根据中国互联网络信息中心第48次《中国互联网络发展状况统计报告》显示,截至2021年6月,中国网民规模为10.11亿人,6—19岁网民占15.7%,共1.58亿人。同时根据第十次中国未成年人互联网运用调查显示,中国未成年人互联网普及率为99.2

curl 调用url时带有&符号被截断

转载请注明出处: 用curl命令在服务器上调试接口时,一直调试不通,执行如下: 在用curl 执行之后,返回了一个 作业id [ 1 ] 23926 ; 并打印出了 调用执行的url,发现 真正执行的url 与请求的url 长度不一致, 且 & 符号后面的参数都被截断了。 具体原因为:终端会将 &

上周热点回顾(10.2-10.8)

热点随笔: · 【故障公告】遭遇用心良苦的疯狂攻击:DDoS + CC攻击 (博客园团队)· 一种对数据库友好的GUID的变种使用方法 (ensleep)· BS系统的登录鉴权流程演变 (风吹草)· 会员权益-新功能发布:定时发布博文 (博客园团队)· 面试官随便问几个问题就知道你究竟做没做过微信支

.NET周报【10月第2期 2022-10-17】

主题 宣布 .NET 7 发布候选版本 2 - .NET Blog https://devblogs.microsoft.com/dotnet/announcing-dotnet-7-rc-2/ .NET 7 RC2 已经发布。 .NET 7 RC2 已经发布。 文章除了链接到过去关于.NET 7的

FFmpeg开发笔记(三十四)Linux环境给FFmpeg集成libsrt和librist

​《FFmpeg开发实战:从零基础到短视频上线》一书的“10.2 FFmpeg推流和拉流”提到直播行业存在RTSP和RTMP两种常见的流媒体协议。除此以外,还有比较两种比较新的流媒体协议,分别是SRT和RIST。 其中SRT全称为Secure Reliable Transport,中文叫做安全可靠传

文心一言 VS 讯飞星火 VS chatgpt (112)-- 算法导论10.2 3题

三、用go语言,用一个单链表L实现一个队列。要求操作 ENQUEUE 和 DEQUEUE 的运行时间仍为O(1)。 文心一言: 在 Go 语言中,使用单链表实现队列的操作,包括入队(ENQUEUE)和出队(DEQUEUE),并保持操作的时间复杂度为 O(1),需要利用两个指针,一个指向队头,另一个指

文心一言 VS 讯飞星火 VS chatgpt (110)-- 算法导论10.2 1题

一、用go语言,单链表上的动态集合操作 INSERT 能否在 O(1)时间内实现?DELETE 操作呢? 文心一言: 在单链表中,INSERT 和 DELETE 操作的时间复杂度通常是 O(n),其中 n 是链表中的元素数量。这主要是因为当你插入或删除元素时,你需要遍历链表来找到正确的插入或删除位置