Linux ソフトウェアハッキング

LinuxカーネルをGUIデバッグする手順【Windows上の仮想環境で動作するOSの中身を解析】

2020年12月20日

ソフトウェアハッキング

  • GUIでLinuxカーネルをデバッグ実行するにはどうすればいいのだろう?

今回はLinuxカーネルをGUIデバッグする手順について解説します。

注意事項

  • Ubunt 14.04.5 LTS、Linux kernel version4.5で構築する場合を例とします。
  • ホストOSはWindows10 64bit版を前提とします。

用意するもの

用意するものは以下の通りです。

LinuxカーネルをGUIデバッグする手順

LinuxカーネルをGUIデバッグする手順は次の通りです。

  1. CygwinをホストOSにインストール
  2. VMWareをホストOSにインストール
  3. UbuntuインストールイメージでVMWare仮想環境を作成
  4. Linuxカーネルの最適化オプションを外す
  5. LinuxカーネルをUbuntu上でビルド
  6. ビルド時のソースコードをホストOSの任意の場所に格納
  7. DS5をホストOSにインストール
  8. DS5のデバッグ用プロジェクト作成
  9. DS5のプロジェクトを実行してアタッチ
  10. カーネルモジュールもデバッグできるようにする

順番に見ていきましょう。

①CygwinをホストOSにインストール

DS5のプロジェクトからCygwinのgdbを起動し、VMWareで起動しているLinuxにアタッチするためにCygwinが必要です。

ホストOSにCygwinをインストールするため、<https://www.cygwin.com/>からsetup-x86_64.exeをダウンロードして実行します。

01-Cygwin-Install

「Install form Internet」を選択します。

02-Cygwin-Install

Cygwinのインストールフォルダを指定します。

03-Cygwin-Install

インストール用一時ファイルの作成場所を指定します。

04-Cygwin-Install

「Direct Connection」を選択します。

05-Cygwin-Install

Cygwinパッケージ取得元のサーバーを指定します。

特にどのサーバーでもいいみたいですが、サーバーごとにインストール速度が変わるようです。

06-Cygwin-Install

インストールするパッケージを選択します。

gdbが含まれている「Devel」を"Default"→"Install"に変更します。

それ以外は"Default"のままでよいです。

07-Cygwin-Install

パッケージのインストールが完了するまで待ちます。

08-Cygwin-Install

パッケージのインストールが完了すれば以下の画面が表示されます。

09-Cygwin-Install

最後に、ホストOSの環境変数にCygwinのパスを追加します。

10-Cygwin-Install

②VMWareをホストOSにインストール

以下のサイトで「VMware Workstation Player」をダウンロードし、ホストOSにインストールします。

<https://www.vmware.com/jp/products/workstation-player.html>

VMWare-top

③UbuntuインストールイメージでVMWare仮想環境を作成

以下の手順に基づき、UbuntuインストールイメージでVMWare仮想環境を作成します。

Ubuntuインストールイメージをダウンロード

以下のサイトで「日本語版 Ubuntuインストールイメージ(iso)」をダウンロードします。

<https://www.ubuntulinux.jp/products/JA-Localized/download>

Ubuntu-top

Ubuntuをインストール

VMWareを起動し、「新しい仮想マシンの作成」を選択します。

そして、「インストーラ ディスク イメージ ファイル」でダウンロードしたUbuntuインストールイメージを選択します。

VMWare-Virtual-Machine

仮想マシン設定

メモリは1GB以上、ハードディスクは30GB以上に設定していれば問題ないでしょう。

プロセッサは、以下のように「プロセッサ コアの数:1」に設定します。

VMWare-config-1

プロセッサ コアの数を2以上にしてしまうと、ステップ実行した時、別のプロセッサで動いているプロセスの実行箇所にいきなり飛んだりしてしまいます。

④Linuxカーネルの最適化オプションを外す

「linux-4.5.tar.gz」などで検索し、Linuxカーネルソースコード一式をダウンロードしましょう。

例えば、以下のミラーサイトにlinux-4.5.tar.gzが格納されています。

<http://ftp.riken.jp/Linux/kernel.org/linux/kernel/v4.x/linux-4.5.tar.gz>

linux-4.5.tar.gzを解凍するため、仮想環境(Ubuntu OS)を起動してログインし、/usr/srcの直下にlinux-4.5.tar.gzを格納します。

そして、ターミナルを開きます。

まず、スーパーユーザーでログインします。

sudo su

linux-4.5.tar.gzを解凍します。

cd /usr/src

tar -xvzf linux-4.5.tar.gz

そして、以下のコマンドを実施し、Makefileに定義されている最適化オプションを外します。

cd linux-4.5

find ./ -type f -name Makefile|xargs -n 1 sed -i.bak -e "s/-g -O2/-g3 -O0/g"

