SENTO NO OBOEGAKI

技術よりのメモやポエムを書いていきます。

MENU

Windows 10でDynamoRIOを使ってみた

DynamoRIOを使ってみたので忘れる前にメモを書いておく。 DynamoRIOはランタイムコード操作 (変換・コード挿入など) を行うためのツールである。いわゆる、Dynamic Binary Instrumentation(DBI)ツールの一つという位置づけで知られている。 DBIツールとしてIntel Pinも有名だが、Windows10で動かない(?)ようなので今回はWindows10でも動作するDynamoRIOを使ってみることにした。

環境

  • Windows 10 Pro 64bit
  • Visual Studio Community 2015
>systeminfo
OS 名:                  Microsoft Windows 10 Pro
OS バージョン:          10.0.14393 N/A ビルド 14393
OS ビルドの種類:        Multiprocessor Free
システムの種類:         x64-based PC
プロセッサ:             1 プロセッサインストール済みです。
                        [01]: Intel64 Family 6 Model 58 Stepping 9 GenuineIntel ~1900 Mhz

インストール

インストール方法について詳しくはここを参照してほしい。 今回はビルド済みのバイナリを含んだzip版をDirect Download Linksからダウンロードする。 サンプルコードのビルド用にCMakeとVisual Studioもインストールしておく。

サンプルコードの実行

ユーザはDynamoRIOが提供しているAPIを使用してDynamoRIOクライアントを作成できる。DynamoRIO本体と組み合わせる形で使用される自作のプログラムのことをDynamoRIOクライアントと呼んでいるらしい。Windowsの場合これをDLLにコンパイルして使うことになる。

まずは最初からsamples/以下に含まれているサンプルコードを動かしてみる。サンプルコードを実行する場合は-cパラメータの後にdllファイルのパスを指定する。

>bin32\drrun.exe -c samples\bin32\opcodes.dll -- notepad

opcodes.dllは実行されたオペコードをカウントする機能が実装されており、実行するとnotepadが立ち上がり、ウィンドウを閉じると実行されたオペコードTop15が表示される。

f:id:sentoneibisu:20170906154436p:plain

ツールの実行

サンプルコードとは別にDynamoRIOにはいくつかのツール1が内蔵されている。これを実行するには-tパラメータの後にツール名を指定する。

>bin32\drrun.exe -t drstrace -- notepad

drstraceはシステムコールをトレースし、ログに書き出す。

NtQueryVirtualMemory
    arg 0: 0xffffffff (type=HANDLE, size=0x4)
    arg 1: 0x002ea880 (type=void *, size=0x4)
    arg 2: 0x0 (type=int, size=0x4)
    arg 3: 0x02abf4f8 (type=<struct>*, size=0x4)
    arg 4: 0x1c (type=unsigned int, size=0x4)
    arg 5: 0x02abf4d4 (type=unsigned int*, size=0x4)
    succeeded =>
    arg 3: <NYI> (type=<struct>*, size=0x4)
    arg 5: 0x02abf4d4 => 0x1c (type=unsigned int*, size=0x4)
    retval: 0x0 (type=NTSTATUS, size=0x4)
NtQueryVirtualMemory
    arg 0: 0xffffffff (type=HANDLE, size=0x4)
    arg 1: 0x002ea880 (type=void *, size=0x4)
    arg 2: 0x3 (type=int, size=0x4)
    arg 3: 0x02abf514 (type=<struct>*, size=0x4)
    arg 4: 0x14 (type=unsigned int, size=0x4)
    arg 5: 0x00000000 (type=unsigned int*, size=0x4)
    succeeded =>
    arg 3: <NYI> (type=<struct>*, size=0x4)
    arg 5: 0x00000000 (type=unsigned int*, size=0x4)
    retval: 0x0 (type=NTSTATUS, size=0x4)
NtQueryVirtualMemory
    arg 0: 0xffffffff (type=HANDLE, size=0x4)
    arg 1: 0x002ea880 (type=void *, size=0x4)
    arg 2: 0x2 (type=int, size=0x4)
    arg 3: 0x02abf54c (type=<struct>*, size=0x4)
    arg 4: 0x212 (type=unsigned int, size=0x4)
    arg 5: 0x00000000 (type=unsigned int*, size=0x4)
    succeeded =>
    arg 3: <NYI> (type=<struct>*, size=0x4)
    arg 5: 0x00000000 (type=unsigned int*, size=0x4)
    retval: 0x0 (type=NTSTATUS, size=0x4)
NtQueryInformationProcess
    arg 0: 0xffffffff (type=HANDLE, size=0x4)
    arg 1: 0x24 (type=int, size=0x4)
    arg 2: 0x02abf520 (type=<struct>*, size=0x4)
    arg 3: 0x4 (type=unsigned int, size=0x4)
    arg 4: 0x00000000 (type=unsigned int*, size=0x4)
    succeeded =>
    arg 2: <NYI> (type=<struct>*, size=0x4)
    arg 4: 0x00000000 (type=unsigned int*, size=0x4)
    retval: 0x0 (type=NTSTATUS, size=0x4)

サンプルコードのビルド

ここではマニュアルを参考に適当なサンプルコードsample_cli.cを書いてビルドしてみる。 sample_cli.cは実行された基本ブロックの数をカウントする最低限のコードで、エラー処理は含めていない。

