使用 ptrace 的远程 mmap 系统调用 (Linux, C)

Remote mmap syscall using ptrace (Linux, C)

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

  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 从攻击者程序中完成它的工作。 运行拱Linux。这两个程序都是用 clang (x64) 编译的。有什么想法吗?

原来问题是我reading/writing内存使用了process_vm_read和process_vm_write。我通过将 read/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;