吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 4288|回复: 71

[汇编] 2.2 常用汇编指令集

  [复制链接]
大飞哥自习室 发表于 2022-3-23 10:48 | 显示全部楼层 |阅读模式

令不熟悉的话,就需要对常用的汇编指令进行学习,以对汇编指令有一个大致的了解。其余
并不常用或者比较生僻的指令,完全可以通过查手册或文档来进行学习。在看书时,有个别字不认识还能继续看下去,如果只有个别字是认识的,恐怕就太困难了。看汇编指令也是如此。本节讨论汇编语言的同时,会在 OD
调试器中实际地进行汇编指令的练习,在练习指令的过程中会观察前面所学寄存器的变化。
本书不是汇编书籍,所以不会详细介绍汇编语言的各种细节。
2.2.1    指令介绍
指令由两部分组成,分别是操作码和操作数,操作码即需要操作执行的指令,操作数是为执行指令提供的数据。在每条指令中,操作码是必需的,而操作数则是根据操作码的不同而不同的。通常的操作码有一个或两个操作数,也
有的操作码没有操作数。指令格式如图 2-10 所示。


图 2-10    指令格式                                                  2


图 2-10 是有两个操作数的操作码,前面的是目的操作数,后面的称为源操作数。通常在查询指令格式的时候,会看到形如以下的样式:
mov r/m8, r8
mov r/m16, r16 mov r/m32, r32 mov r8, r/m8 mov r16, r/m16 mov r32, r/m32 mov r8, imm8 mov r16,
imm16 mov r32, imm32
mov 是汇编指令,r 指的是寄存器,m 指内存,imm 指立即数,8、16、32 指的是数据的宽度,分别是 8 位、16 位和 32 位。通常情况下,32 位的系统都会很好地支持 8 位、16
位和 32 位的数据,因此后面介绍指令时不再专门说明传递数据的宽度。
2.2.2    常用指令介绍
1.数据传递指令
(1)mov 指令
mov 指令是最常见的数据传送指令,它的功能等同于高级语言中的赋值语句。该指令的操作数有两个,分别是源操作数和目的操作数。
指令格式如下:
     mov 目的操作数,源操作数                                                                           
mov 指令可以实现寄存器与寄存器之间、寄存器与内存之间、寄存器与立即数之间、内存与立即数之间的数据传递。
需要注意,内存与内存之间是无法直接传递数据的,目的操作数不能为立即数,两个操作数的宽度必须一致。
mov 指令的用法示例如下:
① mov eax, 12345678h
② mov eax, dword ptr [00401000h]
③ mov eax, ebx
④ mov word ptr [00401000h], 1234h
⑤ mov byte ptr [00401000h], al
将以上的汇编指令,在 OD 中进行练习。打开 OD,单击“文件”菜单的“打开”菜单项,打开第 1 章中用汇编语言编写的可执行程序,OD
会停留在该可执行程序开始执行的地址,通常将程序开始执行的地址称为程序的入口点,简称 EP。这个位置很重要,脱壳时的首要任务就是找到程序的入口点。打开第 1 章用汇编语言编写的程序后,OD 如图
2-11 和图 2-12所示。
从图 2-11 中可以看出,OD 的反汇编窗口分为四列,分别是地址列、HEX 数据列、反汇编列和注释列。首先来观察反汇编窗口的地址列,当调试者用 OD 打开第 1 章中的可执行程序后,


OD 停在了地址为 00401000 处,对应地址 00401000 处的反汇编代码是 PUSH 0,这个地址就是程序的入口地址。当程序运行起来以后,会首先执行 00401000 地址处的 PUSH
0 指令。




图 2-11    OD 停在程序入口位置的反汇编窗口


回忆前一节讨论的寄存器中有一个指令指针寄存器 EIP,它总是指向要执行的那条指令并在执行后自动指向下一条指令的地址。观察图 2-12,可以看到 EIP 的值正好就是入口地址的值 00401000。
在 OD 的反汇编窗口中双击 00401000 地址处的反汇编代码(注意,是双击反汇编代码),会出现修改反汇编代码的窗口,如图 2-13 所示。


图 2-12    OD 停在程序入口位置的寄存器窗口                  图 2-13    修改反汇编代码窗口


