株式会社Ninjastars 技術研究部

「もっと楽しく安全なゲームの世界を創る」

REST API形式のサーバ型メモリ改竄ツールを作った話

株式会社Ninjastars
セキュリティエンジニア:一瀬健二郎

今回は弊社の紹介を兼ねて脆弱性診断用に作成しているツール(Android用)のお話をさせて頂きます。
内容はタイトルの通りですが、プロセス解析機能をAPI化してPC側からアクセスして操作するタイプとなります。
オリジナルのツールを作成することで得られる学びや楽しさを感じ取っていただければ幸いです。
※あくまで紹介であり、ツールを公開する予定もありません。

f:id:Ninjastars:20201001210205p:plain
Go言語でREST形式のサーバを実装する。

※オリジナルのThe Go gopherGopherくん)は、Renée Frenchによってデザインされました。

実装

対象OS:Android
サーバ:GO、C
CGOを使い、一部C言語で実装しました。
Goでのルーティング機能にはgorilla/muxを使いました。

出来たもの

APIの例

端末のURLとポート番号を指定し、リクエストを送ることでデータの送受信が可能です。

・GET:/processes
内容:json形式でプロセスリストを返す。

[{"Pid":1,"Name":"/init/init /init"},{"Pid":311,"Name":"/init/init /sbin/ueventd"},...]

・POST:/maps
内容:指定プロセスIDの/proc/pid/mapsの情報をjson形式で返す。

[{"Start":4194304,"End":5304320,"Name":"/init","Permission":"r-xp"},{"Start":5369856,"End":5394432,"Name":"/init",...]

・POST:/readmem
内容:指定プロセスIDのメモリを読み込みバイト列を返す。

・POST:/writemem
内容:指定プロセスIDのメモリに値を書き込む。

・POST:/firstscan
内容:指定プロセスIDのメモリを検索し、ヒットしたアドレスを返す。

・POST:/nextscan
内容:firstscanでヒットしたアドレスから更に絞り込んだアドレスを返す。
etc

PC側のツール

GUIで操作する機能(C#)。
CUIモード(Python)。
スクリプティング機能(Python)。

f:id:Ninjastars:20201002130651p:plain:w300
プロセスメモリエディタ風のGUIツール
f:id:Ninjastars:20201002132419p:plain:w400
libil2cpp.soのメモリダンプを取るpythonスクリプト

API化することによって実装の効率化や解析の自動化が可能となります。

他のプロセスのメモリを読み書きする

以前このブログではLinux/Androidでptraceや/proc/pid/memを使い他プロセスのメモリを読み書きする手法について紹介しました。

[Linux ptraceによる手法]
自作ゲーム:チートチャレンジ - 株式会社Ninjastars 技術研究部
自作ゲーム:チートチャレンジ2 - 株式会社Ninjastars 技術研究部

[Android /proc/pid/memによる手法]
Android 他のプロセスのメモリを読み書きする - 株式会社Ninjastars 技術研究部

Linux系OSでは他にもprocess_vm_readv/process_vm_writev等を使う手法も存在します。
AndroidNDKでは直接呼び出せないため、syscall関数経由で呼び出す必要があります。
色々理由はありますが、Androidの場合メモリの読み込みはprocess_vm_readvを使い、書き込みはptraceを使うのがお勧めです。

メモリ読み機能の実装(C)

ssize_t process_vm_readv(pid_t pid,
                         const struct iovec *local_iov,
                         unsigned long liovcnt,
                         const struct iovec *remote_iov,
                         unsigned long riovcnt,
                         unsigned long flags)
{
	 return syscall(__NR_process_vm_readv,pid,local_iov,liovcnt,remote_iov,riovcnt,flags);
}

//この関数をGoから呼び出す
int ReadProcessMemory(int pid,unsigned long long lpAddress, void* buffer, int size)
{
	int bread = 0;
	struct iovec local,remote;

	local.iov_base = buffer;
	local.iov_len = size;
	remote.iov_base = (void*)lpAddress;
	remote.iov_len = size;
	bread = process_vm_readv(pid, &local, 1, &remote, 1, 0);
	return bread;
}

プロセスメモリを検索する

Linux/Androidでは/proc/pid/mapsで対象プロセスのメモリマップを読み取ることが出来ます。
アカツキ様のブログに詳細に記載されています。
hackerslab.aktsk.jp

今回はラビン・カープ文字列検索アルゴリズム等を使わず、力任せに検索する方法で実装しました。

まとめ

以前同じようなツールをC言語TCPのソケット通信を使い、構造体のバイトサイズ等を厳密に指定してサーバを実装していました。
Goを使いREST形式のサーバ化することにより簡略化出来るのには大変感動しました。
またCGOを使えばGoからCも呼び出せるので、より低レイヤーな部分はCで実装するということも出来ます。
私自身プログラミングやアルゴリズムについて語れるほど詳しくはありませんが、開発をしていてとても楽しかったです。
実用性はおいて、勉強や診断効率化に自作ツールを作ってみるのは大変お勧めです。

注意事項

本レポートに記載されている内容を許可されていないソフトウェアで行うと、場合によっては犯罪行為となる可能性があります。そのため、記事の内容を試す際には許可されたソフトウェアに対してのみ実施するようにしてください。

本レポートについて
お問い合せ
E-mail:ichise@ninjastars-net.com

株式会社Ninjastarsエンジニア
一瀬健二郎