avatar

4.29日笔记 (week 2)

4.29日更新

👴今天忙的有点久,到家了已经16.11啦,不多说了,想打开游戏玩了,想了想,好无聊还是学习吧

今日任务:小黄书+看雪CTF 第三题

步入正题!

各种算术运算的工作形式

除法

除法计算约定,对应汇编指令分有符号idiv和无符号div两种,除法指令的执行周期比较长,效率比较低,所以编译器会想尽办法用其他运算指令代替除法指令,所以C++中的除法和数学中的除法不同,在C++中,除法运算补保留余数,有专门求取余数的运算(运算符为%),叫做取模运算,对于整数除法,C++的规定是仅仅保留整数部分,小数部分完全舍弃

计算机整数除法,a/b两个无符号整数相除,结果是无符号,两个有符号相除,结果是有符号,一个有符号,一个无符号相除,结果是无符号的,有符号数的最高位(符号位)被作为数据位对持,然后作为无符号数参与计算

对于除法而言,计算机面临着如何处理小数部分的问题,正常来说 7/2 = 3.5,而计算机而言,整数除法的结果必须为整数,对于3.5这样的,计算机取整数部分的方式有如下几种:

向下取整

根据整数值的取值范围,可以画出以下坐标轴:

image-20200429172745689

向下取整:就是取像负无穷方向最接近x的整数值,换而言之也就是取得不大于x的最大整数

例如:+3.5向下取整得到3;-3.5向下取整得到-4

数学描述中,这个符号有点打不出来,我直接画一下

image-20200429174930773

C语言中math.h中,定义了floor函数,作用就是向下取整,也叫为地板取整,向下取整的除法,除数为2的幂时,直接用右移指令 sar来完成

向上取整

所谓对x向上取整,就是取 正无穷方向接近x的整数值,取得不小于x的最小整数

例如:+3.5向下取整得到4;-3.5向下取整得到-3

数学描述中,这个符号有点打不出来,我直接画一下

image-20200429174329067

在C语言的math.h中定义了ceil函数,作用就是向上取整,称为天花板取整

向零取整

所谓对x向零取整,就是取得往0方向最接近x的整数值,换而言之也就是放弃小数部分

例如:+3.5向零取整得到3;-3.5向零取整得到-3

在我们的数学描述中,[x] 表示对 x向零取整

这三个问题都会存在一个定律,我直接用ipad写吧,这个画板不好写

image-20200429184749337

在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,👴就不举例子了,大家都会 很基础

直接说定理吧:

image-20200429215858565

image-20200429221612549

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函数说明略

👴这里最后的那步操作没太懂,我决定调试去看看咋回事

image-20200429231134465

我去试试正负数

image-20200429233828187

我们对除数为2的幂,进行分析

image-20200429235133925

那么上面的代码中拿出来仔细分析一下

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直接去看看

image-20200430014759308

明天👴再继续看,今天太忙了,没怎么学习!呜呜呜呜,自己有点事情!

Author: L0x1c
Link: https://l0x1c.github.io/2020/04/29/2020-4-29/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