简体   繁体   English

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

[英]Remote mmap syscall using ptrace (Linux, C)

I've been stuck with this problem for some days, and still haven't manage to fix it.我已经被这个问题困扰了几天,仍然没有设法解决它。 Basically, I want to do a remote syscall from an attacker program to the target.基本上,我想从攻击者程序到目标进行远程系统调用。 But before showing the code, I think it'd be a good idea to present my thought process, as the problem could be anything at this point.但在展示代码之前,我认为展示我的思考过程是个好主意,因为此时问题可能是任何问题。 I am doing this remote syscall through the following steps:我正在通过以下步骤执行此远程系统调用:

  1. Parse /proc/<process_id>/maps file to get an executable region.解析 /proc/<process_id>/maps 文件以获取可执行区域。
  2. Store the data at the executable region and write a custom buffer that does the syscall to it.将数据存储在可执行区域并写入对其执行系统调用的自定义缓冲区。
  3. Store the old registers and setup new ones to make the syscall存储旧寄存器并设置新寄存器以进行系统调用
  4. Write the new registers and continue the execution写入新寄存器并继续执行
  5. After the syscall, the target program will break, which would allow me to get the output of mmap, set back the old registers and therefore, restore the old execution flow.在系统调用之后,目标程序将中断,这将允许我获取 mmap 的 output,设置旧寄存器,从而恢复旧的执行流程。

I am using my memory lib to parse the mmap files, get process id and process information, etc. As far as I am concerned, it is working properly.我正在使用我的 memory 库来解析 mmap 文件,获取进程 ID 和进程信息等。就我而言,它工作正常。 In any case, here's the source: https://github.com/rdbo/libmem无论如何,这里的来源: https://github.com/rdbo/libmem

And the code I am using to do the call:我用来进行通话的代码:

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;
}

and the main function of the attacker program:以及攻击者程序的主要function:

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;   
}

The target program:目标程序:

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

The output of the attacker program is:攻击者程序的output为:

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

and a Segmentation Fault is raised on the target program.并且在目标程序上引发了分段错误。 The executable region is valid (I manually checked) and the process is valid too.可执行区域有效(我手动检查)并且该过程也有效。 Also, I am having some trouble debugging the target program, apparently GDB wouldn't let ptrace do its job from the attacker program.另外,我在调试目标程序时遇到了一些麻烦,显然 GDB 不会让 ptrace 从攻击者程序中完成它的工作。 Running Arch Linux.运行拱 Linux。 Both programs are compiled with clang (x64).这两个程序都是用 clang (x64) 编译的。 Any ideas?有任何想法吗?

Turns out the problem was that I was reading/writing the memory using process_vm_read and process_vm_write.原来问题是我正在使用 process_vm_read 和 process_vm_write 读/写 memory。 I got it to work by changing the read/write method to ptrace PEEK/POKE data.我通过将读/写方法更改为 ptrace PEEK/POKE 数据来使其工作。 Fixed code (included on my memory lib):固定代码(包含在我的 memory 库中):

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM