2017-04-22 9 views
4

私は、さまざまなメソッド呼び出しエントリのログをいくつか実行するDalvikバイトコードの計測器を書いています。具体的には、さまざまなメソッド呼び出しサイトで、パラメータを収集し、配列Object[]に配置し、それをロギング関数に渡す一連の命令を挿入します。Dalvik Verifierのリファレンスと正確なリファレンス

これはすべてうまくいきました。私は実装して、ほとんどのアプリですべてのkludgesを過ぎました。

java.lang.VerifyError: Verifier rejected class io.a.a.g: void io.a.a.g.r() 
failed to verify: void io.a.a.g.r(): [0x570] register v5 has type Reference: 
java.lang.Object but expected Precise Reference: java.lang.String 

は私が私の計装によって生成されたコードを見て、私がやっているすべてのオブジェクトの配列内のレジスタV5を置くことである。しかし、私は一つの特に不可解なのDalvik検証エラーに遭遇しています。

私はここにいくつか質問があります:

  • 正確参照は何ですか、そしてなぜそれが参照すると互換性がありませんか?
  • ここでのオフセットとは何ですか? [0x570]は、バイトコード命令の中点を指しているため、指示に明確には対応していません。その周辺の命令にはv5が含まれていません。
  • これをデバッグするにはどうすればよいですか?理想的には、私は検証者が何が起こっていると思うかを知り、それを修正したいと思います。

EDIT:

はここで私が話してメソッドのバイトコードのダンプです。 https://gist.github.com/kmicinski/c8382f0521b19643bb24379d91c47d36ご覧のとおり、0x570は命令の始まりではありません。(私が知る限りは)r5がオブジェクトとなるStringと競合する場所はありません。

答えて

3

エラーをよく見れば、Objectを渡していることがわかります。Stringが必要です。とにかく、問題の原因となっている実際のバイトコードを投稿しない限り、それ以上のことは言えません。

0x570は命令の中央を指していますか?それはしないでください。とにかく、それをデバッグする方法は、関連する命令を見て、r5がStringであると想定されるときの理由を理解することです。または、私は見てみることができるようにバイトコードを投稿することができます。

編集:今、あなたは、コードを掲載しましたことを、そこV5されているオブジェクトになり、パスが実際にあるが、それは例外ハンドラ .catch JSONException {:5D8 .. :938} :BDEはにジャンプ

少し微妙です:BDE

例外ハンドラのコードは、キャッチされた例外をv5に格納します。つまり、v5はこの時点ではもはや文字列ではありません。その後、別の例外ハンドラの範囲内にある:162

:BDE 
00000BDE move-exception  v5 
00000BE0 const    v0, 0x00488B36 
00000BE6 invoke-static  Logger->logBasicBlockEntry(I)V, v0 
00000BEC goto/16    :162 

:162にジャンプ:手付かず.catch ClassNotFoundException {:2E .. :594} :BF0

:Bf0葉のV5と:A28

:BF0 
00000BF0 move-exception  v6 
00000BF2 const    v0, 0x00488B3E 
00000BF8 invoke-static  Logger->logBasicBlockEntry(I)V, v0 
00000BFE goto/16    :A28 

:A28にジャンプすると、コードブロックの始まりですこれはv5がStringであることを前提としています。特に、指示:AE0で、v5はStringをとる関数に渡されます。

00000AE0 invoke-virtual  StringBuilder->append(String)StringBuilder, v7, v5 

0xAE0はJesusFrekeが示唆したように、あなたがコード単位のために調整した後、オフセット誤差に示すを説明している、ちょうど2倍0x570です。

これは唯一の壊れたコードパスではないことに注意してください。これは、コードを見ている間に最初に見つかったコードパスです。しかし、v5の型をJSONExceptionで統一してObjectに変換するには、1つの不正なパスで十分です。

+0

はここJEBを介して行わバイトコードのダンプ、です:あなたが見ることができるようにhttps://gist.github.com/kmicinski/c8382f0521b19643bb24379d91c47d36 、0x570は、バイトコード命令の開始として存在するものではありません。さらに、私が言うことができる限り、 'r5'はこのメソッド内のどの場所でも間違った方法で使用されていません(コントロールフローは少し難しいですが、正確なエラーインデックスを持たせるのが助けになります) –

+0

@KristopherMicinski私は編集しました私のポストは、v5が非Stringオブジェクトである不適切な結果となるコントロールフローの例を示しています。 – Antimony

+0

ありがとう!うわー、あなたは本当に上を行きました、そのコードは本当に解釈するのが難しいです。私は間違いなくこれを見て、あなたの答えを受け入れるでしょう.. –

2

