我正在调试一个应用程序,可能有反调试措施,设置断点和停止退出应用程序的信号停止应用程序退出,
$ lldb App
(lldb) target create "App"
error: Invalid fde/cie next entry offset of 0x43029a18 found in cie/fde at 0x1404
Current executable set to 'App' (x86_64).
(lldb) br s -n exit
Breakpoint 1: 3 locations.
(lldb) br s -n _exit
Breakpoint 2: where = libsystem_kernel.dylib`__exit, address = 0x00000000000167a8
(lldb) br s -n _Exit
Breakpoint 3: where = libsystem_c.dylib`_Exit, address = 0x000000000005ed8b
(lldb) process launch -stop-at-entry
Process 17849 stopped
* thread #1: tid = 0xb9ebc, 0x00007fff5fc01000 dyld`_dyld_start, stop reason = signal SIGSTOP
frame #0: 0x00007fff5fc01000 dyld`_dyld_start
dyld`_dyld_start:
-> 0x7fff5fc01000 <+0>: popq %rdi
0x7fff5fc01001 <+1>: pushq $0x0
0x7fff5fc01003 <+3>: movq %rsp, %rbp
0x7fff5fc01006 <+6>: andq $-0x10, %rsp
Process 17849 launched: '/Users/admin/Downloads/App.app/Contents/MacOS/App' (x86_64)
(lldb) process handle -p false -s true
Do you really want to update all the signals?: [y/N] y
NAME PASS STOP NOTIFY
=========== ===== ===== ======
SIGHUP false true true
... [removed for brevity]
(lldb) c
Process 17849 resuming
Process 17849 exited with status = 45 (0x0000002d)
(lldb)
Run Code Online (Sandbox Code Playgroud)
应用程序如何在不触发任何信号,退出,_exit或_Exit的情况下退出?
在lldb中有没有办法运行这个过程,然后在退出时"回溯"以查看它退出的位置?
有没有办法让lldb记录每个汇编指令等(比如它何时中断)所以你可以在退出时追溯它?
对于那些感兴趣的人,可以在这里找到对这个答案的不同看法.
很可能你正在处理像这样的反调试技术:
ptrace(PT_DENY_ATTACH, 0, NULL, 0);
Run Code Online (Sandbox Code Playgroud)
基本思想是只有一个进程可以同时进行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 */
Run Code Online (Sandbox Code Playgroud)
编写一个表现出相同行为的程序是微不足道的:
#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;
}
Run Code Online (Sandbox Code Playgroud)
编译:
clang -Wall -pedantic ptrace.c -o ptrace
Run Code Online (Sandbox Code Playgroud)
简单地运行它将成功退出,但尝试调试它将产生以下结果:
(lldb) r
Process 4188 launched: './ptrace' (x86_64)
--- before ptrace()
Process 4188 exited with status = 45 (0x0000002d)
Run Code Online (Sandbox Code Playgroud)
由于此示例非常小,因此可以直到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)
Run Code Online (Sandbox Code Playgroud)
因此,内核代码会终止进程,但没有信号或正确的exit
系统调用.(TIL这个,它仍然让我感到震惊.)
执行哪个系统调用由EAX
寄存器的值决定,在这种情况下0x200001A
它可能看起来很奇怪,因为ptrace
系统调用号只有26(0x1a
),请参阅syscalls.master
:
26 AUE_PTRACE ALL { int ptrace(int req, pid_t pid, caddr_t addr, int data); }
Run Code Online (Sandbox Code Playgroud)
经过一番挖掘后,我想出了syscall_sw.h
:
#define SYSCALL_CONSTRUCT_UNIX(syscall_number) \
((SYSCALL_CLASS_UNIX << SYSCALL_CLASS_SHIFT) | \
(SYSCALL_NUMBER_MASK & (syscall_number)))
Run Code Online (Sandbox Code Playgroud)
算结果是 0x200001A
dtruss
跟踪ptrace
系统调用?使用dtruss
似乎是一个好主意,不幸的是它没有报告ptrace
系统调用(我的理解是它没有这样做,因为ptrace
在这种情况下系统调用不返回).
幸运的是,一旦输入系统调用(即,不返回后),您可以编写一个DTrace脚本来记录系统调用.要触发该行为,必须从lldb
以下位置启动该程序:
$ lldb ./ptrace
(lldb) process launch --stop-at-entry
Run Code Online (Sandbox Code Playgroud)
注意PID然后:
sudo dtrace -q -n 'syscall:::entry /pid == $target/ { printf("syscall> %s\n", probefunc); }' -p $PID
Run Code Online (Sandbox Code Playgroud)
最后continue
的lldb
,结果应该是:
[...]
syscall> sysctl
syscall> csops
syscall> getrlimit
syscall> fstat64
syscall> ioctl
syscall> write_nocancel
syscall> ptrace
Run Code Online (Sandbox Code Playgroud)
现在最好在ptrace
系统调用之前中断并找到调用它的程序代码,或者只是跳过当前调试会话(LLDB :) thread jump -a ADDRESS
.
当然有人可能试图打破ptrace
库调用,但如果这是真的并且反调试尝试的可能性是实际调用是在asm
块中执行的,那么上面的断点永远不会触发.
一个可能的解决方案可能是使用DTrace在系统调用之前放置一个断点,但这需要禁用系统完整性保护,所以我没有尝试.
或者,可以使用以下ustack()
函数打印userland堆栈跟踪:
sudo dtrace -q -n 'syscall:::entry /pid == $target && probefunc == "ptrace"/ { ustack(); }' -p $PID
Run Code Online (Sandbox Code Playgroud)