内容参考了《CTF 竞赛权威指南 Pwn 篇》

# 一、计算机中的整数

计算机中整数通常分为两种,一种为有符号整数,另一种为无符号整数

c 数据类型

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. 溢出

只有 有符号数 才会发生溢出,我们知道,一般计算机中,有符号数的 最高位 代表着 符号位 ,用来表示一个数正负,通过两个正数相加或者两个负数相减,进位时使得符号位发生变化,这样就导致了溢出

有符号整数

c
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 的值

c
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. 截断

将一个较大宽度的数存入一个宽度较小的操作数中,导致 高位 发生截断

c
0xffff ffff  +  0x0000 0001
=0x 0000 0001 0000 0000 (long long)
=0x 0000 0000   (long)   // 这里高位就发生了截断,只保留了低位

在整数转换中:

整数转换是一种用与表示 赋值强制类型转换 、或者 计算结果 值的底层数据类型的转变 (比如出现小数)。当一个宽度类型转向一个更大的宽度类型,往往会保留 数学值 ,而反过来就会导致高位丢失。例如把一个 unsigned char 加到一个 sign char 上(高位符号损失)。总的来说会产生两种错误:第一损失值,当宽度转到更小的宽度的类型时会 损失值 ;第二损失符号,从 有符号类型转无符号类型 时会损失符号

整型提升是指当表达式中包含了不同宽度的操作数时,较小宽度的操作数会被提升到和较大操作数一样的宽度,然后再进行计算。

c
#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

c
void *memcpy(void *dest,const void *src,size t_n);

memcpy () 函数将 src 所指向的字符串中以 src地址 开始的 前n个字节 复制到 dest 所指的数组中,并返回 dest

# 2.strncpy

c
char *strncpy(char *dest, const char *src,size_t n)

srncpy () 函数从源 src 所指的内存地址的起始位置开始复制 n 个字节到目标 dest 所指的 内存地址 的起始位置中

# 上面两个函数都有一个类型为 size_t 的参数,它是无符号整型的 sizeof 运算符的结果。

c
typedef unsigned int size_t;