Golangは素晴らしい言語です。 強力なタイピング、ガベージコレクション、cgoを介したC関数の呼び出し、リフレクト、ちゃん-おとぎ話! 明らかに、そう思うだけでなく、 goは人気があります。つまり、多くのプログラマーが使用していることを意味します。つまり、誰かがバイナリをリバースする必要がある可能性が高いことを意味します。
準備する
開始するには、windows / amd64の下で新鮮なgolang 1.8を使用してください。 バイナリには、自分の
工芸品の 1つを使用します。
go build
コンパイルし
go build
。 ファイルサイズ-7256576バイト、DWARFが配置されています。 それはしません。 ほとんどの場合、リリースされると、不要なものはすべてバイナリから切り取られます。 stripのような標準ユーティリティはgoでは
うまく機能しません
go build -ldflags "-w -s"
バイナリをトリミングするための一般的なオプションの1つである
go build -ldflags "-w -s"
を見つけます。 これらの
フラグの機能を見ると、
-wが DWARFを削除し、
-sがシンボルテーブルとデバッグ情報を削除していることがわかります。 コンパイルし、ファイルサイズを調べます-4894720、まあ、小さいと
約束した
人はい
ないようです。
機能を探しています
バイナリをidaで開き、検出された機能のために、エントリポイントのみであるため、悲しいです。 少し掘り下げてみると、idaが次のようになっていることがわかります。
lea rax, qword_452018+128h jmp rax
アドレスに行き、関数を宣言し、分析を実行します-215個の関数が見つかりました。

この精神で続けると、解析に時間がかかります。 考える時間です。 標準ライブラリからのリフレクト
により、関数
を名前で呼び出すことができることを思い出してください。つまり、関数の名前をよく、または少なくともそれらのハッシュをどこかに保存する必要があり、アイデアを提供することができます。
必要な構造体へのポインターに
Go build ID: "e07bfb8669c13efb74574c0ad220c5f2cfae5cd4"
することを期待して、ファイルの先頭にスキップしますが、
Go build ID: "e07bfb8669c13efb74574c0ad220c5f2cfae5cd4"
という行を満たします。 golangソースは私たちが利用できるので食べますが、この行
について言及しています。 リンカコードに行があります
ctxt.Syms.Lookup("go.buildid", 0)
これは、いくつかの
go.buildid文字
テーブルの検索について説明してい
ます 。 バイナリでこの行を見つけると仮定して、見つけます。
線のすぐ上に401000hという数字があります。これは、線へのポインタです。 以下に関数の定義を示し、上に-値の1つがポインターのように見えるペアの配列を示します。 これらのポインタが指す場所をチェックし、見逃さないようにします-関数上。
上記の内容を確認するには、デバッグ情報を残してコードをコンパイルします。 idaで開き、この配列が
pclntabと呼ばれることを確認します。この配列は
func(ctxt * Link)pclntab()関数を使用してリンカーに設定されます。
関数名が格納されているpclntabテーブルに関する情報があり、buildidを介してこのテーブルにアクセスする方法があります。 コードを書きます:
関数に名前を付けるコード def go_find_pclntab(): pos = idaapi.get_segm_by_name(".text").endEA textstart = idaapi.get_segm_by_name(".text").startEA while True:
開始して結果を確認します。

ほとんどすべての5728関数には名前があります

タイプを探しています
関数名の後に私が興味を持った2番目のことは、型の識別です。 まず、構造に動的メモリがどのように割り当てられるかに興味があります。これはすぐに
newobject関数につながり、
runtime._typeへのポインタが渡され
ます 。
Runtime._type構造 type _type struct { size uintptr ptrdata uintptr
気づいた? ヒント:
str nameOff
フィールド。 ヒント:strは文字列です。 ヒント:nameOff-> name-> name-> type structure name-> type name。 型構造には型名が含まれています! それだけが何らかの親類です。 strは、モジュールセクションの型の先頭からのオフセット
であり 、モジュール構造の
moduledataから取得できるポインターであることが
わかります。 そのため、タイプを見つけるために、moduledataを見つける必要があります。 簡単にするために、常に1つのモジュールがあると仮定します。 関数
runtime.resolveNameOffでは 、最初のモジュールが変数
firstmoduledataにあることがわかります。これは、バイナリで見つける必要があります。 上記のスクリプトですべての関数のアドレスと名前を既に収集しているので、これに遠ざかる必要はなく、同じ関数resolveNameOffを調べることができます。 認識のための型を見つけるだけです。これは、newobject関数へのすべての呼び出しを取得し、パラメーターから型へのポインターを取得したためです。
lea rbx, _type_p_elliptic_p256Point_6dad60 ; *elliptic.p256Point mov [rsp+0A8h+var_A8], rbx call runtime_newobject ;
まとめ
このバイナリの解析を続行できますが、すでに目標を達成しており、golangバイナリの探索は非常に簡単であり、IDAのスクリプトを使用すると作業が大幅に容易になることが示されています。
ソースコード:
github.com/mogaika/golang_ida_scriptsコードは「概念実証」に基づいて記述されており、欠陥が含まれている場合があります。すべてのクレームは、ここまたはgithubのプライベートメッセージで表現できます。テキストは「すべての善」の権利に基づいて書かれており、テキストの品質に関する主張はプライベートメッセージで期待されています。