凧揚げの様子.

TL;DR:

 新年なので凧揚げをやる.
 具体的にはCylon.jsを用いてLeap MotionからRolling Spiderを操作し,ついでにSlackに通知する.
 依存関係は次の通り.

1
# npm install cylon cylon-keyboard cylon-leapmotion cylon-rolling-spider node-slack

Cylon.js

 Cylon.jsはロボティクス向けに開発されているJavaScriptのラッパーライブラリ.
 マイコンボードやドローン,スマートウォッチなど,さまざまなガジェットのSDKを統一されたインターフェイスで利用できる.反面,バグを踏み抜くと少ししんどい.
 たとえばキーボードに接続するには,次のように書く.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Cylon = require('cylon');
Cylon.robot({
connections: {
'keyboard': { adaptor: 'keyboard' }
},
devices: {
keyboard: {driver: 'keyboard', connection: 'keyboard'}
},
work: function (my) {
my.keyboard.on('a', function(key) {
my.log("A PRESSED!");
});
}
}).start();

 これでお手軽キーロガーのできあがりだ.内部ではkeypressというモジュールが呼ばれる.

Leap Motion

 Leap Motionは人間の手の動きを入力に変換できるモーションセンサー.
 以前から研究室に転がっていたものの,これまでケツ叩きゲームにしか使われてこなかった.
 Cylon.jsで複数のガジェットを扱うときは,connectionsdevicesに追記していけばよい.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Cylon = require('cylon');
Cylon.robot({
connections: {
'keyboard': { adaptor: 'keyboard' },
'leapmotion': { adaptor: 'leapmotion' }
},
devices: {
keyboard: {driver: 'keyboard', connection: 'keyboard'},
leapmotion: { driver: 'leapmotion', connection: 'leapmotion'},
},
work: function (my) {
my.leapmotion.on('gesture', function(g) {
my.log(g.type.toString());
if (g.type == "circle") {
...
}
});
}
}).start();

 これで手の動きが取れる.内部ではleapjsというモジュールが呼ばれる.
 次はドローンだ.

Rolling Spider

 Rolling SpiderはParrot社の小型ドローン.というとすぐドローンの定義は自律航行可能であるとかないとかクアッドコプターという呼称がどうとか言いだすやつがいるけど,ここではドローンと呼んでいる.
 それでは,Rolling SpiderをLeap Motionで操作してみよう.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
var Cylon = require('cylon');
Cylon.robot({
connections: {
'keyboard': { adaptor: 'keyboard' },
'leapmotion': { adaptor: 'leapmotion' },
'rolling-spider': { adaptor: 'rolling-spider', uuid: '***' }
},
devices: {
keyboard: {driver: 'keyboard', connection: 'keyboard'},
leapmotion: { driver: 'leapmotion', connection: 'leapmotion'},
drone: { driver: 'rolling-spider', connection: 'rolling-spider' }
},
work: function (my) {
my.leapmotion.on('gesture', function(g) {
my.log(g.type.toString());
if (g.type == "circle") {
my.drone.takeOff();
} else if (g.type == "swipe" && !flag) {
var isHorizontal = Math.abs(g.direction[0]) > Math.abs(g.direction[1]);
if(isHorizontal){
if(g.direction[0] > 0){
my.drone.right();
} else {
my.drone.left();
}
} else {
if(g.direction[1] > 0){
my.drone.up();
} else {
my.drone.down();
}
}
} else if (g.type == "keyTap") {
my.drone.land();
}
});
my.keyboard.on("down", function() {
my.drone.land();
});
my.keyboard.on("m", function() {
my.drone.emergency();
});
my.keyboard.on("q", function() {
Cylon.halt();
});
}
}).start();

 とりあえずこれで動く.指を回すとドローンが飛び上がり,スワイプに応じて移動する.内部ではnode-rolling-spiderというモジュールが呼ばれる.
 複数台の機体に接続できるから,ドローンの編隊飛行も夢ではない.とはいえ,Bluetoothを経由した中央集権的な制御なので,自律飛行にはいたらないが.ほんとうに自律させたければ,ドローン側のファームウェアに手を入れる必要がある.Parrot社が公開しているRollingSpiderEduによると,どうやら共有ライブラリを用いてドローンのファームウェアに機能を追加できるようだ.いずれはレクサスのPVのように美しく飛ばしてみたいが,いっそドローンから自作したほうがいいかもしれないな.

 なおRolling Spiderの総重量は65gなので,空港等の周辺や人口中心地区の上空でのドローン飛行を制限する改正航空法の対象とはならない.

Slack

 凧揚げもChatOpsの時代.
 node-slackというモジュールと,ドローンのコールバック機能を用いれば,バッテリー残量をSlackに通知できる.SlackのWebHooks URLに投げるだけだ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Cylon = require('cylon'),
