欢迎来到 嗅灵易学

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

[原创]SEH和SafeSEH

[原创]SEH和SafeSEH

  昨天写了个驱动,用vs2010编译不了,但是wdk可以编译。vs2010提示是

  查了很多资料也不知道什么原因,有的说打开/Gs选项,然后链接上一个bufferoverflowU.lib文件,但是出错提示更多符号找不到,仔细看了下问题是链接ntoskrnl.lib的时候有个loadcfg.obj找不到__security_cookie的符号,其实这个符号很常见,用于检测堆栈是否被溢出的,/Gs开关就是控制是否做这个检测的。问题是我链接了那个提供这个符号lib还是不行。试了下xp却可以编译。在vs的目录搜索loadcfg,发现了loadcfg.c的文件,看了下里面用到了__security_cookie,用winhex打开ntoskrnl.lib也能搜到loadcfg.obj,所以问题出在这个.c文件,它也参加了编译和链接。用loadcfg.c里面的其他内容搜索到了原因,应该SafeSEH需要关闭,链接命令加上SafeSEH:NO就行了(还不懂求解释)。结合代码和资料复习了一下SEH,并学习了一下SafeSEH。不过后来搜其他结构的时候发现原来早就有这个资料了,但是就当笔记记录一下吧。
  随便写个函数,然后Release编译下
1,SecurityCheck,这是普通的打开/Gs需要进行检查的

  函数会用__security_cookie xor加密一下ebp,存放起来,也会加密一下scopetable的地址。ebp-10就是异常框架的起点了。在函数结尾,会将ebp ^ cookie的值异或上正确的ebp,用_security_check_cookie检查结果和cookie是否相等,不相等说明ebp-1c的位置被修改了,所以能简单地阻止被溢出。溢出我没研究过。。。还有xxxx的位置,是异常分发下来填写的,用GetExceptionInformation获取这个xxxx,xxxx,xxxx+4就是Context和ExceptionRecord的结构指针, r0分发的时候把2个结构打包到用户堆栈。
2,SafeSEH
  异常分发到r3之后的顺序是KiUserExceptionDispatcher -> RtlDispatchException,不考虑VEH的话,会先判断这2个条件:异常处理的框架在堆栈,异常处理的Handler不在堆栈上,不满足就RaiseException。调用ZwQueryInformationProcess查看一下ProcessExecuteFlags属性,得到ExecuteOption。之后就是RtlIsValidHandler,来检测是否Handler是否正常, ExecuteOption作为参数参入RtlIsValidHandler。
  RtlIsValidHandler-> RtlLookupFunctionTable,查看一下信息。

.text:77EE8B68                 mov     esi, _LdrpInvertedFunctionTable ; 这个是需要检查的模块数目。
.text:77EE8B6E                 cmp     esi, ebx
.text:77EE8B70                 jz      loc_77EDD3F6
.text:77EE8B76                 dec     esi
.text:77EE8B77
.text:77EE8B77 loc_77EE8B77:                           ; CODE XREF: RtlLookupFunctionTable(x,x,x)+69 j
.text:77EE8B77                                         ; RtlLookupFunctionTable(x,x,x)+271 j
.text:77EE8B77                 cmp     esi, ebx
.text:77EE8B79                 jl      loc_77EDD3F6    ; 没找到,就跳走,之后会返回0
.text:77EE8B7F                 lea     ecx, [esi+ebx]  ; ebx=0,esi=count-1,这是二分查找,esi是high,ebx是low指针
.text:77EE8B82                 sar     ecx, 1
.text:77EE8B84                 mov     eax, ecx
.text:77EE8B86                 shl     eax, 4          ; 每一项对应的是16byte大小的结构。off0用于解密
.text:77EE8B89                 add     eax, offset unk_77F9AC0C ; 全局变量,是这个模块表的起点
.text:77EE8B8E                 mov     edi, [eax+4]    ; off4是模块映像基地址,off8是大小,offc是SafeSeh的数量
.text:77EE8B91                 mov     edx, [eax+8]
.text:77EE8B94                 add     edx, edi
.text:77EE8B96                 cmp     [ebp+Handler], edi
.text:77EE8B99                 jb      loc_77EE8DA4    ; 调整high的指针
.text:77EE8B9F                 cmp     [ebp+Handler], edx
.text:77EE8BA2                 jb      short loc_77EE8BA9 ; 找到了
.text:77EE8BA4                 lea     ebx, [ecx+1]    ; 调整low指针
.text:77EE8BA7                 jmp     short loc_77EE8B77
.text:77EE8BA9 ; ---------------------------------------------------------------------------
.text:77EE8BA9
.text:77EE8BA9 loc_77EE8BA9:                           ; CODE XREF: RtlLookupFunctionTable(x,x,x)+64 j
.text:77EE8BA9                 mov     ecx, edi
.text:77EE8BAB                 mov     [ebp+var_1C], ecx
.text:77EE8BAE                 mov     ecx, [eax+0Ch]
.text:77EE8BB1                 mov     edx, [ebp+arg_8] ; 第三个参数返回SafeSeh(handler)的个数
.text:77EE8BB4                 mov     [edx], ecx
.text:77EE8BB6                 push    dword ptr [eax] ; off0用于解密
.text:77EE8BB8                 call    _RtlDecodeSystemPointer@4 ; RtlDecodeSystemPointer(x)
.text:77EE8BBD                 mov     esi, eax        ; 得到的结果就是这个模块的SafeSeh表的地址,返回的也是这个值

  得到了Handler所在模块的SafeSEH函数表,如果有这张表,Hander就必须在表中。也是按地址从低到高排列的,二分查找一下。
