欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]VEH分析

[原创]VEH分析

注册了这么长时间还是临时会员,虽然很惬意,但是怪不好意思的,于是决定发一篇文章赚点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

上传的附件 image001.png
image003.png
image005.png
image007.png
image009.png
image011.png
VEH分析.zip

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功