注册了这么长时间还是临时会员,虽然很惬意,但是怪不好意思的,于是决定发一篇文章赚点Kx
转载请注明出处
正文开始:
当程序发生异常的时候,一共有两种处理异常的方式。一种是SEH,这个不在本次的讨论范围内。另一种就是VEH了,那么VEH到底是什么呢?下面来随我体验VEH之旅。
在Windows系统中有一个双向链表,每当操作系统发生异常的时候,系统会检测进程的PEB结构中的EnvironmentUpdateCount元素,当PEB. EnvironmentUpdateCount符合要求的时候,系统将会遍历VEH异常链表,VEH异常处理函数会得到执行。
在Windows中有两个函数:
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
这两个函数就是向VEH表链添加异常处理函数的,所不同的是AddVectoredExceptionHandler添加的函数会在SEH异常函数之前执行,而AddVectoredContinueHandler添加的函数,会在SEH异常函数之后执行
参数说明:
FirstHandler:是否将VEH函数插入到VEH链表头,插入到链表头的函数先执行。
VectoredHandler 异常处理函数指针
上面是概述,下面说VEH原理
打开VS编辑代码如下,
生成的程序用OD载入,所有代码如下
我们发现其实AddVectoredExceptionHandler是封装了一下ntdll的RtlAddVectoredExceptionHandler函数
我们用IDA打开Ntdll,找到RtlAddVectoredExceptionHandler函数,情形如下
我们发现,RtlAddVectoredExceptionHandler函数其实是对RtlpAddVectoredHandler的封装,
写成c代码样式如下
PVOID WINAPI RtlAddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
)
{
return RtlpAddVectoredHandler(FirstHandler,VectoredHandler,0);
}
看到RtlpAddVectoredHandler的第三个参数被设置成了0,
如果此时你跟踪的是AddVectoredContinueHandler,你会发现此时第三个参数被设置成了1
AddVectoredContinueHandler和AddVectoredExceptionHandler差别就差在第三个参数上,所以才导致AddVectoredExceptionHandler在SEH之前执行,AddVectoredContinueHandler在SEH之后执行。
我们再跟入RtlpAddVectoredHandler函数,情形如下
=============== S U B R O U T I N E =======================================
.text:77EE51E0
.text:77EE51E0 ; Attributes: bp-based frame
.text:77EE51E0
.text:77EE51E0 ; __stdcall RtlpAddVectoredHandler(x, x, x)
.text:77EE51E0 _RtlpAddVectoredHandler@12 proc near ; CODE XREF: RtlAddVectoredExceptionHandler(x,x)+Dp
.text:77EE51E0 ; RtlAddVectoredContinueHandler(x,x)+Dp
.text:77EE51E0
.text:77EE51E0 arg_0 = dword ptr 8
.text:77EE51E0 arg_4 = dword ptr 0Ch
.text:77EE51E0 arg_8 = dword ptr 10h
.text:77EE51E0
.text:77EE51E0 ; FUNCTION CHUNK AT .text:77ECF76D SIZE 00000012 BYTES
.text:77EE51E0
.text:77EE51E0 mov edi, edi
.text:77EE51E2 push ebp
.text:77EE51E3 mov ebp, esp
.text:77EE51E5 mov eax, large fs:18h
.text:77EE51EB mov eax, [eax+30h]
.text:77EE51EE push esi
.text:77EE51EF push 10h ; Size
.text:77EE51F1 push 0 ; int
.text:77EE51F3 push dword ptr [eax+18h] ; int
.text:77EE51F6 call _RtlAllocateHeap@12 ; RtlAllocateHeap(x,x,x)
.text:77EE51FB mov esi, eax
.text:77EE51FD test esi, esi
.text:77EE51FF jz short loc_77EE5263
.text:77EE5201 push ebx
.text:77EE5202 push edi
.text:77EE5203 push [ebp+arg_4]
.text:77EE5206 mov dword ptr [esi+8], 1
.text:77EE520D call _RtlEncodePointer@4 ; RtlEncodePointer(x)
.text:77EE5212 mov ebx, [ebp+arg_8]
.text:77EE5215 imul ebx, 0Ch
.text:77EE5218 add ebx, offset _LdrpVectorHandlerList
.text:77EE521E push ebx
.text:77EE521F mov [esi+0Ch], eax
.text:77EE5222 lea edi, [ebx+4]
.text:77EE5225 call _RtlAcquireSRWLockExclusive@4 ; RtlAcquireSRWLockExclusive(x)
.text:77EE522A cmp [edi], edi
.text:77EE522C jnz short loc_77EE5245
.text:77EE522E mov ecx, large fs:18h
.text:77EE5235 mov eax, [ebp+arg_8]
.text:77EE5238 mov ecx, [ecx+30h]
.text:77EE523B add eax, 2
.text:77EE523E add ecx, 28h
.text:77EE5241 lock bts [ecx], eax
.text:77EE5245
.text:77EE5245 loc_77EE5245: ; CODE XREF: RtlpAddVectoredHandler(x,x,x)+4Cj
.text:77EE5245 cmp [ebp+arg_0], 0
.text:77EE5249 jz loc_77ECF76D
.text:77EE524F mov eax, [edi]
.text:77EE5251 mov [esi], eax
.text:77EE5253 mov [esi+4], edi
.text:77EE5256 mov [eax+4], esi
.text:77EE5259 mov [edi], esi
.text:77EE525B
.text:77EE525B loc_77EE525B: ; CODE XREF:RtlpAddVectoredHandler(x,x,x)-15A66j
.text:77EE525B push ebx
.text:77EE525C call _RtlReleaseSRWLockExclusive@4 ; RtlReleaseSRWLockExclusive(x)
.text:77EE5261 pop edi
.text:77EE5262 pop ebx
.text:77EE5263
.text:77EE5263 loc_77EE5263: ; CODE XREF: RtlpAddVectoredHandler(x,x,x)+1Fj
.text:77EE5263 mov eax, esi
.text:77EE5265 pop esi
.text:77EE5266 pop ebp
.text:77EE5267 retn 0Ch
.text:77EE5267 _RtlpAddVectoredHandler@12 endp
.text:77EE5267
经过分析VEH链表是下面这个样子的
typedef struct _VECTORED_LIST//表链结构
{
_VECTORED_LIST* pNext;//表链的向后指针
_VECTORED_LIST* pPrevious; //表链的向前指针
DWORD dwMark;//一个有效标记,
PVECTORED_EXCEPTION_HANDLER VectoredFunction;//加密后的异常处理函数指针
}VECTORED_LIST ,*PVECTORED_LIST;
typedef struct _VECTORED_LIST_ENTR//表链入口
{
DWORD dwUnknown;//没弄明白是干啥的
PVECTORED_LIST Head;//记录了VECTORED_LIST表链的第一个
PVECTORED_LIST Tail; //记录了VECTORED_LIST表链的最后一个
}VECTORED_LIST_ENTR,*PVECTORED_LIST_ENTR;
VECTORED_LIST_ENTR结构是表链的一个入口,所有的表链都挂载这个入口上,真正的表链结构是VECTORED_LIST
下面再来看RtlpAddVectoredHandler函数,RtlpAddVectoredHandler逆成C是下面的样子,这才是正主。
PVOID WINAPI RtlpAddVectoredHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler,
_In_ ULONG Key
)
{
PPEB pPeb=0;
__asm
{
//取得PEB地址
mov eax,fs:[0x18]
mov eax,[eax+0x30]
mov pPeb,eax
}
//申请内存
PVECTORED_LIST vl=RtlAllocateHeap(pPeb->DefaultHeap,0,sizeof(VECTORED_LIST));
if (vl==0)
{
return 0;
}
vl->dwMark=1;
//加密指针,把参数VectoredHandler加密
DWORD dwEncodePointer=RtlEncodePointer(VectoredHandler);
//LdrpActiveThreadCount是全局变量
PVECTORED_LIST_ENTR pVectordListEntr=LdrpActiveThreadCount;
//根据key选择挂在哪一个链上,key==0的时候所挂载的链上的函数,是从RtlAddVectoredExceptionHandler来的,这一条链上是要在SEH执行前执行的
pVectordListEntr=&pVectordListEntr[Key];
//Slim读写锁
RtlAcquireSRWLockExclusive(pVectordListEntr->Head);
//看看链表是否为空,自己指向自己就说明链表为空
if (pVectordListEntr->Head==(PVECTORED_LIST)&pVectordListEntr->Head)
{
//标志位,很重要,如果不设置这位,将不会检测VEH链
pPeb->EnvironmentUpdateCount|=(0x1<<(Key+2));
}
if (FirstHandler==0)
{
//放在链表的后面
vl->pNext=(PVECTORED_LIST)pVectordListEntr;
vl->pPrevious=pVectordListEntr->Tail;
pVectordListEntr->Tail->pNext=vl;
pVectordListEntr->Tail=vl;
}
else
{
//放在链表前面
vl->pNext=pVectordListEntr->Head;
vl->pPrevious=(PVECTORED_LIST)pVectordListEntr;
pVectordListEntr->Head->pPrevious=vl;
pVectordListEntr->Head=vl;
}
RtlReleaseSRWLockExclusive(pVectordListEntr->Head);
return vl;
}
从上面的代码可以看出VEH的地址是存储在LdrpActiveThreadCount这个全局变量里的,而VEH有两个表链入口,表链入口0,和链表入口1,
表链入口0所挂载的异常处理函数,是函数AddVectoredExceptionHandler挂载上去的,换句话说,表链入口0所挂载的函数是要在SEH异常之前执行的。
而链表入口1所挂载的异常处理函数,是通过AddVectoredContinueHandler挂上去的,也就是说,表链入口1所挂载的函数是要在SEH异常之后执行的。
AddVectoredContinueHandler和AddVectoredExceptionHandler的代码上的差别,上面有提到,这里不在说了。
进阶篇:
如果要替换修改VEH异常处理函数可以如下操作
卸载VEH异常处理函数可以这么做
让VEH失效的话可以这么做
下面的附件里包含了文中的所有代码资源
VEH分析.zip
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!