Ptrace重置断点

时间:2016-02-20 23:41:05

标签: python-3.x ctypes ptrace

在使用Ptrace命中断点后,我无法重置进程。我本质上是在python中包装this code 我在64位Ubuntu上运行它。

我理解在该位置重置数据并递减指令指针的概念,但在我得到陷阱信号并执行此操作后,我的过程没有完成。 代码段:

# Continue to bp
res = libc.ptrace(PTRACE_CONT,pid,0,0)
libc.wait(byref(wait_status))

if _wifstopped(wait_status):
    print('Breakpoint hit. Signal: %s' % (strsignal(_wstopsig(wait_status))))
else:
    print('Error process failed to stop')
    exit(1)

# Reset Instruction pointer
data = get_registers(pid)
print_rip(data)
data.rip -= 1
res = set_registers(pid,data)

# Verify rip
print_rip(get_registers(pid))
# Reset Instruction
out = set_text(pid,c_ulonglong(addr),c_ulonglong(initial_data))

if out != 0:
    print_errno()

print_text(c_ulonglong(addr),c_ulonglong(get_text(c_void_p(addr))))

我从这段代码返回后立即运行PTRACE_DETACH。 当我运行它时,它会命中父进程成功返回的断点,但子进程不会恢复并完成其代码。 如果我注释掉对断点函数的调用,它只是将ptrace附加到进程然后分离它,程序运行正常。
程序本身只是一个小型的c程序,可以打印10次文件。

Full code is in this paste

任何人都看到我的断点代码有错误吗?

1 个答案:

答案 0 :(得分:0)

我最终编写了一个C程序,它与python代码完全相同:

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

void set_unset_bp(pid){
        int wait_status;
        struct user_regs_struct regs;
        unsigned long long addr = 0x0000000000400710;
        unsigned long long data = ptrace(PTRACE_PEEKTEXT,pid,(void *)addr,0);
        printf("Orig data: 0x%016x\n",data);
        unsigned long long trap = (data & 0xFFFFFFFFFFFFFF00) | 0xCC;
        ptrace(PTRACE_POKETEXT,pid,(void *)addr,(void *)trap);
        ptrace(PTRACE_CONT,pid,0,0);    
        wait(&wait_status);
        if(WIFSTOPPED(wait_status)){
                printf("Signal recieved: %s\n",strsignal(WSTOPSIG(wait_status)));
        }else{
                perror("wait");
        }

        ptrace(PTRACE_POKETEXT,pid,(void *)addr,(void *)data);
        ptrace(PTRACE_GETREGS,pid,0,&regs);
        regs.rip -=1;
        ptrace(PTRACE_SETREGS,pid,0,&regs);
        data = ptrace(PTRACE_PEEKTEXT,pid,(void *)addr,0);
        printf("Data after resetting bp data: 0x%016x\n",data);
        ptrace(PTRACE_CONT,pid,0,0);
}

int main(void){
        //Fork child process
        extern int errno;
        int pid = fork();
        if(pid ==0){//Child
                ptrace(PTRACE_TRACEME,0,0,0);   
                int out = execl("/home/chris/workspace/eliben-debugger/print","/home/chris/workspace/eliben-debugger/print",0);
                if(out != 0){ 
                        printf("Error Value is: %s\n", strerror(errno));
                }
        }else{ //Parent

                wait(0);
                printf("Got stop signal, we just execv'd\n");
                set_unset_bp(pid);
                printf("Finished setting and unsetting\n");    
                wait(0);
                printf("Got signal, detaching\n");
                ptrace(PTRACE_DETACH,pid,0,0);
                wait(0);
                printf("Parent exiting after waiting for child to finish\n");
        }
        exit(0);    
}  

将输出与我的Python输出进行比较后,我注意到根据python我的原始数据是0xfffffffffffe4be80x00000000fffe4be8
这让我相信我的返回数据被截断为32位值。

我将get和set方法更改为类似的东西,将返回类型设置为void指针:

def get_text(addr):
    restype = libc.ptrace.restype
    libc.ptrace.restype = c_void_p
    out = libc.ptrace(PTRACE_PEEKTEXT,pid,addr, 0)
    libc.ptrace.restype = restype
    return out

def set_text(pid,addr,data):
    return libc.ptrace(PTRACE_POKETEXT,pid,addr,data)

无法告诉你它是如何工作的,但我能够在陷阱后成功执行子进程。