第八章
本章,我们将来看看前面章节忽略了的一些重要的指令。我们学习完了这部分,就可以开始破解一些小玩意儿了。
循环指令
为了实现循环可以使用前面介绍过一些指令。例如,你可以将任意通用寄存器指定为计数器(通常ECX作为计数器使用),你可以将其初始化为需要循环的次数,然后执行循环体,接着计数器递减1,判断计数器是否为0,如果计数器不为0继续重复前面的过程,如果计数器为0,就不继续循环了,而直接执行下面的代码。代码如下:
XOR ECX,ECX
MOV ECX,15h
将计数器初始化为循环次数15h。接下来就是循环体了:
Label:
DEC ECX
该计数器每次递减1。
其实就是循环体了,循环体里面可以是任意指令。
最后,你需要添加一个判断计数器是否为0的指令以及条件跳转指令。
CMP ECX,0
JNE Label
第一次判断,计数器的值为14h,因为14h不为0,所以将继续执行循环,以此类推,直到计数器为0为止。
我们完整的来写一遍上面的循环:
XOR ECX,ECX
ADD ECX,15h
Label:
DEC ECX
;循环体
TEST ECX,ECX
JNE Label
让我们在OD上输入上面的代码:
黄突出显示的是循环代码,这个部分将重复执行,直到ECX的值为0。循环体里面包含了多个NOP指令。
我们来单步跟踪一下循环代码,亲眼看一下循环的执行过程。按一下F7键看看ECX是怎么初始化的。
再次按F7键,计数器将变成15h,保存了需要循环的次数。
然后继续按F7键,直到DEC ECX,这个时候我们的计数器的值将减少至14h。
继续F7单步直到TEST ECX,ECX。该指令判断ECX是否为0,为0零标志位Z就置1,这样就停止循环,否则将继续循环。
因为此时计数器不为0,零标志位Z没有置1,所以条件跳转指令JNZ将跳转到401007地址处。下一步计数器继续递减1,这一次减少至13h。继续单步跟踪直到计数器为0。
当计数器为0时,零标志位Z将置1,这个时候JNZ将不会跳转至401007地址处,而是继续向下执行。
这里需要注意一下,JNZ指令与我们前面使用过的JZ刚好相反,当零标志位Z为0的时候跳转,为1的时候不跳转。
见到灰箭头,意味着什么?-这里意味着跳转不会发生。按F7键我们将跳出循环继续执行下面的代码。
我们使用熟悉的指令模拟了一个最简单的循环例子。其实有专门用于循环的指令。我们来看一看。
LOOP字符串截取指令
LOOP指令可以帮我们完成前面例子中的事情- 将计数器ECX的值减1,判断ECX的值是否为0,如果为0就跳转到指定的地址-将像前面的例子一样。(可惜的是,大多数现代的处理器中该指令的效率不如前面模拟的例子。)
在DEC ECX指令上单击鼠标右键选择-Binary-Fill with NOPS。对TEST ECX,ECX和JNZ 401007两条指令也进行同样的操作。这三条指令用一条LOOP 401007指令替代。
第一行突出显示(401000)-单击鼠标右键选择-New origin here。现在我们来执行新的LOOP指令吧。
按F7键,再次看到计数器ECX首先被初始化为0了,然后又被设置为15h了。我们继续单步跟踪,直到LOOP指令。
这里还是像之前一样,跳转至401007地址处,因为计数器不为0。这里,计数器递减1,现在为14h。
继续单步跟踪。当计数器为0时,循环将结束。
这里还有一些与LOOP指令相关的指令:
LOOPZ, LOOPE 重复循环,直到零标志位Z置1
LOOPNZ, LOOPNE 重复循环,直到零标志位Z清0
这几条指令同样是循环指令,即重复循环体,直到计数器为0,每次循环将计数器的值递减1。此外,LOOPZ,LOOPNZ指令还将检查零标志位Z是否为0。只有计数器的值和零标志Z同时满足条件时才循环。
接着,我们来介绍一下字符串操作类指令。
串操作
下面,介绍一下串操作类的基本指令
MOVS
该指令是从一个地址向另一个地址复制数据。源地址保存在ESI寄存器中,目的地址保存在EDI寄存器中。我们不需要显示地指定参数。现在我们在OD中写入MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]指令,样子如下:
这里我们用ESI来保存源地址,EDI来保存目的地址。
在拷贝内容之前,让我们在数据窗口中来看一看源地址和目的地址的情况。
你可以在数据窗口中,单击鼠标右键选择-Go to-Expression,然后输入地址40366C。还有一个更加简单的方法:在第一条指令上面单击鼠标右键选择-Follow in Dump-Immediate constant,如下图所示:
源地址(ESI)中指定的地址单元中的待拷贝的内容。
目的地址(EDI)指向的地址单元
目的地址指向的地址单元将保存拷贝过来的内容。
按F7键,执行MOVS指令,我们可以看到4个字节被拷贝了。
这里MOVS拷贝4个字节的内容,即DWORD,另外一种书写形式为:MOVSD。与之对应的还有拷贝两个字节的MOVSW指令和拷贝一个字节的MOVSB指令。
请注意ESI,EDI拷贝的方向,拷贝的方向取决于方向标志位D。
REP
该指令可做为前面介绍的一些指令的前缀,尤其是MOVS指令。该前缀表示当前指令需要执行的次数ECX。每次循环计数器ECX 的值递减1,和前面介绍的循环一样。
因此,REP MOVS不一定拷贝是4个字节,它拷贝的大小为每次拷贝的大小* ECX, 源指针ESI和目的指针EDI每次递增4或者递减4(递增或递减取决于方向标志位D)。该指令看起来很实用,是不是?
该指令可以配合前面介绍的指令实现从一个地址单元拷贝任意数目的字节内容到另一个地址单元,但是很多现代处理器中实现的该指令效率并不是很高。
我们来修改前面的例子:添加REP前缀以及初始化计数器ECX。
源地址现在是40365C(仅供说明),目的地址和之前的一样,是40369C。我们来到第一条指令处,单步跟踪到REP MOVS指令处。
我们注意到OD的解释窗口中-出现了源地址和目的地址已经里面的包含的内容以及其他一些有用的信息。按F7键。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。