【问题标题】:SIGSEGV, after mmapping areaSIGSEGV,映射区域后
【发布时间】:2016-04-15 06:30:21
【问题描述】:

我正在尝试在 SIGQUIT 之后从核心转储中恢复进程。 我真的很想要那块虚拟内存,但是当我尝试映射它时却得到了 SIGSEGV。

编辑:这个区域不是免费的:0xf75d2000 - 0xf7774000,但我仍然想拥有它。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ucontext.h>
#include <elf.h>
#include <sys/procfs.h>
#include <sys/user.h>
#include <linux/unistd.h>
#include <linux/unistd.h>
#include <asm/ldt.h>
#include  <signal.h>
bool flag = false;
int argc2;
char ** argv2;
int main2(){
    FILE * file = fopen("/proc/self/maps", "r");
    if (file) {
        char c;
        while ((c = getc(file)) != EOF)
        putchar(c);
        fclose(file);
    }
    fflush(stdout);
    void* res = mmap((void*)(0xf75d2000), 0x001a5000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
    return 0;
}
int main(int argc, char ** argv){
    argc2 = argc;
    argv2 = argv;
    ucontext_t cont;
    getcontext (&cont);
    if(!flag){
      void* a = mmap((void*)0x34B000, 81920, PROT_EXEC | PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
      if(a == MAP_FAILED){
        printf("mmapfail");
        return 1;
      }
      cont.uc_mcontext.gregs[REG_ESP] = 0x355000;
      flag = true;
      setcontext(&cont);
    } else{
      exit(main2());
    }
}

我正在编译它:

gcc -static -Wl,-Ttext=0x4A9480,--build-idone,-Tdata=0x639480,--section-start=.plt=0x3B9480,--section-start=.rel.plt=0x3AF480,--section-start=.note.ABI-tag=0x39B480 main.c -o main -m32

【问题讨论】:

    标签: c linux memory mmap


    【解决方案1】:

    您尝试映射的地址 (0xf75d2000) 高于虚拟内存中的用户空间/内核拆分。如果你的内核配置了CONFIG_VMSPLIT_3G,你就不能映射0xc0000000以上的任意地址。

    在内核中设置现有映射以公开 vDSO 空间(以协助系统调用)。

    【讨论】:

    • 如果我有纯 x86,那将是真的,但我在 x64 上运行它,不过还是不错的尝试
    • 如果你用-m32编译,会不会创建一个32位虚拟地址空间的32位可执行文件?
    • 它只是在 x64 中模拟的 x86,我可以全部映射
    • 您知道如何将 vdso 和 vvar 部件从那里移开吗?
    【解决方案2】:

    当然你会得到一个 SEGV。您将带有MAP_FIXED 的东西映射到某个不属于您的地址,然后从脚下拉出堆栈。你不能这样做。

    地址空间不是你可以乱来的。MAP_FIXED 仅对覆盖早期映射是安全的。您可以在一个实验中使用它,之后您将丢弃该程序,但任何其他用途都行不通。

    现在您对setcontext 的调用将崩溃,因为它不知道返回何处。你甚至知道函数调用和堆栈是如何交互的吗?您对 setcontext 的调用将返回地址保存在堆栈上,然后 setcontext 更改堆栈指针,然后它尝试返回并死掉,因为它读取 0 作为返回地址(或者 setcontext 可能会将旧堆栈指针保存在其他一些寄存器中并恢复它从该寄存器返回之前,崩溃的是您的另一个覆盖真实堆栈的mmap)。请不要这样做。在不成为操作系统的情况下可靠地更改堆栈的唯一机会是使用sigaltstack 设置信号处理程序,捕获该信号并且永远不会从信号处理程序返回。

    但由于您使用 MAP_FIXED 将新堆栈的内存映射到某个随机地址,您可能会覆盖其他一些重要的数据结构,但它仍然无法工作。

    【讨论】:

    • 这显然是错误的。该程序不会崩溃,您可以例如用 printf 替换 mmap 就可以了。您可以在函数中打印 esp,您会看到它确实更改了堆栈指针。
    【解决方案3】:

    地址空间需要在其他区域被声明之前被声明,因此它需要在可执行文件的元数据中声明。

    用汇编语言创建一个节,然后在链接器的命令行参数中指定它的地址。

    例如:

    #include <stdio.h>
    
    extern char mem[];
    
    asm (R"(
      .section fixed, "aw", @nobits
      .global mem
    mem:
      .zero 0x20000000
    )");
    
    int main() {
      printf("mem = %p\n", mem);
    }
    

    编译和链接:

    gcc -O2 -Wl,--section-start=fixed=0x40000000 -fno-pie -no-pie test.c
    

    不幸的是,在变量定义 GCC 上使用 GCC 的 __attribute__((Section("fixed"))) 会导致可执行文件中出现零。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-06
      • 2012-02-20
      • 2018-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多