สำหรับผู้ที่สนใจ สามารถดูคำตอบที่แตกต่างออกไปได้ ที่นี่
เกิดอะไรขึ้นที่นี่?
เป็นไปได้มากว่าคุณกำลังเผชิญกับเทคนิคต่อต้านการแก้ไขข้อบกพร่องเช่นนี้:
ptrace(PT_DENY_ATTACH, 0, NULL, 0);
แนวคิดพื้นฐานคือมีเพียงกระบวนการเดียวเท่านั้นที่สามารถ ptrace
อีกกระบวนการหนึ่งในเวลาเดียวกัน โดยเฉพาะอย่างยิ่งตัวเลือก PT_DENY_ATTACH
จะทำให้แน่ใจว่า tracee ออกด้วยสถานะ ENOTSUP
(45) ดู man ptrace
เกี่ยวกับ PT_DENY_ATTACH
:
คำร้องขอนี้เป็นการดำเนินการอื่นที่ใช้โดยกระบวนการติดตาม อนุญาตให้กระบวนการที่ไม่ถูกติดตามในปัจจุบันปฏิเสธการติดตามในอนาคตโดยพาเรนต์ อาร์กิวเมนต์อื่นๆ ทั้งหมดจะถูกละเว้น หากกระบวนการกำลังถูกติดตามอยู่ กระบวนการจะออกพร้อมกับสถานะการออกของ ENOTSUP มิฉะนั้นจะตั้งค่าสถานะที่ปฏิเสธร่องรอยในอนาคต ความพยายามโดยผู้ปกครองในการติดตามกระบวนการที่ได้ตั้งค่าสถานะนี้จะส่งผลให้เกิดการละเมิดการแบ่งส่วนในผู้ปกครอง
สำหรับสิ่งที่เกี่ยวข้องกับ 45 โปรดดูที่ /System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/errno.h
:
#define ENOTSUP 45 /* Operation not supported */
จะทำซ้ำสิ่งนี้ได้อย่างไร?
การเขียนโปรแกรมที่แสดงพฤติกรรมเดียวกันนั้นเป็นเรื่องเล็กน้อย:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ptrace.h>
int main() {
printf("--- before ptrace()\n");
ptrace(PT_DENY_ATTACH, 0, NULL, 0);
perror("--- ptrace()");
printf("--- after ptrace()\n");
return 0;
}
คอมไพล์ด้วย:
clang -Wall -pedantic ptrace.c -o ptrace
เพียงเรียกใช้ก็จะออกได้สำเร็จ แต่การพยายามแก้ไขข้อบกพร่องจะให้ผลลัพธ์ดังต่อไปนี้:
(lldb) r
Process 4188 launched: './ptrace' (x86_64)
--- before ptrace()
Process 4188 exited with status = 45 (0x0000002d)
เนื่องจากตัวอย่างนี้ค่อนข้างเล็ก คุณจึงสามารถดำเนินการไปจนถึงคำสั่ง syscall
:
(lldb) disassemble
libsystem_kernel.dylib`__ptrace:
0x7fff6ea1900c <+0>: xorq %rax, %rax
0x7fff6ea1900f <+3>: leaq 0x394f12f2(%rip), %r11 ; errno
0x7fff6ea19016 <+10>: movl %eax, (%r11)
0x7fff6ea19019 <+13>: movl $0x200001a, %eax ; imm = 0x200001A
0x7fff6ea1901e <+18>: movq %rcx, %r10
-> 0x7fff6ea19021 <+21>: syscall
0x7fff6ea19023 <+23>: jae 0x7fff6ea1902d ; <+33>
0x7fff6ea19025 <+25>: movq %rax, %rdi
0x7fff6ea19028 <+28>: jmp 0x7fff6ea10791 ; cerror
0x7fff6ea1902d <+33>: retq
0x7fff6ea1902e <+34>: nop
0x7fff6ea1902f <+35>: nop
(lldb) s
Process 3170 exited with status = 45 (0x0000002d)
ดังนั้นจึงเป็นโค้ดเคอร์เนลที่ ฆ่า กระบวนการ แต่ไม่มีสัญญาณหรือ exit
syscall ที่เหมาะสม (จนถึงสิ่งนี้และมันยังคงพัดใจของฉัน)
syscall ใดที่ถูกดำเนินการจะถูกกำหนดโดยค่าของการลงทะเบียน EAX
ในกรณีนี้ 0x200001A
ซึ่งอาจดูแปลกเพราะหมายเลข ptrace
syscall เป็นเพียง 26 (0x1a
) โปรดดูที่ syscalls.master
:
26 AUE_PTRACE ALL { int ptrace(int req, pid_t pid, caddr_t addr, int data); }
หลังจากขุดมาสักพักฉันก็พบ syscall_sw.h
:
#define SYSCALL_CONSTRUCT_UNIX(syscall_number) \
((SYSCALL_CLASS_UNIX << SYSCALL_CLASS_SHIFT) | \
(SYSCALL_NUMBER_MASK & (syscall_number)))
เมื่อคำนวณผลลัพธ์คือ 0x200001A
เหตุใด dtruss
ไม่ติดตาม ptrace
syscall
การใช้ dtruss
ดูเหมือนเป็นความคิดที่ดี แต่น่าเสียดายที่มันไม่ได้รายงาน ptrace
syscall (ความเข้าใจของฉันคือ ไม่สามารถทำเช่นนั้นได้ เนื่องจาก ptrace
syscall จะไม่ส่งคืนในกรณีนี้)
โชคดีที่คุณสามารถเขียนสคริปต์ DTrace เพื่อบันทึก syscall เมื่อป้อนเข้าไปแล้ว (กล่าวคือ ไม่ใช่หลังจากที่ส่งคืนแล้ว) เพื่อทริกเกอร์พฤติกรรมนี้ โปรแกรมจะต้องเริ่มต้นจาก lldb
:
$ lldb ./ptrace
(lldb) process launch --stop-at-entry
สังเกต PID แล้ว:
sudo dtrace -q -n 'syscall:::entry /pid == $target/ { printf("syscall> %s\n", probefunc); }' -p $PID
สุดท้าย continue
ใน lldb
ผลลัพธ์ควรเป็น:
[...]
syscall> sysctl
syscall> csops
syscall> getrlimit
syscall> fstat64
syscall> ioctl
syscall> write_nocancel
syscall> ptrace
การแก้ปัญหาที่เป็นไปได้
ตอนนี้ เป็นการดีที่จะพังก่อน ptrace
syscall และค้นหาโค้ดโปรแกรมที่เรียกใช้หรือข้ามไปสำหรับเซสชันการดีบักปัจจุบัน (LLDB: thread jump -a ADDRESS
)
แน่นอนว่าใครๆ ก็พยายามหยุดการเรียกไลบรารี ptrace
ได้ แต่หากนี่เป็นจริงและมีโอกาสพยายามป้องกันการดีบักที่การเรียกจริงจะดำเนินการในบล็อก asm
ดังนั้นเบรกพอยต์ข้างต้นจะไม่ทริกเกอร์
วิธีแก้ปัญหาที่เป็นไปได้คือใช้ DTrace เพื่อวางเบรกพอยต์ก่อน syscall แต่จำเป็นต้องปิดการใช้งาน System Integrity Protection ดังนั้นฉันจึงไม่ได้ลอง
อีกทางหนึ่งสามารถพิมพ์ userland stacktrace ด้วยฟังก์ชัน ustack()
:
sudo dtrace -q -n 'syscall:::entry /pid == $target && probefunc == "ptrace"/ { ustack(); }' -p $PID
person
cYrus
schedule
11.12.2017