4.29日更新
👴今天忙的有点久,到家了已经16.11啦,不多说了,想打开游戏玩了,想了想,好无聊还是学习吧
今日任务:小黄书+看雪CTF 第三题
步入正题!
各种算术运算的工作形式
除法
除法计算约定,对应汇编指令分有符号idiv
和无符号div
两种,除法指令的执行周期比较长,效率比较低,所以编译器会想尽办法用其他运算指令代替除法指令,所以C++中的除法和数学中的除法不同,在C++中,除法运算补保留余数,有专门求取余数的运算(运算符为%),叫做取模运算,对于整数除法,C++的规定是仅仅保留整数部分,小数部分完全舍弃
计算机整数除法,a/b
两个无符号整数相除,结果是无符号,两个有符号相除,结果是有符号,一个有符号,一个无符号相除,结果是无符号的,有符号数的最高位(符号位)被作为数据位对持,然后作为无符号数参与计算
对于除法而言,计算机面临着如何处理小数部分的问题,正常来说 7/2 = 3.5,而计算机而言,整数除法的结果必须为整数,对于3.5这样的,计算机取整数部分的方式有如下几种:
向下取整
根据整数值的取值范围,可以画出以下坐标轴:
向下取整:就是取像负无穷方向最接近x的整数值,换而言之也就是取得不大于x的最大整数
例如:+3.5向下取整得到3;-3.5向下取整得到-4
数学描述中,这个符号有点打不出来,我直接画一下
C语言中math.h
中,定义了floor
函数,作用就是向下取整,也叫为地板取整,向下取整的除法,除数为2的幂时,直接用右移指令 sar
来完成
向上取整
所谓对x向上取整,就是取 正无穷方向接近x的整数值,取得不小于x的最小整数
例如:+3.5向下取整得到4;-3.5向下取整得到-3
数学描述中,这个符号有点打不出来,我直接画一下
在C语言的math.h
中定义了ceil
函数,作用就是向上取整,称为天花板取整
向零取整
所谓对x向零取整,就是取得往0方向最接近x的整数值,换而言之也就是放弃小数部分
例如:+3.5向零取整得到3;-3.5向零取整得到-3
在我们的数学描述中,[x] 表示对 x向零取整
这三个问题都会存在一个定律,我直接用ipad写吧,这个画板不好写
在C语言中和其他高级语言中,对整数除法规定为向零取整,也称为 截断除法
除法数学定理以及推导
好的,👴感觉我学了一波计算机无关的东西,学了一堆数学,小盆友你是否有很多问号🤣,这里大部分我将会用iPad写一下
回顾一下除法以以及余数的原则:
假设被除数为a,除数为b,商为q,余数为r,会有如下一些重要的性质:
- r的绝对值 < b的绝对值
- a = b*q + r
- b = (a - r) / q
- q = (a - r) / b
- r = a - q*b
举例子:
1
| printf("8 %% -3 = %d \r\n",8 % -3)
|
C语言规定的整数除法为向零取整,所以 8 / -3 = -2 那么余数就是2,👴就不举例子了,大家都会 很基础
直接说定理吧:
VC++ 6.0对除数为整形常量的除法的处理,如果除数是变量,则只能使用除法指令,如果除数为常量,就有了优化的余地,根据除数值的相关特性,编译器有对应的处理方式,测试一下除数为2的幂,非2的幂,负数等各类情况的处理方式,假设整形为4字节补码的形式
各类型除法转换——Debug版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| //C++源码说明:除法运算 //变量定义 int nVarOne = argc; int nVarTwo = argc; //两个变量做除法 printf("nVarOne / nVarTwo = %d", nVarOne / nVarTwo); //变量除以常量,常量为2的1次方 printf("nVarOne / 2 = %d", nVarOne / 2); //变量除以非2的幂 printf("nVarTwo / 7 = %d", nVarTwo / 7); //变量对非2的幂取模 printf("nVarTwo % 7 = %d", nVarTwo % 7); //变量除以常量,常量为2的3次方 printf("nVarOne / 8 = %d", nVarOne / 8);
//C++源码与对应汇编代码讲解 //C++源码对比,变量定义 int nVarOne = argc; 0040B7E8 mov eax,dword ptr [ebp+8] 0010B7EB mov dword ptr [ebp-4],eax //C++源码对比,变量定义 int nVarTwo = argc; 0040B7EE mov ecx,dword ptr [ebp+8] 0040B7F1 mov dword ptr [ebp-8],ecx //除法运算转换特性 //C++源码对比,变量 / 变量 printf("nVarOne / nVarTwo = %d", nVarOne / nVarTwo); //取出被除数放入eax中 0040B7F4 mov eax,dword ptr [ebp-4] //扩展高位(EDX:EAX,这里表示EDX,EAX连用表示64位数 ) 0040B7F7 cdq //两变量相除,直接使用有符号除法指令idiv 0040B7F8 idiv eax,dword ptr [ebp-8] //eax保存商值,作为参数压栈,调用函数printf 0040B7FB push eax 0040B7FC push offset string "nVarOne / nVarTwo = %d" (00420034) 0040B801 call printf (0040B750) 0040B806 add esp,8 //C++源码对比,变量 / 常量 (常量值为2的1次方) printf("nVarOne / 2 = %d", nVarOne / 2); 0040B809 mov eax,dword ptr [ebp-4] 0040B80C cdq //自身减去扩展高位 0040B80D sub eax,edx //和乘法运算类似,乘法是左移 0040B80F sar eax,1 //printf函数说明略 //C++源码对比,变量 / 常量 (非2的幂) printf("nVarTwo / 7 = %d", nVarTwo / 7); 0040B81F mov eax,dword ptr [ebp-8] 00040B22 cdq 0040B823 mov ecx,7 //无忧化直接使用有符号除法指令idiv 0040B828 idiv eax,ecx //printf函数说明略 //C++源码对比,变量 % 常量 printf("nVarTwo % 7 = %d", nVarTwo % 7); 0040B838 mov eax,dword ptr [ebp-8] 0040B83B cdq //执行 CDQ 后, CDQ 把第 31 bit 复制至 EDX 所有 bit 0040B83C mov ecx,7 //无忧化,直接使用有符号指令idiv 0040B841 idiv eax,ecx //除法指令过后,余数保存在扩展为edx中 0040B843 push edx //printf 函数说明略 //C++源码对比,变量 / 常量(常量值为2的3次方) printf("nVarOne / 8 = %d",nVarOne / 8); //取出被除数放入eax 0040B851 mov eax,dword ptr [ebp-4] //扩展eax高位到edx 0040B854 cdq //如果eax为负数 0040B855 and edx,7 //使用eax加edx,若eax为负数则加7,反之加0 0040B858 add eax,edx //将eax右移3位 0040B85A sar eax,3 //printf函数说明略
|
👴这里最后的那步操作没太懂,我决定调试去看看咋回事
我去试试正负数
我们对除数为2的幂,进行分析
那么上面的代码中拿出来仔细分析一下
1 2 3 4 5 6 7 8 9
| //C++源码对比,变量 / 常量 (常量值为2的1次方) printf("nVarOne / 2 = %d", nVarOne / 2); 0040B809 mov eax,dword ptr [ebp-4] 0040B80C cdq //自身减去扩展高位 0040B80D sub eax,edx //和乘法运算类似,乘法是左移 0040B80F sar eax,1 //printf函数说明略
|
0040B80C的cdq的含义是符号扩展到高位edx,如果eax的最高位(符号位)为1,那么edx的值为0xFFFFFFFF,也就是-1,否则就是0,0040B80D地址处的sub eax,edx
执行的操作就是将eax减去高位edx,实际上就是被除数为负数的情况下,由于除数为整数(+2的幂),除法的商为负数,移位运算等同于向下取整,C语言的除法是向零取整,因此需要对商为负的情况进行+1处理,那么这里的sub eax,edx,如果edx 为0xFFFFFFFF 减去等同于+1,如果为0 减去就是0 说明 eax为正数,那么进行向下取整无问题,所以这样的设计可以避免分支结构的产生
1 2 3 4 5 6 7 8 9 10 11 12 13
| //C++源码对比,变量 / 常量(常量值为2的3次方) printf("nVarOne / 8 = %d",nVarOne / 8); //取出被除数放入eax 0040B851 mov eax,dword ptr [ebp-4] //扩展eax高位到edx 0040B854 cdq //如果eax为负数 0040B855 and edx,7 //使用eax加edx,若eax为负数则加7,反之加0 0040B858 add eax,edx //将eax右移3位 0040B85A sar eax,3 //printf函数说明略
|
0040B854的cdq是符号扩展到高位edx,在0040B855处对edx做位与运算,当被除数为负数时,edx的值为7,在0040B858处的add eax,edx就是被除数为负数时加上2^n -1,不为负数则加0,最后sar右移完成除法
release版本对非2的幂也进行了优化,明天在看!
看雪CTF——寻踪觅源
打开了题,放到ida直接去看看
明天👴再继续看,今天太忙了,没怎么学习!呜呜呜呜,自己有点事情!