haXを完全に破壊します。 機械コードを開いた本として読む

haXeがC ++に変換され、マシンコードに変換される場合、特に一見するとこのコードは仮想メソッドの呼び出しでいっぱいであり、デバッガーを起動せずにメソッド本体のアドレスと相関させるのが難しいため、これは絶望的に見えるかもしれません



しかし、すべてがそれほど悪くはありません。 スクリプトサポートが無効(HXCPP_SCRIPTABLE)であっても、メソッドとフィールドの名前を含む行がファイル内にあります。 このボールを解く方法を分析し、メソッドの名前を仮想メソッドのテーブルのアドレスとオフセットと比較します。



理論のビット



C ++での変換後、すべてのhaXeクラスはhx.Object( hxcpp / include / hx / Object.hで定義されています)から継承されます。 特に興味深いのは次の方法です。



virtual Dynamic __Field(const String &inString, hx::PropertyAccess inCallProp); virtual Dynamic __SetField(const String &inField,const Dynamic &inValue, hx::PropertyAccess inCallProp);
      
      





これらのメソッドはC ++で翻訳されたクラスでオーバーライドされ、その実装はどこでも次のようになります。



src / openfl / geom / Matrix.cpp
 Dynamic Matrix_obj::__Field(const ::String &inName,hx::PropertyAccess inCallProp) { switch(inName.length) { case 1: if (HX_FIELD_EQ(inName,"a") ) { return a; } if (HX_FIELD_EQ(inName,"b") ) { return b; } if (HX_FIELD_EQ(inName,"c") ) { return c; } if (HX_FIELD_EQ(inName,"d") ) { return d; } break; case 2: if (HX_FIELD_EQ(inName,"tx") ) { return tx; } if (HX_FIELD_EQ(inName,"ty") ) { return ty; } break; case 5: if (HX_FIELD_EQ(inName,"clone") ) { return clone_dyn(); } if (HX_FIELD_EQ(inName,"scale") ) { return scale_dyn(); } if (HX_FIELD_EQ(inName,"setTo") ) { return setTo_dyn(); } break; case 6: if (HX_FIELD_EQ(inName,"concat") ) { return concat_dyn(); } if (HX_FIELD_EQ(inName,"equals") ) { return equals_dyn(); } if (HX_FIELD_EQ(inName,"invert") ) { return invert_dyn(); } if (HX_FIELD_EQ(inName,"rotate") ) { return rotate_dyn(); } break; case 7: if (HX_FIELD_EQ(inName,"__array") ) { return __array; } if (HX_FIELD_EQ(inName,"toArray") ) { return toArray_dyn(); } break; case 8: if (HX_FIELD_EQ(inName,"copyFrom") ) { return copyFrom_dyn(); } if (HX_FIELD_EQ(inName,"identity") ) { return identity_dyn(); } if (HX_FIELD_EQ(inName,"toString") ) { return toString_dyn(); } break; case 9: if (HX_FIELD_EQ(inName,"copyRowTo") ) { return copyRowTo_dyn(); } if (HX_FIELD_EQ(inName,"createBox") ) { return createBox_dyn(); } if (HX_FIELD_EQ(inName,"translate") ) { return translate_dyn(); } break; case 10: if (HX_FIELD_EQ(inName,"to3DString") ) { return to3DString_dyn(); } break; case 11: if (HX_FIELD_EQ(inName,"copyRowFrom") ) { return copyRowFrom_dyn(); } if (HX_FIELD_EQ(inName,"setRotation") ) { return setRotation_dyn(); } if (HX_FIELD_EQ(inName,"toMozString") ) { return toMozString_dyn(); } if (HX_FIELD_EQ(inName,"__toMatrix3") ) { return __toMatrix3_dyn(); } break; case 12: if (HX_FIELD_EQ(inName,"copyColumnTo") ) { return copyColumnTo_dyn(); } if (HX_FIELD_EQ(inName,"__transformX") ) { return __transformX_dyn(); } if (HX_FIELD_EQ(inName,"__transformY") ) { return __transformY_dyn(); } break; case 13: if (HX_FIELD_EQ(inName,"__cleanValues") ) { return __cleanValues_dyn(); } break; case 14: if (HX_FIELD_EQ(inName,"copyColumnFrom") ) { return copyColumnFrom_dyn(); } if (HX_FIELD_EQ(inName,"transformPoint") ) { return transformPoint_dyn(); } break; case 16: if (HX_FIELD_EQ(inName,"__transformPoint") ) { return __transformPoint_dyn(); } break; case 17: if (HX_FIELD_EQ(inName,"createGradientBox") ) { return createGradientBox_dyn(); } break; case 19: if (HX_FIELD_EQ(inName,"deltaTransformPoint") ) { return deltaTransformPoint_dyn(); } if (HX_FIELD_EQ(inName,"__transformInverseX") ) { return __transformInverseX_dyn(); } if (HX_FIELD_EQ(inName,"__transformInverseY") ) { return __transformInverseY_dyn(); } break; case 22: if (HX_FIELD_EQ(inName,"__translateTransformed") ) { return __translateTransformed_dyn(); } break; case 23: if (HX_FIELD_EQ(inName,"__transformInversePoint") ) { return __transformInversePoint_dyn(); } } return super::__Field(inName,inCallProp); }
      
      