逐条修改反汇编处的代码,代码如下:
MOV EAX,12345678h MOV BX,AX
MOV CH,AH MOV DL,AL
修改后 OD 的反汇编窗口如图 2-14 所示。
汇编代码修改完成后,按 F8  键执行第一条汇编指令。第一条汇编指令是 mov  eax, 12345678h,那么寄存器 eax 的值应该被修改为 12345678h。除了 eax
寄存器的值发生变化以外,指令指针寄存器 EIP 也会自动指向下一条指令的地址。观察寄存器窗口,如图 2-15 所示。
从图 2-15 中可以看出,寄存器 EAX 的值被修改为 12345678h,指令指针寄存器 EIP 的值被修改为 00401005。


注意:12345678h 是16 进制数,在OD 中数值默认即是16 进制数。因此在显示时省略了值后面的h。


图 2-14    修改指令后的汇编窗口                     图 2-15    按F8 键后的寄存器窗口


通过 F8 键执行 mov bx, ax 指令,该指令执行后将 ax 的值传递给 bx。观察图 2-15,当前 EBX 的值是 7FFD9000,当前 EAX 的值是 12345678,AX 是
EAX 的低 16 位,因此 AX 的值是 5678,将 5678 传递给 EBX 的低 16 位 BX,则被修改后 EBX 寄存器的值应该是 7FFD5678。按下 F8 键,观察寄存器来验证
EBX 的值。从该条指令可以看出,在只修改 EBX 寄存器的低 16 位时,不会影响 EBX 寄存器的高 16 位的值。
接下来的两个 mov 指令,请读者自行练习,并观察各个寄存器的变化,这里不再赘述。前面练习的几条汇编指令,分别是把立即数传递给了寄存器,如 mov eax, 12345678,还
练习了寄存器之间数据的传递,如 mov bx, ax。下面练习关于寄存器与内存之间数据的传递,按照前面的方法,把如下几条指令写入 OD 的反汇编窗口中,代码如下:
MOV DWORD PTR DS:[403020],EAX
MOV WORD PTR DS:[403024],AX MOV BYTE PTR DS:[403028],AL
修改完的 OD 反汇编窗口如图 2-16 所示。


图 2-16    寄存器与内存的数据传递


在汇编代码中,笔者选择了几个内存地址,分别是 00403020、00403024、00403028,分别对其传递一个 DWORD 宽度的数据、WORD 宽度的数据和 BYTE 宽度的数据。用 F8
键依次执行这 3 条代码,然后在内存窗口观察值的变化,如图 2-17 所示。


图 2-17    内存数据被修改后的值


在图 2-17 中观察,数据是以小尾方式存储着数据,00403020 地址为了存储 EAX 寄存器的值,连续占用 00403020、00403021、00403022 和 00403023
四个字节的地址,00403024 接收 AX 寄存器的值只占用了 00403024 和 00403025 两个字节的地址,00403028 接收 AL 寄存器的值只占用了当前的 00403025
一个字节的地址
注意:因为内存非常大,无法为每个内存单元命名一个独立的名字,因此内存单元采用编号的形式进行使用,该内存编号称为内存地址。在 32 位系统下,CPU 可寻址的地址范围是 2 的 32 次方,也就是
4G 的地址范围。其内存的编号从 00000000H 到 FFFFFFFFH,在使用 OD 调试器进行调试时,地址范围始终小于 80000000H,因为在 8000000H
以上的地址属于内核的地址,在OD 中是无法进行调试的。在OD 中按下快捷键Alt+ M,观察当前被调试进程所使用的地址,如图2-18 所示。


图 2-18    被调试进程的内存映射




在与内存进行数据传递时,特别需要注意的是传递数据时需要明确“内存的宽度”。


在介绍 MOV 指令时,笔者演示了寄存器窗口与内存窗口的查看,在讨论其他指令时希望读者按此方法自行练习。
(2)xchg 指令
xchg 指令的功能是交换两个操作数的数据。该指令有两个参数,分别是源操作数和目的操作数。
指令格式如下:
xchg 目的操作数, 源操作数
xchg 的使用方法是:
xchg reg, reg/mem xchg mem, reg
xchg 指令允许寄存器和寄存器之间交换数据,也允许寄存器和内存之间交换数据,但是内存和内存之间是不能进行数据交换的。大部分与数据传递有关的指令有此限制。
xchg 指令的用法示例如下:
XCHG EAX,EBX
XCHG DWORD PTR DS:[403020],EBX XCHG WORD PTR DS:[403024],CX
观察执行 xchg 指令后寄存器及内存数据的变化。
(3)lea 指令
lea 指令,即装入有效地址指令,它将内存单元的地址送至指定的寄存器。它的操作数虽然也是内存单元,但是它获取到的是内存单元的地址,而不是内存单元中的数据。
指令格式如下:
Lea 目的操作数,源操作数
lea r32, mem
lea 指令是将 mem 的地址装入到一个 32 位的寄存器当中。




