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。当然这个结论可能不正确,因为这毕竟是统计学结果。如果想要得到更准确的结果,我们可以采样更多的数据,例如向上递减1000字节。以保证结果的可靠性。

实现好的代码片段如下:

typedefstruct {

ULONGpInstrAddr; //上一条指令的位址

ULONGHitCount; //命中次数

}PREV_INSTR_HITTEST,*PPREV_INSTR_HITTEST;

ULONGGetPrevIp(ULONG Eip)

{

PREV_INSTR_HITTESTHitTest[16];

ULONGCurrentAddr = Eip - 1;

ULONGPrevAddr;

ULONGDisasmLimit = 0x100;

ULONGlen;

ULONGi;

ULONGPrevAddr_MaxHit = 0;

ULONGMaxHit = 0;

if(!Eip)

returnFALSE;

memset(&HitTest,0,sizeof(HitTest));

while(DisasmLimit)

{

PrevAddr= CurrentAddr;

if(!IsAddressExist(PrevAddr)) //保证地址空间可读

break;

while(1)

{

len= FastDisasm(PrevAddr);

if(len!= -1 && len)

{

if(len+ PrevAddr >= Eip)

{

AddHit(&HitTest[0],16,PrevAddr);

break;

}

elseif(len + PrevAddr > Eip)

{

break;

}

}

else

{

break;

}

PrevAddr+= len;

}

DisasmLimit--;

CurrentAddr--;

}

for(i= 0; i < 16; i++)

{

if(HitTest[i].HitCount> MaxHit)

{

MaxHit= HitTest[i].HitCount;

PrevAddr_MaxHit= HitTest[i].pInstrAddr;

}

}

returnPrevAddr_MaxHit;

}

转载于:https://www.cnblogs.com/lucelujiaming/p/9467907.html

基于IntelVt技术的Linux内核调试器 - 2相关推荐

  1. 开源项目-基于Intel VT技术的Linux内核调试器

    本开源项目将硬件虚拟化技术应用在内核调试器上,使内核调试器成为VMM,将操作系统置于虚拟机中运行,即操作系统成为GuestOS,以这样的一种形式进行调试,最主要的好处就是调试器对操作系统完全透明.如下 ...

  2. Linux 内核调试器 调试指南

    Linux 内核调试器内幕 KDB 入门指南 Hariprasad Nellitheertha (nharipra@in.ibm.com), 软件工程师, IBM 简介: 调试内核问题时,能够跟踪内核 ...

  3. linux内核调试器ftrace使用

    本文的实验是在ubuntu(内核版本3.19.0)上运行的. ftrace原理 ftrace是一个追踪器框架,其中一个强大的追踪器就是函数追踪器(即函数的调用过程).它使用gcc的-pg选项让内核中的 ...

  4. 椒图加固软件linux说明书,【椒图科技】Linux内核调试器 - 安全牛课堂 - 领先的信息安全在线教育平台...

    { "i18nChapterName": "章", "i18nUnitName": "节", "i18nLes ...

  5. Linux内核调试方法总结【转】

    转自:http://my.oschina.net/fgq611/blog/113249 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核 ...

  6. Linux内核调试方法总结

    [转]Linux内核调试方法总结 目录[-] 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG ...

  7. Linux内核调试方法【转】

    转自:http://www.cnblogs.com/shineshqw/articles/2359114.html kdb:只能在汇编代码级进行调试: 优点是不需要两台机器进行调试. gdb:在调试模 ...

  8. Linux内核调试技术指南

    前两天,完成了ucos在2440上的移植,以及boot的修改.今天突然想到,我在linux下,该如何来编写,调试比较复杂的驱动.我想这个问题应该从如何调试内核入手,先转载两个文字,待西西看来. 系统搭 ...

  9. 基于windows PE文件的恶意代码分析;使用SystemInternal工具与内核调试器研究windows用户空间与内核空间...

    基于windows PE文件的恶意代码分析:使用SystemInternal工具与内核调试器研究windows用户空间与内核空间 ******************** 既然本篇的主角是PE文件,那 ...

最新文章

  1. 组合使用Laravel和vfsStream测试文件上传
  2. MATLAB表白利器
  3. This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de 错误解决办法
  4. sharemouse切窗口就锁定了什么原因_Excel表格如何锁定,被锁定的表格又该如何解除锁定呢?...
  5. linux最大文件句柄数量总结
  6. 有什么作用_轴套有什么作用?
  7. HTTP缓存与Spring示例
  8. 软件工程学习进度第六周暨暑期学习进度之第六周汇总
  9. Codeforces Round #249 (Div. 2) A. Queue on Bus Stop
  10. L1-075 强迫症 (10 分)-PAT 团体程序设计天梯赛 GPLT
  11. [转载] SimpleHTTPServer解释:如何使用Python发送文件
  12. Linux硬链接和符号链接(转)
  13. 银行排队叫号系统项目总结
  14. 人力资源管理系统概要设计说明书
  15. PHOTOSHOP 小技巧
  16. 网站服务器如何直接播放视频,视频放服务器链接直接播放
  17. Bootstrap下拉菜单失效的解决方法+使用Bootstrap制作响应式网页
  18. GPS导航电文——第三子帧数据解析
  19. 2020年-数据库实验详述-BUPT 信通院-- 十安辰
  20. Git ~ commit 规范

热门文章

  1. linux cp使用注意事项
  2. leetcode算法题--螺旋矩阵 II
  3. linux内核网络协议栈--linux bridge(十九)
  4. 如何判断DropDownList中是否包含某个项
  5. MySQL B+树索引和哈希索引的区别
  6. Linux下搜索文件常用方法
  7. 1.1初识python
  8. 8个写完以后就可以让你成为顶尖开发者的有趣应用程序
  9. 使用注解打造自己的IOC框架
  10. 双击jar运行main主函数实现-fatJAR