四 Prefixes 66h
四 Prefixes 66h
Prefixes
用OllyDbg加载试验用程序,输入
or eax,-1
Mov ecx,edx
And ebx,ebp
接着对上面每一个OpCode再按这种方式输入一次,如第一个OpCode
第一个字节: 66
然后是OpCode: 83F8FF (Or eax,-1)
对剩余的两条指令用同样的方式输入.可以看到我们用这种方式产生的指令对3条指令都适用,除了16位寄存器指令,如
Or ax,-1
Mov cx,dx
And bx,bp
66h就是所谓的Prefix.
Prefixes是产生OpCode的第一个域.
回忆一下OpCode的6个域:
1. prefixes
2. Code
3. byte mod r/m
4. byte sib
5. offset in command
6. imm. Operand
记住:
在实际的应用中,并不是所有的这6个域都会被用到,但是有一项是一定会有的,即第二项Code,这6个域的顺序绝对不能乱,必须严格按照上面的顺序进行.
Prefixes是所有域中最容易理解的一个,请先明了它的一些特性:
1. 它是唯一的一个可能出现在code之前的域
2. 所有的Prefixes都只有一个字节
3. 在一个OpCode中可能会有多个Prefixes
Prefix 66 的意思是“切换默认的操作数的大小”.例如在有的系统中有两种默认的操作数大小:16位和32位.在Win32编程环境中默认的操作数的大小是32位,但操作数有可能会被写成16位或者32位,唯一的区分方法是看它有没有Prefix 66.
我们来看一些例子,输入下面这些单字节指令:
LODSB
LODSW
LODSD
啊!LODSW和LODSD使用同样的code域AD!其实,LODSW和LODSD这两条指令是同一个指令,只不过它们的操作数的大小不一样而已LODSW使用WORD(不是Win32默认操作数大小)作为操作数,而LODSD则使用DWORD(是Win32默认操作数大小)作为操作数.
所以对于LODSW指令需要用prefix 66切换操作数大小.请注意我们并没有说“指定”而是说“切换”,反映到这个例子中,就是“切换默认的32位操作数到16位”,而不是“指定操作数的大小为16位“.如果默认的操作数大小是WORD,那么切换后就是DWORD;反之,如果默认的操作数大小是DWORD,那么切换后就是WORD.那些没有使用WORD或DWORD的指令(BYTES,QWORDS等),不会对操作数的大小做任何改变.
输入:
Mov al,0ff
Mov al,cl
接着在这两条指令之前加上66h,可以看到
66:B0 FF MOV AL,0FF
66:8A C1 MOV AL,CL
什么都没有被改变.
记住:Prefix 66 仅对操作数为WORD和DWORD的指令起作用.
我们在使用BYTE操作数的指令前加入prefix 66 ,是不是产生了一条非法指令呢?嗯,事实并不是这样子的.
运行指令
66:B0 FF MOV AL,0FF
66:8A C1 MOV AL,CL
没有任何问题,它们和
MOV AL,0FF
MOV AL,CL
的功能是一样的.
如果Prefixes不能对随它之后的OpCode起作用,那么处理器将忽略它.
我们来看看另一个有趣的例子.
另外的一个Prefix rep的作用是让处理器对随后的指令循环执行ecx(cx)次,指令
Inc eax的OpCode是40,我们来看看如何使用prefix F3(rep)使inc eax连续执行3次.
在我们的试验程序里输入
Xor eax,eax
Mov ecx,3
Rep inc eax
然后运行.你将看到两点:
1. 最后eax = 1,这意味着 prefix F3并没有起作用它被忽略了;
2. 没有任何异常(execption)产生.
在OllyDbg里面我们可能会遇到以下两个问题:
1. 如果你输入rep inc eax它将提示“无法识别的指令”,我们试下看,输入F3 40行不行.奥!OllyDbg还是没有正确的识别!~~如果按F8单步执行我们刚刚输入的指令,这样运行的结果是:
1) Eax = 1
2) 没有任何异常发生,处理器忽略了prefix F3.
如果Prefixes不能对随它之后的OpCode起作用,那么处理器将忽略它.
输入包含两个prefix的指令
rep lodsw
66: F3: AD (REP LODS [WORD DS:ESI])
在这条指令中我们看到两个prefix,66和F3.
所以Prefixes域的另一个重要的特性是:一条指令可能只有一个CODE域,一个mod r/m域,或者一个offset域等,但是可以有多个Prefixes.
有关prefix 66h的最后的“新闻”是:
你可能错误的认为在实模式下默认的操作数大小是WORD,而在保护模式下是DWORD,事实并不完全这样.我们唯一可以确定的是当在Win32环境下默认操作数DWORD.因此,你在Win32下使用的任何操作字(WORD)的指令都会变长一个字节(prefix 66),并且需要多花一个时钟周期去执行.这个并不是一点好处也没有,当你习惯于计算数学问题,你可以通过取舍指令大小和运行速度来确定是不是用WORD更好一些.任何使用字的指令将多花你一个字节和一个用来解码的时钟周期.
那么默认操作数大小是如何确定的呢这是被段描述符的第0Dh位确定的.在实模式下它被赋值位0,所以在实模式下默认的操作数往往是WORD.在保护模式下可能是0或者1(在Win32应用程序中是1).
所以:
If (PROTECTED MODE && BIT D = = 1)
AD = LODSD
66 AD = LODSW
Else
AD = LODSW
66 AD = LODSD
这就是我们之前所说的:不同的OpCode可以有同样的助记符,一个OpCode可能有多个不同的助记符.
我们也看到某些OpCode可能有两种不同的功能,而这依赖一些条件.如果你认为指令可以和prefix 66配合使用,你将明白这里的条件和功能分别指的什么.
下次我们继续学习OpCode的第一个域Prefixes.同时我推荐读者查找一下x86指令集查询手册,上面有所有的OpCode对应的助记符和指令构成.
原文第四帖
