64位Linux下如何将执行流程重定向至堆上代码?
解决64位Linux下堆代码执行重定向问题
嘿,我来帮你搞定这个把执行流跳转到堆代码的问题!你的代码有几个关键问题导致编译失败或者运行出错,我来逐个拆解并修正:
原代码的核心问题
- 64位寄存器指令错误:你用了
movl(32位移动指令)操作64位的rip寄存器,这在64位编译环境下会报错,应该用movq来处理64位数据。 - 操作方向完全搞反:你的汇编指令是把
rip的值写入t指向的内存,而我们需要的是让rip跳转到t指向的堆内存,也就是要把t的地址加载到rip。 - 堆内存默认无执行权限:Linux下
malloc分配的堆内存默认是RW(可读可写)但不可执行的,直接跳转过去会触发段错误,需要用mprotect修改内存权限为RWX。 - 输入类型不匹配:用
int接收十六进制字节再赋值给char可能会有符号扩展问题,建议改用unsigned char。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> int main() { size_t len = 0; unsigned char *t; unsigned int chr; // 用unsigned int避免符号问题 printf("Input code length: "); scanf("%zu", &len); // 分配堆内存,注意要对齐到页边界(mprotect要求页对齐) t = malloc(len + getpagesize()); if (!t) { perror("malloc failed"); exit(1); } // 计算页对齐的起始地址 unsigned char *aligned_t = (unsigned char*)((unsigned long)t & ~(getpagesize() - 1)); // 修改内存权限为可读、可写、可执行 if (mprotect(aligned_t, len + getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { perror("mprotect failed"); free(t); exit(1); } printf("Input hex bytes (space-separated): "); for (size_t i = 0; i < len; ++i) { scanf("%x", &chr); t[i] = (unsigned char)chr; } // 跳转到堆上的代码:用call或者jmp指令修改rip asm volatile( "jmp *%0" // 间接跳转,把%0的值(t的地址)加载到rip : : "r"(t) : "memory" ); // 下面的代码不会被执行到 free(t); return 0; }
关键修改点说明
内存权限修改:
- 用
mprotect修改堆内存权限,它要求操作的内存区域必须是页对齐的,所以我们先分配了额外的页大小空间,再计算对齐后的起始地址。 PROT_READ | PROT_WRITE | PROT_EXEC让内存同时拥有读写执行权限。
- 用
内联汇编修正:
- 用
jmp *%0实现间接跳转,把t的地址加载到rip,这是64位下修改指令指针的正确方式。 asm volatile确保编译器不会优化掉这段汇编代码,"memory"约束告诉编译器这段代码会修改内存,避免乱序优化。
- 用
输入与内存分配优化:
- 用
size_t存储长度,匹配malloc和mprotect的参数类型。 - 用
unsigned char存储代码字节,避免符号扩展导致的错误。
- 用
编译与运行注意事项
编译时需要关闭栈保护和PIE(位置无关执行),否则可能会影响执行:
gcc -fno-stack-protector -no-pie your_code.c -o heap_exec
然后运行程序,输入代码长度和对应的十六进制机器码(比如输入2和c3,这是x86_64的返回指令),就能看到执行流跳转到堆上的代码了。
内容的提问来源于stack exchange,提问作者enedil
相关产品推荐
相关产品推荐