Slack = require('node-slack'),
slack = new Slack('https://hooks.slack.com/services/***');
...
work: function (my) {
...
my.drone.on('battery', function(){
slack.send({
text: ':battery:: ' + my.drone.getBatteryLevel() + '%',
channel: '#drone',
username: 'Drone'
});
});
...

 こんなふうに通知される.

 ちなみに,ドローンが移動するたびに通知なんかしていると「短期間に送信されたメッセージが多すぎるから表示しないよ!」と怒られる.

その他の凧揚げ

 Leap Motionによるドローンの操作はたぶんこの人が初.

 やっぱデカいドローンを飛ばしたいっすね.

おわりに

 ジェスチャーでドローンを操作できるようになったことだし,フォースの力みたいでかっこいいと思って,年始にRolling Spiderを多摩川の土手で飛ばしていたらドローン嫌いっぽい人にクソ怒られた.すいません.
 あとドローンといえば『伊藤計劃トリビュート』所収の藤井太洋「公正的戦闘規範」がおもしろいぞ!!!

謝辞

  • Leap Motionは@shunki9から借りた.
  • Rolling SpiderはGehirn Inc.に買ってもらった.

 本稿は慶應義塾大学SFC村井&徳田研 Advent Calendar 2015の19日目である.

TL;DR:

 村井研・徳田研の卒論・修論・博論アーカイブから過去の論文タイトルを収集し,RNNLM(Recurrent Neural Network Language Model, 再帰型ニューラルネット言語モデル)[1]を用いて論文タイトルを自動生成する.

手順

  1. 論文アーカイブを雑にスクレイピング.
  2. 簡単のため英語タイトルを削る.データサイズは540行・16294字.
  3. MeCabの-Owakatiオプションで分かち書き.単語数は6272と少ない.
  4. RNNLMで学習,出力.

 生成した論文タイトルは次の通り.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
改変適応機環境におけるインターネット化の設計と実装
暮らし計算発見を利用したUDL機器
インターネットを用いた機器プローブ体系のVoDについて開発な攻撃運用手法
自律計算ネットワークの行動に-移動システムに関する研究
衛星情報を用いたビジュアライゼーション定義アーキテクチャ表をする検出ユビキタスノードのファイル構成に関する研究
あしあと適応セキュリティネットワークを用いた負荷議事取り入れたセンサNEMOMANET解決
ユーザエンティティに適した仮想な通信接近による制御対象の設計と実装
蓄積学校利用性のための再作業に関する持続
型連携ネットワークのインターネットに基づく遠隔コードの向けたデジタル機構の構築
アプリケーション:型と時間特定を用いた利用型路準拠相関の構築
自律電話教育における異種品質空間支援システムの設計と実装
位置AV端末著作授業特殊トポロジ型し分析高速構築
広域を利用した次応用におけるレイヤーな授業に関する研究プロシステムに関する研究
次制御対するカメラを利用したAd型間屋外パブリック構築
協調:不sネットワークにおけるOSストリーミングの考慮機器コミュニケーション配信システムの設計と実装
センサ環境におけるデバイスソフトウェア文字通信システム
無線作業めぐるにおけるパケットIP発信・支援機構
ELA上環境に応じたしたコンピュータ測定に関する研究
インターネット型プラットホームの遠隔パケットとシステム
無線コントロールセンサを考慮する参加参加サービスの研究
移動者における位置適応オブジェクト手法の研究
インターネットグルーピングの回覧性をシステムした経路消耗性の実現
自己情報を用いた実を量子よる協調表行動流通
デジタル前遠隔しるしの情報共有支援機構
ネットワークト可視ネットワークを車的基盤」関数の分析
環境Computing通信DVB携帯性に関する移動同期利用の設計と実装
マルチパスFunction無線センサノードを用いた技術マルチ支援支援促進に関する研究
スマート世代ネットワークにおける相互ブリッジ家電システムの設計と実装
インターネット情報のための興味無支援システムの構築
オペレーティング型IP環境のプロファイリング的な信頼に関する研究
実方向自動ポロジにおける同期機器性における研究
センサ-抽出生成を支援情報登録抽出機構の構築
アドホックOSにおけるOS環境のGlass性基盤機構の構築
DV地アプリケーションの者による最適いアルゴリズム
商上のための選択なMobile効率システムの構築
ユビキタス世代環境における効率Wireless情報のMarkit
系列演奏位置スレッドサービス二マッチングマルチプロアクティブモデルの構築
パケット適応付け利用を考慮したネットワークブログモデルの設計と実装
TranS情報環境におけるグループホスト同期への安全履歴と分散
の情報対象でのMediatorコラボレーションに関する研究
類似Allシステム
情報グループを利用するデジタル制御動画像配送システムの構築
RFIDを利用したネットワークのプラットフォーム転送に関する研究
周辺上セキュリティにおけるコンテキスト者基無線の提案に関する研究
Link型服行動RCSにおける効率しない機構
クラシック型車載インターネットを解決した分散音声なりすましシステム
Dynamic特徴解析に基づく仮想制御機構の設計と実装
多段CSMAにおける多様な解析管理機構の設計と実装
移動機:を用いた-コンテンツシステムの構築
技術センサ環境におけるホーム化による経路サーバ
アプリケーション回線に基づく薦環境におけるする作成への行動利用に関する研究
インターネットを用いたしたな動画ルールに関する研究
機器協調利用Efficientのネットワーク解決のエンド映像構築
計算情報を利用した家電抽象の迷い連携への構築
関連情報教育における動的メールイベント収集の研究
通知におけるデータベース方式購買によるした状態時間グループに関する研究
コンテキスト:6のためのための設計と実装
電子ネットワークのための提案と実装
自己電話を利用したオブジェクト分散型授業属性制御機構の実現
ネットワークの抽出化環境の向上圧縮システムの構築
Networks対戦ネットワークにおける家庭テリトリー手法の構築
広バイト依存患者のための収集及び積極システム
IPコンテキスト精度点のデータベース的と-発見環境動画像モデル行動-
通信作業システムの含む属性センサノード付け可能メディアの提案と開発
情報世代環境時による最適視点環境機構の設計と実装
協調蓄積モバイルバッテリ支援Shumu機器型設置インシデントIrma保護量
分散回線にセンサデータ適応をマッチングと基準化に関する研究
環境さ時複数用方式と審議に関する研究
自律リンク環境におけるインターネット・属性品質
オブジェクトしにおけるインタフェース技法化推薦機構の研究
インターネットにおける光を用いた最適遠隔支援環境の構築
モーバイルs利用とへの実現自動手法の設計と実装
インターネットに適した時端末型TCP型データ基準の内
インターネットを用いた受信・辞書収集
DOS的利用環境におけるbased配信システムの構築
i:と利用した情報モーダルの作成に関する研究
APIの情報におけるソフトウェアするをシステムの自律
インターネットを用いたセル内回避手法アプリケーションFunction選択制御機構
マルチ:ユーザ時のLooking支援競合の参加情報に関する研究
マルチネットワークによる動的リモコンアーキテクチャの分析に関する研究
インターネット:文字を含むと対策軽減地理ファイルの構築
アプリケーションのデジタル行動環境における負荷教育の設計と実装
移動制御型メディア取り入れたにおける自律トポロジモデルの提案
日本回線時におけるセンサのアーキテクチャ
『世代sionを支援情報ユーザの実現
Mobileの行動空間に基づくするコンポーネントシステムの設計と構築
キットライブラリのアドレス利用を支援するアプリケーションの行動に関する研究
間:環境における認証検出の考察
鍵盤を用いた基盤経路トポロジ支援手法の研究
RFIDネットワークを用いたとユーザ
自律型機的な支援レイヤー手法の設計と実装
アドホックを用いたデジタル最適暗号システムの研究
WWW人IPvシステムの動的2を化フローインフォメーション配送化手法
次的機タフェース6システムの実現
IPレイヤーネットワークにおけるグループ取得遠隔システムに関する研究
二通信ユニバーサルにおける複数共有再についての研究
Tapirus上の情報的実現あるの構築
環境上における仮想指機構の構築
インターネットにおけるRFID経路エンドノードデータベース付けモデルの設計と実装
2回線学習への効率的型環境の提案と構築
...

 これはRNNLMが生成した論文タイトルを100件無作為抽出したもの.全リストはgistに置いている.この程度のデータサイズでRNNLMの性能を云々するのはいささか危うい気がするが,論文タイトルの末尾はえてして「~の構築」「~の研究」「~の実現」になるというルールを獲得できている,ということだろうか.
 同じデータセットから,ありがちな2単語プリフィックスのマルコフ連鎖で生成した論文タイトルは次の通り.

1
2
3
4
5
6
7
8
9
10
11
注目した二点間接続基盤ソフトウエアの構築インターネット
地域における高等教育協力手法の研究ホームネットワークにおける多様
かつスケーラブルな識別子管理システムゲームコンソールに対応する管理
運用基盤分析に関する研究ポリシ経路制御に関する研究大
任意の物理的量子ビットに対する効率的な情報閲覧
脳疲労検知システムの提案次世代インターネット経路制御を用い
鍵交換機構の実装と評価初等中等教育におけるWWW
プロセスデザイン片方向ブロードキャストメディアを用いたユビキタスコンピューティング環境に適し
連携システムの設計と実装exPhoto:周辺機器と撮影
に関する研究分散環境におけるプロアクティブ制御方式に関する研究アドホック
...

 パッと見でRNNLMが生成した論文タイトルの方がより自然に思える.で,RNNLMって何なの?

RNNLM

 読んで字のごとくRNNLMはRNNの言語モデルへの応用である.
 RNNは内部に有向閉路をもつニューラルネット.系列データの各時刻$t$につき1つの入力$x^t$をとり1つの出力$y^t$を返す.ふつう順伝播型ニューラルネットは入力1つをとり1つの入力を与える写像を表現するが,RNNは任意の系列–すなわち過去のすべての入力から任意の系列への写像を表現する.
 これは時刻$t-1$の隠れ層の出力を,時刻$t$の隠れ層の入力に与えることで実現される.

 文章は系列データであり,文章に含まれる各単語は直前の単語の並びに依存している.そこで,1990年のエルマンネット[2]以来,RNNを用いた文章の分析が試みられてきた–近頃「RNNは深層学習の一種」というような言辞を見かけるが,RNN$\in$深層学習ではない
 RNNLMと既存手法との相違点は,単語を潜在空間に写像して単語の意味を獲得しようとしているところだ.
 上図において隠れ層のベクトルは単語の潜在ベクトルとその履歴より$\begin{split}s(t) =& f(Uw(t) + Ws(t-1))\end{split}$となる.次の単語の確率は$\begin{split}y(t) =& g(Vs(t))\end{split}$となる.
 ここで活性化関数$f(z)$は標準シグモイド関数で,$g(z)$はソフトマックス関数$\dfrac {e^{zm}} {\Sigma _{k}e^{zm}}$である.
 学習では確率的勾配降下法を用いて重み$U$, $W$, $V$を更新していくが,このときRNNLMは各層を時間方向に展開し,最後の時刻$t$から誤差逆伝播計算をおこなう(BPTT, Backpropagation through time法).
 ここにおいてRNNは多層の順伝播型ニューラルネットのようにみなせる.

 BPTT法において,ある時刻$t$における出力層の誤差は正解ベクトル$d(t)$から出力$y(t)$を引いた出力誤差$e_{o}(t)$と,時刻$t+1$から伝播してきた誤差の和となる.ここでたとえば$V(t+1) = V(t) + s(t)e_{o}(t)^t\alpha - V(t)\beta$$\alpha$は学習率であり,各層で行列の勾配に掛かる.$\beta$はL2正則化の係数.
 このBPTT法で長い系列を扱うとき勾配が消失してしまう問題[3]があり,解決策としてLSTM(Long Short-Term Memory, 長・短期記憶)[4]が提案されているが,割愛する.
 なお今回のパラメータは次の通り.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
last probability of validation data: -441.610349
number of finished iterations: 14
current position in training data: 0
current probability of training data: -441.576456
save after processing # words: 0
# of training words: 6272
input layer size: 1295
hidden layer size: 200
compression layer size: 0
output layer size: 1096
direct connections: 0
direct order: 3
bptt: 6
bptt block: 10
vocabulary size: 1095
class size: 1
old classes: 0
independent sentences mode: 1
starting learning rate: 0.100000
current learning rate: 0.000195
learning rate decrease: 1

おわりに

 恥知らずのクソ野郎なので何番煎じともわからない記事を書いてしまった.元ネタはNLP論文ネタ一覧
 このほか青空文庫やなろう小説[5]をRNNに学習させてはいるが,LSTM込みでもいまだ人間が見て自然に思える文章の生成は難しく思える.
 いずれは論文タイトルばかりか論文の内容も自動生成して知の欺瞞をもう一発カマしたいのだが.SFC自体が知の欺瞞っぽいところはさておき.
 物語の自動生成なら円城塔がなんとかしてくれるだろう.

参考文献

 本稿は情報セキュリティ系論文紹介 Advent Calendar 2015の11日目である.

TL;DR:

 テイント解析(taint analysis)やプログラムスライシング(program slicing),記号的実行(symbolic execution)やファジング(fuzzing)といったバイナリ解析技術を総動員して,マルウェアが通信するC&CサーバのフィンガープリントをC&Cサーバと通信せずとも生成する手法–AUTOPROBE[1]を紹介する.

背景

 マルウェアによるサイバー犯罪はC&Cサーバやコンフィグファイルの配布元,二次検体の配布元など様々な悪性サーバ(malicious server)からなるインフラによって支えられている.攻撃者はこれらの悪性サーバを頻繁に閉鎖・移転させて対策から逃れようとする.とりわけ攻撃者の命令を送信してマルウェアの動作を決定するC&Cサーバはその後の攻撃の起点となるため早期発見が望ましい.したがって不審なサーバがC&Cサーバかどうか判定するフィンガープリントが必要となる.
 しかしながら従来のフィンガープリント生成手法にはC&Cサーバとの長期的な通信を前提としている[2],サーバとして動作するマルウェアを前提としている[3]といった問題点があった.

問題定義

 あるマルウェアのファミリFに属する検体Pを入力として受け取り,Fが用いるC&Cサーバのフィンガープリントφを出力することがAUTOPROBEの目的である.C&Cサーバ側のコードは参照できず,マルウェアの検体はシンボル情報やソースコードを含んでいなくともよい.またマルウェアは複数のリクエストをC&Cサーバに送信するものとする.

提案手法

 あるサーバにマルウェアと同様のリクエストを送って,マルウェアがコマンドとして解釈できるレスポンスが返ってきたら,そのサーバは疑いようもなくC&Cサーバである–AUTOPROBEの鍵となる発想は至極単純だ.
 AUTOPROBEは検体の命令・API・システムコールの実行を–おそらくQEMUによって–トレースし,リクエストを生成する処理とレスポンスをハンドルする処理をそれぞれマルウェアから抽出する.つづいて前者によって不審なサーバに対するリクエストを生成し,後者によってレスポンスをハンドルする.レスポンスハンドリングの可否をもってフィンガープリントとするから,その処理の抽出さえできれば不審なサーバがC&Cサーバかどうか判定するまで実際のC&Cサーバと通信する必要はない–でもどうやって?

リクエスト生成

 マルウェアの多くは時間や擬似乱数,OS情報などの環境依存の値によって動作を変更する.たとえばWin32/LoadMoney.AFはレジストリキーの有無によって送信するリクエストを変更する.

 不審なサーバがC&Cサーバかどうか判定するためには攻撃者の期待するリクエストをより多く送信しより多くレスポンスを得たい.そこでAUTOPROBEは以下のアルゴリズムにしたがって複数の実行トレースを得る.これは条件分岐の度に直前のシステムコールを参照する,ある種の深さ優先探索として実装される.

 さらにAUTOPROBEはトレースをスライスし,システムコールの結果に依存する値を環境依存の値として決定論的な値・定数と区別する.ここで環境依存の値を書き換えれば攻撃者の期待するリクエストを送信できるようになる.また環境依存の値に影響するシステムコールの戻り値に時間,IPアドレス,擬似乱数,OS情報など200種類のラベルを設定しているとのことだ.

レスポンスハンドリング

 生成したリクエストをC&Cサーバに送信してレスポンスを得たら,AUTOPROBEはレスポンスの各バイトを記号値として扱い,実行トレースから検体の分岐制約を充足する解およびスライスθ1を得る.ここで探索の終了条件はclosesocketexitprocessに到達した場合,データを受信してから50個の条件分岐にわたってそのデータを参照するものが存在しない場合である.つづいてレスポンスとして正しく解釈できない値を検体に与え,実行トレースから分岐制約を充足する解およびスライスθ2を得る.ここでηは以下の式によって得られる実行経路の類似度である.

 bnとfnはそれぞれ各スライスに含まれるユニークなコードブロックとシステムコールの数を示す.AUTOPROBEはηが閾値10を下回ればレスポンスを正しく解釈していない実行経路とみなしてスライスを破棄し,上回れば別々のハンドラであるとしてそれぞれの分岐制約をフィンガープリントとして保持する.たとえばこんなふうに.

 C&Cサーバからレスポンスを得られなければ,AUTOPROBEは記号的実行とファジング,フラグレジスタの書き換えによる強制実行(forced execution)を併用してレスポンスに依存する実行経路を探索する.

 このファジングでは検体にランダム値を与えるが,検体が用いるアルゴリズムがHTTPなど既知のものであればエラーメッセージのハンドラを起点に探索する.
 AUTOPROBEはこのように探索したレスポンスのハンドラをフィンガープリントとするが,期待されるレスポンスが得られずさきほどのアルゴリズムによって探索した場合は以下のようにC&Cサーバの尤もらしさを算出する.

評価

 論文ではふたつのデータセットを用いてAUTOPROBEの性能を評価している.ひとつはSality, ZeroAccess, Ramnit, Bamital, Taidoorを含む37ファミリ10亜種計370検体.もうひとつはネットワーク通信の特徴量に基づいてフィンガープリントを生成する既存研究–CYBERPROBE[2]で用いられた19ファミリ. “which have been kindly provided to us by the authors of CYBERPROBE”って書いてるけどAUTOPROBEと同じメンバーじゃねえか.
 検体の実行時間は5分.

リクエスト生成

 まずはリクエストの生成から.可能であればC&Cサーバとの接続をともなうがAlexaトップ10,000サイトは除外している.ここでAUTOPROBEはC&Cサーバに接続せずとも検体のバイナリを分析してCYBERPROBEと同様の結果を得ている.

 AUTOPROBEはふたつのデータセットに含まれる56ファミリのトレースから105のリクエストを生成した.生成にかかった時間は平均13.2分.リクエストはすべてHTTPであったとのこと.

レスポンスハンドリング

 生成したリクエストのうち76件がC&Cサーバからのレスポンスを引き出した.さらにAUTOPROBEはHTTP 200レスポンスコードを含む同数のランダムなレスポンスを生成し,検体に与えた.これはレスポンスハンドリングの可否によって検体の異なる挙動を確認したいためだ.結果として76件のテストケース中71件(93%)で検体は異なる挙動を示した–期待される受信データを得たマルウェアは一般に10以上のシステムコールと50以上のコードブロックを実行する.残りの5件はC&Cサーバではなかった.つまりAUTOPROBEはマルウェアの通信先がC&Cサーバかどうか正しく判定できている.

ケーススタディ

 たとえばBamitalのリクエストはファイル名・GetVersionExによって得たOS情報・DGA (domain generation algorithm) によって生成したホスト名を含んでいた.AUTOPROBEはこれらの値が依存するシステムコールを得ている.

 C&Cサーバと接続せずともHTTP/1.1 200 OKを与えればBamitalのハンドラは分析できるとのこと.
 また標的型攻撃に用いられるTaidoorのリクエストはGetAdaptersInfoによって得たMACアドレスに依存する値を含んでいた.これによってTaidoorのC&Cサーバは感染端末のみで動作するレスポンスを送信していたようだ.
 ドライブバイダウンロード検体であるSalityのC&Cサーバはspm/s_tasks.php, logos_s.gif, 231013_d.exeというファイルをレスポンスに含んでいた.AUTOPROBEはこれらのファイルの存在するサーバをSalityのC&Cサーバとみなす.などなど.

限定的な探索

 Malware Domain Listに含まれる9,500アドレスの近傍/24サブネット・2.6Mアドレスのスキャン結果.

 VirusTotal, Malware Domain List, そしてURLQueryに発見されていないC&Cサーバを特定できている.

インターネット全体の探索

 ここではBGPからインターネット全体をスキャンし,7,100万ものHTTPサーバを発見している.

 このうちマルウェア3ファミリのC&Cサーバをどれだけ発見できるかCYBERPROBEと比較した結果.

 CYBERPROBEが40件のC&Cサーバを特定しているのにたいしてAUTOPROBEは54件のC&Cサーバを特定している.
 高度化するマルウェアが通信内容を難読化・秘匿する傾向にあることを鑑みると,CYBERPROBEのようにネットワーク通信の特徴量を用いる手法よりもAUTOPROBEのようにバイナリ解析を応用した手法こそ吟味されるべきだろう.

感想

 AUTOPROBEは折しも私が昨年度のインターンシップでほとんど同じようなテーマに取り組んでいたとき発表された.テイント解析のソースを受信データではなくシステムコールの結果に設定することで,リクエストのセマンティクスを復元しようとするAUTOPROBEの発想は野心的であり,さまざまなバイナリ解析技術を結集して未知のC&Cサーバを発見する手際は鮮やかというほかない.
 私は来年書くことになる卒業論文のテーマとして,インターネット接続のない閉環境におけるマルウェアの分析に本手法を応用できないか検討している.Ryzhykら[4]はデバイスドライバと環境(デバイスおよびOS)との相互作用を有限オートマトンを用いて表現し,デバイスドライバを半自動的に合成する手法を提案している.問題領域は異なるがこうした手法も検討したい.

用語解説

  • テイント解析
    • 任意のデータに設定したタグをルールにしたがって伝搬させることで,データ間の依存関係を分析する手法
  • プログラムスライシング
    • 任意のデータに依存する処理部分をプログラムから抽出する手法
  • 記号的実行
    • プログラムの分岐条件を充足論理問題とし,制約を充足する解を得る手法
  • ファジング
    • 開発者が意図しない入力を自動生成してプログラムに与えることで脆弱性を顕在化させる手法

参考文献

TL;DR:

 ソフトウェアの多くは外部からの入力に依存する実行パス(trigger-based code)をもつ.
 これを記号的実行(symbolic execution, シンボリック実行)などの解析手法から隠蔽する手法として,コラッツの問題を用いた線型難読化(linear obfuscation)がある[1].
 本稿ではしかし,線型難読化されたコードはコンパイラ最適化によってある程度除去できることを示す.

コラッツの問題

 コラッツの問題は数論の未解決問題のひとつである.
 任意の1でない自然数nに対して,nが偶数ならば2で割り,nが奇数ならば3倍して1を足す.この操作を繰り返していくと,どのような自然数nから出発しても,有限回の操作のうちに必ず1に到達する.
 この定理は経験則的に正しいと考えられているが,いまだ証明はなされていない.

線型難読化

 たとえば次のプログラムtr.cは外部からの入力に依存する実行パスをもつ.

1
2
3
4
5
6
7
8
9
10
11
// tr.c
#include <stdio.h>
int main(int argc, char *argv[])
{
int x=argc;
if(x==2)
printf("triggered!\n");
return 0;
}

 LLVM bitcodeレベルでのtr.cの制御フローグラフは次のようになる.

1
2
3
# clang -emit-llvm -c -g tr.c
# opt tr.bc -dot-cfg > /dev/null
# dot -Tpng cfg.main.dot > tr.png

 この単純なプログラムにたいして,コラッツの問題にもとづくループを挿入する.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// tr2.c
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = argc;
int y = x + 1000;
while(y > 1)
{
if(y % 2 == 1)
y = 3 * y + 1;
else
y = y / 2;
if((x - y > 0)&&(x + y < 4))
{
printf("triggered!\n");
break;
}
}
return 0;
}

 変数yは必ず1に到達する,いわば偽の変数である.
 難読化されたtr2.cの制御フローグラフは次のようになる.

 ループが挿入されたことによって実行パスが複雑化していることが見て取れる.

