ошибка ptrace(PTRACE_PEEKDATA, ): дамп данных

Я хочу получить инструкции от запущенного процесса и изменить его с помощью ptrace. Когда переменная instr (содержит текущую инструкцию - PTRACE_PEEKDATA) беззнаковая, все работает, но когда я меняю ее на long int, возникает ошибка (дамп памяти). ptrace(PTRACE_PEEKDATA, ...) возвращает long int, так что это не должно быть проблемой. Я работаю на Убунту.

Где я ошибся? Я новичок в этом, так что это, вероятно, будет что-то глупое.

Мой код:

#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/types.h>
#include <stdlib.h>
#include <wait.h>

int main()
{
    int status; 
    char *pid_char;
    pid_t PID;
    struct user_regs_struct  reg; /* register */
    long int instr;
    unsigned changedInstr;


    printf("Tracee PID: ");
    scanf("%s", pid_char);
    PID = atoi(pid_char);
    printf("\n");

    /* PTRACE STARTS */
    ptrace(PTRACE_ATTACH, PID, NULL, NULL);
    waitpid(PID, &status, 0); 

    ptrace(PTRACE_GETREGS, PID, NULL, &reg);

    instr = ptrace(PTRACE_PEEKDATA, PID, reg.rip, NULL);
    printf("Current Instruction: %llx\n", instr);

    scanf("%u", &changedInstr);
    ptrace(PTRACE_POKEDATA, PID, reg.rip, &changedInstr);

    ptrace(PTRACE_DETACH, PID, NULL, NULL);

    return 0;
}

person daniel098    schedule 08.11.2018    source источник
comment
On error, all requests return -1, and errno is set appropriately. Вы проверили ошибку? А ты сменил instr с unsigned long? Это единственное изменение? Как я могу воспроизвести его с помощью какой-либо программы?   -  person KamilCuk    schedule 09.11.2018


Ответы (1)


В x86_64 PTRACE_PEEKDATA возвращает 8 байтов, а PTRACE_POKEDATA передает 8 байтов, начиная с адреса, на который указывает его аргумент addr. Достаточно использовать long или unsigned long.

Если вы присоединяетесь к процессу, находящемуся в системном вызове nanosleep, поток инструкций выглядит следующим образом:

(gdb) disass /r
Dump of assembler code for function __nanosleep_nocancel:
   0x00007ffff7ad92e9 <+0>: b8 23 00 00 00  mov    $0x23,%eax
   0x00007ffff7ad92ee <+5>: 0f 05   syscall 
=> 0x00007ffff7ad92f0 <+7>: 48 3d 01 f0 ff ff   cmp    $0xfffffffffffff001,%rax
   0x00007ffff7ad92f6 <+13>:    73 31   jae    0x7ffff7ad9329 <nanosleep+73>
   0x00007ffff7ad92f8 <+15>:    c3  retq  

После выполнения instr = ptrace(PTRACE_PEEKDATA, PID, reg.rip, NULL); instr будет 3173fffff0013d48, если оно длинное без знака, или f0013d48, если оно беззнаковое.

В вашей программе changedInstr не имеет знака, а ptrace(PTRACE_POKEDATA, PID, reg.rip, &changedInstr); будет передавать 4 байта changedInstr, за которыми следуют 4 байта того, что находится рядом с ним в стеке, возможно, это часть какой-то другой локальной переменной. Эти 4 байта могут быть чем-то безобидным или чем-то, что заставит целевой процесс получить исключение, как вы видели.

Что должно работать в этом случае, если вы хотите написать 4-байтовую инструкцию по адресу reg.rip, это

unsigned changedInstr;
...
instr = ptrace(PTRACE_PEEKDATA, PID, reg.rip, NULL);
scanf("%u", &changedInstr);
instr = (instr & ~0xFFFFFFFFul) | changedInstr
ptrace(PTRACE_POKEDATA, PID, reg.rip, &instr);
person Mark Plotnick    schedule 12.11.2018