【问题标题】:Remote mmap syscall using ptrace (Linux, C)使用 ptrace 的远程 mmap 系统调用(Linux,C)
【发布时间】:2025-02-26 19:10:02
【问题描述】:

我已经被这个问题困扰了几天,但仍然没有设法解决它。基本上,我想从攻击者程序到目标进行远程系统调用。但在展示代码之前,我认为展示我的思考过程是个好主意,因为此时问题可能是任何问题。 我正在通过以下步骤执行此远程系统调用:

  1. 解析 /proc//maps 文件以获取可执行区域。
  2. 将数据存储在可执行区域并编写一个自定义缓冲区,对其进行系统调用。
  3. 存储旧寄存器并设置新寄存器以进行系统调用
  4. 写入新寄存器并继续执行
  5. 在系统调用之后,目标程序将中断,这将允许我获取 mmap 的输出,设置旧寄存器,从而恢复旧的执行流程。

我正在使用我的内存库来解析 mmap 文件、获取进程 ID 和进程信息等。就我而言,它工作正常。无论如何,这里是来源:https://github.com/rdbo/libmem

以及我用来调用的代码:

mem_voidptr_t allocate_ex(mem_process_t process, mem_size_t size, mem_alloc_t allocation)
{
    mem_voidptr_t alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
    if(!mem_process_is_valid(&process)) return alloc_addr;
    int status;
    int mmap_syscall = __NR_mmap;
    struct user_regs_struct old_regs, regs;
    mem_byte_t injection_buf[] = 
    {
        0x0f, 0x05, //syscall
        0xcc        //int3
    };


    //Parse /proc/<process.pid>/maps to get executable region

    char path_buffer[64];
    snprintf(path_buffer, sizeof(path_buffer), "/proc/%i/maps", process.pid);
    int fd = open(path_buffer, O_RDONLY);
    if(fd == -1) return alloc_addr;

    int read_check = 0;
    mem_size_t file_size = 0;
    mem_string_t file_buffer = mem_string_init();

    for(char c; (read_check = read(fd, &c, 1)) != -1 && read_check != 0; file_size++)
    {
        mem_string_resize(&file_buffer, file_size);
        mem_string_c_set(&file_buffer,  file_size, c);
    }

    mem_size_t   injection_address_pos, injection_address_end;
    mem_string_t injection_address_str = mem_string_init();
    mem_voidptr_t injection_address = (mem_voidptr_t)MEM_BAD_RETURN;

    injection_address_pos = mem_string_find(&file_buffer, "r-xp", 0);
    injection_address_pos = mem_string_rfind(&file_buffer, "\n", injection_address_pos);
    if(injection_address_pos == file_buffer.npos) return alloc_addr;

    injection_address_end = mem_string_find(&file_buffer, "-", injection_address_pos);
    injection_address_str = mem_string_substr(&file_buffer, injection_address_pos, injection_address_end);
    injection_address = (mem_voidptr_t)strtoull(mem_string_c_str(&injection_address_str), NULL, 16);
    if(injection_address == (mem_voidptr_t)MEM_BAD_RETURN || injection_address == (mem_voidptr_t)0)
        return alloc_addr;

    printf("Injection address: %p\n", injection_address);

    //Store the old data at 'injection_address' and write the injection buffer to it

    mem_byte_t old_data[sizeof(injection_buf)];
    mem_ex_read(process, injection_address, (mem_voidptr_t)old_data, sizeof(old_data));
    mem_ex_write(process, injection_address, (mem_voidptr_t)injection_buf, sizeof(injection_buf));

    //Attach to process and store current registers

    ptrace(PTRACE_ATTACH, process.pid, NULL, NULL);
    ptrace(PTRACE_GETREGS, process.pid, NULL, &old_regs);
    memcpy(&regs, &old_regs, sizeof(regs));

    //Setup syscall registers

    regs.rax = mmap_syscall;          //syscall number
    regs.rdi = 0;                     //address        (arg0)
    regs.rsi = size;                  //length         (arg1)
    regs.rdx = allocation.protection; //protection     (arg2)
    regs.r10 = allocation.type;       //flags          (arg3)
    regs.r8  = -1;                    //fd             (arg4)
    regs.r9  = 0;                     //offset         (arg5)

    regs.rip = (unsigned long long)injection_address; //next instruction to execute

    //Call mmap on external process

    ptrace(PTRACE_SETREGS, process.pid, NULL, &regs);
    ptrace(PTRACE_CONT, process.pid, NULL, NULL);
    waitpid(process.pid, &status, WSTOPPED);

    //Get the registers after syscall to store the return of mmap

    ptrace(PTRACE_GETREGS, process.pid, NULL, &regs);
    alloc_addr = (mem_voidptr_t)regs.rax; //store the return of mmap

    //Restore the original buffer at 'injection_address'

    mem_ex_write(process, injection_address, (mem_voidptr_t)old_data, sizeof(old_data));

    //Continue the original execution

    ptrace(PTRACE_SETREGS, process.pid, NULL, &old_regs);
    ptrace(PTRACE_CONT, process.pid, NULL, NULL);

    //Return allocation address, if valid
    if((mem_uintptr_t)alloc_addr >= (mem_uintptr_t)-2048)
        alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
    return alloc_addr;
}

以及攻击者程序的主要功能:

int main()
{
    mem_pid_t pid = mem_ex_get_pid(mem_string_new("target"));
    mem_process_t process = mem_ex_get_process(pid);

    int buffer = 10;
    mem_alloc_t allocation = mem_alloc_init();
    allocation.protection = PROT_READ | PROT_WRITE;
    allocation.type       = MAP_ANON  | MAP_PRIVATE;
    mem_voidptr_t alloc_addr = allocate_ex(process, sizeof(buffer), allocation);
    printf("Allocation Address: %p\n", alloc_addr);
    if(alloc_addr == (mem_voidptr_t)MEM_BAD_RETURN)
    {
        printf("Invalid allocation\n");
        return -1;
    }

    //Check if worked by reading/writing to that buffer
    int read_buffer = 0;
    mem_ex_write(process, alloc_addr, &buffer, sizeof(buffer));
    mem_ex_read(process, alloc_addr, &read_buffer, sizeof(read_buffer));
    printf("Read buffer: %i\n", read_buffer);
    if(read_buffer == buffer)
        printf("Success!\n");

    return 0;   
}

目标程序:

int main()
{
    printf("Waiting for injection\n");
    while(1);
}

攻击者程序的输出是:

Injection address: 0x55f6e104a000
Allocation Address: (nil)
Read buffer: 0

并且在目标程序上引发了分段错误。可执行区域有效(我手动检查)并且该过程也有效。 另外,我在调试目标程序时遇到了一些麻烦,显然 GDB 不会让 ptrace 从攻击者程序中完成它的工作。运行 Arch Linux。这两个程序都是用 clang (x64) 编译的。有什么想法吗?

【问题讨论】:

    标签: c linux memory system-calls ptrace


    【解决方案1】:

    原来问题是我正在使用 process_vm_read 和 process_vm_write 读取/写入内存。我通过将读/写方法更改为 ptrace PEEK/POKE 数据来使其工作。固定代码(包含在我的内存库中):

    mem_voidptr_t injection_address;
        struct user_regs_struct old_regs, regs;
        int status;
    
        const mem_byte_t injection_buffer[] = 
        {
            0x0f, 0x05, //syscall
            0xcc        //int3 (SIGTRAP)
        };
    
        mem_byte_t old_data[sizeof(injection_buffer)];
    
        //Find injection address
    
        char path_buffer[64];
        snprintf(path_buffer, sizeof(path_buffer), "/proc/%i/maps", process.pid);
        int fd = open(path_buffer, O_RDONLY);
        if(fd == -1) return alloc_addr;
    
        int read_check = 0;
        mem_size_t file_size = 0;
        mem_string_t file_buffer = mem_string_init();
    
        for(char c; (read_check = read(fd, &c, 1)) != -1 && read_check != 0; file_size++)
        {
            mem_string_resize(&file_buffer, file_size);
            mem_string_c_set(&file_buffer,  file_size, c);
        }
    
        mem_size_t   injection_address_pos, injection_address_end;
        mem_string_t injection_address_str = mem_string_init();
        injection_address = (mem_voidptr_t)MEM_BAD_RETURN;
    
        injection_address_pos = mem_string_find(&file_buffer, "r-xp", 0);
        injection_address_pos = mem_string_rfind(&file_buffer, "\n", injection_address_pos);
        if(injection_address_pos == file_buffer.npos) return alloc_addr;
    
        injection_address_end = mem_string_find(&file_buffer, "-", injection_address_pos);
        injection_address_str = mem_string_substr(&file_buffer, injection_address_pos, injection_address_end);
        injection_address = (mem_voidptr_t)strtoull(mem_string_c_str(&injection_address_str), NULL, 16);
        if(injection_address == (mem_voidptr_t)MEM_BAD_RETURN || injection_address == (mem_voidptr_t)0) return alloc_addr;
    
        //Inject
        ptrace(PTRACE_ATTACH, process.pid, NULL, NULL);
    
        //Store data at injection_address
        for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
            ((mem_byte_t*)old_data)[i] = (mem_byte_t)ptrace(PTRACE_PEEKDATA, process.pid, injection_address + i, NULL);
    
        //Write injection buffer to injection address
        for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
            ptrace(PTRACE_POKEDATA, process.pid, injection_address + i, ((mem_byte_t*)injection_buffer)[i]);
    
        ptrace(PTRACE_GETREGS, process.pid, NULL, &old_regs);
        regs = old_regs;
    
        regs.rax = __NR_mmap;                        //syscall number
        regs.rdi = (mem_uintptr_t)0;                 //arg0 (void* address)
        regs.rsi = (mem_uintptr_t)size;              //arg1 (size_t size)
        regs.rdx = (mem_uintptr_t)protection;        //arg2 (int protection)
        regs.r10 = MAP_PRIVATE | MAP_ANON;           //arg3 (int flags)
        regs.r8  = -1;                               //arg4 (int fd)
        regs.r9  = 0;                                //arg5 (off_t offset)
        regs.rip = (mem_uintptr_t)injection_address; //next instruction
    
        ptrace(PTRACE_SETREGS, process.pid, NULL, &regs);
        ptrace(PTRACE_CONT, process.pid, NULL, NULL);
        waitpid(process.pid, &status, WSTOPPED);
        ptrace(PTRACE_GETREGS, process.pid, NULL, &regs);
        alloc_addr = (mem_voidptr_t)regs.rax;
    
        //Restore old execution
        ptrace(PTRACE_SETREGS, process.pid, NULL, &old_regs);
    
        for(mem_size_t i = 0; i < sizeof(injection_buffer); i++)
            ptrace(PTRACE_POKEDATA, process.pid, injection_address + i, ((mem_byte_t*)old_data)[i]);
    
        //ptrace(PTRACE_CONT, process.pid, NULL, NULL);
        ptrace(PTRACE_DETACH, process.pid, NULL, NULL);
    
        if(alloc_addr == (mem_voidptr_t)__NR_mmap)
            alloc_addr = (mem_voidptr_t)MEM_BAD_RETURN;
    
        return alloc_addr;
    

    【讨论】:

      最近更新 更多