shellcode(2)

1. はじめに

 前回に引き続きshellcodeを生成して読んでみる.

 

 

2. shellcodeの生成

 Kali Linux内のmetasploitを利用してshellcodeを生成する.

 

root@kali:~# uname -a
Linux kali 4.5.0-kali1-amd64 #1 SMP Debian 4.5.3-2kali1 (2016-05-09) x86_64 GNU/Linux

 

root@kali:~# msfconsole

msf > use payload/windows/exec

msf payload(exec) > show options

Module options (payload/windows/exec):

Name Current Setting Required Description
---- --------------- -------- -----------
CMD yes The command string to execute
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)

msf payload(exec) > set CMD calc

msf payload(exec) > generate -h
Usage: generate [options]

Generates a payload.

OPTIONS:

-E Force encoding.
-b <opt> The list of characters to avoid: '\x00\xff'
-e <opt> The name of the encoder module to use.
-f <opt> The output file name (otherwise stdout)
-h Help banner.
-i <opt> the number of encoding iterations.
-k Keep the template executable functional
-o <opt> A comma separated list of options in VAR=VAL format.
-p <opt> The Platform for output.
-s <opt> NOP sled length.
-t <opt> The output format: bash,c,csharp,dw,dword,hex,java,js_be,js_le,num,perl,pl,powershell,ps1,py,python,raw,rb,ruby,sh,vbapplication,vbscript,asp,aspx,aspx-exe,dll,elf,elf-so,exe,exe-only,exe-service,exe-small,hta-psh,loop-vbs,macho,msi,msi-nouac,osx-app,psh,psh-net,psh-reflection,psh-cmd,vba,vba-exe,vba-psh,vbs,war
-x <opt> The executable template to use

 

msf payload(exec) > generate -f calc.bin -t raw

 

 

f:id:kmdnet:20160913131943p:plain

  はじめのブロックから読んでいく. call命令でEIPを積んでいる.

f:id:kmdnet:20160913132150p:plain

  pop ebpで先ほど積んだEIPをEBPに渡している. lea命令でebp+0xB2, つまり先ほどのEIPの0x6と0xB2を足した0xB8の値をeaxに渡している.

 

f:id:kmdnet:20160913132715p:plain

 0xB8を見てみると以上のようになっているが,以下のように解釈をさせる.

f:id:kmdnet:20160913132913p:plain

 つまりeaxに渡しているのは'calc'のアドレスである. call ebpでは先ほど積んだEIPつまり0x6に飛ぶことになる.

f:id:kmdnet:20160913133410p:plain

 fs:[eax+30h]はeaxが0であるため, fs:[30h]となりPEB(Process Environment Block)を指すことになる.そこからはshellcode(1)で書いたことと同じである.

なお[edx+28h]はモジュール名(BaseDLLName), [edx+26h]モジュール名長となる. (∵ +0x8)

 

0:000> dt _LDR_DATA_TABLE_ENTRY -r2
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
      +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
         +0x000 Flink            : Ptr32 _LIST_ENTRY
         +0x004 Blink            : Ptr32 _LIST_ENTRY
   +0x018 DllBase          : Ptr32 Void
   +0x01c EntryPoint       : Ptr32 Void
   +0x020 SizeOfImage      : Uint4B
   +0x024 FullDllName      : _UNICODE_STRING
      +0x000 Length           : Uint2B
      +0x002 MaximumLength    : Uint2B
      +0x004 Buffer           : Ptr32 Uint2B
   +0x02c BaseDllName      : _UNICODE_STRING
      +0x000 Length           : Uint2B
      +0x002 MaximumLength    : Uint2B
      +0x004 Buffer           : Ptr32 Uint2B
   +0x034 FlagGroup        : [4] UChar

 

 

f:id:kmdnet:20161108135820p:plain

 

 edxがLdr->InMemoryOrderModuleListを指すため, [edx+10h]はDLLBaseを, [edx+3Ch]はe_lfanewを[ecx+edx+78h]はIMAGE_DATA_DIRECTORYである.

 次に[ecx+20h]はAddressOfNamesを[ecx+18h]はNumberOfNamesを示す.

typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;                /*+0x14*/
DWORD NumberOfNames;                 /*+0x18*/
DWORD AddressOfFunctions; // RVA from base of image    /*+0x1C*/
DWORD AddressOfNames; // RVA from base of image     /*+0x20*/
DWORD AddressOfNameOrdinals; // RVA from base of image /*0x24*/
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
/* ~ */は筆者注 

 

 IMAGE_EXPORT_DIRECTORY

・ AddressOfFunctions : Export Address Table (EAT) 

・ AddressOfNames : 関数名のRVA

・ AddressOfNameOrdinals : EATのn番目の要素

 

 仮にN番目の時に関数のハッシュと一致したとする. そのN番目の値をAddressOfNameOrdinalsの配列の要素として取り出し, それを序数 (ordinal) としてAddressOfFunctions (EAT) の配列の要素とする. そうして取り出した値が目的関数のRVAとなる.

つまり

 AddressOfNameOrdinals[AddressOfNameOrdinals[N]]

として関数のアドレスを導出する.

なおそれぞれの要素数は, 以下のようになる.

 AddressOfFunctions[NumberofFunctions]

 AddressOfNames[NumberOfNames]

 AddressOfNameOrdinals[NumberOfNames]

 

 loc_4Fはshellcode(1)と同じくハッシュの生成を行なっている.

f:id:kmdnet:20161108142740p:plain

 

 eaxはスタックからIMAGE_EXPORT_DIRECTORYを戻しているため, [eax+24h]はAddressOfNameOrdinalsであり[ebx+ecx*2]は序数(ordinals)である. ここでecxはecx番目に目的関数を一致した値であり, 上記のNに当たる. また[eax+1Ch]はAddressOfFunctionsであり, [ebx+ecx*4]は目的関数のRVAである. 

 ここでecxを2倍, 4倍しているのはAddressOfNameOrdinalsの要素のサイズが2バイト,EAT)の要素のサイズは4バイトであるからである.

 

 前回と同様にAPIハッシュを計算するプログラムを作成した. 前回との変更点は, 最終的にモジュールのハッシュをAPIのハッシュに加算する箇所である. 同様にIDAに読み込ませるとWinExecとExitProcessが呼ばれていることがわかる.

 

 

f:id:kmdnet:20161109194759p:plain

 

 

3.  おまけ

 shellcodeを実際に実行してみる. 引数にPIDを与えてやるとコードインジェクションを行い, 実際に電卓が起動することが確認できる. 

 

 

4 . 参考

http://www.openrce.org/reference_library/files/reference/PE%20Format.pdf

リバースエンジニアリング入門(5):PEフォーマットを解釈せよ! (1/3) - @IT