C 复制堆栈的一部分并使用mmap将其映射到当前进程

C 复制堆栈的一部分并使用mmap将其映射到当前进程,c,stack,mmap,systems-programming,C,Stack,Mmap,Systems Programming,我希望我的程序执行以下操作: 打开一个新文件 将堆栈中包含当前帧指针地址的(页面对齐)部分复制到文件 将文件内容映射回与堆栈原始部分相同范围内的进程地址空间,以便进程将文件用于堆栈的该部分,而不是系统最初为堆栈分配给它的内存区域 下面是我的代码。我在调用mmap时遇到了一个分段错误,特别是mmap使用vsyscall进行系统调用时。(我在Ubuntu服务器(x86-64)下使用GCC4.4.3和Glibc2.11.1。我编译并运行了64位和32位配置,结果相同 #include <stdi

我希望我的程序执行以下操作:

  • 打开一个新文件
  • 将堆栈中包含当前帧指针地址的(页面对齐)部分复制到文件
  • 将文件内容映射回与堆栈原始部分相同范围内的进程地址空间,以便进程将文件用于堆栈的该部分,而不是系统最初为堆栈分配给它的内存区域
  • 下面是我的代码。我在调用mmap时遇到了一个分段错误,特别是mmap使用vsyscall进行系统调用时。(我在Ubuntu服务器(x86-64)下使用GCC4.4.3和Glibc2.11.1。我编译并运行了64位和32位配置,结果相同

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <stdint.h>
    #include <string.h>
    #include <sys/mman.h>
    #include <assert.h>
    #include <unistd.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    #define PAGE_SIZE 0x1000
    #define FILENAME_LENGTH 0x10
    #if defined ARCH && ARCH == 32
    #define PAGE_SIZE_COMPLEMENT 0xfffff000
    #define UINT uint32_t
    #define INT int32_t
    #define BP "ebp"
    #define SP "esp"
    #define X_FORMAT "%x"
    #else
    #define PAGE_SIZE_COMPLEMENT 0xfffffffffffff000
    #define UINT uint64_t
    #define INT int64_t
    #define BP "rbp"
    #define SP "rsp"
    #define X_FORMAT "%lx"
    #endif
    #define PAGE_ROUND_UP(v) (((v) + PAGE_SIZE - 1) & PAGE_SIZE_COMPLEMENT)
    #define PAGE_ROUND_DOWN(v) ((v) & PAGE_SIZE_COMPLEMENT)
    
    
    UINT stack_low, stack_high, stack_length;
    
    void find_stack_high(void) {
    
        UINT bp = 0;
        UINT raw_stack_high = 0;
    
        /* Set the global stack high to the best
         * approximation.
         */
    
        asm volatile ("mov %%"BP", %0" : "=m"(bp));
        while (bp) {
            raw_stack_high = bp;
            bp = *(UINT *)bp;
        }
        stack_high = PAGE_ROUND_UP(raw_stack_high);
    }
    
    
    int file_create(void) {
    
        int fd;
        char filename[FILENAME_LENGTH];
    
        strcpy(filename, "tmp.XXXXXX");
        fd = mkstemp(filename);
        if (fd == -1) {
            perror("file_create:mkstemp");
            exit(EXIT_FAILURE);
        }
    
        unlink(filename);  
        return fd;
    }
    
    
    int main(void) {
    
    
        int fd, bytes_written;
        UINT bp;
        off_t offset;
    
        printf("In main\n");
    
        fd = file_create();
        printf("fd %d\n", fd);
    
        find_stack_high();
    
        // Get the current frame pointer.
    
        asm volatile ("mov %%"BP", %0" : "=m" (bp));
    
        // Store page boundary below 
        // frame pointer as end of potentially shared stack.
    
        stack_low = PAGE_ROUND_DOWN(bp);
        stack_length = stack_high - stack_low;
    
        printf("start "X_FORMAT"   end "X_FORMAT"   length "X_FORMAT"\n",
               stack_low, stack_high, stack_length);
    
        bytes_written = 
            write(fd, (const void *)stack_low, PAGE_SIZE);
        if (bytes_written != PAGE_SIZE) {
            perror("main: write");
            fprintf(stderr, "Num bytes: %x\n", bytes_written);
            exit(EXIT_FAILURE);
        }
    
        offset = 0;
    
        if (mmap((void *)stack_low, PAGE_SIZE, PROT_READ | PROT_WRITE,
             MAP_SHARED | MAP_FIXED | MAP_GROWSDOWN, fd, offset) ==
            MAP_FAILED) {
            perror("file_copy: mmap");
            exit(EXIT_FAILURE);
        }
    
        close(fd);
    
        return EXIT_SUCCESS;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #定义页面大小0x1000
    #定义文件名长度0x10
    #如果定义了ARCH&&ARCH==32
    #定义页面大小补码0xfffff000
    #定义UINT uint32\u t
    #定义INT int32\t
    #定义BP“ebp”
    #定义SP“esp”
    #定义X_格式“%X”
    #否则
    #定义页面大小补码0xFFFFFFFFFFF000
    #定义UINT uint64\u t
    #定义INT int64\t
    #定义BP“rbp”
    #定义SP“rsp”
    #定义X_格式“%lx”
    #恩迪夫
    #定义页面大小(v)((v)+页面大小-1)和页面大小(U)补充)
    #定义页面大小(v)((v)和页面大小(U)补充)
    UINT堆栈低、堆栈高、堆栈长;
    void find_stack_high(void){
    UINT-bp=0;
    UINT原始堆栈高=0;
    /*将全局堆栈设置为最佳
    *近似值。
    */
    asm挥发性(“mov%%”BP“,%0”:“=m”(BP));
    while(bp){
    原始堆高=bp;
    bp=*(UINT*)bp;
    }
    堆叠高度=页面四舍五入(原始堆叠高度);
    }
    int文件创建(无效){
    int-fd;
    字符文件名[文件名长度];
    strcpy(文件名,“tmp.XXXXXX”);
    fd=mkstemp(文件名);
    如果(fd==-1){
    perror(“文件创建:mkstemp”);
    退出(退出失败);
    }
    取消链接(文件名);
    返回fd;
    }
    内部主(空){
    int fd,写入的字节数;
    UINT-bp;
    偏移量;
    printf(“主\n”);
    fd=文件创建();
    printf(“fd%d\n”,fd);
    查找_stack_high();
    //获取当前帧指针。
    asm挥发性(“mov%%”BP“,%0”:“=m”(BP));
    //将页面边界存储在下面
    //帧指针作为潜在共享堆栈的结尾。
    堆栈低=页面向下舍入(bp);
    堆栈长度=堆栈高-堆栈低;
    printf(“开始”X_格式“结束”X_格式“长度”X_格式”\n”,
    堆栈低、堆栈高、堆栈长);
    写入的字节数=
    写入(fd,(const void*)堆栈低,页面大小);
    如果(写入的字节数!=页面大小){
    佩罗(“主要:写作”);
    fprintf(标准,“字节数:%x\n”,写入的字节数);
    退出(退出失败);
    }
    偏移量=0;
    如果(mmap((void*)堆栈低,页面大小,保护读取,保护写入,
    地图|共享地图|固定地图|地图|增长下降、fd、偏移)==
    映射(U失败){
    perror(“文件副本:mmap”);
    退出(退出失败);
    }
    关闭(fd);
    返回退出成功;
    }
    

    谢谢!

    尝试打开执行权限?在任何情况下,症状都表明您已成功映射到堆栈顶部,破坏了返回指针。

    在复制堆栈后,堆栈会发生变化(例如
    mmap
    调用的返回地址)。我可以想出两种可能的解决方法:

  • 编写不需要堆栈来执行新映射的asm
  • 调用具有大量本地数据的函数,使工作堆栈位于与要映射的页面不同的页面上。然后,此函数返回后,您可以通过第二次调用
    mmap
    来映射较低的地址

  • 无论你做什么,这都是一个可怕的黑客攻击,可能是一个坏主意。

    他肯定需要第三次使用sys/mman.h。@b咒语:+1让我发笑对不起--没有注意到重复。是的,我想这就是问题所在。我在早期的草稿中使用了你的两个策略中的后一个,这很有效,但我一直在尝试简化我的代码--并且获得了该特定要求的原因。@Amittai Aviram:您可以使用
    makecontext()
    在使用不同堆栈的不同临时上下文中进行堆栈复制和
    mmap
    。您还可以使用具有备用信号堆栈的信号处理程序,以避免在运行代码时对堆栈造成重击的问题。