なお、最適化オプションを外した状態でLinuxカーネルをビルドすると、コンパイルエラーが出る場合があります。

最適化オプションを外したことが原因である可能性が高いので、コンパイルエラーが出た箇所だけ最適化オプションを追加して再ビルドしましょう。

たとえば、コンパイルエラー箇所の先頭と末尾に以下のコードを追加します。

/* add O1 option start */
#pragma GCC push_options
#pragma GCC optimize ("O1")

// コンパイルエラーが出た関数、またはファイル全体を囲む。

#pragma GCC pop_options
/* add O1 option end */

⑤LinuxカーネルをUbuntu上でビルド

Linuxのビルドに必要となるパッケージをインストールします。

apt-get install -y build-essential kernel-package libssl-dev ncurses-dev libncurses-dev fakeroot dpkg-dev

現在のLinuxカーネルと同じバージョンでビルドする場合、以下のコマンドを実行して設定ファイルを現在の設定で上書きします。

cp -vi /boot/config-`uname -r` .config

カーネルの設定を変えたい場合は、以下のコマンドを実行して設定を変更します。

※詳細は「https://www.itmedia.co.jp/help/tips/linux/l0347.html」を参照。

make menuconfig

Linuxカーネルをビルドします。

(環境によってはかなり時間がかかります)

time fakeroot make-kpkg --initrd kernel-image kernel-headers

Linuxカーネルのビルドが完了したら、以下のコマンドを実行して次回起動時にビルドしたLinuxカーネルが動作するようにします。

cd ../

dpkg -i linux-headers-4.5.0_4.5.0-10.00.Custom_i386.deb

dpkg -i linux-image-4.5.0_4.5.0-10.00.Custom_i386.deb

Linuxを再起動します。

reboot

Linuxの再起動後、「uname」コマンドを実行してビルドしたLinuxで動作しているかを確認します。

実行例

root@ubuntu:~# uname -a
Linux ubuntu 4.5.0 #21 SMP Mon Feb 19 02:07:45 AEDT 2018 i686 i686 i686 GNU/Linux

⑥ビルド時のソースコードをホストOSの任意の場所に格納

ビルド時のLinuxカーネルのソースコードを、ビルドしてできたvmlinuxファイルと一緒にホストOSの任意の場所に格納します。

Linux-vmlinux

もし、フォルダのサイズが10GB以上など大きすぎる場合は、以下のコマンドで一時ファイルの「.oファイル」を削除しておきましょう。

find /usr/src/linux-4.5 -type f|grep ".*\.o$" |xargs -I {} rm -f {}

⑦DS5をホストOSにインストール

ARM Development Studio 5 (DS- 5) Community Edition(CE)をダウンロードし、ホストOSにインストールします。

<https://developer.arm.com/tools-and-software/embedded/legacy-tools/ds-5-development-studio/downloads>

ARMDevelopmentStudio5

ライセンス(無料)の設定

(設定しなくても問題ないかもしれませんが)ライセンス(無料)の設定手順を解説します。

まず、以下にアクセスし、画面右側に表示されている「Activation Code」を控えておきます。

<https://developer.arm.com/tools-and-software/embedded/legacy-tools/ds-5-development-studio/editions/customized-editions/intel-soc-fpga/community-edition>

01-License-Code

次に、DS5を起動し、「ヘルプ」→「ARM License Manager」を選択します。

ARM License Manager画面が表示されるので、「ライセンスの追加」を選択します。

01-ARM-License-Manager

ライセンスタイプは「ライセンスファイル、ライセンスサーバ、シリアル番号、またはアクティブ化コードの使用」を選択します。

02-ARM-License-Manager

アクティブ化コードを指定します。

03-ARM-License-Manager

ネットワークインターフェースを選択します。

04-ARM-License-Manager

開発者のアカウント情報を設定して「終了」を選択すると、ライセンスが生成されます。

アカウントがない場合は、下図の「アカウントがない場合は、ここをクリックして作成してください。」のリンクから無料で作成できます。

05-ARM-License-Manager

ライセンスの生成が完了すると、下図のようにライセンスが登録された状態になります。

06-ARM-License-Manager

⑧DS5のデバッグ用プロジェクト作成

DS5で「実行」→「デバッグの構成」を選択します。

以下のデバッグ構成画面が表示されますので、「C/C++ リモート・アプリケーション」をダブルクリックして新規構成を作成します。

作成した新規構成の「メインタブ」→「C/C++ アプリケーション:」に、Linuxカーネルのビルドにより作成された「vmlinux」への絶対パスを設定します。

ARMDevelopmentStudio5-config-1

「デバッガータブ」→「メインタブ」に遷移し、以下を設定します。

  • GDBデバッガー:gdb
  • GDBコマンド・ファイル:.gdbinit

