Windows下DLL文件调试

在Windows操作系统下主流的C语言开发工具是Visual Studio,这个工具与Windows操作系统集成较好,功能强大,是很多Windows平台下C语言开发者的首选工具。但是VS过于庞大臃肿,并不适合个人日常开发使用。本人习惯使用mingw作为C/C++开发环境。

在VS中调试(可执行程序,DLL或者COM组件等等)很方便,一般不会遇到太大问题。然而,如果使用mingw进行调试,遇到的问题则会比较多。mingw环境下一般使用gdb进行调试。大多数情况下,使用gdb调试不会有太大问题;然而,如果使用mingw开发DLL和COM组件,则遇到的困难会比在VS下多。如果使用低版本的mingw编译器,你甚至可能无法编译COM源代码。

本文中我们将会探讨Windows下如何利用mingw进行DLL创建及调试。

创建DLL文件

利用mingw创建一个DLL文件很容易,只需要在调用gcc/g++时指定-shared -fPIC选项或者利用-Wl,dll给ld传递参数。我一般使用第一种方式,-shared选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号);而-fPIC指定生成位置无关代码(Position Independent Code),不过在Windows平台上-shared应该是包含-fPIC选项的,所以这个选项有点多余,可以不加(加了反而会给出一个warning)。但似乎不是所有系统都支持,所以最好显式加上-fPIC选项。

创建一个DLL文件的命令一般像下面这样:

gcc -shared -fPIC -o libabc.a abc.c

而源码文件abc.c和普通的源文件没有什么两样:

#include <stdio.h>

int f(int a, int b) {
    return (a+b);
}

调用生成的动态链接库也是极为简单:

gcc -L. -labc -o main.exe main.c

这比利用VS开发DLL的那一套流程简单很多。不需要使用__declspec(dllexport)或者__declspec(dllimport)这样的函数修饰符,不需要写def文件。程序员需要做的就是编写一个普通的C/C++源码文件,然后执行上面的简单命令即可。当然,mingw也支持利用__declspec(dllexport)或者__declspec(dllimport)修饰符或者def文件来控制DLL的生成。

此外,mingw的工具链中还提供了dlltool,支持从导入库文件和.def文件创建DLL文件,或者通过.dll文件及.def文件创建导入库文件。关于这个命令的详细说明,运行dlltool --help
此外,高版本的mingw中还提供了gendef等工具,支持从.dll文件生成.def文件。
这些工具无疑降低了利用mingw进行DLL开发的难度(此外还有pexports,reimp等等)。

调试DLL文件

下面回到本文的重点,即如何利用mingw工具链对DLL文件进行调试。相比于DLL创建相关的主题,网上关于mingw DLL调试的讨论似乎少了很多。

下面提出两种用于调试DLL文件的方法

  1. 使用wrapper程序调试DLL
  2. 使用rundll32.exe调试DLL

首先,引用网上找到的一句话:

首先明白一点的是,只要有模块(exe,dll,sys等是模块)对应的正确符号文件,我们都可以使用代码去调试。

注意,为了能够对DLL文件进行调试,需要待调试文件中包含调试信息。所以,调试DLL和调试其他代码一样,首先需要在编译时指定-g -ggdb选项。且在调试前不能使用strip命令去掉可执行文件中的调试信息。

调试DLL程序的难点在于:DLL文件不像普通的可执行程序那样有一个静态的入口(例如xx.exe的main函数)。所以我们调试DLL文件时自然就要从这一点上入手寻找思路。

一种自然而然的想法就是创建一个wrapper函数,封装对DLL中代码的调用,之后调试这个wrapper程序,通过设置断点,单步跟踪,逐步进入到DLL函数内部进行调试。这也就是本文第一种方法的思路。

为了演示,我们编写一个测试文件main.c来调试上一节中创建的libabc.dll文件。这样main.c就构成了libabc.dll的wrapper。
从abc.c创建DLL文件,并利用-g选项添加调试信息:

gcc -shared -fPIC -g -ggdb -o libabc.a abc.c

main.c源码如下所示:

#include <stdio.h>
#include <stdlib.h>
extern int f(int a, int b);

int main(int argc, char *argv[]) {
    int a = 1;
    int b = 2;

    if (argc > 1) { a = atoi(argv[1]); }
    if (argc > 2) { a = atoi(argv[2]); }

    printf("a = %d, b = %d, a+b = %d\n", a, b, f(a, b));
    return 0;
}

将其编译连接为可执行文件main.exe:

gcc -g -o main.exe -L. -labc main.c

之后,我们运行gdb main.exe就可以开始调试了。
输入b main在main函数处设置断点。输入r开始运行程序,程序会在main函数处暂停。我们还可以通过b f在DLL中的子函数f处设置断点,以便进入DLL中进行调试。在执行b f时若弹出Make breakpoint pending on future shared library load? (y or [n]) ,选择y,使得gdb加载DLL时在函数断点处阻塞,则最终gdb会在f函数处暂停。之后使用step或者next命令即可逐步进入到DLL文件内部进行调试。bingo!

