連載 [第6回] :
  脆弱性診断の現場から

クロスコンパイルできるC言語のビルド&実行環境をGitHubActionsとQEMUで作る

2024年12月27日(金)
小竹 泰一
第6回の今回は、GitHub Actionsを利用して、x64とARM32/ARM64のバイナリをコミット毎にビルドする環境の構築方法を解説します。

はじめに

株式会社ステラセキュリティ 取締役CTOの小竹(@tkmru)です。

「ARM環境でディスアセンブルを妨害するテクニック」シリーズでは、アンチディスアセンブルを施されたC言語のコードを紹介しました。

⚫︎「ARM環境でディスアセンブルを妨害するテクニック」

⚫︎「32ビットのARM環境のみで有効なディスアセンブルを妨害するテクニック」

アンチディスアセンブルや難読化の実験を行う際には、少しコードを変更する度に、ビルドしてバイナリを確認しています。しかし、そんな作業を手元でちまちま行うのは面倒です。そのため、私はコードを少し変更する度に、CIを利用してコミット毎に自動でバイナリがビルド&実行されるようにしています。変更前のバイナリもダウンロードできる状態で残るので、比較もしやすくなります。

本記事では、GitHub Actionsを利用して、x64とARM32/ARM64のバイナリをコミット毎にビルドする環境を構築します。

全体像

ディレクトリ構成は次のようになります。.gitはコミットを管理するためのディレクトリです。Gitリポジトリを作成すると自動で作成されます。.github/workflowsには、GitHub Actionsの設定ファイルを配置します。Gitリポジトリには、必要に応じて、README.mdを作成しておくと、他の人が内容を理解しやすくなります。

arm32arm64x64ディレクトリには、それぞれのアーキテクチャ向けのコードを配置します。 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を利用しています。

make
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よりバイナリをダウンロードできる

おわりに

GitHub Actionsを利用して、x64とARM32/ARM64のバイナリをコミット毎にビルドする環境を構築しました。CIを活用することで、アンチディスアセンブルや難読化の実験を行う際に、手元でビルドする手間を省くことができます。また、ビルドしたバイナリをダウンロードできるようになるため、比較もしやすくなります。この記事が、マルチアークテクチャでのアンチディスアセンブルや難読化の実装を行なっている方の参考になれば幸いです。

※本記事は、株式会社ステラセキュリティが配信しているコンテンツ「Sterra Security Tech Blog」からの転載です。
元記事についてはこちらをご覧ください。

株式会社ステラセキュリティ 取締役副社長 CTO
大学卒業後、株式会社ディー・エヌ・エーに入社し、セキュリティエンジニアとして活躍。その後、株式会社アカツキに1人目のセキュリティエンジニアとして入社し、脆弱性診断内製化、セキュリティチーム組成に尽力。 著書に『ポートスキャナ自作ではじめるペネトレーションテスト』『マスタリングGhidra』(いずれもオライリー・ジャパン)、『リバースエンジニアリングツールGhidra実践ガイド』(マイナビ出版)

連載バックナンバー

セキュリティ技術解説
第6回

クロスコンパイルできるC言語のビルド&実行環境をGitHubActionsとQEMUで作る

2024/12/27
第6回の今回は、GitHub Actionsを利用して、x64とARM32/ARM64のバイナリをコミット毎にビルドする環境の構築方法を解説します。
セキュリティ技術解説
第5回

32ビット/64ビットの両方のARM環境で有効なディスアセンブルを妨害するテクニック

2024/11/22
第5回の今回は、ドキュメントが少ない32ビット/64ビットのARM環境におけるアンチディスアセンブル技術の使い方を解説します。
働き方技術解説
第4回

アップロード機能の検証のためのファイルを作成するツール「dummy」を作った話

2024/10/18
第4回の今回は、脆弱性診断やQAに役立つ静的ファイルを作成するコマンドラインツール「dummy」の使い方を解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています