|
本帖最后由 随便注册 于 2014-2-27 12:47 编辑
字符串操作指令
这里包括CMPS,SCAS,LODS,STOS,MOVS,INS和OUTS等。这些指令有一个共同的特点,就是没有显式的操作数,而由硬件规定使用DS:[ESI]指向源字符串,用ES:[EDI]指向目的字符串,用AL/AX/EAX做暂存。这是硬件规定的,所以在使用这些指令之前一定要设好相应的指针。
这里每一个指令都有3种宽度形式,如CMPSB(字节比较)、CMPSW(字比较)、CMPSD(双字比较)等。
CMPSB:比较源字符串和目标字符串的第一个字符。若相等则Z标志置1。若不等则Z标志置0。指令执行完后,ESI 和EDI都自动加1,指向源/目标串的下一个字符。如果用CMPSW,则比较一个字,ESI/EDI自动加2以指向下一个字。
如果用CMPSD,则比较一个双字,ESI/EDI自动加4以指向下一个双字。(在这一点上这些指令都一样,不再赘述)
SCAB/W/D 把AL/AX/EAX中的数值与目标串中的一个字符/字/双字比较。
LODSB/W/D 把源字符串中的一个字符/字/双字送入AL/AX/EAX
STOSB/W/D 把AL/AX/EAX中的直送入目标字符串中
MOVSB/W/D 把源字符串中的字符/字/双字复制到目标字符串
INSB/W/D 从指定的端口读入字符/字/双字到目标字符串中,端口号码由DX寄存器指定。
OUTSB/W/D 把源字符串中的字符/字/双字送到指定的端口,端口号码由DX寄存器指定。
串操作指令经常和重复前缀REP和循环指令LOOP结合使用以完成对整个字符串的操作。而REP前缀和LOOP指令都有硬件规定用ECX做循环计数器。举例:
LDS ESI,SRC_STR_PTR
LES EDI,DST_STR_PTR
MOV ECX,200
REP MOVSD
上面的代码从SRC_STR拷贝200个双字到DST_STR. 细节是:REP前缀先检查ECX是否为0,若否则执行一次MOVSD,ECX自动减1,然后执行第二轮检查、执行......直到发现ECX=0便不再执行MOVSD,结束重复而执行下面的指令。
LDS ESI,SRC_STR_PTR
MOV ECX,100
LOOP1:
LODSW
.... (deal with value in AX)
LOOP LOOP1
.....
从SRC_STR处理100个字。同样,LOOP指令先判断ECX是否为零,来决定是否循环。每循环一轮ECX自动减1。
REP和LOOP 都可以加上条件,变成REPZ/REPNZ 和 LOOPZ/LOOPNZ. 这是除了ECX外,还用检查零标志Z. REPZ 和LOOPZ在Z为1时继续循环,否则退出循环,即使ECX不为0。REPNZ/LOOPNZ则相反。
====================================================
高级语言程序的汇编解析
在高级语言中,如C和PASCAL等等,我们不再直接对硬件资源进行操作,而是面向于问题的解决,这主要体现在数据抽象化和程序的结构化。例如我们用变量名来存取数据,而不再关心这个数据究竟在内存的什么地方。这样,对硬件资源的使用方式完全交给了编译器去处理。不过,一些基本的规则还是存在的,而且大多数编译器都遵循一些规范,这使得我们在阅读反汇编代码的时候日子好过一点。这里主要讲讲汇编代码中一些和高级语言对应的地方。
1. 普通变量。通常声明的变量是存放在内存中的。编译器把变量名和一个内存地址联系起来(这里要注意的是,所谓的“确定的地址”是对编译器而言在编译阶段算出的一个临时的地址。在连接成可执行文件并加载到内存中执行的时候要进行重定位等一系列调整,才生成一个实时的内存地址,不过这并不影响程序的逻辑,所以先不必太在意这些细节,只要知道所有的函数名字和变量名字都对应一个内存的地址就行了),所以变量名在汇编代码中就表现为一个有效地址,就是放在方括号中的操作数。例如,在C文件中声明:
int my_age;
这个整型的变量就存在一个特定的内存位置。语句 my_age= 32; 在反汇编代码中可能表现为:
mov word ptr [007E85DA], 20
所以在方括号中的有效地址对应的是变量名。又如:
char my_name[11] = "lianzi2000";
这样的说明也确定了一个地址,对应于my_name. 假设地址是007E85DC,则内存中[007E85DC]='l',[007E85DD]='i', etc. 对my_name的访问也就是对这地址处的数据访问。
指针变量其本身也同样对应一个地址,因为它本身也是一个变量。如:
char *your_name;
这时也确定变量"your_name"对应一个内存地址,假设为007E85F0. 语句your_name=my_name;很可能表现为:
mov [007E85F0], 007E85DC ;your_name的内容是my_name的地址。
2. 寄存器变量
在C和C 中允许说明寄存器变量。register int i; 指明i是寄存器存放的整型变量。通常,编译器都把寄存器变量放在esi和edi中。寄存器是在cpu内部的结构,对它的访问要比内存快得多,所以把频繁使用的变量放在寄存器中可以提高程序执行速度。
字数限制,请看下一篇
|
|