sample_cli.c
#include "dr_api.h"

static void event_exit(void);
static void bbcount();
static dr_emit_flags_t event_bb(void *drcontext, void *tag, instrlist_t *bb, 
                                bool for_trace, bool translating);

static unsigned int global_count = 0;

DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
    dr_enable_console_printing();
    dr_register_exit_event(event_exit);
    dr_register_bb_event(event_bb);
}

static void event_exit(void)
{
    dr_printf("bb count: %d\n", global_count);
}

static void bbcount()
{
    global_count++;
}

static dr_emit_flags_t 
event_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating)
{
    dr_insert_clean_call(drcontext, bb, instrlist_first_app(bb), (void *)bbcount, false, 0);
    return DR_EMIT_DEFAULT;
}
CMakeLists.txt
add_library(sample_cli SHARED sample_cli.c)
find_package(DynamoRIO)
if(NOT DynamoRIO_FOUND)
    message(FATAL_ERROR"DynamoRIO package required to build")
endif(NOT DynamoRIO_FOUND)
configure_DynamoRIO_client(sample_cli)
フォルダ階層(ビルド前)

フォルダは以下の構成になっている。ここでは作成したVSプロジェクトと自作のサンプルコード置き場としてmybuild/mysamples/を事前に作っている。 sample_cli.cとCMakeLists.txtはmysamples/内のsample_cli/以下に置いている。

C:.
├─bin32
├─bin64
├─cmake
├─docs
├─drmemory
├─dynamorio
├─ext
├─include
├─lib32
├─lib64
├─logs
├─mybuild    #VSプロジェクト置き場
├─mysamples  #自作サンプルコード置き場
│  └─sample_cli
│     ├─CMakeLists.txt
│     └─sample_cli.c
├─samples
└─tools
サンプルコードのビルド手順
>cd mybuild
>mkdir sample_cli && cd sample_cli
>cmake -DDynamoRIO_DIR="..\..\cmake" ..\..\mysamples\sample_cli
>(作成されたProject.slnをVisual Stduioで開いてソリューションのビルドを実行)
#Debugフォルダにtest_cli.dll、test_cli.lib、その他もろもろが作成される
フォルダ階層(ビルド後)
C:.
├─bin32
├─bin64
├─cmake
├─docs
├─drmemory
├─dynamorio
├─ext
├─include
├─lib32
├─lib64
├─logs
├─mybuild
│  └─sample_cli
│     └─Debug
│        ├─sample_cli.dll
│        └─sample_cli.lib
├─mysamples
│  └─sample_cli
│     ├─CMakeLists.txt
│     └─sample_cli.c
├─samples
└─tools
自作サンプルコードの実行
>bin32\drrun.exe -c mybuild\sample_cli\Debug\sample_cli.dll -- notepad
bb count: 10147879

参考リンク


  1. 実際にはサンプルコードより大規模で洗練されたDynamoRIOクライアントである。

CTF for ビギナーズ 2016 弘前 Write-up

CTF for ビギナーズ 2016 弘前が、私の所属する研究室から徒歩8秒の講義室で開催されたので、参加してきました。結果は1100ptsで4位(同点が結構いましたが)でした(1位は3人で1400ptsでした)。

 

f:id:sentoneibisu:20160912133454p:plain

最後のCTF演習のwrite-upだけ簡単に書きます(※最後のbin300は時間内に解けてません)。

Misc100: README

 問題文に書かれたctf4b{}形式のflagをコピペして終わり。

 

Forensics100: みてみよう

 与えられたpcapファイルをWiresharkで開いてみてみると、HTTPでflag.pngをダウンロードしているのを見つけたのでそのファイルを抽出。開いて見ても真っ白い画像だったので、バイナリエディタで眺めてみたらflagがあった。

 

Forensics200: エクスポートできるかな?

 与えられたpcapファイルを見ると、FTPでPNGデータをやりとりしていたので、エクスポートして開くとflagが書いてた。

 

Forensics200: ICMP?

 与えられたpcapファイルを見ると、ICMPのrequestとreplyがひたすら続いていたので眺めてると、何やら明らかにサイズがでかいICMPパケットがいくつかある。ということでデータ部分をよく見ると、なぜかHTTPリクエストとHTTPレスポンスが入っている。HTTPリクエストでflag.txtを要求しているので、レスポンスのボディを見てみるとbase64っぽい文字列がありデコードするとflagが得られた。

 

Binary100: HiddenFlag

strings bin100_1 | grep ctf4b

でflagが出た。

 

Binary200: 復習

 講義でやった内容(5つのkeyを探す問題)のkeyが3つバージョン。与えられたバイナリ内の3つの関数Stage1~Stage3の戻り値(eax)がそのままkeyになるので、命令を辿っていけば分かる。

 echo -en "23\n945\n-46\n" | ./bin200_1 

でflagが出た。

 

Binary200: ASCII

 とりあえず与えられたバイナリを実行してみるとInput flag>>と出てくる。objdumpで逆アセンブルしてみるとcheck_flagという関数があるので見てみると次のように命令が並んでいる。

