欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

六 Prefixes Segment override and LOCK(二)

六 Prefixes Segment override and LOCK(二)

有少数语句不是很好翻译,后面有相应的原文以供对照.
    重要的一点是DS,SS,CS段都只是用户态段的别名.(Important thing to remember is that DS SS CS segment are alias segments in Win32 user mode app. )这意味着我们可以通过任一段寄存器寻址数据.
    如果你正试着做"String到Dwords的转换",你可以使用栈操作,当栈顶指针直接指向.data节,使用push可以直接将常量字符串放置在数据节.
    同样,使用.code节的数据可以这样子:
        .code
    msg db 'Some text',0
    start:
    invoke MessageBox,0,offset sometext,......
    另外,下面的代码完成的功能是一致的:
        .data
    somedw dd 12343
    ......
        .code
    ......
    mov eax,somedw
    mov eax,dword ptr offset somedw
    mov eax,dword ptr DS:offset somedw
    mov eax,dword ptr CS:offset somedw
    mov eax,dword ptr SS:offset somedw
    ......
    我们还须记住的是某些节上的数据可能受到保护,(可以在链接的时候使用/section:[sectionname] option选项改变).然而如果你可以用"ptr somedata"的方式寻址数据,那么你也一定可以任意使用DS,CS,SS作为段选择子.我们将在系统编程方面详细讨论这个.
    
    那么剩下的寄存器呢?
    我们先看ES,我们在串操作指令经常要关心它.
    在DOS时代我们经常要比较ES是否等于DS,来确认我们的源和目的是不是在同一个段.
    在Win32用户态编程中我们不需要这样,但这并不是说ES不再被串操作指令使用.
    我们开始试验之前先做点笔记:ES在包含源和目的两个内存操作数的串操作指令中仍然被使用.
    例如:movsb = move dword ptr DS:ESI to ES:EDI
那么如果对于上例包含两个内存操作数的指令运用Segment override,那么是哪个段选择子被改变呢?
    输入:
    A5 65 A5
    A5        movs  [dword es:edi], [dword ds:esi]
    65:A5     movs  [dword es:edi], [dword gs:esi]
我们看到Segment override改变了源.改变目的选择子是不被允许的!如果你想改变目的,你可以改变ES的值,而不是改变作为选择子的寄存器.所有的包含两个内存操作数的串操作指令都是如此.
    顺便说一下,如果你开始试着去查看OpCode,那么你肯定会看到所有的串操作指令都是单字节的,假设你不使用66 prefix去改变操作数大小,那么在速度允许的情况下,可以写出非常简洁的代码.
    我们先前说到,ES仍然在被使用,但是我们不需要特意的用DS去初始化它,当系统加载程序的时候就已经为我们做好这个了.然而你可能会轻易的破坏它的值然后糟糕的后果立马而至.
    我们看看选择子是如何影响串操作指令的.栈从高址往低址生长,这意味这我们可以使用比当前栈顶指针小的地址,而不会破坏栈内的数据(返回地址,参数等).例如,在OllyDbg里当前栈顶的内容为:
    0012FFC4   77F1B9EA  RETURN to KERNEL32.77F1B9EA
    0012FFC8   0012E2D4
    0012FFCC   77F92CD4  ntdll.77F92CD4
    0012FFD0   7FFDF000
所以我们可以使用0012FFC0以下的地址空间,需要注意的一点是默认的栈大小是1MB,当然可以在链接的时候使用相应的选项去改变这个值;并且不要使用开头的1kb,那是用来捕捉错误的.
    你可以使用一些push和pop指令观察栈的变化情况.
    现在我们给局部变量分配一些空间来检测一下movs对于不同的选择子是怎么样工作的.我们要做的是:
1.用短的字符串填充栈空间
2.以字节为单位将字符串拷贝到接近栈的区域,并且每一步我们指定不同的段寄存器
 - to get impression that selectors in DS,SS,CS pointing to alias segment
 - to check if ES is still matter by spoiling it in last step
    输入:
    lea  edi,[esp-4]
现在edi指向局部变量所在的空间,所以我们不用担心破坏栈中的返回值和参数等等.我们将使用小于等于esp-4的地址空间.
    输入助记符
    std
我们设置方向位为1,串操作指令将使用递减的地址.顺便说一下,当你在窗口回调函数中置方向位,请务必记得在返回前清方向位,因为回调函数是通过置DF=0为系统返回值的.(When you change DF to 1 in your window callback procedure - remember to set it to 0 before your proc returns - your proc returns to system code and the code assumes that DF=0)
    输入另一个助记符
    mov  al,5
    stosb
    dec  al
    输入OpCode
    75 FB