ARMDevelopmentStudio5-config-2

「デバッガータブ」→「接続タブ」に遷移し、以下を設定します。

  • ホスト名またはIPアドレス:localhost
  • ポート番号:後述

ポート番号には、仮想マシン(vmxファイル)の起動時、仮想マシンと同じ階層に出力される「vmware.log」の「VMWare Player is listening for debug connection on port <ポート番号>」の値(下図の赤文字)を設定します。

ARMDevelopmentStudio5-project-port

⑨DS5のプロジェクトを実行してアタッチ

仮想マシンを起動し、仮想環境上でUbuntu OSが起動した状態にしておきます。

DS5のプロジェクトを実行するために、「実行」→「デバッグの構成」→「<作成したデバッグ構成名称>」→「デバッグ」を選択すれば、仮想環境上のUbunt OS(Linuxカーネル部分)にアタッチします。

2回目以降は、下図のように「実行」→「デバッグ履歴」→「<作成したデバッグ構成名称>」を選択すればよいです。

ARMDevelopmentStudio5-start

ただ、初めはソースファイルのパスを登録していない状態のため、アタッチしても「ソースが見つかりませんでした」という警告が表示されるはずです。

ソースファイルをデバッグ構成に追加するため、「ソース・ルックアップ・パスの編集」を選択します。

01-ソース・ルックアップ・パスの編集

「追加」を選択します。

02-ソース・ルックアップ・パスの編集

「パス・マッピング」を選択します。

03-ソース・ルックアップ・パスの編集

「追加」を選択し、「コンパイル・パス:」にビルド時のソースファイル格納フォルダパス、「ローカル・ファイル・システム・パス:」にホストOSにコピーしたソースファイル格納フォルダパスを指定します。

04-ソース・ルックアップ・パスの編集

05-ソース・ルックアップ・パスの編集

ブレークポイントを設定したい場合は、ブレークしたい行の行番号左側を右クリックし、「ブレークポイント型」を「C/C++ ブレークポイント」に設定しておきます。

01-ブレークポイント設定

その後、デバッグを一時停止した状態で任意の場所にブレークポイントを貼れば、デバッグ再開後、その処理が実行されたタイミングでブレークします。

DS5では、ステップ実行時に以下の情報を確認できます。

  • レジスタ
  • 変数
  • メモリ
  • コールスタック
  • 逆アセンブル

ARMDevelopmentStudio5-debug-1

ステップ実行をしていくと、以下の「レジスタ」「変数」表示のように、変更された設定値がピンク背景になります。

ARMDevelopmentStudio5-debug-pink

また、下図右下のように、メモリの現在設定値を表示できます。

下図では、スタックポインタ($esp)の位置のメモリ設定値を表示しています。

ARMDevelopmentStudio5-debug-memory

なお、下図赤枠の「命令ステップ・モード」を選択状態にすると、C言語の命令単位ではなく、アセンブラ言語の命令単位でステップ実行できます。

ARMDevelopmentStudio5-debug-step

⑩カーネルモジュールもステップ実行できるようにする

カーネルモジュール(koファイル)もステップ実行できるようにしたい場合は以下の設定を行います。

まず、DS5でデバッグ中のプロジェクトを一時停止します。

そして、DS5のデバッグコンソールで以下のコマンドを実行します。

add-symbol-file <ホストOS上のkoファイルの絶対パス> <ロードされたkoモジュールの番地(0x・・・)>

koモジュールの番地には、下図の例のように「/proc/modules」に定義されているkoモジュールに紐づく値(仮想アドレス空間にロードされたkoモジュールの番地)を設定します。

Linux-proc-modules

もし、ステップ実行したいkoモジュールが複数ある場合、以下の手順でもよいです。

  • 「.gdbinit」ファイルにkoモジュールのシンボル読み込み処理を記述する。
  • DS5のbinフォルダ(下図参照)の下に「.gdbinit」ファイルを格納する。
  • DS5でデバッグ用プロジェクトを実行する。

ARMDevelopmentStudio5-debug-ko-config

ただし、仮想アドレス空間にロードされるkoモジュールの番地は、Linuxを再起動するたびに変化してしまいます。

このため、「.gdbinit」ファイルの中身を自動で作成するツールを自作し、Linuxの起動時に「.gdbinit」ファイルを作成する仕組みを入れておく方がよいです。

例えば、「/etc/rc.local」に自作したツールの起動処理を追記すれば、起動時に「.gdbinit」ファイルが作成できます。

まとめ

LinuxカーネルをGUIデバッグする手順について解説しました。

一度GUIのデバッグ環境を作ってしまえばLinuxカーネルのデバッグが楽になります。

LinuxカーネルをデバッグしながらOSがどのように動いているかを理解していきましょう。

-Linux, ソフトウェアハッキング