PWN入门到入狱-Integer
关于整数的异常情况主要有三种:
- 溢出
- 只有有符号数才会发生溢出。有符号数最高位表示符号,在两正或两负相加时,有可能改变符号位的值,产生溢出
- 溢出标志
OF
可检测有符号数的溢出
- 回绕
- 无符号数
0-1
时会变成最大的数,如 1 字节的无符号数会变为 255
,而 255+1
会变成最小数 0
。
- 进位标志
CF
可检测无符号数的回绕
- 截断
- 将一个较大宽度的数存入一个宽度小的操作数中,高位发生截断
有符号整数溢出
1 2 3 4
| int i; i = INT_MAX; i++; printf("i = %d\n", i);
|
1 2 3
| i = INT_MIN; i--; printf("i = %d\n", i);
|
无符号数回绕
涉及无符号数的计算永远不会溢出,因为不能用结果为无符号整数表示的结果值被该类型可以表示的最大值加 1 之和取模减(reduced modulo)。因为回绕,一个无符号整数表达式永远无法求出小于零的值。
使用下图直观地理解回绕,在轮上按顺时针方向将值递增产生的值紧挨着它:
1 2 3 4 5 6 7
| unsigned int ui; ui = UINT_MAX; ui++; printf("ui = %u\n", ui); ui = 0; ui--; printf("ui = %u\n", ui);
|
截断
1 2 3
| 0xffffffff + 0x00000001 = 0x0000000100000000 (long long) = 0x00000000 (long)
|
1 2 3
| 0x00123456 * 0x00654321 = 0x000007336BF94116 (long long) = 0x6BF94116 (long)
|
整型提升和宽度溢出
整型提升是指当计算表达式中包含了不同宽度的操作数时,较小宽度的操作数会被提升到和较大操作数一样的宽度,然后再进行计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include<stdio.h> void main() { int l; short s; char c;
l = 0xabcddcba; s = l; c = l;
printf("宽度溢出\n"); printf("l = 0x%x (%d bits)\n", l, sizeof(l) * 8); printf("s = 0x%x (%d bits)\n", s, sizeof(s) * 8); printf("c = 0x%x (%d bits)\n", c, sizeof(c) * 8);
printf("整型提升\n"); printf("s + c = 0x%x (%d bits)\n", s+c, sizeof(s+c) * 8); }
|
1 2 3 4 5 6 7
| $ ./a.out 宽度溢出 l = 0xabcddcba (32 bits) s = 0xffffdcba (16 bits) c = 0xffffffba (8 bits) 整型提升 s + c = 0xffffdc74 (32 bits)
|
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
| gdb-peda$ disassemble main Dump of assembler code for function main: 0x0000056d <+0>: lea ecx,[esp+0x4] 0x00000571 <+4>: and esp,0xfffffff0 0x00000574 <+7>: push DWORD PTR [ecx-0x4] 0x00000577 <+10>: push ebp 0x00000578 <+11>: mov ebp,esp 0x0000057a <+13>: push ebx 0x0000057b <+14>: push ecx 0x0000057c <+15>: sub esp,0x10 0x0000057f <+18>: call 0x470 <__x86.get_pc_thunk.bx> 0x00000584 <+23>: add ebx,0x1a7c 0x0000058a <+29>: mov DWORD PTR [ebp-0xc],0xabcddcba 0x00000591 <+36>: mov eax,DWORD PTR [ebp-0xc] 0x00000594 <+39>: mov WORD PTR [ebp-0xe],ax 0x00000598 <+43>: mov eax,DWORD PTR [ebp-0xc] 0x0000059b <+46>: mov BYTE PTR [ebp-0xf],al 0x0000059e <+49>: sub esp,0xc 0x000005a1 <+52>: lea eax,[ebx-0x1940] 0x000005a7 <+58>: push eax 0x000005a8 <+59>: call 0x400 <puts@plt> 0x000005ad <+64>: add esp,0x10 0x000005b0 <+67>: sub esp,0x4 0x000005b3 <+70>: push 0x20 0x000005b5 <+72>: push DWORD PTR [ebp-0xc] 0x000005b8 <+75>: lea eax,[ebx-0x1933] 0x000005be <+81>: push eax 0x000005bf <+82>: call 0x3f0 <printf@plt> 0x000005c4 <+87>: add esp,0x10 0x000005c7 <+90>: movsx eax,WORD PTR [ebp-0xe] 0x000005cb <+94>: sub esp,0x4 0x000005ce <+97>: push 0x10 0x000005d0 <+99>: push eax 0x000005d1 <+100>: lea eax,[ebx-0x191f] 0x000005d7 <+106>: push eax 0x000005d8 <+107>: call 0x3f0 <printf@plt> 0x000005dd <+112>: add esp,0x10 0x000005e0 <+115>: movsx eax,BYTE PTR [ebp-0xf] 0x000005e4 <+119>: sub esp,0x4 0x000005e7 <+122>: push 0x8 0x000005e9 <+124>: push eax 0x000005ea <+125>: lea eax,[ebx-0x190b] 0x000005f0 <+131>: push eax 0x000005f1 <+132>: call 0x3f0 <printf@plt> 0x000005f6 <+137>: add esp,0x10 0x000005f9 <+140>: sub esp,0xc 0x000005fc <+143>: lea eax,[ebx-0x18f7] 0x00000602 <+149>: push eax 0x00000603 <+150>: call 0x400 <puts@plt> 0x00000608 <+155>: add esp,0x10 0x0000060b <+158>: movsx edx,WORD PTR [ebp-0xe] 0x0000060f <+162>: movsx eax,BYTE PTR [ebp-0xf] 0x00000613 <+166>: add eax,edx 0x00000615 <+168>: sub esp,0x4 0x00000618 <+171>: push 0x20 0x0000061a <+173>: push eax 0x0000061b <+174>: lea eax,[ebx-0x18ea] 0x00000621 <+180>: push eax 0x00000622 <+181>: call 0x3f0 <printf@plt> 0x00000627 <+186>: add esp,0x10 0x0000062a <+189>: nop 0x0000062b <+190>: lea esp,[ebp-0x8] 0x0000062e <+193>: pop ecx 0x0000062f <+194>: pop ebx 0x00000630 <+195>: pop ebp 0x00000631 <+196>: lea esp,[ecx-0x4] 0x00000634 <+199>: ret End of assembler dump.
|
在整数转换的过程中,有可能导致下面的错误:
- 损失值:转换为值的大小不能表示的一种类型
- 损失符号:从有符号类型转换为无符号类型,导致损失符号
漏洞多发函数
我们说过整数溢出要配合上其他类型的缺陷才能有用,下面的两个函数都有一个 size_t
类型的参数,常常被误用而产生整数溢出,接着就可能导致缓冲区溢出漏洞。
1 2 3
| #include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
|
memcpy()
函数将 src
所指向的字符串中以 src
地址开始的前 n
个字节复制到 dest
所指的数组中,并返回 dest
。
1 2 3
| #include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
|
strncpy()
函数从源 src
所指的内存地址的起始位置开始复制 n
个字节到目标 dest
所指的内存地址的起始位置中。
两个函数中都有一个类型为 size_t
的参数,它是无符号整型的 sizeof
运算符的结果。
1
| typedef unsigned int size_t;
|