lldb找到app的退出点

时间:2016-03-26 15:47:54

标签: c assembly lldb

我正在调试一个可能具有反调试措施的应用程序,设置断点和停止退出应用程序的信号并不会停止应用退出,

$ 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)

应用程序如何在不触发任何信号,退出,_exit或_Exit的情况下退出?

lldb中是否有办法运行该过程,退出后然后回溯&#39;看看它退出的地方?

lldb是否有办法记录每个汇编指令等(例如它何时中断),以便在退出时可以追溯它?

1 个答案:

答案 0 :(得分:6)

对于有兴趣的人,可以找到对此答案的不同看法here

这里发生了什么?

你最有可能正在处理像这样的反调试技术:

ptrace(PT_DENY_ATTACH, 0, NULL, 0);

基本思路是,只有一个进程可以同时ptrace另一个进程,特别是PT_DENY_ATTACH选项可确保 tracee 以{{1}退出(45)状态。关于ENOTSUP

,请参阅man ptrace
  

此请求是跟踪进程使用的其他操作;它允许当前未跟踪的进程拒绝其父级的未来跟踪。所有其他参数都被忽略。如果当前正在跟踪该进程,它将以ENOTSUP的退出状态退出;否则,它会设置一个拒绝未来痕迹的标志。父级尝试跟踪已设置此标志的进程将导致父级中的分段违规。

关于45的问题,请查看PT_DENY_ATTACH

/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) 系统调用。 (TIL这个,它仍然让我大吃一惊。)

执行哪个系统调用由exit寄存器的值决定,在本例中为EAX,它可能看起来很奇怪,因为0x200001A系统调用号仅为26({{1} }),请参阅syscalls.master

ptrace

经过一番挖掘后,我想出了syscall_sw.h

0x1a

数学结果为26 AUE_PTRACE ALL { int ptrace(int req, pid_t pid, caddr_t addr, int data); }

为什么#define SYSCALL_CONSTRUCT_UNIX(syscall_number) \ ((SYSCALL_CLASS_UNIX << SYSCALL_CLASS_SHIFT) | \ (SYSCALL_NUMBER_MASK & (syscall_number))) 不跟踪0x200001A系统调用?

使用dtruss似乎是一个好主意,不幸的是它没有报告ptrace系统调用(我的理解是它没有这样做,因为dtruss系统调用没有返回情况)。

幸运的是,您可以编写一个DTrace脚本,以便在输入后记录系统调用(即,在返回后不会)。要触发该行为,必须从ptrace

启动该程序
ptrace

注意PID:

lldb

$ lldb ./ptrace (lldb) process launch --stop-at-entry 中的最后sudo dtrace -q -n 'syscall:::entry /pid == $target/ { printf("syscall> %s\n", probefunc); }' -p $PID ,结果应为:

continue

可能的解决方案

现在最好在lldb系统调用之前中断,找到调用它的程序代码,或者只是跳过当前调试会话(LLDB:[...] syscall> sysctl syscall> csops syscall> getrlimit syscall> fstat64 syscall> ioctl syscall> write_nocancel syscall> ptrace )。

当然有人可能试图打破ptrace库调用,但如果这是真的并且反调试尝试的可能性是实际调用是在thread jump -a ADDRESS块中执行的,那么上面断点永远不会触发。

一种可能的解决方案可能是使用DTrace在系统调用之前放置一个断点,但这需要禁用系统完整性保护,所以我没有尝试。

或者,可以使用ptrace函数打印userland堆栈跟踪:

asm