.text:77EE8ADD                 call    _RtlLookupFunctionTable@12 ; RtlLookupFunctionTable(x,x,x)
.text:77EE8AE2                 xor     edi, edi
.text:77EE8AE4                 mov     [ebp+SafeSehTable], eax
.text:77EE8AE7                 cmp     eax, edi
.text:77EE8AE9                 jz      ModDontHaveSehTable ; 不在模块内部
.text:77EE8AEF                 mov     esi, [ebp+nCount]
.text:77EE8AF2                 cmp     esi, edi
.text:77EE8AF4                 jz      ModDontHaveSehTable ; 或者在模块内部,但是模块本身没有sehtable
.text:77EE8AFA                 cmp     eax, 0FFFFFFFFh
.text:77EE8AFD                 jz      loc_77F4DE4A
.text:77EE8B03
.text:77EE8B03 loc_77EE8B03:                           ; CODE XREF: RtlIsValidHandler(x,x)+65390 j
.text:77EE8B03                 sub     ebx, [ebp+pMouduleBase] ; 得到Handler的Rva
.text:77EE8B06                 xor     edx, edx
.text:77EE8B08                 cmp     esi, edi        ; esi是high索引,edx是low索引
.text:77EE8B0A                 jl      loc_77F37DAE    ; 没有找到
.text:77EE8B10
.text:77EE8B10 loc_77EE8B10:                           ; CODE XREF: RtlIsValidHandler(x,x)+198 j
.text:77EE8B10                 lea     ecx, [edx+esi]
.text:77EE8B13                 sar     ecx, 1
.text:77EE8B15                 mov     edi, [eax+ecx*4] ; SafeSehTable里面的项
.text:77EE8B18                 cmp     ebx, edi
.text:77EE8B1A                 jb      loc_77EE8D94    ; 调整一下索引
.text:77EE8B20                 ja      loc_77EE8C5F    ; 调整一下索引
.text:77EE8B26
.text:77EE8B26 RetTrue:                                ; CODE XREF: RtlIsValidHandler(x,x)+4F5CF j
.text:77EE8B26                                         ; RtlIsValidHandler(x,x)+653C2 j ...
.text:77EE8B26                 mov     al, 1           ; Return True
.text:77EE8B28
.text:77EE8B28 Ret:                                    ; CODE XREF: RtlIsValidHandler(x,x)+65398 j
.text:77EE8B28                                         ; RtlIsValidHandler(x,x)+65403 j
.text:77EE8B28                 mov     ecx, [ebp+var_4]
.text:77EE8B2B                 pop     edi
.text:77EE8B2C                 pop     esi
.text:77EE8B2D                 xor     ecx, ebp
.text:77EE8B2F                 pop     ebx
.text:77EE8B30                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:77EE8B35                 leave
.text:77EE8B36                 retn    8

  没找到模块或者模块没有SafeSEH就继续判断。
