ソースなしのAndroidアプリのデバッグ:ネイティブメソッド

この記事について



以前の2つの記事では、 JavaソースコードなしでAndroidアプリケーションをデバッグする方法、ブレークポイントをインストールするいくつかの機能について説明しました 。 親愛なる読者がこれらの記事をまだ読んでいない場合は、まず始めてからこの記事を読むことを強くお勧めします。



たまたま、私はこれまで、Dalvikバイトコードのデバッグについてのみ話し、ネイティブメソッドのデバッグについては語っていませんでした。 しかし、最も巧妙なものがしばしば隠されているのはネイティブな方法です-unningな防御、興味深いマルウェア機能、ゼロデイ脆弱性。 したがって、今日は簡潔で、「水」なしで、C / C ++ソースコードなしでネイティブメソッドをデバッグする方法を教えます(まあ、何でも、読者がそこに書かれています)。



私の話から利益を得るには、少し「主題に」なる必要があります。 特に、読者は



それはおそらく読者が必要とするすべての知識とスキルです。 ツールに移りましょう。



ツール



今日必要なもの:



準備に移りましょう。



準備する



読者は、次の準備手順を独立して実行するのに十分な経験があると想定されます。



すべての準備が完了したので、デバッグに進むことができます。



デバッグ



NetBeansに組み込まれているデバッガーの下でDalvikバイトコードをデバッグするためと、gdbの下でネイティブメソッドの呼び出しをデバッグするためだけに、2つのデバッガーの下でアプリケーションを直接駆動するという考え方です。 少し奇妙に聞こえますが、実際にはそれ自体で機能します。 常にではありませんが、次のセクション「落とし穴」を参照してください。



したがって、読者が前の「準備」セクションのすべての準備手順を完了し、デバイスまたはエミュレーターで実行中のアプリケーションを再構築した場合、NetBeansはコンピューターで開かれ、 System.load(...)



またはSystem.loadLibrary(...)



呼び出した後のどこかにデバッグが配置されSystem.loadLibrary(...)



、ただしネイティブメソッドの最初の呼び出しの前。 さらに、読者はどのライブラリーでどのJNI関数がどのネイティブメソッドに対応するかをすでに認識しています。 ここから始めます。



次はステップバイステップの説明です。 Windows用に作成されましたが、LinuxとMacOSでも動作すると思います。 指示に正確に従ってください:

  1. abd shell



    コマンドを使用して、デバイスまたはエミュレーターのADBコンソールを開きます。 ADBコンソールでps



    コマンドを使用して、アプリケーションプロセスのPIDを見つけます。 同じコンソールで、次のコマンドを実行します。

     gdbserver :5039 --attach %PID%
          
          



    ここで、 %PID%



    はアプリケーションプロセスのPIDです。 応答として、gdbserverは次のようなものを出力するはずです。

     Attached; pid = %PID% Listening on port 5039
          
          



    今後、NetBeansでのデバッグはフリーズします。 つまり もちろん、そこのボタンをクリックすることもできますが、 NetBeansでデバッグしようとしているアプリケーションは、現在GDBデバッガーの下で停止しています。 パニックにならないでください。
  2. コンピューターで新しいコンソールを開き、コマンドを実行します

     adb forward tcp:5039 tcp:5039
          
          



  3. 同じコンソールで、Android NDKからgdbを実行します。

     gdb libMyNativeLibrary.so
          
          



    libMyNativeLibrary.so



    は、関心のあるJNI関数を含む同じ.soライブラリです。 ライブラリは、コンピュータの起動時にgdbの現在のディレクトリにある必要があります。 これにより、gdbコンソールが開きます。
  4. gdbコンソールで、次のコマンドを入力します。

     (gdb) target remote :5039
          
          



    これらの操作の後、次のようなメッセージ

     Remote debugging using :5039 0x4009d58c in ?? ()
          
          



    そしてADBコンソールで(それはまだ開いています、覚えていますか?)

     Remote debugging from host 127.0.0.1
          
          



  5. gdbコンソールで

     (gdb) info functions
          
          



    関数のリストを表示します。 他の関数の中でも、リストには、次のような関心のあるJNI関数も含める必要があります。

     0x5b5f7bac Java_my_app_for_debug_MainActivity_coolNativeMethod 0x5b5f7c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod 0x5b5f7c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
          
          



  6. gdbコンソールで、興味のあるJNI関数のアドレスにブレークポイントを設定します。この場合は

     (gdb) break *0x5b5f7bac (gdb) break *0x5b5f7c0c (gdb) break *0x5b5f7c1c
          
          



  7. gdbコンソールでc



    コマンドを使用して、アプリケーションを再開します。 このコマンドを実行すると、NetBeansでのデバッグが「フリーズ」し、Dalvikバイトコードを再度デバッグできるようになります。


