安全路透社
当前位置:安全路透社 > 安全客 > 正文

【技术分享】Windows x64 shellcode编写指南

http://p5.qhimg.com/t0119a4cca6bdd5cddf.jpg


前言


最近我重写了几个shellcode,将之前32位平台下实现的shellcode移植到64位平台。在向64位平台移植过程,我发现很难在网上找到相关资料,因此我将我的移植过程写成一篇博客(我的第一篇),希望能帮到像我一样需要移植64位shellcode的人。

网上已经有几篇教程介绍关于shellcode的相关基础知识了,因此我不会介绍这些。虽然我会介绍关于调用约定、register clobbering和寄存器相关知识,但是我不会讨论很多汇编基础知识。

请参考Skape的Understanding Windows Shell code等文章,或者资源project-shellcode进行深入学习(Understanding Windows Shell code原始链接失效,这里替换了,原始链接为http://repo.hackerzvoice.net/depot_madchat/windoz/vulns/win32-shellcode.pdf)。

我将介绍32位汇编与64位汇编的差异,以及如何利用Windows系统中的结构体用于开发64位shellcode。我还将介绍我开发的2种漏洞利用辅助开发工具。

在开始之前需要说明的是我仍然在学习漏洞利用开发的初级阶段,为简单化,本文实验的系统为Window 7 x64版本。为简化叙述,x86指Win32平台,x64指Win64平台。


寄存器(Registers)


x86

x86处理器中有8个32位的通用寄存器

– eax-累加器

– ecx-计数寄存器

– edx-数据寄存器

– ebx-基地址寄存器

– esp-堆栈指针

– ebp-基址指针

– esi-源索引寄存器

– edi-目标索引寄存器

– eip-指令寄存器

由于向后兼容原因,其中4个寄存器(eax,ebx,ecx,edx)可以拆分16位和8位寄存器

– AX-EAX的低16位

– AH-AX的高8位

– AL-AX的低8位

– BX-EBX的低16位

– BH-EBX的高8位

– BL-EBX的低8位

ECX和EDX也使用字母(C,D)和后缀(X,H或L)表示16位和8位寄存器。

x64

64位处理器使用前缀“R”扩展上述8个寄存器,RAX,RCX,RDX等。需要注意的是x86平台下的寄存器表示方式仍然可用(eax,ax,al等)。

还引入了8个新的寄存器,r8、r9、r10、r11、r12、r13、r14和r15。这些寄存器也可以分为32位、16位和8位的版本。

– r#=64位

– r#d=低32位

– r#w=低16位

– r#b=8位

不幸的是,这些新的8位扩展寄存器不能够使用像eax中的低16位的高8位


Clobber寄存器(Clobber Registers)


Clobber寄存器是一些可能在函数(如Windows API)中被覆盖修改的寄存器。在汇编代码中不应该使用这些寄存器,容易引发程序不稳定,但是如果明确知道在api函数中那些寄存器会别修改还是可以使用这些寄存器的。

在Win32 API中,EAX、ECX和EDX都是Clobber寄存器,在Win64 API中,除了RBP、RBX、RDI、RSI、R12、R13、R14和R15,其他的寄存器都是Clobber寄存器。
RAX和EAX分别用于x64和x86函数的返回值。


调用约定(Calling Convention)


x86

Win32 API一般使用__stdcall调用约定,从右到左向堆栈上传递参数。

如调用函数有两个参数int x和int y的函数foo

foo(int x,int y)

在堆栈上传递为

push y
push x

x64

Win64平台下函数调用约定不同,但与Win32平台下的__fastcall相似,均使用寄存器传参,前四个参数分别使用RCX、RDX、R8和R9传递,其他多余的参数使用堆栈传递。需要注意的是,使用寄存器传参时从右到左传递参数。

如对Windows函数MessageBox的调用声明如下:

int WINAPI MessageBox(
_In_opt_  HWND hWnd,
_In_opt_  LPCTSTR lpText,
_In_opt_  LPCTSTR lpCaption,
_In_      UINT uType
);

在Win64调用预定下,参数为:

r9 = uType
r8 = lpCaption
rdx = lpText
rcx = hWnd


Shellcode


让我们开始吧,现在Win64下的Shellcode与Win32的关键区别我们已经知道了,开始我们的文章吧。

为显示运行Win64 shellcode,我将编写代码弹出MessageBox对话框。当shellcode最终完成后,我将使用我编写的工具将shellcode代码注入到calc进程中,验证shellcode能够在另一个进程中运行。


注释


我将使用NASM编译汇编代码,使用Jeremy Gordon编写的golink链接程序。使用你最喜欢的文本编辑器编辑汇编代码,我使用Windows平台下的Notepad++,然后开始编写代码。


开始


1).声明NASM指令