mov    BYTE PTR [ebp-0x28],0x63
mov    BYTE PTR [ebp-0x27],0x74
mov    BYTE PTR [ebp-0x26],0x66
mov    BYTE PTR [ebp-0x25],0x34
mov    BYTE PTR [ebp-0x24],0x62
mov    BYTE PTR [ebp-0x23],0x7b
mov    BYTE PTR [ebp-0x22],0x63
mov    BYTE PTR [ebp-0x21],0x30
mov    BYTE PTR [ebp-0x20],0x6d
mov    BYTE PTR [ebp-0x1f],0x70
mov    BYTE PTR [ebp-0x1e],0x34
mov    BYTE PTR [ebp-0x1d],0x72
mov    BYTE PTR [ebp-0x1c],0x31
mov    BYTE PTR [ebp-0x1b],0x6e
mov    BYTE PTR [ebp-0x1a],0x67
mov    BYTE PTR [ebp-0x19],0x5f
mov    BYTE PTR [ebp-0x18],0x77
mov    BYTE PTR [ebp-0x17],0x31
mov    BYTE PTR [ebp-0x16],0x37
mov    BYTE PTR [ebp-0x15],0x68
mov    BYTE PTR [ebp-0x14],0x5f
mov    BYTE PTR [ebp-0x13],0x31
mov    BYTE PTR [ebp-0x12],0x6e
mov    BYTE PTR [ebp-0x11],0x70
mov    BYTE PTR [ebp-0x10],0x75
mov    BYTE PTR [ebp-0xf],0x37
mov    BYTE PTR [ebp-0xe],0x7d
mov    BYTE PTR [ebp-0xd],0x0

ASCIIっぽい値と文字列の終端っぽい0x0を格納しているのでこれをASCIIに直したらflagが出た。

 

Binary300: HiddenFlag -returns-

 とりあえず与えられたバイナリを実行してみると

HiddenFlag returns

You cannot get flag! Haha

と言われる。objdumpで逆アセンブルしてみるとmain関数は文字列をputsで出力するだけで終わっている。しかし、main以外にgenflagという関数が一つあり、関数名からここでflagを生成しているっぽい。

08048514 <genflag>:
 8048514:    55                       push   ebp
 8048515:    89 e5                    mov    ebp,esp
 8048517:    56                       push   esi
 8048518:    53                       push   ebx
 8048519:    83 ec 40                 sub    esp,0x40
 804851c:    65 a1 14 00 00 00        mov    eax,gs:0x14
 8048522:    89 45 f4                 mov    DWORD PTR [ebp-0xc],eax
 8048525:    31 c0                    xor    eax,eax
 8048527:    c6 45 df 04              mov    BYTE PTR [ebp-0x21],0x4
 804852b:    c6 45 e0 b2              mov    BYTE PTR [ebp-0x20],0xb2
 804852f:    c6 45 e1 0f              mov    BYTE PTR [ebp-0x1f],0xf
 8048533:    c6 45 e2 47              mov    BYTE PTR [ebp-0x1e],0x47
 8048537:    c6 45 e3 33              mov    BYTE PTR [ebp-0x1d],0x33
 804853b:    c6 45 e4 84              mov    BYTE PTR [ebp-0x1c],0x84
 804853f:    c6 45 e5 7f              mov    BYTE PTR [ebp-0x1b],0x7f
 8048543:    c6 45 e6 dd              mov    BYTE PTR [ebp-0x1a],0xdd
 8048547:    c6 45 e7 44              mov    BYTE PTR [ebp-0x19],0x44
 804854b:    c6 45 e8 bd              mov    BYTE PTR [ebp-0x18],0xbd
 804854f:    c6 45 e9 d6              mov    BYTE PTR [ebp-0x17],0xd6
 8048553:    c6 45 ea 98              mov    BYTE PTR [ebp-0x16],0x98
 8048557:    c6 45 eb ad              mov    BYTE PTR [ebp-0x15],0xad
 804855b:    c6 45 ec 98              mov    BYTE PTR [ebp-0x14],0x98
 804855f:    c6 45 ed d2              mov    BYTE PTR [ebp-0x13],0xd2
 8048563:    c6 45 ee 36              mov    BYTE PTR [ebp-0x12],0x36
 8048567:    c6 45 ef 14              mov    BYTE PTR [ebp-0x11],0x14
 804856b:    c6 45 f0 f1              mov    BYTE PTR [ebp-0x10],0xf1
 804856f:    c6 45 f1 26              mov    BYTE PTR [ebp-0xf],0x26
 8048573:    c6 45 f2 85              mov    BYTE PTR [ebp-0xe],0x85
 8048577:    c6 45 f3 1b              mov    BYTE PTR [ebp-0xd],0x1b
 804857b:    c7 04 24 00 00 00 00     mov    DWORD PTR [esp],0x0
 8048582:    e8 49 fe ff ff           call   80483d0 <srand@plt>
 8048587:    c7 04 24 16 00 00 00     mov    DWORD PTR [esp],0x16
 804858e:    e8 0d fe ff ff           call   80483a0 <malloc@plt>
 8048593:    89 45 d8                 mov    DWORD PTR [ebp-0x28],eax
 8048596:    c7 45 d4 00 00 00 00     mov    DWORD PTR [ebp-0x2c],0x0
 804859d:    eb 21                    jmp    80485c0 <genflag+0xac>
 804859f:    8b 55 d4                 mov    edx,DWORD PTR [ebp-0x2c]
 80485a2:    8b 45 d8                 mov    eax,DWORD PTR [ebp-0x28]
 80485a5:    8d 1c 02                 lea    ebx,[edx+eax*1]
 80485a8:    8d 55 df                 lea    edx,[ebp-0x21]
 80485ab:    8b 45 d4                 mov    eax,DWORD PTR [ebp-0x2c]
 80485ae:    01 d0                    add    eax,edx
 80485b0:    0f b6 30                 movzx  esi,BYTE PTR [eax]
 80485b3:    e8 38 fe ff ff           call   80483f0 <rand@plt>
 80485b8:    31 f0                    xor    eax,esi
 80485ba:    88 03                    mov    BYTE PTR [ebx],al
 80485bc:    83 45 d4 01              add    DWORD PTR [ebp-0x2c],0x1
 80485c0:    8b 45 d4                 mov    eax,DWORD PTR [ebp-0x2c]
 80485c3:    83 f8 14                 cmp    eax,0x14
 80485c6:    76 d7                    jbe    804859f <genflag+0x8b>
 80485c8:    8b 55 d4                 mov    edx,DWORD PTR [ebp-0x2c]
 80485cb:    8b 45 d8                 mov    eax,DWORD PTR [ebp-0x28]
 80485ce:    01 d0                    add    eax,edx
 80485d0:    c6 00 00                 mov    BYTE PTR [eax],0x0
 80485d3:    8b 45 d8                 mov    eax,DWORD PTR [ebp-0x28]
 80485d6:    8b 4d f4                 mov    ecx,DWORD PTR [ebp-0xc]
 80485d9:    65 33 0d 14 00 00 00     xor    ecx,DWORD PTR gs:0x14
 80485e0:    74 05                    je     80485e7 <genflag+0xd3>
 80485e2:    e8 a9 fd ff ff           call   8048390 <__stack_chk_fail@plt>
 80485e7:    83 c4 40                 add    esp,0x40
 80485ea:    5b                       pop    ebx
 80485eb:    5e                       pop    esi
 80485ec:    5d                       pop    ebp
 80485ed:    c3                       ret    
 80485ee:    66 90                    xchg   ax,ax