练习对比如下两条指令:
MOV EAX,DWORD PTR DS:[403000] LEA EBX,DWORD PTR DS:[403000]


将上面两条指令输入到 OD 的反汇编窗口中,并通过 F8 键单步执行这两句汇编代码。执行完成后观察 EAX 和 EBX 的值。EAX 寄存器的值是 12h,EBX 寄存器的值是
00403000h。通过这两条指令可以看出,EAX 寄存器获得了 00403000h 这个地址中的数据,而 EBX 寄存器得到了 00403000h 这个地址的编号,即 00403000h。
2.逻辑运算指令
常用的逻辑运算有 and(与)、or(或)、xor(异或)和 not(非)。
(1)and 指令
and  指令是逻辑按位与运算指令,用于将目的操作数中的每个数据位与源操作数中的对应位进行逻辑与操作。
指令格式如下:
and 目的操作数,源操作数 and reg, imm/reg/mem and mem, imm/reg
对应的位在进行“与”操作时,对应的位同为 1 时,结果是 1,否则相“与”的结果为 0。 and 指令影响的标志位有 OF、SF、ZF、PF 和 CF。
练习如下指令:
MOV EAX,0B AND EAX,9
在执行完 mov 操作后,EAX 寄存器的值为 0B;在执行完 and 指令后,EAX 寄存器的值为 9。在数值进行运算时切记要转换为二进制后进行运算,因为汇编中的逻辑运算是按“位”进行运算的。


注意:在练习汇编指令时,无论是位运算还是算数运算等,都要密切观察标志寄存器的变化。


(2)or 指令
or 指令是逻辑按位或运算指令,用于将目的操作数中的每个数据位与源操作数中的对应位进行逻辑或操作。
指令格式如下:
or 目的操作数,源操作数 or reg, imm/reg/mem or mem, imm/reg
对应的位在进行“或”操作时,对应的位同为 0 时,结果是 0,否则相“或”的结果为 1。 or 指令影响的标志位有 OF、SF、ZF、PF 和 CF。
(3)not 指令
not 指令是逻辑非指令,通过该指令可以将操作数的各位取反,原来该位是“0”则变为 “1”,原来该位为“1”则变为“0”。
指令格式如下:
not 目的操作数
not reg/mem
not 指令不影响标志寄存器的任何位。


练习如下指令:
MOV EAX,0 NOT EAX
MOV EAX,11111111 NOT EAX
(4)xor 指令
xor  指令是按位异或指令,将源操作数的每位与目的操作数的对应位进行异或操作。当只有源操作数和目的操作数对应位不同时,结果才为 1。
指令格式如下:
xor 目的操作数,源操作数 xor reg, imm/reg/mem xor mem, imm/reg
xor 指令影响的标志位有 OF、SF、ZF、PF 和 CF。练习如下指令:
XOR EAX,EAX
MOV EAX,12345678 XOR EAX,87654321 XOR EAX,87654321
以上四条指令都有特殊的意义。执行完第一条 xor  eax,  eax 指令后,eax 寄存器为 0。执行完第二条指令mov eax, 12345678 后,eax 寄存器的值为
12345678,接下来 xor 两次 87654321后,eax 寄存器的值又变为 12345678。
(5)总结以上几条指令的特殊用法
① and 指令可用于复位某些位(复位就是将该位设置为 0)而不影响其他位。例如,将 AL 的低 4 位清零,and al, 0f0h。
② and 指令可用于保留某位的值不变,其他位清零。例如,将 AL 的最高位保留,其他位清零,and al, 10h。
③ or  指令可用于置位某些位(置位就是将该位设置为 1)而不影响其他位。例如,将 AL 的低 4 位置 1,or al, 0fh。
④ xor 指令可用于对某个寄存器进行清零,例如 xor eax, eax。
⑤ xor 指令可用于简单的加密与解密,例子可参考 xor 指令的练习指令。
3.算数运算指令
(1)add 指令
add 指令是加法指令,将源操作数和目的操作数相加,相加的结果存储在目的操作数中,操作数的长度必须相同。
指令格式如下:
add 目的操作数,源操作数 add reg, imm/reg/men add mem, imm/reg
练习如下指令:
MOV AL,1 MOV BL,2 ADD AL,BL
(2)sub 指令
sub 指令是减法指令,将目的操作数和源操作数相减,相减的结果存储在目的操作数中。指令格式如下:
sub 目的操作数,源操作数 sub reg, imm/reg/mem sub mem, imm/reg
练习如下指令:
MOV CL,2 MOV DL,1 SUB CL,DL
(3)adc 指令