ご覧のとおり、通常はフィールドと見なされるものだけでなく、動的ラッパーのメソッドも抽出する必要がありますが、メソッドもHaKsトランスレーターの理解においてフィールドであると理解されます。



準備する



したがって、__ Fieldメソッドを見つけることから始める価値があります。 たとえば、メソッドの名前を持つ行へのリンクをたどることで、それにアクセスできます。 ファイル内の行を読み取ると、たとえば__ToStringまたはRTTIの後方リンクに到達できます。 これらのうち、戻るリンクに従ってVMTに移動します。 文字列がフィールドの名前である場合、__ Fieldの代わりに、同様の__SetFieldメソッドを使用できますが、メソッドの動的ラッパーへのリンクがないため、あまり適していません。 VMTで、オーバーライドされたメソッド(アドレスによって割り当てられた)を開き、それらのうち__Fieldに似ているものを探します(最初に大きなスイッチがあります)。



__Fieldを開始
 .text:010B3DB8 var_30 = -0x30 .text:010B3DB8 var_2C = -0x2C .text:010B3DB8 var_28 = -0x28 .text:010B3DB8 var_20 = -0x20 .text:010B3DB8 .text:010B3DB8 PUSH.W {R4-R9,LR} .text:010B3DBC SUB SP, SP, #0x14 .text:010B3DBE MOV R7, R2 .text:010B3DC0 MOV R4, R0 .text:010B3DC2 LDR R0, [R7] .text:010B3DC4 MOV R9, R3 .text:010B3DC6 MOV R5, R1 .text:010B3DC8 SUBS R0, #4 ; switch 28 cases .text:010B3DCA CMP R0, #0x1B .text:010B3DCC BHI.W def_10B3DD0 ; jumptable 010B3DD0 default case .text:010B3DD0 TBH.W [PC,R0,LSL#1] ; switch jump .text:010B3DD0 ; --------------------------------------------------------------------------- .text:010B3DD4 jpt_10B3DD0 DCW 0x1C ; jump table for switch statement .text:010B3DD6 DCW 0x35
      
      





__SetFieldを開始
 .text:010B48DC var_38 = -0x38 .text:010B48DC var_30 = -0x30 .text:010B48DC var_28 = -0x28 .text:010B48DC var_24 = -0x24 .text:010B48DC var_20 = -0x20 .text:010B48DC arg_0 = 0 .text:010B48DC .text:010B48DC PUSH.W {R4-R9,LR} .text:010B48E0 SUB SP, SP, #0x1C .text:010B48E2 MOV R7, R2 .text:010B48E4 MOV R8, R0 .text:010B48E6 LDR R0, [R7] .text:010B48E8 MOV R6, R3 .text:010B48EA LDR R5, [SP,#0x38+arg_0] .text:010B48EC MOV R9, R1 .text:010B48EE SUBS R0, #6 ; switch 13 cases .text:010B48F0 CMP R0, #0xC .text:010B48F2 BHI.W def_10B48F6 ; jumptable 010B48F6 default case .text:010B48F6 TBH.W [PC,R0,LSL#1] ; switch jump .text:010B48F6 ; --------------------------------------------------------------------------- .text:010B48FA jpt_10B48F6 DCW 0xD ; DATA XREF: .text:01329970↓o .text:010B48FA ; jump table for switch statement .text:010B48FC DCW 0x25
      
      





仮想メソッドテーブルの__Fieldは__SetFieldよりも前にあり、通常はオプションが少なくなっています。 この例では、13対28です。



