Linux内存保护实战:mprotect()函数防止内存越界的5个关键技巧

Linux中mprotect()函数详解 测试源码 //mmp.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> int *g_ps32Result; void add(int a, int b) { *g_ps32Result = a + b; } void s... 阅读详情

Linux内存保护实战:mprotect()函数防止内存越界的5个关键技巧

在Linux C/C++开发中,内存越界问题就像幽灵一样难以捉摸。它可能潜伏数周甚至数月,然后在某个深夜的生产环境中突然爆发,导致程序崩溃、数据损坏,甚至引发安全漏洞。传统的调试手段,如valgrind或gdb,虽然强大,但往往是在问题发生后才介入,属于“事后诸葛亮”。有没有一种方法,能让我们在内存越界发生的瞬间就捕获它,甚至主动预防它?答案是肯定的,Linux内核提供的内存保护机制,特别是mprotect()函数,就是一把锋利的手术刀,能让我们对进程内存空间进行精细化的权限控制,从而构建起一道动态的、可编程的防线。

这篇文章不是对mprotect() API的简单复述,而是从一个实战开发者的视角,分享如何将这项底层能力转化为解决实际内存安全问题的五个关键技巧。无论你是正在处理复杂数据结构的系统程序员,还是对内存安全有极致要求的应用开发者,这些技巧都能帮助你更主动地管理内存,将潜在的运行时错误扼杀在摇篮里。

1. 理解mprotect()的核心:从内存页到信号机制

在深入技巧之前,我们必须先建立正确的认知模型。mprotect()的操作对象不是随意的字节,而是内存页。这是理解其所有行为的基础。现代操作系统(包括Linux)使用分页机制管理物理内存,通常一页的大小是4096字节(4KB)。当你调用mprotect(addr, len, prot)时,addr必须是页对齐的(即地址是4096的整数倍),len最好也是页大小的整数倍。如果len不是页大小的整数倍,内核会自动将其向上舍入到下一个页边界。这意味着,你实际保护的内存区域可能比你指定的要大。

提示:可以使用 sysconf(_SC_PAGE_SIZE) 或 getpagesize() 来动态获取系统页大小,确保代码的可移植性。

mprotect()的真正威力在于它与操作系统信号机制的联动。当你将某块内存区域设置为PROT_NONE(不可访问)或移除PROT_WRITE(写权限)后,任何违反此权限的访问尝试(如向只读内存写入数据),都会立即触发内核向进程发送一个SIGSEGV(段错误)信号。如果没有被捕获,进程会默认终止。但如果我们提前安装一个信号处理器,就能在崩溃发生的那一刻,获取到详细的错误上下文,包括违规访问的准确地址。

下面这个简单的例子展示了如何设置一个信号处理器来捕获非法写入:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/mman.h>
#include <unistd.h>

void segv_handler(int sig, siginfo_t *info, void *ucontext) {
    fprintf(stderr, "[拦截] 段错误发生在地址: %p\n", info->si_addr);
    fprintf(stderr, "[拦截] 尝试进行%s操作\n",
            (info->si_code == SEGV_ACCERR) ? "权限访问" : "映射访问");
    // 这里可以记录日志、生成堆栈回溯,然后优雅退出或恢复
    exit(EXIT_FAILURE);
}

int main() {
    struct sigaction sa;
    sa.sa_sigaction = segv_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGSEGV, &sa, NULL);

    // 后续进行内存保护和访问测试...
    return 0;
}

这种“保护-触发-捕获”的范式,是我们所有高级技巧的基石。它把内存访问错误从一种不可控的崩溃,变成了一个可观察、可处理的程序事件。

2. 技巧一:利用“防护页”隔离敏感数据缓冲区

内存越界最常见的形式就是数组或缓冲区溢出。例如,一个长度为100的数组,在索引101处进行了写入。单纯依赖程序员的小心翼翼是不可靠的。我们可以利用mprotect(),在关键缓冲区的末尾(或开头)设置一个不可访问的“防护页”,一旦溢出发生,程序会立即触发段错误,而不是悄无声息地污染相邻内存。

