例子:
401000 xor eax,eax
…
402398 jmp 401000
位移量 = 401000h - 402398h - 5h = FFFFEC63h(取后32位
转移机器码 = E9 + 63 EC FF FF = E9 63 EC FF FF
lea 指令可以用来替代add和sub指令,如lea edx, dword ptr [ecx+eax+78] 等价于 edx=ecx+eax+78h
乘法运算符一般被编译成mul、imul指令,这些指令的运行速度比较慢。编译器为了提高代码的效率,倾向于使用其他指令来完成同样的计算。如果一个数是2的幂,那么会用左移指令shl来实现乘法运算。另外,加法对于提高3 、 5、 6、 7 、9 等数的乘法运算效率非常有用。例如eax*5 可以写成lea eax,[eax+4*eax]。lea指令可以实现寄存器乘以2,4,8的运算
除法运算符一般被编译成div、idiv指令。除法运算的代价是相当高的,大概需要比乘法运算多消耗10倍的CPU时钟。
strlen函数在优化编译模式下的汇编代码如下:
mov ecx,FFFFFFFF ; 如果看到这一句,程序很可能是要获得字符串的长度
sub eax,eax
repnz ;重复串操作,直到ecx=0为止
scasb ;把AL的内容与edi指向的附加段中的数据逐一比较
not ecx ;ecx=字符长度+1
dec ecx ;ecx是真实的长度
je xxxxxx ;如果ecx=0,意味着字符串的长度为0
这段代码使用串扫描指令scasb把AL的内容与edi指向的附加段中的字节逐一比较,把edi指向的字符串长度保存在ecx中
功能 | 指令 | 机器码 | 指令长度(byte) |
---|---|---|---|
替换1字节 | nop | 90 | 1 |
替换2字节 | nop nop | 90 90 | 1 1 |
mov edi,edi | 8B FF | 2 | |
push eax pop eax | 50 58 | 1 1 | |
inc eax dec eax | 40 48 | 1 1 | |
jmp xx | eb00 | 2 | |
寄存器清零 | mov eax,00000000h | B8 00 00 00 00 | 5 |
push 0 pop eax | 6A 00 58 | 2 1 | |
sub eax,eax/ xor eax,eax | 2B C0 / 33 C0 | 2 | |
测试寄存器的值是否为0 | cmp eax,00000000h je _label_ | 83 F8 00 74xx/0F84xxxxxxxx | 3 2/6 |
or eax,eax / test eax,eax je _label_ | 0B C0 / 85 C0 74xx/0F84xxxxxxxx | 2 2/6 | |
置寄存器为0FFFFFFFFh | mov eax,0ffffffffh | B8 FF FF FF FF | 5 |
xor eax,eax/ test eax,eax dec eax | 33 C0 /2B C0 48 | 2 1 | |
stc sbb eax,eax | F9 2B C0 | 1 2 | |
转移指令 | jmp _label_ | EBxx/E9xxxxxxxx | 2/5 |
push _label_ ret | 68 xx xx xx xx C3 | 5 1 |