27


2




























adc 指令是带进位的加法,类似于 add 指令,区别在于将目的操作数与源操作数相加后,需要再加上标志寄存器 CF 位的值,执行 adc 指令后的结果为目的操作数=目的操作数+源操作数+CF
位的值。
指令格式如下:
adc 目的操作数,源操作数 adc reg, imm/reg/men adc mem, imm/reg
(4)sbb 指令
sbb 指令是带借位的减法,类似于 sub 指令,区别在于将目的操作数与源操作数相减后,需要再减去标志寄存器CF 位的值,执行sbb 后的结果为目的操作数=目的操作数?源操作数?CF 位的值。
指令格式如下:
sbb 目的操作数,源操作数 sbb reg, imm/reg/mem sbb mem, imm/reg
(5)inc 指令
inc 指令是加一指令,用于对目的操作数进行加一操作。指令格式如下:
Inc 目的操作数
Inc reg/mem
练习如下指令:
INC EAX
INC DWORD PTR DS:[403000]
从功能上讲,inc eax 指令与 add eax, 1 指令的功能相同,但是 inc 的机器码更短,执行速度更快。
(6)dec 指令
dec 指令是减一指令,用于对目的操作数进行减一操作。指令格式如下:
Dec 目的操作数
Dec reg/mem
练习如下指令:
DEC EAX
DEC DWORD PTR DS:[403000]




注意:在进行算数运算时,需要注意各个指令所影响的标志寄存器的位,这部分内容请读者自行查阅相关手册。


4.堆栈操作指令
在了解堆栈指令之前,先简单说明一下什么是堆栈。堆栈是一个“后进先出(LIFO)”或者说是“先进后出(FILO)”的内存区域。它的本质还是一块内存,堆栈的内存分配是由高地址向低地址延伸的。


在什么情况下使用堆栈呢?这里给一些使用堆栈的情况。
① 用于存储临时的数据;
② 高级语言中参数的传递。
堆栈的操作只有两个,一个是入栈,一个是出栈。堆栈的结构如图 2-19 所示。
堆栈结构的描述如下:
① esp 和 ebp 是两个 32 位的通用寄存器,里面存储的是关于堆栈的内存地址;
② ebp 中存放的是栈底的地址;
③ esp 寄存器中存放的是栈顶的地址;
④ 存储数据时的操作叫作入栈,入栈时 esp


寄存器指向的地址会减4,然后将数据存入;








图 2-19    堆栈结构图










⑤ 释放数据所占的空间叫作出栈,出栈时 esp 寄存器指向的地址会先将数据取出,然后将 esp 寄存器指向的地址减 4;
⑥ 堆栈分配空间的方向是由高到低;
⑦ 执行入栈和出栈指令时(即 PUSH、POP、PUSHAD、POPAD 等),总是在 esp 寄存器的一端;
⑧ 读取堆栈中的数据时,可以通过 ebp 或 esp 加上偏移后获得。
(1)堆栈数据操作指令
关于堆栈数据操作的指令有两个,分别是 PUSH 指令和 POP 指令。指令格式如下:
push reg/mem/imm
pop reg/mem
打开 OD 来详细观察堆栈操作时的变化。打开 OD 后,观察 EBP 和 ESP 指针的指向及
OD 窗口右下角的堆栈窗口,如图 2-20 和图 2-21 所示。
图 2-20    寄存器窗口中的 ESP 和EBP 寄存器


观察图 2-20,ESP 和 EBP 的寄存器指向的地址分别是 0012FF8Ch 和 0012FF94h。ESP寄存器指向的地址是栈顶,EBP  寄存器指向的地址是栈底,但是观察图 2-21  
时可以发现, EBP 寄存器所指向的地址 0012FF94h 的下方(也就是高地址方向)还是有数据的,这是为什么呢?所谓栈底和栈顶是相对的,因为在高级语言编程或者使用 Win32 汇编时,在遇到函数


调用时为了保护调用函数的数据,会重新生成一块堆栈,称作新的堆栈框架。因此,看到 ebp
寄存器指向的栈底下面还有数据,也是其他过程或函数的堆栈框架。
2




图 2-21    OD 堆栈窗口数据


