当前位置: 首页 > article >正文

漏洞技术分析实践_整数溢出

1. 内容概述

整数溢出漏洞发生在当整数存储超出其数据类型所能表示的范围时。主要原因在于计算机中的整数类型分为有符号整数和无符号整数。有符号整数使用最高位来表示正负号:0 表示正数,1 表示负数,而无符号整数没有这个规则。常见的整数类型有 8 位、16 位和 32 位等。整数溢出通常发生在存储的值超出了该类型可表示的范围时,从而导致数据异常和潜在的安全漏洞。

2. 基础知识

数据类型占用字节数范围
字符类型 char1-128~127
无符号字符类型 unsigned char10~255
布尔类型 bool10~1
短整型 short2-32768~32767
无符号整型 unsigned short20~65535
整型 int4-2147483648 ~2147483647
无符号整型 unsigned int40~4294967295
长整型 long4-2147483648 ~2147483647
无符号长整型 unsigned long40~4294967295
64位整型 long long8-2^63~2^63 -1

3. 漏洞原理

3.1 上溢问题

#include<stdio.h>

int main() {
    unsigned short a;
    a = 65535;    // 无符号短整型最大值
    a = a + 5;    // 超出范围,发生整数溢出
    printf("a = %d\n", a);  // 输出溢出后的值
    // 运行结果: a = 4

    short b;
    b = 32767;    // 有符号短整型最大值
    b = b + 5;    // 超出范围,发生整数溢出
    printf("b = %d\n", b);  // 输出溢出后的值
    // 运行结果: b = -32764

    return 0;
}

整数上溢是指当一个整数通过计算(如赋值)超过了其能够表示的上限时,结果会变成一个较小的数。例如,一个 `unsigned short` 类型的变量 `a = 65535`,当它再加 5 时会变成 4。这是因为在计算机中,65535 加上 5 的二进制表示相当于将 `(1111111111111111)₂` 加上 `(101)₂`,得到 `(10000000000000100)₂`。由于 `unsigned short` 类型的数据空间是 16 位,最高位的 1 会被舍弃,剩下低 16 位,即 `(0000000000000100)₂`,表示的数为 4。

当一个 `short` 类型的变量 `a = 32767` 时,若再加上 5,结果会变成 -32764。这是因为在计算机中,32767 的二进制表示是 `(0111111111111111)₂`,加上 5 后得到 `(1000000000000100)₂`。由于 `short` 类型的最高位是符号位,0 表示正数,1 表示负数,因此结果超出了 `short` 类型数据的表示范围,导致发生上溢,最终结果为 `(1000000000000100)₂`,对应的十进制值为 -32764。

计算机中的整数以补码形式存储,正数的补码与原码一致,负数的补码则是对原码按位取反再加 1。例如,`(1000000000000100)₂` 的补码经过反码处理后,加 1 的结果为 `(-32764)`,这是溢出后的最终值。

3.2 下溢问题

#include<stdio.h>

int main() {
    unsigned short a;
    short b;
    b = -4;    // 赋值一个负数给有符号短整型变量
    a = b;     // 将有符号短整型赋值给无符号短整型
    printf("a = %d\n", a);  // 输出无符号短整型变量的值

    return 0;
}

/* 输出结果:
a = 65532
*/

整数下溢是指当一个整数通过运算(赋值)低于它能表示的下限时,结果会变成一个很大的数。由于无符号整数不能识别负数,当一个 `short` 类型的变量 `b = -4` 赋值给 `unsigned short` 类型的变量 `a` 时,`a` 的值会变成 65532。这是因为 -4 是负数,在计算机中以补码形式存储,其二进制补码为 `(1111111111111100)₂`。由于 `unsigned short` 类型的变量没有符号位,所有位都用于表示数值,因此这个二进制补码被解释为正数 `(65532)₁₀`。

3.3 截断问题

#include<stdio.h>

int main() {
    short b;
    int a = 65537;  // 赋值一个超过 short 类型范围的数值
    b = a;          // 将 int 类型赋值给 short 类型
    printf("b = %d\n", b);  // 输出 short 类型的值

    return 0;
}

/* 输出结果:
b = 1
*/

截断是指将数据放入比它本身小的存储空间中,从而导致溢出。例如,当一个 `int` 类型的变量 `a = 65537` 被赋值给 `short` 类型的变量时,`short` 类型只能接受低 16 位,导致溢出。65537 的二进制表示为 `(*10000000000000001)₂`,只保留低 16 位,结果是 `b = 1`。这种情况下,数据的高位部分被舍弃,产生截断效应。

3.4 符号问题

#include <stdio.h>

int main() {
    short a = 32767;  // short 类型的最大值
    short b = -5;

    // 比较 a 和 b 的大小
    if (a > b) {
        printf("a = %d is greater than b = %d\n", a, b);
        // 正确的输出应该是 a > b
    } else {
        printf("a = %d is less than or equal to b = %d\n", a, b);
    }

    // 通过作差展示溢出问题
    short diff = a - b;  // 计算 a - b,发生溢出
    printf("Difference (a - b) = %d\n", diff);  // 输出溢出后的值

    return 0;
}

