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

安全编程期末复习12(红色重点向下兼容)

目录

一.导论

(1)安全缺陷 

(2)漏洞 

 漏洞和缺陷的关系:

漏洞分类 

N-Day漏洞 

代码问题、配置错误、环境问题、其他 

漏洞利用

方式(病毒、蠕虫、木马)

造成的安全事件 

(3)产生缺陷、漏洞的原因

​编辑

(4)缓解安全问题 

二、字符串

(1)软件工程中字符串数据类型

界限、低位地址、高位地址、TooFar、目标大小、空字符结尾、长度

字符串编码方式 

多字节字符集(MBCS)、宽字符(宽字符串) 和 UTF-8

(2)C/C++中的字符串

字符串字面值

字符类型

std::string

核心差异示例

字符常量类型 

计算字符串大小

​编辑(3)常见的字符串操作错误(安全缺陷)

无界字符串复制(unbounded string copy)

差一错误(Off-by-One Error )

空结尾错误(null termination error)

字符串截断(string truncation)

与函数无关的字符串错误

(4)字符串漏洞及其利用

手动去掉 \n:Password[strcspn(Password, "\n")] = '\0';

gets不会限制输入的字符数,读完后将换行符从输入中移除,并自动用空字符(\0)替换它

fgets会限制读取的字符数,会读入换行符;但会截断,截断后会自动添加\0 

gets_s会限制读取的字符数,不保留换行符在字符串中;会截断,截断后会自动添加\0

(5)栈溢出攻击(理解记忆,无需死记)

栈溢出攻击

代码注入

(6)防御措施(理解记忆,无需死记)

安全字符串操作(预防) 

运行时侦测 

恢复方案 


一.导论

(1)安全缺陷 

安全缺陷包括软件缺陷安全缺陷

(2)漏洞 

 漏洞和缺陷的关系:

安全缺陷会导致漏洞

每个漏洞至少由一个安全缺陷引起

不是所有安全缺陷都会导致漏洞

在没有安全缺陷的情况下,不可能存在漏洞

安全缺陷是漏洞的必要条件(无缺陷则无漏洞),但非充分条件(有缺陷未必有漏洞) 

漏洞分类 

N-Day漏洞 
类型零日漏洞(0 - Day)N - Day 漏洞(如 1 - Day)
补丁状态无补丁,
厂商未发现漏洞
补丁已发布,
但用户未更新
攻击窗口漏洞未被公开,
攻击隐蔽性极强
攻击者利用用户更新延迟
防御重点依赖主动防御(如行为监控)依赖用户及时安装补丁
代码问题、配置错误、环境问题、其他 

漏洞利用

方式(病毒、蠕虫、木马)
特性蠕虫病毒木马
依赖宿主××
传播方式通过网络
自动传播
依赖宿主文件,
需要用户触发
伪装成合法软
件,诱骗用户
安装
自我复制×
主要目标快速传播,消
耗资源
破坏文件或系统窃取数据或
远程控制
造成的安全事件 

缓冲区错误类漏洞占比最大,代码问题最突出 

(3)产生缺陷、漏洞的原因

与软件缺陷一样,产生软件漏洞的原因,存在于软件生命周期的各个环节

产生软件缺陷、漏洞的原因贯穿软件生命周期各环节:安全需求分析时,若业务功能与安全需求(如输入验证)梳理不全面,易埋下风险;架构设计环节,认证授权策略、消息及数据交换方式的不合理,会使架构存在安全短板;代码安全层面,编程语言实现功能时的编码错误、逻辑缺陷,会直接产生代码级漏洞;调试测试阶段,因安全测试方法、用例不充分或工具选择不当,难全面发现问题;安全部署与配置环节,若未合理关闭冗余服务端口、开启审核策略,也会引入漏洞,让软件暴露于攻击风险 

(4)缓解安全问题 

首选是找到并修正漏洞 

二、字符串

字符串由一个以空(null)字符作为结束的连续字符序列组成,包含此空字符

(1)软件工程中字符串数据类型