0x570は、それぞれ2バイトのコード単位のオフセットです。バイトオフセットは実際には0xAE0であり、これは命令に対応し、その命令はv5を参照します。

v5に文字列を格納するコードがありますが、文字列がv5に格納されている場所と使用されている場所の間にマージする別のコードパスがあり、コードパスには別のオブジェクトがありますv5に格納された型。コードパスがマージされると、2つのタイプの共通のスーパークラスがレジスタのタイプとして使用されます。したがって、2つの型が完全に無関係な場合、java.lang.Objectはスーパークラスになります。

あなたは(あなたが簡単に0xAE0を見つけることができるように、また--code-offsetsをして)この問題は--register-info ARGS,DEST,FULLMERGEオプションを使用してbaksmali実行され、デバッグ、およびその後0xAE0から逆方向に見て、V5の種類が設定されている場所を確認するために何ができますオブジェクト。

+0

私の答えに記載されているように、レジスタ情報でBaksmaliの逆アセンブリを投稿することができれば、私は見てみることができます。または、何を探すかを示してください:) – JesusFreke

+0

おかげさまで、お手伝いをしてくれた、@ JesusFreke。私は実際にこれらのことを強調するのに役立つコントロールフロー分析を持っているので、コードオフセット項目がわかっているのは私が本当に探していたものです! (私はこれを見て、あなたに戻って..!) –

+0

非文字列オブジェクトは例外ハンドラから来ています。例外ハンドラはv5に例外を格納し、最終的にv5を再割り当てせずにAE0に到達します。詳細は私のポストを参照してください。 – Antimony

2

私は他の人があまり一般化していないトリッキーな質問に答えるために時間を寄付してくれたので、私の答えを追加したいと思います!

@Antimonyが指摘するように、v5に例外が格納された例外ハンドラで開始私のコード内の制御パスが例外ハンドラ内「ダポイント、次いでgotoObjectことがv5を引き起こす)がありました。その例外ハンドラにより、v5が文字列として使用され、検証エラーが発生しました。

アプリのオリジナルコードでは、そのgotoのターゲットで薄いのはreturn-void命令でした。このため、Dalvikベリファイアはパスを例外ハンドラに伝播しませんでした。

残念なことに、このアプリケーションを書き直すと、その例外ハンドラの対象にこのreturn-void命令以上の内容が含まれ、そのブロックとキャッチされた例外ハンドラに検証の理由があります。具体的には、return-voidの前に、Logger.logMethodExitへのコールを挿入しました。ベリファイアは、例外ハンドラ(この場合は:BF0)に制御を戻し、最終的にv5が文字列として使用された場所に転送することができます。元のアプリケーションでは、(gen/killデータフローの意味で)殺されました。しかし、書き換えに際して、私はこの余分な呼び出しをデータフローの不変条件を破ることを含めました。

私はこれを私の実装で修正する方法を知っていると思うが、確かに分かりにくい痛みだった!

は、より一般的なレッスンがここに学んだ:

  • 検証エラーオフセットはJVMバイトコードとは異なり、実際にバイトコード内のわずか2 *インデックス

  • ある、のDalvikバイトコードはreturnを含め、非スロー可能オペコードのサブセットを考慮します。これは

  • 正確な参照は何かが1つの基本ブロックでのオブジェクトの特定の改善、および他でObjectなるように制約されることを意味データフロー解析に影響を与えます(このエラーは私には少し難解なようだけれども...)

  • バイトコードを書き直すときは、暗黙のうちに対処しているgen/killセットを認識する必要があります。特にreturn-*命令はすぐに物事を殺しますが、try..の中の基本ブロックの先頭にジャンプすると、それらのものは生きる。

+0

あなたはこのコーナーケースに遭遇したことに興味があります。 Javaのバイトコードでは、すべての命令がスローされる可能性があります(リターンを含む)。しかし、Dalvikはよりスマートにしようとし、オペコードのサブセットだけを投げることができると宣言します。私はそのリストを手放すことは覚えていないが、明らかにリターンは非投げとみなされる。 – Antimony

+0

あなたはこれをどのように理解したのか覚えていますか?コンパイラのソースを読むだけでしたか?私はいくつかを読んだことがありますが、明らかにあなたの入力が絶対に役立つようにはありません:-)私はそうではないと思ったでしょうか? –

+2

baksmaliのデオドキシング機能を実装しながら、レジスタタイプの推論ロジックを模倣しようとしながら、dalvikバイトコードベリファイアを読んだとき、私はこれを実行しました(特定の命令だけがスローできます)。例えば、 https://android.googlesource.com/platform/dalvik/+/kitkat-release/opcode-gen/bytecode.txtの "throw"命令フラグ – JesusFreke