首页
社区
课程
招聘
[原创]编写自己的反汇编引擎
发表于: 2011-1-20 19:30 30925

[原创]编写自己的反汇编引擎

2011-1-20 19:30
30925

首先声明本帖首发于赏金论坛,希望大家能多多关注这个正在成长的论坛www.sgoldcn.com 

想要编写自己的反汇编引擎并不是一件难事,只不过是要学习大量指令相关东西。学习指令最详细的文档莫过于官方手册,但由于是英文的,让很多新手无法着手。这里我提供一些中文与英文的教程,大家可以全面参考。因为已经有人讲解了指令格式等基本的东西,所以这里我就不再讲解了,需要时只做编程需要的描述。先说明一下,我的代码是查表法,自己建立Opcode表来进行查询,随后放出来的只是解码引擎,不加翻译成助记符的模块。因为翻译模块当时选用了让我现在无比懊悔的switch  case,导致有很多已知和未知的翻译错误,解码引擎可以说是基本不会有错误,可以正确解码4张opcode表、Group表、FPU表,因为当时写引擎时定位的就是能够解码全部intel指令,现在基本已经达到了。 写的时候得到了同事的帮助,在此表示感谢。 
  

中文教程: 
打造自己的反汇编引擎——Intel指令编码学习报告 
intel指令格式与长度反汇编引擎ADE32分析  http://bbs.pediy.com/showthread.php?t=54180 
x86/x64 指令编码内幕(适用于 AMD/Intel)http://www.mouseos.com/x64/index.html 
x64 指令系统之指令编码内幕http://linux.chinaunix.net/bbs/viewthread.php?tid=1050480 
关于MMX指令  http://dev.gameres.com/search_articles.asp?page=1&name=MMX 
Inetl官方手册汉化版,看雪上有翻译好的。 
  

英文教程: 
《ArtOfDisassembly》 如果英文好,一定要看这个教程 
关于x86指令比较好的英文站点http://www.sandpile.org/ 
Inetl官方手册下载 http://www.intel.com/products/processor/manuals/ 
AMD官方手册下载 http://developer.amd.com/documentation/guides/Pages/default.aspx 
  

代码: 
一个超轻量级的反汇编引擎 http://cyclotron.ycool.com/post.2758008.html 
XDE反汇编引擎源代码 http://www.zeroplace.cn/article.asp?id=142 
OllyDBG代码 
  

         上面这些信息足够足够用了,如果英文好的话,《ArtOfDisassembly》一定要看,非常不错的教程。由于这里我不偏重于基础,而是拿代码说话,所以我假设你已经对指令格式等有所了解,如果你不了解就看我写的这些东西,你会觉得我说的不清不楚,不明不白,你看的也会是云里雾里。如果看官方手册,必须看的是《Volume 2A:Instruction Set Reference, A-M》中的CHAPTER 2  INSTRUCTION FORMAT,即第2章。《Volume 2B: Instruction Set Reference, N-Z》中的Appendix A Opcode Map,即附录A。  
    
上面这张图是指令格式,可以看到由6部分组成,所以我们应该定义一个结构体,并且结构体中应该包含以上六个部分,这样才能在翻译指令时把指令的各部分信息保存在结构体中。 
typedef struct  _INSTRUCTION 

  BYTE  RepeatPrefix;  //重复指令前缀 
  BYTE  SegmentPrefix;  //段前缀 
  BYTE  OperandPrefix;  //操作数大小前缀0x66 
  BYTE  AddressPrefix;  //地址大小前缀0x67 
  

  BYTE  Opcode1;    //opcode1 
  BYTE  Opcode2;    //opcode2 
  BYTE  Opcode3;    //opcode3 
  
  BYTE  Modrm;      //modrm 
  
  BYTE  SIB;      //sib 
  
  union         //displacement联合体 
  { 
    BYTE  DispByte; 
    WORD  DispWord; 
    DWORD DispDword; 
  }Displacement; 
  

  union         //immediate联合体 
  { 
    BYTE  ImmByte; 
    WORD  ImmWord; 
    DWORD ImmDword; 
  }Immediate; 
  
  BYTE; InstructionBuf[32]; //保存指令代码 
  DWORD dwInstructionLen; //返回指令长度 
    
}INSTRUCTION,*PINSTRUCTION; 
  

指令前缀Instruction prefixes由4部分组成,所以这里我也直接定义了4个部分。 
指令前缀共有以下4组,一条指令最多可使用4种不同组前缀。 
重复与锁指令:              F0    Lock 
                                               F2    REPNE/REPNZ (cmps,scas) 
                                               F3     REP    (movs,ins outs,lods,stos) 
                                                        REPE/REPZ                (cmps,scas) 
  

