1. 程式人生 > >64位shellcode程式設計(不錯) Windows x64 Shellcode

64位shellcode程式設計(不錯) Windows x64 Shellcode

Contents

Introduction

Shellcode refers to a chunk of executable machine code (along with any associated data) which is executed after being injected into the memory of a process usually by means of a buffer-overflow type of security vulnerability.  The term comes from the fact that in early exploits against Unix platforms, an attacker would typically execute code that would start a command shell listening on a TCP/IP port, to which the attacker could then connect and have full access to the system.  For the common web-browser and application exploits on Windows today, the “shellcode” is more likely to download and execute another program than spawn a command shell, but the term remains.

In general, shellcode can be thought of as any code that is capable of being executed from an arbitrary location in memory and without relying on services provided by the operating system loader as with traditional executables. Depending on the exploit, additional requirements for shellcode may include small size and avoiding certain byte patterns in the code. In any case, there are two tasks performed by the loader which shellcode must take care of itself:

  1. Getting the addresses of data elements (such as strings referenced by the code)
  2. Getting the addresses of system API functions used

This article describes a shellcode implementation of the x64 assembly program from my Windows Assembly Languages article (refer to that article for general x64 assembly programming issues such as calling conventions and stack usage).  As you’ll see, the main program code doesn’t look much different.  Task #1 above actually turns out to be a non-issue on x64 platforms due to a new feature called RIP-relative addressing.

 Task #2 is what comprises the bulk of the effort.  In fact, the code for looking up API functions is significantly larger and more complex than the main program itself.  The only other difference between the vanilla and shellcode versions of x64 hello world is that the shellcode does not use a .data section, instead placing the strings in the .code section after main.  This is because “sections” are a feature of the executable file format, whereas shellcode needs to be just a single block of code and data.

RIP-Relative Addressing

RIP refers to the instruction pointer register on x64, and RIP-relative addressing means that references to memory addresses being read or written can be encoded as offsets from the currently-executing instruction.  This is not a completely new concept, as jmp and call instructions have always supported relative targets on x86, but the ability to read and write memory using relative addressing is new with x64.

On x86, the labels referring to data variables would be replaced with actual hard-coded memory addresses when the program was assembled and linked, under the assumption that the program would be loaded at a specific base address.  If at runtime the program needed to load at a different base address, the loader would perform relocationby updating all of those hard-coded addresses.  Because shellcode needed to run from anywhere in memory, it needed to determine these addresses dynamically and typically used a trick where the call instruction would push the address just past itself onto the stack as the return address.  This “return address” could then be popped off the stack to get a pointer to the string at runtime:

    call skip
    db ‘Hello world’, 0
skip:
    pop esi      ;esi now points to ‘Hello world’ string

On x64 we do not need this trick.  RIP-relative addressing is not only supported but is in fact the default, so we can simply refer to strings using labels as with ordinary code and it Just Works.

API Lookup Overview

Even the most trivial programs generally need to call various operating system API functions to perform some of type of input/output (I/O) – displaying things to the user, accessing files, making network connections, etc.  On Windows these API functions are implemented in various system DLLs, and in standard application development these API functions can simply be referred to by name.  When the program is compiled and linked, the linker puts information in the resulting executable indicating which functions from which DLLs are required.  When the program is run, the loader ensures that the necessary DLLs are loaded and that the addresses of the called functions are resolved.

Windows also provides another facility that can be used by applications to load additional DLLs and look up functions on demand:  the LoadLibrary() and GetProcAddress() APIs in kernel32.dll.  Not having the benefit of the loader, shellcode needs to use LoadLibrary() and GetProcAddress() for all API functions it uses.  This unfortunately presents a Catch-22:  How does the shellcode get the addresses of LoadLibrary() and GetProcAddress()?

It turns out that an equivalent to GetProcAddress() can be implemented by traversing the data structures of a loaded DLL in memory.  Also, kernel32.dll is always loaded in the address space of every process on Windows,  so LoadLibrary() can be found there and used to load other DLLs.