さらっと見てみると、先ほどの問題と同じようにmov BYTE PTR [ebp-0xYY],0xZZのような命令が並んでいる。しかし、ASCIIではない。そこでもう少し詳しく見ていくと、どうやらrand()で得られる乱数と1byteずつxorして、malloc()で確保した領域に入れているっぽい。ということで、gdbを使用しmainで実行を止めた後、eipをgenflagのアドレスに書き換えて、ループが終わったあたりまで実行し、malloc()で確保した領域を表示してみたらflagが出た。

$ gdb -q bin300
(gdb) b *main
(gdb) run
(gdb) set $eip=*genflag
(gdb) u *0x80485d6
(gdb) x/s $eax

感想

今回初めて「CTFの講義」を聴きましたが、知らなかったこともいろいろあり勉強になりました。webの講義も受けていたのにweb問を1問も解けなかったのが残念でした。久しぶりのCTFでしたが全体的に難しすぎない良い感じの問題が多かったので楽しめました。同じ大学の同期や後輩も参加していましたが、みんな楽しめたようで良かったです。

Immunity DebuggerでWin32 APIのフックをやってみる

「Immunity Debuggerを使ってみる」でPyCommandデビューしたため、もっと実用的なそれっぽいことをやってみる。

環境

・Windows 10 Pro 64bit版

・Immunity Debugger v1.85

・Python 2.7.9

 

デバッグ対象

・C:\Windows\SysWOW64\notepad.exe

 

Win32 APIのフック

Immunity DebuggerではFastLogHookというオブジェクトが提供されており、簡単にフックをセットアップできる。フックをセットする一連のコードは次のようになる。

imm = immlib.Debugger()
fast = immlib.FastLogHook(imm)

fast.logFunction(address,num_arguments)     #フックポイントを定義
fast.logRegister(register)                  #フック時における特定のレジスタの値をログ出力
fast.logDirectMemory(address)               #フック時における一定のメモリオフセットをログ出力
fast.logBaseDisplacement(register,offset)   #レジスタから一定のオフセットに位置するデータをログ出力
fast.Hook()

メソッドFastLogHook()でフックポイントを設定できる。このメソッドはフックを設定すべきアドレスと捕捉すべき引数の数を受け取る。logRegister(),logDirectMemory(),logBaseDisplacement()が実際にログ出力するメソッドであり、状況によって使い分けることができる。

フックポイントに到達してログ出力メソッドが実行されると、捕捉された情報はFastLogHookオブジェクトが作成したアロケート済みメモリ領域に保存される。そのためフックの結果を取得する必要があり、それにはgetAllLog()というメソッドが使える。このメソッドは対象のメモリ領域の内容を解析して次の形のリストを返す。

[(hook_address,(arg1,arg2,...,argN)),...]

 

呼出し規約による違い