练习如下指令,并观察 ESP 寄存器的变化以及出栈和入栈顺序:
MOV EAX,12345678 PUSH EAX
PUSH 1234
PUSH 12
MOV ECX,DWORD PTR SS:[ESP+4] MOV EBX,DWORD PTR SS:[EBP+C] POP EAX
POP EBX POP ECX
前 3 句push 指令分别是将“1234578”“1234”和“12”进行入栈,观察寄存器,即使入栈的值是“1234”和“12”,ESP 寄存器的值依然是每次减 4。
中间的两句 mov 指令,分别是通过 esp 和 ebp 来获取刚才入栈的值。
最后 3 句pop 指令,分别是将栈中的数据送入 EAX 寄存器、EBX 寄存器和 ECX 寄存器。
(2)保存/恢复通用寄存器现场
常用的保存通用寄存器的指令有两个,分别是 pushad 和popad。pushad 指令在堆栈上按顺序压入所有的 32 位通用寄存器,顺序依次是
EAX、ECX、EDX、EBX、ESP、EBP、ESI和 EDI。popad 指令以相反的顺序从堆栈中弹出这些通用寄存器。
在用汇编语言编写过程(函数)中修改了很多寄存器,则可以在过程的开始部分和结束部分分别用 pushad 和popad
指令来保存和恢复通用寄存器的值。高级语言编写函数或过程时,依据编译器的不同会保存不同的寄存器,不一定会使用 pushad 和 popad 指令。
指令格式如下:
Pushad Popad
练习如下指令:
PUSHAD
MOV EAX,12345678 MOV EBX,87654321 POPAD
观察 OD 的寄存器窗口可以发现,在执行完 pushad 后,被修改的寄存器只有 ESP 寄存器,因为 8 个通用寄存器入栈以后,ESP 寄存器会向指向新的栈顶。pushad 指令压入的 ESP
寄存器的值是在 ESP 寄存器被改变前的值。
(3)保存/恢复标志寄存器
通常保存和恢复标志寄存器的指令有两个,分别是 pushfd 和popfd。pushfd 指令在堆栈上压入
32 位的 EFLAGS 标志寄存器的值,popfd 指令将堆栈顶部的值弹出并送至 EFLAGS 标志寄存器。指令格式如下:
Pushfd
Popfd
保存 EFLAGS 标志寄存器不被修改是很有用的指令,通常使用方法如下:
Pushfd
; 其他可能修改EFLAGS 标志寄存器的语句
Popfd
除此之外,在某些情况下可能会手动修改 EFLAGS 标志寄存器中的某个标志位,比如需要让程序单步执行,需要设置 EFLAGS 标志寄存器的第 8 位 TF 标志位,因此需要将
EFLAGS标志寄存器的第 8 位置为 1,代码如下:
PUSHFD
POP EAX
OR EAX,100 PUSH EAX POPFD
首先将 EFLAGS 标志寄存器送入堆栈中,然后通过 pop eax 将保存在栈顶的标志寄存器送入 eax 寄存器中。通过 or 指令将eax 的第 8 位进行置位,然后通过 push eax
将eax 寄存器的值送入堆栈中,最后通过 popfd 将栈顶的值送入 EFLAGS 标志寄存器中。
在 OD 中将以上代码录入至反汇编代码窗口中,然后通过 F8 键单步执行每条指令。注意在 OD 的寄存器窗口中,观察标志寄存器中的 T 位的变化及栈顶的变
注意:EFLAGS 标志寄存器也是一个32 位的寄存器,这点不要忘记。


5.转移指令
在前面介绍的数据传递指令当中,可以对通用寄存器进行操作,比如 mov eax, 12345678,但是不能用此类的方式改变 EIP 寄存器的值。在前面介绍指令时,通过使用 OD
调试器可以发现,在汇编指令被执行的过程中 EIP 的值是自动进行修改,使得程序可以逐条执行各个汇编指令。那么,本节就来介绍如何改变 EIP
寄存器的值,从而使程序可以跳跃执行,而不只是顺序执行。转移指令用于实现分支、循环、过程(函数)等程序结构。
(1)无条件转移指令
jmp 指令是一条无条件转移指令。只要遇到 jmp 指令,即跳转到相应的地址进行执行。
jmp 指令格式如
     Jmp reg/mem/imm                                                                    
jmp 指令的本质就是修改 EIP 的值,从而使得 EIP 指向其他的位置进行执行。
练习如下代码:                                                                  2
JMP 00401022
MOV EAX, 0040102A JMP EAX
JMP DWORD PTR DS:[403000]
在查看反汇编中,jmp  后面跟着一个地址或者存储地址的寄存器或者存储地址的内存单元。在书写汇编代码的时候,jmp  后面可能是一个跳转的标签,而这个跳转的标签,在反汇编中就是一个地址。
(2)条件转移指令
条件转移指令有多条,通常称条件转移指令为 jcc 指令集,该指令集包含(但不限于)jz、
jnz、je、jne、ja、jna 等,如表 2-2 所列。
表 2-2                                                        JCC 指令表
转移指令                               标志位                                含义
JO                                                                       OF=1                       
                                           溢出
