Well-known DEP-violating thunks

来源:互联网 发布:金融炼金术知乎 编辑:程序博客网 时间:2024/06/05 08:27
这几天调试发现一个奇怪的现象,一个在数据区段执行的指令,在开启了系统dep时

竟然还可以继续执行,换成其他指令就不可以,而且执行时这个内存属性仍旧是不可

执行属性。同时按理指令是一条一条执行,但是这个指令的执行是跳跃了,瞬间差点毁了

多年的指令执行三观。

debug

win7+system dep open

Breakpoint 0 hit
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=77427094 esi=001ff860 edi=00030240
eip=2e81e342 esp=0006dfbc ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
MDIVWCTL!DllUnregisterServer+0x4399:
2e81e342 ffd6            call    esi {001ff860}                    --------------〉问题点
0:000> db esi
000b1ec8  c7 44 24 04 84 ec 2d 00-e9 45 ad 76 2e ab ab ab  .D$...-..E.v....
000b1ed8  ab ab ab ab ab fe ee fe-00 00 00 00 00 00 00 00  ................
000b1ee8  23 02 05 00 ee 14 ee 00-78 01 09 00 78 01 09 00  #.......x...x...
000b1ef8  ee fe ee fe ee fe ee fe-ee fe ee fe ee fe ee fe  ................
000b1f08  ee fe ee fe ee fe ee fe-ee fe ee



*** ERROR: Module load completed but symbols could not be loaded for mspview.exe
0:000> u 001ff860
001ff860 c744240484609d04 mov     dword ptr [esp+4],49D6084h       --------------〉数据段2条要执行指令
001ff868 e9add3612e      jmp     MDIVWCTL!DllUnregisterServer+0x2c71 (2e81cc1a)
001ff86d ab              stos    dword ptr es:[edi]
001ff86e ab              stos    dword ptr es:[edi]
001ff86f ab              stos    dword ptr es:[edi]
001ff870 ab              stos    dword ptr es:[edi]
001ff871 ab              stos    dword ptr es:[edi]
001ff872 ab              stos    dword ptr es:[edi]
0:000> t
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=77427094 esi=001ff860 edi=00030240
eip=001ff860 esp=0006dfb8 ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
001ff860 c744240484609d04 mov     dword ptr [esp+4],49D6084h ss:0023:0006dfbc=00030240
0:000> t                            -----------------〉单步了,但是直接跳到这里了,毁三观吧
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=77427094 esi=001ff860 edi=00030240
eip=2e81cc1b esp=0006dfb4 ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
MDIVWCTL!DllUnregisterServer+0x2c72:
2e81cc1b 8bec            mov     ebp,esp
0:000> !address 001ff860

                                     
Failed to map Heaps (error 80004005)
Usage:                  <unclassified>
Allocation Base:        001d0000
Base Address:           001d0000
End Address:            00202000
Region Size:            00032000
Type:                   00020000    MEM_PRIVATE
State:                  00001000    MEM_COMMIT
Protect:                00000004    PAGE_READWRITE                ----------->没有可以执行属性,但是竟然可以执行