bits 64 
section .text
global start

2).设置堆栈

start: 
    sub rsp, 28h                ;reserve stack space for called functions
    and rsp, 0fffffffffffffff0h ;make sure stack 16-byte aligned

3).得到Kernel32的基地址

Win64与Win32平台的PEB结构位置是不同的,在Win32中,PEB为[fs:30h]指向的地址,而在Win64为[gs:60h]

虽然PEB结构发生了巨大变化,但是我们只关心LDR链表(PEB_LDR_DATA)所在的未知,在Windbg中使用”!peb”命令可以得到LDR所在的位置。

在Windbg输出的PEB结构中,Ldr.InMemoryOrderModuleList中包含Kernel32.dll,在链表第三个未知,此列表显示了进程中各个内存模块(PE文件,可执行文件和dll文件)在内存中所在的位置。

Ldr.InMemoryOrderModuleList:         00000000002b3150 . 00000000002b87d0
        Base TimeStamp                     Module
    ff600000 4a5bc9d4 Jul 13 16:57:08 2009 C:\Windows\System32\calc.exe
    77b90000 4ce7c8f9 Nov 20 05:11:21 2010 C:\Windows\SYSTEM32\ntdll.dll
    77970000 4ce7c78b Nov 20 05:05:15 2010 C:\Windows\system32\kernel32.dll

通过在Windbg中使用dt命令填充PEB结构,确定Ldr链表所在的位置。