界限、低位地址、高位地址、TooFar、目标大小、空字符结尾、长度

字符串数据类型概念中文说明描述
Bound界限数组中元素的数量。
Lo地位地址数组第一个元素的地址。
Hi高位地址数组最后一个元素的地址。
TooFar越界数组一个 “越界” 元素的地址,即刚好在 Hi 元素之后的那个元素的地址。
Target size(Tsize)目标大小

等价于用 sizeof 拿到的数组总字节数,比如数组里有 5 个 int 元素(每个 int 占 4 字节 ),Tsize 就是 5 * 4 = 20 字节 。

Null-terminated空字符结尾在 Hi(高位地址)位置或其之前,存在空终止符(\0 )
Length长度空终止符(\0)之前的字符数量。
字符串数据类型对应值(基于 hello\0 示例)
Bound6(元素总数,含 \0 )
Loh 的地址
Hi\0 的地址
TooFar\0 下一个字节的地址
Target size(Tsize)

每个字符(包括 \0 )占 1 字节 。共 6 个元素,所以 Tsize = 6 * 1 = 6 字节(也等价于 sizeof(数组名) 的值 )

Null-terminated是(存在 \0 )
Length5(\0 前有效字符数 )
strlen():计算字符串的实际字符数,不包括终止符 '\0'
sizeof():计算字符数组的总长度,包括终止符 '\0 
#include <stdio.h>
#include <string.h> int main() {// 用字符串字面量初始化字符数组,编译器会自动在末尾加 '\0',数组实际存储:'a' 'b' 'c' '\0'char str[] = "abc";  size_t len = strlen(str);size_t size = sizeof(str);  printf("strlen(str) = %zu\n", len);  printf("sizeof(str) = %zu\n", size); return 0;
}

输出结果:

strlen(str) = 3

sizeof(str) = 4

字符串编码方式 

多字节字符集(MBCS)、宽字符(宽字符串) 和 UTF-8

UTF-8
单字节:0xxxxxxx(7位X) 

双字节:110xxxxx 10xxxxxx(5位X + 6位X) 

三字节:1110xxxx 10xxxxxx 10xxxxxx(4位X + 6位X + 6位X)

四字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(3位X + 6位X + 6位X + 6位X) 

(2)C/C++中的字符串

字符串字面值

const char s[] = "abc"; //不要指定一个用字符串字面值初始化的字符数组的界限 意思是,当用像 "abc" 这样的字符串字面值去初始化 char 数组时,建议让编译器自动推导数组的大小(即不手动写方括号里的数字) 。因为字符串字面值 "abc" 在实际存储时,除了包含 'a''b''c' 这三个字符,还会自动在末尾添加一个用于标记字符串结束的空符 '\0' ,编译器能准确知晓需要多大的数组来容纳这些内容,手动指定大小容易出错或者显得多余。

关于 const char s[4] = "abc";

  • 语法与存储:这样写在 C 和 C++ 里语法是合法的。因为字符串字面值 "abc" 实际存储的是 'a''b''c''\0' ,共 4 个字符,所以用 const char s[4] 来承载,能正确容纳这些内容,s 数组里就会依次是 'a''b''c''\0' 。

  • 存在的问题:虽然这么写结果可能是对的,但存在隐患。比如,要是后续修改了字符串字面值,把 "abc" 改成更长的,比如 "abcd" ,而数组大小还是写死的 4 ,就会因为字符数量(连 '\0' 是 5 个)超过数组大小,导致数组越界等未定义行为 ;另外,手动写大小也显得冗余,毕竟编译器本就能自动算出来应该开多大的数组。所以才会建议直接用 const char s[] = "abc"; ,让编译器自动处理数组大小。

字符类型

std::string

 为什么是11?不理解???自己执行一遍,发现是5