/* 输出结果:
a = 32767 is less than or equal to b = -5
Difference (a - b) = -32764
*/

有符号整数之间的比较:在有符号整数的比较中,通常通过作差来判断两个数的大小。如果结果为正,则说明第一个数大于第二个数;如果结果为负,则第一个数小于第二个数。然而,当两个有符号整数作差时出现上溢或下溢,比较结果可能与实际情况相反。例如,short 类型的 32767 与 -5 比较时,由于 32767 - (-5) 的计算导致溢出,结果为 -32764,从而得出错误的结论:32767 小于 -5。这种情况会导致比较结果不准确。

#include <stdio.h>

int main() {
    // 演示上溢
    short a = 32767;  // short 类型的最大值
    short b = 1;
    short result_overflow = a + b;  // 发生上溢

    printf("Overflow: %d + %d = %d\n", a, b, result_overflow);
    // 输出结果应为:Overflow: 32767 + 1 = -32768

    // 演示下溢
    short c = -32768;  // short 类型的最小值
    short d = -1;
    short result_underflow = c + d;  // 发生下溢

    printf("Underflow: %d + %d = %d\n", c, d, result_underflow);
    // 输出结果应为:Underflow: -32768 + (-1) = 32767

    return 0;
}

有符号整数的运算中,当两个有符号整数进行运算时,可能会发生上溢或下溢的情况。例如,如果运算结果超出有符号整数的表示范围,就会导致溢出,从而使结果不符合预期。

#include <stdio.h>

int main() {
    short a = -5;                // 有符号短整型,值为 -5
    unsigned short b = 13;       // 无符号短整型,值为 13

    // 比较有符号和无符号整数
    if (a > b) {
        printf("a = %d is greater than b = %u\n", a, b);
    } else {
        printf("a = %d is less than or equal to b = %u\n", a, b);
    }

    return 0;
}

/* 输出结果:
a = -5 is greater than b = 13
*/

当无符号整数和有符号整数进行比较或运算时,有符号整数会被转换为无符号整数,从而导致上溢或下溢,并得出与事实相反的结论。例如,当 `short` 类型的 -5 和 `unsigned short` 类型的 13 进行比较时,-5 会被转换为无符号整数 65531,因此比较结果为 65531 > 13,这与实际相反。这种转换可能会导致错误的逻辑判断。

4. 实例分析

#include <stdio.h>
#define N 105

int main() {
    int Number;
    unsigned short number;

    while (1) {
        printf("ID Number: ");
        scanf("%d", &Number);
        number = Number;

        if (number <= N && Number > N) {
            puts("Welcome !");
            break;
        } else {
            printf("%d\n", number);
            printf("It is illegal, please check your id number.\n");
        }
    }

    return 0;
}

利用整数溢出的思路是通过输入一个超出数据类型范围的数值,使变量发生溢出,从而达到程序逻辑要求的条件。以 unsigned short 为例,当输入的值超过其最大值 65535 时,会发生溢出,重新从 0 开始计数。通过输入如 65541 的值,unsigned short 类型的变量 number 将溢出为较小的值(例如 65541 - 65536 = 5),这样满足了 number <= 105Number > 105 的条件,从而输出 "Welcome!"。这种方法利用了无符号整数溢出后循环计数的特性,来满足程序中看似冲突的逻辑条件。


http://www.kler.cn/news/365161.html

相关文章:

  • 解决Redis缓存穿透(缓存空对象、布隆过滤器)
  • Servlet(三)-------Cookie和session
  • 博弈论 C++
  • 虚拟机安装麒麟v10、配置网络、安装docker
  • 第五十四章 安全元素的详细信息 - DerivedKeyToken 详情
  • Android13、14特殊权限-应用安装权限适配
  • “智能科研写作:结合AI与ChatGPT提升SCI论文和基金申请质量“
  • 微信小程序实现canvas电子签名
  • 开源进销存软件如何助力中小企业数字化转型?
  • [论文阅读]TELeR: A General Taxonomy of LLM Prompts for Benchmarking Complex Tasks
  • 二分查找_在排序数组中查找元素的第一个和最后一个位置
  • 测试WIFI和以太网的TCP带宽、UDP带宽和丢包率、延时
  • 揭开C++ STL的神秘面纱之string:提升编程效率的秘密武器
  • 没错,Go 语言的函数参数没有引用传递方式
  • 考研读研生存指南,注意事项
  • useEffect简单介绍
  • java -jar启动 报错: Error: Unable to access jarfile
  • NVR小程序接入平台/设备EasyNVR多品牌NVR管理工具/设备的多维拓展与灵活应用
  • 基于SSM+微信小程序的家政服务管理系统(家政2)
  • 批量归一化(Batch Normalization)
  • 混个1024勋章
  • [笔记] 关于CreateProcessWithLogonW函数创建进程
  • Linux系统
  • 鸿蒙到底是不是纯血?到底能不能走向世界?
  • 蓝桥杯题目理解
  • Python爬虫:urllib_ajax的get请求豆瓣电影前十页(08)