YYY。。溢出懂点点。有问题还指正。。
我们要知道,EIP指向的是可执行节,否则我们可以认为这是一次恶意攻击。SHELLCODE一般是会调用一些系统调用的
,我们可以从这里入手来检测。
栈回溯: 通过HOOK常见的API,判断其返回地址来判断。
缺点: 一些API没HOOK(没HOOK W版本,或者漏了NTDLL.DLL),伪造EBP。
这里我们提出一种办法来检测。
首先,API是在某个模块里,一般搜索API会使用到 某个模块的基地址
1 通过PEB获取。
2 SEH获得:默认的异常处理函数时在KERNEL32.DLL里面的,SEH链最后一个异常处理函数的地址,以64K对齐向上查
找MZ标记获取基地址
3 TEB: TEB(FS:[0X18]) + 0X1C是kernel32的地址空间,接着搜MZ标记。
我们重点是针对第一种办法,后面俩中我们去掉MZ标记就可以了。
去掉MZ标记
BOOL NO_MZ_PE(ULONG pid)
{
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
HANDLE hProcess;
ULONG Modulebase, dwOldProtect = 0;;
USHORT ReadFromProcess=0,WriteToMemory=0;
hProcess = OpenProcess(PROCESS_ALL_ACCESS,TRUE,pid);
if(hProcess==NULL)
{
printf("failed to open process!!\n");
return FALSE;
}
hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid );
if( hModuleSnap == INVALID_HANDLE_VALUE )
{
printf("CreateToolhelp32Snapshot failed!!\n");
return( FALSE );
}
me32.dwSize = sizeof( MODULEENTRY32 );
if( !Module32First( hModuleSnap, &me32 ) )
{
CloseHandle( hModuleSnap );
return( FALSE );
}
do
{
if(!strcmpi(me32.szModule,"kernel32.dll"))
{
Modulebase=(DWORD)me32.modBaseAddr;
printf("module :%s found at :%x\n","kernel32.dll",Modulebase);
break;
}
} while( Module32Next( hModuleSnap, &me32 ) );
VirtualProtectEx(hProcess,(PVOID)Modulebase, 2, PAGE_READWRITE, &dwOldProtect);
if(!WriteProcessMemory(hProcess,(PVOID)Modulebase, (void *)&WriteToMemory, 2, NULL))
printf("Write MZ Failed!!\n");
ReadProcessMemory(hProcess,
(LPCVOID)((char*)(Modulebase+0x3c)), //Offset To NT Header
&ReadFromProcess,
sizeof(USHORT),
NULL);
VirtualProtectEx(hProcess,(PVOID)(Modulebase+ReadFromProcess), 2, PAGE_READWRITE, &dwOldProtect);
if(!WriteProcessMemory(hProcess,(PVOID)(Modulebase+ReadFromProcess), (void *)&WriteToMemory, 2, NULL))
printf("Write MZ Failed!!\n");
CloseHandle(hProcess);
CloseHandle( hModuleSnap );
return( TRUE );
}
通过PEB获取基地址比较简单,很多SHELLCODE都用。
返回地址异常的判断
1 根据 NT_TIB中的栈信息: 不可靠,可被修改
2 根据EBP+X来得到返回地址判断所在模块,不可靠,因为SHELLCODE可能复制到.DATA段
我们简单的这么认为: EIP不是在已知的模块内,或者在可读可写(非.TEXT之类的),栈内就是可能的溢出
为了定位API,SHELLCODE会访问PEB来获取模块基地址
一般会有如下的代码
mov eax, fs:0x30
mov eax,[eax+0xc]
mov esi,[eax+0x1c]
lodsd
mov eax,[eax+0x08] /// kernel32地址
只要我们对 eax+0x08 下一个读类型的硬件断点就可以了。这样 mov eax,[eax+0x08] 就会符合条件断下来,我们通
过挂接INT 1,分析EIP就可以了。
DR0-DR3是设置断点的地址 ,我们可以吧剩余的断在常见API上。
可能在多线程的环境下,我们需要 DR7的 GD,全局检测启用,位13,防止线程切换时,DRX被切换。
R/W位(21,20),11表示读写
LEN 一般是双字对齐吧,11就可以
DR7中,将保留位15,14,12,11填0,10填1