#include<iostream>
#include<string>
int main() {std::string str = "hello\0world";std::cout << "String content: " << str << std::endl;std::cout<< "String length: " << str.length() << std::endl;return 0;
} 

  1. 字符串的初始化情况:当使用字符串字面量"hello\0world"std::string进行初始化时,\0会被当作字符串结束的标志。所以,str实际上存储的内容仅仅是hello
  2. 输出内容方面std::cout << str只会输出hello,因为std::string的输出操作是依据已存储的内容,而不是像 C 风格字符串那样依靠空字符来判断结束。
  3. 字符串长度计算str.length()返回的结果是 5,这是因为std::string在计算长度时,统计的是从开始到第一个空字符(初始化时遇到的)之间的字符数量。

关键区别总结

场景输出内容长度原因
C++ std::stringhello5初始化时遇到\0自动截断,str只包含hello。输出基于内部长度,而非\0
C 风格字符串 (char[])hello5字符串以\0结尾,strlen()printf遇到\0停止。剩余字符world被忽略。

为什么输出结果相同?

  1. C++ std::string

    std::string str = "hello\0world";  // 初始化截断为"hello"
    std::cout << str;  // 输出"hello"(长度5)
    
     

    std::string在初始化时遇到\0就停止解析,因此str内部只存储hello,长度为 5。

  2. C 风格字符串

    char str[] = "hello\0world";  // 数组实际为 ['h','e','l','l','o','\0','w','o','r','l','d']
    printf("%s", str);  // 输出"hello"(遇到\0停止)
    
     

    数组包含完整字符,但字符串以第一个\0结尾,printfstrlen只处理到\0为止。

核心差异示例

显式指定长度时,差异才会体现

// C++ std::string:显式指定长度
std::string str("hello\0world", 11);  // 强制包含所有字符(包括\0)
std::cout << str.length();  // 输出11
std::cout << str;           // 输出"hello"(cout遇到\0停止)
 
// C风格字符串:无法直接表示包含\0的字符串
// 以下代码会导致未定义行为(printf遇到\0提前结束)
char str[11] = {'h','e','l','l','o','\0','w','o','r','l','d'};
printf("%s", str);  // 输出"hello",无法显示后续字符

总结

  • 原始代码输出相同是因为初始化方式(隐式截断)导致两者内容一致。
  • 机制不同
    • std::string依赖内部长度,可存储\0
    • C 风格字符串依赖\0,无法存储\0作为有效字符。

std::string str = "helloworld";或者std::string str = "helloworld\0";

运行结果:

String content: helloworld
String length: 10

字符常量类型 

字符类型-unsignedchar的独特属性

存储在unsignedchar类型对象中的值,会被表示为二进制,并且不会被加上填充位 

当需要操作的对象可能是任何类型(如结构体、数组、指针等),并且需要访问该对象的所有二进制位时, unsigned char类型非常有用
任何类型的非二进制位域(non-bit-field)对象都可以复制到unsignedchar数组
中,并通过逐个字节检查其二进制表示。
//定义一个结构体 MyStruc
struct MyStruct {int a;     //整型成员,通常占 4 字节float b;   //单精度浮点型成员,通常占 4 字节
};// 1. 初始化结构体对象
// 用初始化列表给结构体成员赋值,a 为 10,b 为 3.14
struct MyStruct obj = {10, 3.14};// 2. 定义 unsigned char 数组 buffer
// sizeof(obj) 计算结构体对象占用的总字节数,用它作为数组长度,确保能容纳结构体所有二进制数据
unsigned char buffer[sizeof(obj)];// 3. 将结构体复制到unsigned char 数组中
// memcpy 是 C 标准库函数,作用是从源地址(&obj)拷贝指定字节数(sizeof(obj))的数据到目标地址(buffer)
memcpy(buffer, &obj, sizeof(obj));// 4. 将二进制数据写入文件
// fwrite 函数用于二进制写文件,参数依次是:数据缓冲区(buffer)、每个数据单元的大小(1 字节,因为 unsigned char 占 1 字节 )
// 数据单元的个数(sizeof(obj),即要写多少个 1 字节单元 )、文件指针(file)
fwrite(buffer, sizeof(unsigned char), sizeof(obj), file);// 1. 定义并初始化 int 变量 x
int x = 0x12345678;// 2. 定义 unsigned char 数组 buffer
// sizeof(x) 计算 int 变量占用的字节数(通常 4 字节 ),以此作为数组长度,确保容纳 x 的全部二进制数据
unsigned char buffer[sizeof(x)];// 3. 将int 复制到unsigned char 数组中
memcpy(buffer, &x, sizeof(x));// 4. 循环遍历 buffer 数组,逐字节打印二进制内容(以十六进制形式展示 )
// size_t 是无符号整数类型,用于表示对象大小
for (size_t i = 0; i < sizeof(x); i++) {// %zu 用于输出 size_t 类型的 i;02X 表示以十六进制、取两位(不足两位前面补 0 )、大写形式输出 buffer[i] 的值 printf("Byte %zu: 0x%02X\n", i, buffer[i]);
}

