取締役CTOの小竹(aka tkmru)です。 「ARM環境でディスアセンブルを妨害するテクニック」シリーズでは、アンチディスアセンブルを施されたC言語のコードを紹介しました。
アンチディスアセンブルや難読化の実験を行う際には、少しコードを変更する度に、ビルドしてバイナリを確認しています。 しかし、そんな作業を手元でちまちま行うのは面倒です。 そのため、私はコードを少し変更する度に、CIを利用してコミット毎に自動でバイナリがビルド&実行されるようにしています。 変更前のバイナリもダウンロードできる状態で残るので、比較もしやすくなります。
本記事では、GitHub Actionsを利用して、x64とARM32/ARM64のバイナリをコミット毎にビルドする環境を構築します。
全体像
ディレクトリ構成は次のようになります。
.git
はコミットを管理するためのディレクトリです。
Gitリポジトリを作成すると自動で作成されます。
.github/workflows
には、GitHub Actionsの設定ファイルを配置します。
Gitリポジトリには、必要に応じて、README.md
を作成しておくと、他の人が内容を理解しやすくなります。
arm32
、arm64
、x64
ディレクトリには、それぞれのアーキテクチャ向けのコードを配置します。
Makefile
を作成することで、手元でも、CIでも同じ手順で手軽にビルドできるようになります。
├── .git ├── .github │ └── workflows │ └── build.yml ├── Makefile ├── README.md ├── arm32 │ └── sample.c ├── arm64 │ └── sample.c └── x64 └── sample.c
Makefileの作成
Makefileの内容を紹介します。 次のMakefileは、x64のUbuntu 22.04上でビルドすることを想定しています。 冒頭では、ビルド先のディレクトリを指定しています。
:
の手前に記載されているのは、ターゲット名です。
make <ターゲット名>
で、そのターゲットに対応する処理が実行されます。
その後に記載されているのは、そのターゲットに対応するビルド処理です。
対象のアーキテクチャによって使用するコンパイラが異なるため、それに応じて記載するコマンドが異なります。
通常Makefileには、ビルドしたバイナリを実行する部分は記載しないのですが、今回は動作確認の手間を省くために実行する部分も記載しています。
ARM32/ARM64バイナリの実行には、QEMUを利用しています。
ARM32_BUILD_DIR = build/arm32 ARM64_BUILD_DIR = build/arm64 X64_BUILD_DIR = build/x64 # Makefile for ARM32 target arm32_binary: mkdir -p $(ARM32_BUILD_DIR) arm-linux-gnueabihf-gcc -marm -o $(ARM32_BUILD_DIR)/insert-pld arm32/sample.c echo "Running ARM32 binaries" QEMU_LD_PREFIX='/usr/arm-linux-gnueabihf/' qemu-arm-static ./$(ARM32_BUILD_DIR)/sample # Makefile for ARM64 target arm64_binary: mkdir -p $(ARM64_BUILD_DIR) aarch64-linux-gnu-gcc -o $(ARM64_BUILD_DIR)/sample arm64/sample.c echo "Running ARM64 binaries" QEMU_LD_PREFIX='/usr/aarch64-linux-gnu/' qemu-aarch64-static ./$(ARM64_BUILD_DIR)/sample # Makefile for x64 target x64_binary: mkdir -p $(X64_BUILD_DIR) gcc -o $(X64_BUILD_DIR)/BogusControlFlow x64/sample.c -lm echo "Running x64 binaries" ./$(X64_BUILD_DIR)/sample clean: rm -r $(X64_BUILD_DIR) $(ARM32_BUILD_DIR)
このMakefileはmakeコマンドによって、実行できます。 ターゲットを指定することで、そのアーキテクチャ向けのバイナリをビルドできます。
$ make arm32_binary # ARM32向けのバイナリをビルド $ make arm64_binary # ARM64向けのバイナリをビルド $ make x64_binary # x64向けのバイナリをビルド $ make clean # ビルドしたバイナリを削除
GitHub Actionsの設定
GitHub Actionsの設定内容を.github/workflows/build.yml
に記載します。
この設定では、main
ブランチにコミットがプッシュされる度に、このワークフローが実行されます。
buildジョブは、Ubuntu上で実行します。
冒頭では、リポジトリのコードを使用できるように、actions/checkout@v4
を利用してコードをチェックアウトしています。
その後、ARM向けのGCC、QEMUをインストールしています。Intel x64向けのGCCはデフォルトでインストールされています。
環境のセットアップが完了したら、Makefileを利用してARM32/ARM64/x64向けのバイナリをビルドします。
最終的にはバイナリが格納されているbuild
フィルダをアップロードします。
name: Build x64 and ARM Binaries on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Install ARM GCC run: sudo apt install -y gcc-arm-linux-gnueabihf gcc-aarch64-linux-gnu qemu-user-static - name: Build ARM32 binary run: make arm32_binary - name: Build ARM64 binary run: make arm64_binary - name: Build x64 binary run: make x64_binary - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: binaries path: | build
使い方
リポジトリを作成し、各ファイルを配置します。 GitHubにリポジトリをプッシュすると、GitHub Actionsがビルドジョブを実行します。 ビルドが成功すると、ビルドしたバイナリがダウンロードできるようになります。
リポジトリのActions
タブをクリックすると、ビルドジョブの実行結果を確認できます。
ダウンロードしたコミットの実行結果を選択し、各実行結果ページよりArtifacts
からバイナリをダウンロードできます。
まとめ
本記事では、GitHub Actionsを利用して、x64とARM32/ARM64のバイナリをコミット毎にビルドする環境を構築しました。 CIを活用することで、アンチディスアセンブルや難読化の実験を行う際に、手元でビルドする手間を省くことができます。 また、ビルドしたバイナリをダウンロードできるようになるため、比較もしやすくなります。 この記事が、マルチアークテクチャでのアンチディスアセンブルや難読化の実装を行なっている方の参考になれば幸いです。