二 OpCode结构
二 OpCode结构
机器指令回答处理器3个问题:
1. What to do ?
2. With what to do ?
3. How to do ?
其中第三个问题是可选的.
要回答这些问题,机器指令可能有多个逻辑块.在列举这些逻辑块之前我们先来做一些练习.
为了更好的试验,在开始这些练习之前我们先来定制一个特别的程序:
让程序变得小一点,这样OllyDbg可以更快的加载我们的程序;
用一个字节的助记符填充它,这样就可以把代码段当作白纸一样使用;
我们可以利用批处理文件或者设定快捷键等方式,更方便地加载我们的程序.
下面是这个程序的源代码:
.code
start:
rept 256
nop
endm
call ExitProcess
end start
它将在程序中插入256 个 nop 指令.
既然现在你已经知道在call ExitProcess 之前有256个 90h ,我们是否可以用16进制来编码?
.code
start:
rept 256
db 90h
endm
call ExitProcess
end start
或者我们检验一下看看
db 90h
nop
xchg eax,eax
这三个是不是一样的.我们可以这样改写程序:
.code
start:
rept 20
nop
endm
rept 118
xchg eax,eax
endm
rept 118
db 90h
endm
call ExitProcess
end start
当我们用OllyDbg加载编译好的可执行文件后,就可以看到在call ExitProcess之前全部是90 nop.
OpCode域简介
有6个域是OpCode可能会用到的,它们的名字是什么这并不重要,重要的是它们的排列顺序.
它们是:
1. Prefixes
2. Code
3. byte Mod r/m
4. Byte SIB
5. Offset in command
6. imm. Operand
并不是这所有的6个域都会被用到,但是有一项却是一定会有的,那就是第2项 Code,有些指令甚至只会用到这一项.
在试验程序中输入
C3
2F
90
AD
所有输入的这些OpCode都只有Code域.
(提示:它们对应的助记符依次为:retn, das,nop,lod**.)
现在我们知道lod**的OpCode是ACh.我们来看看是不是可以增加额外的域来扩展它的功能呢.我们输入
F3 AC
可以看到
rep lod**.
我们可以确定AC 是Code域,F3 AC 则是[Prefixes] [Code],所以F3是Prifixes域.F3表示的是Rep Prefixes,它也能与mov**, sto**等指令连用.
现在请试着输入一些可以使用rep prefixes的助记符,并观察对应的OpCode;然后再在Ctrl+e窗口中输入这些以F3开头的OpCode,观察对应的助记符。
我们首先应该记住:OpCode由6个域组成,它们不必都用上,但是Code是一定会有的.
我们再来看六个BCD码运算指令,输入
DAA
DAS
AAS
AAA
AAM
AAD
在Intel的文档中,这些都是只包含1个域的指令,这意味着它们都只有Code域.然而最后两条指令却是2字节的.我们可以通过观察这些指令,然后断定这些指令的格式和附加的域.
先不要看下面的答案,试着自己想一想AAM和AAD和别的指令有什么不同……
27 DAA
2F DAS
3F AAS
37 AAA
D4 0A AAM
D5 0A AAD
很明显我们可以看到:
1. AAM和AAD都是2字节的,然而其余的4条指令都是1字节的.
2. AAM和AAD的OpCode的第2 个字节都是0Ah
不知你是否想起这两条指令的描述:
AAM: divide AL by 10
商 放在AH里
余数 放在AL里
AAD: AL = AL * 10 + AL
两者的操作都与10 有关,而且它们的第二个字节都是 10(0Ah).那么0Ah会不会是偶然的呢?会不会是操作数的一部分呢?那AAM和AAD的指令格式会不会不是
AAM: D4 0A
AAD: D5 0A
而是:
AAM: D4 imm8
AAD: D5 imm8
以及 imm8 可以是任何别的值呢?
我们可以验证一下,输入:
Mov al,8
D4 07 ;作用和D40A相同,只是除以7而不是10
如果我们是正确的,那么按F8单步执行刚输入的指令后 AH(商) = 1, AL(余数) = 1.
现在,我们又知道了一种新的指令格式:
[ CODE ] [ imm ] (域2 和 域6)
同时我们也发现某些 OpCode 没有对应的助记符.在这里我得说一下,Oleh (OllyDbg的作者)允许使用立即数作为AAM和AAD的操作数.
理解了OpCode 的规则,将有助于底层程序员明白一些鲜为人知的事情--一些未在文档上列出的OpCode,这也是掌握OpCode的另一个好处.
之后我们将详细学习OpCode的每一个域.
现在我们来看一些基本的规则:虽然并不是6个域都是必要的,但是,它们的排列顺序绝对不能乱,必须严格按照上面的顺序进行.有些域或许不会出现,但只要出现了,编号小的域就绝对不允许出现在编号大的域的后面,反之亦然.
例如,[Prefixes] [code] 的顺序绝不允许变为[code] [Prefixes].
输入
0110
和
1001
你将看到它们的不同.
(提示:0110 --add dword ptr [eax], edx 1001--adc byte ptr [ecx], al)
下次我们将讨论处理器是如何确认OpCode的开始和结束的,我们也会看到 2 + 2是怎样等于3的.
原文第二帖