記号的実行

 では,線型難読化がどれほど記号的実行にたいして効力をもつか見てみよう.
 今回はLLVM bitcodeを扱う記号的実行ツールKLEEを用いる.
 まず次のコードを解析対象のソースに追記する必要がある.これは,変数xにたいして記号的実行を適用するという意味である.

1
2
3
4
5
6
7
@@ -3,6 +3,7 @@
int main(int argc, char *argv[])
{
int x=argc;
+ klee_make_symbolic(&x, sizeof(x), "x");
if(x==2)
printf("triggered!\n");

 次に,LLVM bitcodeを生成する.

1
2
3
4
5
6
# clang -emit-llvm -c -g tr.c
tr.c:6:5: warning: implicit declaration of function 'klee_make_symbolic' is
invalid in C99 [-Wimplicit-function-declaration]
klee_make_symbolic(&x, sizeof(x), "x");
^
1 warning generated.

 警告が出るが,気にしてはいけない.この関数呼び出しがKLEEにトラップされることになるのだ.
 ソースコードのないマルウェアなどを分析するにあたっては,IDAなどのデコンパイラでソースを出力し,型情報やシグナルハンドラなどの記述を整えたのち上記のようなコードを挿入するか,あるいはS2EやPANDAといった動的解析環境を頼ることになるだろう.
 それはさておき,KLEEを動かしてみよう.少し前までKLEEをビルドして動かすのはとても面倒だったが,いまではDocker Imageが提供されている.

1
2
# sudo docker pull klee/klee
# sudo docker run --rm -ti klee/klee

 線型難読化をおこなう前のtr.cについて記号的実行をおこなった結果を示す.

1
2
3
4
5
6
7
8
9
10
11
12
13
klee@4d625535c122:~$ time klee tr.bc
KLEE: output directory is "/home/klee/klee-out-1"
KLEE: WARNING: undefined reference to function: printf
KLEE: WARNING ONCE: calling external: printf(39707328)
triggered!
KLEE: done: total instructions = 17
KLEE: done: completed paths = 2
KLEE: done: generated tests = 2
real 0m0.032s
user 0m0.009s
sys 0m0.012s

 パスは2つしか存在しないため,32msecで解析が終わっている.
 ならば,線型難読化を施した後のtr2.cについてはどうか.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
klee@4d625535c122:~$ time klee tr2.bc
KLEE: output directory is "/home/klee/klee-out-2"
KLEE: WARNING: undefined reference to function: printf
KLEE: WARNING ONCE: calling external: printf(49859696)
triggered!
triggered!
triggered!
triggered!
triggered!
...
KLEE: done: total instructions = 285809
KLEE: done: completed paths = 158
KLEE: done: generated tests = 158
real 6m11.933s
user 3m56.240s
sys 2m14.245s

 さきほどに比べ,実行パスは158に増加し,解析に11622.9倍(!)もの時間がかかっている.
 今回の単純なプログラムでさえこのようになるならば,複数の入出力に依存する実行パスに線型難読化が施されたらどうなることか.

コンパイラ最適化

 難読化とはえてしてコンパイラ最適化の逆写像である.
 KLEEがLLVMにもとづいているということもあって,LLVMの最適化が線型難読化を除去できるかどうか興味をもった.検証してみよう.

1
# opt -O3 tr2.bc -o tr3.bc

 -O3をもって最適化した後の制御フローグラフは次のようになる.

 最初のtr.cほどではないが,いくらか単純になっていることがわかる.printf()puts()に変換されている.
 では,実行パスは減少しているだろうか.記号的実行をおこなった結果は次の通り.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
klee@4d625535c122:~$ time klee tr3.bc
KLEE: output directory is "/home/klee/klee-out-4"
KLEE: WARNING: undefined reference to function: puts
KLEE: WARNING ONCE: calling external: puts(32090720)
triggered!
triggered!
triggered!
triggered!
triggered!
triggered!
triggered!
triggered!
triggered!
KLEE: done: total instructions = 3383
KLEE: done: completed paths = 10
KLEE: done: generated tests = 10
real 0m2.845s
user 0m2.490s
sys 0m0.357s

 実行パスは10とさきほどのtr2.cよりも減少している.実行時間はtr.cの88.9倍であった.

おわりに

 線型難読化は脅威ではないことがわかった–少なくとも提唱者の思惑ほどには.
 塵も積もれば山となるように,線型難読化を多数の箇所に施せばその効力は増すだろう.しかしそれはクラスタリングなどの手法で対処される可能性を高めるだけである.もちろん,どれほどの範囲で難読化を適用すれば効果的かという閾値を探ることに価値はある.
 LLVMの-O3最適化は複数の最適化パスを組み合わせ,再帰的に適用することによっておこなわれる.どのパスが線型難読化の除去にもっとも寄与しているか調べてみるとおもしろいかもしれない(やる気がない).

参考文献

はじめに

いわゆるVM床抜きをXenで試す.

Xenのインストール

いつかインストールしたXen 4.4.1を用いた.

  • 依存パッケージ

    1
    # sudo apt-get install wget git bcc bin86 gawk bridge-utils iproute libcurl3 libcurl4-openssl-dev bzip2 module-init-tools pciutils-dev build-essential make gcc libc6-dev libc6-dev-i386 linux-libc-dev zlib1g-dev python python-dev python-twisted python-gevent libncurses5-dev patch libvncserver-dev libssl-dev libsdl-dev iasl libbz2-dev e2fslibs-dev git-core uuid-dev ocaml libx11-dev bison flex ocaml-findlib xz-utils gettext libyajl-dev libpixman-1-dev libaio-dev libfdt-dev cabextract libglib2.0-dev autoconf automake libtool check libjansson-dev libfuse-dev
  • Xenのビルド

    1
    2
    3
    4
    5
    6
    7
    # wget http://bits.xensource.com/oss-xen/release/4.4.1/xen-4.4.1.tar.gz
    # tar xzvf xen-4.4.1.tar.gz
    # cd ./xen-4.4.1
    # export C_INCLUDE_PATH=/usr/include/x86_64-linux-gnu
    # ./configure --enable-systemd --enable-githttp
    # make -j4 dist-xen
    # make -j4 dist-tools
  • DomUの設定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # sudo su
    # make -j4 install-xen
    # make -j4 install-tools
    # echo "GRUB_CMDLINE_XEN_DEFAULT=\"dom0_mem=4096M,max:4096M dom0_max_vcpus=4 dom0_vcpus_pin=true hap_1gb=false hap_2mb=false\"" >> /etc/default/grub
    # echo "/usr/local/lib" > /etc/ld.so.conf.d/xen.conf
    # ldconfig
    # update-grub
    # echo "none /proc/xen xenfs defaults,nofail 0 0" >> /etc/fstab
    # echo "xen-evtchn" >> /etc/modules
    # echo "xen-privcmd" >> /etc/modules
    # update-rc.d xencommons defaults 19 18
    # update-rc.d xendomains defaults 21 20
    # update-rc.d xen-watchdog defaults 22 23
    # reboot
  • 動作確認

    1
    2
    # sudo xen-detect
    Running in PV context on Xen v4.4.

ハイパーコールの追加

予約済みの__HYPERVISOR_xc_reserved_opに代わって39番目のハイパーコールを定義する.

  • xen-4.4.1/xen/include/public/xen.h

    1
    2
    3
    4
    5
    6
    7
    8
    @@ -100,6 +100,7 @@
    #define __HYPERVISOR_domctl 36
    #define __HYPERVISOR_kexec_op 37
    #define __HYPERVISOR_tmem_op 38
    +#define __HYPERVISOR_rdtsc_hypercall 39
    #define __HYPERVISOR_xc_reserved_op 39 /* reserved for XenClient */
    /* Architecture-specific hypercall definitions. */
  • xen-4.4.1/xen/arch/x86/x86_64/entry.S

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @@ -757,6 +757,7 @@
    .quad do_domctl
    .quad do_kexec_op
    .quad do_tmem_op
    + .quad do_rdtsc_hypercall
    .rept __HYPERVISOR_arch_0-((.-hypercall_table)/8)
    .quad do_ni_hypercall
    .endr
    @@ -805,6 +806,7 @@
    .byte 1 /* do_domctl */
    .byte 2 /* do_kexec */
    .byte 1 /* do_tmem_op */
    + .byte 1 /* do_rdtsc_hypercall */
    .rept __HYPERVISOR_arch_0-(.-hypercall_args_table)
    .byte 0 /* do_ni_hypercall */
    .endr
  • xen-4.4.1/xen/include/asm-x86/hypercall.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @@ -110,4 +110,8 @@
    arch_compat_vcpu_op(
    int cmd, struct vcpu *v, XEN_GUEST_HANDLE_PARAM(void) arg);
    +extern int
    +do_rdtsc_hypercall(
    + char* str);
    +
    #endif /* __ASM_X86_HYPERCALL_H__ */
  • xen-4.4.1/xen/arch/x86/traps.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @@ -3762,6 +3762,14 @@
    __domain_crash_synchronous();
    }
    +int do_rdtsc_hypercall(char* str)
    +{
    + unsigned long long tsc;
    + __asm__ volatile("rdtsc" : "=A" (tsc));
    + printk("str: %s, tsc: %llu\n", str, tsc);
    + return 0;
    +}
    +
    /*
    * Local variables:
    * mode: C

このハイパーコールは引数として受け取った文字列をTSCの値とともにコンソールログに出力する.有効化するには再度make -j4 dist-xenして再起動するとよい.

ハイパーコールの呼び出し

ハイパーコールの呼び出しはlibxcやlibxlによって抽象化されているが,ここではそれらが内部で参照している/proc/xen/privcmdに対してioctlを発行してみる.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// rdtsc_hypercall.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <fcntl.h>
#include <string.h>
#include <xenctrl.h>
#include <xen/sys/privcmd.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("input the param");
exit(1);
}
char *message = malloc(sizeof(char)*(strlen(argv[1])+1));
strcpy(message, argv[1]);
privcmd_hypercall_t my_hypercall = {
39, // __HYPERVISOR_rdtsc_hypercall
{(__u64)message, 0, 0, 0, 0}
};
int fd = open("/proc/xen/privcmd", O_RDWR);
if(fd < 0)
{
printf("cannot open /proc/xen/privcmd");
exit(1);
}
if(!ioctl(fd, IOCTL_PRIVCMD_HYPERCALL, &my_hypercall))
{
printf("cannot call do_rdtsc_hypercall");
exit(1);
}
return 0;
}

このプログラムを実行するとハイパーコールが呼び出されたことが分かる.

1
2
3
4
# gcc -o rdtsc_hypercall rdtsc_hypercall.c
# sudo ./rdtsc_hypercall test
# sudo xl dmesg | less
(XEN) str: test, tsc: 2835883086

おわりに

Xenに追加したハイパーコールを呼び出すことができた.
さしあたってはMirage OSからこのハイパーコールを呼び出す方法を知りたいのだが.

参考文献

はじめに

2015.08.11~15にわたって開催されたセキュリティ・キャンプ全国大会 2015に解析トラックの講師として参加した.講義では「仮想化技術を用いてマルウェア解析」と題して,QEMUをベースに開発が行われているDECAFという解析プラットフォームを用いて演習を行った.

講義資料

講義内容

演習では実際のマルウェアに用いられている解析妨害機能を備えたサンプルプログラムを扱った.素のDECAFには解析妨害機能への対策が施されていない.そこで,受講者にはDECAFのプラグインを拡張し,対策手法を実装して頂いた.
演習で用いたプログラムはGitHub上で公開している.

  • 解析妨害機能を備えたサンプルプログラム
  • DECAFプラグインのひな形

ひな形にある通り,IsDebuggerPresent()をフックするDECAFプラグインは以下のように書ける.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static DECAF_Handle isdebuggerpresent_handle = DECAF_NULL_HANDLE;
typedef struct {
uint32_t call_stack[1]; //paramters and return address
DECAF_Handle hook_handle;
} IsDebuggerPresent_hook_context_t;
/*
* BOOL IsDebuggerPresent(VOID);
*/
static void IsDebuggerPresent_ret(void *param)
{
IsDebuggerPresent_hook_context_t *ctx = (IsDebuggerPresent_hook_context_t *)param;
hookapi_remove_hook(ctx->hook_handle);
DECAF_printf("EIP = %08x, EAX = %d\n", cpu_single_env->eip, cpu_single_env->regs[R_EAX]);
free(ctx);
}
static void IsDebuggerPresent_call(void *opaque)
{
DECAF_printf("IsDebuggerPresent ");
IsDebuggerPresent_hook_context_t *ctx = (IsDebuggerPresent_hook_context_t*)malloc(sizeof(IsDebuggerPresent_hook_context_t));
if(!ctx) return;
DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 4, ctx->call_stack);
ctx->hook_handle = hookapi_hook_return(ctx->call_stack[0], IsDebuggerPresent_ret, ctx, sizeof(*ctx));
}
static void geteip_loadmainmodule_callback(VMI_Callback_Params* params)
{
if(strcmp(params->cp.name,targetname) == 0)
{
DECAF_printf("Process %s you spcecified starts \n", params->cp.name);
target_cr3 = params->cp.cr3;
isdebuggerpresent_handle = hookapi_hook_function_byname("kernel32.dll", "IsDebuggerPresent", 1, target_cr3, IsDebuggerPresent_call, NULL, 0);
}
}

