内容参考了《CTF 竞赛权威指南 Pwn 篇》
# 一、计算机中的整数
计算机中整数通常分为两种,一种为有符号整数,另一种为无符号整数
c 数据类型
//32 位 最小值~最大值 | |
[signed] char -128 ~ 127 | |
unsigned char 0 ~ 255 | |
short -32 768 ~ 32 767 | |
unsigned short 0 ~ 65 535 | |
int -2 147 483 648 ~ 2 147 483 647 | |
unsigned 0 ~ 4 294 967 295 | |
long -2 147 483 648 ~ 2 147 483 647 | |
unsigned long 0 ~ 4 294 967 295 | |
//64 位 | |
[signed] char -128 ~ 127 | |
unsigned char 0 ~ 255 | |
short -32 768 ~ 32 767 | |
unsigned short 0 ~ 65 535 | |
int -2 147 483 648 ~ 2 147 483 647 | |
unsigned 0 ~ 4 294 967 295 | |
long -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 | |
unsigned long 0 ~ 18 446 744 073 709 551 615 |
# 二、整数安全漏洞
整数的异常情况通常有三种, 溢出
; 回绕
; 截断
# 1. 溢出
只有 有符号数
才会发生溢出,我们知道,一般计算机中,有符号数的 最高位
代表着 符号位
,用来表示一个数正负,通过两个正数相加或者两个负数相减,进位时使得符号位发生变化,这样就导致了溢出
有符号整数
int i=max_int ; // i=2 147 483 647 | |
i++; // i= -2 147 483 648 ,此时会导致上溢出,改变符号位 | |
int j=min_int ; // j= -2 147 483 648 | |
j--; //j= 2 147 483 647 下溢出 |
# 2. 回绕
无符号的数永远不会溢出,当它达到最大值的时候会回到最小值【并且由上面可以看见,无符号数最小值都为 0
】,因此一个无符号的整数表达式永远也不会得到小于 0 的值
unsigned int i=unsign_max_int; // i=4 294 967 295 (x86-32,x64-64 相同) | |
i++; // i=0; 产生回绕 | |
unsigned int j=unsign_min_int ; // j=0; | |
j-- ; // j=4 294 967 295 |
# 3. 截断
将一个较大宽度的数存入一个宽度较小的操作数中,导致 高位
发生截断
0xffff ffff + 0x0000 0001 | |
=0x 0000 0001 0000 0000 (long long) | |
=0x 0000 0000 (long) // 这里高位就发生了截断,只保留了低位 |
在整数转换中:
整数转换是一种用与表示 赋值
、 强制类型转换
、或者 计算结果
值的底层数据类型的转变 (比如出现小数)。当一个宽度类型转向一个更大的宽度类型,往往会保留 数学值
,而反过来就会导致高位丢失。例如把一个 unsigned char
加到一个 sign char
上(高位符号损失)。总的来说会产生两种错误:第一损失值,当宽度转到更小的宽度的类型时会 损失值
;第二损失符号,从 有符号类型转
为 无符号类型
时会损失符号
整型提升是指当表达式中包含了不同宽度的操作数时,较小宽度的操作数会被提升到和较大操作数一样的宽度,然后再进行计算。
#include<stdio.h> | |
int l=0xabcddcba; | |
short s=l; | |
char c=l; | |
// 宽度溢出 | |
printf("l=0x%x (%d bits) \n ”, l , sizeof(l)*8"); | |
printf("l=0x%x (%d bits) \n " , s , sizeof(s)*8"); | |
printf("l=0x%x (%d bits) \n ", c , sizeof(c)*8"); | |
// 整型提升 | |
printf("s+c =0x%x (%d bits) \n " , s+c , sizeof(s+c)*8"); | |
// 输出结果: | |
l=0xabcd dcba (32 bits) | |
s= 0xffff dcba (16 bits) | |
c=0xffff ffba (8bits) | |
s+c=0xffff dc74 (32 bits) |
# 3. 漏洞多发函数
整数溢出往往配合着其他类型的缺陷才能有用, size_t
类型的参数(size_t 是 无符号整数类型
的 sizeof()的结果,会将别的数转化为无符号整型), 常常被误用而产生整数溢出,然后可能导致缓冲区溢出
# 1.memcpy
void *memcpy(void *dest,const void *src,size t_n); |
memcpy () 函数将 src 所指向的字符串中以 src地址
开始的 前n个字节
复制到 dest 所指的数组中,并返回 dest
。
# 2.strncpy
char *strncpy(char *dest, const char *src,size_t n) |
srncpy () 函数从源 src
所指的内存地址的起始位置开始复制 n 个字节到目标 dest 所指的 内存地址
的起始位置中
# 上面两个函数都有一个类型为 size_t 的参数,它是无符号整型的 sizeof 运算符的结果。
typedef unsigned int size_t; |