JNO                                                                    OF=0                        
                                         无溢出
JB/JC/JNAE                                                       CF=1                              
                                    低于/进位/不高于等于




JAE/JNB/JNC JE/JZ JNE/JNZ JBE/JNA JA/JNBE
JS JNS JP/JPE
JNP/JPO JL/JNGE JGE/JNL JLE/JNG JG/JNLE




CF=0 ZF=1 ZF=0
CF=1 或 ZF=1 CF=0 且 ZF=0 SF=1
SF=0 PF=1 PF=0
SF≠OF SF=OF
ZF≠OF 或 ZF=1 SF=OF 且 ZF=0




高于等于/不低于/无进位相等/等于零
不相等/不等于零低于等于/不高于高于/不低于等于符号为负
符号为正
“1”的个数为偶 “1”的个数为奇小于/不大于等于大于等于/不小于小于等于/不大于大于/不小于等于






条件转移指令根据 EFLAGS 标志寄存器中不同的标志位决定如何进行跳转。这些指令并不是所有的都会经常被用到,因此只要在写程序的时候留意和掌握经常使用的即可,其他的在使用时相应地选择。
jcc 指令的格式与 jmp 指令的格式相同,但是需要介绍两个经常与 jcc 指令配合使用的指令,分别是测试指令(TEST)和比较指令(CMP)。
① test 指令
测试指令 test 对两个操作数进行逻辑与运算,结果不送入目的操作数,但影响标志位 OF、 SF、ZF、PF 和 CF。指令格式如下:
Test reg, imm/reg/mem
     Test mem, imm/reg                                                                  
test 指令通常用于测试一些条件是否满足。












② cmp 指令
比较指令 cmp 对两个操作数进行比较,比较的方式相当于用目的操作数减源操作数的减法操作,但是 cmp 只影响相应的标志寄存器,不会将减法的结果送入目的操作数中。
指令格式如下:
Cmp reg, imm/reg/mem
     Cmp mem, imm/reg                                                                  
该指令影响的标志位有 OF、SF、ZF、AF、PF 和 CF。
在 OD 中练习如下指令:
MOV EAX,1 MOV EBX,2 CMP EAX,EBX JE 0040102B MOV ECX,1 JMP 00401030 MOV ECX,2
以上的代码使用了 cmp 指令、je 指令和 jmp 指令,cmp 指令比较 eax 和 ebx 的值是否相等,相等则 ecx 的值 2,不相等则 ecx 的值为 1。请注意观察 cmp
指令所影响的 EFLAGS 标志寄存器的相应标志位,并注意观察 je 指令的跳转。
在例子中 eax 寄存器的值为 1,ebx 寄存器的值为 2,显然是不相等的,也就是说 je 指令是不会进行跳转的。那么如何使 je 指令进行跳转呢?可以将 eax 和 ebx
修改为相同的值,这是在代码上进行修改。另一种方式是,在执行完 cmp
指令时,在寄存器窗口修改 ZF  标志位来改变跳转指令的,如图 2-22 所示。
通过表 2-2 可以得知,je 指令是否跳转主要依赖于 ZF 标志位,因此在 OD 的寄存器窗口中,通




过双击改变“Z”后面的值来改变 ZF 标志位的值,从而改变 je 指令的跳转状态。


图 2-22    OD 寄存器窗口




注意:该方法在分析软件流程时是非常有用的,上面的汇编例子代码相当于C 语言中的if/else 结构。
(3)循环指令
loop 指令是循环控制指令,需要使用 ecx 寄存器来进行循环计数,当执行到 loop 指令时,先将 ecx 寄存器中的值减 1,如果 ecx 寄存器中的值大于 0,则转移到 loop
指令后的地址处,如果 ecx 寄存器中的值等于 0,则执行 loop 指令的下一条指令。
在使用汇编语言编写代码的时候,loop 后面跟随一个标号,而在反汇编代码中 loop 指令
后跟随一个地址值。在 OD 中练习如下代码:
MOV EAX,0 MOV ECX,5 ADD EAX,ECX
LOOP 00401020
在代码中 loop 后面的 00401020 是 add eax,ecx 指令的地址,在练习时读者请自行修改为自己的地址。单步跟踪以上代码,注意观察 ecx 寄存器的变化与循环的次数。
(4)调用过程(函数)指令和返回指令
call 指令是调用过程(函数)的指令,它的作用类似于 jmp 指令,可以修改 EIP 寄存器