输出:

Byte 0: 0x78

Byte 1: 0x56

Byte 2: 0x34

Byte 3: 0x12   

%02X:用于输出无符号整数(如 buffer[i])的十六进制表示,并按以下规则格式化:
0:不足宽度时补前导零(如 05 而非 5)。
2:宽度至少为 2 位。
X:输出大写十六进制字母(如 A-F) 

计算字符串大小

大小(size):分配给数组的字节数(等于sizeof(array))。

计数(count):在数组中的元素数目。

长度(length):在空终结符\0之前的字符数。

#include <stdio.h>
#include <string.h>int main() {// 定义一个包含5个字符和1个终止符的字符数组char arr[6] = "hello";// 1. 大小(size):数组占用的总字节数size_t size = sizeof(arr);// 2. 计数(count):数组中的元素数目size_t count = sizeof(arr) / sizeof(arr[0]);// 3. 长度(length):终止符 '\0' 前的字符数size_t length = strlen(arr);printf("数组内容: %s\n", arr);printf("大小(size): %zu 字节\n", size);printf("计数(count): %zu 个元素\n", count);printf("长度(length): %zu 个字符\n", length);return 0;
}

char arr[] = "hello";或者char arr[6] = "hello";
数组内容: hello
大小(size): 6 字节
计数(count): 6 个元素
长度(length): 5 个字符 

char arr[7] = "hello";

数组内容: hello
大小(size): 7 字节
计数(count): 7 个元素
长度(length): 5 个字符 

宽字符串使用wchar_t类型表示;窄字符串使用char类型表示

(3)常见的字符串操作错误(安全缺陷)

 

