逆向——汇编中的位运算
汇编中的移位指令
1、算术移位指令
SAL(算术左移)指令的操作与《SHL指令》一节中的 SHL 指令一样。每次移动时,SAL 都将目的操作数中的每一位移动到下一个最高位上。最低位用 0 填充;最高位移入进位标志位,该标志位原来的值被丢弃:
如,二进制数 1100 1111 算术左移一位,得到 1001 1110:
SAR(算术右移)指令将目的操作数进行算术右移:
-
SAL(Shift Arithmetic Left):算术左移
-
SAR(Shift Arithmetic Right):算术右移
-
格式:SAL/SAR Reg/Mem, CL/Imm
即算数移位指令后面的第一个操作数是寄存器或者内存;
第二个操作数是寄存器或者立即数
SAL eax,2
SAL ax,1
SAL al,3
举例:
SAR al,1 //al:10000001右移一位最高位补原来符号位,最低位移入CF,即11000000 / CF:1
SAL al,2 //al:10000001左移两位最高位移入CF,最低位补0,即00000100 / CF:0
2.逻辑移位指令
SHL(左移)指令使目的操作数逻辑左移一位,最低位用 0 填充。最高位移入进位标志位,而进位标志位中原来的数值被丢弃:
若将 1100 1111 左移 1 位,该数就变为 1001 1110:
shl dl, 2 ;移动后:00101000
SHR(右移)指令使目的操作数逻辑右移一位,最高位用 0 填充。最低位复制到进位标志位,而进位标志位中原来的数值被丢弃:
-
SHL(Shift Left):逻辑左移
-
SHR(Shift Right):逻辑右移
-
格式:
SHL/SHR Reg/Mem, CL/Imm
SHL eax,2
SHR word ptr es:[ebp],1
SHL al,1
举例:
SHR al,1 //al:10000001右移一位最高位补0,最低位移入CF,即01000000 / CF:1
SHL al,1 //al:01000001左移一位最高位移入CF,最低位补0,即10000010 / CF:0
-
shl/sal区别?这样一看,似乎SHL和SAL指令是完全一样的
区别在于,SHL是逻辑左移,SAL是算术左移
SAL算术移位需要考虑最高位的符号位,要保证移到后的最高位,也就是符号位不变。用于带符号数的运算。
最高位符号位移入CF,但本身保持原值,其余位顺序左移,次高位被舍弃。
SHL逻辑移不用考虑直接移就ok。所有位顺序左移,最高位移入CF,最低位补0。用于无符号数的计算
3.循环移位指令
以循环方式来移位即为位元循环(Bitwise Rotation)。一些操作中,从数的一端移出的位立即复制到该数的另一端。还有一种类型则是把进位标志位当作移动位的中间点。
ROL(循环左移)指令把所有位都向左移。最高位复制到进位标志位和最低位。该指令格式与 SHL 指令相同:
位循环不会丢弃位。
ROR(循环右移)指令把所有位都向右移,最低位复制到进位标志位和最高位。该指令格式与 SHL 指令相同:
-
ROL(Rotate Left):循环左移
-
ROR(Rotate Right):循环右移
-
格式:
ROL/ROR r/m, i8/CL
ROL eax,3
ROL cx,1
ROR cl,2
举例
ROL al,1 //al:10000001循环左移一位最高位补到最低位,最高位移入CF,即00000011 / CF:1
ROR al,1 //al:10000001循环右移一位最低位补到最高位,最低位移入CF,即11000000 / CF:1
4.带进位的循环移位指令
-
RCL(Rotate through Carry Left):带进位循环左移
-
RCR(Rotate through Carry Right):带进位循环右移
-
格式:RCL/RCR r/m, i8/CL
RCL eax,1
RCR cx,2
RCR al,1
举例:
RCL al,1 //al:10000001,CF:0循环左移一位最高位移入CF,CF原来的数补到最低位,即al:00000010 / CF:1
RCR al,1 //al:10000001,CF:0循环右移一位最低位移入CF,CF原来的数补到最高位,即al:01000000 / CF:1
C语言中的移位运算
与运算 &
printf("%d",2&3); //2
或运算 |
printf("%d",2|3); //3
非运算 ~
printf("%d",~2); //-3,因为%d打印的是有符号的整数
异或运算 ^
printf("%d",2^3); //1
移位运算 << >>
左移运算,有符号和无符号是无区别的
int a = 8;
printf("%d",a<<1); //16
unsigned int b = 8;
printf("%d",b<<1); //16
查看反汇编:生成的汇编指令都是shl,逻辑左移
右移运算,有符号对应SAR即算数右移,无符号对应SHR即逻辑右移
int a = 0xF0000000;
printf("%d",a>>1); //-134217728
unsigned int b = 0xF0000000;
printf("%d",b>>1); //2013265920
查看反汇编:有符号右移生成的指令为SAR,无符号右移生成的指令为SHR
作业【有错,pass吧】
#include "stdafx.h"
void Function_1_1()
{
unsigned char x=0xf5,y=0xff;
int i;
scanf("%d",&i);
/* if((x=x&(y<<i))==0)
(printf("第%d位是0",i+1));
else
(printf("第%d位是1",i+1));
*/
printf("第%d位是%x",i,x=x&(y>>(8-i)));
// printf("%x",x);
}
int main()
{
Function_1_1();
return 0;
}
16: unsigned char x=0xf5,y=0xff; 0040D830 mov byte ptr [ebp-0Ch],0F5h 0040D834 mov byte ptr [ebp-10h],0FFh 17: int i; 18: scanf("%d",&i); 0040D838 lea ecx,[ebp-14h] 0040D83B push ecx 0040D83C push offset string "112" (00422134) 0040D841 call scanf (0040faa0) 0040D846 add esp,8 19: /* if((x=x&(y<<i))==0) 20: (printf("第%d位是0",i+1)); 21: else 22: (printf("第%d位是1",i+1)); 23: */ 24: printf("第%d位是%x",i,x=x&(y>>(8-i))); 0040D849 mov edx,dword ptr [ebp-0Ch] 0040D84C and edx,0FFh 0040D852 mov eax,dword ptr [ebp-10h] 0040D855 and eax,0FFh 0040D85A mov ecx,8 0040D85F sub ecx,dword ptr [ebp-14h] 0040D862 sar eax,cl;//********************关键************** 0040D864 and edx,eax 0040D866 mov byte ptr [ebp-0Ch],dl 0040D869 mov ecx,dword ptr [ebp-0Ch] 0040D86C and ecx,0FFh 0040D872 push ecx 0040D873 mov edx,dword ptr [ebp-14h] 0040D876 push edx 0040D877 push offset string "%d\n" (0042201c) 0040D87C call printf (00401120) 0040D881 add esp,0Ch
看来这个小伙做错了!!!
-
用十六进制文本编辑器分别打开一个.exe、.dll、.sys、.txt、.doc、.jpg、.pdf等将前两个字节写在下面
.exe:内存中显式为4D 5A,我们读作0x5A4D .dll:同样是4D 5A .sys:4D 5A开头,但也有25 75 .txt:0A 66,读作0x660A .doc:D0 CF,读作0xCFD0 .jpg:FF D8 .pdf:25 50,读作0x5025
-
将一个在十六进制编辑器中打开的.exe文件,拖拽到最后,观察文件中的大小和硬盘上的大小
可以发现用十六进制编辑器打开一个.exe文件,它的最大地址0x0829CDF0,换算成10进制,就刚好等于这个文件的大小136957440字节