熟悉x64dbg调试器的使用
x64dbg 是一款开源、免费、功能强大的动态反汇编调试器,它能够在Windows
平台上进行应用程序的反汇编、调试和分析工作。与传统的调试器如Ollydbg
相比,x64dbg调试器的出现填补了Ollydbg
等传统调试器的不足,为反汇编调试工作提供了更高效、更可靠的解决方案。正是因为有了这些优点,才能使其成为当今最受欢迎的反汇编调试软件之一。
x64dbg官方地址:https://x64dbg.com/
x64dbg和Ollydbg虽都是Windows
平台上的调试器工具,但它们有一些不同之处:
- 支持架构:Ollydbg仅支持x86架构,而x64dbg支持x86和x64架构,因此x64dbg可以调试64位应用程序,而Ollydbg则无法做到。
- 开源性:x64dbg是一个开源项目,而Ollydbg是闭源软件。
- 插件生态:x64dbg的插件系统更加完善,有更多的第三方插件可供使用,而Ollydbg的插件系统相对较少。
- 用户界面:x64dbg的界面比Ollydbg更现代化,具有更好的可定制性,可以自定义布局,颜色和字体等。
- 功能比较:x64dbg具有许多高级功能,如内存映射,数据跟踪,反汇编和代码绘图等,而Ollydbg则相对较少。
在学习使用该工具之前第一步则是要安装软件,x64dbg调试器的安装很容易,读者只需要前往x64dbg
的官方开源仓库中找到下载按钮。
以Github仓库为例,读者需访问https://github.com/x64dbg/x64dbg/releases
并在其中找到所对应的snapshot_2023-03-04_02-26.zip
版本点击下载即可;
下载好以后解压这个压缩包,读者会看到两个文件夹,其中pluginsdk
代表的是x64dbg
的开发工具包,如果需要开发自定义插件则需要引入该工具包,而release
则是编译版本的x64dbg
本体,打开release
文件夹读者会看到如下图所示的目录结构,其中x32/x64
分别代表的是两种架构的调试器,而如果用户需要安装调试器到系统内只需要点击x96dbg.exe
并依次点击下一步即可完成调试器的安装;
1.1 如何启动调试
不同于OllyDBG调试器,x64dbg默认就支持两种调试模式,通常读者可以选择使用打开文件的方式调试程序,也可以直接附加到一个已经存在的进程上调试同时支持脱离进程,需要注意的是为了保证最佳的调试效果,调试器的打开请读者使用管理员方式运行。
当用户需要打开调试程序时,可以通过菜单"文件"->"打开"(或者按下快捷键F3),会弹出如下图所示的打开文件对话框,默认对话框会停留在x64dbg
软件的根目录,此时通过选择所需调试的进程即可打开调试;
第二种调试方式则是直接附加到一个已经存在的进程上,可以通过菜单"文件"->"附加"(或者按下快捷键Alt+A)会弹出如下图所示的附加对话框,读者只需要选中一个正在运行的程序即可附加到特定进程内,当用户调试结束后可通过Ctrl+Alt+F2脱离进程;
1.2 熟悉x64dbg窗口
当被调试程序与调试器之间建立调试关系以后,就可以开始进行动态调试分析了,在x64dbg中有许多的窗口,例如CPU窗口,寄存器窗口,堆栈窗口,十六进制窗口等,如下图所示则是x64dbg运行后的主界面;
这里增加一个知识点,关于动态反汇编调试与静态反汇编调试的区别:
-
动态反汇编调试(Dynamic Disassembly Debugging)是指在程序运行时动态地反汇编机器码,从而获取指令级别的执行信息和内存访问情况,以辅助调试和分析程序。动态反汇编调试需要借助特殊的调试工具,如GDB、x64dbg、Ollydbg等,通常用于调试崩溃、死锁、内存泄漏等问题。
-
静态反汇编调试(Static Disassembly Debugging)是指对程序进行反汇编分析,不需要运行程序,只需要对程序进行静态分析就可以获得指令级别的执行信息和内存访问情况,以辅助调试和分析程序。静态反汇编调试需要使用特殊的工具,如IDA Pro、Binary Ninja等,通常用于逆向工程、恶意代码分析、漏洞挖掘等方面。
如上图所示,当调试器运行后最大的窗口则是CPU窗体,CPU窗体是x64dbg的默认主窗口,用户所有的调试工作都在CPU窗体中完成,因为CPU窗体就是用于反应当前CPU以及寄存器执行状态的,CPU窗体其内部包含了五个子窗口,分别是反汇编窗口,寄存器窗口,栈地址窗口,十六进制数据窗口,以及堆栈参数解析窗口。
(1)反汇编窗口
反汇编窗口是x64dbg
的一个主要功能之一,它可以显示被调试程序的汇编指令,并允许用户查看和修改这些指令(需要注意的是当一个进程被加载默认EIP会停在系统模块中),其主要的作用包括;
- 显示程序的汇编指令:反汇编窗口可以将二进制文件转换为汇编代码,并显示在窗口中。这使得程序员可以深入了解程序的内部工作原理,并且可以查找程序中的漏洞和安全问题。
- 允许用户修改指令:反汇编窗口不仅可以显示程序的汇编指令,还可以允许用户直接在窗口中修改指令。这对于调试和修复程序中的错误非常有用,但需要小心操作,以避免引入新的错误。
- 跟踪代码执行:反汇编窗口还可以跟踪被调试程序的代码执行过程,以便程序员可以了解程序在运行时的行为和状态。这对于调试和优化程序非常有用。
- 显示程序的符号表:反汇编窗口可以显示程序的符号表,这对于程序员来说非常重要,因为它可以帮助他们更好地理解程序中的变量、函数和其他标识符。
(2)寄存器窗口
寄存器窗口则是在调试器最右侧的顶端,该窗口用于显示和解释当前线程环境下CPU寄存器的各种状态值和内容,并且读者可以通过双击这些寄存器组来对其进行动态的调整参数;
寄存器 | 描述 |
---|---|
EAX | 通用寄存器,用于存放函数返回值、算术运算结果等。 |
EBX | 通用寄存器,用于存放内存地址或其他通用数据。 |
ECX | 通用寄存器,用于存放函数参数。 |
EDX | 通用寄存器,用于存放内存地址或其他通用数据。 |
ESI | 通用寄存器,用于存放源数据的地址,例如字符串拷贝等操作。 |
EDI | 通用寄存器,用于存放目标数据的地址,例如字符串拷贝等操作。 |
EBP | 基址指针,用于存放当前栈帧的基址。 |
ESP | 堆栈指针,用于存放当前堆栈顶部的地址。 |
EIP | 指令指针,用于存放当前正在执行的指令的地址。 |
EFLAGS | 标志寄存器,用于存放CPU的状态标志,例如进位标志、零标志、符号标志等。 |
其中标志寄存器(Flag Register)是一个16位的寄存器,用于存储CPU运算的状态标志,例如进位标志、零标志、符号标志等。下面列出了x86架构CPU中的标志寄存器以及它们的含义:
标志位 | 中文名称 | 描述 |
---|---|---|
CF | 进位标志 | 当无符号整数加法或减法的结果超出了寄存器的宽度时,该标志位会被设置为1。CF=1表示有进位发生,CF=0表示无进位发生。 |
PF | 奇偶标志 | 当一个操作的结果中有偶数个二进制位为1时,该标志位会被设置为1。PF=1表示结果中有偶数个1,PF=0表示结果中有奇数个1。 |
AF | 调整标志 | 当一个二进制算术操作(例如加法或减法)中低4位产生了进位或借位时,该标志位会被设置为1。AF=1表示有进位或借位发生,AF=0表示无进位或借位发生。 |
ZF | 零标志 | 当一个操作的结果为零时,该标志位会被设置为1。ZF=1表示结果为零,ZF=0表示结果不为零。 |
SF | 符号标志 | 当一个操作的结果为负数时,该标志位会被设置为1。SF=1表示结果为负数,SF=0表示结果为非负数。 |
TF | 陷阱标志 | 该标志位用于单步调试,当TF=1时,CPU会在每个指令执行后暂停并等待调试器的命令。 |
IF | 中断标志 | 该标志位用于控制CPU是否响应中断请求。当IF=1时,CPU允许中断请求;当IF=0时,CPU禁止中断请求。 |
DF | 方向标志 | 该标志位用于控制字符串操作的方向,当DF=1时,字符串操作是从高地址向低地址进行的;当DF=0时,字符串操作是从低地址向高地址进行的。 |
OF | 溢出标志 | 当有符号整数加法或减法的结果超出了寄存器的宽度时,该标志位会被设置为1。OF=1表示有溢出发生,OF=0表示无溢出发生。 |
(3)堆栈窗口
堆栈窗口用于显示当前线程的栈,栈是一种后进先出(Last-In-First-Out,LIFO)的数据结构。堆栈窗口会随着ESP(Extended Stack Pointer)寄存器的变化而变化,ESP指向当前栈顶元素的地址。堆栈窗口可以显示栈帧、函数调用和局部变量等信息,这对于调试和优化程序非常有用。此外,堆栈窗口还可以允许用户修改栈上的数据,这对于调试和修复程序中的错误非常有用,但需要小心操作,以避免引入新的错误。
堆栈窗口有两个重要的作用,一个是用于调用函数时的参数传递,另一个作用是函数内部的变量局部空间,函数内定义的变量和临时变量都存储在堆栈中,当函数返回时,这些变量会自动销毁。堆栈窗口可以帮助程序员查看函数内部变量的值和状态,从而更好地理解程序的行为和调试错误。
(4)十六进制窗口
在x64dbg最左侧底部则是十六进制窗口,该窗口的主要作用适用于浏览内存,或在特定区域读入或写出数据时使用,在某些时候还可以用来在特定内存区域下断点,以下是一些关于它的主要作用:
- 浏览内存 - 十六进制窗口可以让您直接查看内存中的数据。您可以使用箭头键或鼠标滚轮向上或向下滚动,以浏览更多内存。这对于分析程序的内存布局和查找特定数据非常有用。
- 读入或写出数据 - 可以在十六进制窗口中选择特定的内存区域,并使用右键菜单中的“复制”和“粘贴”选项来读入或写出数据。这对于修改程序的内存状态或在调试过程中注入数据非常有用。
- 设置断点 - 可以在特定的内存地址上设置断点,以便在程序执行到该地址时中断。要设置断点,请在十六进制窗口中右键单击该地址,并选择“设置断点”。
除了以上这些功能外,十六进制窗口还可以显示与特定内存地址相关的汇编代码,并提供许多其他的操作选项,例如搜索内存、转换数据格式等等。
(5)掌握常用快捷键
熟练掌握x64dbg的快捷键可以使调试和分析工作更加高效和快速。x64dbg提供了丰富的快捷键选项,让用户在操作时可以尽可能地减少鼠标操作,更专注于代码和调试数据的分析。通过掌握快捷键,可以更快速地执行调试命令、添加或删除断点、修改寄存器、查找内存等操作,从而提高调试和分析的效率。因此,熟练使用x64dbg的快捷键是成为一名高效的调试和分析人员的必备技能之一,以下是x64dbg所支持的快捷键分类,读者可更具自己的实际情况学习掌握;
调试控制
- F2:开始/停止调试
- F3:单步执行
- F7:进入函数调用
- F8:跳过函数调用
- F9:继续执行
- F12:暂停/继续执行
断点
- F5:添加/删除断点
- Ctrl + F5:添加/删除硬件断点
- F6:添加/删除条件断点
- F9:启用/禁用断点
- Ctrl + F9:启用/禁用所有断点
寄存器
- Ctrl + R:打开/关闭寄存器窗口
- Ctrl + G:跳转到指定地址
- F2/F4/F6:在寄存器窗口中修改寄存器的值
内存
- Ctrl + M:打开/关闭内存窗口
- Ctrl + E:打开/关闭表达式窗口
- Ctrl + F:查找指定字节序列
- Ctrl + Shift + F:查找指定指令序列
- Ctrl + D:将内存中的数据以十六进制形式导出到文件中
动态分析
- Ctrl + A:打开/关闭汇编窗口
- Ctrl + B:打开/关闭堆栈窗口
- Ctrl + C:打开/关闭CPU窗口
- Ctrl + L:打开/关闭日志窗口
其他
- Ctrl + F2:打开/关闭工具栏
- Ctrl + F10:打开/关闭菜单栏
- Ctrl + Q:退出x64dbg
注意:以上快捷键是x64dbg的默认设置,您也可以通过"选项"菜单中的"快捷键"选项来自定义快捷键。
1.3 熟悉x64dbg断点
断点机制是调试器的重要功能,x64dbg为调试着提供了多种调试断点,通常包括了软件断点,硬件断点,内存断点,一次性断点,条件断点,消息断点等,熟练使用断点是调试程序的基本技能,断点通常可被总结为如下;
(1)软件断点
在程序执行到特定指令时暂停程序执行。这种断点类型是由调试器模拟实现的,通常通过修改程序指令来实现。它是最常见的断点类型,因为可以在任何代码段中设置断点。
在x64dbg中,调试这可以通过F2
快捷键在所需下断点的位置下断,当该位置被下断点后,则调试器会以红色标注,而当前EIP指针则会使灰色显示,如下图所示;
此外软件断点同样可以使用bp/bpx
等命令下断,或通过bc
来取消断点,当读者需要在特定函数上下断点时,可通过Ctrl+G
调出地址跳转表达窗口,并输入如MessageBoxA
等函数,跳转后手动下断点。
亦可在命令窗口输入bp MessageBoxA
自动下断点,断点可在断点
选项卡中看到该断点的详细参数;
(2)硬件断点
与软件断点不同,硬件断点是通过修改特定的处理器寄存器来实现的,这些寄存器用于跟踪指令地址和数据访问地址。硬件断点通常比软件断点更快,但是受到硬件限制,可以在某些情况下设置的数量有限。
在x64dbg中,硬件断点可以分为三种类型:硬件执行断点、硬件读取断点和硬件写入断点。以下是每种类型的解释:
-
硬件执行断点 - 当程序执行到指定地址时,触发硬件执行断点。这种断点类型适用于在特定指令处调试程序,例如函数调用或跳转指令。在x64dbg中,硬件执行断点可以通过在指令前面的地址上设置“e”来实现。
-
硬件读取断点 - 当程序尝试从指定内存地址读取数据时,触发硬件读取断点。这种断点类型适用于在特定内存位置上调试程序,例如检测某个变量的值何时被更改。在x64dbg中,硬件读取断点可以通过在指定内存地址上设置“r”来实现。
-
硬件写入断点 - 当程序尝试向指定内存地址写入数据时,触发硬件写入断点。这种断点类型适用于在特定内存位置上调试程序,例如检测某个变量的值何时被更改。在x64dbg中,硬件写入断点可以通过在指定内存地址上设置“w”来实现。
(3)内存断点
当程序在特定内存地址处读取、写入或执行时,暂停程序执行。这种断点类型通常用于检测内存访问错误或跟踪特定变量的更改。而一次性断点则是在程序执行到特定指令时只触发一次。这种断点类型通常用于调试复杂代码中的问题,而不是在每次执行到指令时都触发断点。
在x64dbg中,内存断点可以根据不同的操作类型进行分类,内存断点被分为内存访问,内存执行,内存写入,内存读取等,他们之间的应用各不相同;
- 内存访问断点(Access breakpoint):当程序试图访问特定内存地址时触发,可以用于检测对敏感数据的未授权访问,例如密码、私钥等。
- 内存执行断点(Execute breakpoint):当程序试图执行特定内存地址处的指令时触发,可以用于检测恶意代码注入、代码劫持等攻击。
- 内存写入断点(Write breakpoint):当程序试图写入特定内存地址时触发,可以用于检测对敏感数据的修改、防止非法更改程序代码等。
- 内存读取断点(Read breakpoint):当程序试图从特定内存地址读取数据时触发,可以用于检测对敏感数据的窃取、防止非法读取程序代码等。
(4)消息断点
x64dbg消息断点(Message Breakpoint)是一种特殊类型的断点,可以用于监视程序执行期间的Windows消息,包括键盘输入、鼠标操作、窗口消息等。当程序接收到指定类型的消息时,消息断点会触发,程序执行会暂停,以便进行调试。
消息断点在调试GUI程序时非常有用,可以帮助调试窗口消息的处理代码,定位程序中的错误或异常行为。例如,当你想要调试一个窗口消息处理函数时,可以在该函数上设置消息断点,当程序接收到对应的消息时,程序会暂停在该函数内部,方便进行调试。
1.4 熟悉x64dbg代码跟踪
(1)反汇编调试命令
在前面的章节中笔者介绍了x64dbg的窗口以及断点的基本设置方法,接下来将介绍x64dbg中的代码跟踪,代码跟踪是任何一个调试器的核心功能,一般而言跟踪功能都会包括,单步步过(快捷键F8),单步步进(快捷键F7),运行到返回(快捷键Ctrl+F9),以及运行到用户代码(Alt+F9快捷键)等常用功能。
在x64dbg中进行代码跟踪可以帮助我们分析程序的执行过程和调试代码,以便更好地理解程序的运行机制。下面是一些常用的代码跟踪功能,及对该功能的解析:
- 单步步过(快捷键F8):该功能用于逐行执行程序,当执行到函数调用时,会直接跳到函数内部执行,而不会逐行执行函数内部的代码。
- 单步步进(快捷键F7):该功能用于逐行执行程序,当执行到函数调用时,会进入函数内部逐行执行代码,直到执行完毕并返回到函数调用处。
- 运行到返回(快捷键Ctrl+F9):该功能用于直接运行程序,直到执行到当前函数的返回语句处停止,并返回到函数调用处。
- 运行到用户代码(快捷键Alt+F9):该功能用于直接运行程序,直到执行到用户指定的代码位置停止,可以通过设置断点来指定代码位置。
在进行代码跟踪时,我们可以在代码窗口中看到当前执行的行和执行指针所指向的内存地址,也可以在寄存器窗口中查看各个寄存器的值,以便更好地理解程序的执行过程。
(2)反汇编检索命令
在x64dbg中,读者可通过Ctrl+F
快捷键命令调出命令搜索窗口,并输入一条反汇编指令集,即可实现检索模块内具备的命令位置,当用户勾选整块搜索时,x64dbg会搜寻整个进程内所有匹配的命令,比较遗憾的是x64dbg目前还不支持搜寻多条指令集;
虽然无法搜索连续的汇编指令集,但却可是搜寻二进制字符串,通过快捷键Ctrl+B
读者可打开二进制搜索框,我们以如下这条命令为例;
打开二进制搜索框,输入8B FF 55 8B EC
即可用于检索上方的指令片段,也算是一种折中的方法;
同时x64dbg还支持搜索字符串,通过右键菜单即可调出搜索框,并实现特殊字符串的检索;
出处:https://www.cnblogs.com/LyShark/p/17531170.html
=======================================================================================
1.5 为x64dbg编写插件
任何一个成熟的软件都会具有可扩展性,可扩展性是现代软件的一个重要特征,因为它使软件更易于维护和适应变化的需求,x64dbg
也不例外其可通过开发插件的方式扩展其自身功能,x64dbg
提供了多种插件接口,包括脚本插件、DLL插件、Python插件和.NET插件等。此外,x64dbg
还支持用户自定义命令和快捷键。这使得用户可以自由地扩展和自定义软件的功能,从而更好地适应开发需求。
我们以C/C++
语言为开发模板,x64dbg
插件表现出来的其实也是一个DLL
文件,他里面导出了x64dbg
所需要的几个函数,从而可以在x64dbg
启动时被加载,除去所必须的导出函数外,其他功能的实现与DLL
基本一致。
(1)开发前的准备工作
在开发x64dbg
插件时,首先需要配置插件的开发工具包,请读者将x64dbg插件包中的pluginsdk
解压到任意路径下,该包内就是开发所必须要用到的SDK库。
并打开Visual Studio 2013
并新建一个DLL
空项目,此时请读者打开"调试"->"属性页"并在配置属性页,VC++目录中引入pluginsdk
库,读者只需要配置包含目录
及库目录
即可,具体配置参数如下图所示;
当读者正确引入后,那么下一步则是新建配置文件,x64dbg官方针对配置方法给予了一个模板文件,读者可以自行去下载该模板使用。
在开发插件时,至少需要导出两个函数,函数plugsetup
以及pluginit
这两个函数是插件的标识,x64dbg将会通过这两个函数来判断是否可被加载,所以在开发时这两个插件是必须要存在的。
以下是 void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
函数的原型和解释:
void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
plugsetup 函数是一个可选的插件函数,用于初始化插件和设置其参数。该函数在插件加载时由主程序调用。
该函数的参数是一个指向 PLUG_SETUPSTRUCT
结构的指针,该结构包含了一些与插件有关的信息和设置。通过读取和设置 PLUG_SETUPSTRUCT
结构中的字段,插件可以初始化其自身并与主程序进行通信。具体来说,PLUG_SETUPSTRUCT
结构包含以下字段:
- StructSize: 结构体大小,用于指示传递给插件的结构的大小。插件应该使用 sizeof 运算符来设置此字段的值。
- PluginHandle: 插件的句柄,由主程序分配并传递给插件,用于标识该插件。
- hwndDlg: 插件界面的句柄,如果插件具有界面,则应该将该字段设置为其窗口句柄。
- hMenu: 插件的菜单句柄,如果插件具有菜单,则应该将该字段设置为其菜单句柄。
- hMenuDisasm: 反汇编窗口的菜单句柄,如果插件需要访问反汇编窗口的菜单,则应该将该字段设置为反汇编窗口的菜单句柄。
- hMenuDump: 转储窗口的菜单句柄,如果插件需要访问转储窗口的菜单,则应该将该字段设置为转储窗口的菜单句柄。
插件应该使用 plugsetup
函数来完成其自身的初始化和参数设置,以便在主程序中正确地运行。
以下是 bool pluginit(PLUG_INITSTRUCT* initStruct)
函数的原型和解释:
bool pluginit(PLUG_INITSTRUCT* initStruct);
pluginit 函数是一个必需的插件函数,用于初始化插件和注册插件的命令。该函数在插件加载时由主程序调用。
该函数的参数是一个指向 PLUG_INITSTRUCT
结构的指针,该结构包含了一些与插件有关的信息和设置。通过读取和设置 PLUG_INITSTRUCT
结构中的字段,插件可以初始化其自身并注册其命令。具体来说,PLUG_INITSTRUCT
结构包含以下字段:
- StructSize: 结构体大小,用于指示传递给插件的结构的大小。插件应该使用 sizeof 运算符来设置此字段的值。
- PluginHandle: 插件的句柄,由主程序分配并传递给插件,用于标识该插件。
- sdkVersion: 主程序的 SDK 版本号,由主程序传递给插件。
- pluginVersion: 插件的版本号,由插件设置并传递给主程序。
- PluginName: 插件的名称,由插件设置并传递给主程序。
- PluginAuthor: 插件的作者,由插件设置并传递给主程序。
- PluginDescription: 插件的描述,由插件设置并传递给主程序。
插件应该使用 pluginit
函数来完成其自身的初始化和命令注册。该函数应该返回 true
表示初始化成功,或 false
表示初始化失败。如果返回 false
,主程序将卸载该插件并显示错误信息。
如上函数解释,我们可知pluginit
适用于初始化插件的,例如增加插件菜单栏,设置插件功能等,我们以如下代码为例做一个简单的解释;
PLUG_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct)
{
initStruct->pluginVersion = PLUGIN_VERSION;
initStruct->sdkVersion = PLUG_SDKVERSION;
strncpy_s(initStruct->pluginName, PLUGIN_NAME, _TRUNCATE);
pluginHandle = initStruct->pluginHandle;
// 插件初始化
initStruct->sdkVersion = PLUG_SDKVERSION;
initStruct->pluginVersion = 1;
const char *name = "CheckME -->";
memset(initStruct->pluginName, 0, 128);
memcpy(initStruct->pluginName, name, strlen(name));
return pluginInit(initStruct);
}
代码中通过initStruct->pluginVersion
设置了插件版本,通过initStruct->sdkVersion
设置了SDK版本,并在initStruct->pluginName
中设置了插件名称,最后调用了pluginInit(initStruct)
初始化了插件,至此该插件即创建完毕了;
而plugsetup
函数则是用于在初始化时在setupStruct->hMenu
也就是菜单顶部增加一个菜单栏,标题为PowerBy LyShark
的列表。
PLUG_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
{
hwndDlg = setupStruct->hwndDlg;
hMenu = setupStruct->hMenu;
hMenuDisasm = setupStruct->hMenuDisasm;
hMenuDump = setupStruct->hMenuDump;
hMenuStack = setupStruct->hMenuStack;
// 增加二级菜单
char sub_menu[] = { "PowerBy LyShark" };
_plugin_menuaddentry(setupStruct->hMenu, 2, sub_menu);
pluginSetup();
}
当菜单被点击是则会触发CBMENUENTRY
函数,执行该函数内的流程,如上就是插件的加载流程。
(2)开发插件实战
在本节,笔者将带领大家实现一个简单的x64dbg插件,该插件的功能很简单,当用户点击菜单栏中的选项是,我们让其弹出一个消息框,此处为了开发方便,我做了精简化,你可以直接使用我的方法来新建文件,建出来的文件只有两个非常简洁。
首先在头文件部分新建一个pluginmain.h
并增加PLUGIN_NAME
替换成自己项目的名字。
#pragma once
// Plugin information
#define PLUGIN_NAME "LySharkBlog"
#define PLUGIN_VERSION 1
#include "./bridgemain.h"
#include "./_plugins.h"
#include "./_scriptapi_argument.h"
#include "./_scriptapi_assembler.h"
#include "./_scriptapi_bookmark.h"
#include "./_scriptapi_comment.h"
#include "./_scriptapi_debug.h"
#include "./_scriptapi_flag.h"
#include "./_scriptapi_function.h"
#include "./_scriptapi_gui.h"
#include "./_scriptapi_label.h"
#include "./_scriptapi_memory.h"
#include "./_scriptapi_misc.h"
#include "./_scriptapi_module.h"
#include "./_scriptapi_pattern.h"
#include "./_scriptapi_register.h"
#include "./_scriptapi_stack.h"
#include "./_scriptapi_symbol.h"
#include "./DeviceNameResolver/DeviceNameResolver.h"
#include "./jansson/jansson.h"
#include "./lz4/lz4file.h"
#include "./TitanEngine/TitanEngine.h"
#include "./XEDParse/XEDParse.h"
#ifdef _WIN64
#pragma comment(lib, "./x64dbg.lib")
#pragma comment(lib, "./x64bridge.lib")
#pragma comment(lib, "./DeviceNameResolver/DeviceNameResolver_x64.lib")
#pragma comment(lib, "./jansson/jansson_x64.lib")
#pragma comment(lib, "./lz4/lz4_x64.lib")
#pragma comment(lib, "./TitanEngine/TitanEngine_x64.lib")
#pragma comment(lib, "./XEDParse/XEDParse_x64.lib")
#else
#pragma comment(lib, "./x32dbg.lib")
#pragma comment(lib, "./x32bridge.lib")
#pragma comment(lib, "./DeviceNameResolver/DeviceNameResolver_x86.lib")
#pragma comment(lib, "./jansson/jansson_x86.lib")
#pragma comment(lib, "./lz4/lz4_x86.lib")
#pragma comment(lib, "./TitanEngine/TitanEngine_x86.lib")
#pragma comment(lib, "./XEDParse/XEDParse_x86.lib")
#endif //_WIN64
#define Cmd(x) DbgCmdExecDirect(x)
#define Eval(x) DbgValFromString(x)
#define dprintf(x, ...) _plugin_logprintf("[" PLUGIN_NAME "] " x, __VA_ARGS__)
#define dputs(x) _plugin_logprintf("[" PLUGIN_NAME "] %s\n", x)
#define PLUG_EXPORT extern "C" __declspec(dllexport)
//superglobal variables
extern int pluginHandle;
extern HWND hwndDlg;
extern int hMenu;
extern int hMenuDisasm;
extern int hMenuDump;
extern int hMenuStack;
//functions
bool pluginInit(PLUG_INITSTRUCT* initStruct);
void pluginStop();
void pluginSetup();
其次新建一个实现文件pluginmain.cpp
并写入以下代码,多数情况下我为了方便调试会使用这段代码,当我们点击菜单时会触发菜单功能,以此可以快速测试特定函数是否正常。
#include "pluginmain.h"
#include <Windows.h>
#include <process.h>
int pluginHandle;
HWND hwndDlg;
int hMenu;
int hMenuDisasm;
int hMenuDump;
int hMenuStack;
// 导出函数
extern "C" __declspec(dllexport) void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info);
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT* setupStruct);
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct);
// 在这里初始化插件数据。
bool pluginInit(PLUG_INITSTRUCT* initStruct)
{
// 返回false以取消加载插件。
return true;
}
// 在此处取消初始化插件数据。
void pluginStop()
{
}
// 在这里做GUI/菜单相关的事情。
void pluginSetup()
{
}
// 菜单被点击回调
void CBMENUENTRY(CBTYPE cbType, PLUG_CB_MENUENTRY* info)
{
// 此菜单用于实现功能,并测试
MessageBox(0, L"hello lyshark", 0, 0);
}
PLUG_EXPORT bool pluginit(PLUG_INITSTRUCT* initStruct)
{
initStruct->pluginVersion = PLUGIN_VERSION;
initStruct->sdkVersion = PLUG_SDKVERSION;
strncpy_s(initStruct->pluginName, PLUGIN_NAME, _TRUNCATE);
pluginHandle = initStruct->pluginHandle;
// 插件初始化
initStruct->sdkVersion = PLUG_SDKVERSION;
initStruct->pluginVersion = 1;
const char *name = "CheckME -->";
memset(initStruct->pluginName, 0, 128);
memcpy(initStruct->pluginName, name, strlen(name));
return pluginInit(initStruct);
}
PLUG_EXPORT bool plugstop()
{
pluginStop();
return true;
}
PLUG_EXPORT void plugsetup(PLUG_SETUPSTRUCT* setupStruct)
{
hwndDlg = setupStruct->hwndDlg;
hMenu = setupStruct->hMenu;
hMenuDisasm = setupStruct->hMenuDisasm;
hMenuDump = setupStruct->hMenuDump;
hMenuStack = setupStruct->hMenuStack;
// 增加二级菜单
char sub_menu[] = { "PowerBy LyShark" };
_plugin_menuaddentry(setupStruct->hMenu, 2, sub_menu);
pluginSetup();
}
通过使用Visual Studio 2013
编译这段程序,其默认会生成一个*.dll
的文件,根据不同版本需要将其更改为*.dp32
或者*.dp64
以此来代表这是一个插件,并将更改好的插件放入到x32/plugins
目录下,重启x64dbg
至此即可看到插件已经被加载成功。
出处:https://www.cnblogs.com/LyShark/p/17531183.html
=======================================================================================
关注我】。(●'◡'●)
如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的【因为,我的写作热情也离不开您的肯定与支持,感谢您的阅读,我是【Jack_孟】!
本文来自博客园,作者:jack_Meng,转载请注明原文链接:https://www.cnblogs.com/mq0036/p/17533894.html
【免责声明】本文来自源于网络,如涉及版权或侵权问题,请及时联系我们,我们将第一时间删除或更改!
posted on 2023-07-07 09:20 jack_Meng 阅读(5612) 评论(0) 编辑 收藏 举报