読者です 読者をやめる 読者になる 読者になる

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関数が00007ffbcd48e911`にあることがわかる。

ついでに、シェルコードから呼び出す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, eaxmov 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.H­H.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バイトである。

関連リンク