
Goツールチェーンをラップし、識別子、パッケージパス、位置データをハッシュに置き換え、デバッグ情報とビルド情報を削除することで、Goビルドを難読化します。
go install mvdan.cc/garble@latest # または @master
Go ツールチェーンをラップして Go コードを難読化します。Go 1.27 以降が必要です。
garble build [build flags] [packages]
このツールは、難読化されたコードでテストを実行する garble test、
シンプルなプログラムを難読化して実行する garble run、
スタックトレースなどのテキストを難読化解除する garble reverse、
記入済みのバグレポートを提出する garble bug もサポートしています。
利用可能なすべてのコマンドとフラグを確認するには garble -h を実行してください。
通常のビルドと同様に動作しながら、元のソースコードに関する情報を可能な限り 含まないバイナリを生成します。
このツールは以下のように設計されています:
cmd/go と連携するこのツールは Go コンパイラとリンカへの呼び出しをラップして Go ビルドを変換し、 以下のことを行います:
-literals フラグが指定された場合、リテラルを難読化する-tiny フラグが指定された場合、追加情報を削除するこのツールは、標準ランタイムを含む、ビルドされるすべてのサポート対象パッケージを難読化します。
サポート対象外の runtime/cgo および crypto/internal/fips140 パッケージは除外されます。
ランタイムの難読化は Go がサポートする GOOS/GOARCH ターゲットに従います。Garble は
より狭いアーキテクチャの許可リストを維持していません。
garble build のようなコマンドは、$PATH にある go のバージョンを使用することに
注意してください。異なるバージョンの Go を使用するには、GOTOOLCHAIN を使用できます。
よくある疑問は、コンパイル言語である Go にコード難読化ツールが必要な理由です。 Go バイナリには、元のソースに関する驚くほど多くの情報が含まれています。 デバッグ情報とシンボルテーブルを削除しても、トレース、リフレクション、デバッグのために 多くの名前と位置がそのまま残ります。
Go の一部のユースケースでは、エンドユーザーと Go バイナリを共有する必要があります。 バイナリのソースコードが非公開であるか購入が必要な場合、 その難読化はリバースエンジニアリングを抑制するのに役立ちます。
同様のユースケースとして、ソースが非公開または購入された Go ライブラリがあります。 Go ライブラリはバイナリ形式でインポートできず、Go プラグインには 欠点があるため、 難読化されたソースコードを共有することが選択肢となります。 #369 を参照してください。
難読化は、ライセンスとはまったく無関係な側面にも役立ちます。
たとえば、-tiny フラグはバイナリを 15% 小さくすることができ、
アプリサイズを削減するための Android での一般的な慣行に似ています。
難読化は、Go バイナリを誤ってマルウェアとして扱うアンチウイルススキャンを
回避するために、一部のオープンソース開発者にも役立っています。
-literals フラグを使用すると、文字列などのリテラル式が、
実行時に同じ値に解決されるより複雑な式に置き換えられます。
-ldflags=-X を介して注入された文字列リテラルも、このフラグによって置き換えられます。
この機能はオプトインです。入力コードによっては速度低下を引き起こす可能性があります。
定数式で使用されるリテラルは、コンパイル時に解決されるため難読化できません。
これには、たとえば const 宣言の一部である式が含まれます。
このプロセスは、十分な労力をかければ元に戻せることに注意してください。 #984 を参照してください。
-tiny フラグを使用すると、Go バイナリからさらに多くの情報が削除されます。
位置情報は難読化されるのではなく、完全に削除されます。
パニック、致命的エラー、トレース/デバッグ情報を出力するランタイムコードが削除されます。
多くのシンボル名もリンク時にバイナリセクションから省略されます。
全体として、これによりバイナリを約 15% 小さくすることができます。
このフラグを使用すると、パニックや致命的なランタイムエラーが出力されることはありませんが、
通常どおり recover で内部的に処理することは可能です。
このフラグはクラッシュのデバッグを難しくする可能性があることに注意してください。
パニックはスタックトレースを出力せずにプログラム全体を終了し、
ソースコードの位置と多くの名前が削除されます。
同様に、garble reverse は通常このモードでは役に立ちません。
参照: CONTROLFLOW.md
garble build は go build の約 2 倍の時間がかかります。
入力コードをロードして型チェックするための元のビルドと、
難読化されたビルドの 2 つのビルドを完了する必要があるためです。
Garble は Go が一度に 1 つのパッケージをコンパイルする方法を反映して、
一度に 1 つのパッケージを難読化します。これにより Garble は Go のビルドキャッシュを
完全にサポートできます。増分の garble build 呼び出しは、
変更されたコードのみを再ビルドおよび再難読化するはずです。
最初の garble build 呼び出しは比較的遅くなる可能性があることに注意してください。
各パッケージを初めて難読化する必要があるためです。これは、
go clean -cache で GOCACHE をクリアして go build をゼロから実行するのに似ています。
Garble は Go の GOCACHE と同様に、作業を再利用するために独自のキャッシュも利用します。
デフォルトではユーザーのキャッシュディレクトリ配下のディレクトリ、
たとえば ~/.cache/garble に配置され、GARBLE_CACHE を設定することで別の場所に配置できます。
Go と同様に、garble のビルドは本質的に決定的で再現可能です。
これには、ビルドのキャッシュや、garble reverse を使用してスタックトレースを
難読化解除できるなど、大きな利点があります。
デフォルトでは、garble は各パッケージを独自の方法で難読化します。 これはビルド入力が変更されると変化します: garble のバージョン、Go のバージョン、 パッケージのソースコード、または GOOS や -tags などのビルドパラメータです。 これらの入力を推測するのは非常に難しいため、これは妥当なデフォルトです。
-seed フラグを使用して、独自の難読化ランダムシードを提供できます。
同じシードを再利用すると同じコード難読化を生成するのに役立ち、
デバッグや問題の再現に役立ちます。
シードを定期的にローテーションすることは、長期的なリバースエンジニアリング対策にも役立ちます。
そうしないと、Go の標準ライブラリがどのように難読化されるかの変化を見て、
一連のビルドを通じて Go や garble のバージョンがいつ変更されたかを推測できてしまいます。
ビルドごとに常に異なるシードを使用するには、-seed=random を使用します。
カスタムシードを使用する場合は特に注意が必要です:
ビルドで使用された -seed の値が失われると、garble reverse は機能しません。
これらのほとんどは、時間と労力とともに改善される可能性があります。このセクションの目的は、 このツールの現在の欠点を文書化することです。
エクスポートされたメソッドは、インターフェースで必要とされる可能性があるため、 現時点では決して難読化されません。この領域は作業中です。 #3 を参照してください。
選択したファイルやパッケージの難読化を除外するサポートされた方法はありません。 多くの場合、ユーザーはバグを回避するためにこれを行いたいと考えます。代わりにバグを報告してください。
Go プログラムは一度に 1 つのパッケージで初期化されます。 インポートされたパッケージは常にそのインポータより前に初期化され、 それ以外の場合はインポートパスの字句順に初期化されます。 garble はインポートパスを難読化するため、この字句順は任意に変化する可能性があります。
Go プラグインは現在サポートされていません。#87 を参照してください。
Garble はリンカにパッチを適用するために git を必要とします。これは go-gitdiff が
非厳密パッチをサポートすれば回避できます。
runtime.GOROOT や
runtime/debug.ReadBuildInfo のような API は
難読化されたバイナリでは機能しません。これはたとえば
タイムゾーンの読み込みに影響する可能性があります。
新しいコントリビューターを歓迎します。コントリビュートしたい場合は、 出発点として CONTRIBUTING.md を参照してください。
バグレポートであれ機能リクエストであれ、PR を送る前に issue を提出してください。 そうすることで、問題とその解決策について先に合意できます。