本文已参与「新人创作礼」活动,一起开启掘金创作之路。
《加密与解密 漏洞篇 14.2 Shellcode》
@[toc]
前言
Shellcode实际上是一段可以独立执行的代码(也可以认为是一段填充数据),在触发了缓冲区溢出漏洞并获取了eip指针的控制权后,通常会将eip指针指向Shellcode以完成漏洞利用过程。
eip指针是指令指针的一种,指令指针IP/EIP/RIP的基本作用是指向要执行的下一条地址
Shellcode主要工作流程:
Shellcode的结构
Shellcode在漏洞样本中的存在形式一般为一段可以自主运行的汇编代码。 特点:
- 它不依赖任何编译环境,也不能像在DE中直接编写代码那样调用API函数名称来实现功能。
- 它通过主动查找DLL基址并动态获取API地址的方式来实现API调用,然后根据实际功能调用相应的API函数来完成其自身的功能。
Shellcode分为两个模块,分别是基本模块和功能模块,结构如图14.10所示。
基本模块
基本模块用于实现Shellcode初始运行、获取Kernel32基址及获取API地址的过程。
1.获取Kernel32基址
获取Kernel32基址的常见方法有:
- 暴力搜索、
- 异常处理链表搜索
- TEB(Thread Environment Block)搜索。
这里只介绍目前最常用的动态获取Kernel32.dl基址的方法-TEB查找法。
其原理是:
在NT内核系统中,fs寄存器指向TEB结构,TEB+0x30偏移处指向PEB(Process Environment Block)结构,PEB+0x0c偏移处指向PEB_LDR_DATA结构,PEB_LDR_DATA+0x1c偏移处存放着程序加载的动态链接库地址,第1个指向Ntdll.dll,第2个就是Kernel.32.dll的基地址。
FS寄存器指向当前活动线程的TEB结构(线程结构) 偏移 说明 000 指向SEH链指针 004 线程堆栈顶部 008 线程堆栈底部 00C SubSystemTib 010 FiberData 014 ArbitraryUserPointer 018 FS段寄存器在内存中的镜像地址 020 进程PID 024 线程ID 02C 指向线程局部存储指针 030 PEB结构地址(进程结构) 034 上个错误号
如图14.11所示。
代码实现:
(使用了内联汇编,注意vs不支持x64的汇编)
//Windows XP SP3+VC6.0,编译选项:Debug(默认配置)
#include <stdio.h>
#include <stdio.h>
#include <Windows.h>
int main(int argc, char*argv[])
{
DWORD hKernel32 = 0;
_asm{
mov eax,fs:[30h]
mov eax,dword ptr[eax + 0ch]
mov esi,dword ptr[eax + 1ch]
lodsd
mov eax,dword ptr[eax + 8]
mov hKernel32, eax
; 获取Kernel32基址
}
printf("hKernel32 0x%x\n", hKernel32);
system("pause");
return 0;
}
3.获得API地址
从DLL文件中获取API地址的方法如图14.12所示,步骤如下:
- 在DLL基址+3ch偏移处获取e lfanew的地址,即可得到PE文件头。
- 在PE文件头的78h偏移处得到函数导出表的地址。
- 在导出表的1ch偏移处获取AddressOfFunctions的地址,在导出表的20h偏移处获取AddressOfNames的地址,在导出表的24h偏移处获取AddressOfNameOrdinalse的地址。
- AddressOfFunctions函数地址数组和AddressOfNames函数名数组通过函数AddressOfName Ordinalse一一对应。
在实际应用中,如果API函数名称直接以明文出现,就会降低Shellcode的分析难度。 而且, API函数名称占用的空间一般都比较大,这会使Shellcode的体积跟着增大。要知道,在内存中用于存放Shellcode的空间可谓寸土寸金,所以,黑客们想了一个好办法,利用Hash算法将要获取的函数名称转换为一个4字节的Hash值,在搜索过程中按此算法计算DLL中的文件名称的Hash值,对比两个Hash值是否相同。这样就有效减小了Shellcode的体积,同时提高了Shellcode的隐蔽性。
获取API地址示例代码: (参考 加密与解密代码 可私信我获得)
FindApi:
push ecx
push ebp
mov esi, dword ptr[ebx + 3Ch] // e_lfanew
mov esi, dword ptr[esi + ebx + 78h]// EATAddr
add esi, ebx
push esi
mov esi, dword ptr[esi + 20h] //AddressOfNames
add esi, ebx
xor ecx, ecx
dec ecx
Find_Loop :
inc ecx
lods dword ptr[esi]
add eax, ebx
xor ebp, ebp
//计算hash值
Hash_Loop :
movsx edx, byte ptr[eax]
cmp dl, dh
je hash_OK
ror ebp, 7
add ebp, edx
inc eax
jmp Hash_Loop
hash_OK :
//判断hash值是否相等
cmp ebp, dword ptr[edi]
jnz Find_Loop
pop esi
mov ebp, dword ptr[esi + 24h] //Ordinal Table
add ebp, ebx
mov cx, word ptr[ebp + ecx * 2]
mov ebp, dword ptr[esi + 1Ch] //Address Table
add ebp, ebx
mov eax, dword ptr[ebp + ecx * 4]
add eax, ebx
另外,因为Shellcode需要实现的功能不同,所以调用的可能不仅仅是Kernel32中的API。 如何解决这个问题呢? 在得到Kernel32的基址之后,通过上面的方法获取Kernel32里的两个重要API的地址 (即LoadLibrary和GetProcessAddress) 。通过它们的组合就可以获取任意DLL中的API地址了。
功能模块
功能模块就是实现漏洞利用目的的那部分Shellcode.。前面介绍的基础模块所做的工作,其目的就是在这里实现相应的功能。下面介绍Shellcode的几种常见功能:
1.下载执行(Download & Execute)
具有这个功能的Shellcode最常被浏览器类漏洞样本使用,其功能就是从指定的URL下载一个exe文件并运行,工作流程如图14.13所示。
2.捆绑 (Binder)
具有这个功能的Shellcode最常见于Office等漏洞样本中,其功能是将捆绑在样本自身上的exe数据释放到指定目录中并运行,工作流程如图14.14所示。
3.反弹shell
具有这个功能的Shellcode多见于主动型远程溢出漏洞样本中,攻击者可以借助NC等工具,在实施攻击后获取一个远程She以执行任意命令,工作流程如图14.15所示。
Shellcode通用技术
Shellcode编写
Shellcode利用提取和调试
反Shellcode注入和反反Shellcode注入
反Shellcode-什么是DEP保护
DEP将非代码段的页表属性设置成“不可执行”,一旦系统从这些地址空间进行取指令操作, CPU就会报告“内存违规”异常,进而“杀死”进程。栈空间也被操作系统设置了“不可执行”属性,因此,注入栈中的Shellcode就无法执行了。
开启DEP保护
控制面板->系统和安全->系统->高级系统设置->高级->性能->数据执行保护
反反Shellcode注入-绕过DEP保护的方法
ROP技术