zoukankan      html  css  js  c++  java
  • NtCallbackReturn是否导致了用户态栈的不平衡

    0:000> u ntdll!KiFastSystemCall
    ntdll!KiFastSystemCall:
    7c92eb8b 8bd4            mov     edx,esp
    7c92eb8d 0f34            sysenter
    ntdll!KiFastSystemCallRet:
    7c92eb8f 90              nop
    7c92eb90 90              nop
    7c92eb91 90              nop
    7c92eb92 90              nop
    7c92eb93 90              nop
    ntdll!KiFastSystemCallRet:
    7c92eb94 c3              ret
    

    而我们通过dump disasm得到的结果

    0x77d1a146 :             FF 15 24 14 D1 77 		call dword ptr [0x77d11424]
    【ntdll.dll.[.text].NtCallbackReturn】
    0x7c92d51d :                B8 14 00 00 00 		mov eax, 0x14
    0x7c92d522 :                BA 00 03 FE 7F 		mov edx, 0x7ffe0300
    0x7c92d527 :                         FF 12 		call dword ptr [edx]
      【ntdll.dll.[.text].KiFastSystemCall】   0x7c92eb8b : 8B D4 mov edx, esp   0x7c92eb8d : 0F 34 sysenter   0x7c92eb94 : C3 ret
    0x77ef67d4 : C3 ret

    根据上面的结果显示,KiFastSystemCall在执行结束本来应该返回到0x7c92d527的下一条指令即0x7c92d529处执行,但是为什么会返回到0x77ef67d4处呢?

    通过查看调用栈

            call [0x0012f43c]     :     0x7c92d527 --> 0x7c92eb8b
        return [0x0012f458]     :     0x77ef67d4 <-- 0x7c92eb94

    Thread 38:

       

       [# 0] [0x0012fb20]:[0x7c941739 - 0x7c94173e] ==> [0x7c93c9e4] ntdll.dll.[.text].LdrFindResourceDirectory_U_0x000000af [# 1] [0x0012fa18]:[0x7c93cba6 - 0x7c93cbab] ==> [0x7c921193] ntdll.dll.[.text].LdrInitializeThunk_0x00000015 [# 2] [0x0012f9f8]:[0x7c9211a4 - 0x7c9211a7] ==> [0x77d1f518] USER32.dll.[.text].UserClientDllInitialize [# 3] [0x0012f46c]:[0x77d1f76f - 0x77d1f774] ==> [0x77d1f791] USER32.dll.[.text].UserClientDllInitialize_0x00000279 [# 4] [0x0012f460]:[0x77ef655e - 0x77ef6563] ==> [0x77ef67c8] GDI32.dll.[.text].GdiProcessSetup_0x000001ac [# 5] [0x0012f45c]:[0x77ef67d2 - 0x77ef67d4] ==> [0x7c92eb8b] ntdll.dll.[.text].KiFastSystemCall [# 6] [0x0012f450]:[0x7c92eae0 - 0x7c92eae3] ==> [0x77d1a12e] USER32.dll.[.text].ClientThreadSetup_0x00000134 [# 7] [0x0012f440]:[0x77d1a146 - 0x77d1a14c] ==> [0x7c92d51d] ntdll.dll.[.text].NtCallbackReturn --> [# 8] [0x0012f43c]:[0x7c92d527 - 0x7c92d529] ==> [0x7c92eb8b] ntdll.dll.[.text].KiFastSystemCall

    可见,0x77ef67d4是在第一次调用KiFastSystemCall的时候,保存在栈上的返回地址,当时的栈的位置是0x0012f45c(还没有将返回地址压到栈上时);

    而在返回时,栈的位置是0x0012f458,因此正好是返回到了第一次调用KiFastSystemCall的位置。

    如果是这样,那么我们的影子调用栈出现了什么问题呢?

    我们知道,当用户态程序进入内核态执行后(通过sysenter),内核可能会调度到用户态的APC等等回调函数执行,这些回调函数的执行发起者是内核态代码,因此它们需要将执行流程返回给内核代码;

    Windows提供了一种机制,当用户态的回调函数没有提供显式的返回到内核的代码时,Windows会自动执行默认的返回到内核态的代码,而这一段代码恰好也是通过KiFastSystemCall机制完成的(因为KiFastSystemCall是用户态通向内核态的唯一入口),这一机制被称为NtCallbackReturn。

    因此,我们怀疑,NtCallbackReturn机制会使用户态的代码孤立起来看,是不符合栈平衡的,原因就是NtCallbackReturn的内核服务程序中会对用户态的栈做调整,以掩盖异步回调函数被执行过的痕迹。

    因此,我们相信,NtCallbackReturn的存在,使得我们单纯地依据用户态的call/ret指令建立起来的影子调用栈出现了不平衡的现象。

    解决方法,就是在调用过NtCallbackReturn之后,添加三次等效ret的效果,可以解决影子栈的不平衡问题。


    事情到了这里,还没有结束,因为还不知道有哪些系统调用会破坏用户态栈的平衡。

    比如NtContinue就很可疑(根据实现结果),那么用户态的影子调用栈要通过什么方式来保证其正确性呢?

    更普遍的做法,可以根据KiFastSystemCallRet来判断,它可以与KiFastSystemCall配对,从而消除中间的多余项目。

  • 相关阅读:
    探讨变量的内存分配方式
    色彩之RGB和灰阶
    Perl语言:qw简写
    【转】位操作
    [转]Perl学习笔记
    Spaghetti code&Ravioli code&Lasagna code&Spaghetti with meatballs
    交叉编译lsusb
    GCC,LLVM,Clang编译器对比
    如何判断自己是否到了该辞职的时候
    Javascript Array和String的互转换。
  • 原文地址:https://www.cnblogs.com/long123king/p/3816457.html
Copyright © 2011-2022 走看看