ここまでの説明ではFastLogHook()を使用すると書いたが、Win32 APIではstdcall呼出し規約が使われているためSTDCALLFastLogHook()というメソッドを使う必要がある。ただし、使い方は上で説明したFastLogHook()と変わらない。

 

実際にPyCommandを作成してみる

ここではRtlAllocateHeap関数をフックポイントに設定し、3つのパラメータを捕捉しログ出力する。

RtlAllocateHeapのプロトタイプは次のようになっている。

PVOID RtlAllocateHeap(
  _In_     PVOID  HeapHandle,
  _In_opt_ ULONG  Flags,
  _In_     SIZE_T Size
);

実際にスクリプトを書くと次のようになる。

import immlib

def main(args):
    imm = immlib.Debugger()

    fast = imm.getKnowledge("my_hook")
    if fast:                                                      #すでにフックが設定されていたら結果をログ出力
        hook_list = fast.getAllLog()
        for a in hook_list:
           imm.log("RtlAllocateHeap(0x%08x,0x%08x,0x%08x)" % (a[1][0],a[1][1],a[1][2]))
        return "Logged : %d hook hits." % len(hook_list)
        
    imm.pause()                                                   #デバッガを停止
    addr_rtlallocate = imm.getAddress("ntdll.RtlAllocateHeap")    #ntdll.dll!RtlAllocateHeapのアドレスを取得
    imm.log("ntdll.RtlAllocateHeap : 0x%08x" % addr_rtlallocate)
    
    #ここからフックの構築
    fast = immlib.STDCALLFastLogHook(imm)
    fast.logFunction(addr_rtlallocate,3)
    fast.Hook()                                                   #フックの設定
    imm.addKnowledge("my_hook",fast,force_add=1)                  #あとから結果を取得できるようにフックオブジェクト(fast)を保存

    return "Hook set, press F9 to continue the process."

addKnowledge()メソッドはPythonのオブジェクトをタグ(1つ目のパラメータ)をつけてナレッジデータベースに追加するメソッドで、getKnowledge()で、追加したPythonオブジェクトをタグを指定して取り出すことができる。上のコードではフックが設定されているかどうかをこのメソッドで確かめている。

このスクリプトを次の手順で実際に使ってみる。

  1. Immunity Debuggerを起動しnotepad.exeを開く
  2. コマンドバーに!myhookのように感嘆符に続けてスクリプト名を入力しENTERキーを押下
  3. F9キーでnotepad.exeのウィンドウが表示されるまで進める
  4. 2と同様にスクリプトを実行

上の手順通りに進めるとログウィンドウ([View] -> [Log])に次のような結果が表示され、RtlAllocateHeapのパラメータを捕捉できていることが分かる。

f:id:sentoneibisu:20151003181106p:plain

 

おまけ:フック後のアセンブリコードを見てみる

RtlAllocateHeapの逆アセンブルコードは次のようになっている。

f:id:sentoneibisu:20151003190347p:plain

しかしフック後は先頭の5バイト(MOV EDI,EDI;PUSH EBP;MOV EBP,ESP)がJMP命令(JMP 060F0008)に書き換えられている。このジャンプ先がフックコードである。

f:id:sentoneibisu:20151003190507p:plain

CPUウィンドウで[Ctrl-G] -> 060f0008を入力しJMP先のアドレスを見てみると、以下のフックコードが生成されているのが分かる。

f:id:sentoneibisu:20151003190603p:plain

上のコードでは060F0008PUSHAD,060F0078POPADによって元のレジスタの値を保存している。

上のフックコードの処理内容を簡単にまとめると次のようになる。

  1. 060F0000から(捕捉したRtlAllocateHeapのパラメータが保存される)メモリ領域へのアドレスをEDIに格納する
  2. 060F0042MOV EAX,DWORD PTR SS:[ESP+C]PUSHADで保存したESPの値をEAXに格納する
  3. 060F0049MOV EAX,DWORD PTR DS:[EAX+4]で1つ目のパラメータ([ESP+4])をEAXに格納する
  4. 060F004FSTOS DWORD PTR ES:[EDI]で1つ目のパラメータをEDIの指す先に格納する
  5. 2~4と同様に2つ目、3つ目のパラメータもメモリ領域に格納する
  6. 最後に060F0000に格納されたメモリ領域へのアドレスを更新する

捕捉したRtlAllocateHeapのパラメータが保存されるメモリ領域を確認してみる。

16進ダンプウィンドウで[Ctrl-G] -> 060f0000を入力すると、赤枠部分に目的のアドレスが格納されているのが分かる。

f:id:sentoneibisu:20151003191046p:plain

16進ダンプウィンドウで[Ctrl-G] -> 060f6b24を入力して少し上にスクロールすると、ピンク枠部分に捕捉したRtlAllocateHeapのパラメータが保存されているのが分かる。

f:id:sentoneibisu:20151003191342p:plain

060F0079から最後までの命令は次のようになっており、フックコードへのJMP命令で書き換えられていた5バイトの命令を実行した後、元のRtlAllocateHeapにリターンしているのが分かる。

MOV  EDI,EDI
PUSH EBP
MOV  EBP,ESP
PUSH 7752DA95
RETN

Immunity Debuggerを使ってみる

Immunity Debuggerとは

高機能なGUIユーザーモードデバッガ。Pythonによる機能拡張が可能。