操作步骤:

  1. 使用mmap()或memalign()分配一块比实际需要更大的、页对齐的内存区域。例如,你需要一个N字节的缓冲区,就分配N + 2 * PAGE_SIZE。
  2. 将缓冲区起始地址调整为页对齐,并确保缓冲区完全位于分配区域的中间。
  3. 使用mprotect()将缓冲区前面和后面的各一页内存设置为PROT_NONE(不可访问)。
  4. 现在,对缓冲区的任何越界访问(无论是向前还是向后),只要跨越了防护页边界,就会立刻被捕获。
void* create_guarded_buffer(size_t data_size) {
    long page_size = sysconf(_SC_PAGE_SIZE);
    // 分配:数据区 + 前防护页 + 后防护页
    size_t total_size = data_size + 2 * page_size;
    // 使用mmap分配可读写内存
    void *area = mmap(NULL, total_size, PROT_READ | PROT_WRITE,
                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (area == MAP_FAILED) return NULL;

    // 计算数据缓冲区的起始位置(页对齐)
    void *data_buf = (char*)area + page_size;

    // 设置前防护页为不可访问
    mprotect(area, page_size, PROT_NONE);
    // 设置后防护页为不可访问
    mprotect((char*)data_buf + data_size, page_size, PROT_NONE);

    // 返回可用的数据缓冲区指针
    return data_buf;
}

// 使用完毕后,需要munmap整个区域,包括防护页
void destroy_guarded_buffer(void *data_buf, size_t data_size) {
    long page_size = sysconf(_SC_PAGE_SIZE);
    void *area_start = (char*)data_buf - page_size;
    size_t total_size = data_size + 2 * page_size;
    munmap(area_start, total_size);
}

这种方法特别适用于保护那些存储密钥、证书或其他敏感信息的缓冲区,既能防止逻辑错误导致的越界,也能增加恶意代码利用缓冲区溢出的难度。

3. 技巧二:动态切换内存权限进行“只读快照”与“写时捕获”

有些场景下,我们希望内存数据在大部分时间是只读的(比如配置表、历史数据),只有在特定、受控的代码段中才允许修改。频繁地复制数据(快照)会有性能开销。此时,可以结合mprotect()实现动态的权限切换。

应用场景:

  • 配置热重载:主线程持有只读的配置指针,后台线程在准备好新配置后,临时将内存改为可写、更新数据,再改回只读,最后原子地切换指针。这避免了在重载过程中读取到不一致的配置。
  • 数据完整性检查:将关键数据结构设置为只读。任何意外的修改企图都会触发信号。在信号处理器中,可以记录是哪个函数、在什么调用路径下试图修改,这对于调试并发数据竞争问题非常有用。

下面是一个模拟配置更新的简化示例:

typedef struct {
    int timeout;
    char server_ip[16];
} config_t;

config_t *global_config;

void update_config_safely(int new_timeout, const char* new_ip) {
    long page_size = sysconf(_SC_PAGE_SIZE);
    // 假设global_config是页对齐分配的

    // 第一步:临时赋予写权限
    if (mprotect(global_config, page_size, PROT_READ | PROT_WRITE) == -1) {
        perror("mprotect enable write failed");
        return;
    }

    // 第二步:执行更新(这是一个临界区,应确保无其他线程同时读写)
    global_config->timeout = new_timeout;
    strncpy(global_config->server_ip, new_ip, sizeof(global_config->server_ip)-1);
    global_config->server_ip[sizeof(global_config->server_ip)-1] = '\0';

    // 第三步:立即改回只读,防止后续意外修改
    if (mprotect(global_config, page_size, PROT_READ) == -1) {
        perror("mprotect disable write failed");
        // 即使失败,也应尝试恢复,此处处理略
    }
    // 可以在这里发出配置已更新的通知
}

通过这种“锁-改-锁”的内存权限模式,我们为数据增加了一层运行时保护,使得非法的写操作无处遁形。它比互斥锁更轻量级(在无竞争时),并且能捕获到所有试图写入的行为,包括那些因为逻辑错误而忘记加锁的代码。

4. 技巧三:结合backtrace在信号处理器中定位越界根源

仅仅知道错误地址是不够的,我们更需要知道是哪一行代码导致了这次非法访问。这就是backtrace系列函数的用武之地。在SIGSEGV的信号处理器中,我们可以获取当前的函数调用堆栈,并将其打印或保存到日志中。

注意:在信号处理器中调用backtrace()等函数是安全的,但很多标准库函数(如printf, malloc)是异步信号不安全的,使用它们可能导致死锁。最安全的做法是在信号处理器中只将必要信息(如地址、堆栈)保存到预先分配的或静态缓冲区中,然后在主逻辑中处理。

一个更健壮的信号处理器实现如下:

#include <execinfo.h>
#define BT_BUF_SIZE 100

static void* bt_buffer[BT_BUF_SIZE];
static char error_log[1024];
static volatile sig_atomic_t segv_occurred = 0;

void segv_handler_with_bt(int sig, siginfo_t *info, void *ucontext) {
    int nptrs;
    // 1. 保存错误地址
    uintptr_t fault_addr = (uintptr_t)(info->si_addr);
    // 2. 获取调用堆栈(在信号上下文中相对安全)
    nptrs = backtrace(bt_buffer, BT_BUF_SIZE);
    // 3. 将堆栈符号化(这个操作可能不安全,此处简化演示。生产环境应避免在此调用backtrace_symbols)
    // char **symbols = backtrace_symbols(bt_buffer, nptrs);
    segv_occurred = 1;
    // 实际项目中,应将fault_addr和bt_buffer的内容通过安全的方式(如写入管道、设置标志)传递给主线程处理
    _exit(EXIT_FAILURE); // 简单起见,直接退出
}

// 主程序初始化后
int main() {
    // ... 安装信号处理器 ...
    // ... 业务逻辑 ...

    // 主循环中可以检查 segv_occurred 标志,进行后续日志记录等
    if (segv_occurred) {
        // 在这里安全地调用 backtrace_symbols 并打印日志
        char **symbols = backtrace_symbols(bt_buffer, BT_BUF_SIZE);
        if (symbols != NULL) {
            for (int j = 0; j < BT_BUF_SIZE && bt_buffer[j] != NULL; j++) {
                fprintf(stderr, "[BT] %s\n", symbols[j]);
            }
            free(symbols);
        }
    }
    return 0;
}

为了获得更有意义的函数名和行号,需要在编译时加上-g和-rdynamic选项,并可能需要在运行时借助libdw或addr2line等工具进行离线分析。将这种堆栈捕获能力与防护页结合,你就能在发生越界的第一时间,得到一份完整的“事故现场报告”,极大缩短调试时间。

5. 技巧四:实现自定义的“释放后使用”检测器

“释放后使用”是另一类令人头疼的内存错误。对象被free()后,其对应的内存可能被重新分配,此时再访问原指针会导致数据混乱或崩溃。虽然工具如ASAN能很好检测,但有时我们需要一种轻量级的、可集成到产品中的检测机制。

我们可以利用mprotect()模拟一个简单的检测器:

  1. 自定义一个内存分配函数,分配时多申请一页,并将用户返回的指针置于该页的起始位置(页对齐)。
  2. 在释放内存时,不立即调用free()归还给系统,而是先用mprotect()将该页设置为PROT_NONE(不可访问)。
  3. 任何在释放后对该页的访问都会立即触发SIGSEGV。
  4. 可以将这些被保护起来的“已释放”页放入一个延迟回收池,定期或当池满时再真正调用munmap()释放。

这种方法的优缺点非常明显:

优点缺点
检测及时,在UAF发生时立刻崩溃并给出地址内存开销大,每个分配至少消耗一页(如4KB)
无需修改编译器或链接特殊库无法检测对“已释放”内存相邻区域的访问
原理简单,易于理解集成性能有损耗,涉及系统调用和信号处理
能捕获所有类型的访问(读、写、执行)无法像ASAN那样提供详细的错误报告(如分配/释放堆栈)

它更适合于在测试环境或对特定模块进行深度调试时使用,作为一种补充手段。

// 简化的概念验证代码
typedef struct {
    void *real_page; // mmap分配的真实页起始地址
    size_t size;
} guarded_block_t;

void* my_malloc_guarded(size_t size) {
    long page_size = sysconf(_SC_PAGE_SIZE);
    void *addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (addr == MAP_FAILED) return NULL;
    // 在实际实现中,需要将元数据(如size)也存储起来,并返回给用户一个偏移后的指针
    return addr; // 这里简单返回页首
}

void my_free_guarded(void *ptr) {
    long page_size = sysconf(_SC_PAGE_SIZE);
    // 1. 根据ptr找到元数据和真实的页起始地址(需要自定义内存布局)
    // 2. 将页保护起来,禁止所有访问
    mprotect(ptr, page_size, PROT_NONE);
    // 3. 将页加入延迟释放队列
    // ... queue_push(delay_pool, ptr);
}

6. 技巧五:性能敏感场景下的优化与注意事项

在性能至关重要的代码路径中频繁调用mprotect()和处理信号是不可取的。系统调用有上下文切换开销,信号处理也有其成本。因此,需要权衡安全性与性能。

优化策略:

  • 批量操作:不要为每个小缓冲区单独设置防护页。可以考虑将多个逻辑上独立的小缓冲区打包分配在一个大的保护区域内,共享前后的防护页。
  • 分层保护:在开发、测试和压力测试阶段开启全面的内存保护(包括防护页、只读快照等)。在稳定和生产环境中,可以只对最核心、风险最高的模块保留保护,或者仅开启写保护,关闭读/执行保护的检查。
  • 使用PROT_NONE进行预留:在程序启动时,可以用mmap(..., PROT_NONE, ...)预留一大块地址空间。当需要内存时,再用mprotect()将其改为可读写。这避免了后续mmap可能导致的地址空间碎片化,并且对预留区域的任何非法访问都会被立即捕获。
  • 避免在信号处理器中做复杂操作:如前所述,保持信号处理器的逻辑绝对简单。设置标志、保存地址、写入原子变量是安全的,调用printf、malloc或任何可能锁住非异步信号安全锁的函数是危险的。

常见陷阱:

  1. 地址对齐:这是新手最容易犯错的地方。传递给mprotect()的addr必须是页对齐的。使用memalign()、posix_memalign()或计算对齐偏移来确保这一点。

    // 错误示例
    char *buf = malloc(1024);
    mprotect(buf, 1024, PROT_READ); // buf很可能不是页对齐的,会失败并设置errno为EINVAL
    
    // 正确示例
    void *aligned_buf;
    long page_size = sysconf(_SC_PAGE_SIZE);
    if (posix_memalign(&aligned_buf, page_size, 1024) != 0) {
        // 处理错误
    }
    mprotect(aligned_buf, page_size, PROT_READ); // 假设1024小于一页,len向上取整为一页
    
  2. 权限继承与mmap:通过mmap()映射文件时,指定的prot参数必须与文件打开模式兼容。之后再用mprotect()提升权限(如从只读提升为可写)可能会失败(EACCES)。

  3. 多线程环境:mprotect()修改的是整个进程的页表,会立即影响所有线程。如果一个线程正在写某块内存,而另一个线程同时将其改为只读,结果将是未定义的。因此,对同一内存区域的权限修改需要与访问该区域的其他线程进行同步(例如,使用互斥锁)。

将这些技巧融入你的开发实践,意味着你从被动的“调试者”转向了主动的“内存架构师”。你不再仅仅依赖静态代码审查和动态分析工具,而是在运行时部署了一套自防御体系。当程序因为一个隐秘的越界错误而突然终止,并清晰地打印出违规地址和完整的调用堆栈时,你会感谢自己提前布下的这些“哨兵”。

C语言_mprotect 函数深度解析与应用实践 Linux mprotect 函数深度解析与应用实践 阅读详情

相关推荐

Linux内存保护实战:mprotect()函数防止内存越界的5个常见场景

本文详细介绍了Linux系统中mprotect()函数在内存保护中的实战应用,通过五个常见场景展示如何防止内存越界、函数指针篡改等高风险操作。每个场景均配有代码示例和配置建议,帮助开发者有效提升系统安全性和稳定性。

weixin_33737774的博客 740

Linux中mprotect()函数的用法

mprotect()函数修改在内存映像上的保护模式。 函数原型: #include    #include    int mprotect(const void *start, size_t len, int prot); mprotect把自start开始的内存区的保护模式修改为prot指定的值,如果执行成功返回0,如果执行失败,mprotect返回-1,并且设置errno变量。

Roland_Sun的专栏 5万+

RPA实战-UiPath自动化处理Outlook邮件附件全流程解析

本文详细解析了如何使用UiPath实现Outlook邮件附件的自动化处理全流程,涵盖环境配置、核心流程实现、高级功能扩展及性能优化技巧。通过RPA技术,企业可大幅提升邮件附件处理效率,减少人工操作错误,适用于财务、物流等多个场景。

weixin_33701251的博客 429

【CTF | pwn篇】mprotect()函数

本文介绍了Linux中mprotect()函数的使用方法,该函数用于修改指定内存区域的保护属性。文章详细说明了函数原型、参数选项(PROT_READ、PROT_WRITE等)及其组合使用方式,并强调了内存区间必须对齐页边界的要求。同时列出了函数可能返回的错误码及其具体原因(如EACCES、ENOMEM等),帮助开发者理解调用失败时的处理方式。最后提供了官方文档参考链接。

序章的博客 838

实验室LIMS系统选型指南:从赛默飞到金现代,5大主流方案横评

本文深入分析了实验室LIMS系统选型的5大核心维度,包括架构选择、行业适配性、合规性设计、智能化程度和成本模型。通过对比赛默飞、金现代等主流厂商的优劣势,提供实战避坑策略,帮助实验室管理者做出明智决策,提升运营效率和合规水平。

plum99的博客 259

DFT知识点扫盲——DFT scan chain

实际在项目中synccell一般直接上ULVT,既然是SDF,就是带Scan接口的DFF,SN (set negtive)是低置位,RP(reset positive)是高复位,既没有SN也没有RP的就是无复位寄存器。1:定义:满足可测试性设计(DFT),将设计中所有的触发器连接到一条或者若干条链上,称为scan chain,将一个复杂的时序电路转换为简单的组合电路进行测试,这一步是在逻辑综合(DC)中实现。当SE=1当SI=1时,电路进入scan状态,当SI=0时,电路工作在normal状态。

笨蛋大乌龟的博客 4940

mprotect

mprotect是一个在Linux操作系统中使用的函数,它允许用户修改一段指定内存区域的访问权限。

qq_42805085的博客 1080

汽车诊断服务(UDS——0x11服务解析)

ECU复位服务(0x11)详解:该服务支持硬件/钥匙开关/软件三种复位类型(0x01-0x03),复位前需发送肯定响应。请求格式为11+子功能,响应含51+子功能。典型应用需配合安全访问流程,测试时需监控CAN总线通信及DTC状态。复位后ECU将返回默认会话,期间不处理任何诊断请求。服务在编程会话且安全解锁后执行,硬复位会初始化存储数据。

从事汽车行业 979

打破边界,Linux环境下的内存越界调试技巧(转)

在 Linux 系统中,程序的运行依赖于内存的合理分配和使用。当程序发生内存越界时,就好比一个人在自己的房间里肆意妄为,不仅把自己房间搞得乱七八糟,还闯入了别人的房间捣乱。程序会访问到不属于它分配范围内的内存空间,这会导致一系列严重的后果。比如,它可能会改写其他重要数据,就像在别人房间随意涂改别人的东西,使得程序后续使用这些数据时出现错误;也可能会导致内存泄漏,就像一个人不断占用别人的房间却不归还,使得系统内存资源越来越少;更严重的是,直接导致程序崩溃,整个 “居住环境” 都被破坏。

oushaojun2的专栏 1194

radsystems的激活(破解)和使用

上个文章讲述了radsystems的下载,但是只有30天的免费使用权,这篇文章将会讲解如何破解激活radsystems。注意:如果不想去官网下载的话,点击安装包的exe文件。不要急,打开我给你们的安装包,点击crack。如果提示done则完成(我的是激活过的)注意:这是8.7版本的。点击generate。

weixin_65144487的博客 3529

乐鑫ESP32-C6产测全流程:RAM固件测试与阈值校准实战指南

Wi-Fi模组量产测试是保障IoT设备射频性能与硬件可靠性的关键环节,其核心在于基于RAM的无损固件加载、可配置的阈值判定机制及多工位协同控制。该流程依托串口协议与结构化配置(如JSON阈值文件、.bin测试固件),实现RF性能(fb_rssi、tx_power)、GPIO导通性、Flash ID识别与固件版本校验等原子化测试项的自动化执行。技术价值体现在避免Flash擦写风险、支持动态温补与在线学习校准,并无缝对接MES系统实现质量数据闭环。典型应用场景包括ESP32-C6/C3/S2等乐鑫全系Wi-Fi

weixin_35756690的博客 312

深入解析Linux中mprotect()函数的内存保护机制

本文深入解析Linux系统中mprotect()函数的内存保护机制。作为内存管理的核心系统调用,mprotect()能精确控制内存区域的读、写、执行权限,是保障程序安全与健壮性的关键。文章通过剖析其工作原理、内核实现流程(涉及VMA、页表与TLB刷新),并结合制造段错误、动态代码生成(JIT)等实战示例,详细展示了其应用场景与避坑指南。

yhn456789的博客 966

基于Simulink的超级电容快速响应特性建模与仿真

本文介绍了基于Simulink的超级电容快速响应特性建模与仿真方法。主要内容包括:超级电容的工作原理、一阶RC等效电路模型及其动态电压方程;通过Simulink搭建超级电容模型的两种方法(Simscape Electrical推荐方案和基础模块透明教学方案);仿真100A×100ms放电脉冲下的电压响应特性,并与锂电池进行动态对比;分析超级电容在混合储能系统中的功率分配优势。文章还提供了工程实践要点和扩展方向,如电压均衡、温度影响评估以及与DC-DC变换器集成等。通过建模实践,验证了超级电容在毫秒级响应、高

xiaoheshang_123的博客 98

Linux内存权限攻防:mprotect系统调用与shellcode执行实战

在计算机安全领域,内存保护机制是防止恶意代码执行的重要防线。其核心原理是通过虚拟内存管理单元(MMU)控制页表权限,实现数据与代码的隔离。现代操作系统普遍采用NX/DEP技术,将数据页标记为不可执行,从而阻断攻击者直接执行注入代码的路径。mprotect系统调用作为关键的用户层接口,允许程序动态修改指定内存区域的权限属性,在安全研究和漏洞利用中具有重要技术价值。通过将内存权限从读写提升为读写执行,mprotect为shellcode的最终执行创造了条件。这一技术广泛应用于CTF竞赛、渗透测试和漏洞分析场景,

weixin_30500473的博客 410

Python 爬虫实战:爬取哔哩哔哩知识区,抓取高赞课程信息

哔哩哔哩(Bilibili)是一个广受欢迎的综合性视频社区,其中的知识区汇聚了大量优质的教育、科普、技术等课程内容。本文将指导读者如何使用 Python 爬虫技术从哔哩哔哩知识区抓取高赞课程信息,并进行数据分析。本文深入探究了利用 Python 爬虫技术从哔哩哔哩知识区抓取高赞课程信息的全流程。从前期的环境搭建、模拟登录,到核心的课程数据抓取、存储,再到后续的数据分析与可视化,都详细展开讲解并提供代码支持。通过此方法所获取的数据,能够助力创作者优化内容、用户发现优质课程、平台洞察用户需求。

u014481728的博客 1137

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

pkey_mprotect系统调用及示例

这篇文章深入介绍了Linux系统中的pkey_mprotect系统调用,它是比传统mprotect更细粒度的内存保护机制。主要内容包括: 功能原理:通过Memory Protection Keys (MPK)机制,将内存区域与保护密钥关联,实现硬件加速的线程级访问控制。即使有mprotect权限,若线程的密钥权限掩码(PKRU)禁止,访问仍会失败。 关键特性: 需要页对齐地址 支持PROT_READ/WRITE/EXEC权限组合 可绑定0-15的保护密钥 典型应用:安全沙箱、调试器保护关键数据、高性能库的内

悟空看八戒烤熟了没的专栏c)4Upqt4SeVk 1221

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Linux内存权限攻防:mprotect系统调用在CTF漏洞利用中的实战应用

