NtContinue笔记

来源:互联网 发布:黑客常用的编程语言 编辑:程序博客网 时间:2024/06/15 21:31

NtContinue (IN PCONTEXT Context, IN BOOLEAN TestAlert)

{

    PKTHREAD Thread =KeGetCurrentThread();

    PKTRAP_FRAME TrapFrame = Thread->TrapFrame;

    PKTRAP_FRAME PrevTrapFrame = (PKTRAP_FRAME)TrapFrame->Edx;

    PFX_SAVE_AREA FxSaveArea;

    KIRQL oldIrql;

 

    DPRINT("NtContinue: Context: Eip=0x%x, Esp=0x%x\n", Context->Eip, Context->Esp );

    PULONG Frame = 0;

    __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );

    . . . . . .

 

    /*

    * Copy the supplied context over the register information that was saved

    * on entry to kernel mode, it will then be restored on exit

    * FIXME: Validate the context

    */

    KeContextToTrapFrame ( Context, TrapFrame );//把当前TrapFrame恢复为上CONTEXT的内容,实际是恢复到KiServiceExit时的状态

 

 

    /* Put the floating point context into the thread's FX_SAVE_AREA

    * and make sure it is reloaded when needed.

    */

    FxSaveArea = (PFX_SAVE_AREA)((ULONG_PTR)Thread->InitialStack –

                                               sizeof(FX_SAVE_AREA));

    if (KiContextToFxSaveArea(FxSaveArea, Context))

    {

        Thread->NpxState = NPX_STATE_VALID;

        KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);

        if (KeGetCurrentPrcb()->NpxThread == Thread)

        {

            KeGetCurrentPrcb()->NpxThread = NULL;

            Ke386SetCr0(Ke386GetCr0() | X86_CR0_TS);

        }

        else

        {

            ASSERT((Ke386GetCr0() & X86_CR0_TS) == X86_CR0_TS);

       }

        KeLowerIrql(oldIrql);

    }

 

    /* Restore the user context */

    Thread->TrapFrame = PrevTrapFrame;//上一次TrapFrame

__asm__("mov %%ebx, %%esp;\n" "jmp _KiServiceExit": : "b" (TrapFrame));

 

    return STATUS_SUCCESS; /* this doesn't actually happen */

}

注意从KiUserApcDispatcher()NtContinue()并不是普通的函数调用,而是系统调用,这中间经历了空间的切换,也从用户空间堆栈切换到了系统空间堆栈。CPU进入系统调用空间后,在_KiSystemServicex下面的代码中把指向中断现场的框架指针保存在当前线程的KTHREAD数据结构的TrapFrame字段中。这样,很容易就可以找到系统空间堆栈上的调用框架。当然,现在的框架是因为系统调用而产生的框架;而要想回到当初、即在执行用户空间APC函数之前的断点,就得先恢复当初的框架。那么当初的框架在哪里呢?它保存在用户空间的堆栈上,就是前面KiInitializeUserApc()保存的CONTEXT数据结构中。所以,这里通过KeContextToTrapFrame()把当初保存的信息拷贝回来,从而恢复了当初的框架。

 

_KiSystemServicex下面的代码中把指向中断现场的框架指针保存在当前线程的KTHREAD数据结构的TrapFrame字段中.保存当前在TrapFrame的DX中,将新的TrapFrame对应

KTHREAD的TrapFrame,在KiServiceExit中将使用TrapFrame的值来返回,在INT2E方式下将使用IRET,在SYSENTER下,将利用TrapFrame中的值来恢复ESP-ECX,EDX-EIP,来返回上次位置

NtContinue的系统级调用将产生一个新的TrapFrame,他利用该TRAPFRAME,将开始备份的CONTEXT,恢复到他的TrapFrame中,同时将上一次的TrapFrame恢复

 

1.        从系统调用、中断、或异常返回途径_KiServiceExit,如果APC队列中有等待执行的APC请求,就调用KiDeliverApc()

2.        KiDeliverApc(),从用户APC队列中摘下一个APC请求。

3.        KiInitializeUserApc()中保存当前框架,并伪造新的框架。

4.        回到用户空间。

5.        KiUserApcDispatcher()中调用目标APC函数。

6.        通过系统调用NtContinue()进入系统空间。

7.        NtContinue()中恢复当初保存的框架。

8.        NtContinue()返回、途径_KiServiceExit时,如果APC队列中还有等待执行的APC请求,就调用KiDeliverApc()。于是转回上面的第二步。

 

 

这个过程一直要循环到APC队列中不再有需要执行的请求。注意这里每一次循环中保存和恢复的都是同一个框架,就是原始的、开始处理APC队列之前的那个框架,代表着原始的用户空间程序断点。一旦APC队列中不再有等待执行的APC请求,在_KiServiceExit下面就不再调用KiDeliverApc(),于是就直接返回用户空间,这次是返回到原始的程序断点了。所以,系统调用neContinue()的作用不仅仅是切换回到被中断了的上下文,还包括执行用户APC队列中的下一个APC请求。

对于KiUserApcDispatcher()而言,它对NtContinue()的调用是不返回的。因为在NtContinue()中CPU不是“返回”到对于KiUserApcDispatcher()的另一次调用、从而对另一个APC函数的调用;就是返回到原始的用户空间程序断点,这个断点既可能是因为中断或异常而形成的,也可能是因为系统调用而形成的。

 

原创粉丝点击