段改写与分支指令:     2E     CS 
                                               36     SS 
                                               3E     DS 
                                               26     ES 
                                               64     FS 
                                               65     GS 
                                               2E     分支未实现 
                                               3E     分支实现 
  

操作数大小改写指令:66     改变操作数大小 
  

地址大小改写指令:     67     改变寻址方式 
  

         Opcode是可变的,有三种可能:1字节、2字节、3字节,所以定义了三个Opcode。Modrm与SIB没什么可说的,直接定义就行了。Displacement 与Immediate都被定义成了联合体,因为他们也都是可变的,有可能是1字节、1字、1双字大小。最后一个dwInstructionLen是保存指令的长度,一条指令最短只要1字节,最长…自己数数吧。 
         定义好指令格式结构体之后,我们还需要把4张Opcode表(没错,是4张,因为第三张表还会被分成2张表)和1张Group表还有FPU表用自己的方式表示出来。为了能描述所有的表,我们还需要定义自己的标志。 
#define     ModRM                               0x00000001              //含有ModRM 
#define     Imm8                                    0x00000002              //后面跟着1字节立即数 
#define     Imm16                                 0x00000004              //后面跟着2字节立即数 
#define     Imm66                                 0x00000008              //后面跟着立即数(Immediate),立即数长度得看是否有0x66前缀 
#define     Addr67                                 0x00000010              //后面跟着偏移量(Displacement),偏移量长度得看是否有0x67前缀 
#define     OneByte                              0x00000020              //只有1个字节,这1个字节独立成一个指令 
#define Mxx                                          0x00100000              //mod != 11时才可解码 
#define     TwoOpCode0F                    0x00000040              //0x0F,2个opcode 
#define     Group                                   0x00000200              //Group表opcode 
#define     Reserved                             0x00000400              //保留 
#define     PreSegment                        0x00400000              //段前缀 
#define     PreOperandSize66            0x00800000              //指令大小前缀0x66 
#define     PreAddressSize67             0x01000000              //地址大小前缀0x67 
#define     PreLockF0                            0x02000000              //锁前缀0xF0 
#define     PreRep                                 0x04000000              //重复前缀 
#define     Prefix                                    (PreSegment+PreOperandSize66+PreAddressSize67+PreLockF0+PreRep) 
         看到上面这些东西,大家一定有打退堂鼓的意思了。其实没那么难,只要你像我说的,在看这些之前先学习上面的教程,这些东西你肯定能看得懂的,而且每行后面我也做了注释。上面这些只是列出了opcode表1能用到的标志,像opcode表2和3还有Group与FPU都没列出来。其实写这个引擎最难的也就是定义标志,然后把几张表里面填充相应标志。 
 
         来看看这张表,这是opcode1的表,表里面的每个部分都很重要,甚至一个字母的差别都会影响指令的走向。像Eb、Gb、Ev、Gv等都代表着这个指令的一些特性,不要偷懒,好好把上面的教程看了,你会懂的,最好看官方原版手册。 
         好了,现在开始填表,先来填Opcode1表: 
 
         Opcode1表我在word里面填充了一下,每个不同的颜色都需要不同的处理,在word里我只填充了Opcode1,其他表格我都是拿着不同颜色的笔在纸上画出来的,想学习就别偷懒,自己赶紧画一下吧。填充表格不是一天两天、一个星期两个星期能完成的,可能需要你一个月两个月。所以,想写引擎确实是个苦力活,不是编写的难点太多,而是难在太多简单却冗长的工作要做。 
         至于如何填表,我这里举上几例,如表格的第一项00,他是ADD指令,下标是Eb,Gb 
E: A ModR/M byte follows the opcode and specifies the operand. Opcode后面跟着ModRM 
G: The reg field of the ModR/M byte selects a general register. ModRM中的reg被用作选择通用寄存器 
b: Byte, regardless of operand-size attribute.不管是否有66前缀操作数都只是一个字节 
         可见,E和G都说明这个指令需要ModRM,所以填充标志为ModRM。再来看一个8D,大家可以看官方手册《Volume 2B: Instruction Set Reference, N-Z》的Appendix A Opcode Map,即附录A,里面有所有的表。8D这条指令是LEA,下标是Gv,M,G我们已经知道了,说明这条指令肯定有ModRM,再来看M和v: 