これで、Dalvikバイトコードがネイティブメソッド呼び出しを呼び出すたびに

 const/high16 v8, 0x4100 invoke-static {v8}, Lmy/app/for/debug/MainActivity;->theCoolestNativeMethodEver(F)V
      
      



NetBeansでのデバッグはフリーズしますが、gdbは次のようなメッセージであなたを喜ばせます

 Breakpoint 1, 0x5b5f7c1c in Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver () from libMyNativeLibrary.so
      
      





これは実際にそれです。 次に、 x/i $pc



stepi



一般に、ARM命令を次々にフォワードデバッグします(最初にARMアセンブラが必要になると言ったのを覚えていますか?-まあ...)



落とし穴



ああ、たくさんあります。 落とし穴の庭。 Ainol Auroraデバイス( まだ古いもの )でAndroid 4.0.3のGNU gdbserver(GDB)7.4.1と組み合わせてGNU gdb(GDB)7.4.1を使用したときに遭遇した最も記憶に残るグリッチを次に示します。



  1. しばらくして、ウォッチドッグタイムアウトによってgdbがgdbserverから定期的に落ちる場合は、コンソールでgdb set watchdog 18000



    実行します-これが役立つはずです。
  2. info functions



    結果として、 info functions



    のリストにメモリ内の関数のアドレスが表示されず、.soファイル内のオフセットが表示される場合があります。次に例を示します。

     0x000c0bac Java_my_app_for_debug_MainActivity_coolNativeMethod 0x000c0c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod 0x000c0c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
          
          



    この場合、 libMyNativeLibrary.so



    実際に起動時にgdbのディレクトリにあるかどうかを確認し、同じgdb libMyNativeLibrary.so



    コマンドgdb libMyNativeLibrary.so



    を再起動します。
  3. info functions



    結果として、 info functions



    のリストにメモリ内の関数のアドレスと.soファイル内のオフセットが表示される場合があります。次に例を示します。

     0x5b5f7bac Java_my_app_for_debug_MainActivity_coolNativeMethod 0x5b5f7c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod 0x5b5f7c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver 0x000c0bac Java_my_app_for_debug_MainActivity_coolNativeMethod 0x000c0c0c Java_my_app_for_debug_MainActivity_anotherCoolNativeMethod 0x000c0c1c Java_my_app_for_debug_MainActivity_theCoolestNativeMethodEver
          
          



    .soファイルのオフセットを無視し、メモリ内の関数のアドレスにブレークポイントを置きます。
  4. 関数名のbreak



    コマンドが正常に機能する場合-幸運なことに、そうでない場合は...まあ、実際にはそれが機能しないので、この記事では関数のアドレスにブレークポイントを設定します。
  5. set stop-on-solib-events



    ない場合があります。 私にはうまくいきません。
  6. 時々 Cannot access memory at address 0x1



    が表示さCannot access memory at address 0x1



    。 無視してください。


これは、ソースコードなしでネイティブメソッドをデバッグすることを隠すグリッチの完全なリストとはほど遠いものであり、他の研究者は、私が遭遇したことのないまったく異なるユニークなグリッチを思い付くでしょう。 他の誰かが見つけたら-コメントで共有してください。 また、コメントでは、質問をするか、テキストを技術的に修正してください。 できるだけ早く回答するよう努めますが、バカな場合は我慢してください。 私は皆に答えようとします。



ハッピーデバッグ!






PS記事を無視した人たちへのリクエストで、まったく気に入らなかったものについてのコメントを退会してください。 可能であれば、修正を試みます。



PPS記事は大きなプラスになりました-デバッグとリバースエンジニアリングのトピックが人々にとって興味深いことを意味し、私は私の経験をさらに共有します。 冒頭の記事の短所については、まだ理解していませんでした-ああ、ハブラシラのランダムな変動を相殺しましょう。



PPPS記事のトピックに関する技術的な支援が必要な場合-連絡してください、私は何をお手伝いできます。



All Articles