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
はじめのブロックから読んでいく. call命令でEIPを積んでいる.
pop ebpで先ほど積んだEIPをEBPに渡している. lea命令でebp+0xB2, つまり先ほどのEIPの0x6と0xB2を足した0xB8の値をeaxに渡している.
0xB8を見てみると以上のようになっているが,以下のように解釈をさせる.
つまりeaxに渡しているのは'calc'のアドレスである. call ebpでは先ほど積んだEIPつまり0x6に飛ぶことになる.
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
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)と同じくハッシュの生成を行なっている.
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が呼ばれていることがわかる.
3. おまけ
shellcodeを実際に実行してみる. 引数にPIDを与えてやるとコードインジェクションを行い, 実際に電卓が起動することが確認できる.
4 . 参考
http://www.openrce.org/reference_library/files/reference/PE%20Format.pdf