M:The ModR/M byte may refer only to memory (for example, BOUND, LES, LDS, LSS, LFS, LGS, CMPXCHG8B). ModRM只能用于内存操作数。 
         可见这个M里面还是有说道的,所以我们必须去查一下关于ModRM的说明。来到《Volume 2A:Instruction Set Reference, A-M》中的CHAPTER 2  INSTRUCTION FORMAT,即第2章看一下。 
 
         从这张图里可以看到,当ModRM中的Mod不等于11时才有寻址内存的操作,像[EAX]、[EBX]+disp32等。所以这个M标志要说明的就是ModRM中的Mod != 11,所以我们定义了一个标志Mxx,只能在Mod != 11时才可解码,如果等于了11,说明没这条指令。 
#define Mxx              0x00100000              //mod != 11时才可解码 
         所以8D指令的标志应该是ModRM+Mxx。 
         用OD1.0测试一下8D指令: 
0040322B      8D00          LEA EAX,DWORD PTR DS:[EAX] 
0040322D      8DC0          LEA EAX,EAX                  ;  非法使用寄存器 
         可以看到8D00可以正常显示,因为Mod == 00,而8DC0是不应该显示的,因为Mod ==11,但这里OD给解码了,这是OD的Bug,用WinDBG就不会解码8DC0: 
7c9211e0 8d              ??? 
7c9211e1 c00000          rol     byte ptr [eax],0 
         因为8DC0不能组成一组有效的指令,所以直接显示成???,并把C0当成下一组指令的代码, OD2.0版本也未修正这个Bug,初学者不建议用OD,因为他有许多Bug会误导你,可以使用WinDBG,毕竟微软的东西还是信得过的,虽然他的引擎中我也发现过解码错误,但是非常的少。OD的引擎不是很完美,解码会出现很多错误,我已经向Olly本人发过多个BugReport,他也回复说会在下个版本中修正。  

         WinDBG的引擎解码错误的指令我没有记录,只记得个crc32指令,错误在官方手册里面说CRC32  r32, r/m8,目的操作数只能是32位寄存器,而WinDBG用的是ax,大家可以查一下OD2.0与IDA,使用的都是EAX。 
 
WinDBG: 
 
         其实,我有很多细节都省略掉了,真正打算学习反汇编引擎的人是会深挖进去的,如果想写一个自己的引擎,那就先动手去填写标志吧。最后填充完是这样子的: 
/************************************************************************/ 
/* 1个opcode指令表                                                      */ 
/************************************************************************/ 
DWORD OneOpCodeMapTable[256]= 

         /*               0                          1                          2       …      E                          F                 */ 
         /*0*/        ModRM,           ModRM,           …,              OneByte,          TwoOpCode0F, 
         /*1*/        ModRM,           ModRM,           …,              OneByte,          OneByte,          
         /*2*/        ModRM,           ModRM,           …,              PreSegment,    OneByte,          
         …… 
         /*F*/         PreLockF0,        OneByte,          …               Group,               Group 
}; 
         一个表最多是256条指令,所以定义的大小为256。 
         今天先到这吧。 

 opcode1表填充图.rar (178 K) 
 Test_Disassembler.rar (4505 K)