以下のリンクからダウンロードできる(はず)

http://debugger.immunityinc.com/

 

メインインタフェース

 Immunity Debuggerのメインインタフェースは次の5つの区画で構成されている。

f:id:sentoneibisu:20151003114146p:plain

①プロセスのアセンブリコードを表示するウィンドウ

②各種レジスタを表示するウィンドウ

③メモリの16進ダンプを表示するウィンドウ

④スタックを表示するウィンドウ

⑤コマンドバー

PyCommandとは

Immunity Debuggerの内部で動かせるPythonスクリプト。ここでは、簡単なPyCommandを作成して実際に動作させてみる。

PyCommandは次のコードのように、必ず一つのパラメータ(PyCommandに渡される引数のリスト)を受け取り、文字列を返す必要がある。

import immlib
def main(args):
    imm = immlib.Debugger()
    return "PyCommand Executed!"

 PyCommandを実行させるためには、Immunity Debuggerのインストールディレクトリ中のPyCommandsというディレクトリにスクリプトを保存し、Immunity Debuggerのコマンドバーで感嘆符に続けてスクリプト名を入力(!<スクリプト名>)する。このとき、拡張子は入力しない。

 

簡単なPyCommandを作成してみる

環境

・Windows 10 Pro 64bit版

・Immunity Debugger v1.85

・Python 2.7.9

デバッグ対象

・notepad.exe

Immunity Debuggerは64bitアプリケーションには対応していないため、ここではC:\Windows\SysWOW64*1\notepad.exeをデバッグ対象とする。

 

モジュールntdll.dll内の関数を列挙するスクリプトは次のように書ける。

import immlib

def main(args):
    imm = immlib.Debugger()                            #immlib.Debuggerをインスタンス化
    ntdll = imm.getModule("ntdll.dll")                 #モジュールオブジェクトを返す
    addr_ntdll = ntdll.getBaseAddress()                #ベースアドレスを取得
    imm.log("================================")
    imm.log("Address of ntdll.dll : 0x%08x" % addr_ntdll)
    imm.log("================================")
    func_list = imm.getAllFunctions(addr_ntdll)        #関数のアドレスのリストを返す
    for func in func_list:
        imm.log("0x%08x" % func)
    return "F I N I S H !!!"

Immunity DebuggerのAPIリファレンスについてはインストールディレクトリ中のDocumentation\Refを参照のこと。

出力結果はLogウィンドウ([View] -> [Log])に出力される。

f:id:sentoneibisu:20151003032116p:plain

*1:Windows 32bit On Windows 64bit

セキュリティ・キャンプ全国大会2015に行ってきたお話

4泊5日のキャンプに行ってきたので学んできたことや感想をまとめます。

(何かまずいこと書いていたら教えてください)

 

キャンプ1日目

半日あれば青森県から千葉県へ余裕で着けそうだったため、前泊はせずに当日朝5時に起床して行きました。

 空の旅では丁度窓側で良い席だったため写真を撮ったりして旅行(?)を楽しみました。

 羽田に到着し迷子になりつつバスに乗り、会場近くの海浜幕張駅へ。

バスの中ではTwitterのTL上に常に存在していた「友利奈緒」をググったり景色を眺めたりしていました。

 そんなこんなで会場のクロス・ウェーブ幕張に10時45分に到着!

 緊張しながら会場に入るとすでに到着していた参加者がちらほら。

楽しそうに談笑していました。

誰だコイツ...って感じのコワイ目で見られたのは一生忘れません(※冗談です)。みんな優しい目で迎えてくれました!

そこでコミュ障を発揮しつつ名刺交換をしたりワイワイしました。

受付を済ませたあとは一旦ホールに移動し、名刺交換などで軽く交流したのち食堂でランチセッションでした。

 

昼食を済ませた後は開講式・オリエンテーションと続き、経済産業省の方やIPAの会長さん、講師主査の上野さんからのお話などがありました。

 

まもるくん

そして、今回スペシャルゲストにあの、まもるくんが遥々まもりの国から来てくれました。

\キャー!/    \ダレ?/    \スゴーイ!/    \カワイー!/ と、すごい盛り上がっていたように思いました。

一部のまもるくんを知らない人のためにプロフィールを載せときます。

情報処理推進機構:IPA 情報セキュリティ 標語・ポスターコンクール:まもるくん-PROFILE-

 

 セキュリティ基礎

まもるくんからのお話(?)の後は遂に上野さんによる「セキュリティ基礎」の講義が始まりました。『情報漏えいはなぜ起こるのか』、『メールの添付ファイルによるウイルス被害が発生しないようにするための対策』などをグループで話し合い発表をしました。

 

 グループワーク

セキュリティ・キャンプ全国大会では毎年グループワークという取り組みがあり、何人かでグループを作り、調査対象のテーマを決めて取り組みます。テーマは次の4つのどれかを選べとのこと。

 テーマ1  組織的犯行対策(事前、事後など)
 テーマ2 ヒアリングマラソン:技術人材のゴール像とステップ構想
 テーマ3 リサーチマラソン:セキュリティ情報収集の効果的な方法とアクション
 テーマ4 技術ビジョン構想:セキュアなものづくりを実現するには

 私自身はテーマ1を選びました。ここでそれぞれ選んだテーマごとにいくつかのグループを作りグループワークのメンバーが決定しました。