.text:77F3807A ModDontHaveSehTable:                    ; CODE XREF: RtlIsValidHandler(x,x)+2D j
.text:77F3807A                                         ; RtlIsValidHandler(x,x)+38 j
.text:77F3807A                 cmp     [ebp+ExeOption], edi
.text:77F3807D                 jz      loc_77F37D91
.text:77F38083
.text:77F38083 CmpExecuteOption:                       ; CODE XREF: RtlIsValidHandler(x,x)+4F2E7 j
.text:77F38083                                         ; RtlIsValidHandler(x,x)+653A8 j
.text:77F38083                 mov     eax, [ebp+ExeOption]
.text:77F38086                 and     eax, 30h
.text:77F38089                 cmp     al, 30h         ; ExecuteDispatchEnable  |  ImageDispatchEnable
.text:77F3808B                 jz      RetTrue         ; Return True
.text:77F38091                 jmp     loc_77F4DE69

  当ExecuteOption的ExecuteDispatchEnable和ImageDispatchEnable位都为1,就直接返回TRUE,表示Handler可以执行。否则查询一下Handler所在内存的情况。
.text:77F4DE69 loc_77F4DE69:                           ; CODE XREF: RtlIsValidHandler(x,x)+4F5D5 j
.text:77F4DE69                 lea     eax, [ebp+var_14]
.text:77F4DE6C                 push    eax
.text:77F4DE6D                 push    1Ch
.text:77F4DE6F                 lea     eax, [ebp+var_30]
.text:77F4DE72                 push    eax             ; Buffer
.text:77F4DE73                 push    edi             ; 0 = MemoryBasicInformation
.text:77F4DE74                 push    ebx             ; Handler
.text:77F4DE75                 push    0FFFFFFFFh
.text:77F4DE77                 call    _NtQueryVirtualMemory@24 ; NtQueryVirtualMemory(x,x,x,x,x,x)
.text:77F4DE7C                 test    eax, eax
.text:77F4DE7E                 jl      RetTrue         ; Return True
.text:77F4DE84                 test    [ebp+var_1C], 0F0h ; #define PAGE_EXECUTE           0x10
.text:77F4DE84                                         ; #define PAGE_EXECUTE_READ      0x20
.text:77F4DE84                                         ; #define PAGE_EXECUTE_READWRITE 0x40
.text:77F4DE84                                         ; #define PAGE_EXECUTE_WRITECOPY 0x80
.text:77F4DE88                 jz      short HandlerNotExecutive
.text:77F4DE8A                 cmp     [ebp+var_18], MEM_IMAGE ; #define SEC_IMAGE         0x1000000
.text:77F4DE91                 jnz     short loc_77F4DEB7
.text:77F4DE93                 lea     eax, [ebp+nCount]
.text:77F4DE96                 push    eax
.text:77F4DE97                 lea     eax, [ebp+SafeSehTable]
.text:77F4DE9A                 push    eax
.text:77F4DE9B                 push    [ebp+Base]
.text:77F4DE9E                 call    _RtlCaptureImageExceptionValues@12 ; RtlCaptureImageExceptionValues(x,x,x)
.text:77F4DEA3                 cmp     [ebp+SafeSehTable], edi
.text:77F4DEA6                 jz      RetTrue         ; Return True
.text:77F4DEAC                 cmp     [ebp+nCount], edi
.text:77F4DEAF                 jz      RetTrue         ; Return True
.text:77F4DEB5                 jmp     short RetFalse  ; Table和Count都=-1,返回0
.text:77F4DEB7 ; ---------------------------------------------------------------------------

  当两个符号都不都为1,先看内存的Protect属性,如果不在可执行段,但是ExecuteDispatchEnable为1,就返回TRUE,Handler可以执行,否则RaiseException。如下。
