六 Prefixes Segment override and LOCK(一)
六 Prefixes Segment override and LOCK
这次我们将结束对prefixes的学习.
剩下的两个Prefixes是"Segment override"和 "LOCK" prefixes.术语"Segment override"可能会让某些初学者感到困惑,但它却像是为Intel 指令系统定制的.
打开我们之前的试验用程序.输入:
Mov eax,[ebx]
可以看到
8B03 MOV EAX,[DWORD DS:EBX]
输入
Mov eax,GS:[ebx]
可以看到
65:8B03 MOV EAX,[DWORD GS:EBX]
65就是一个"segment override prefix",用来改变默认的段为GS.事实上,在使用内存中的数据时,处理器必须首先知道它的段地址和偏移量,但是如果在每个地方都要显式的直接指出段地址,那么在OpCode格式中就必须额外增加一个域,这将会比现有的OpCode体系多占用大量的字节,而且要处理器必须多花费额外的时钟周期来进行解码无论在空间上还是时间上,都不值得!
因此,为了解决这个问题,一个方案诞生了.指令按不同的定义被划分为不同的组,每个组各自有一个默认的段:
CS: for EIP pointer
ES: 目的操作数是内存单元的串指令(movs, cmps等),在这里源操作数是储存在段DS里面.
SS: 堆栈操作(push, pop等)
DS: 剩下的数据操作指令.
有了这个规则,处理器识别当前应该用哪个段将会变得非常简单而直接:如果有“Segment override prefix”,那么就使用这个prefix所指定的段;否则就使用默认的段.
输入
AC
3E AC
可以看到
AC LODS [BYTE DS:ESI]
3E:AC LODS [BYTE DS:ESI]
3E是表示段DS,但是实际上在这里即使不直接指明3E,处理器也是会使用DS的,因为DS是指令LODS的默认段.
对于程序员来说,使用非默认的段将多占用1个字节,并且多花费1个时钟周期去解码.对于其他的那些改变默认内容的指令也如此(如66,67).
编写Win32用户态汇编程序往往不需要去改变段寄存器,但底层程序员可能会用到.现在我们总结一下Win32用户态模式段寄存器的内容:
CS:对于所有的用户态程序这个是一样的,在NT系列操作系统中是1Bh,而在9x中则为227h.如果你记得关于绝对地址跳转的宏,我可以给出一个简单的绝对地址跳转指令格式,但这个在9x中不同.
我们一起看看远跳转的OpCode.EA byte "code",这个字节告诉处理器这是一个直接远跳转的OpCode,当处理器遇到EA,他将得知后面会跟着一个48位地址,低32位是偏移量而高16位确定段寄存器.
既然现在我们知道对于NT代码段寄存器的值是1Bh,所以为了跳转到地址12345678h,我们可以这样编写代码:
db EA ;long jump
dd 12345678h ;offset where to jump
dw 1Bh ;segment selector for code in NT
对于9x段来说只是代码段选择子的值不同而已
db EA ;long jump
dd 12345678h ;offset where to jump
dw 227h ;segment selector for code in 9x
如果限制应用程序运行在特定的操作系统(NT或者9x),这样做很有好处:
;NT=1
absjmp macro addr
ifdef NT
db 0EAh
dd addr
dw 01Bh
else
db 0EAh
dd addr
dw 227h
endif
endm
用法:
absjump 401000h
注释行NT = 1 依赖与你想使用这个绝对地址跳转的系统.
可以试着在OllyDbg中写入一些跳转指令,比如你想跳转到一行,比如
00401013 |. 90 NOP
你可以输入
EA 13 10 40 00
剩下的两个字节在NT中是1B 00 ,而在9x中是27 02.
原文第六帖