Developing shellcode using this technique requires a solid understanding of the Portable Executable (PE) file format used on Windows for EXE and DLL files, and the next section of this article assumes some familiarity. The following references and tools may be helpful:

  • Matt Pietrek’s An In-Depth Look into the Win32 Portable Executable File Format: part1 and part2.  Note that this only covers 32-bit and not 64-bit PE files, but the differences are very minor – mostly just widening some memory address fields to 64 bits
  • Daniel Pistelli’s CFF Explorer is a nice GUI tool for viewing and editing PE files, with 64-bit support
  • The dumpbin utility included with Visual C++ (including Express Edition) – the most useful switches for our purposes are /headers and /exports
  • Many of the PE data structures are documented in MSDN under ImageHlp Structures
  • Definitions of the data structures can be found in winnt.h in the Include directory of the Windows SDK
  • The dt command in WinDbg is able to display many of these structures

API Lookup Demo

This demonstration of how to find the address of a function in a loaded DLL can be followed by attaching WinDbg to any 64-bit process (I’m using notepad.exe).  Note that the particular values seen here may be different on your system.

First we’ll get the address of the Thread Environment Block (TEB), sometimes also referred to as the Thread Information Block (TIB). The TEB contains a large number of fields pertaining to the current thread, and on x64 the fields can be accessed as offsets from the GS segment register during program execution (the FS register was used on x86). In WinDbg, the pseudo register $teb contains the address of the TEB.

0:001> r $teb
$teb=000007fffffdb000
0:001> dt _TEB @$teb
ntdll!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x038 EnvironmentPointer : (null)
   +0x040 ClientId         : _CLIENT_ID
   +0x050 ActiveRpcHandle  : (null)
   +0x058 ThreadLocalStoragePointer : (null)
   +0x060 ProcessEnvironmentBlock : 0x000007ff`fffdd000 _PEB
   +0x068 LastErrorValue   : 0
   [...]

The only field from the TEB we are interested in is the pointer to the Process Environment Block (PEB). Note that WinDbg also has a $peb pseudo-register, but in the shellcode implementation we will have to use the GS register to go through the TEB first.

0:001> dt _PEB 7ff`fffdd000
ntdll!_PEB
   +0×000 InheritedAddressSpace : 0 ''
   +0×001 ReadImageFileExecOptions : 0 ''
   +0×002 BeingDebugged    : 0×1 ''
   +0×003 BitField         : 0×8 ''
   +0×003 ImageUsesLargePages : 0y0
   +0×003 IsProtectedProcess : 0y0
   +0×003 IsLegacyProcess  : 0y0
   +0×003 IsImageDynamicallyRelocated : 0y1
   +0×003 SkipPatchingUser32Forwarders : 0y0
   +0×003 SpareBits        : 0y000
   +0×008 Mutant           : 0xffffffff`ffffffff Void
   +0×010 ImageBaseAddress : 0×00000000`ff8b0000 Void
   +0×018 Ldr              : 0×00000000`779a3640 _PEB_LDR_DATA
   [...]

The PEB contains numerous fields with process-specific data and we are interested in the Ldr field at offset 0x18 which points to a structure of type PEB_LDR_DATA.

0:001> dt _PEB_LDR_DATA 779a3640
ntdll!_PEB_LDR_DATA
   +0×000 Length           : 0×58
   +0×004 Initialized      : 0×1 ''
   +0×008 SsHandle         : (null)
   +0×010 InLoadOrderModuleList: _LIST_ENTRY [ 0x00000000`00373040 - 0x39a3b0 ]
   +0×020 InMemoryOrderModuleList: _LIST_ENTRY [ 0x00000000`00373050 - 0x39a3c0 ]
   +0×030 InInitializationOrderModuleList: _LIST_ENTRY [ 0x00000000`00373150 - 0x39a3d0 ]
   +0×040 EntryInProgress  : (null)
   +0×048 ShutdownInProgress : 0 ''
   +0×050 ShutdownThreadId : (null)