.text:77F4DEC4 HandlerNotExecutive:                    ; CODE XREF: RtlIsValidHandler(x,x)+653CC j
.text:77F4DEC4                 test    byte ptr [ebp+ExeOption], 10h
.text:77F4DEC8                 jnz     RetTrue         ; Return True
.text:77F4DECE                 push    0FFFFFFFEh
.text:77F4DED0                 push    0FFFFFFFEh
.text:77F4DED2                 jmp     short loc_77F4DE59

  如果处于可执行段,再检查是否是映像文件,不属于映像文件,但是ImageDispatchEnable置为1也可以执行。
.text:77F4DEB7 loc_77F4DEB7:                           ; CODE XREF: RtlIsValidHandler(x,x)+653D5 j
.text:77F4DEB7                 mov     al, byte ptr [ebp+ExeOption]
.text:77F4DEBA                 shr     al, 5
.text:77F4DEBD                 and     al, 1
.text:77F4DEBF                 jmp     Ret

  最后如果Handler属于可执行内存且不是映像文件,从映像头部在获取一次SafeSEH。这次调用了RtlCaptureImageExceptionValues。这个函数不是分开的了,直接F5了。。
int __thiscall RtlCaptureImageExceptionValues(void *this, int a2, int a3, int a4)
{
  int v4; // eax@2
  int v5; // eax@4
  int result; // eax@5
  int v7; // ecx@8
  void *v8; // [sp+0h] [bp-4h]@1
  v8 = this;
  if ( !(*(_WORD *)(RtlImageNtHeader(a2) + 94) & 0x400) )// IMAGE_DLLCHARACTERISTICS_NO_SEH  0x0400 
  {
    v4 = RtlImageDirectoryEntryToData(a2, 1, 10, (int)&v8);
    if ( v4 && v8 && (v8 == (void *)64 || v8 == *(void **)v4) )
    {
      if ( *(_DWORD *)v4 >= 0x48u )
      {
        v7 = *(_DWORD *)(v4 + 64);
        if ( v7 )
        {
          if ( *(_DWORD *)(v4 + 68) )
          {
            *(_DWORD *)a3 = v7;
            result = *(_DWORD *)(v4 + 68);
            *(_DWORD *)a4 = result;
            return result;
          }
        }
      }
    }
    v5 = RtlImageDirectoryEntryToData(a2, 1, 14, (int)&a2);// 传说里的.net的某个标志?PeLord写的是com。
    if ( !v5 || !(*(_BYTE *)(v5 + 16) & 1) )
    {
      *(_DWORD *)a3 = 0;
      result = a4;
      *(_DWORD *)a4 = 0;
      return result;
    }
  }
  *(_DWORD *)a3 = -1;
  result = a4;
  *(_DWORD *)a4 = -1;
  return result;
}

  RtlImageDirectoryEntryToData返回目录项的Va和大小(v8和a2),第一个结构IMAGE_LOAD_CONFIG_DIRECTORY32_2是那个loadcfg.c里面设置的,有点关联?查到了然后读出SafeSEH table和项数。#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14,第二次查的是这个。COM Runtime descriptor。不太清楚,看资料。可能跟.net有关。这里返回0的,只有两种情况,必须没有IMAGE_DLLCHARACTERISTICS_NO_SHE,同时LoadConfig目录读出来没找到SafeSEH table(项数为0),或者第15个目录某个标志位没有置位。返回之后会对比结果。只有返回0,才能认为Handler可以执行。
  总结一下SafeSEH就是对比Handler是不是正常的Handler。如果找到了Table,就必须在Table中。如果没找到,就看ExecuteDispatchEnable和ImageDispatchEnable ,同时置1就直接让Handler执行。不是同时为1,就先看Handler所在内存是不是可执行,若不是可执行,如果ExecuteDispatchEnable = 1就 return TRUE,否则FALSE。如果内存可执行也是MEM_IMAGE类型的内存,得看ImageDispatchEnable,置位了就TRUE,否则FALSE。如果不是MEM_IMAGE类型的可执行内存,再给次机会看看Handler所在模块的基地址,如果还是没找到,也放行。
  虽然能编译,不过问题的原因还是不知道,感觉问题是这里,搜loadcfg找到的不明字符串。

上传的附件 2.jpg
3.jpg
1.jpg

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

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

0 0 0 举报
复制成功