您正在看的文章来自赏金论坛 http://www.sgoldcn.com,原文地址:http://www.sgoldcn.com/read.php?tid=915.如有转载,请误删除此信息


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (25)
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
2
很不错  我曾经写过一个反汇编引擎   好不容易坚持下来   还有很多错误  这里的艰辛谁能知道?
2011-1-20 20:19
0
雪    币: 878
活跃值: (496)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
有现成的谁自己写
2011-1-20 21:10
0
雪    币: 601
活跃值: (256)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
4
掌握原理性的东西就掌握了技术的灵魂
2011-1-20 21:13
0
雪    币: 38
活跃值: (52)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
自己顶一个了[QUOTE][/QUOTE]
2011-1-20 21:31
0
雪    币: 217
活跃值: (41)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
typedef struct  _INSTRUCTION
{
  BYTE  RepeatPrefix;  //重复指令前缀
  BYTE  SegmentPrefix;  //段前缀
  BYTE  OperandPrefix;  //操作数大小前缀0x66
  BYTE  AddressPrefix;  //地址大小前缀0x67
  

  BYTE  Opcode1;    //opcode1
  BYTE  Opcode2;    //opcode2
  BYTE  Opcode3;    //opcode3
  
  BYTE  Modrm;      //modrm
  
  BYTE  SIB;      //sib
  
  union         //displacement联合体
  {
    BYTE  DispByte;
    WORD  DispWord;
    DWORD DispDword;
  }Displacement;

根据这个结构没看出指令参数在什么哪里

很奇怪不提供翻译成助记符的函数啊,不提供 你自己怎么测试和调bug啊

而且你提供助记符翻译模块,代码才容易看懂,不提供给不懂汇编指令翻译的人,你的代码

也无法使用啊!

有源代码么,Test_Disassembler.rar是源代码吗? 又下载不了

然后是很佩服lz,研究的很仔细,十分强大,只能拜摸,无法学习
2011-1-21 08:50
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
7
你没有仔细思考作者的方法   作者是用 opcode 来翻助记符的   指能说不太完善   但是已经很不错了
请不要说  只能膜拜无法学习之类的。
2011-1-21 09:14
0
雪    币: 217
活跃值: (41)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
8
个人愚昧的认为

输入的结构应该表示为: 指令+参数

然后提供一个函数把上面的结构,显示为字符串,这个主要给人看

而指令+参数的表示,很方便做指令分析

虽然保留了比较多的原始指令的解析信息很不错,但是我认为,翻译出来的指令后

我根本不关注他,有多少个前缀,有多少个opcode,有没有Modrm,是否有SIB

因为指令最终我们想知道的主要是他操作的参数,和指令表达的意思,至于解析的结构

我不必知道

比如发现一条指令,如果他是push,我就只想知道他push的是立即数,寄存器,还是地址内容

同样mov也是,我只想知道mov的目标和源,个人感觉ida的汇编引擎很牛b,od很多翻译部了,和错误

的地方,他都没问题,很想知道ida设计的输出结构,网上很多开源反汇编引擎,不是输出助记符,就是

输出,解析的opcode和mod,sib,如果我要做指令分析,或者虚拟机,前者,我必须用对比,字符串的方法

显然不可用,后者它没输出参数,只有指令结构,我可以说必须再做一次解析。

以上个人愚见。
2011-1-21 09:21
0
雪    币: 217
活跃值: (41)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
不好意思  只能拜摸 无法学习是玩笑话

不必当真

lz成果非常不错,不可否认。
2011-1-21 09:23
0
雪    币: 209
活跃值: (83)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
10
这只是一个引擎模块,还有一个解析这个结构成助记符的模块,那个模块我写的太垃圾,所以没敢放,或许以后会拿出来丢人吧,全是switch case,后悔ing,当初就不应该选择这条路。。。。
不要说不翻译成给人看的助记符就没有用,要知道,反病毒引擎,VM都不是给人看的,是给机器看的,只要机器看懂了,机器就能干活了。。。后面我会给你个例子,看看这个引擎都能干什么。。。
2011-1-21 10:36
0
雪    币: 217
活跃值: (41)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
11
关注中 ...............................
2011-1-21 13:45
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
12
我根本不关注他,有多少个前缀,有多少个opcode,有没有Modrm,是否有SIB

因为指令最终我们想知道的主要是他操作的参数,和指令表达的意思,至于解析的结构

我不必知道

当你做虚拟机引擎的时候  你就知道这些东西是必须要知道的。而且很重要
2011-1-21 16:49
0
雪    币: 324
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
楼主的成果来之不易,期待加关注!
2011-1-22 00:25
0
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark备查
2011-1-22 08:35
0
雪    币: 163
活跃值: (75)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
15
反汇编指令确实有很多现成的工具可以借鉴,难点是怎么构造确定的CFG和定位函数起始结束位置,特别是针对于被模糊过的二进制码,最近正在研究这个东西,希望有成果后跟大家分享吧
2011-1-22 13:27
0
雪    币: 348
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
研究这个东西厉害
2011-1-22 23:30
0
雪    币: 217
活跃值: (41)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
17
确实是这样,不过后者也重要,所以最好2个都有,但是后者要表示所有参数,我感觉更难定义,前者是解析必经的过程,可以说是把解析中的中间信息保留,感觉要容易点。

个人看法。
2011-1-24 09:32
0
雪    币: 69
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
额,ia32 指令看了。
但是不想写,觉得太麻烦了
2011-1-24 16:49
0
雪    币: 347
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
提供了很多资料,感谢
2011-1-27 19:52
0
雪    币: 2323
活跃值: (4118)
能力值: ( LV12,RANK:530 )
在线值:
发帖
回帖
粉丝
20
里面介绍的中英文教材很不错~~ArtOfDisassembly~嘿嘿~~希望咱也能写个反汇编引擎出来!
2011-1-28 08:59
0
雪    币: 362
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
学习学习...........
2011-1-28 09:15
0
雪    币: 23
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
我觉得我什么都不会了
2011-1-31 10:23
0
雪    币: 233
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
打死不自己写这东西。。。
2011-2-16 23:16
0
雪    币: 433
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
浏览起来有压力,看来路还远。
2018-2-1 09:28
0
雪    币: 965
活跃值: (89)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
感谢楼主分享
2018-2-7 17:27
0
游客
登录 | 注册 方可回帖
返回
//