現在のプロセスがデバッガのコンテキストで実行されていない場合,IsDebuggerPresent()は0を返す.ここで,IsDebuggerPresent()の戻り値を0にするには,モジュール(この場合はkernel32.dll)から戻る段階でeaxを書き換えてやればよい.

1
2
3
4
5
6
7
8
static void IsDebuggerPresent_ret(void *param)
{
IsDebuggerPresent_hook_context_t *ctx = (IsDebuggerPresent_hook_context_t *)param;
hookapi_remove_hook(ctx->hook_handle);
cpu_single_env->regs[R_EAX] = 0; // 追加
DECAF_printf("EIP = %08x, EAX = %d\n", cpu_single_env->eip, cpu_single_env->regs[R_EAX]);
free(ctx);
}

このようにDECAFのプラグインを書くことで,ゲストOSに解析用のエージェントを挿入することなくAPIの戻り値を書き換えることができる.
APIの引数を書き換えたい場合はモジュールに入る段階でコンテキスト構造体のcall_stack[]を書き換えてやればよい.

おわりに

受講者にサンドボックス開発の楽しさと難しさを実感してもらえたなら,講師として冥利に尽きる.
サンプルプログラムには4種類の解析妨害機能を実装しており,APIフックで対処できるのはうち前半2つだけとなっている.限られた演習時間の制約上,3つ目以降の解析妨害機能を回避できた受講者はいなかった.解析トラックリーダーの岩村さんから,受講者の2割がギリギリ解けないような問題を作るようにと仰せつかっていたが,やや意地悪な問題設定だったと思う.
なお,今回は拙作のサンプルを用いたが,より多くの解析妨害機能を備えたOSSにpafishがある.pafishはBackdoor.Win32.Agent.dkbp(MD5: de1af0e97e94859d372be7fcf3a5daa5)など一部のマルウェアに流用されている.

はじめに

QEMUは動的バイナリ変換を用いた完全仮想化式のハイパーバイザである.
QEMUは実行対象の命令列を逆アセンブルし,いちど中間表現に変換したうえで,ホストのアーキテクチャの命令列に変換して実行する.これによりQEMUは異なるアーキテクチャのバイナリを実行することができる.
しかしQEMUは遅かったため,アクセラレータとしてkqemuが開発された.
kqemuはユーザーモードのコードをホストのCPUに実行させる準仮想化ドライバであり,やがてKVMへと変貌を遂げた.さらにKVMはハードウェアによる仮想化支援機能を用いることで,さらなる高速化を実現した.もはやKVMにおいてQEMUはハードウェアのエミュレーションにしか用いられていない.
かわいそうなQEMU!
しかしQEMUの活躍する場面はいまだ多く残されている.たとえばマルウェア解析で.
それでも遅いのは困りものだ.QEMUはどこが遅いのだろうか.
ここではperfを用いてQEMUのボトルネックを雑に特定する.

実験