的值,从而使指令转移到其他地址继续执行。与 jmp 指令不同的地方是,call 指令在修改 EIP
寄存器的值之前,会将 call 指令的下一条指令的地址保存至堆栈,以便在调用过程(函数)
后再继续从 call 指令处执行。                                                           2
指令格式如下:
call reg/mem/imm
ret 指令用于过程(函数)的返回,该指令从堆栈的栈顶中弹出 4 个字节(这里的 4 个字节特指 32 位系统)送入 EIP
寄存器中。一般该指令在过程(函数)需要返回的位置或者是过程(函数)的结尾处。
call 指令调用过程(函数)时会将 call 指令的下一条指令压入栈顶,当过程(函数)执行中遇到 ret 指令时,会将 call 指令压入的指令弹出送入 EIP 寄存器中,这样代码的流程就会接着
call 指令的下一条指令继续执行。
指令格式如下:
Ret
Retn imm
ret 指令不需要修正堆栈栈顶的位置直接返回,retn 指令则需要修正堆栈栈顶的位置后再进行返回。
在 OD 调试器中练习如下指令:
CALL 00401024
MOV EAX, 00401024 CALL EAX
MOV DWORD PTR DS:[403000], 00401024 CALL DWORD PTR DS:[403000]
在地址 401024 地址处,写入如下指令:
RET
用 OD 调试以上代码,在遇到 CALL 指令时,应该使用 F7 键单步步入来观察堆栈的变化。在笔者的机器上,录入以上代码后如图 2-23 所示。


图 2-23    CALL 及RET 指令练习
首先在执行 CALL 之前需要观察堆栈,然后 F7 键单步步入值再次观察 EIP 和堆栈,堆栈变化前后如图 2-24 和图 2-25 所示。
从图 2-24 和图 2-25 中可以看出,在使用 OD 的 F7 键单步步入 CALL 指令后,堆栈栈顶由原来的 0012FF8C 变为 0012FF88,并且将 00401005
这个地址保存在了堆栈中。然后观察 EIP 寄存器的值为 00401024,查看反汇编窗口,当前要执行的代码停留在了 RETN 处。
在 RETN 处按下 F8 或 F7 键,将栈顶的值 00401005 送入 EIP 寄存器中,并且堆栈栈顶又变回到了原来的 0012FF8C  处。查看反汇编窗口,当前要执行的代码停留在了地址为


00401005 处的 mov eax, 00401024 处。


图 2-24    CALL 指令单步步入之前           图 2-25    CALL 指令单步步入之后


6.串操作指令
串操作指令主要操作在内存中连续区域的数据,此处讨论 movs、stos 和 rep 三个常用的指令。
(1)串传送指令
串传送指令 MOVS 是借助 ESI 寄存器和 EDI 寄存器,把内存中源地址(ESI 指向源地址)的数据送入内存的目的地址(EDI 指向目的地址)中。MOVS 指令有 MOVSB、MOVSW 和
MOVSD 三种宽度。
指令格式如下:
MOVSB MOVSW MOVSD
在默认的情况下,MOVS 相当于 MOVSD,因为笔者是以 32 位操作系统来讨论该条汇编指令的。
在执行了 MOVS 指令后,ESI 寄存器和 EDI 寄存器指向的地址会自动增加 1 个单位(根据指令增加 1 个字节、2 个字节或 4 个字节)或者自动减少 1 个单位(根据指令减少 1
个字节、2 个字节或 4 个字节)。两个寄存器指向的地址是增加还是减少,需要依赖 EFLAGS 标志寄存器的 DF 标志位进行控制。当 DF 标志位为 0 时,执行 MOVS 指令后 ESI
寄存器和 EDI寄存器指向的地址会自增;当 DF 标志位为 1 时,执行 MOVS 指令后 ESI 寄存器和 EDI 寄存器指向的地址会自减。
在 OD 调试器中练习如下代码:
MOV ESI, 00403000
MOV EDI, 00403010 CLD
MOVS STD MOVS
代码中,CLD 指令是对 DF 标志位进行复位,也就是设置 DF 标志位为 0;STD 指令是对 DF 标志位进行置位,也就是设置 DF 标志位为 1。在执行 MOVS 后,注意观察 ESI
寄存器和 EDI 寄存器的值的变化,以及其指向的地址中的值的变化。
(2)串存储指令
串存储指令 STOS 是将 AL/AX/EAX 的值存储到 EDI 寄存器指向的内存单元。STOS 指令有 STOSB、STOSW 和 STOSD 三种宽度。




