PtraceのiOSでのデバッグの利用と解読

6060 ワード

デバッガはいったいどのように動作しますか?プロセスattach(マウント)をappにブロックする方法と、これらの保護(いわゆる逆デバッグと逆デバッグ)をどのように解読しますか?

システムコールについて


ptraceはシステム呼び出しです.システム呼び出しは何ですか?システムが提供する強力な最下位サービスです.ユーザ層のフレームワークはsystem call上に構築されている.macOS Sierraは約500個のシステム呼び出しを提供している.次のコマンドで、システム上のシステム呼び出しの数を理解します.➜ ~ sudo dtrace -ln 'syscall:::entry' | wc -lこのコマンドは、DTraceというより強力なツールを使用しています.

デバッグの基本--ptrace


コマンドラインで実行:(SIP(system integrity protection)➜ ~ sudo dtrace -qn 'syscall::ptrace:entry { printf("%s(%d, %d, %d, %d) from %s
", probefunc, arg0, arg1, arg2, arg3, execname); }'
を閉じる必要があることに注意)このコマンドは、ptrace関数が実行されるたびに実行されるDTrace を作成します.コマンドラインの別のTabで実行されます:➜ ~ lldb -n Finderこの時点でdtraceのtabは出力します:ptrace(14, 283, 0, 0) from debugserverこれはdebugserverという名前のプロセスがptraceを呼び出し、Finderプロセスにattachしたように見えます.しかしdebugserverはどのように呼び出されますか?私たちはLLDBを通じてFinderにattachし、debugserverではありません.また、このdebugserverプロセスはまだ生存していますか?➜ ~ pgrep debugserver=>43474このプロセスが存在する以上、どのように起動し、どのような起動パラメータがあるかを観察します.
➜  ~ ps -fp `pgrep -x debugserver
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501 43474 43473   0  4:24PM ttys004    0:00.15 /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/debugserver --native-regs --setsid --reverse-connect 127.0.0.1:63719
cmd: /path/to/debugserver --native-regs --setsid --reverse-connect 127.0.0.1:63719

次に、起動パラメータを削除または変更するとどのような影響を及ぼすかを見てみましょう.削除--reverse-connect 127.0.0.1:63719で何が起こるかを確認し、どのプロセスがdebugserverを起動したかを確認します.
➜  ~ ps -o ppid= $(pgrep -x debugserver)
43473
➜  ~ ps -a 43473
  PID TTY           TIME CMD
43473 ttys004    0:05.19 /Applications/Xcode.app/Contents/Developer/usr/bin/lldb -n Finder
#  LLDB debugserver , debugserver ptrace attach Finder 。

ptraceのパラメータ


Mac上のコマンドラインアプリケーションを作成し、このようなSwiftコードを実行します.
import Foundation

// ptrace(PT_DENY_ATTACH, 0, nil, 0)  #  , 

while true {
  sleep(2)
  print("Hello, Ptrace")
}

プログラムが実行されると、ptrace呼び出しが2回スナップされます.
ptrace(14, 45762, 0, 0) from debugserver
ptrace(13, 45762, 5891, 0) from debugserver

クリックするとptraceが存在するヘッダファイルが表示され、以下のように定義されます.
#define PT_TRACE_ME 0   /* child declares it's being traced */
#define PT_READ_I   1   /* read word in child's I space */
#define PT_READ_D   2   /* read word in child's D space */
#define PT_READ_U   3   /* read word in child's user structure */
#define PT_WRITE_I  4   /* write word in child's I space */
#define PT_WRITE_D  5   /* write word in child's D space */
#define PT_WRITE_U  6   /* write word in child's user structure */
#define PT_CONTINUE 7   /* continue the child */
#define PT_KILL     8   /* kill the child process */
#define PT_STEP     9   /* single step the child */
#define PT_ATTACH   ePtAttachDeprecated /* trace some running process */
#define PT_DETACH   11  /* stop tracing a process */
#define PT_SIGEXC   12  /* signals as exceptions for current_proc */
#define PT_THUPDATE 13  /* signal for thread# */
#define PT_ATTACHEXC    14  /* attach to running process with signal exception */

#define PT_FORCEQUOTA   30  /* Enforce quota for root */
#define PT_DENY_ATTACH  31

#define PT_FIRSTMACH    32  /* for machine-specific requests */

int ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);

最初のパラメータはptraceがしなければならないことを示します.2番目のパラメータは、プロセスを操作するPIDを示し、3番目と4番目は最初のパラメータに依存します.上のptraceの出力が見える14はPT_ATTACHEXCです.man ptraceで検索して、この具体的な意味を確認できます.
This request allows a process to gain control of an otherwise unrelated process and begin tracing it. It does not need any cooperation from the to-be-traced process. In this case, pid specifies the process ID of the to-be-traced process, and the other two arguments are ignored.
この情報に基づいて,なぜ最初のptrace呼び出しが発生したのかが分かる.13のPT_THUPDATEについては、アップルはこれ以上説明していません.制御プロセス(lldb)が制御プロセス(Xcode runのapp)に伝達されるUnix信号とMachメッセージをどのように処理するかに関係する.

デバッグのバックグラウンド方法


上のコードのコメント行にコメントをキャンセルします:ptrace(PT_DENY_ATTACH, 0, nil, 0) XcodeのRunが起きるとdebug consoleがこれを印刷していることがわかります:Program ended with exit code: 45これはXcodeのデフォルトの起動プログラムがlldb attachで同時に使用されているためです.ptrace関数を実行してPT_DENY_ATTACHパラメータを付けるとlldbは事前に終了し、プログラムは実行を中止します.プログラムを単独で実行しようとすると、後でattachに行くとlldbも失敗し、プログラムは正常に実行されても影響を受けません.多くのMacOSの応用は、この方法で逆デバッグを達成することです.しかし、この方法は非常に解読されやすい.

デバッグを逆にする方法


開発者が逆デバッグの目的を達成するには、ptrace(PT_DENY_ATTACH, 0, 0, 0)がある場所(ほとんどがmain関数)で実行されるに違いない.だから反デバッグの構想は非常に簡単で、この実行の発生を阻止することです.lldbに-wというオプションがある以上、プロセスの起動を待つには、lldbを使用してプロセスの起動をキャプチャし、プログラムがptraceコマンドを実行する前にPT_DENY_ATTACHコマンドを変更または無視することができます.コマンドライン実行:➜ ~ sudo lldb -n "helloptrace" -w.ここでsudoを使うのはlldbのバグのためで、lldbにプロセスの起動を待たせるとsudoを使わないとエラーになります.上記の項目のバイナリファイルを見つけて、コマンドラインにドラッグして実行し、lldbはattachに成功するはずです.
➜  ~ sudo lldb -n "helloptrace" -w
(lldb) process attach --name "helloptrace" --waitfor
Process 8336 stopped
* thread #1, stop reason = signal SIGSTOP
    frame #0: 0x0000000109522b9a dyld`__ioctl + 10
dyld`__ioctl:
->  0x109522b9a : jae    0x109522ba4               ; 
    0x109522b9c : mov    rdi, rax
    0x109522b9f : jmp    0x109522325               ; cerror
    0x109522ba4 : ret

Executable module set to "/Users/gogleyin/Library/Developer/Xcode/DerivedData/helloptrace-bjtaxdebpzdyraaogpbcrihdgwku/Build/Products/Debug/helloptrace".
Architecture set to: x86_64h-apple-macosx.
(lldb) rb ptrace -s libsystem_kernel.dylib continueが実行を続行すると、ptrace関数が実行されるときに停止します.lldbを使用して、プログラムがその関数を実行しないようにして、事前に戻ることができます:(lldb) thread return 0 continueは実行を続けて、1つの逆デバッグは達成しました!プログラムはptrace関数に入りますが、lldbに関数ロジックが実行されないように早めに戻るように伝えます.

に続く


しかし、プロセスがptraceシステム呼び出しをいつ実行したか分からない場合、上記の方法は少し急いでいます.この場合、どのようにデバッグを反転しますか?両者のどちらが優れているかは、後続の文章を見てください.