无界字符串复制(unbounded string copy
差一错误( off-by-one error
空结尾错误( null termination error
字符串截断( string truncation

无界字符串复制(unbounded string copy)

本质使用 strcpy 等无长度限制的字符串复制函数,将源字符串内容复制到目标缓冲区时,不检查目标缓冲区剩余空间,若源字符串长度超过目标缓冲区可容纳的大小,就会发生缓冲区溢出(栈溢出 / 堆溢出等 )。

风险:破坏内存布局(覆盖相邻变量、返回地址等 ),可能导致程序崩溃、执行流劫持(如注入恶意代码 ),是栈溢出攻击的典型 “导火索”。

差一错误(Off-by-One Error )

逻辑因边界条件处理失误,循环、数组索引或缓冲区操作时,多操作或少操作一个字符。比如本应循环处理 n 个字符,却因 i <= n 而非 i < n ,导致超出预期范围。

影响:可能造成缓冲区溢出(多写一个字符覆盖相邻内存 )、数组越界访问,或字符串截断异常。这类问题较隐蔽(仅差一个字符 ),调试、发现难度高,但可能引发严重内存安全问题。 

空结尾错误(null termination error)

  • 关键:C/C++ 等语言中,字符串依赖 \0(空字符 )标识结尾。若操作中丢失、错误处理空字符,会破坏字符串结构。比如:
    • 未正确添加 \0,导致字符串后续内存被误读为有效内容,输出 / 处理时出现乱码、逻辑错误;
    • 错误覆盖 \0 位置,使字符串 “变长”,超出缓冲区范围引发溢出。
  • 风险:程序逻辑异常(如字符串比较、拼接出错 ),或因字符串边界失控引发内存安全问题。

字符串截断(string truncation)

场景目标缓冲区空间不足时,强行截断字符串(如 strncpy 未正确补 \0 ,或业务逻辑直接截断用户输入 )

问题:截断后字符串可能失去完整语义(如用户密码、关键标识被截断 ),导致功能异常(如登录验证失败 );若截断未妥善处理(如未补 \0 ),还可能引发空结尾错误、后续内存解析混乱。

与函数无关的字符串错误

  • 范畴:不依托特定字符串函数(如 strcpy/strcat ),而是因业务逻辑、内存管理等层面的设计缺陷,引发的字符串安全问题。比如:
    • 手动拼接字符串时,未计算总长度,导致缓冲区溢出;
    • 多线程环境下,字符串共享资源未加锁,并发操作引发数据混乱;
    • 对字符串编码(如 UTF-8 转 GBK )处理不当,产生截断、乱码甚至溢出。
  • 特点:成因更分散,需结合具体业务逻辑排查,常因开发者对 “字符串全生命周期(创建、处理、销毁 )” 考虑不全引发。

简单归纳:

无界字符串复制聚焦 “无长度校验的复制操作”,直接关联缓冲区溢出;

差一错误因 “边界 ±1 失误” 引发,问题隐蔽但危害连锁;

空结尾错误围绕 \0 字符的丢失 / 破坏,动摇字符串 “结尾标识” 基础;

字符串截断强调 “内容被截断” 的结果,可能引发功能 / 语义异常;

与函数无关的字符串错误脱离特定库函数,因业务 / 逻辑设计缺陷产生,覆盖场景更广。

(4)字符串漏洞及其利用

在 C/C++ 中,字符串常以 \0(空字符)作为结束标志,但输入数据(如用户输入密码)可能包含换行符 \n(或其他特殊字符),若未正确处理,会导致 逻辑漏洞(如密码验证绕过)或 内存问题(如缓冲区溢出)。

 

IsPasswordOK() 这类函数,通常用于验证用户输入的密码是否合法,但如果对输入字符串的边界(如 \n、内存分配)处理不当,就会被利用攻击。

手动去掉 \nPassword[strcspn(Password, "\n")] = '\0';

  • 问题场景
    用户输入密码时,输入可能带换行符(如用 fgets() 读取时,fgets 会把输入的 \n 一并读入字符串)。而密码验证逻辑通常只认 \0 前的字符,若保留 \n,会导致验证逻辑错误(比如预期密码是 123,实际存成 123\n,验证时匹配失败)。

  • 代码拆解

    • strcspn(Password, "\n")
      扫描 Password,找到第一个 \n 的位置,返回该位置的索引(比如 Password 是 "123\n45",则返回 3,即‘\n’的索引 )。
    • Password[... ] = '\0';
      把 \n 所在位置替换成 \0,直接截断字符串。比如原字符串是 "123\n",处理后变成 "123",确保密码验证逻辑正确。

① \n 手动换成 \0
如上面的例子,若函数逻辑依赖 \0 作为字符串结束,而输入可能引入 \n,则必须主动替换。这是因为不同函数对 “字符串结束” 的定义不同(比如 fgets 保留 \n,但密码验证逻辑只认 \0 )。

② 函数返回指针时,判空再使用
若函数返回动态分配的字符串(如 char* GetPassword() ),使用前必须检查是否为 NULL

char* pwd = GetPassword();
if (pwd != NULL) { // 安全使用 pwd printf("密码:%s\n", pwd); free(pwd); // 动态内存需释放 
} else {// 处理失败逻辑(如内存分配失败)printf("获取密码失败!\n");
}

若不判空,直接使用 NULL 指针会触发段错误(Segfault)

③ 动态内存记得 free
若字符串是动态分配的(如用 mallocnew 分配),使用完必须释放,否则会导致 内存泄漏。例如:

char* pwd = (char*)malloc(100); 
if (pwd != NULL) {// 使用 pwd ...free(pwd); // 释放内存,避免泄漏
}

gets不会限制输入的字符数,读完后将换行符从输入中移除,并自动用空字符(\0)替换它

#include <stdio.h>      // 提供标准输入输出函数,如printf、gets、puts
#include <string.h>     // 提供字符串处理函数,如strcmp
#include <stdlib.h>     // 提供标准库函数,如exit// 验证用户输入的密码是否正确
// 返回值:如果密码正确返回true(1),否则返回false(0)
bool IsPasswordOK(void) {char Password[12];  // 定义长度为12的字符数组存储密码// 实际只能存储11个字符+1个字符串结束符'\0'gets(Password);     // ❗️危险:使用gets函数读取输入// 该函数无法限制输入长度,可能导致缓冲区溢出// 攻击者可通过超长输入覆盖相邻内存区域,执行任意代码// 验证输入的密码是否等于预设密码"goodpass"return 0 == strcmp(Password, "goodpass");
}int main(void) {bool PwStatus;      // 存储密码验证结果   puts("Enter password:");  // 提示用户输入密码PwStatus = IsPasswordOK(); // 调用密码验证函数// 检查密码验证结果if (PwStatus == false) {puts("Access denied"); // 密码错误,输出拒绝访问exit(-1);              // 终止程序,返回错误码-1}puts("Access granted");    // 密码正确,输出访问授权 return 0;                  // 程序正常结束,返回0
}

fgets会限制读取的字符数,会读入换行符;但会截断,截断后会自动添加\0 

#include <stdio.h>   // 包含标准输入输出库
#include <stdlib.h>  // 包含标准库(如exit函数)
#include <string.h>  // 包含字符串处理函数(如strcmp, strcspn)// 验证用户输入的密码是否正确
bool IsPasswordOK(void) {char Password[12];  // 定义一个长度为12的字符数组存储密码(最多11个有效字符+1个字符串结束符)// 从标准输入读取最多11个字符(留一个位置给字符串结束符)fgets(Password, sizeof(Password), stdin);// 查找换行符'\n'首次出现的位置,并将其替换为字符串结束符'\0'// 这样可以消除fgets可能读入的换行符Password[strcspn(Password, "\n")] = '\0';// 将处理后的用户输入与预设密码"goodpass"进行比较// 若相同则返回0(转换为bool类型后为true),否则返回非0值(false)return 0 == strcmp(Password, "goodpass");
}int main(void) {bool PwStatus;  // 存储密码验证结果的布尔变量puts("Enter password:");  // 提示用户输入密码PwStatus = IsPasswordOK();  // 调用密码验证函数并保存结果// 检查密码验证结果if (PwStatus == false) {puts("Access denied");  // 验证失败提示exit(-1);  // 终止程序并返回错误码-1}puts("Access granted");  // 验证通过提示return 0;  // 程序正常结束返回0
}

gets_s() 返回一个指针,如果成功读取,返回的是输入字符串的指针; 如果失败(例如输入超出缓冲区的大小),返回 NULL

gets_s会限制读取的字符数,不保留换行符在字符串中;会截断,截断后会自动添加\0

bool IsPasswordOK(void) {// 定义一个长度为12的字符数组存储密码(可容纳11个字符和1个字符串结束符)char Password[12];// 使用安全版本的输入函数gets_s读取密码// 参数1:目标缓冲区;参数2:缓冲区大小// 若读取失败(如输入超长或发生错误)返回NULL,此时返回验证失败if (gets_s(Password, sizeof(Password)) == NULL) { return false; }// 将输入的密码与预设密码"goodpass"进行比较// 若相同则返回0(对应true),不同则返回非0值(对应false)return 0 == strcmp(Password, "goodpass");
}
bool IsPasswordOK(void) {char *Password = NULL;         // 初始化为NULL,由getline动态分配内存size_t len = 0;                // 初始大小为0,getline会设置合适的大小,会自动添加\0// 使用getline从标准输入读取一行,自动分配内存并设置len// 返回读取的字符数(不含终止符),出错或EOF时返回-1ssize_t nread = getline(&Password, &len, stdin);// 处理读取失败的情况(如EOF或输入错误)if (nread == -1) { free(Password);            // 释放可能已分配的内存return false;              // 返回验证失败}// 移除字符串末尾的换行符(如果存在)// strcspn返回首个'\n'的位置,将其替换为字符串终止符Password[strcspn(Password, "\n")] = '\0'; // 安全比较输入的密码与预设密码"goodpass"bool result = (0 == strcmp(Password, "goodpass"));// 释放动态分配的内存,防止内存泄漏free(Password);return result;                 // 返回比较结果
}

如果输入的行超出了初始缓冲区大小,getline() 会自动调整缓冲区的大小,避免缓冲区溢出。
1、会读入换行符
2、要记得free
3、要记得失败处理

(5)栈溢出攻击(理解记忆,无需死记)

栈溢出攻击

核心逻辑程序向栈中缓冲区写入数据时,未检查数据长度,写入内容超出缓冲区边界,覆盖栈中其他关键数据(如返回地址、栈帧基址等 ),破坏程序栈结构,进而劫持程序执行流。比如函数里用 gets 这类不安全函数读取用户输入,输入超长就可能触发。

代码注入

定义攻击者往程序内存空间(如栈、堆)注入自定义恶意代码(像 shellcode ),并想办法让程序执行这些注入的代码,从而获取权限、破坏程序等。

与栈溢出关系:栈溢出常是代码注入的 “跳板”—— 利用栈溢出覆盖返回地址,让程序跳转到注入的恶意代码区域执行。但现代系统开启 NX/DEP(数据执行保护 )后,直接在栈 / 堆执行注入代码会被阻止,推动了 ROP 等绕过技术发展。

弧注入 Arc Injection: 将控制流转移到程序中已有的代码(如 libc 库中的函数), 而不
是注入新代码shellcode 的攻击手段(漏洞利用的技术)
返回导向编程( ROP )是一种更复杂的攻击方法。
它不用注入代码、 不依赖于直接跳转到一个函数,而是通过精确控制栈上的返回地址, 依次跳转到程序中的多个 现有代码片段( gadgets,通常以 ret 指令 结尾 )逐步执行攻击

(6)防御措施(理解记忆,无需死记)

安全字符串操作(预防) 

运行时侦测 

恢复方案 

http://www.xdnf.cn/news/13214.html

相关文章:

  • 河南建筑安全员C证考试常见题及答案解析
  • 2.7 判断.lib和.a是静态库 还是动态库的导入库
  • 基于Docker部署MYSQL主从复制
  • RT_Thread——线程管理(下)
  • 数学公式中latex的粗体问题
  • vSphere环境ubuntu24.04虚拟机从BIOS切换为EFI模式启动
  • 链表反转示例代码
  • 每日算法刷题Day27 6.9:leetcode二分答案2道题,用时1h20min
  • 论文解析:一文弄懂U-Net(图像分割)!
  • WEB3全栈开发——面试专业技能点P5中间件
  • 华为智选携手IAM:突破技术边界,重塑智慧健康家居新时代
  • 苍穹外卖|学习笔记|day07
  • C#学习第29天:表达式树(Expression Trees)
  • 俩人相向而行,何时相遇问题思考。
  • 《创始人IP打造:知识变现的高效路径》
  • EXCEL 实现“点击跳转到指定 Sheet”的方法
  • 《Minio 分片上传实现(基于Spring Boot)》
  • 第三章 3.2 无线传感器网络的组成
  • 本周黄金想法
  • yaml读取写入常见错误 (‘cannot represent an object‘, 117)
  • STM32ADC模数转换器,让你的32可以测量电压!!!
  • Mobile ALOHA全身模仿学习
  • MyBatis-Plus 常用条件构造方法
  • 什么是WBS项目管理?wbs项目管理有哪些核心功能?
  • 生产管理系统开发:专业软件开发公司的实践与思考
  • linux驱动开发(3)-页面分配器
  • C++模版初阶
  • deepseek: 重装系统
  • 【C/C++】玩转正则表达式
  • SQL-labs通关(23-38)