在Linux系统安全与漏洞利用领域,内存保护机制是防御攻击的关键防线。现代操作系统通过虚拟内存管理,为每个内存页设置了读(R)、写(W)、执行(X)等权限位,以此实现如NX/DEP(数据执行保护)等安全特性,防止将数据区当作代码执行。其核心原理在于限制内存区域的访问权限,从而阻断常见的代码注入攻击。从技术价值看,理解并操控这些权限是进行高级漏洞利用、绕过安全机制的基础。在CTF竞赛和渗透测试中,攻击者常需在存在漏洞但开启了NX保护的程序中执行自定义代码。此时,Linux提供的mprotect系统调用便成为关

weixin_30564901的博客 381

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

从mprotect到shellcode:Linux内存权限修改的实战技巧(以HNCTF赛题为例)

本文以HNCTF赛题为例,深入解析了Linux系统中mprotect系统调用在内存权限修改中的实战应用。通过结合PWN技术和ret2shellcode利用技巧,详细阐述了如何绕过NX等内存保护机制,实现从权限修改到代码执行的全过程,为二进制安全研究提供了实用的攻防视角。

rock5的博客 314

Linux内存保护属性,[译]如何在任意进程中修改内存保护属性

最近,我们面临着一个非常特殊的任务:在任意进程中更改内存区域的保护标志。 这项任务看似微不足道,但是我们遇到了一些障碍,并在此过程中学到了新的东西,主要是关于Linux机制和内核开发。 以下是我们工作的简要概述,包括我们采取的三种方法以及每次寻求更好解决方案的原因。引言在现代操作系统中,每个进程都有自己的虚拟地址空间(从虚拟地址到物理地址的映射)。 此虚拟地址空间由内存页(某些固定大小的连续内存块...

weixin_31998371的博客 996

虚拟内存操作

操作会设置共享内存段的一个属性,而不是进程的属性,意味着分页一旦因故障被锁进了内存,那么即使所有进程都与这个共享内存段分离了,分页还是会保持驻留在内存中的状态。与之相反的是,使用 mlock()(或 mlockall())锁进内存的区域只有在还存在进程持有该区域上的锁时才会保持被锁进内存的状态。由于加锁操作的单位是分页,因此被锁住的区域的结束位置为大于 length 加 addr 的下一个分页边界。adr地址必须是分页对齐的,并且由于返回的信息是有关整个分页的,length分页大小的下一个整数倍。

weixin_50866517的博客 1616

vc++ hook 拦截自己进程指定的api函数_深入Linux | 如何在任意进程中修改内存保护(含PoC)...

前言最近,我们遇到一个非常具体的问题:改变任意进程的内存区域的保护标志。这项任务看似微不足道,但是我们着实遇到了一些麻烦,在此过程中也学到了关于Linux机制和内核开发相关的东西。以下是一些简要概述,其中包括了当时采取的三种方案,每次也在寻求更好解决方案。概述在现代操作系统中,每个进程都有自己的虚拟地址空间(从虚拟地址映射到物理地址)。此虚拟地址空间由内存页(某些固定大小的连续内存块)组...

weixin_39640767的博客 487

Wasmtime 内存保护键(MPK)实战:用示例程序量化池化内存的压缩收益

Memory Protection Keys(MPK)是 Intel/AMD 新一代 x86 处理器与 Linux 内核提供的一种硬件级内存隔离能力,Wasmtime 借助它在池化分配器(pooling allocator)中消除了线性内存之间的 guard 页,从而在同样的虚拟内存预算下容纳更多 WebAssembly 实例。本文以仓库中的官方示例 [examples/mpk.rs](https

gitblog_00348的博客 342

tlpi: 虚拟内存操作

虚拟内存操作 1.改变内存保护 mprotect()调用修改起始地址为addr,长度为length字节的虚拟内存地址中分页的保护 #include<sys/mman.h> int mprotect(void *addr, size_t lenght, int prot); //成功:返回 0 失败:返回-1 参数解释: addr addr必须为分页大小的整数倍 length 会被舍入到系统分页大小的下一个整数倍 prot 指定了新内存区域上的保护,可以有以下取值,

永远的纸条的博客 397

pwn学习日记Day5 基础知识积累

知识杂项 int mprotect(const void *start, size_t len, int prot); mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。 int fflush(FILE *stream) fflush():会强迫将缓冲区内的数据写回参数stream 指定的文件中. 如果参数stream 为NULL,ff...

anxiong1803的博客 239

Linux系统编程——内存管理

Linux系统编程中的内存管理是一个广泛的主题,涉及到许多概念和技术。当谈到Linux系统编程中的内存管理时,一般包括动态内存分配、内存映射、共享内存等。内存保护:Linux使用页表来实现内存保护。通过设置页的权限,可以防止进程访问未授权的内存区域。使用mprotect()函数来修改内存页的权限。:在C语言中,我们通常使用malloc()和free()函数进行动态内存分配和释放。:通过内存映射,可以将文件映射到进程的地址空间中,从而实现对文件的直接访问。

m0_60697583的博客 327
上一篇: Petalinux内核开发实战:如何用devtool modify永久保存你的内核修改
下一篇: Anaconda环境下Labelme安装避坑指南:从PyQt配置到成功运行
echo99
博客等级 码龄18年 40粉丝 1171原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值