Upgrade to Pro — share decks privately, control downloads, hide ads and more …

本当はこわいLLDB

 本当はこわいLLDB

iOSDC2020 Day2 16:20〜
https://fortee.jp/iosdc-japan-2020/proposal/62a95cf9-1b1b-401a-8a1c-11c0490aad00

【Special Thanks】
発表資料のレビューとリハに付き合ってくれた
ddd503(@dk31486981 )

MiyasakaKazutoshi

September 21, 2020
Tweet

More Decks by MiyasakaKazutoshi

Other Decks in Programming

Transcript

  1. ⾃⼰紹介 Name: みやし Twitter: @po_miyasaka Work:minne @ GMOペパボ OSSの宣伝: 


    @yhkaplan⽒とVisionAPIを使ったクレカリーダーのOSSを作成し ました。
 iOS13以降必須ですが、card.ioよりも読み取れるカードも多く、 UIもカスタマイズしやすいので、ぜひ活⽤してみてください!
 https://github.com/yhkaplan/credit-card-scanner 2
  2. ⽬次 • 下準備 • スライドについての備考 • 拡張コマンドのインストール • SIPを解除する •

    アプリにアタッチする • アプリ全体を眺める • 依存しているダイナミックライブラリの⼀覧化 • アセンブリを書き出す • アプリ内でハードコードされた⽂字列を⼀覧化 • ローカルデータベースの中身を⾒る • アプリ内のDocumentsやCachesの中身を⾒る • 表示中のView階層を⾒る 4 • 気になる箇所にブレークポイントを挟む • 正規表現でマッチする関数でブレーク • 関数のアドレスを指定してブレーク • UI操作契機でブレーク • ブレークした関数の引数の中身を調べる • 引数とレジスタの関係 • 引数が⽂字列だった場合 • 引数がクラスインスタンスだった場合 • 引数が関数だった場合 • ブレークした関数の返り値を調べる • ブレークした関数の引数や返り値の書き換え • リバースエンジニアリングの防⽌法 • 結論
  3. LLDBの便利な拡張コマンドをインストール • https://github.com/facebook/chisel   使⽤するコマンド: pvc, pviews • https://github.com/po-miyasaka/LLDB 使⽤するコマンド:

    vinfo, cbd • https://github.com/DerekSelander/LLDB 使⽤するコマンド: lsof, ls, lookup, tv, yoink ※ Derek⽒の「Advanced Apple Debugging & Reverse Engineering」はおすすめです。 下準備 6
  4. 任意のMACアプリにアタッチする Xcodeで以下のようなMy Macをターゲットに持つProjectを 開きます。
 
 
 Debug > Attach to

    Process PID or Name から
 任意のアプリ名を⼊⼒してアタッチします。 下準備 8
  5. UI操作契機でブレーク② まずは以下のブレークポイントを貼る。
 -Cオプションに指定したコマンドはブレークしたとき実⾏される
 -G trueは⾃動的にContinueするためのオプション Continueして操作したときに、以下のようなログが取れる。 上記を解釈すると、「ToolbarItemのアクションが発⽕により- [HOGEViewController backAction]が実⾏される」
 ということなので、以下のブレークポイントを貼ることにより、ハンドリングできるようになる、


    (ブレークポイントが作成できない場合、シンボルがない可能性があります。その場合の救済処置を後のページに記載します) (lldb) rbreak targetForAction:to:from: -C 'p/a $arg3' -C 'po $arg4' -C 'po $arg5' -G true p/a $arg3 (unsigned long) $180 = 0x0000000102ff5ed0 "backAction" po $arg4 <HOGEViewController: 0x6000008e6490> po $arg5 <ToolbarItem: 0x60000312cbb0> identifier = "back" 
 (lldb) break set -n ‘HOGEViewController backAction’ 気になる箇所にブレークポイントを挟む 19
  6. 引数とレジスタの関係① Objective-Cからコンパイルされた任意の関数でブレークし た時レジスタには以下のように値がマッピングされます。
 
 $arg1 ( $rdi ) : 関数のレシーバ

    
 $arg2 ( $rsi ) : セレクタ名 
 $arg3 ( $rdx ) : 第⼀引数 
 $arg4 ( $rcx ) : 第⼆引数 
 $arg5 ( $r8 ) : 第三引数 
 $arg6 ( $r9 ) : 第四引数 20 ブレークした関数の引数の中身を確認する
  7. 引数とレジスタの関係② 例えば以下の関数でブレークした場合は
 以下のように引数がレジスタにマッピングされます。 AppKit`-[NSApplication targetForAction:to:from:]: $arg1 ( $rdi ) :

    関数のレシーバ → NSApplicationのインスタンス 
 $arg2 ( $rsi ) : セレクタ名   → 'targetForAction:to:from:’という⽂字列 
 $arg3 ( $rdx ) : 第⼀引数    → targetForAction: に対する値 
 $arg4 ( $rcx ) : 第⼆引数    → to: に対する値 
 $arg5 ( $r8 ) : 第三引数    → from: に対する値 
 $arg6 ( $r9 ) : 第四引数    → nil 21 ブレークした関数の引数の中身を確認する
  8. 引数とレジスタの関係③ 実際レジスタには以下のように謎い64bitの数字が⼊ってます。
 この数字をみても⼈間には何を表しているかわかりません。
 これらの数字がインスタンスか、⽂字列か、関数のアドレスを指すかなどを調べ るために、いろんなコマンドを使⽤して、その実態を調べる必要があります。
 次の章から値の実態の確認をしていきます。 $arg1 ( $rdi )

    : 関数のレシーバ → 0x00007f92d9105e40 
 $arg2 ( $rsi ) : セレクタ名   → 0x00007fff7950ab9b 
 $arg3 ( $rdx ) : 第⼀引数    → 0x00007fff24da1a1e 
 $arg4 ( $rcx ) : 第⼆引数    → 0x00006000020d8510 
 $arg5 ( $r8 ) : 第三引数    → 0x0000600002703d80 
 $arg6 ( $r9 ) : 第四引数    → nil 22 ブレークした関数の引数の中身を確認する
  9. 引数がクラスインスタンスだった場合⑤ レジスタがBlockクラスのインスタンスを指していた場合、以下のよう にinvoke:に割り当てられているアドレスにブレークポイントを設置す ることで該当のBlockが実⾏されたときにブレークできます。 (lldb) po $arg1 <__NSStackBlock__: 0x70000ab7c018> signature: hogehogehumohumo

    invoke : 0x7fff43be780e (DFRFoundation`___DFRHandleHandshake_block_invoke) copy : 0x7fff43be9c2c (DFRFoundation`__copy_helper_block_e8_32r40r48r) dispose : 0x7fff43be9c76 (DFRFoundation`__destroy_helper_block_e8_32r40r48r) (lldb) break set -a 0x7fff43be780e 28 ブレークした関数の引数の中身を確認する
  10. 引数が関数だった場合 レジスタの中身が関数のアドレスだった場合、
 以下の⽅法で関数かどうかがわかります。 以下のようにSummaryに関数名が表示されます。 (lldb) image lookup -a 0x70000ab7c018 (lldb)

    image lookup -a 0x7fff27d93211 Address: CoreFoundation[0x00007fff26c7d211] Summary: CoreFoundation`__27-[__NSSet:]_block_invoke 29 ブレークした関数の引数の中身を確認する
  11. アタッチされてる場合の分岐を実装 以下のコードでアタッチを判別できるようです。
 ローカルビルドでiPhone実機とMACで動作確認しました。
 シュミレータは常にアタッチしてるものとみなされているようです。
 リリースビルドではためしてません。
 (アセンブリレベルで操作されたら意味ない気もする) 
 リバースエンジニアリングの防⽌策 let mib

    = UnsafeMutablePointer<Int32>.allocate(capacity: 4) mib[0] = CTL_KERN mib[1] = KERN_PROC mib[2] = KERN_PROC_PID mib[3] = getpid() var size: Int = MemoryLayout<kinfo_proc>.size var info: kinfo_proc? = nil sysctl(mib, 4, &info, &size, nil, 0) if (info.unsafelyUnwrapped.kp_proc.p_flag & P_TRACED) > 0 { print(“アタッチされているのでリターンします”) return  } 32