有时,有些wrapper可能会接收命令行参数,那这时如何利用gdb调试呢?
有三种方式:

1、在命令行通过--args指定程序参数。例如

> gdb --args main.exe 1 2

2、在gdb中通过set args指定程序参数。例如

> gdb main.exe
(gdb) set args 1 2
(gdb) r

3、在gdb中通过r(un)命令添加参数。例如

> gdb main.exe
(gdb) r 1 2

可以看到,此处的可执行文件main.exe主要起到一个调试入口的作用,理论上,其他可执行文件也可以作为调试入口,这也就是第二种方法的思路,即,利用rundll32.exe作为调试入口。
rundll32.exe是Windows操作系统自带的一个系统程序,其作用是执行DLL文件中的内部函数,这样我们就可以利用它作为载体,加载DLL文件执行。虽然rundll32.exe本身并不含调试信息,无法在其中设置断点。但是,我们可以在DLL内部函数中设置断点,这样gdb仍然会在断点处暂停,我们就可以进入DLL函数内部进行调试。

下面简单介绍一下rundll32的使用方式和工作原理,之后再说明如何借助rundll32调试DLL文件。

使用方法为:

rundll32.exe DLLname,Functionname [Arguments]

DLLname为需要执行的DLL文件名;Functionname为需要执行的DLL文件中的函数名称;[Arguments]为函数的具体参数。参数之间可以用逗号或者空格分隔。

以下三条命令等效,它们均可调用shell32.dll中的Control_RunDLL函数打开控制面板。

rundll32.exe shell32.dll,Control_RunDLL
rundll32.exe shell32.dll Control_RunDLL
rundll32.exe shell32 Control_RunDLL

rundll32.exe被调用时执行以下步骤:

  1. 分析命令行。
  2. 通过LoadLibrary()加载指定的DLL。
  3. 通过GetProcAddress()获取<entrypoint>函数的地址。
  4. 调用<entrypoint>函数,并传递作为<optional arguments>的命令行尾。
  5. <entrypoint>函数返回时,rundll32.exe将卸载DLL并退出。

下面叙述利用rundll32调试DLL文件的步骤。
首先利用gdb --args rundll32.exe libabc.dll f 1 2启动调试器。其中rundll32的参数也可以进入gdb以后再设置。通过b f在函数f处设置断点,以便进入DLL中进行调试。此时若弹出Make breakpoint pending on future shared library load? (y or [n]) ,则选择y。输入r运行程序,程序会在f入口处暂停,从而可以进入函数内部进行调试。

相比于第一种方式,利用rundll32的方式不需要额外编写wrapper程序,减轻了编程负担。然而,这种“轻松”也是有代价的。由上面对rundll工作流程的分析中可以看出:rundll是通过LoadLibrary加载DLL文件,并通过GetProcAddress获取函数地址的。对于标准的动态链接库文件,例如windows系统的kernel32.dll,user32.dll等等文件,不会有太大问题。但是子windows平台上还有很多非常规的动态连接文件。例如,Matlab的mex文件(扩展名为.mexw32或者.mexw64)本质上就是一种动态连接文件,其入口函数是mexFunction,利用rundll32无法正常加载。此外,python扩展模块(扩展名为.pyd)也是一种动态链接文件。但是这类文件无法通过rundll32来加载,也就无法使用这种方式来调试。反之,利用wrapper函数进行调试的方式则具有更大的灵活性,理论上,mex文件等也可以利用这种方式进行调试。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows DLL 分析工具是一种用于分析和调试动态链接库(DLL文件的软件工具。DLL文件Windows操作系统中常用的文件类型,它包含可被应用程序调用的可执行代码和共享资源。 Windows DLL 分析工具提供了多种功能,帮助开发人员和安全专家更好地理解和解决与DLL相关的问题。以下是一些常见的功能: 1. 导入表和导出表分析:Windows DLL 文件通常包含其他DLL文件的引用,导入表用于记录这些引用。分析工具可以将导入表中引用的DLL文件列表展示出来,帮助开发人员了解应用程序所需的依赖项。 2. 调试功能:分析工具可以提供调试功能,允许开发人员在DLL文件中设置断点、单步调试和查看变量的值。这有助于开发人员跟踪代码执行流程和定位问题。 3. 反汇编:分析工具可以将DLL文件的机器码转换为汇编代码,使开发人员能够更深入地理解DLL的执行逻辑和结构。 4. 静态代码分析:分析工具可以自动检查DLL文件的代码,寻找潜在的错误或不安全的编程实践。这有助于提高代码质量和安全性。 5. 库依赖分析:Windows DLL 文件通常与其他DLL文件有依赖关系。分析工具可以分析DLL文件的依赖关系,包括被引用的DLL文件和被其他DLL文件引用的情况,帮助开发人员处理依赖冲突和版本兼容性问题。 总之,Windows DLL 分析工具是开发人员和安全专家的重要工具,它们帮助分析、调试和优化DLL文件,确保应用程序的正常运行和安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值