今回はARMエミュレーションについて調査した.ホスト・ゲストともOSにはUbuntu 12.04(3.2.0-23-generic)を用いた.perfでQEMUのプロファイリングを行っている間,ゲストではgccを用いてコンパイルを実行した.

  • ARM版Ubuntu 12.04のインストール

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # sudo apt-get install qemu
    # wget http://odroid.us/odroid/users/osterluk/qemu-example/qemu-example.tgz
    # tar xzf qemu-example.tgz ./zImage
    # wget http://releases.linaro.org/12.04/ubuntu/precise-images/developer/linaro-precise-developer-20120426-86.tar.gz
    # tar xzf linaro-precise-developer-20120426-86.tar.gz
    # qemu-img create -f raw rootfs.img 3G
    # mkfs.ext3 rootfs.img
    # mkdir mnt
    # sudo mount -o loop rootfs.img mnt
    # rsync -a binary/boot/filesystem.dir/ mnt/
    # sudo umount mnt
  • QEMUのビルド

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # sudo apt-get build-dep qemu
    # git clone git://git.qemu-project.org/qemu.git
    # cd qemu
    # git log | grep '^commit' | head -1 | awk '{print $2}'
    6169b60285fe1ff730d840a49527e721bfb30899
    # git submodule update --init dtc
    # git submodule update --init pixman
    # ./configure --extra-ldflags=-pg --target-list=arm-softmmu
    # make
  • perfのインストール

    1
    2
    # sudo apt-get install linux-tools
    # sudo echo 0 > /proc/sys/kernel/perf_event_paranoid
  • perfによるプロファイリング

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # perf record -a -g -F100000 qemu/arm-softmmu/qemu-system-arm -M vexpress-a9 -m 512 -kernel zImage -sd rootfs.img -append "root=/dev/mmcblk0 rw physmap.enabled=0 console=ttyAMA0" -monitor stdio
    WARNING: Image format was not specified for '../../rootfs.img' and probing guessed raw.
    Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
    Specify the 'raw' format explicitly to remove the restrictions.
    QEMU 2.3.90 monitor - type 'help' for more information
    (qemu) audio: Could not init `oss' audio driver
    (qemu) q
    [ perf record: Woken up 37 times to write data ]
    [ perf record: Captured and wrote 9.465 MB perf.data (~413514 samples) ]
    Failed to open /tmp/perf-6993.map, continuing without symbols
    Failed to open [vmxnet], continuing without symbols
    Failed to open [vmci], continuing without symbols
    Failed to open [vsock], continuing without symbols
    no symbols found in /bin/dash, maybe install a debug package?
    no symbols found in /usr/bin/xargs, maybe install a debug package?
    no symbols found in /usr/bin/updatedb.mlocate, maybe install a debug package?
    no symbols found in /usr/bin/dpkg-query, maybe install a debug package?
  • Flame Graphsによる可視化

    1
    2
    3
    4
    # wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/stackcollapse-perf.pl
    # wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/flamegraph.pl
    # perf script> perf_data.txt
    # perl stackcollapse-perf.pl perf_data.txt|perl flamegraph.pl --title "Flame Graphs - qemu-system-arm" > flamegraphs-qemu-system-arm.svg

perfの結果をFlame Graphsを用いて可視化すると下図のようになった.Flame GraphsはUSENIX LISA’2013にて発表された可視化ツールである.

x軸は時系列,y軸はコールスタックを意味する.横幅が広く上位に位置する関数がボトルネックであるといえる.
よってボトルネックはclear_page()すなわちTCGの各関数で発生しているページフォルトである.

おわりに

ページフォルトだけは勘弁してほしい.

参考文献

はじめに

田中久美子『記号と再帰: 記号論の形式・プログラムの必然』(“Semiotics of Programming,” Cambridge University Press, 2010.)は,プログラミング言語の記号論についての記念碑的な書籍である.かねてよりPeter B. Andersenらによってコンピュータ記号論(computional semiotics)は語られていたものの,プログラミング言語については議論が追い付いていなかった.
本書はまず,対応関係が不明瞭であったソシュールの記号論とパースのそれとを,関数型パラダイムとオブジェクト指向パラダイムとの対比をもって整理する.その上で,記号の本質は再帰にある(p.1)として,絵画,プログラミング言語,哲学を横断した考察が展開される.とまあ随分と衒学的な書籍なのだが,それゆえ危うさを孕んでいるようにも思う.ここではあえて,本書の批判とはいかないまでも,いくつかの不満点を提示したい.

汎記号主義

本書は「記号を媒介することなく対象を人間が認識できない」(p.27)とされ,「記号の解釈を記号系の中だけで捉える」(p.26)という汎記号主義を前提としている.この妥当性について考えても埒が明かないので,ひとまずはそういうことにしておこう.

HaskellとJavaによるプログラム例

本書で用いられるプログラミング言語は,HaskellとJavaである.
以下はHaskellによる平面上の長方形,楕円,円の面積を計算するプログラムである(p.15).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
data Shape = Rectangle Double Double
| Ellipse Double Double
| Circle Double
area (Rectangle width height) = width * height
area (Ellipse width height) = pi * width * height / 4.0
area (Circle radius) = area (Ellipse (radius * 2.0)(radius *2.0))
main = let
r = Rectangle 5.0 8.0
u = Ellipse 3.0 4.0
v = Circle 3.0
ss = [r, u, v]
in
for (\s -> putStr("area: "++show (area s)++"\n")) ss
for f [] = do return ()
for f (s:ss) = do { (f s); for f ss}

いきなり不安になってくる.Haskellについてはずぶの素人の私だが,それでもこのような場面ではforMmapMを用いるべきだと知っている.「このfor関数は,次の図2.2のJavaプログラムとの整合性をふまえて定義されている」(p.17)とあるが,首を傾げざるを得ない.なおmapはp.141に至るまで登場しない.
続いて,同様にJavaによるプログラムが示される(p.18).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Shape{
double width, height;
Shape (double w, double h){ width = w; height = h; }
public double area () { return width * height; }
}
class Rectangle extends Shape{
Rectangle (double w, double h){ super(w, h); }
}
class Ellipse extends Shape{
Ellipse (double w, double h){ super(w, h); }
public double area(){ return Math.PI*width*height/4.0; }
}
class Circle extends Ellipse{
Circle(double r){ super(r*2.0, r*2.0); }
}
void run(){
Rectangle r = new Rectangle(5.0, 8.0);
Ellipse u = new Ellipse(3.0, 4.0);
Circle v = new Circle(3.0);
Shape [] ss = new Shape[]{r, u, v};
for (Shape s : ss){ putStr("area: " + s.area() + "\n"); }
}

これらのプログラムは異なるパラダイム,異なる記法によって書かれているが,しかし同じ出力を得ることができる.ここに本書は,ソシュール二元論とパース三元論を託つける.

ソシュール二元論とパース三元論

ソシュールとパースはそれぞれ記号について以下のように整理した.

  • ソシュール二元論
シニフィアン シニフィエ
“tree”
ラベル 内容
  • パース三元論
表意体 解釈項 対象
“tree” 木の解釈
ラベル イデア 対象

この対応について,本書は,パース「記号は人に働きかけ,人の心の中に等価な記号を作り出し,あるいはより発展した記号を作り出す.もとの記号が作り出すこの記号のことを私は解釈項と呼ぶ」を根拠に(p.40),「解釈項は記号の解釈を呼び,記号過程を生成するもので」あり「これに対応するものがソシュールの記号モデルの中にあるならば,それはシニフィエ以外にありえない」(p.41)と導く.
続いて,「記号の差異は他の記号と対比されてはじめて立ち現れるものである.とすると,記号の解釈にまつわる記号の使用はソシュールの記号モデルの構成要素には含まれてはいなが,全体論的価値として位置付けられていると考えることができる」(p.41)ことから,「ソシュールのシニフィエはパースの直接対象に対応し,そしてパースの解釈項はソシュールの記号モデルに外在する全体論的価値に内包される」(pp.41-42)という仮説を提示する.

全体論的価値

関数型パラダイムとオブジェクト指向パラダイムの比較を通じて仮説を検証する前に,この「全体論的価値」とは何を意味するのだろうか.おそらくは意図的なのだろうが,本書にはパロールやラング,レフェランといった語が一切登場しない.これによって混乱してしまったのは私だけだろうか.記号論についてもずぶの素人である私の苦し紛れの解釈では「全体論的価値」はラングに相当するが,詳細は述べられていない.ソシュールの「言語記号は恣意的です」という原理も後に登場する(p.57)が,社会的慣習と全体論的価値が絡められることはなかった.

ソシュール二元論とパース三元論の接続

さて,ソシュールとパースの関係は,先に挙げたプログラム例のarea関数のあり方によって整理される.

プログラミング言語 Haskell Java
記号論 二元論 三元論
area関数 外在的 内在的

Haskellのプログラム例では,areaは他のデータ構造に外在するが,Javaのプログラム例では内在する(p.42).言い換えれば,Haskellにおいては記号がその使用によって意味付けられるが,Javaにおいては記号の定義がその使用を内在する.つまり,次のような対応関係になる(p.49).

  • ソシュールのシニフィアンはパースの表意体に対応する.
  • ソシュールのシニフィエはパースの直接対象に相当する(心的な対象).
  • パースの解釈項は識別子の使用に相当する.パースの記号モデルでは,記号の使用は解釈項として記号モデルの中に埋め込まれ,記号過程は解釈項を次々と呼ぶことによって生成される.ソシュールにおいては,記号過程は記号が別の記号に呼ばれ,それがまた別の記号に呼ばれることにより生成される.ある記号の使用によって記号に付される価値は記号モデルに外在し,それは記号を併置した際の差異として現れる.

こうして,ソシュールとパースの論が接続された.なるほど綺麗にまとまったかのように思える.

プログラミングパラダイム

しかし,これはプログラミングパラダイムから見て妥当だろうか.HaskellやJavaに固有の特徴と,パラダイムの特徴を同一視していないだろうか.本書において,HaskellやJavaを選択した基準は最後まで明確に示されない(私はJavaScriptが良いのではないかと思った).
CTMCPこと『コンピュータプログラミングの概念・技法・モデル』(“Concepts, Techniques, and Models of Computer Programming, “ The MIT Press, 2004.)のPeter Van-Royによると,オブジェクト指向パラダイムとは関数型パラダイムに状態(state)を追加したものである.私はこの考えに沿った展開に期待していたのだが,しかし状態の概念はp.190に至るまでほとんどといって触れられず,HaskellとJavaの比較に絡められることはなかった.
オブジェクト指向すなわちカプセル化という前提に立っているところ,プロトタイプベースのオブジェクト指向について触れていないところも勿体無い.

モナドと状態

状態の概念は,「インタラクションを参照透明に記述しようと思うと,」「莫大な量の記号を使い捨てなければならない」(pp.198-199)からこそモナドが考案されたという流れで援用される.すなわち,参照透明性を保ちつつ状態を記述するためにモナドがある(p.196).失敗系モナドが無視されているにせよ,このあたりの説明が明快なだけに,HaskellとJavaの比較において状態の概念が持ち出されなかったことが残念に思われる.

参照の値渡し

本書はJavaを「関数に値とアドレスのどちらかを渡すのかは,再び文脈により実用論的に決まり,」「プログラマが定義して導入する複合型」は「アドレスを渡す」ものとして紹介している(p.117).これは端的に誤りである.なぜなら,Javaはあらゆる場面において値渡しであり,参照は参照の値(reference values)として扱われるためである.

おわりに

主にプログラミングの観点から,『記号と再帰』に見られる不満点を述べた.要約すると次のようになる.

  • Haskellのコードが微妙
  • 全体論的価値の定義が不明瞭
  • HaskellとJavaを題材に選んだ根拠が不明瞭
  • Javaの参照の値渡しについての説明が不適切

だからといって私は本書が駄本であると切り捨てたいわけではない.
シニフィアンを持たない状態で分節されたλ項にシニフィアンが付与されていく簡約の過程は大変面白かったし,再帰から是態(交換不可能性?)が立ち上るという考えも論証不足ながら興味深かった.
汎記号主義という極端な見地から,記号が投機的に記号系に導入されるからこそ再帰が成立すると言い切ることで,ここまで手際よく語ることができるのかという興奮があった.
だからこそ,(今後本書が重版されることがあればの話だが)今回述べたような不満点が解消されることを望む.
終盤に本書は,「何ら形式的な制約なく自然に作られた」自然言語の構造的な系と,「停止性が保証される最小の記号群からはじめ,停止性の制約下でボトムアップに構築される」プログラミング言語の構成的な系とを対比している(p.183).再帰による是態の獲得.構造的な系.次に語られるのはニューラルネットの記号論だろうか.

参考文献

Dynamic Binary Instrumentation

Dynamic Binary Instrumentation(DBI)とは,実行中のプログラムにコードを挿入する技術である.
その目的は,プログラムの性能評価であったり,アーキテクチャのシミュレーションであったり,プログラムのデバッグであったり,プログラムのsheparding(ポリシを用いたセキュリティ制限)であったり,プログラムの最適化であったり,テイント解析などのデータフロー解析であったり,リバースエンジニアリングであったり,マルウェア解析であったり,……と多岐にわたっている.
デバッガとDBIを区別することは難しく,ニュアンスの問題になってしまうが,DBIはより「挿入するコードがプログラマブルであること」を重視している.また,instrumentation(名詞)/instrument(動詞)という語は,コードの挿入を通してプログラムの情報を取得したり,プログラムを制御したりといったニュアンスを含んでいる.
DBIのメリットとして,言語に依存しないこと,古いソフトウェアに対しても適用できること,再コンパイルの必要がないこと,動的に生成されるコードを扱うことができること,実行中のプロセスにアタッチできることが挙げられる.

Pin

Intelによって開発されているPinは,DBIフレームワークの中でも最も成功している一つであり,そのAPIの数は450種類を越える.
Pinにおけるinstrumentationの手法は,コードの実行時にinstrumentするjust-in-time Instrumentation(JITI)と,イメージ(実行ファイルや共有ライブラリ,構造体)のロード時にinstrumentするahead-of-time-insturumentation(AOTI)の二種類に大別される.
これらのinstrumentにあたって,Pinは解析対象のプログラムにpinvm.dllを挿入する.

JITI

JIITは以下の三種類を単位として行うことができる.

Instruction Level

第一に,最も低い粒度として,命令単位のinstrumentationがサポートされている.
これは,INS_AddInstrumentFunctionのコールバックとして実現される.関連するAPIは,Pin 2.1.3の時点で142種類存在する.

Basic Block Level(BBL)

第二に,Basic Block(BB)単位のinstrumentationがサポートされている.
ここでのBBとは,一つの入口と一つの出口を持ち,内部に分岐を含まない命令列のことであり,コンパイラ最適化におけるそれと同義である.Pinでは,BBL_InsHeadBBL_InsTailBBL_NextBBL_Prevといった関数を用いてBBの有向グラフを操作することができる.一方でBBL_AddInstrumentationFunctionのコールバックは存在しない.関連するAPIは,Pin 2.1.3の時点で14種類存在する.

Trace Level

第三に,Trace単位のinstrumentationがサポートされている.
Traceとは,Pin独自の単位であり,分岐命令を入り口とし,無条件分岐(jmp, call, retなど)を出口とする命令列のことである.つまり,Traceは複数のBBを含みうる.これは,TRACE_AddInstrumentFunctionのコールバックとして実現される.関連するAPIは,Pin 2.1.3の時点で14種類存在する.
Pinでは,objdumpなどと同様に線型分析法によってバイナリを逆アセンブルし,Traceを取得する.ここで,JITIに用いるVMをトラップすることなく別のTraceに分岐を行うためのtrace-linking optimization[PDF]が行われる.そのため,Trace単位のinstrumentationが命令単位のinstrumentationよりも高速になる場合がある.

AOTI

AOTIは以下の二種類を単位として行うことができる.

IMG instrumentation

第一に,IMG単位のinstrumentationがサポートされている.
IMGは,特定の実行ファイルないし共有ライブラリ,構造体に相当する単位である.これは,IMG_AddInstrumentFunction APIによって実現される.また,IMGのセクション(SEC)についてもinstrumentを行うことができる.関連するAPIは,IMGについて27種類,SECについて16種類が存在する.

RTN instrumentation

第二に,RTN単位のinstrumentationがサポートされている.
RTNとは関数(Routine)に相当する単位である.これは,RTN_AddInstrumentFunction APIによって実現される.関連するAPIは,Pin 2.1.3の時点で39種類存在する.

Transparency

このように様々な機能を備えるPinだが,マルウェア解析においてはどれほど有用なのだろうか.
Pinのinstrumentationは,素朴なDLLインジェクションによって成り立っており,Anti-Debuggingについては考えられていない.そのためPinは,親プロセス,引数,ロードされるpinvm.dll, エクスポート関数であるCharmVersionC, ZwAllocateVirtualMemoryの実行権限,KiUserApcDispatcher, KiUserCallbackDispatcher, KiUserExceptionDispatcher, LdrInitializeThunkのインラインフック,命令のパターン,セクション名,FSTENV命令,FSAVE命令,FXSAVE命令,……といった様々な要素から検出される危険性を孕んでいる.
これらは,eXaitを用いてテストできる.なお,筆者の環境ではint2e/SYSENTERについてfalse positiveが発生した.
Pinを検出するマルウェアの存在は寡聞にして知らないが,Pinをマルウェア解析に用いるのであれば,現状の構造は不適切である.
Anti-Debuggingのイタチごっこから脱するためには,エージェントをゲストOSに挿入するin-VMではなく,エージェントをゲストOSに挿入しないout-of-VMな解析環境でなければならない.

QEMU

さらにPinにはカーネルへのinstrumentationが不可能であるという欠点が存在する.
翻ってQEMUは,フルシステムエミュレーションを行うため,アーキテクチャに依存することなくカーネルへのinstrumentationを行うことができる.しかしながら,QEMUのソースコードは難解であり,PinのようにユーザーフレンドリーなAPIが提供されていない.QEMUはC言語によるメタプログラミングとオブジェクト指向の良質なサンプルであるとも言えるが,できることなら避けて通りたい道である.

TEMU

QEMUにテイント解析機能を搭載し,これまで多くの研究で用いられてきたTEMUは,QEMUのソースコードを理解するための労力を削減することに成功した(と,後述するPEMUの論文にすら書かれている)が,APIの充実度はPinに劣っている.また,カーネルモジュールをゲストOSに挿入する必要があり,マルウェアから検出される可能性があること,いまや時代遅れのQEMU 0.9.1をベースとしていることから,現代的なマルウェア解析環境の候補からは外れつつある.
後継のDECAFはQEMU 1.0.0をベースとしており,カーネルモジュールを必要としないものの,APIの充実度はTEMUと変わらない.

PEMU

こうした流れを汲んで開発されたのが,VEE’15にて新たに発表されたPEMUである.嬉しい事にオープンソースとして提供されている.
Pinはin-VMであり,マルウェアの解析やカーネルへのinstrumentationに適さない.QEMUやTEMUはout-of-VMだが,Pinほど充実したAPIを用いることができない.そこでPEMUは,両者の利点と欠点を踏まえた上で,以下の四点を目的として開発された.

  1. Rich APIs
    • Pinの既存APIをそのまま流用できること
  2. Cross-OS
    • WindowsとLinuxをサポート
  3. Strong Isolation
    • ゲストOSのRing 3やRing 0ではなく,out-of-VM(Ring -1)から監視(VM Introspection)を行うこと
  4. VM Introspection
    • ゲストOSのプロセスやカーネルのセマンティクス情報を取得するAPIを提供すること

これらを達成するため,PEMUはQEMUの動的バイナリ変換器Tiny Code Generator(TCG)に手を加えてPin APIのサポートを追加した.

Instrumentation Engine

さて,PEMUにおけるinstrumentationはどのようになっているのだろうか.
問題となるのは,QEMUとPinとの差異である.QEMUは命令とBB単位でしかinstrumentできず,PinにおけるTrace単位をサポートしていない.また,PEMUのベースとなっているQEMU 1.5.3では,BBの数が640までと制限されている.
そこで,PEMUではBBとTraceを対応付けるTRACE Constructorと,PinのAPIを通じてinstrumentするCode Injectorが導入された.

TRACE Constructor

TRACE Constructorは,複数のBBを組み合わせてTraceの単位に抽象化するものである.
まずXED2ライブラリを用いて,ゲストの実行前にQEMUでBBの先頭から条件分岐命令まで逆アセンブルを行う(/target-i386/PEMU/DISAS.c).スワップされたか,ロードされていない命令については,ページフォルトを通じて取得する.さらに,コードを挿入する命令の位置をglobal hooking point hash-table(HPHT)にキャッシュする(/target-i386/PEMU/hashTable.c).
ここにおけるアルゴリズムは以下のようになっている.

まず,Trace単位の開始アドレスであるPCの度にXED2を呼び出し,PCをTPCという保存領域に保存する.次に,全命令を逆アセンブルし,命令単位のinstrumentationが必要な場合はそのアドレスをHPHTに保存する.さらに,無条件分岐の度にBBとTraceを対応付け,BB単位のinstrumentationが必要な場合はそのアドレスをHPHTに保存する.そして,新しいBBを割り当て,次のTrace単位が始まるアドレスを探す.Trace単位のinstrumentationが必要な場合はそのアドレスをHPHTに保存する.そして,さらなるTraceを逆アセンブルしていくといった仕組みになる.

Code Injector

TraceとBBの対応が取れた後は,QEMUのTCGが提供するtcg_gen_helperを用いてHPHTを参照し,instrumentを行えばよい(/target-i386/PEMU/pemu_hook_helper.c).
IMGやSEC単位のinstrumentはmallocなどの引数を確認する(セマンティクス情報を復元する)ことで実現している.
全体像は以下のようになる.

Introspection Engine

PEMUでは,Page Global Directory(PGD)とCR3レジスタの対応を用いてプロセスの識別を行う(target-i386/PEMU/pemu_helper.h).ここでは,プロセス作成時に発生するCR3レジスタへのMOVを検出して利用する.終了したプロセスのCR3は再利用されるため,プロセスの終了を検出する必要があるが,これはexit syscallを監視することで実現できる.スレッドは,kernel stack pointerのマスクから識別する.
ゲストとのセマンティックギャップの解決にあたっては,一時的にゲストを停止してシステムコールを挿入する手法を用いる.PEMU_というprefixでsyscallに相当するAPIが提供され,getpid, gettimeofdayなどゲストOSの情報を取得するためのAPI 28種類と,open, fstat, lseekなどゲストOSを操作するためのAPI 15種を用いることができる.これらは都度レジスタコンテキストの退避と復元を伴って実行される.

評価

論文での性能評価では,32-bit Ubuntu 12.04(Linux kernel 3.0.0-31-generic-pae)をホストとし,Intel Core i7, メモリ8GBのマシンが用いられた.速度の評価には32-bit Ubuntu 11.04(Linux kernel 2.6.38-8-generic)がゲストとして用いられた.
その結果として,PEMUではQEMUよりも4.33倍,Pinよりも83.61倍のオーバーヘッドが生じることが明らかになっている.

また,eXaitを用いた検出テストのため,Windows XP 32bitがゲストとして用いられた.論文ではeXaitの17手法全てがPinを検出した一方で,いずれの手法もPEMUを検出することができなかった.

関連研究

PEMUがQEMUにPin APIのサポートを追加したのに対して,PinOSはXenにPin APIのサポートを追加した.だがPinOSのinstrumentationは貧弱であり,セマンティクス情報を取得するAPIが提供されていない.また,解析ルーチンがカーネルや解析対象からアクセスできるため,isolationとして不十分である.
また,PinOS以外にカーネルへのinstrumentationをサポートしているDRKは,LKMとして実装されているため,in-VMな手法に留まっている.

おわりに

こうして,Pin APIを用いてout-of-VMにマルウェアを解析するための道標が示された.
今後のPEMUを用いた研究に期待したい,と他人事のように言っている場合ではなく,そろそろ論文を書かなければ.

補足

PEMUは新しいといえどQEMU 1.5.3をベースとしており,やがてはQEMU 0.9.1をベースとしているTEMUのように技術的負債となるだろう.
そこで,PEMUをforkしQEMU 1.5.3からQEMU 2.2.1にアップデートを試みた.が,QEMU 2.2.1に至るまでcpu_single_env変数が廃止されたことと,謎のコンパイルエラーが発生することから頓挫中である.そのうち何とかする.

参考文献

耐解析機能の分類

Gabriel N. BarbosaRodrigo R. Brancoは,マルウェアの耐解析機能をAnti-Debugging, Anti-Disassembly, Obfuscation, Anti-VMの四種類に分類した.彼らはIntelのセキュリティ研究者であり,マルウェアに備わった耐解析機能の統計を過去二回に渡って発信してきた.
気になるのは,サンドボックスの実装にあたって問題となるAnti-VMだ.8,103,167もの検体を用いた調査によると,その内訳は以下のようになっている.

これらはいずれもVMware社製品を検出するための手法である.残念ながら他の仮想マシンモニタについての情報は掲載されていない

仮想マシンモニタの分類

以前にも少しばかり述べた,サンドボックスの透明性(transparency)に関する話を蒸し返そう.
サンドボックスにOut-of-the-boxなVMIを採用すれば,少なくともAnti-Debuggingについては無視できる.
では,サンドボックスにXenを用いれば,QEMUより検出されにくくなるだろうか.あるいは,その逆はどうだろうか.
多くの研究者が好き勝手なことを言っているが,仮想マシンモニタの実現手法をもってサンドボックスの透明性を語るのは些か性急だろう.なぜなら,サンドボックスの透明性はその設計ではなくマルウェアの実装によって左右されるからだ.Out-of-the-box VMIは設計の話だが,Anti-VMは実装の話だ(と思う).
ところで私は,仮想マシンモニタの実現手法を「プロセッサ拡張によるもの」と「バイナリ変換によるもの」に分類していたが,別の分類方法を見つけた.

命令セットアーキテクチャを基準に分類することで,より分かりやすくなっている.今後はこの図に準拠したい.
話を戻そう.
何が言いたいのかというと,in-the-wildなマルウェアに備わったAnti-VMの内訳が分からないことには,サンドボックスの透明性について云々することはできないということだ.

検体

誰もやっていないようなので,調べてみることにした.
やや恣意的(誤用?)な選択だが,今回はVirusShare.comからCitadelのバリアント479検体や,MandiantのAPT1レポートにて報告された中国軍総参謀部第三部第二局こと61398部隊と思しきグループ製の874検体を含む総計33,260検体を用いた.

Anti-VMの実態

YARAを用いてAnti-VMの実態を調べた.
ルールには公式のantidebug.yarを用いた.なお,このルールにはAnti-VMのみならずAnti-Debuggerについても記載されている.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# awk '{ print $1}' yara.log | sort | uniq -c | sort -r
20340 DebuggerPattern__RDTSC
18966 DebuggerPattern__CPUID
9292 DebuggerTiming__Ticks
7371 DebuggerPattern__SEH_Inits
6955 DebuggerException__UnhandledFilter
5715 DebuggerTiming__PerformanceCounter
4377 DebuggerPattern__SEH_Saves
2949 DebuggerCheck__API
2631 DebuggerCheck__QueryInfo
2182 SEH__vba
1315 DebuggerOutput__String
887 ThreadControl__Context
604 vmdetect
303 DebuggerException__SetConsoleCtrl
186 SEH__vectored
83 DebuggerHiding__Thread
80 DebuggerHiding__Active
52 DebuggerException__ConsoleCtrl
10 Check_Dlls
5 DebuggerCheck__RemoteAPI
2 DebuggerCheck__GlobalFlags
1 ▒▒DebuggerPattern__RDTSC
1 Debuggeattern__SEH_Inits
1 Check_VBox_VideoDrivers
1 Check_VBox_Description

マルウェアの開発者がよりgenericな手法を好むことは,RDTSC命令を用いる検体が61.15%, CPUID命令を用いる検体が57.02%含まれていることからも明らかだ.たった2バイトを基準に検出しているため,false positiveだらけなのだろうとはいえ.
一方でvmdetectとして計上された検体は全体の1.18%に過ぎない.これはVMware, Virtual PC, Xen, Virtual Boxに関する文字列が含まれる検体を抽出するルールであり,コードは以下のようになっている.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
rule vmdetect
{
meta:
author = "nex"
description = "Possibly employs anti-virtualization techniques"
strings:
// Binary tricks
$vmware = {56 4D 58 68}
$virtualpc = {0F 3F 07 0B}
$ssexy = {66 0F 70 ?? ?? 66 0F DB ?? ?? ?? ?? ?? 66 0F DB ?? ?? ?? ?? ?? 66 0F EF}
$vmcheckdll = {45 C7 00 01}
$redpill = {0F 01 0D 00 00 00 00 C3}
// Random strings
$vmware1 = "VMXh"
$vmware2 = "Ven_VMware_" nocase
$vmware3 = "Prod_VMware_Virtual_" nocase
$vmware4 = "hgfs.sys" nocase
$vmware5 = "mhgfs.sys" nocase
$vmware6 = "prleth.sys" nocase
$vmware7 = "prlfs.sys" nocase
$vmware8 = "prlmouse.sys" nocase
$vmware9 = "prlvideo.sys" nocase
$vmware10 = "prl_pv32.sys" nocase
$vmware11 = "vpc-s3.sys" nocase
$vmware12 = "vmsrvc.sys" nocase
$vmware13 = "vmx86.sys" nocase
$vmware14 = "vmnet.sys" nocase
$vmware15 = "vmicheartbeat" nocase
$vmware16 = "vmicvss" nocase
$vmware17 = "vmicshutdown" nocase
$vmware18 = "vmicexchange" nocase
$vmware19 = "vmdebug" nocase
$vmware20 = "vmmouse" nocase
$vmware21 = "vmtools" nocase
$vmware22 = "VMMEMCTL" nocase
$vmware23 = "vmx86" nocase
$vmware24 = "vmware" nocase
$virtualpc1 = "vpcbus" nocase
$virtualpc2 = "vpc-s3" nocase
$virtualpc3 = "vpcuhub" nocase
$virtualpc4 = "msvmmouf" nocase
$xen1 = "xenevtchn" nocase
$xen2 = "xennet" nocase
$xen3 = "xennet6" nocase
$xen4 = "xensvc" nocase
$xen5 = "xenvdb" nocase
$xen6 = "XenVMM" nocase
$virtualbox1 = "VBoxHook.dll" nocase
$virtualbox2 = "VBoxService" nocase
$virtualbox3 = "VBoxTray" nocase
$virtualbox4 = "VBoxMouse" nocase
$virtualbox5 = "VBoxGuest" nocase
$virtualbox6 = "VBoxSF" nocase
$virtualbox7 = "VBoxGuestAdditions" nocase
$virtualbox8 = "VBOX HARDDISK" nocase
// MAC addresses
$vmware_mac_1a = "00-05-69"
$vmware_mac_1b = "00:05:69"
$vmware_mac_1c = "000569"
$vmware_mac_2a = "00-50-56"
$vmware_mac_2b = "00:50:56"
$vmware_mac_2c = "005056"
$vmware_mac_3a = "00-0C-29" nocase
$vmware_mac_3b = "00:0C:29" nocase
$vmware_mac_3c = "000C29" nocase
$vmware_mac_4a = "00-1C-14" nocase
$vmware_mac_4b = "00:1C:14" nocase
$vmware_mac_4c = "001C14" nocase
$virtualbox_mac_1a = "08-00-27"
$virtualbox_mac_1b = "08:00:27"
$virtualbox_mac_1c = "080027"
condition:
any of them
}

見ての通りQEMUに関する記述が存在しない.
そこで,vmdetectに以下の様なコードを追加し,再度スキャンしてみた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
rule vmdetect
{
~ 略 ~
strings:
$QEMU1 = "Bochs" nocase
$QEMU3 = "QEMU_AUDIO_DRV" nocase
$QEMU4 = "QEMU VVFAT" nocase
$QEMU5 = "QEMU ADB Mouse" nocase
$QEMU6 = "QEMU ADS7846-driven Touchscreen" nocase
$QEMU7 = "QEMU HARDDISK" nocase
$QEMU8 = "QEMU CD-ROM" nocase
$QEMU9 = "QEMU MICRODRIVE" nocase
$QEMU10 = "QEMU-MEMORY" nocase
$QEMU11 = "QEMU_BIOS" nocase
$QEMU12 = "QEMU PS/2 Mouse" nocase
$QEMU13 = "QEMU Sun Mouse" nocase
$QEMU14 = "QEMU TSC2102-driven Touchscreen" nocase
$QEMU15 = "QEMU USB Mouse" nocase
$QEMU16 = "QEMU USB Tablet" nocase
$QEMU17 = "QEMU_VERSION" nocase
$QEMU18 = "QEMU USB Keyboard" nocase
$QEMU19 = "QEMU USB Hub" nocase
$QEMU20 = "QEMU USB MSD" nocase
$QEMU21 = "QEMU PenPartner tablet" nocase
$QEMU22 = "QEMUware SVGA" nocase
condition:
any of them
}

結果は608検体.QEMUを検出するのは4検体だけという結果になった.
自分がマルウェアを開発する立場だったらこうした文字列をそのまま埋め込むことはしないし,このルールは表層的なものでしかなく,例えばQEMUの動的バイナリ変換のスケジューリングに着目したAnti-VMを検出することはできない.
とはいえ,おかげで現状を大雑把に把握することはできた.

パッカーの実態

ついでに,PEiDを用いてパッカーの利用状況を調べた.
UserDBにはBob / Team PEiD signaturesを用いた.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# awk '{{for(i=3;i<NF;i++)printf("%s ",$i) }print($NF)}' peid.log | sort | uniq -c | sort -r
21159 Nothing found *
3065 Nothing found [Overlay] *
1412 Microsoft Visual Basic 5.0 / 6.0
916 Microsoft Visual C++ 8.0 DLL Method2
747 Microsoft Visual C++ 6.0
685 Microsoft Visual Basic 5.0 - 6.0
405 Borland Delphi 6.0 - 7.0 [Overlay]
351 UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo
333 Microsoft Visual C++ 7.0 [Overlay]
320 UPX 2.93 - 3.00 [LZMA] -> Markus Oberhumer, Laszlo Molnar & John Reiser [Overlay] *
239 Borland Delphi 6.0 - 7.0
201 Microsoft Visual C++ 6.0 [Overlay]
185 UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo [Overlay]
167 Nullsoft PiMP Stub [Nullsoft PiMP SFX] *
99 Microsoft Visual C++ 6.0 DLL
83 Microsoft Visual C++ 7.0 [Debug] [Overlay]
82 Nothing found [Debug] *
79 UPX 0.89.6 - 1.02 / 1.05 - 2.90 (Delphi) stub -> Markus & Laszlo [Overlay]
63 Microsoft Visual C++ 7.0
62 Microsoft Visual C# / Basic .NET
61 Microsoft Visual Basic 5.0 / 6.0 [Overlay]
42 Themida 1.8.x.x - 1.9.x.x -> Oreans Technologies
42 ASPack 2.12 -> Alexey Solodovnikov [Overlay]
38 ASProtect 1.2x - 1.3x [Registered] -> Alexey Solodovnikov
32 ASPack 2.12 -> Alexey Solodovnikov
31 PE Win32 DLL (0 EntryPoint)
31 Nothing found [RAR SFX] *
31 Microsoft Visual C++ 6.0 DLL [Overlay]
28 Borland C++
26 PECompact 2.x -> Jeremy Collake [Overlay]
24 Morphine 1.2 - 1.3 -> rootkit
23 MinGW GCC 3.x [Overlay] *
22 UPX 0.89.6 - 1.02 / 1.05 - 2.90 (Delphi) stub -> Markus & Laszlo
22 Microsoft Visual C# / Basic .NET [Overlay]
19 Nothing found [ZIP SFX] *
18 PECompact 2.x -> Jeremy Collake
18 Microsoft Visual C++ 7.0 [Debug]
15 Borland C++ [Overlay]
14 Borland Delphi 4.0 - 5.0
14 ASProtect 1.33 - 2.1 Registered -> Alexey Solodovnikov *
13 UPX 2.93 - 3.00 [LZMA] -> Markus Oberhumer, Laszlo Molnar & John Reiser *
13 Microsoft Visual C++ 6.0 [Debug]
13 Microsoft Visual C++ 5.0
12 UPX 0.80 - 1.24 DLL -> Markus & Laszlo
12 BobSoft Mini Delphi -> BoB / BobSoft [Overlay] *
11 Themida 1.8.x.x -> Oreans Technologies *
11 Microsoft Visual C++ Private Version 1
11 Borland Delphi 6.0
11 Borland Delphi 2.0 [Overlay]
10 BobSoft Mini Delphi -> BoB / BobSoft *
9 NsPack 2.9 -> North Star *
9 Microsoft Visual C++ 5.0 [Debug]
8 Microsoft Visual C++ 7.1 DLL
8 Microsoft Visual C++ 7.0 Method2
7 Xtreme-Protector v1.05 *
7 Microsoft Visual C++ 8.0 DLL Method2 [Overlay]
6 Xtreme-Protector v1.05 [Overlay] *
6 Microsoft Visual C++ 8.0 [Debug] [Debug]
6 ASProtect 1.2x - 1.3x [Registered] -> Alexey Solodovnikov [Overlay]
5 nSPack 3.7 -> North Star/Liu Xing Ping
5 Wise Installer Stub [Overlay] *
5 WinZip 32-bit SFX 8.x module
5 Thinstall 2.4x - 2.5x -> Jitit Software [Overlay] *
5 PureBasic 4.x -> Neil Hodgson *
5 PE Win32 DLL (0 EntryPoint) [Overlay]
5 Microsoft Visual C++ 7.1 DLL [Overlay]
4 tElock 0.98b1 -> tE!
4 Themida 1.8.x.x - 1.9.x.x -> Oreans Technologies [Overlay]
4 PureBasic 4.x -> Neil Hodgson [Overlay] *
4 PC-Guard 5.0 -> Blagoje Ceklic [Overlay]
4 MoleBox v2.0 *
4 Microsoft Visual C++ 8.0 [Debug]
4 Microsoft Visual C++ 5.0 [Overlay]
4 Borland Delphi 4.0 - 5.0 [Overlay]
4 Borland C++ 1999 [Overlay]
4 Borland C++ 1999
4 Armadillo 3.78 - 4.xx -> Silicon Realms Toolworks
4 Armadillo 1.xx - 2.xx -> Silicon Realms Toolworks
4 ASProtect 2.1x SKE -> Alexey Solodovnikov
3 nSPack 3.7 -> North Star/Liu Xing Ping [Overlay]
3 Upack 0.39 beta -> Dwing
3 UPX 0.80 - 1.24 DLL -> Markus & Laszlo [Overlay]
3 Themida 1.2.0.1 (compressed) -> Oreans Technologies [Overlay]
3 Themida 1.2.0.1 (compressed) -> Oreans Technologies
3 NsPack 3.4 -> North Star *
3 NakedPacker 1.0 - by BigBoote *
3 MinGW GCC 3.x [ZIP SFX] *
3 Microsoft Visual C++ [Overlay]
3 Microsoft Visual C++ 6.0 SPx Method 1
3 Microsoft Visual Basic 5.0 - 6.0 [Overlay]
3 Inno Setup Module Heuristic Mode [Overlay]
3 ASProtect 2.1x SKE -> Alexey Solodovnikov [Overlay]
2 tElock 0.90 -> tE! [Overlay]
2 Upack 0.28 - 0.399 (relocated image base) - Delphi, .NET, DLL -> Dwing
2 Upack 0.24 - 0.29 beta -> Dwing
2 UltraProtect 1.x -> RISCO Software Inc. [Overlay]
2 UltraProtect 1.x -> RISCO Software Inc.
2 UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo [RAR SFX]
2 SVKP 1.3x -> Pavol Cerven
2 PureBasic DLL -> Neil Hodgson *
2 PEtite 2.x [Level 0] -> Ian Luck
2 PE-Armor V0.7X -> hying *
2 PC-Guard 5.0 -> Blagoje Ceklic
2 Nullsoft Install System 2.1x [Nullsoft PiMP SFX]
2 NsPack 1.4 -> Liuxingping *
2 NTKrnl Security Suite -> NTKrnl Team
2 Morphine 1.4 - 2.7 -> Holy_Father & Ratter/29A
2 MinGW GCC 3.x *
2 Microsoft Visual C++ Private Version 1 [Overlay]
2 Microsoft Visual C++ DLL Method 1
2 Microsoft Visual C++ 7.0 Method2 [Debug] [Overlay]
2 Microsoft Visual C++ 7.0 DLL Method 3
2 Microsoft Visual C++
2 Microsoft Visual Basic 5.0 / 6.0 [Debug]
2 MASM32 / TASM32
2 EXECryptor 2.2.4 -> Strongbit/SoftComplete Development (h1) *
2 EXECryptor 1.x.x -> SoftComplete Developement
2 Borland Delphi DLL
2 Borland Delphi 6.0 [Overlay]
2 Borland Delphi 3.0
2 Borland C++ DLL Method 2 [Overlay]
1 yoda's Protector 1.3 -> Ashkbiz Danehkar
1 yoda's Protector 1.03.3 -> Ashkbiz Danehkar
1 yoda's Protector 1.03.2 -> Ashkbiz Danehkar
1 tElock v0.90 [Overlay] *
1 tElock v0.90 *
1 tElock 1.0 (private) -> tE!
1 tElock 0.61 -> tE!
1 nSpack V2.3 -> LiuXingPing *
1 eXPressor 1.3.0 -> CGSoftLabs
1 Winkript 1.0 -> Mr. Crimson/WKT
1 WinRAR 32-bit SFX Module [RAR SFX] *
1 WWPack32 1.x -> Piotr Warezak
1 Upack 0.28 - 0.399 (relocated image base) - Delphi, .NET, DLL -> Dwing [Overlay]
1 Upack 0.24 - 0.29 beta -> Dwing [Overlay]
1 UPX 1.03 - 1.04 -> Markus & Laszlo [Overlay]
1 UPX 1.03 - 1.04 -> Markus & Laszlo
1 UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus & Laszlo [Nullsoft PiMP SFX]
1 Themida 1.8.x.x -> Oreans Technologies [Overlay] *
1 Themida -> Oreans Technologies 2004 *
1 SafeDisc 2.05.030 -> Macrovision [Overlay]
1 RLPack V1.11 -> ap0x [Overlay] *
1 RLPack 1.20 Basic Edition [aPLib] -> Ap0x *
1 RCryptor v1.6d --> Vaska *
1 Pelles C 3.00, 4.00, 4.50 EXE (X86 CRT-LIB) [Overlay]
1 PKLITE32 v1.1 *
1 PEtite 2.x [Level 1/9] -> Ian Luck [Overlay]
1 PEtite 2.x [Level 1/9] -> Ian Luck
1 PEncrypt 4.0 Gamma / 4.0 Phi -> junkcode
1 PESpin 0.3x - 1.xx -> cyberbob
1 PECompact 2.xx --> BitSum Technologies [Overlay] *
1 PECompact 2.xx --> BitSum Technologies *
1 PECompact 1.40 - 1.45 -> Jeremy Collake [Overlay]
1 PEBundle 2.0x - 2.4x-> Jeremy Collake
1 PEBundle 0.2 - 3.x -> Jeremy Collake
1 PE Pack 1.0 -> ANAKiN [Overlay]
1 PE Pack 1.0 -> ANAKiN
1 PE Crypt 1.02 -> random, killa & acpizer
1 Obsidium 1.3.0.4 -> Obsidium Software [Overlay]
1 Nullsoft PiMP stub [Nullsoft PiMP SFX]
1 Nullsoft Install System 2.x [Nullsoft PiMP SFX]
1 NsPack 2.9 -> North Star [Overlay] *
1 Nothing found [CAB SFX] *
1 NSPack 3.x -> Liu Xing Ping *
1 Morphine 1.4 - 2.7 -> Holy_Father & Ratter/29A [Overlay]
1 MoleBox v2.0 [Overlay] *
1 Microsoft Visual C++ 8.0 [Debug] [Overlay]
1 Microsoft Visual C++ 7.0 Method2 [Overlay]
1 Microsoft Visual C++ 7.0 DLL Method 3 [Overlay]
1 Microsoft Visual C++ 6.0 [ZIP SFX]
1 Microsoft Visual C++ 6.0 SPx Method 1 [Overlay]
1 Microsoft Visual C++ 6.0 DLL [Debug]
1 Microsoft Visual C++ 4.x [Overlay]
1 Microsoft Visual C++ 4.x [Debug]
1 Microsoft Visual Basic 6.0 DLL [Overlay]
1 Microsoft CAB SFX module
1 LCC Win32 1.x -> Jacob Navia
1 KByS V0.28 -> shoooo [Overlay] *
1 KByS V0.22 -> shoooo *
1 InstallShield AFW [Overlay]
1 Install Stub 32-bit -> InstallShield [Overlay]
1 Inno Installer 4.0.5 [Inno SFX]
1 FSG 2.0 -> bart/xt
1 EZIP 1.0 -> Jonathan Clark [Overlay]
1 EXECryptor 1.x.x -> SoftComplete Developement [Overlay]
1 CD-Cops II -> Link Data Security [Overlay]
1 Borland Delphi DLL [Overlay]
1 Borland Delphi 5.0 KOL/MCK [Overlay]
1 Borland Delphi 5.0 KOL [Overlay]
1 Borland Delphi 3.0 [Overlay]
1 Borland Delphi 2.0 [Inno SFX]
1 Borland Delphi 2.0
1 Borland Component
1 Armadillo v1.71 [Overlay] *
1 Armadillo 3.78 - 4.xx -> Silicon Realms Toolworks [Overlay]
1 Armadillo 2.01 -> Silicon Realms Toolworks [Overlay]
1 ASProtect SKE 2.1x (exe) -> Alexey Solodovnikov [Overlay] *
1 ASProtect 2.0x Registered -> Alexey Solodovnikov [Overlay]
1 ASProtect 2.0x Registered -> Alexey Solodovnikov
1 ASProtect 1.33 - 2.1 Registered -> Alexey Solodovnikov [Overlay] *
1 ASProtect 1.2 / 1.2c-> Alexey Solodovnikov
1 ASPack 2.12b -> Alexey Solodovnikov
1 ASPack 1.08.03 -> Alexey Solodovnikov [Overlay]
1 ASPack 1.06b / 1.061b -> Alexey Solodovnikov [Overlay]
1 AHTeam EP Protector 0.3 (fake Microsoft Visual C++ 7.0) -> FEUERRADER *
1 ACProtect V2.0 -> risco *
1 ACProtect 2.00 - RISCO Software Inc. [Overlay]
1 ACProtect 1.40 - 1.41 - RISCO Software Inc.
1 .BJFNT 1.1b -> :MARQUiS:
1 * PseudoSigner 0.2 [Microsoft Visual Basic 5.0 - 6.0] --> Anorganix [Overlay] *
1 * PseudoSigner 0.2 [Microsoft Visual Basic 5.0 - 6.0] --> Anorganix *

ほとんど検出できていないのが残念.ひときわ面倒なThemidaは11検体だった.

サンドボックスの実態

サンドボックスの透明性が研究されている一方で,実際のマルウェアはあまり凝ったAnti-VMを備えていなかった.
一方で,現実のサンドボックスはどうだろうか.いくら論文で優れた手法が編み出されようが,役立てられていなければ意味がない.
そこで,一般に公開されているサンドボックスに対してシステム情報を取得するプログラムを挿入し,仮想マシンモニタの種類を判別できるかどうか試してみたのだが,実態はあまりにもお粗末だった.
詳細は言うまい.
だが,systeminfoコマンドとNtQuerySystemInformationだけで露見する代物ばかりだった.
マルウェアの解析ではなく検出を目的としたサービス,言ってしまえばVirusTotalをサンドボックスのフロントエンドとしたことが,この一因として挙げられる.
凝ったAnti-VMを備えたマルウェアがあまり多くないことも考えると,現状のサンドボックスで十分だということなのだろうか.

感想

研究をしているうちに袋小路に突入してしまい,サンドボックスの透明性やAnti Anti-VMあくまでマルウェア解析の一側面に過ぎないことを私は忘れかけていた.
本質は別のところにある.

TEQUILABOOMBOOM

公開されているサンドボックスの中にTEQUILABOOMBOOMというホスト名のものがあった.
Trojan.Win32.Inject.uljvBackdoor.Win32.Agent.dkbpなど一部のマルウェアは,このホスト名に基づいてサンドボックスを検出することが知られている.調べてみると,Question about keylogged PC - Printable Version[FUD][FREE] Agent Tesla [Keylogger] [ClipboardLogger] [On-Screen Keyboard Logger] - Page 16など,2013年の時点で情報が攻撃者の間で共有されている.

So I managed to crypt a keylogger and have my victim download it through dropbox (I think). I haven’t received any logs yet but it shows that I’m connected to a computer from france which is janettedoe@TEQUILABOOMBOOM. I’m not sure how this is possible, or if it’s some sort of error. Does anybody know what this is? My keylogger hasn’t been sent anywhere other than to my targets email.

どうやらこれはGoogleが動かしているCuckoo Sandboxが用いるホスト名らしく,攻撃者によるサンドボックスの実態調査が進んでいることを伺わせる.
サンドボックスの透明性について拘りすぎる必要はないにせよ,その情報が攻撃者の手に渡ることを前提としたサービスについては検討の余地がある.

参考文献