The PEB_LDR_DATA structure contains three linked lists of loaded modules – InLoadOrderModuleList, InMemoryOrderModuleList, and InInitializationOrderModuleList. A module or image refers to any PE file in memory – the main program executable as well as any currently-loaded DLLs. All three lists contain the same elements just in a different order, with the one exception that InInitializationOrderModuleList only contains DLLs and excludes the main executable.

The elements of these lists are of type LDR_DATA_TABLE_ENTRY, though you can’t tell from the previous output because they are only shown as LIST_ENTRY which is the generic linked list header datatype used throughout Windows.  A LIST_ENTRY simply consists of a forward and back pointer for creating circular, doubly-linked lists.  The address of the _LIST_ENTRY within the _PEB_LDR_DATA structure represents the list head. When traversing the circular list, arriving back at the list head is the way to know when complete.

0:001> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
   +0×000 Flink            : Ptr64 _LIST_ENTRY
   +0×008 Blink            : Ptr64 _LIST_ENTRY

The !list command provides the ability to traverse these types of lists and execute a specific command for each element in the list (in this case displaying the element as an LDR_DATA_TABLE_ENTRY data structure). WinDbg commands can get nasty-looking sometimes but are quite powerful. Here we display the InLoadOrderModuleList with list head at offset 0x10 from the beginning of the PEB_LDR_DATA structure (very long output truncated to show just part of one element):

