4 基于IntelVt技术的Linux内核调试器- 调试器设计与实现(2):调试核心


4.1反汇编引擎

如果说调试框架是一个调试器的灵魂,那么接口与反汇编引擎就是一个调试器的身体。我们在调试过程中是要阅读指令代码的,而反汇编引擎则提供将二进制元指令翻译成可阅读的汇编代码这个功能。

设计并实现一个初级的反汇编引擎很简单,但是计算机指令系统并不简单,将这个反汇编引擎实现到可以实际应用的级别需要不断地调试与修复Bugs,这个过程需要耗费大量精力。所以我选择了开源反汇编引擎。虽然网上有很多开源反汇编引擎,但是大部分都依赖于用户态的C库,导致不能很好地移植到内核模块上使用,而且具有优秀效率的反汇编引擎很少。这里我选择了libudis86,它是一个开源反汇编引擎,我只对其语法转换部分进行了一些修改,使其反汇编出来的指令格式符合比较好的阅读习惯。

4.1.1libudis86的基本使用方法

Libudis86库的使用很简单,只要一个提供各种信息的结构体,调用ud_disasmembe函数即可。例如下面定义了一个反汇编某个指令的函数:

ULONGOriDisasm(PUCHAR str,ULONG Eip)

{

UDISud_obj;

ULONGlen;


ud_init(&ud_obj); //初始化结构体

ud_set_mode(&ud_obj,32); //设置为32位元CPU模式

ud_set_syntax(&ud_obj,UD_SYN_INTEL); //结果使用Intel语法

ud_set_pc(&ud_obj,(int)Eip); //设置反汇编起点

ud_set_input_buffer(&ud_obj,(uint8_t*)Eip,32); //设置输入缓冲区

len=ud_disassemble(&ud_obj); //开始反汇编

strcpy(str,ud_insn_asm(&ud_obj)); //复制结果

returnlen;

}

该函数反汇编Eip指向的二进制元指令码,将结果复制到str指向的缓冲区中。

4.1.2使用libudis86反汇编某段程序

上面的函数OriDisasm只能反汇编一条指令,通常我们的调试器需要反汇编一段程序。OriDisasm在反汇编一条指令结束后会返回该指令长度(位元组),递增指令指针循环继续这项操作即可反汇编一段代码。

4.1.3向上反汇编:递减命中率算法

调试器的反汇编窗口应该像控制台窗口一样具有翻页功能,这样便于我们查阅反汇编代码,但是单纯地使用libudis86只能从上往下进行反汇编,如果我们需要向上翻页,则需要从下往上翻页。看似简单,实际上有一个很重要的难题。

我们知道x86汇编指令是不定长的,一条指令可能最小只占用1个字节,最长可能占用15个字节,而我们并不知道某一条指令它的上一条指令占用多少个位元组,因此不能直接获取某一条指令的上一条指令是什么。例如下面的指令:

004017F0 75 64 jnz short 00401856

004017F2 8B45 10 mov eax, dword ptr [ebp+10]

004017F5 C1E8 10 shr eax, 10

004017F8 83F8 07 cmp eax, 7

第一列是指令位址,第二列是指令二进制元编码,第三列是对应的汇编代码。假设当前我们反汇编的位置是4017F8那么我们对这个内存位置的83F8 07进行反汇编可以得到cmpeax,7这条指令,但是我们并不知道这条指令的上一条指令从什么位址开始,如果我们贸然猜测上一条指令的位址显然我们会得到一个错误的反汇编结果。例如对4017F6反汇编得到:

004017F6 E8 1083F807 call 08389B0B

结果是一个占用5个位元组的call指令,而且这个指令覆盖了位于4017F8本来正确的结果。

我设计了一个算法用以解决这个问题,算法的思想是递减地址指针,从这个指标开始向下循环反汇编,当长度刚刚好到达我们预期的位置时,记录上一条指令的位置。然后将这些结果进行统计。

例如当前指令是

004017F8 83F8 07 cmp eax, 7

我们想要知道他的上一条指令是什么,就递减地址,得到4017F7,反汇编得到:

004017F7 1083 F807754B adc byte ptr [ebx+4B7507F8], al

结果覆盖了4017F8,此结果作废。继续向上递减。

004017F6 E8 1083F807 call 08389B0B

结果覆盖了4017F8,此结果作废。继续向上递减。

004017F5 C1E8 10 shr eax, 10

004017F8 83F8 07 cmp eax, 7

结果可能正确,记录下上一条指令位址4017F5,命中率为1次。

继续向上递减。

004017F4 10C1 adc cl, al

004017F6 E8 1083F807 call 08389B0B

覆盖了4017F8,此结果作废。继续向上递减。

004017F3 45 inc ebp

004017F4 10C1 adc cl, al

004017F6 E8 1083F807 call 08389B0B

覆盖了4017F8,此结果作废。继续向上递减。

004017F2 8B45 10 mov eax, dword ptr [ebp+10]

004017F5 C1E8 10 shr eax, 10

004017F8 83F8 07 cmp eax, 7

刚好得到4017F8,记录下4017F8的上一条指令是4017F5,因为刚刚记录命中了一次,因此当前的统计结果是:4017F8上一条指令4017F5,命中率2次。

就这样继续不断递减地址,从递减后的地址向下反汇编,当结果刚好可以到达4017F8时记录下上一条指令位址。假设我们向上递减200字节,记录结果可能如下。

4017F5 40

XXXXXX 1

那么根据统计结果上看,上一条指令有很大可能是从4017F5开始,那么我就可以认为上一条指令是4017F5。当然这个结论可能不正确,因为这毕竟是统计学结果。如果想要得到更准确的结果,我们可以采样更多的数据,例如向上递

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
“凯征调试器”版本说明 凯征调试器 0.2.0版 1. 增加“项目浏览器”功能,能显示浏览本调试器创建的项目。 2. 增加“新建空项目”功能,自动创建基础的"configure.ac","Makefile.am"和一个只包含main函数的项目C文件。 3. 增加“选择工作路径”菜单,以适应不同的Linux系统。 注意:使用项目浏览器功能,一定要把当前的工作路径设置到项目文件夹中。使用“项目”菜单中的“选择工作路径”菜单进行设置。否则会显示“未知的项目”。 凯征调试器 0.1.1版 1. 编译文件时,将make命令调整为 make -j8。 2. 修正另存文件时,覆盖同名文件无提醒的BUG。 3. 修正打开同名并已修改文件时无提醒,自动重新打开该文件,造成已修改部分丢失的BUG。 4. 增加了对GDB 8.2的支持。 凯征调试器 0.1.0版 将GtkSourceView、VTE及GDB等软件组合在一起,实现了一些基础功能。主要功能是在调试应用程序时,可以自动打开源文件并且跳到代码所在行。 注意:使用本软件的调试功能,一定要使用菜单中的“调试程序”或工具栏中的“程序”开始调试,千万不要在终端中用命令行开始,因为这样会跳过GDB与调试器建立通讯这一关键步骤,也就无法实现自动打开文件并跳到代码所在行的功能了。调试进程也是如此,一定要使用菜单中的“调试进程”或工具栏中的“进程”来开始。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值