指令格式如下:
STOSB STOSW STOSD


在默认情况下,STOS 相当于 STOSD,因为笔者是以 32 位操作系统来讨论该条汇编指令的。
在执行了 STOS 指令后,EDI 寄存器指向的地址会自动增加 1 个单位(根据指令增加 1个字节、2 个字节或 4 个字节)或者自动减少 1 个单位(根据指令减少 1 个字节、2 个字节或 4
个字节)。EDI 寄存器指向的地址是增加还是减少,需要依赖 EFLAGS 标志寄存器的 DF 标志位进行控制。当 DF 标志位为 0 时,执行 STOS 指令后 EDI 寄存器指向的地址会自增;当
DF 标志位为 0 时,执行 STOS 指令后 EDI 寄存器指向的地址会自减。
在 OD 调试器中练习如下代码:
MOV AL,1
MOV EDI, 00403000 STOSB
MOV AX,2 STOSW
MOV EAX,3 STOSD
STD STOSD
在执行 MOVS 指令后,注意观察 EDI 寄存器的值的变化,以及其指向的地址中的值的变化。
在初始化某块缓冲区时会用到 STOS 指令。
(3)重复前缀指令
MOVS 指令和 STOS 指令每执行一次,最多能操作 4 个字节的数据,但是通过配合重复前缀指令则可以实现 MOVS 指令或 STOS 指令的重复执行。
REP 指令通过配合 ECX 寄存器即可实现重复执行的操作,当执行一次 REP 指令时,ECX寄存器的值都会自动减 1,如果 ECX 寄存器的值不为 0 则重复执行,如果 ECX 寄存器的值为 0
则重复执行结束。
启动 OD 调试器,在数据窗口中将 00403000 地址处进行数据填充。首先,选中 00403000
地址处的 16 个字节的数据,然后按下空格即可对该地址处的数据进行编辑,如图 2-26 所示
(注意,需要修改多少数据,就需要先选中多少数据)。在反汇编窗口中输入如下指令:
MOV ESI, 00403000
MOV EDI, 00403010
MOV ECX, 4
REP MOVS 通过 F7 键单步步入调试跟踪以上的汇编指令,在执行到 REP MOVS 时,注意 ECX 寄存器、ESI 寄存器和 EDI 寄存器的变化,并注意 EDI 寄存器指向的
00403010 地址处值的变化。
在执行 4 次 REP  MOVS 后,地址 00403010 处开始的 16 个字节的数据与地址 00403000
处开始的 16 个字节的数据相同,为什么执行 4 次即可将 16 个字节的数据从地址 00403000处全部传递到地址 00403010 处呢?原因是,代码中的 MOVS 一次传递 4
个字节的数据,因此执行 4 次即可传递完成。




图 2-26    填充 00403000 地址


读者可自行修改为一次传递一个数据的 MOVSB 指令进行测试。
STOS 指令配合 REP 指令完成重复串存储的代码这里不再进行演示,读者可参照“串存储指令”的代码例子配合 REP 指令自行完成。

2.png 3.png 4.png 5.png 6.png 7.png 未命名1648002810.png

评分

参与人数 14HB +12 THX +5 收起 理由
花盗睡鼠 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
消逝的过去 + 1
后学真 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
风里去 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
taykey + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
凌夏随缘 + 1
agan8888 + 1
wq180029819 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
一蓑烟雨 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
zxjzzh + 2 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
空白001 + 1
ldljlzw + 1
wbxs2077 + 2 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
成丰羽 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
gesq32957 发表于 2022-3-23 10:48 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
PbE35648 发表于 2022-3-23 10:48 | 显示全部楼层

谢谢分享
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
SzuPpJd5860 发表于 2022-3-23 10:48 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
JELdVp 发表于 2022-3-23 12:20 | 显示全部楼层

感谢楼主
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
VXBlSUP138 发表于 2022-3-23 12:46 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
CYTNRFji381 发表于 2022-3-23 12:46 | 显示全部楼层

楼主的帖子不错,多发点~
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
ALNHRvIO6270 发表于 2022-3-23 12:57 | 显示全部楼层

谢谢分享
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
eTPtJuZd85 发表于 2022-3-23 13:02 | 显示全部楼层

感谢楼主
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
lDeJTj 发表于 2022-3-23 13:09 | 显示全部楼层

我现在已经把楼主作为我的学习目标了!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表