0:001> !list -t ntdll!_LIST_ENTRY.Flink -x "dt _LDR_DATA_TABLE_ENTRY @$extret" 779a3640+10
   [...]
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`00333620 - 0x333130 ]
   +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000`00333630 - 0x333140 ]
   +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000`003344e0 - 0x333640 ]
   +0x030 DllBase          : 0x00000000`77650000 Void
   +0x038 EntryPoint       : 0x00000000`7766eff0 Void
   +0x040 SizeOfImage      : 0x11f000
   +0x048 FullDllName      : _UNICODE_STRING "C:\Windows\system32\kernel32.dll"
   +0x058 BaseDllName      : _UNICODE_STRING "kernel32.dll"
   +0x068 Flags            : 0x84004
   [...]

Interesting fields for us within an LDR_DATA_TABLE_ENTRY structure are DllBase at 0x30 and BaseDllName at 0x58. Note that BaseDllName is a UNICODE_STRING, which is an actual data structure and not simply a null-terminated Unicode string. The actual string data can be found at offset 0x8 in the structure, for a total of 0x60 from BaseDllName.

0:001> dt _UNICODE_STRING
ntdll!_UNICODE_STRING
   +0×000 Length           : Uint2B
   +0×002 MaximumLength    : Uint2B
   +0×008 Buffer           : Ptr64 Uint2B

Armed with this knowledge, we now have the ability to obtain the base address of any DLL given it’s name. Once we have the base address we can traverse the DLL in memory to locate any function exported by the DLL. Also note that the return value of LoadLibrary() is in fact a DLL base address. The base address of a loaded DLL can also be obtained in WinDbg with the lm command. Let’s take a look at kernel32.dll:

0:001> lm m kernel32
start             end                 module name
00000000`77650000 00000000`7776f000   kernel32   (deferred)

An interesting feature of the PE file and loader is that the PE file format in memory is exactly the same as it is on disk, at least as far as the headers. It’s not exactly true that the entire file is read verbatim into memory, because each section is loaded at a certain byte alignment in memory (typically a multiple of 4096, the virtual memory page size) that may be different from where it falls in the file. Also, some sections (like a debug data section) may not be read into memory at all. However, when we look at the DLL base address in memory, we can expect to find what we see at the beginning of any PE file: a DOS “MZ” header. That’s an IMAGE_DOS_HEADER structure to be exact:

0:001> dt _IMAGE_DOS_HEADER 77650000
ntdll!_IMAGE_DOS_HEADER
   +0×000 e_magic          : 0x5a4d
   +0×002 e_cblp           : 0×90
   +0×004 e_cp             : 3
   +0×006 e_crlc           : 0
   +0×008 e_cparhdr        : 4
   +0x00a e_minalloc       : 0
   +0x00c e_maxalloc       : 0xffff
   +0x00e e_ss             : 0
   +0×010 e_sp             : 0xb8
   +0×012 e_csum           : 0
   +0×014 e_ip             : 0
   +0×016 e_cs             : 0
   +0×018 e_lfarlc         : 0×40
   +0x01a e_ovno           : 0
   +0x01c e_res            : [4] 0
   +0×024 e_oemid          : 0
   +0×026 e_oeminfo        : 0
   +0×028 e_res2           : [10] 0
   +0x03c e_lfanew         : 0n224

The e_lfanew field at 0x3c (which for some reason is displayed as a decimal number even though everything else is hex) contains the byte offset to the NT header (IMAGE_NT_HEADERS64). Converting 224 to hex 0xe0 and adding to the image base will point to the NT header at 0x776500e0. We can use the –r option (recursive) to expand the embedded OptionalHeader field (which is a misnomer as it is required and always present):

0:001> dt -r _IMAGE_NT_HEADERS64 776500e0
ntdll!_IMAGE_NT_HEADERS64
   +0×000 Signature        : 0×4550
   +0×004 FileHeader       : _IMAGE_FILE_HEADER
      +0×000 Machine          : 0×8664
      +0×002 NumberOfSections : 6
      +0×004 TimeDateStamp    : 0x4a5bdfdf
      +0×008 PointerToSymbolTable : 0
      +0x00c NumberOfSymbols  : 0
      +0×010 SizeOfOptionalHeader : 0xf0
      +0×012 Characteristics  : 0×2022
   +0×018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER64
      +0×000 Magic            : 0x20b
      +0×002 MajorLinkerVersion : 0×9 ''
      +0×003 MinorLinkerVersion : 0 ''
      [...]
      +0×068 LoaderFlags      : 0
      +0x06c NumberOfRvaAndSizes : 0×10
      +0×070 DataDirectory    : [16] _IMAGE_DATA_DIRECTORY
      [...]

The DataDirectory field is located a total of 0x88 bytes from the NT headers (offset 0x70 from OptionalHeader which is 0x18 from the NT headers). This is an array of 16 elements corresponding to the various types of data in a PE file.

0:001> dt -a16c _IMAGE_DATA_DIRECTORY 776500e0+88
ntdll!_IMAGE_DATA_DIRECTORY
[0] @ 0000000077650168 +0×000 VirtualAddress 0xa0020  +0×004 Size 0xac33
[1] @ 0000000077650170 +0×000 VirtualAddress 0xf848c  +0×004 Size 0x1f4
[2] @ 0000000077650178 +0×000 VirtualAddress 0×116000  +0×004 Size 0×520
[3] @ 0000000077650180 +0×000 VirtualAddress 0x10c000  +0×004 Size 0×9810
[4] @ 0000000077650188 +0×000 VirtualAddress 0  +0×004 Size 0
[5] @ 0000000077650190 +0×000 VirtualAddress 0×117000  +0×004 Size 0x7a9c
[6] @ 0000000077650198 +0×000 VirtualAddress 0x9b7dc  +0×004 Size 0×38
[7] @ 00000000776501a0 +0×000 VirtualAddress 0  +0×004 Size 0
[8] @ 00000000776501a8 +0×000 VirtualAddress 0  +0×004 Size 0
[9] @ 00000000776501b0 +0×000 VirtualAddress 0  +0×004 Size 0
[10] @ 00000000776501b8 +0×000 VirtualAddress 0  +0×004 Size 0
[11] @ 00000000776501c0 +0×000 VirtualAddress 0x2d8  +0×004 Size 0×408
[12] @ 00000000776501c8 +0×000 VirtualAddress 0x9c000  +0×004 Size 0x1c70
[13] @ 00000000776501d0 +0×000 VirtualAddress 0  +0×004 Size 0
[14] @ 00000000776501d8 +0×000 VirtualAddress 0  +0×004 Size 0
[15] @ 00000000776501e0 +0×000 VirtualAddress 0  +0×004 Size 0

We are interested in the Export Directory which is the first one in the list having VirtualAddress 0xa0020 and Size 0xac33. See the MSDN documentation of the IMAGE_DATA_DIRECTORY structure for a reference on which type of data goes with each array element.

A virtual address, also called a Relative Virtual Address (RVA) is an offset from the base load address of the module. RVAs are used extensively in PE files, including for the pointers to the function names and function addresses in the export table. To get the actual memory address pointed to by an RVA, simply add the base address of the module.

(For convenience, note that the !dh command can be used to automatically display much of the PE header information we’ve extracted manually so far.)

Given that the Export Directory begins at RVA 0xa0020, we add the base address 0x77650000 and should therefore expect to find an IMAGE_EXPORT_DIRECTORY structure at 0x776f0020. Unfortunately IMAGE_EXPORT_DIRECTORY is not understood by the dt command or documented in MSDN, so we will have to refer to the structure definition in winnt.h:

?
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD   Characteristics; DWORD   TimeDateStamp; WORD    MajorVersion; WORD    MinorVersion; DWORD   Name; DWORD   Base; DWORD   NumberOfFunctions; DWORD   NumberOfNames; DWORD   AddressOfFunctions;     // RVA from base of image DWORD   AddressOfNames;         // RVA from base of image

相關推薦

64shellcode程式設計(不錯) Windows x64 Shellcode

Contents Introduction Shellcode refers to a chunk of executable machine code (along with any associated data) which is executed after being i

win7 64專業版下的x64編譯問題

選擇 得到 位數 int 入口 frame iso bin pointer 在Django的開發過程中,碰到一個問題,就是所有本地庫的位數必須是相同的,於是某些庫需要重新編譯一下,工作環境,不能用盜版程序,VC++ 2008\2010 Express版本身都不支持X64的

dll檔案3264檢測工具以及Windows資料夾SysWow64的坑

自從作業系統升級到64位以後,就要不斷的需要面對32位、64位的問題。相信有很多人並不是很清楚32位程式與64位程式的區別,以及Program Files (x86),Program Files的區別。同時,對於程式的dll檔案應該放到System32資料夾,還是SysW

NanoPC-T3 64裸機程式設計 —— 啟動和執行狀態切換

參考: https://github.com/metro94/s5p6818_spl https://github.com/trebisky/Fire3/tree/master/Boot_NSIH https://github.com/SamsungARTIK/bl1-artik710 https:/

Windows64程式設計

C/C++僅僅定義了這些基本資料型別之間的關係,並沒有定義嚴格定義它們的字長。在不同的平臺上,根據編譯器不同的實現,它們的字長如下表所示:   資料型別 LP64 ILP64 LLP64 ILP32 LP32  char 8 8 8

Windows程式設計】如何判斷作業系統是64還是32

我為我基礎知識掌握的薄弱,感到深深的“折服”。感覺從知識體系的掌握上就有問題!!需要探索性的改進自身的知識架構。加油吧…… 一,不同位數作業系統下型別簡介 型別 32位作業系統(位元組) 64位作業系統(位元組) char 1 1 short 2 2 int 4 4 l

【pwnable.kr】 asm seccomp sadbox & 64shellcode 讀 flag

題目資訊 nc之後檢視資訊如下。根據readme提示,本關是要求登陸伺服器後,nc 9026埠連線asm程式來讀flag。flag檔名很長。 看了原始碼發現程式做了如下操作。mmap了一塊記憶體讀取shellcode並執行,開啟了seccomp沙箱[1],限制只能

棧溢位攻擊系列:shellcode在linux x86 64攻擊獲得root許可權(三)linux下程序中的多使用者

在linux程序中會存在兩個使用者狀態,一種是實際使用者ID, 一種是有效使用者ID 實際使用者ID uid, 就是我是誰,也就是你在操作時候的使用者ID, 通常你可以用命令id 來檢視自己的資訊 >id uid=1005(raintung) gid=100(use

棧溢位攻擊系列:shellcode在linux x86 64攻擊獲得root許可權(二)shellcode

shellcode 是一組指令opcode, 是可以被程式執行,因為shellcode是要直接操作暫存器和函式,所以opcode 必須是十六進位制的形式。 既然是攻擊,那shellcode 主要的目的是呼叫系統函式,而在x86下 在linux下有兩種方式。 第一種是通過直接

如何在w7-64上安裝postgresql-9.6.2-1-windows-x64

href 此外 分開 賬戶 需要 管理系統 輸入密碼 sys gem PostgreSQL安裝:一、windows下安裝過程安裝介質:postgresql-9.6.2-1-windows-x64(166M),安裝過程非常簡單,過程如下:1、開始安裝: 2、選擇程序安

windows 64下 Octave 不能畫圖的解決辦法

window windows pac class plot ace 窗口 strong n) 如果不能畫圖,可能需要更改圖形工具包。 1、首先,查看當前的工具包。在Octave命令行中鍵入 graphics_toolkit,結果如下: 

windows server 2008 64MySQL5.6免安裝版本配置說明

zip mage 系統 9.png 技術 版本 變量 成功 過程 1 通過官網下載MySQL5.6版本壓縮包,mysql-5.6.36-winx64.zip; 2 在D盤創建目錄,比如D:\MySQL,將mysql-5.6.36-winx64.zip解壓縮到該目錄下,如下

dumpbin判斷windows程序是32還是64(包括DLL)

tro 信息 option hand article tools style 安裝 net http://blog.csdn.net/csfreebird/article/details/10105681 dumpbin /HEADERS gdal18.dll(or xx

針對Windows 64系統中Matlab沒有LED Control Activex控件的解決方法

its span activex cti 64bit .html gauge con windows Win 10 64bits系統中Matlab 64位軟件沒有LED Control Activex控件,LED ActiveX Control控件位於Gauges Bl

qt4.8.7 源碼在win7+vs2010環境的x64編譯(qt 64)

ror rtu con build htbox mat spec 安裝 ssis 由於qt官網上,沒有直接提供x64的安裝包,但由於項目需要x64的qt,所以,小編不得不下載qt的源碼,經歷了一次長達約4個小時的編譯過程。今年國慶7天,就遭這事上了,哈哈~~~ 幾個下載鏈

windows平臺vs2010編譯64libiconv與libxml2

nmake gnu libiconv fix bat sources files 解壓 flags (一)安裝libiconv下載路徑https://ftp.gnu.org/pub/gnu/libiconv/註意這裏選擇libiconv-1.11.1版本,因為之後的版本沒有

Windows 64下安裝Redis

conf album targe 展示 ef7 文件夾 文件解壓 跳過 down 官方網站:http://redis.io/ 官方下載:http://redis.io/download 可以根據需要下載不同版本 windows版:https://github.com/myt

關於64 windows&linux雙系統引導問題

start trac track uid too 處理 blog windows for 換了臺本子win7 64位,抽空做個雙系統,裝了下linux。 遇到開機問題:進linux可以正常使用,進win7花屏死機,初步估計是grub(此時的boot sector位grub)

windows 64 系統非HOOK方式監控進程創建

mman log syntax typedef pan set parameter logs hand 以下內容參考黑客防線2012合訂本354頁 MSDN 原話: The PsSetCreateProcessNotifyRoutineEx routine regist

Windows 64下安裝Redis詳細教程

enc 展示 eap redis 安裝 htm isp benchmark 新建 ref 工具/原料 Windows 64位操作系統 Redis 安裝包(當前教程版本2.8.12) 方法/步驟 在D盤新建文件夾【redis】,右鍵解壓Re