这些指令类似于下面的汇编源码:
    lea edi,[esp-4]
    std
    mov al,5
@@:   
    stosb
    dec al
    jne @B
    
    这里面有一个问题,短转指令的二进制格式是
        0111tttn:imm8
前四位0111确定了只是一个段跳转指令,在调试器里它表现为第一个十六进制位是7,接着的四位是用来确定条件的”tttn”位域,这和许多指令中用来测试标志位的格式是一样的.第二个字节是在跳转指令被解码后将被加到EIP的有符号数.
我们试着解码OpCode 75 FB:
    7  短条件跳转的标志
    5  tttn 0101,0100 代表‘e’,zf = 0;0101 代表‘ne’,zf = 1;可以看到改变最后一位就相当于把条件改变位‘非’,这就是为什么我们叫它“tttn“.
    FB  即-5,我们需要EIP会跳5个字节
1字节  AA             STOS [BYTE ES:EDI]
2字节 FEC8           DEC AL
2字节 75 FB          JNZ SHORT 
------
5字节
    好,我们回到"register override".
    切换到数据窗口,在OllyDbg的Command文本框里输入命令 d  esp-4 ,并往上面滚动一行,这样好看到当前栈顶的内容,因为我们将用字符串来填充esp-4往下(地址变小)的空间.我们按F8单步执行,可以清楚的看到字节01 02 03 04 05 是如何存放的.
    
    我们首先来验证一下DS,CS,SS是否是段选择子的别名.(First we check if DS CS SS are alias semgent selectors)
    输入
    lea  esi,[edi+5]
这条指令执行后esi将指向之前我们构造的字符串(01 02 03 04 05).并且edi已经指向该字符串的末尾,这样我们就可以开始拷贝了.
    movsb的OpCode是 A4
    A4 = COPY FROM [BYTE DS:ESI] TO [BYTE ES:EDI]
    在A4前使用segment override prefix我们就能改变源选择子,这样的prefix有CS-2E,SS-36,ES-26.
    
    我们先看看默认的情况:
    输入
    A4
然后使用F8单步执行,并观察数据窗口内容的变化.
    现在我们看看CS的情形:
    输入
    2E A4
然后使用F8单步执行,并观察数据窗口内容的变化.
同样测试一下36和26.
    如果操作正确,你将可以看到字符串的头四个字节被成功拷贝,这表明(That shows that all SS DS CS ES
pointed to the same alias segment through different selectors.).
    你可以试一下别的segment override prefix,你得到的肯定是一个错误提示.
    我们用刚才字符串剩下的一个字节来测试一下ES.
    输入
    66 6A 00
即push word 0
    输入
    pop es
    A4
同样的,按F8键,当到达lodsb指令行时,OD的状态栏会提示异常.
    这表明ES仍然在双内存操作数串操作指令中被使用.我们可以输入
    push ds
    pop  es
来解决刚才的问题.
    执行这两条指令,然后输入A4,然后一切正常.
    值得提到的另一个段寄存器是FS,FS被用作异常处理.然而现在你记住这句话就好了,每当你使用FS操作数据的时候,将多消耗你一个字节以及一个时钟.
    当然使用这个寄存器要看你的意图是什么.
    下一个Prefix是LOCK.
    如果在Pentium 和 Pentium MMX下使用如F00FC7C8这样的指令,可以冻结处理器,在这里我仅仅引用其中的一些描述.如果你想了解这个可以去看Dr.Dobbs的文章.
    对于LOCK prefix我没有什么要说的,在Intel的手册里有很好的解释.在结束这部分之前我想提一些关于prefixes的特殊的用途,但是在P III下处理器会忽略掉这些,Intel提出在新的模型中那些特殊的用法可能有新的特殊意义,不正当的用法可能导致一些不可预料的结果.关于新一代Prefixes应该提到3E “hint” prefix.
    只有在多处理器系统才需要LOCK.当对内存操作数执行如下操作过程:
    1.读取内存
    2.在算术逻辑单元操作读取到的内存变量
    3.写回内存
这时才需要.
    可以看到在第1和第3阶段有一段间隔,当目前的处理器正在处理读取到的内存数据的时候,其他处理器也可以读取同一个内存数据,最后可能导致其中的一个被覆盖.而LOCK信号可以阻止任何其他处理器访问共享内存,直到指令将数据写回内存.大多在同步系统中被使用.

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功