硬编码

#硬编码(Intel x86)

平台:Win8 CPU Intel x86

需要用到的工具:OllyDbg (验证用) WinHex

需要用到的资料:Intel® 64 and IA-32 Architectures Software Developer’s Manual

资料地址:链接:https://pan.baidu.com/s/1kBRd5BFlEL0eA5ldDP_KQQ 提取码:qkzb

写作目的:讲解一下如何将一个二进制的机器码通过查手册转换为汇编指令(仅限32位,64位自己看手册扩展)

首先:让我们用WinHex来打开一个.exe的可执行文件,如下图:
在这里插入图片描述
上图红色方框中就是这个.exe文件的内容,我们可以看到,它全是由二进制组成(其排版是按PE格式进行排版的,PE格式自行了解),为了便于阅读WinHex将其转换成了十六进制进行展示,换句话说,计算机只认识0和1,任何其他的数它都不认识,都会被转换为0和1; 这些0和1细分由可以分为两部分,数据和指令;数据和指令在二进制层面并没有明确的界限,如果把存数据的地址赋值给EIP (指令指针寄存器,它存什么地址CPU下次就会执行什么地址的指令,简单的来说,它像一个指挥官,它指哪CPU打哪)CPU就会将其作为指令来执行,但是指令本身是有自己的格式(每个生产厂商自己定义的格式Intel有Intel的格式,ARM有ARM的格式)的,如果数据不满足指令的格式,CPU就会执行错误,如果数据遵从指令的格式,CPU也是会执行的;通俗来说硬编码就是指令所要遵守的指令格式,硬编码就是机器码,就是指令;

那么Intel的指令格式长什么样子?我们来看看下面这张图(这张图在我们上面给的资料的557页)
在这里插入图片描述
由上图我们可以看到这个指令格式由六部分组成,其中两部分又可以分成3小段;当然不是每一个指令都有这六个部分,资源有限嘛,当然不能每个指令都占那么多资源了;

那么我们下面来一部分一部分的看下这指令格式的意思咯:

Instruction Prefixes:

Instruction Prefixes部分,这个部分有些人把它叫前缀指令,有的人把它叫指令前缀,这个嘛别在意叫法了,知道意思就行了,在我们下面都叫他指令前缀吧!指令前缀为可选格式,可以有也可以没有,CPU判断某个指令是不是指令前缀主要是根据值来判断的,指令前缀是有限的,就只有那几个;

这个指令前缀的玩意它分四组(Tips:懂英文的直接看我们资料的557也558就行了,原滋原味):

1.LOCK和repeat指令前缀

指令前缀 十六进制
LOCK F0H
REPNE/REPNZ F2H
REP/REPZ F3H

2.段指令前缀

段寄存器 十六进制
CS 2EH
SS 36H
DS 3EH
FS 64H
GS 65H

3.操作数宽度指令前缀

只有一个十六进制的:66H

4.地址宽度指令前缀

只有一个十六进制的:67H

指令前缀每组在一个指令中只能出现一个,其顺序没有影响,如第一组出现了F0H就不能出现F2H和F3H当然还可以出现其他组的指令前缀;

操作数宽度指令前缀用于改变操作数的宽度,如55H(H代表这各数是十六进制)在32位模式下对应的汇编指令是push eax,如果加上操作数宽度前缀66H后变为6655H则对应的汇编为push ax; 55H在16位模式下对应的汇编指令是push ax,如果加上操作数宽度前缀66H后变为6655H则对应的汇编为push eax;

地址宽度指令前缀用于改变默认的地址,32位模式下加地址宽度指令前缀变16位地址,16位下加变32位;

Opcodes

前面我们说指令前缀就只有那么几个,所以除了那几个以外,如果CPU遇到其他的就当opcode 处理了,每条指令opcode部分是必须的,最少一个字节,最多三个字节;opcode ModR/M and SIB三个部分就能决定一个指令有多长;opcode决定后面有没有ModR/M ,ModR/M决定后面有没有SIB;

定长指令:就是固定长度的指令,只要opcode确定了,这指令的长度就确定了;如50H 51H 52H等

变长指令:是仅仅通过opcode无法确定指令长度的指令,如00H;

如何查是否是定长指令和变长指令,可以通过上面的架构资料在1975页的表来区分;该表部分长相如下图:
在这里插入图片描述
图中为1字节opcode,纵向为高4位,横向表示低4位,如常用的编程中常见的push ebp(机器码55H),则前一个5为纵向,后一个5位横向;我们可以简单的通过图中标示的E G开头来判断是否是变长指令,有

E G开头的都是变长指令;

为看懂这表,我们还需要看资料1967页 的Codes for Addressing Method ,这有对参数对应字母的解释;如Eb中E的解释为:A ModR/M byte follows the opcode and specifies the operand. The operand is either a general-purpose register or a memory address. If it is a memory
address, the address is computed from a segment register and any of the following values: a base register, an index register, a scaling factor, a displacement. b的解释为:Byte, regardless of operand-size attribute. 其他的这种形式的两个字母组合类似,不再废话;

定长指令我们可以通过这两张表来查出机器码对应的汇编指令,如50H先找纵向的5,再找横向的0,确定单元格,行的最上方就有PUSH这个指令,然后单元格里面就有气对应的寄存器,rAX在32位中表示EAX,所以50H对应汇编指令为PUSH EAX

ModR/M

我们知道了怎么区分定长指令和变长指令,定长指令我们通过opcode就能确定其长度,那么变长指令呢;变长指令后面就需要一字节的ModR/M或者还需要加上SIB部分来确定其长度;
在这里插入图片描述
上图来自资料561页,可见它将一个字节分为了三部分,前两位,中间三位和后三位,中间三位来描述1975页里的G,如下列出几个典型的机器码:

0x88 MOV Eb, Gb G:通用寄存器

0x89 MOV Ev, Gv E寄存器/内存

0x8A MOV Gb, Eb b字节

0x8B MOV Gv, Ev v:word, double,quadword

所以我们要将其转换为汇编就需要将E G确定,我们可以通过中间三位来查出G,通过前两位和后三位来查出E,从而得到对应汇编指令;
在这里插入图片描述
那当我们遇到下图中的这种情况又怎么确定?
在这里插入图片描述
SIB

要解决上面的问题,我们就需要SIB段了
在这里插入图片描述
由最开始的这这张格式图,我们可以看到SIB同样将一个字节分三部分,这时我们就可以通过564页的表,来查了
在这里插入图片描述
查的方法同ModR/M部分的查法, 不再重复;

至于后面两部分,前面的确定了,后面的就确定了,不再过多介绍;

猜你喜欢

转载自blog.csdn.net/lifeshave/article/details/85237439