自写反汇编引擎(二)

自写反汇编引擎(二)

资料:
1.intel指令格式与长度反汇编引擎ADE32分析(https://bbs.pediy.com/thread-54180.htm)

2.编写自己的反汇编引擎(https://bbs.pediy.com/thread-128411.htm)

在上一篇文章中我们已经讲解来看一下基础的IA-32指令,那么在这篇文章我们将具体来实现反汇编引擎的编写,首先上效果图(只是完成了一些基本的内容,其实什么也没有)在这里插入图片描述
接下来,让我们正式开始:

一、编写前准备

在编写引擎的整个中都需要用到的一个东西,定义一个好的结构体有助于我们梳理编写思路,那具体需要定义在结构体中定义那些成员呐?我希望的是能根据自己的具体需要去考虑,毕竟每个人的思路都不一样,虽然我们能借鉴别人的程序,但思路还是我们自己的,也就是说程序是死的,而人是活的(笑),而我将其中需要用到的每一个元素都定义在了结构体中,所以在我编写的时候,我只需要去查看结构体中对应的成员,就可以让整个思路如同一条线路一样在我脑中很清晰的呈现。

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    E;				//E区域在前
	BYTE    G;				//G区域在前
	BYTE    Size;           //判断大小(8/16/32)=(1/2/3)

	BYTE	Sib;			//sib

	BYTE    GRP;

	union					
	{
   
		BYTE	DispByte;
		WORD	DispWord;
		DWORD	DispDword;
	}Displacement;          //位移联合体

	union					
	{
   
		BYTE	ImmByte;
		WORD	ImmWord;
		DWORD	ImmDword;
	}Immediate;             //立即数联合体


	BYTE  	InstructionBuf[32];	//保存指令代码
	DWORD    InstrucCompil[32];   //保存汇编指令
	DWORD    Identification[32];  //指令表示,1表示字符,2表示0x16(8),3表示0x16(16),4表示0x16(32)
	DWORD	dwInstructionLen;	 //返回指令长度

}INSTRUCTION, *PINSTRUCTION;

typedef struct _REGISTER
{
   
	BYTE	_0;
	BYTE	_1;
	BYTE	_2;
	BYTE	_3;
	BYTE	_4;
	BYTE	_5;
	BYTE	_6;
	BYTE	_7;
}REGISTER, *REGIETER;

在这里我就根据了自己的需要定义了两个结构体,当然因为我属于菜鸡,所以定义的结构体不是很美观(笑),但我还是希望大家看一下每个成员的具体作用,特别是最后的几个数组、dwInstructionLen和REGISTER结构体,我需要强调一下,这个几个成员是非常重要的,我们如何打印出机器码与对应的汇编代码都与这几个成员有关。(具体的作用我之后会详细讲解)

二、填充opcode表

这一点就是我主要借鉴的地方了,具体思路也非常简单,我们在上一篇文章讲到了指令的组成结构,那么这里就是需要将这些结构进行区分与定义,我们给每一个属性定义一个标识,再将opcode表中的内容划分为不同的标识组合,那么在我们查询指令的时候,就可以直接查询这些标识了,这是非常利于我们编写代码的,任何一种标识所对应的属性,我们都能一目了然,这也就大大提升了我们的效率,这一部分网上有许多的文章进行了详细的讲解,我就不再赘述了(其实是我的理解还十分浅显,远远不如网上的大神们,就不在这里献丑了)。在本篇文章的开篇处给出的资料相信会对你有一个很好的启发,而我就在列举一下我的标识就行了。

#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 TwoOpcode			0x00000040		//0x0F,2个opcode
#define ThreeOpCode0F38		0x00000080		//0x0F38,3个opcode
#define ThreeOpCode0F3A		0x00000100		//0x0F3A,3个opcode
#define Grp				    0x00000200		//Group表opcode
#define Reserved			0x00000400		//保留
#define MustHave66			0x00000800		//必须有0x66前缀,只在opcode38/3A表中有这样的指令
#define MustHaveF2			0x00001000		//目前只有一个指令是必须有0xF2前缀的:0FF0
#define MustHavePrefix		0x00002000		//必须有前缀
#define MustNo66			0x00004000		//必须没有0x66前缀,在扫描指令时出现66则取标志指令看是否有此标志,有则直接返回1,说明此66前缀是多余的
#define MustNoF2			0x00008000		//意义同上
#define MustNoF3			0x00010000		//意义同上

#define StringInstruction	0x00020000		//指令重复前缀0xF2 0xF3(REPNE REP/REPE)在Opcode1表中只能与下面7组字符串指令组合,
                                                        // 		0xA4: 0xA5:		MOVS
														// 		0xA6: 0xA7:		CMPS
														// 		0xAE: 0xAF:		SCAS
														// 		0xAC: 0xAD:		LODS
														// 		0xAA: 0xAB:		STOS
														// 		0x6C: 0x6D:		INS
														// 		0x6E: 0x6F:		OUTS

#define Uxx					0x00040000		//rm用于寻XMM,只能在mod==11时才可以解码,可能的opcode: 66 0F 50/C5/D7/F7	F2 OF D6
#define Nxx					0x00080000		//rm用于寻MMX,只能在mod==11时才可以解码,可能的opcode: OF C5/D7/F7			F3 OF D6
											//Nxx没用,因为在Uxx里面已经全部包括了Nxx的可能 
#define Mxx					0x00100000		//mod != 11时才可解码
#define Rxx					0x00200000		//mod == 11时才可解码

#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)

#define FPUOpCode			0x08000000		//FPU




static DWORD OneOpcode[256] = {
   /*0      1      2      3     4      5       6        7       8      9      A      B     C      D       E        F*/
	/*0*/  ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, OneByte, OneByte, ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, OneByte, TwoOpcode,
	/*1*/  ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, OneByte, OneByte, ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, OneByte, OneByte,
	/*2*/  ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, PreSegment, OneByte, ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, PreSegment, OneByte,
	/*3*/  ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, PreSegment, OneByte, ModRM, ModRM, ModRM, ModRM, Imm8, Imm66, PreSegment, OneByte,
	/*4*/  OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte,
	/*5*/  OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte,
	/*6*/  OneByte, OneByte, ModRM, ModRM, PreSegment, PreSegment, PreOperandSize66, PreAddressSize67, Imm66, ModRM + Imm66, Imm8, ModRM + Imm8, OneByte + StringInstruction,  OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction,
	/*7*/  Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8,
	/*8*/  Grp, Grp, Grp, Grp, ModRM, ModRM, ModRM, ModRM, ModRM, ModRM, ModRM, ModRM, ModRM, ModRM + Mxx, ModRM, Grp,
	/*9*/  OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, Imm16 + Imm66, OneByte, OneByte, OneByte, OneByte, OneByte,
	/*A*/  Addr67, Addr67, Addr67, Addr67, OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction, Imm8, Imm66, OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction, OneByte + StringInstruction,
	/*B*/  Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Addr67, Addr67, Addr67, Addr67, Addr67, Addr67, Addr67, Addr67,
	/*C*/  Grp, Grp, Imm16, OneByte, ModRM + Mxx, ModRM + Mxx, Grp, Grp, Imm8 + Imm16, OneByte, Imm16, OneByte, OneByte, Imm8, OneByte, OneByte,
	/*D*/  Grp, Grp, Grp, Grp, Imm8, Imm8, Reserved, OneByte, Reserved, Reserved, Reserved, Reserved, Reserved, Reserved, Reserved, Reserved,
	/*E*/  Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm8, Imm66, Imm66, Imm66 + Imm16, Imm8, OneByte, OneByte, OneByte, OneByte,
	/*F*/  PreLockF0, OneByte, PreRep, PreRep, OneByte, OneByte, Grp, Grp, OneByte, OneByte, OneByte, OneByte, OneByte, OneByte, Grp, Grp
};

三、打印汇编代码

在正式开始编写前我认为必须讲解一下我对于打印的思路了,网上对于编写反汇编引擎的文章有很多,我也确实借鉴了一些,但我发现一点那就是这些文章都只讲解了如何根据opcode表,Modrm表等找到反汇编的机器码,而并没有去打印对应的汇编代码,这也是我为什么要写这篇文章的原因,我相信有许多像我一样的初学者希望能不仅仅是局限于打印出机器码,而是希望能有汇编代码,虽然这样会大大增加其中的工作量,但请相信我,在看见自己的反汇编引擎能打印出正确的汇编代码时,那种成就感是非常让人愉悦的。

我在仔细研究了intel手册的opcode表后发现,其中每一个机器码对应的汇编代码都是不一样的,即使你咋一看觉得会有很多相识之处,但请记住,我们是希望去打印汇编代码的,所以重点要看每一个机器码对应的汇编代码,这样你就能明白我所说的了。

那么既然是这样就不能轻易地编写了,到这里其实我已经有了两种思路了,一种是直接写很多种判断,将每一个机器码对应的情况都一个个的打印出来,但我并不推荐这种方法,一来这样的判断实在是太多了,而且这样就会使得我们上面填充opcode表变得豪无意义(我可不想做无用功);那么现在就只剩第二种方法了,我们将每种类型分开后再去判断,我相信有人看到这里就会有疑问了:这和第一种有什么不一样吗?诶,别急,往下看就知道了。

我们已经在上面定义了许多表格属性了,那么就要利用起来,并且如果通过查找表格属性去打印就会节省很多的判断,不同的类型就到对应位置去查询,然后我们在将判断的情况统一存入一个地址中,再统一的打印,这样是不是就使得整个编写更有可读性,让思路更清晰了呐?没错,聪明的小伙伴已经发现了,我们在第一步结构体中定义的几个数组就是用在这里的,接下来请往下看。

static const char *sets[229] = {
   
	"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",				//8byte
	"ax", "cx", "dx", "bx", "sp", "bp", "si", "di",				//16byte
	"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",     //32byte

	//段寄存
	
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值