0:000> r eip=001ff86d
0:000> r
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=77427094 esi=001ff860 edi=00030240
eip=001ff86d esp=0006dfb4 ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
001ff86d ab              stos    dword ptr es:[edi]   es:0023:00030240=????????
0:000> eb 001ff86d  90 90 90 90                              ------------>更换成其他指令不可以执行
0:000> r
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=77427094 esi=001ff860 edi=00030240
eip=001ff86d esp=0006dfb4 ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
001ff86d 90              nop
0:000> t
(9f0.9f4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=77427094 esi=001ff860 edi=00030240
eip=001ff86d esp=0006dfb4 ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
001ff86d 90              nop




查阅资料发现这个是为了兼容ATL实现的dep 兼容指令,叫做well-known DEP-violating thunks

如果PE header里面没有设置IMAGE_DLLCHARACTERISTICS_NX_COMPAT 标志既可以保持这个兼容性。


 (The application compatibility team found that there were so many programs written with application frameworks that were not DEP-compatible (ATL mostly, but a few others) that nobody would actually enable DEP because the odds were close to 100% that there would be some program on the system that was not DEP-ready. Even DEP-fan Leo Davidson runs a couple of programs that don't work with DEP enabled. And it takes only one program to foul an upgrade.

When the kernel encounters a DEP exception, it checks whether thunk emulation is enabled, and if so (which it usually is), it checks whether the code sequence is one of the "well-known DEP-violating thunks". If so, then it simulates the actions the thunks would have performed and resumes execution instead of raising the exception. For example, if thunk emulation is enabled and you just took a DEP exception on the code sequence

mov ecx, 12345678
jmp 43218765

the kernel thunk emulator will perform the moral equivalent of

pContext->Ecx = 0x12345678;
pContext->Eip = 0x43218765;
return EXCEPTION_CONTINUE_EXECUTION;)




查看内核代码,发现可执行的三组序列指令 (不同版本会有所差别)

004442BC >    C74424 04 54EC2D00      MOV DWORD PTR SS:[ESP+4],2DEC54
004442C4    - E9 45AD762E             JMP 2EBAF00E

004442C9      B9 57FF15D0             MOV ECX,D015FF57
004442CE    - E9 40006681             JMP 81AA4313

004442D3      BA 4D5A75E9             MOV EDX,E9755A4D
004442D8    - E9 483C03C8             JMP C8477F25
004442DD      E1 FF                   LOOPDE SHORT MSPVIEW.004442DE


内核实现过程 ntkrnlpa.exe

KiDispatchException

.text:004274DA                 lea     eax, [ebp-2E8h]
.text:004274E0                 push    eax
.text:004274E1                 push    ecx
.text:004274E2                 push    ebx
.text:004274E3                 call    _KeContextFromKframes@12 ; KeContextFromKframes(x,x,x)
.text:004274E8                 mov     eax, [esi]
.text:004274EA                 cmp     eax, 80000003h
.text:004274EF                 jz      short loc_42755F
.text:004274F1                 cmp     eax, 10000004h
.text:004274F6                 jnz     short loc_427565
.text:004274F8                 mov     dword ptr [esi], 0C0000005h
.text:004274FE                 cmp     byte ptr [ebp+14h], 1
.text:00427502                 jnz     short loc_427565
.text:00427504                 lea     eax, [ebp-2E8h]
.text:0042750A                 push    eax
.text:0042750B                 push    esi
.text:0042750C                 call    _KiCheckForAtlThunk@8 ; KiCheckForAtlThunk(x,x)


signed int __stdcall KiEmulateAtlThunk(int *opcode_buf, int *a2, int a3, int a4, int a5)
{
  int v6; // edx@3
  int v7; // esi@3
  int v8; // eax@5
  int v9; // eax@9
  int v10; // edi@9
  int v11; // eax@11
  int v12; // eax@16
  int v13; // [sp+18h] [bp-28h]@3
  int v14; // [sp+20h] [bp-20h]@3
  char v15; // [sp+27h] [bp-19h]@5

  if ( *(_BYTE *)(*(_DWORD *)(*MK_FP(__FS__, 292) + 68) + 107) & 4 )
    return 0;
  v7 = *opcode_buf;
  v6 = *a2;
  v13 = *a2;
  v14 = 0;
  if ( *opcode_buf >= (unsigned int)MmUserProbeAddress )
    *MmUserProbeAddress = 0;
  v8 = *MK_FP(__FS__, 24) + 0xFB4;
  v15 = *(_BYTE *)v8;
  if ( *(_BYTE *)v8 )
    *(_BYTE *)v8 = 0;
  if ( *(_DWORD *)v7 != 0x42444C7 || *(_BYTE *)(v7 + 8) != 0xE9u )  ---------------〉三组指令类型
  {
    if ( *(_BYTE *)v7 != 0xB9u || *(_BYTE *)(v7 + 5) != 0xE9u )
    {
      if ( *(_BYTE *)v7 != 0xBAu
        || *(_BYTE *)(v7 + 5) != 0xB9u
        || *(_WORD *)(v7 + 0xA) != 0xE1FFu
        || (v10 = *(_DWORD *)(v7 + 6), !MmCheckForSafeExecution(v7, v6, *(_DWORD *)(v7 + 6), 0)) )
        return v14;
      *(_DWORD *)a5 = *(_DWORD *)(v7 + 1);
      *(_DWORD *)a4 = v10;
    }
    else
    {
      v12 = *(_DWORD *)(v7 + 6);
      v10 = v12 + v7 + 10;
      if ( !MmCheckForSafeExecution(v7, v6, v12 + v7 + 10, 1) || !v15 )
        return v14;
      *(_DWORD *)a4 = *(_DWORD *)(v7 + 1);
    }
  }
  else
  {
    v9 = *(_DWORD *)(v7 + 9);
    v10 = v9 + v7 + 13;
    if ( !MmCheckForSafeExecution(v7, v6, v9 + v7 + 13, 1) || !v15 )
      return v14;
    v11 = v13 + 4;
    if ( v13 + 4 >= (unsigned int)MmUserProbeAddress )
      *(_BYTE *)MmUserProbeAddress = 0;
    *(_BYTE *)v11 = *(_BYTE *)v11;
    *(_BYTE *)(v13 + 7) = *(_BYTE *)(v13 + 7);                -----------〉模拟执行
    *(_DWORD *)v11 = *(_DWORD *)(v7 + 4);
  }
  *opcode_buf = v10;
  return 1;
}


debug过程
eax=2e81e301 ebx=00000000 ecx=0006df7c edx=778a7094 esi=002cf600 edi=000401fe
eip=2e81e342 esp=0006dfbc ebp=0006dffc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
MDIVWCTL!DllUnregisterServer+0x4399:
2e81e342 ffd6            call    esi {002cf600}
0:000> dd 7ffdffca                    //查看这个SafeThunkCall    标志是1
7ffdffca  00000421 00000000 00000000 00000000
7ffdffda  00000000 80300000 0000002a 00000000
7ffdffea  00000000 00000000 00000000 00000000
7ffdfffa  00000000 00000000 00000000 68ac0fa0
7ffe000a  00028b19 00020000 44df0000 34638cef
7ffe001a  346301ce c00001ce ffbcf1dc ffbcffff
7ffe002a  014cffff 0043014c 005c003a 00690057
7ffe003a  0064006e 0077006f 00000073 00000000

KiEmulateAtlThunk获取SafeThunkCall    标志代码
83f017ad 64a118000000    mov     eax,dword ptr fs:[00000018h]
83f017b3 05ca0f0000      add     eax,0FCAh
83f017b8 8a08            mov     cl,byte ptr [eax]          ds:0023:7ffdffca=21
83f017ba 80e101          and     cl,1
83f017bd 884d17          mov     byte ptr [ebp+17h],cl



0:000> dt _TEB 7ffdf000
ntdll!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x01c EnvironmentPointer : (null)
   +0x020 ClientId         : _CLIENT_ID
   +0x028 ActiveRpcHandle  : (null)
   +0x02c ThreadLocalStoragePointer : 0x7ffdf02c Void
   +0x030 ProcessEnvironmentBlock : 0x7ffd3000 _PEB
   +0x034 LastErrorValue   : 0
   +0x038 CountOfOwnedCriticalSections : 0
   +0x03c CsrClientThread  : (null)
   +0x040 Win32ThreadInfo  : 0xfdb31168 Void
   +0x044 User32Reserved   : [26] 0
   +0x0ac UserReserved     : [5] 0
   +0x0c0 WOW32Reserved    : (null)
   +0x0c4 CurrentLocale    : 0x804
   +0x0c8 FpSoftwareStatusRegister : 0
   +0x0cc SystemReserved1  : [54] (null)
   +0x1a4 ExceptionCode    : 0n0
   +0x1a8 ActivationContextStackPointer : 0x002107e0 _ACTIVATION_CONTEXT_STACK
   +0x1ac SpareBytes       : [36]  ""
   +0x1d0 TxFsContext      : 0xfffe
   +0x1d4 GdiTebBatch      : _GDI_TEB_BATCH
   +0x6b4 RealClientId     : _CLIENT_ID
   +0x6bc GdiCachedProcessHandle : (null)
   +0x6c0 GdiClientPID     : 0
   +0x6c4 GdiClientTID     : 0
   +0x6c8 GdiThreadLocalInfo : (null)
   +0x6cc Win32ClientInfo  : [62] 0x20000008
   +0x7c4 glDispatchTable  : [233] (null)
   +0xb68 glReserved1      : [29] 0
   +0xbdc glReserved2      : (null)
   +0xbe0 glSectionInfo    : (null)
   +0xbe4 glSection        : (null)
   +0xbe8 glTable          : (null)
   +0xbec glCurrentRC      : (null)
   +0xbf0 glContext        : (null)
   +0xbf4 LastStatusValue  : 0xc0000034
   +0xbf8 StaticUnicodeString : _UNICODE_STRING "ntdll.dll"
   +0xc00 StaticUnicodeBuffer : [261]  "ntdll.dll"
   +0xe0c DeallocationStack : 0x00030000 Void
   +0xe10 TlsSlots         : [64] (null)
   +0xf10 TlsLinks         : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0xf18 Vdm              : (null)
   +0xf1c ReservedForNtRpc : 0x0022d350 Void
   +0xf20 DbgSsReserved    : [2] (null)
   +0xf28 HardErrorMode    : 0
   +0xf2c Instrumentation  : [9] (null)
   +0xf50 ActivityId       : _GUID {00000000-0000-0000-0000-000000000000}
   +0xf60 SubProcessTag    : (null)
   +0xf64 EtwLocalData     : (null)
   +0xf68 EtwTraceData     : (null)
   +0xf6c WinSockData      : (null)
   +0xf70 GdiBatchCount    : 0
   +0xf74 CurrentIdealProcessor : _PROCESSOR_NUMBER
   +0xf74 IdealProcessorValue : 0
   +0xf74 ReservedPad0     : 0 ''
   +0xf75 ReservedPad1     : 0 ''
   +0xf76 ReservedPad2     : 0 ''
   +0xf77 IdealProcessor   : 0 ''
   +0xf78 GuaranteedStackBytes : 0
   +0xf7c ReservedForPerf  : (null)
   +0xf80 ReservedForOle   : 0x002232b8 Void
   +0xf84 WaitingOnLoaderLock : 0
   +0xf88 SavedPriorityState : (null)
   +0xf8c SoftPatchPtr1    : 0
   +0xf90 ThreadPoolData   : (null)
   +0xf94 TlsExpansionSlots : (null)
   +0xf98 MuiGeneration    : 0
   +0xf9c IsImpersonating  : 0
   +0xfa0 NlsCache         : (null)
   +0xfa4 pShimData        : (null)
   +0xfa8 HeapVirtualAffinity : 0
   +0xfac CurrentTransactionHandle : (null)
   +0xfb0 ActiveFrame      : (null)
   +0xfb4 FlsData          : 0x002160a0 Void
   +0xfb8 PreferredLanguages : (null)
   +0xfbc UserPrefLanguages : 0x00217fb0 Void
   +0xfc0 MergedPrefLanguages : 0x00218150 Void
   +0xfc4 MuiImpersonation : 1
   +0xfc8 CrossTebFlags    : 0
   +0xfc8 SpareCrossTebBits : 0y0000000000000000 (0)
   +0xfca SameTebFlags     : 0x421
   +0xfca SafeThunkCall    : 0y1                     这个标志为1时,表示开启dep 兼容




跟踪下啥时候设置的

一开始是SafeThunkCall标志为0

(f08.bfc): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=0006fb08 edx=778a7094 esi=fffffffe edi=00000000
eip=7790054e esp=0006fb24 ebp=0006fb50 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2c:
7790054e cc              int     3
0:000> dd  7ffdf000 +0xfca
7ffdffca  00000420 00000000 00000000 00000000
7ffdffda  00000000 00000000 00000000 00000000
7ffdffea  00000000 00000000 00000000 00000000
7ffdfffa  00000000 00000000 00000000 14200fa0
7ffe000a  00040123 00040000 c9200000 34652aaa
7ffe001a  346501ce c00001ce ffbcf1dc ffbcffff
7ffe002a  014cffff 0043014c 005c003a 00690057
7ffe003a  0064006e 0077006f 00000073 00000000

   +0xfc0 MergedPrefLanguages : (null)
   +0xfc4 MuiImpersonation : 0
   +0xfc8 CrossTebFlags    : 0
   +0xfc8 SpareCrossTebBits : 0y0000000000000000 (0)
   +0xfca SameTebFlags     : 0
   +0xfca SafeThunkCall    : 0y0
   +0xfca InDebugPrint     : 0y0
   +0xfca HasFiberData     : 0y0


跟着何时设置的,ba w 1 7ffdffca


76f3c4c8 57              push    edi
76f3c4c9 53              push    ebx
76f3c4ca 68cdabbadc      push    0DCBAABCDh
76f3c4cf 56              push    esi
76f3c4d0 ff7518          push    dword ptr [ebp+18h]
76f3c4d3 ff7514          push    dword ptr [ebp+14h]
76f3c4d6 ff7510          push    dword ptr [ebp+10h]
76f3c4d9 ff750c          push    dword ptr [ebp+0Ch]
76f3c4dc 64800dca0f000001 or      byte ptr fs:[0FCAh],1
76f3c4e4 ff5508          call    dword ptr [ebp+8]    ss:0023:0006f89c={ole32!OleMainThreadWndProc (770d63e5)}
76f3c4e7 648025ca0f0000fe and     byte ptr fs:[0FCAh],0FEh


这里进行标志位设置
eax=c0000000 ebx=00000000 ecx=40000000 edx=7471a970 esi=00000081 edi=0006f9d8
eip=76f3c4e4 esp=0006f870 ebp=0006f894 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
USER32!InternalCallWinProc+0x20:
76f3c4e4 ff5508          call    dword ptr [ebp+8]    ss:0023:0006f89c={ole32!OleMainThreadWndProc (770d63e5)}
函数执行完毕之后 又恢复标志位
0:000> g
7ffdffca  00000420 00000000 00000000 00000000
7ffdffda  00000000 80300000 0000001d 00000000
7ffdffea  00000000 00000000 00000000 00000000
7ffdfffa  00000000 00000000 00000000 99e80fa0
7ffe000a  000517f9 00050000 4ee80000 34664181
7ffe001a  346601ce c00001ce ffbcf1dc ffbcffff
7ffe002a  014cffff 0043014c 005c003a 00690057
7ffe003a  0064006e 0077006f 00000073 00000000
eax=00000001 ebx=00000000 ecx=76f350e8 edx=778a7094 esi=00000081 edi=0006f9d8
eip=76f3c4ef esp=0006f880 ebp=0006f894 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
USER32!InternalCallWinProc+0x2b:
76f3c4ef 817c2404cdabbadc cmp     dword ptr [esp+4],0DCBAABCDh ss:0023:0006f884=dcbaabcd


至于有什么用。。。