後で某チューターさんに話を聞くと、過去のグループワークは漠然と「セキュリティについて」くらいのテーマだったようで、今回は具体的なテーマ設定になっているから難しそうとのこと。実際取り組んで分かりましたが、テーマが絞られている分、題材選びでオリジナリティを出すのが難しくかなり苦労しました。

 

特別講義(1)

最初は『2020年を迎えるみなさんへ』という題で東京オリンピックパラリンピック競技大会組織委員会テクノロジーサービス局局長の方からのお話がありました。

現代のアスリート事情、スポーツとITの関わりなど興味深い内容でした。

スポーツは『ゲーム』であり、選手はもちろん見ている人を楽しませるのが本来の目的であり、そのためにITを活用するというのは確かになぁと思う部分がありました。しかし、伝統や文化を重んじる日本人ならではの「ここは変わってほしくない」みたいな人の気持ちも分かるので、どこまでITに依存させるかということも難しい問題なんだろうなぁと思いました。

次に、『東京2020までの取り組みとその後』という題で東京オリンピック・パラリンピック競技大会組織委員会警備局サイバー攻撃対処部の方からのお話がありました。

サイバー攻撃など東京オリンピックのセキュリティに直接関わっている方からのお話ということでとても貴重なお話でした。

過去のオリンピック・パラリンピックで起こったテロやサイバー攻撃について、年々増加している話や今後どのような方法でどの程度テロやサイバー攻撃が起こるのかという予測をして備えなければならない。ということで自分には関係ないから~なんて言わず積極的に考える必要がある!と思いました。

最後に、ボランティアとして東京オリンピック・パラリンピックに関与してほしいというお話がありました。(興味はあるけど何をするんだろう...)

 

特別講義(2)

 警視庁サイバー攻撃特別捜査隊の担当の方からの講演がありました。このお話の内容は、とても重要かつ機微な内容なので、口外は×。ということでここには書きません。

 

チューター成果報告

チューターの中から代表して二人の方のプレゼンがありました。

まずは、@kitokayさんから「自動車にセキュリティを求めるのは間違っているのだろうか」という題で発表がありました。感想としては、なんか凄い!内容は難しくてよく分かりませんでした。CAN(Control Area Network)を初めて聞いたので新鮮&面白そうとは思いましたが、CAN-BusにDoS攻撃とか怖すぎる((( ;゜ Д ゜)))

怖い人だなぁと思いました。

 

次は、@ytn86さんから「「脆弱性をみつける」から「脆弱性をなくす」へ」という題での発表でした。「脆弱性を見つけること」の意義について「世の中を安全にするため」、「一般ユーザがより安全にサービスを使える世界にしたい」など。カッコいいなぁ...

私自身今のところ楽しいから技術を学んでいるという部分が大きくて、この話以外でもキャンプで特に思ったのは、最終的には人のため社会のために今学んでいる技術をどう使うかというのが重要だということ。これは当たり前のようであまり深く考えてこなかった部分でもありとても参考になりました。

 

専門講義(1-D):Linuxにおける脆弱性攻撃とその対策

 1日目の最後に遂に待ちに待った最初の講義。内容は、GDBというデバッガの使い方やバッファオーバーフロー脆弱性を利用した攻撃について簡単に学んだあと、実際に脆弱性があるプログラムに対してバッファオーバーフローを起こしてその時のスタックの状態をGDBで確認してみる、シェルコードを書いてみるなどをしました。

事前課題などで勉強していたので、難しくて理解できなかったということにはならなくて良かったと思います。ただ、もう少し踏み込んだ内容だとなお嬉しかったとも思いました。

何はともあれ、事前課題でもとてもお世話になったももいろテクノロジーの執筆者である講師の@inaz2さんの講義を直接受けれたのはとても良かったです。(@inaz2さんとは別の日にもいろいろ話を聞いてもらったり貴重なことを教えていただいたり大変お世話になりました有り難うございましたm(__)m)

 

キャンプ2日目

そして2日目。 朝起きれないんじゃないかと3段階目覚ましにしましたが、目覚ましが鳴る前に起きれました。(私はやればできるということを知れました。ありがとうございます。)

 

専門講義(2・3-C):攻撃検知と機械学習

 この講義では機械学習を扱うということで、興味はあったが知識はほぼ無いという状態での講義でした。

内容は、SQL Injectionの攻撃ログや、謎の攻撃ログから目やツールを使用してどういう特徴があるのかなどを見つけるというものでした。

統合機械学習環境Wekaを使ったり、統計解析向けの言語であるR言語を使っていろいろ特徴抽出とかの雰囲気を味わえました。

とても面白くて、これから機械学習について勉強してもっと触ってみようと思いました。

 

専門講義(4・5-C):遠隔操作マルウェアの検知および検知回避

この講義ではShinoBOTを使ってRATを体験しました。

終始スゲースゲー言ってた気がします。

その後、グループを作り、それぞれ役割を決めてRATをvbsで作成しました。(全く役に立てなかったので申し訳ない気持ち...)

ここで一つ言っておきたいのは