0:000> dt _PEB 000007fffffd4000
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   +0x003 BitField         : 0x8 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsLegacyProcess  : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 SpareBits        : 0y000
   +0x008 Mutant           : 0xffffffff`ffffffff Void
   +0x010 ImageBaseAddress : 0x00000000`ff600000 Void
   +0x018 Ldr              : 0x00000000`77cc2640 _PEB_LDR_DATA

Ldr链表位于PEB的0x18位置

现在,我们还需要做一下步骤:

– 通过在PEB偏移0x18处获得Ldr链表,获得Ldr链表后,访问位于Ldr结构偏移0x20处的InMemoryOrderModuleList,如下输出

0:000> dt _PEB_LDR_DATA 77cc2640
ntdll!_PEB_LDR_DATA
   +0x000 Length           : 0x58
   +0x004 Initialized      : 0x1 ''
   +0x008 SsHandle         : (null) 
   +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x00000000`002b3140 - 0x00000000`002b87c0 ]
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x00000000`002b3150 - 0x00000000`002b87d0 ]
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x00000000`002b3270 - 0x00000000`002b87e0 ]
   +0x040 EntryInProgress  : (null) 
   +0x048 ShutdownInProgress : 0 ''
   +0x050 ShutdownThreadId : (null)

– 偏移0x20处为InMemoryOrderModuleList,由InMemoryOrderModule列表的输出的图中可以看出,Kernel32.dll是第3个内存模块。在_LIST_ENTRY结构包含向前和向后的指针,所有_LIST_ENTRY组成一个循环链表,通过这个链表可以找到Kernel32的基地址。

0:000> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
   +0x000 Flink            : Ptr64 _LIST_ENTRY
   +0x008 Blink            : Ptr64 _LIST_ENTRY

在Windbg中,使用!list可以遍历遍历_LIST_ENTRY结构组成的链表。!lis -x可以用于为每个位置的元素指定一个命令。我们用它去解析_PEB_LDR_DATA结构中的0x20偏移量,并通过_LIST_ENTRY元素进行解析。

将列出所有InMemoryOrderModule链表并显示相关的_LDR_DATA_TABLE_ENTRY

0:000> dt _LDR_DATA_TABLE_ENTRY
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x010 InMemoryOrderLinks : _LIST_ENTRY
   +0x020 InInitializationOrderLinks : _LIST_ENTRY
   +0x030 DllBase          : Ptr64 Void
   +0x038 EntryPoint       : Ptr64 Void
   +0x040 SizeOfImage      : Uint4B
   +0x048 FullDllName      : _UNICODE_STRING
   +0x058 BaseDllName      : _UNICODE_STRING
   +0x068 Flags            : Uint4B
   +0x06c LoadCount        : Uint2B
   +0x06e TlsIndex         : Uint2B
   +0x070 HashLinks        : _LIST_ENTRY
   +0x070 SectionPointer   : Ptr64 Void
   +0x078 CheckSum         : Uint4B
   +0x080 TimeDateStamp    : Uint4B
   +0x080 LoadedImports    : Ptr64 Void
   +0x088 EntryPointActivationContext : Ptr64 _ACTIVATION_CONTEXT
   +0x090 PatchInformation : Ptr64 Void
   +0x098 ForwarderLinks   : _LIST_ENTRY
   +0x0a8 ServiceTagLinks  : _LIST_ENTRY
   +0x0b8 StaticLinks      : _LIST_ENTRY
   +0x0c8 ContextInformation : Ptr64 Void
   +0x0d0 OriginalBase     : Uint8B
   +0x0d8 LoadTime         : _LARGE_INTEGER

在_LDR_DATA_TABLE_ENTRY结构中,InLoadOrderLinks指向下一个模块结构,DllBase是模块的基地址,FullDllName是它模块名称的Unicode字符串。

因为我们知道kenel32.dll是第三个模块

0:000> !list -t ntdll!_LIST_ENTRY.Flink  -x "dt _LDR_DATA_TABLE_ENTRY @$extret" 002b3270
---CUT
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`002b3830 - 0x00000000`002b3260 ]
   +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000`002b4980 - 0x00000000`002b3840 ]
   +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000`77970000 - 0x00000000`77985ea0 ]
   +0x030 DllBase          : 0xbaadf00d`0011f000 Void
   +0x038 EntryPoint       : 0x00000000`00420040 Void
   +0x040 SizeOfImage      : 0x2b35c0
   +0x048 FullDllName      : _UNICODE_STRING "kernel32.dll"
   +0x058 BaseDllName      : _UNICODE_STRING "ꩀ矌"
   +0x068 Flags            : 0x77ccaa40
   +0x06c LoadCount        : 0
   +0x06e TlsIndex         : 0
   +0x070 HashLinks        : _LIST_ENTRY [ 0xbaadf00d`4ce7c78b - 0x00000000`00000000 ]
   +0x070 SectionPointer   : 0xbaadf00d`4ce7c78b Void
   +0x078 CheckSum         : 0
   +0x080 TimeDateStamp    : 0
   +0x080 LoadedImports    : (null) 
   +0x088 EntryPointActivationContext : 0x00000000`002b4d20 _ACTIVATION_CONTEXT
   +0x090 PatchInformation : 0x00000000`002b4d20 Void
   +0x098 ForwarderLinks   : _LIST_ENTRY [ 0x00000000`002b36e8 - 0x00000000`002b36e8 ]
   +0x0a8 ServiceTagLinks  : _LIST_ENTRY [ 0x00000000`002b3980 - 0x00000000`002b3750 ]
   +0x0b8 StaticLinks      : _LIST_ENTRY [ 0x00000000`77c95124 - 0x00000000`78d20000 ]
   +0x0c8 ContextInformation : 0x01d00f7c`80e29f8e Void
   +0x0d0 OriginalBase     : 0xabababab`abababab
   +0x0d8 LoadTime         : _LARGE_INTEGER 0xabababab`abababab
   ---CUT

现在知道加载的模块基地址在_LDR_DATA_TABLE_ENTRY的0x30偏移处。

a.通过访问[gs:60h]找到PEB

b.通过在PEB中偏移0x18进入LDR链表。

c.偏移0x20是InMemoryOrderModuleList。

d.InMemoryOrderModuleList中的第3个元素是Kernel32,0x30th offset是模块的基地址。

e.我们要调用ExitProcess,这实际上是来自ntdll.dll的RtlExitUserProcess。Ntdll.dll是InMemoryOrderModuleList中的第二个条目,我也将获取它的基地址并将其存储在r15中供以后使用。我发现这个方法比依靠Kernel32在ntdll中正确执行一个函数更容易和更可靠

http://p7.qhimg.com/t014fa3d2c7879a319c.jpg

从dependency walker中可以看出ExitProcess指向Ntdll.RtlExitUserProcess。


开始编写汇编代码


mov r12, [gs:60h]       ;peb
mov r12, [r12 + 0x18]   ;Peb --> LDR
mov r12, [r12 + 0x20]   ;Peb.Ldr.InMemoryOrderModuleList
mov r12, [r12]          ;2st entry
mov r15, [r12 + 0x20]   ;ntdll.dll base address!
mov r12, [r12]          ;3nd entry
mov r12, [r12 + 0x20]   ;kernel32.dll base address! We go 20 bytes in here as we are already 10 bytes into the _LDR_DATA_TABLE_ENTRY from the InMemoryOrderModuleList

这里将Kernel32的地址放入r12寄存器(r12寄存器不是Clobber寄存器),在shellcode执行期间需要已知保留Kernel32的地址。现在找到了kernel32模块的地址,可以通过kernel32加载其他模块和获取其他函数的地址。

HMODULE WINAPI LoadLibrary(
_In_  LPCTSTR lpFileName
);

LoadLibraryA用于将其他dll模块加载到当前进程,因为shellcode需要与地址无关,不能依赖任何已经在目标进程中的dll。在本例中需要加载user32.dll。
为了使用LoadLibraryA函数,它必须在kernel32.dll中找它的地址,这就需要GetProcAddress函数了。

FARPROC WINAPI GetProcAddress(
_In_  HMODULE hModule,
_In_  LPCSTR lpProcName
);

GetProcAddress需要两个参数,需要获得的函数模块句柄以及函数名。

;find address of loadLibraryA from kernel32.dll which was found above. 
mov rdx, 0xec0e4e8e  ;lpProcName (loadLibraryA hash from ror13)
mov rcx, r12         ;hModule
call GetProcessAddress

一旦我们知道LoadLibraryA的地址,我们可以使用它来加载user32.dll。

“0xec0e4e8e”为函数名称的hash,将该hash赋值给rdx作为函数名参数。

0xec0e4e8e是LoadLibraryA的每个字母ROR 0x13相加所得的总和。这在Shellcode中是常见的,在MetaSploit等项目使用。我写了一个简单的C程序来用来计算这些hash。

#./rot13.exe LoadLibraryA
LoadLibraryA
ROR13 of LoadLibraryA is: 0xec0e4e8e

现在加载user32.dll

;import user32
lea rcx, [user32_dll]
call rax                ;load user32.dll
user_32dll: db 'user32.dll', 0

现在可以获得MessageBox函数的地址了

mov rdx, 0xbc4da2a8     ;hash for MessageBoxA from rot13
mov rcx, rax
call GetProcessAddress

然后执行MessageBox

;messageBox
xor r9, r9            ;uType
lea r8, [title_str]   ;lpCaptopn
lea rdx, [hello_str]  ;lpText
xor rcx, rcx          ;hWnd
call rax              ;display message box  
title_str:  db  '0xdeadbeef',   0
hello_str:  db  'This is fun!', 0

最后使用ExitProcess函数结束进程

VOID WINAPI ExitProcess(
_In_  UINT uExitCode
);

需要注意的是ExitProcess是kernel32所导出的函数,但是这里使用RtlExitUserProcess。

;ExitProcess
mov rdx, 0x2d3fcd70             
mov rcx, r15            ;base address of ntdll
call GetProcessAddress
xor  rcx, rcx           ;uExitCode
call rax

完整的shellcode与GetProcess函数实现如下:

这里使用call/pop指令实现”lea”指令

bits 64
section .text
global start
start:
;get dll base addresses
    sub rsp, 28h                     ;reserve stack space for called functions
    and rsp, 0fffffffffffffff0h      ;make sure stack 16-byte aligned   
    mov r12, [gs:60h]                ;peb
    mov r12, [r12 + 0x18]            ;Peb --> LDR
    mov r12, [r12 + 0x20]            ;Peb.Ldr.InMemoryOrderModuleList
    mov r12, [r12]                   ;2st entry
    mov r15, [r12 + 0x20]            ;ntdll.dll base address!
    mov r12, [r12]                   ;3nd entry
    mov r12, [r12 + 0x20]            ;kernel32.dll base address!
;find address of loadLibraryA from kernel32.dll which was found above. 
    mov rdx, 0xec0e4e8e
    mov rcx, r12
    call GetProcessAddress         
;import user32
    jmp getUser32
returnGetUser32:
    pop rcx
    call rax                        ;load user32.dll
;get messageBox address
    mov rdx, 0xbc4da2a8
    mov rcx, rax
    call GetProcessAddress  
    mov rbx, rax
;messageBox
    xor r9, r9                     ;uType
    jmp getText
returnGetText:
    pop r8                         ;lpCaption
    jmp getTitle
returnGetTitle:
    pop rdx                        ;lpTitle
    xor rcx, rcx                   ;hWnd
    call rbx                       ;display message box 
;ExitProcess
    mov rdx, 0x2d3fcd70             
    mov rcx, r15
    call GetProcessAddress
    xor  rcx, rcx                  ;uExitCode
    call rax       
;get strings    
getUser32:
    call returnGetUser32
    db  'user32.dll'
    db  0x00
getTitle:
    call returnGetTitle
    db  'This is fun!'
    db  0x00
getText:
    call returnGetText
    db  '0xdeadbeef'
    db  0x00
;Hashing section to resolve a function address  
GetProcessAddress:      
    mov r13, rcx                     ;base address of dll loaded 
    mov eax, [r13d + 0x3c]           ;skip DOS header and go to PE header
    mov r14d, [r13d + eax + 0x88]    ;0x88 offset from the PE header is the export table. 
    add r14d, r13d                  ;make the export table an absolute base address and put it in r14d.
    mov r10d, [r14d + 0x18]         ;go into the export table and get the numberOfNames 
    mov ebx, [r14d + 0x20]          ;get the AddressOfNames offset. 
    add ebx, r13d                   ;AddressofNames base. 
find_function_loop: 
    jecxz find_function_finished   ;if ecx is zero, quit 🙁 nothing found. 
    dec r10d                       ;dec ECX by one for the loop until a match/none are found
    mov esi, [ebx + r10d * 4]      ;get a name to play with from the export table. 
    add esi, r13d                  ;esi is now the current name to search on. 
find_hashes:
    xor edi, edi
    xor eax, eax
    cld         
continue_hashing:   
    lodsb                         ;get into al from esi
    test al, al                   ;is the end of string resarched?
    jz compute_hash_finished
    ror dword edi, 0xd            ;ROR13 for hash calculation!
    add edi, eax        
    jmp continue_hashing
compute_hash_finished:
    cmp edi, edx                  ;edx has the function hash
    jnz find_function_loop        ;didn't match, keep trying!
    mov ebx, [r14d + 0x24]        ;put the address of the ordinal table and put it in ebx. 
    add ebx, r13d                 ;absolute address
    xor ecx, ecx                  ;ensure ecx is 0'd. 
    mov cx, [ebx + 2 * r10d]      ;ordinal = 2 bytes. Get the current ordinal and put it in cx. ECX was our counter for which # we were in. 
    mov ebx, [r14d + 0x1c]        ;extract the address table offset
    add ebx, r13d                 ;put absolute address in EBX.
    mov eax, [ebx + 4 * ecx]      ;relative address
    add eax, r13d   
find_function_finished:
    ret

有关GetProcAddress函数的magic请参考Skape的教程。

现在我们编译链接我们的shellcode,然后测试是否可以执行

nasm -f win64 messageBox64bit.asm -o messageBox64bit.obj  
golink /console messageBox64bit.obj
./messageBox64bit.exe

http://p0.qhimg.com/t011f542965ffc01037.jpg

通过这种方式编译出来的shellcode是一个PE可执行文件,这里在将他编译成一个纯shellcode文件。

nasm -f bin messageBox64bit.asm -o messageBox64bit.sc 
xxd -i messageBox64bit.sc
xxd -i messageBox64bit.sc
unsigned char messageBox64bit_sc[] = {
  0x48, 0x83, 0xec, 0x28, 0x48, 0x83, 0xe4, 0xf0, 0x65, 0x4c, 0x8b, 0x24,
  0x25, 0x60, 0x00, 0x00, 0x00, 0x4d, 0x8b, 0x64, 0x24, 0x18, 0x4d, 0x8b,
  0x64, 0x24, 0x20, 0x4d, 0x8b, 0x24, 0x24, 0x4d, 0x8b, 0x7c, 0x24, 0x20,
  0x4d, 0x8b, 0x24, 0x24, 0x4d, 0x8b, 0x64, 0x24, 0x20, 0xba, 0x8e, 0x4e,
  0x0e, 0xec, 0x4c, 0x89, 0xe1, 0xe8, 0x68, 0x00, 0x00, 0x00, 0xeb, 0x34,
  0x59, 0xff, 0xd0, 0xba, 0xa8, 0xa2, 0x4d, 0xbc, 0x48, 0x89, 0xc1, 0xe8,
  0x56, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc3, 0x4d, 0x31, 0xc9, 0xeb, 0x2c,
  0x41, 0x58, 0xeb, 0x3a, 0x5a, 0x48, 0x31, 0xc9, 0xff, 0xd3, 0xba, 0x70,
  0xcd, 0x3f, 0x2d, 0x4c, 0x89, 0xf9, 0xe8, 0x37, 0x00, 0x00, 0x00, 0x48,
  0x31, 0xc9, 0xff, 0xd0, 0xe8, 0xc7, 0xff, 0xff, 0xff, 0x75, 0x73, 0x65,
  0x72, 0x33, 0x32, 0x2e, 0x64, 0x6c, 0x6c, 0x00, 0xe8, 0xcf, 0xff, 0xff,
  0xff, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x66, 0x75, 0x6e,
  0x21, 0x00, 0xe8, 0xc1, 0xff, 0xff, 0xff, 0x30, 0x78, 0x64, 0x65, 0x61,
  0x64, 0x62, 0x65, 0x65, 0x66, 0x00, 0x49, 0x89, 0xcd, 0x67, 0x41, 0x8b,
  0x45, 0x3c, 0x67, 0x45, 0x8b, 0xb4, 0x05, 0x88, 0x00, 0x00, 0x00, 0x45,
  0x01, 0xee, 0x67, 0x45, 0x8b, 0x56, 0x18, 0x67, 0x41, 0x8b, 0x5e, 0x20,
  0x44, 0x01, 0xeb, 0x67, 0xe3, 0x3f, 0x41, 0xff, 0xca, 0x67, 0x42, 0x8b,
  0x34, 0x93, 0x44, 0x01, 0xee, 0x31, 0xff, 0x31, 0xc0, 0xfc, 0xac, 0x84,
  0xc0, 0x74, 0x07, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xeb, 0xf4, 0x39, 0xd7,
  0x75, 0xdd, 0x67, 0x41, 0x8b, 0x5e, 0x24, 0x44, 0x01, 0xeb, 0x31, 0xc9,
  0x66, 0x67, 0x42, 0x8b, 0x0c, 0x53, 0x67, 0x41, 0x8b, 0x5e, 0x1c, 0x44,
  0x01, 0xeb, 0x67, 0x8b, 0x04, 0x8b, 0x44, 0x01, 0xe8, 0xc3
};
unsigned int messageBox64bit_sc_len = 258;

shellcode以16进制字符返回,然后通过我编写的另一个小程序在calc进程中执行该shellcode。需要说明的是这个小程序现在仅仅还是一个测试版,编写这个小程序的目的主要是为了测试另一个开源反汇编项目BeaEngine

http://p1.qhimg.com/t01634db17992b52ab8.jpg

启动这个小程序,将16进制数据拷贝到左边文本框中,选择64位版本,点击Fire Shellcode后,汇编代码将出现在右侧文本框中。实现汇编功能主要为了让这个小程序功能更加完整,通过这个功能也可以反编译一些不知道功能的shellcode。

点击fire后,小程序将运行calc并注入一个线程执行shellcode。

http://p7.qhimg.com/t010b8bee06e6642bb3.jpg

成功!


后记


我希望这篇文章有助于帮助开发Win64 Shellcode,我刚开始写我在研究中学到的东西,希望以后能够继续坚持写。可以在这里下载我使用的一些程序,我将它压缩并放到这里:资源

更新2015年3月18日:我开源的我开发的shellcode Tester,将它放在github:shellcodeTester

写于2014年12月4日


原文链接:https://www.tophertimzen.com/blog/windowsx64Shellcode/

未经允许不得转载:安全路透社 » 【技术分享】Windows x64 shellcode编写指南

赞 (0)
分享到:更多 ()

评论 0

评论前必须登录!

登陆 注册