最初の段階:動的ラッパーを探しています



両方のメソッドが見つかったら、__ Fieldに移動し、0 == memcmpの後の分岐の場所を見て、ラッパーに名前を付ける必要があります。 この場合、通常のフィールドとラッパーの両方に遭遇する可能性があります。 それらを区別する方法を学ぶのは簡単です、ここでは通常のフィールドの例、そしてメソッドの動的ラッパーです:



 .text:010B44B0 loc_10B44B0 ; CODE XREF: __Field+16A↑j .text:010B44B0 LDR R0, [R5,#0x20] .text:010B44B2 B loc_10B4582 .text:010B44B4 ; --------------------------------------------------------------------------- .text:010B44B4 .text:010B44B4 loc_10B44B4 ; CODE XREF: __Field+1B0↑j .text:010B44B4 LDR R2, =(get_error_dyn+1 - 0x10B44BA) .text:010B44B6 ADD R2, PC ; get_error_dyn .text:010B44B8 B loc_10B44D2
      
      





このファイルにはないが、ラッパーへのポインターが認識されないという問題がありました。 オレンジ色の異常に大きな整数オペランドのように見えます。 Ctrl + Rを使用して、IDAでオフセットにする必要があります。



第二段階:最も単純なケース



まず、C ++での翻訳後、メソッドとラッパーがどの程度配置されているかを見てみましょう。



src / openfl / geom / Matrix.cpp
 // … ::lime::math::Matrix3 Matrix_obj::__toMatrix3( ){ HX_STACK_FRAME("openfl.geom.Matrix","__toMatrix3",0xaf6ed17e,"openfl.geom.Matrix.__toMatrix3","openfl/geom/Matrix.hx",480,0xa0d54189) HX_STACK_THIS(this) HX_STACK_LINE(482) Float tmp = this->a; HX_STACK_VAR(tmp,"tmp"); HX_STACK_LINE(482) Float tmp1 = this->b; HX_STACK_VAR(tmp1,"tmp1"); HX_STACK_LINE(482) Float tmp2 = this->c; HX_STACK_VAR(tmp2,"tmp2"); HX_STACK_LINE(482) Float tmp3 = this->d; HX_STACK_VAR(tmp3,"tmp3"); HX_STACK_LINE(482) Float tmp4 = this->tx; HX_STACK_VAR(tmp4,"tmp4"); HX_STACK_LINE(482) Float tmp5 = this->ty; HX_STACK_VAR(tmp5,"tmp5"); HX_STACK_LINE(482) ::lime::math::Matrix3 tmp6 = ::lime::math::Matrix3_obj::__new(tmp,tmp1,tmp2,tmp3,tmp4,tmp5); HX_STACK_VAR(tmp6,"tmp6"); HX_STACK_LINE(482) return tmp6; } HX_DEFINE_DYNAMIC_FUNC0(Matrix_obj,__toMatrix3,return ) Void Matrix_obj::__transformInversePoint( ::openfl::geom::Point point){ { HX_STACK_FRAME("openfl.geom.Matrix","__transformInversePoint",0xde42fb73,"openfl.geom.Matrix.__transformInversePoint","openfl/geom/Matrix.hx",487,0xa0d54189) // …
      
      





メソッドの本体が最初に移動し、次にマクロで動的ラッパーが構築され、次に次のメソッド、その動的ラッパーなどが構築されることがわかります。 ラッパーには最初の段階で名前が付けられていますが、メソッド自体はまだではないため、名前付きルーチンに名前付きルーチンが散在している場合、IDAのルーチンのリストに「縞模様」の画像が表示されます。



これは完全に真実ではありませんが、この段階で処理する必要があるのは最も明白なケースのみです。動的ラッパー間にサブルーチンが1つだけあり、これがメソッドである可能性が高い場合です。 彼は、彼より低いラッパーから名前を与えられます。



注意 :IDAがメソッドの本体をサブルーチンとして認識しなかったが、メソッドの後に来る補助的なものを認識した場合、このファイルにはありませんでした。 このメソッドは、VMTからバックトラックされています。



第3段階:ラッパー間に2つのサブプログラムがある場合



動的ラッパーは、次のようなマクロで作成されます



 #define HX_DEFINE_DYNAMIC_FUNC0(class,func,ret) \ ::Dynamic __##class##func(hx::Object *inObj) \ { \ ret reinterpret_cast<class *>(inObj)->func(); return ::Dynamic(); \ }; \ ::Dynamic class::func##_dyn() \ {\ return hx::CreateMemberFunction0(this,__##class##func); \ }
      
      





ご覧のとおり、ここでは、型付きと型なしの2つのラッパーが一度に作成されますが、通常、型付きはC ++トランスレーターによって不必要にスローされます。 動的ラッパーの間に一度に2つの名前のないサブプログラムがある場合、最初のメソッドが目的のメソッドであり、2番目が型付きラッパーである可能性があります。



3番目の段階の開始までに、ほとんどのメソッドにはすでに名前が付けられているはずです。したがって、VMTから見ると、これらは単一のスペースになり、この段階で削除されます。



第4段階:VMTの大きなギャップを埋める



2つ以上の方法で、VMTに大きなギャップがある場合があります。 繰り返しになりますが、VMTから見ると便利であることがわかります。 したがって、__ Fieldトラバーサル中に1つのメソッドを見逃すと、IDAルーチンのリストにある動的ラッパー間の3つの名前のないサブルーチンのように見えますが、ハックは他のニーズに合わせて追加のルーチンを生成でき、動的ラッパー間の3つの名前のないサブルーチンも取得できます。



VMTから、次のことがわかります。2つの要素のスペースがある場合、これは__Fieldで欠落している動的ラッパーです。 このギャップがあるルーチンのリストで、中間のルーチンに移動すると、ラッパーになります。 Xを使用して、バックリンクのリストを開きます。その中に__Fieldがあります。 そこに行き、ラッパーの名前を見つけ、サブプログラムのリスト内のスペースをストリップで「ドラッグ」し、次に説明したアルゴリズムに従ってメソッドの名前を配置します。



Hx.Objectのメソッド



完全を期すために、 hxcpp / include / hx / Object.hを開き、すべての仮想メソッドを順番に書き出して、VMTの先頭でメソッドを特定できます。



フィールドと引数のデータ型の定義



メソッド(すべての仮想メソッドなど)がフィールドと引数で呼び出される場合、どのVMTでそれらを検索するかを理解する必要があります。そのためには、一般にどのタイプであるかを理解する必要があります。 デバッガーを実行しない場合、動的ラッパーがこれを支援します。 これらは、入力として正式な型(動的、動的、動的など)の引数を受け取り、呼び出しを行うために、最初に動的をメソッドが期待する実際の型にキャストします。 この変換中に、まさにこれらの型を認識することができます。



たとえば、ラッパーの本文にある場合:



 .text:010B3884 LDR R1, =(off_23DE1D4 - 0x10B388E) .text:010B3886 MOVS R3, #0 .text:010B3888 LDR R2, =(off_23E04A0 - 0x10B3890) .text:010B388A ADD R1, PC ; off_23DE1D4 .text:010B388C ADD R2, PC ; off_23E04A0 .text:010B388E LDR R1, [R1] ; hx_Object_ci .text:010B3890 LDR R2, [R2] ; off_22D9DE0 .text:010B3892 BLX.W __dynamic_cast
      
      





...次に、hx.Objectから他の何かへのキャストが行われていることがわかります。 hx_Obejct_ciがまだ特定されていない場合、両方のクラスは不明になりますが、これは解決できます。 ポインタが導くRTTIを調べ(この例ではoff_22D9DE0)、名前を付けて結論を出します。



同様に、__SetFieldはフィールドに便利です。これは、型をDynamicからフィールドの実際の型にキャストすることを強制されるため、ヒントを提供します。



静的フィールドとメソッド



クラスに静的要素がある場合、静的メソッド__GetStaticおよび/または__SetStaticをオーバーライドします。 VMTでは、明らかな理由で、それらは表示されませんが、クラスに静的要素と通常の要素の両方がある場合、翻訳されたコードは__Field、__ GetStatic、__ SetField、__ SetStaticの順序になるため、__ Fieldと__SetFieldを計算できる場所がわかりますおよびそれらの隣の__SetStatic。 また、文字列の長さに沿ってスイッチの先頭にあり、次に比較操作。



スクリーンキャスト







00:00 Find __Fieldおよび__SetField

03:00第1段階:動的ラッパーを探しています

21:30第2段階:最も単純なケース

30:48第3段階:ラッパー間に2つのサブプログラムがある場合

33:15第4段階:VMTの大きなギャップを埋める

49:00 hx.Objectメソッド



All Articles