如果你学过汇编,把机器码反汇编就是:
6A 00 PUSH 0
6A 00 PUSH 0
68 00 30 40 00 PUSH 00403000;这个就是字符串Hello World在内存的地址
6A 00 PUSH 0
FF 15 08 20 40 00 CALL 00402008;这个就是MessageBoxA函数引入表在内存的地址
6A 00 PUSH 0
FF 15 00 20 40 00 CALL 00402000;这个就是ExitProcess函数引入表在内存的地址
那么以后我们看到6A 是不是直接写汇编PUSH呢?看以上面是很有规律的?
不是的,你看68 00 30 40 00 这行就没有6A 汇编也是PUSH。当然有些是有规律的,如果不考虑前面的机器码,我们单独看到50 那就是PUSH EAX ,51 就是ECX,52 就是EDX,53 就是EBX....但如果我看到88 89 2E 33等等,那么我们要怎样识别得出来?这需要一系列化的学习,因为这部分要比汇编,C++等高级语更难学,实际上上高级语言编译连接后生成的PE最后还是和我们的机器码一样的,只不过我们现在直接写吧了。
这个是很简单的机器码编程,至于系统是怎么识别这些机器码的
深入部分我们以后再说。下面我们从很简单的知识学起,在学习这些知识之前我假定你会一些汇编知识了,慢慢学习,你可能,精通这个词我可能不敢说。起码你见过的汇编代码你都会看了
至于调试,逆向,一般来说已经不成问题。
为了以后以后方便,我顺便复习了一下16进制和2进制相对应,
所有的机器码都是由下面的数字组成,我们编程和看得到的是16进制机器码,
系统运行时自动把16进制 转换为相反的2进制
16进制 0 1 2 3 4 5 6 7
2进制 0000 0001 0010 0011 0100 0101 0110 0111
16进制 8 9 A B C D E F
2进制 1000 1001 1010 1011 1100 1101 1110 1111
熟识寄存器
8位寄存器 AL CL DL BL AH CL DH BH
16位寄存器 AX CX DX BX SP BP SI DI
32位寄存器 EAX ECX EDX EBX ESP EBP ESI EDI
对应的确十六进制 0 1 2 3 4 5 6 7
对应的二进制 000 001 010 011 100 101 110 111
好以后我们一看到 AL AX EAX 我就想起 十六进制0 二进制进制000 后面的同理
这是怎么说的,因为如果你机器是16位的,那么AX 就是十六进0 二进制制000
现在大多数是32位机的,那么EAX 就是十六进制0 二进制制000
没有8位机,64位我们现暂不讨论
注意:我上面列出的,并不是说以后我们看到机器吗0就代表AL,AX,EAX 寄存器了 后面的同样
好把上面的背熟,其实也挺简单的,多看几次就行,你一定想问了,这和我们机器码编程有什么关系?
让你记这些是为我们后面理解打基础的,因为后面我们常常要用到,我不想让你们查表
就这几个数字,干脆我们在这里死记硬背下来了。因为当初我也是偷懒就没有背,后来要翻来翻去
浪费了好多时间。我们今天就记这些。
指令结构
一条指令的结构分为6大部分
Prefixes Opcode ModR/M SIB Displacement Immediate
一条指令的6大部分一定得有Opcode别的部分可以没有,如果有那么结构的顺序不能变
Prefixes一定是在Opcode前的,ModR/M 一定是在Opcode 后,如果有SIB则一定有ModR/M
如果有ModR/M 不一定有SIB(至于原因我们后面再说),Displacement 在Immediate 前
Prefixes 1字节(可有多个,每个一字节)
Opcode 1~3字节
ModR/M 1字节
SIB 1字节
Displacement 1字节,2字节,4字节
Immediate 1字节,2字节,4字节
一般来说一条指令不会超过15字节
先把上面记好了,学习这部分得慢慢来不能急,为的是打好基础
Prefixes指令前缀
Prefixes指令前缀有4组
第1组:Lock 和repeat前缀 Lock 机器码 F0
REPNE和REPNZ 机器码 F2
REP,REPE和REPZ 机器码 F3
第2组:段寄存器改写前缀 CS: 机器码 2E
DS: 机器码 3E
ES: 机器码 26
SS: 机器码 36
FS: 机器码 64
GS: 机器码 65
第3组:操作数大小改写前缀: 机器码 66
第4组:地址大小改写前缀: 机器码 67
我们要把上面的对应关系背出来,前缀就是上面这么多了11个
前面说了 Prefixes 1字节(可有多个,每个一字节) 例:F0 2E 二字节
X86指令系统为什么要分组呢?我们从例子中去理解
例:F0 F3 26 A4(A4是Opcode) 它被这样解析成二条指令F0 和F3 26 A4,因为F0 F3同在第1组
反汇编:
F0 LOCK
F3 26 A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR ES:[ESI] 如果没有26默认的段是DS:
那前面不是说一条指令中一定要有Opcode的吗?没错,前面F0只是前缀,没有Opcode,尽管可以,但已经没有了意义,有些系统简单会显示一个异常,所以实际上只有F3 26 A4这条指令了,前缀的顺序随便的,你也可以写成 26 F3 A4,注意Opcode 一定得在前缀之后,如果写成A4 26 F3或26 A4 F3那变成了另外的指令了,功能意义不一样了,这就是分组,就是说同一组中只能有一个出现,如果多个出现,有些系统就会省去前面的出现的那个
我们再来看一些例子
例:F3 2E 66 A4 2E 这样解析 F3 2E 66 A4 和 2E
反汇编:
F3 2E 66 A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR CS:[ESI]
2E ;多余前缀
例:F3 2E 66 2E A4 2E 它分为F3 和2E 和66 2E A4 和2E
这4条指令中只有一条有意义66 2E A4 占3字节
反汇编:
F3 PREFIX REP: ; 多余前缀
2E PREFIX CS: ; 多余前缀
66 2E A4 MOVS BYTE PTR ES:[EDI],BYTE PTR CS:[ESI]
2E ; 多余前缀
所以前缀就是改变Opcode的操作方式,Opcode是受到了Prefixes制约的
例:50 66 50
反汇编:
50 PUSH EAX
66 50 PUSH AX
4组11个机器码
有关前缀就先说这么多知识,理解起来可能有点难度,所以大家得花点时间在这部分
因为这部分是下一部分的关键,这里一定要记熟,不要看过就行,到时再翻转来看就麻烦了
Mod/RM
我们先跳过Opcode,先来讲习Mod/RM这部分,上面说了如果指令中有Mod/RM那么只占一个字节
尽管只有一字节,但它分得相当细,一字节占8位
Mod/RM实际上分为Mod(6-7bit),Reg/Opcode(3-5bit),R/M(0-2bit),三部分,R就是Register指寄存器,M就是Memory指内存:
7 6 5 4 3 2 1 0
Mod Reg/Opcode R/M
可以看到Mod/RM中的Reg/Opcode(3-5bit)也可以做为Opcode,
所以这就是为什么我要先说Mod/RM这部分
Mod/RM为一字节8位,我们来取得各个部分:
Mod=(Mod/RM >> 6) & 3
Reg/Opcode = (Mod/RM >> 3) & 7
R/M = Mod/RM & 7
Mod有2位所以有2^2=4种寻址模式 00 01 10 11
当Mod为00时所操作的是[寄存器]
当Mod为01时所操作的是[寄存器+8位数](8位数,由Displacement部分的值决定)
当Mod为10时所操作的是[寄存器+32位数](32位数,由Displacement部分的值决定)
当Mod为11时所操作的是寄存器,R/M所表示的是寄存器
当Mod不等于11和R/M的值为100的时候,表示指令后面有SIB部分,该内存数由SIB来决定
例:机器码8B C1 对应的反汇编是什么呢?8B是Opcode 对应汇编mov(为什么是mov,以后再说)
C1就是Mod/RM了,因为只占一字节,我们来或分Mod(6-7bit),Reg/Opcode(3-5bit),R/M(0-2bit),
16进制C1对应的二进制为11 000 001,Mod(11),Reg/Opcode(000),R/M(001),
上面说了当Mod为11时所操作的是寄存器,R/M所表示的是寄存器所以形式应为MOV 寄存器,寄存器
Reg/Opcode(000)代表的是EAX ,R/M(001)代表的是ECX
那么8B C1到底是MOV EAX,ECX还是MOV ECX,EAX这个决定由Opcode 8B的倒数第二位来决定
如果倒数第二位为0那么Reg/Opcode为源操作数,R/M为目的操作数:MOV R/M,Reg/Opcode
如果倒数第二位为1那么R/M为源操作数,Reg/Opcode为目的操作数:MOV Reg/Opcode,R/M
8B二进制为10001011倒数第二位为1所以:MOV Reg/Opcode(000),R/M(001)
8B C1反汇编就是MOV EAX,ECX
再看一个例:8B C8
8B二进制为10001011倒数第二位为1所以:MOV Reg/Opcode,R/M
C8 11 001 000 Mod(11),Reg/Opcode(001),R/M(000)
反汇编就是MOV ECX,EAX
SIB
上面说过Mod/RM中当Mod不等于11和R/M的值为100的时候,表示指令后面有SIB部分,该内存数由SIB来决定 SIB也占一字节8位
SIB的形式是这样的 [base + index * n + displacement] n=1,2,4,8取其中一个
7 6 5 4 3 2 1 0
SS index base
base 和 index 各占三位标示8个寄存器
sib为一字节8位,我们来取得各个部分:
SS=(sib >> 6) & 3
index = (sib >> 3) & 7
base = sib & 7
SS有2位所以有2^2=4种寻址模式 00 01 10 11
当SS为00时所操作的是[寄存器] 这个寄存器只是base的,没有index
当SS为01时所操作的是[寄存器*2]
当SS为10时所操作的是[寄存器*4]
当SS为11时所操作的是[寄存器*8]
注意 如果index寄存器等于esp标示为none 为什么呢,mov eax, [esp*1] 是可行的,但mov eax, [esp*2]却是无效的 所以在这种情况下SS不能大于2
例:机器码8B 04 24 对应的反汇编是什么呢?8B是Opcode 对应汇编mov(为什么是mov,以后再说)
04就是Mod/RM了,因为只占一字节,我们来或分Mod(6-7bit),Reg/Opcode(3-5bit),R/M(0-2bit),
16进制04对应的二进制为00 000 100,Mod(00),Reg/Opcode(000),R/M(100)
上面说了当Mod为00时所操作的是[寄存器]这样的形式,R/M所表示的是[寄存器]所以形式应为MOV 寄存器,[寄存器],为什么不是 MOV [寄存器],寄存器 这个决定由Opcode 8B的倒数第二位来决定
如果倒数第二位为0那么Reg/Opcode为源操作数,R/M为目的操作数:MOV R/M,Reg/Opcode
如果倒数第二位为1那么R/M为源操作数,Reg/Opcode为目的操作数:MOV Reg/Opcode,R/M
Reg/Opcode(000)代表的是EAX ,R/M(100) 由于R/M为100 所以这个部分由SIB决定
8B二进制为10001011倒数第二位为1所以 MOV eax,[寄存器]
下一字节就是 24 了 二进制就是00 100 100 SS(00),index (100),base (100)
[base + index * n + displacement]
由于SS(00) 所以只有base寄存器了 [base]=[esp]
反汇编就是MOV EAX,[ESP]
例:8B 8C 88 44 33 22 11
8B是Opcode 对应汇编mov
8C是Mod/RM二进为10(mod) 001(reg/opcode) 100(r/m)
mod 10 就是[寄存器]+disp32形式
我们看看前言机器码编程的部分
你一定问这个机器码是怎样识别的,你又是怎样知道这些数字能显示一个对话框?
如果你学过汇编,把机器码反汇编就是:
6A 00 PUSH 0
6A 00 PUSH 0
68 00 30 40 00 PUSH 00403000;这个就是字符串Hello World在内存的地址
6A 00 PUSH 0
FF 15 08 20 40 00 CALL 00402008;这个就是MessageBoxA函数引入表在内存的地址
6A 00 PUSH 0
FF 15 00 20 40 00 CALL 00402000;这个就是ExitProcess函数引入表在内存的地址
那么以后我们看到6A 是不是直接写汇编PUSH呢?看以上面是很有规律的?
不是的,你看68 00 30 40 00 这行就没有6A 汇编也是PUSH。当然有些是有规律的,如果不考虑前面的机器码,我们单独看到50 那就是PUSH EAX ,51 就是ECX,52 就是EDX,53 就是EBX....但如果我看到88 89 2E 33等等,那么我们要怎样识别得出来?这需要一系列化的学习,因为这部分要比汇编,C++等高级语更难学,实际上上高级语言编译连接后生成的PE最后还是和我们的机器码一样的,只不过我们现在直接写吧了。
这个是很简单的机器码编程,至于系统是怎么识别这些机器码的
深入部分我们以后再说。下面我们从很简单的知识学起,在学习这些知识之前我假定你会一些汇编知识了,慢慢学习,你可能,精通这个词我可能不敢说。起码你见过的汇编代码你都会看了
至于调试,逆向,一般来说已经不成问题。
为了以后以后方便,我顺便复习了一下16进制和2进制相对应,
所有的机器码都是由下面的数字组成,我们编程和看得到的是16进制机器码,
系统运行时自动把16进制 转换为相反的2进制
16进制 0 1 2 3 4 5 6 7
2进制 0000 0001 0010 0011 0100 0101 0110 0111
16进制 8 9 A B C D E F
2进制 1000 1001 1010 1011 1100 1101 1110 1111
熟识寄存器
8位寄存器 AL CL DL BL AH CL DH BH
16位寄存器 AX CX DX BX SP BP SI DI
32位寄存器 EAX ECX EDX EBX ESP EBP ESI EDI
对应的确十六进制 0 1 2 3 4 5 6 7
对应的二进制 000 001 010 011 100 101 110 111
好以后我们一看到 AL AX EAX 我就想起 十六进制0 二进制进制000 后面的同理
这是怎么说的,因为如果你机器是16位的,那么AX 就是十六进0 二进制制000
现在大多数是32位机的,那么EAX 就是十六进制0 二进制制000
没有8位机,64位我们现暂不讨论
注意:我上面列出的,并不是说以后我们看到机器吗0就代表AL,AX,EAX 寄存器了 后面的同样
好把上面的背熟,其实也挺简单的,多看几次就行,你一定想问了,这和我们机器码编程有什么关系?
让你记这些是为我们后面理解打基础的,因为后面我们常常要用到,我不想让你们查表
就这几个数字,干脆我们在这里死记硬背下来了。因为当初我也是偷懒就没有背,后来要翻来翻去
浪费了好多时间。我们今天就记这些。
指令结构
一条指令的结构分为6大部分
Prefixes Opcode ModR/M SIB Displacement Immediate
一条指令的6大部分一定得有Opcode别的部分可以没有,如果有那么结构的顺序不能变
Prefixes一定是在Opcode前的,ModR/M 一定是在Opcode 后,如果有SIB则一定有ModR/M
如果有ModR/M 不一定有SIB(至于原因我们后面再说),Displacement 在Immediate 前
Prefixes 1字节(可有多个,每个一字节)
Opcode 1~3字节
ModR/M 1字节
SIB 1字节
Displacement 1字节,2字节,4字节
Immediate 1字节,2字节,4字节
一般来说一条指令不会超过15字节
先把上面记好了,学习这部分得慢慢来不能急,为的是打好基础
Prefixes指令前缀
Prefixes指令前缀有4组
第1组:Lock 和repeat前缀 Lock 机器码 F0
REPNE和REPNZ 机器码 F2
REP,REPE和REPZ 机器码 F3
第2组:段寄存器改写前缀 CS: 机器码 2E
DS: 机器码 3E
ES: 机器码 26
SS: 机器码 36
FS: 机器码 64
GS: 机器码 65
第3组:操作数大小改写前缀: 机器码 66
第4组:地址大小改写前缀: 机器码 67
我们要把上面的对应关系背出来,前缀就是上面这么多了11个
前面说了 Prefixes 1字节(可有多个,每个一字节) 例:F0 2E 二字节
X86指令系统为什么要分组呢?我们从例子中去理解
例:F0 F3 26 A4(A4是Opcode) 它被这样解析成二条指令F0 和F3 26 A4,因为F0 F3同在第1组
反汇编:
F0 LOCK
F3 26 A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR ES:[ESI] 如果没有26默认的段是DS:
那前面不是说一条指令中一定要有Opcode的吗?没错,前面F0只是前缀,没有Opcode,尽管可以,但已经没有了意义,有些系统简单会显示一个异常,所以实际上只有F3 26 A4这条指令了,前缀的顺序随便的,你也可以写成 26 F3 A4,注意Opcode 一定得在前缀之后,如果写成A4 26 F3或26 A4 F3那变成了另外的指令了,功能意义不一样了,这就是分组,就是说同一组中只能有一个出现,如果多个出现,有些系统就会省去前面的出现的那个
我们再来看一些例子
例:F3 2E 66 A4 2E 这样解析 F3 2E 66 A4 和 2E
反汇编:
F3 2E 66 A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR CS:[ESI]
2E ;多余前缀
例:F3 2E 66 2E A4 2E 它分为F3 和2E 和66 2E A4 和2E
这4条指令中只有一条有意义66 2E A4 占3字节
反汇编:
F3 PREFIX REP: ; 多余前缀
2E PREFIX CS: ; 多余前缀
66 2E A4 MOVS BYTE PTR ES:[EDI],BYTE PTR CS:[ESI]
2E ; 多余前缀
所以前缀就是改变Opcode的操作方式,Opcode是受到了Prefixes制约的
例:50 66 50
反汇编:
50 PUSH EAX
66 50 PUSH AX
4组11个机器码
有关前缀就先说这么多知识,理解起来可能有点难度,所以大家得花点时间在这部分
因为这部分是下一部分的关键,这里一定要记熟,不要看过就行,到时再翻转来看就麻烦了
Mod/RM
我们先跳过Opcode,先来讲习Mod/RM这部分,上面说了如果指令中有Mod/RM那么只占一个字节
尽管只有一字节,但它分得相当细,一字节占8位
Mod/RM实际上分为Mod(6-7bit),Reg/Opcode(3-5bit),R/M(0-2bit),三部分,R就是Register指寄存器,M就是Memory指内存:
7 6 5 4 3 2 1 0
Mod Reg/Opcode R/M
可以看到Mod/RM中的Reg/Opcode(3-5bit)也可以做为Opcode,
所以这就是为什么我要先说Mod/RM这部分
Mod/RM为一字节8位,我们来取得各个部分:
Mod=(Mod/RM >> 6) & 3
Reg/Opcode = (Mod/RM >> 3) & 7
R/M = Mod/RM & 7
Mod有2位所以有2^2=4种寻址模式 00 01 10 11
当Mod为00时所操作的是[寄存器]
当Mod为01时所操作的是[寄存器+8位数](8位数,由Displacement部分的值决定)
当Mod为10时所操作的是[寄存器+32位数](32位数,由Displacement部分的值决定)
当Mod为11时所操作的是寄存器,R/M所表示的是寄存器
当Mod不等于11和R/M的值为100的时候,表示指令后面有SIB部分,该内存数由SIB来决定
例:机器码8B C1 对应的反汇编是什么呢?8B是Opcode 对应汇编mov(为什么是mov,以后再说)
C1就是Mod/RM了,因为只占一字节,我们来或分Mod(6-7bit),Reg/Opcode(3-5bit),R/M(0-2bit),
16进制C1对应的二进制为11 000 001,Mod(11),Reg/Opcode(000),R/M(001),
上面说了当Mod为11时所操作的是寄存器,R/M所表示的是寄存器所以形式应为MOV 寄存器,寄存器
Reg/Opcode(000)代表的是EAX ,R/M(001)代表的是ECX
那么8B C1到底是MOV EAX,ECX还是MOV ECX,EAX这个决定由Opcode 8B的倒数第二位来决定
如果倒数第二位为0那么Reg/Opcode为源操作数,R/M为目的操作数:MOV R/M,Reg/Opcode
如果倒数第二位为1那么R/M为源操作数,Reg/Opcode为目的操作数:MOV Reg/Opcode,R/M
8B二进制为10001011倒数第二位为1所以:MOV Reg/Opcode(000),R/M(001)
8B C1反汇编就是MOV EAX,ECX
再看一个例:8B C8
8B二进制为10001011倒数第二位为1所以:MOV Reg/Opcode,R/M
C8 11 001 000 Mod(11),Reg/Opcode(001),R/M(000)
反汇编就是MOV ECX,EAX
SIB
上面说过Mod/RM中当Mod不等于11和R/M的值为100的时候,表示指令后面有SIB部分,该内存数由SIB来决定 SIB也占一字节8位
SIB的形式是这样的 [base + index * n + displacement] n=1,2,4,8取其中一个
7 6 5 4 3 2 1 0
SS index base
base 和 index 各占三位标示8个寄存器
sib为一字节8位,我们来取得各个部分:
SS=(sib >> 6) & 3
index = (sib >> 3) & 7
base = sib & 7
SS有2位所以有2^2=4种寻址模式 00 01 10 11
当SS为00时所操作的是[寄存器] 这个寄存器只是base的,没有index
当SS为01时所操作的是[寄存器*2]
当SS为10时所操作的是[寄存器*4]
当SS为11时所操作的是[寄存器*8]
注意 如果index寄存器等于esp标示为none 为什么呢,mov eax, [esp*1] 是可行的,但mov eax, [esp*2]却是无效的 所以在这种情况下SS不能大于2
例:机器码8B 04 24 对应的反汇编是什么呢?8B是Opcode 对应汇编mov(为什么是mov,以后再说)
04就是Mod/RM了,因为只占一字节,我们来或分Mod(6-7bit),Reg/Opcode(3-5bit),R/M(0-2bit),
16进制04对应的二进制为00 000 100,Mod(00),Reg/Opcode(000),R/M(100)
上面说了当Mod为00时所操作的是[寄存器]这样的形式,R/M所表示的是[寄存器]所以形式应为MOV 寄存器,[寄存器],为什么不是 MOV [寄存器],寄存器 这个决定由Opcode 8B的倒数第二位来决定
如果倒数第二位为0那么Reg/Opcode为源操作数,R/M为目的操作数:MOV R/M,Reg/Opcode
如果倒数第二位为1那么R/M为源操作数,Reg/Opcode为目的操作数:MOV Reg/Opcode,R/M
Reg/Opcode(000)代表的是EAX ,R/M(100) 由于R/M为100 所以这个部分由SIB决定
8B二进制为10001011倒数第二位为1所以 MOV eax,[寄存器]
下一字节就是 24 了 二进制就是00 100 100 SS(00),index (100),base (100)
[base + index * n + displacement]
由于SS(00) 所以只有base寄存器了 [base]=[esp]
反汇编就是MOV EAX,[ESP]
例:8B 8C 88 44 33 22 11
8B是Opcode 对应汇编mov
8C是Mod/RM二进为10(mod) 001(reg/opcode) 100(r/m)
mod 10 就是[寄存器]+disp32形式
8B二进制为10001011倒数第二位为1所以 MOV reg/opcode,r/m
8B ecx,r/m 由于R/M为100 便有一字节SIB 88
88 二进制10(Scaled) 001(index ) 000(base)
[base + index * n + displacement]=[eax+ ecx * 4 + 11223344]
反汇编就是 MOV ECX,[EAX+ECX*4+11223344]
Displacement
ModR/M 决定了sib和Displacement存在,没有ModR/M就一定不会有sib Displacement,有sib Displacement就一定有ModR/M
可以看到当ModR/M中的mod 为00时是没有Displacement
immediate
这个相对简单的,这是和opcode一起编码的。有singed 和unsigned
这个要看opcode怎么处理了
下面的是大牛
The Svin
http://www.asmcommunity.net/board/index.php?topic=8963
罗聪
http://www.luocong.com/learningopcode
xiep
http://bbs.pediy.com/showthread.php?t=66501
egogg
http://bbs.pediy.com/showthread.php?t=75094
mik
http://linux.chinaunix.net/bbs/thread-1050480-1-1.html
摘自邪恶八进制http://hi.baidu.com/xieebajinzhi/blog/item/efca25f758f1322a730eecf2.html