正当な理由がないのに、同意なしに他人のPC上で動作させる目的で、マルウェアを開発、提供、利用する(しようとする)と、3年以下の懲役または50万円以下の罰金が科せられる

 違法にマルウェアを作るのはダメ!

セキュリティ・キャンプに参加すればマルウェア作成が体験できる(かも)。

怖いのでRATのコードは載せません。ぜひキャンプに参加して作ってみてください!

 

専門講義(6-B):一夜でできる?!PC用OS自作入門

この講義の目的は、すべてを理解することではない!

こんな簡単にOSって作れるんだよ。という規模感を感じてほしい!

とのこと。

結論から言うと、凄い。本当凄い。楽しい。夢が広がりました。

すべてを理解しなくても、「ここをいじったらここが変わりそうだなぁ。こっちはどうだ?」といろいろ改造してみてとりあえずやってみる、ということが大事だということが分かりました。

 

キャンプ3日目

 あっという間にキャンプ3日目。ここからは怒涛の解析講義でした。

 

専門講義(7・8・9-D):脆弱性攻撃と対策

 この講義ではWindowsでROP(Return-Oriented-Programming)によるDEP回避をするExploitを使って電卓を起動させるということをやりました。詳しくはももいろテクノロジーを読みましょう。

WindowsでReturn-oriented Programming(ROP)によるDEP回避をやってみる - ももいろテクノロジー

その後は、UPXのゼロデイを見つけようということでひたすら解析をしました。(この時間内では誰も見つけることができませんでした)

近いうちにいろいろ試して見つけてみたいですねー。(※期待はしないでください)

 

専門講義(10-D):コードから読み解くマルウェアの真実(マルウェア分析概論、静的分析基礎)

 この講義は、マルウェアの研究をしようとしている私としてはとても楽しみにしていた講義の一つでした。内容はマルウェア分析について座学で学び、IDAの使い方を覚えるという感じでした。詳しくは専門講義(13・14-D)のとこで書きます。

 

(11・12):出張 CTF for ビギナーズ 2015 幕張 in セキュリティ・キャンプ

3日目の最後は出張 CTF for ビギナーズということでCTFをやりました。CTFは1か月前くらいに始めた(ksnctfを少々)ばかりですが思ってたより難しくて、簡単な10点問題しか解けなかったのが悔しかったです。

今後に期待!

 

キャンプ4日目

 もう4日目。ここまで来ると、この日で講義が終わる寂しさとグループワークの進捗の無さで悲しみいっぱいでした。

 

専門講義(13・14-D):コードから読み解くマルウェアの真実(実践マルウェア分析)

 この講義では、実際にいくつかのマルウェアをひたすらIDAで分析するという内容でした。難しかったけど、私にとって得るものが多くとても為になりました。

IDAを使ったマルウェア分析では、

“IDAのコメント量が分析力だ!”

なるほど...私はいったい今まで何をやっていたんだ...

これからは頑張ります...!

 

専門講義(15・16-D):仮想化技術を用いたマルウェア解析

 早いもので最後の講義となってしまいました。

この講義では、DECAFというQEMUベースの解析プラットフォームを用いて、DECAFのプラグインを拡張し、サンプルプログラムの解析妨害機能を回避するということを体験しました。実際にプラグインを拡張するのは楽しかったのですが難しい...!

テイント解析やシンボリック実行といった近年の研究動向についても少し解説がありました。が...やはり難しかったです。

これから勉強していきたいと思いました。

 

キャンプ5日目

 最終日は寝ていません。

 

グループワーク発表

キャンプ最終日です。この日はグループワークの発表などがありチョー緊張しました。夜中グループワークのメンバーを部屋に連れ込んでワイワイしているのは楽しかったけど、進捗は厳しい状態でした。

なんとか当日の朝にはスライドを完成させて一同一安心。発表しました。

発表後は緊張が解けて、他のグループの発表を聞きながらスゲースゲー言ってました。

 結果、賞などは貰えませんでしたがとても良い経験になりました。

一緒にいろいろ考えて力を合わせてくれたグループワークメンバーに感謝!

インタビューに答えて頂いた講師・チューターの方々有り難うございました。m(__)m

 

成果報告会、閉講式と続き無事セキュリティ・キャンプ全行程終了!!

みなさんお疲れ様でした。

 いろいろ頂けてサイコー!!!

 

まとめ

・ごはんおいしい

・野菜いっぱい嬉しい

・仲間いっぱい嬉しい

・講師やチューターの方々が優しかった

・グループワーク大変

・普段朝5時に寝る生活を続けていても意外と起きれる(キャンプ中は早く寝ないと無理!)

 

来年のキャンプ参加を考えている方へ

・迷わず応募すべし

・間違いなく最高の夏になります

・こんな濃密な5日間は他にない!

・友達いっぱい増えます

・コミュ力的に不安がある方!問題ないです。講師もチューターもとても優しくてコミュ力の塊なので何も喋らなくても成立します。

・もちろん講義も楽しい

・やる気UP↑↑↑・モチベUP↑↑↑

 

謝辞

 いろいろ教えて頂いたりアドバイスをくれた講師の皆様、サポートしてくれたチューターの皆様、陰で支えてくれた運営の皆様、実施協議会の皆様、心より感謝いたします。