查看原文
其他

驱动注入用户线程之跨session通知csrss之真正解决

2018-03-12 mlgbwoai 看雪学院

首先这事得搜索网络,发现网络上的看起来很牛的文章,咱们得承认在之前是还算不错的,但是也没有真正实现。


或许有实现的,但是网上肯定是搜不到具体的,肯定都当私货处理了,拿到现在这年代来说,都算是被淘汰了。


为什么这么说呢,因为这得分具体情况。在vista以及以后的系统中,未导出函数ZwCreateThreadEx已经自己内置实现了跨session 通知csrss等操作,所以用起来还是很省心的。填充好参数,直接就不管了,也很顺利的运行。


但是吧,咱们写程序就是为了兼容性(不然内心一万匹草泥马在翻滚)。在国内xp可还是一大堆的~


但是xp却没有ZwCreateThreadEx,只有ZwCreateThread

(请自行ida一下xp的ntkrnlpa.exe,

至于为什么是这个而不是ntoskrnl.exe当然这个pchunter看下驱动的加载就好了啊。本文不是扫盲篇,这些不具体解释,当然我也是大菜鸟)


然而ZwCreateThread这个家伙 可真是让人操碎了心。


因为按照ZwCreateThread的参数 我填充好之后,编译,然后直接往taskmgr.exe里面注入dll,而dll的代码就是dllmain里MessageBox了一下。


大家可以想到结果是什么。


结果是:弹框出来了,哎呀,万事大吉,收工保存,抽只烟,然后休息下,真爽。


等过了些时候,打算用用了。于是就真正的写功能dll,至于dll的功能当然是有点小复杂。


然后测试下。


哎,不对啊,怎么有点问题。是不是我系统坏了,或者dll那里有问题?真是奇怪。


仔细检查dll,没发现啥错误啊,为啥结果不正确。


得仔细定位dll.加了点调试信息输出。最终定位到dll里 线程创建失败了?然后,又仔细核对了下创建线程的函数。


也没发现什么问题啊。真是莫名其妙。


还是网上搜搜前辈们吧。这一搜不要紧,原来这事 绝没这么简单,复杂的在后面呢。


所以本文的重点是针对版本号3790(2003和64位xp)以及以下的所有的系统的兼容性,核心问题也就是:如何正确处理ZwCreateThread?


那么到底如何正确处理呢?


我们为了解决这个问题,自然会网上搜,但是真正会思考这个问题的一定会联想到CreateRemoteThread这个r3的api因为这个大家肯定太熟悉了。

好吧,先说个题外话。鲁迅曾经说过:"不想吃天鹅的蛤蟆不是好的蝌蚪",还说过:"电脑不备好ida,windbg,od,wrk,reatos的程序员不是好的厨师"


那么我最开始的想法是:先看看reatos里有没有,实在没有就ida逆CreateRemoteThread(反正有符号,没符号的话真没太大勇气逆)


天公做美竟然给搜出来了,路径在这里

dll\win32\kernel32\thread\thread.c


我们来看下内容


HANDLE

WINAPI

CreateRemoteThread(HANDLE hProcess,

                   LPSECURITY_ATTRIBUTES lpThreadAttributes,

                   DWORD dwStackSize,

                   LPTHREAD_START_ROUTINE lpStartAddress,

                   LPVOID lpParameter,

                   DWORD dwCreationFlags,

                   LPDWORD lpThreadId)

{

    NTSTATUS Status;

    INITIAL_TEB InitialTeb;

    CONTEXT Context;

    CLIENT_ID ClientId;

    OBJECT_ATTRIBUTES LocalObjectAttributes;

    POBJECT_ATTRIBUTES ObjectAttributes;

    HANDLE hThread;

    ULONG Dummy;


    DPRINT("CreateRemoteThread: hProcess: %ld dwStackSize: %ld lpStartAddress"

            ": %p lpParameter: %lx, dwCreationFlags: %lx\n", hProcess,

            dwStackSize, lpStartAddress, lpParameter, dwCreationFlags);


    /* Clear the Context */

    RtlZeroMemory(&Context, sizeof(CONTEXT));


    /* Write PID */

    ClientId.UniqueProcess = hProcess;


    /* Create the Stack */

    Status = BasepCreateStack(hProcess,

                              dwStackSize,

                              dwCreationFlags & STACK_SIZE_PARAM_IS_A_RESERVATION ?

                              dwStackSize : 0,

                              &InitialTeb);

    if(!NT_SUCCESS(Status))

    {

        SetLastErrorByStatus(Status);

        return NULL;

    }


    /* Create Initial Context */

    BasepInitializeContext(&Context,

                           lpParameter,

                           lpStartAddress,

                           InitialTeb.StackBase,

                           1);


    /* initialize the attributes for the thread object */

    ObjectAttributes = BasepConvertObjectAttributes(&LocalObjectAttributes,

                                                    lpThreadAttributes,

                                                    NULL);


    /* Create the Kernel Thread Object */

    Status = NtCreateThread(&hThread,

                            THREAD_ALL_ACCESS,

                            ObjectAttributes,

                            hProcess,

                            &ClientId,

                            &Context,

                            &InitialTeb,

                            TRUE);

    if(!NT_SUCCESS(Status))

    {

        BasepFreeStack(hProcess, &InitialTeb);

        SetLastErrorByStatus(Status);

        return NULL;

    }


    /* Are we in the same process? */

    if (hProcess == NtCurrentProcess())

    {

        PTEB Teb;

        PVOID ActivationContextStack;

        THREAD_BASIC_INFORMATION ThreadBasicInfo;

#ifndef SXS_SUPPORT_FIXME

        ACTIVATION_CONTEXT_BASIC_INFORMATION ActivationCtxInfo;

        ULONG_PTR Cookie;

#endif

        ULONG retLen;


        /* Get the TEB */

        Status = NtQueryInformationThread(hThread,

                                          ThreadBasicInformation,

                                          &ThreadBasicInfo,

                                          sizeof(ThreadBasicInfo),

                                          &retLen);

        if (NT_SUCCESS(Status))

        {

            /* Allocate the Activation Context Stack */

            Status = RtlAllocateActivationContextStack(&ActivationContextStack);

        }


        if (NT_SUCCESS(Status))

        {

            Teb = ThreadBasicInfo.TebBaseAddress;


            /* Save it */

            Teb->ActivationContextStackPointer = ActivationContextStack;

#ifndef SXS_SUPPORT_FIXME

            /* Query the Context */

            Status = RtlQueryInformationActivationContext(1,

                                                          0,

                                                          NULL,

                                                          ActivationContextBasicInformation,

                                                          &ActivationCtxInfo,

                                                          sizeof(ActivationCtxInfo),

                                                          &retLen);

            if (NT_SUCCESS(Status))

            {

                /* Does it need to be activated? */

                if (!ActivationCtxInfo.hActCtx)

                {

                    /* Activate it */

                    Status = RtlActivateActivationContext(1,

                                                          ActivationCtxInfo.hActCtx,

                                                          &Cookie);

                    if (!NT_SUCCESS(Status))

                        DPRINT1("RtlActivateActivationContext failed %x\n", Status);

                }

            }

            else

                DPRINT1("RtlQueryInformationActivationContext failed %x\n", Status);

#endif

        }

        else

            DPRINT1("RtlAllocateActivationContextStack failed %x\n", Status);

    }


    /* Notify CSR */

    Status = BasepNotifyCsrOfThread(hThread, &ClientId);

    if (!NT_SUCCESS(Status))

    {

        ASSERT(FALSE);

    }

    

    /* Success */

    if(lpThreadId) *lpThreadId = HandleToUlong(ClientId.UniqueThread);


    /* Resume it if asked */

    if (!(dwCreationFlags & CREATE_SUSPENDED))

    {

        NtResumeThread(hThread, &Dummy);

    }


    /* Return handle to thread */

    return hThread;

}


我们来简化一下代码方便继续。


CreateRemoteThread()

{

//栈

BasepCreateStack

//上下文

BasepInitializeContext

//创建挂起线程

NtCreateThread

//判断是否自身,我们不管忽略过去,我们不会往csrss里面注

//通知

BasepNotifyCsrOfThread

//恢复

NtResumeThread

}


从上面的伪代码中我们可以看到除了通知这个,其他的都很好处理。所以我们直接将重点精力继续追下去,也就是说看BasepNotifyCsrOfThread的具体实现


当然还是reatos里面搜,路径如下

dll\win32\kernel32\process\procsup.c


内容如下:


NTSTATUS

WINAPI

BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,

                       IN PCLIENT_ID ClientId)

{

    ULONG Request = CREATE_THREAD;

    CSR_API_MESSAGE CsrRequest;

    NTSTATUS Status;


    DPRINT("BasepNotifyCsrOfThread: Thread: %lx, Handle %lx\n",

            ClientId->UniqueThread, ThreadHandle);


    /* Fill out the request */

    CsrRequest.Data.CreateThreadRequest.ClientId = *ClientId;

    CsrRequest.Data.CreateThreadRequest.ThreadHandle = ThreadHandle;


    /* Call CSR */

    Status = CsrClientCallServer(&CsrRequest,

                                 NULL,

                                 MAKE_CSR_API(Request, CSR_NATIVE),

                                 sizeof(CSR_API_MESSAGE));

    if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrRequest.Status))

    {

        DPRINT1("Failed to tell csrss about new thread\n");

        return CsrRequest.Status;

    }


    /* Return Success */

    return STATUS_SUCCESS;

}


一看就知道,实际上这个函数算是个小封装,没意思,重点还是在CsrClientCallServer里面。


那么继续搜,路径在

dll\ntdll\csr\connect.c


看下内容如下:


NTSTATUS 

NTAPI

CsrClientCallServer(PCSR_API_MESSAGE ApiMessage,

                    PCSR_CAPTURE_BUFFER CaptureBuffer OPTIONAL,

                    CSR_API_NUMBER ApiNumber,

                    ULONG RequestLength)

{

    NTSTATUS Status;

    ULONG PointerCount;

    PULONG_PTR Pointers;

    ULONG_PTR CurrentPointer;

    DPRINT("CsrClientCallServer\n");


    /* Fill out the Port Message Header */

    ApiMessage->Header.u2.ZeroInit = 0;

    ApiMessage->Header.u1.s1.DataLength = RequestLength - sizeof(PORT_MESSAGE);

    ApiMessage->Header.u1.s1.TotalLength = RequestLength;


    /* Fill out the CSR Header */

    ApiMessage->Type = ApiNumber;

    //ApiMessage->Opcode = ApiNumber; <- Activate with new CSR

    ApiMessage->CsrCaptureData = NULL;


    DPRINT("API: %lx, u1.s1.DataLength: %x, u1.s1.TotalLength: %x\n", 

           ApiNumber,

           ApiMessage->Header.u1.s1.DataLength,

           ApiMessage->Header.u1.s1.TotalLength);

                

    /* Check if we are already inside a CSR Server */

    if (!InsideCsrProcess)

    {

        /* Check if we got a a Capture Buffer */

        if (CaptureBuffer)

        {

            /* We have to convert from our local view to the remote view */

            ApiMessage->CsrCaptureData = (PVOID)((ULONG_PTR)CaptureBuffer +

                                                 CsrPortMemoryDelta);


            /* Lock the buffer */

            CaptureBuffer->BufferEnd = 0;


            /* Get the pointer information */

            PointerCount = CaptureBuffer->PointerCount;

            Pointers = CaptureBuffer->PointerArray;


            /* Loop through every pointer and convert it */

            DPRINT("PointerCount: %lx\n", PointerCount);

            while (PointerCount--)

            {

                /* Get this pointer and check if it's valid */

                DPRINT("Array Address: %p. This pointer: %p. Data: %lx\n",

                        &Pointers, Pointers, *Pointers);

                if ((CurrentPointer = *Pointers++))

                {

                    /* Update it */

                    DPRINT("CurrentPointer: %lx.\n", *(PULONG_PTR)CurrentPointer);

                    *(PULONG_PTR)CurrentPointer += CsrPortMemoryDelta;

                    Pointers[-1] = CurrentPointer - (ULONG_PTR)ApiMessage;

                    DPRINT("CurrentPointer: %lx.\n", *(PULONG_PTR)CurrentPointer);

                }

            }

        }


        /* Send the LPC Message */

        Status = NtRequestWaitReplyPort(CsrApiPort,

                                        &ApiMessage->Header,

                                        &ApiMessage->Header);


        /* Check if we got a a Capture Buffer */

        if (CaptureBuffer)

        {

            /* We have to convert from the remote view to our remote view */

            DPRINT("Reconverting CaptureBuffer\n");

            ApiMessage->CsrCaptureData = (PVOID)((ULONG_PTR)

                                                 ApiMessage->CsrCaptureData -

                                                 CsrPortMemoryDelta);


            /* Get the pointer information */

            PointerCount = CaptureBuffer->PointerCount;

            Pointers = CaptureBuffer->PointerArray;


            /* Loop through every pointer and convert it */

            while (PointerCount--)

            {

                /* Get this pointer and check if it's valid */

                if ((CurrentPointer = *Pointers++))

                {

                    /* Update it */

                    CurrentPointer += (ULONG_PTR)ApiMessage;

                    Pointers[-1] = CurrentPointer;

                    *(PULONG_PTR)CurrentPointer -= CsrPortMemoryDelta;

                }

            }

        }


        /* Check for success */

        if (!NT_SUCCESS(Status))

        {

            /* We failed. Overwrite the return value with the failure */

            DPRINT1("LPC Failed: %lx\n", Status);

            ApiMessage->Status = Status;

        }

    }

    else

    {

        /* This is a server-to-server call. Save our CID and do a direct call */

        DbgBreakPoint();

        ApiMessage->Header.ClientId = NtCurrentTeb()->ClientId;

        Status = CsrServerApiRoutine(&ApiMessage->Header,

                                     &ApiMessage->Header);

       

        /* Check for success */

        if (!NT_SUCCESS(Status))

        {

            /* We failed. Overwrite the return value with the failure */

            ApiMessage->Status = Status;

        }

    }


    /* Return the CSR Result */

    DPRINT("Got back: 0x%lx\n", ApiMessage->Status);

    return ApiMessage->Status;

}


内容很长我们继续自己读一下然后写伪代码来逼迫自己清晰思路


CsrClientCallServer()

{

//填充消息

if (!InsideCsrProcess) //这里我就是顾名思义,我们不往csrss里面注

{

xxxxxxxxxx

NtRequestWaitReplyPort //发送lpc消息

返回然后xxxx我们不管

}

}


好了,看到这里,我想思路基本就明确了,写一下伪代码


MyNtCreateThread()

{

//栈

//上下文

ZwCreateThread

ZwRequestWaitReplyPort //发消息通知csrss等着返回

恢复


}


=============我是华丽的分割线==========================



其中除了ZwRequestWaitReplyPort之外基本没啥难点。下来所说的全部都是针对ZwRequestWaitReplyPort所说的。


如果看到这里的话,请忘记上面所有的内容,重点关注着

ZwRequestWaitReplyPort

ZwRequestWaitReplyPort(Handle, PPORT_MESSAGE, PPORT_MESSAGE)


这个里面重要是参数的结构体非常复杂,这里可以网上搜basemsg.h,这里面都包含着呢。所以第二第三个参数可以说是能解决,那么第一个参数呢?


这个从网上找资料说\windows\apiport 不能连(实际上不准,连是可以连的,只是创建了共享内存,发现有共享内存就失败了而已),也不能attach

而有前辈们说可以从目标进程中查找已经存在的port类型句柄(LPC port,ALPC port),然后ZwDuplicateObject出来用。


恩,好象还真是那么回事。那么怎么办?快去想办法实现啊,不去实现你永远也不可能知道行还是不行。


那么针对第一个参数这个问题,自然重点就是如何查找指定pid的\windows\apiport的句柄~


放出伪代码


Enumed(IN PHANDLE_TABLE_ENTRY HandleEntry,IN HANDLE Handle,IN PVOID Context)

{

  POBJECT_HEADER Obh;

  PLPCP_PORT_OBJECT CsrApiPort = (PLPCP_PORT_OBJECT)Context;

  Obh = ObpGetObject(HandleEntry);

  Port = (PLPCP_PORT_OBJECT)&Obh->Body;

  if (Port->ConnectionPort == CsrApiPort)

  {

   

  }

}


CsrPortHandle(PEPROCESS proc, PHANDLE pCsrHandle)

{

  UNICODE_STRING usType;

  POBJECT_TYPE *pType;

  UNICODE_STRING uName;

  PLPCP_PORT_OBJECT ApiPort = NULL;


  RtlInitUnicodeString(&uType, L"LpcPortObjectType");

  pType = MmGetSystemRoutineAddress(&uType)))

  RtlInitUnicodeString(&uName, L"\\Windows\\ApiPort");

  ObReferenceObjectByName(&uName, 0, NULL, PORT_ALL_ACCESS, *pType, KernelMode, NULL, &ApiPort);

  ExEnumHandleTable(xxxxxxx, Enumed, ApiPort, pCsrHandle);

  ObDereferenceObject(ApiPort);

}


那么好了,第一  第二  第三个参数都有了,直接调用就好了~


恩。到这里好象可以收尾了。


为什么这么说呢?因为编译好之后 我在xp下 往taskmgr注入了个dll,这个dll里 不仅仅创建了线程,线程里又创建了线程,然后还降权做了点事情


测试结果一切正常。于是就抽了只烟。然后再看看xp 64位,恩也可以,好的,再最后看看2003,

哎呀,我草。。我草。。。完了,不对,杂没出来啊,什么也不对,这这??难道,莫非?,不应该啊。

伤心了。但是伤心之余还得继续看。


首先看了下驱动输出,奇怪,windows\apiport句柄杂返回0  没找到。

把pchunter放进去看看,哎,竟然不支持。好吧 procexp


用这个看了一下,忽然意识到好象要出大问题。因为看到了session\2\windows\apiport


这么忽然一想。感觉好象自己被自己骗了。不行,把session全部弄出来。

对着session=0的随便一个进程注入试试(我不是iocontrol的是驱动里直接注入的所以session=0)


可以预见,这个可以。没问题。再找个session=2的注入试试。真的不行


然后又想到了测试的xp和xp 64。再进去看看,一看里面的所有的session=0


进行内心纠结的总结:因为同都在session 0下  所以才成功,如果用多用户登陆,那肯定失败。或者说驱动session=0 注session=2 失败

这下看来真的碰到江湖上传说中的跨session问题了,或者说跨session通知csrss的问题。


于是仔细观看进程,发现2003里面有两个csrss.exe 这下也意识到原来每个csrss.exe控制着一个session


进程中有10个csrss.exe就控制着10个session


而且每个csrss与每个csrss之间 互不打扰,隔离。


或者通俗的说,我们的电脑登陆一个用户就创建一个csrss


于是想到刚接触电脑时候,网上问:“我进程中有两个csrss.exe是怎么回事,下面回答肯定有一个是病毒,杀掉就行,当然我也那样认为”

现在想起来,这好象再正常不过啊~



我们到这里需要对上面所有所有的内容去总结:

那就是注和驱动一样session的完全没有问题,不同session的创建是成功的只是(如果dll里再创建线程等会失败)


那么现在问题就来了,我们的驱动session=0(说了没有使用iocontrol,否则session是调用它的exe所在session)


那么怎么去通知session=2的csrss呢?(我们一切都以我2003上目前的这个状态来说事情)


基于上面已经实现的和总结的,我们是不是可以这样想:我们既然要通知csrss并且session=2的,那么我们是否选择一个session=2的其他进程(虽然dll里再创建线程失败,但是dllmain入口里的没问题啊)


然后注给它,然后它再去通知csrss(session=2)的。恩


想了想,逻辑没问题。于是继续写查找session的代码一大通。编译好。

自己脑袋里过了一遍,感觉很紧张,因为思路上是通的。要是测试通过,就可以以后安心使用了,也算是对抗精神嫡的再一次成功。如果失败,那么必疯无疑,甚至开始怀疑人生。


拿出来一试,十万个我草。失败。

真想用李云龙的话说事情。。。奶奶个腿


骂没有用,得找出是为什么失败来。经过系列跟踪,发现,csrss(session=2)的 确实是成功了的


但是,csrss回复的时候,是回复给  我们选择的  session=2的 所谓的其他进程


现在到这里,所有的问题成了一个僵局

而且越弄越复杂了。


到底应该怎么办呢?


到底应该怎么办呢?


能不能柳暗花明呢?


=============我是华丽的分割线=========================


下文就是让柳暗花明。


我们上文当中碰到了一个非常麻烦的事情,那就是session=0的驱动,去通知session=2的csrss.exe。


我们尝试的找个session=2的a.exe,往a.exe注一点代码,这个代码就负责通知session=2的csrss


最后发现的是session=2的csrss收到消息后回复给了a.exe,我们驱动里并没有得到通知。


我们所有的问题都是为了得到通知。实际上这里走了很多弯路,实验了很多方法。真的是弄的脑袋爆。


驱动注入用户线程之跨session通知csrss之真正解决 中的所谓的‘真正’的含义!


这里真正是加了引号,实际上我是重点标记,我说的的确是真正。


当问题没办法解决的时候会怎么办呢?当然是所有的办法也猜测,所有的资料也看。这里我们为了节约时间突出重点。


我们不再说我们走的弯路(实际上弯路很多很多)


当然这其中也免不了看 csrss.exe用了什么模块。自然会关注到CSRSRV.dll


网上搜也会看到这个是csrss.exe的核心。既然我们关注了这个dll.我们也就避免不了看看导出啊,对感兴趣的函数逆逆啊,搜搜啊什么的


我们来看这个dll的导出函数。

如下:

导出, CSRSRV.dll

序列 地址 名字

00000001 75AA544F CsrAddStaticServerThread

00000002 75AA4160 CsrCallServerFromServer          看名字我有兴趣

00000003 75AA3FCE CsrConnectToUser 看名字我有兴趣

00000004 75AA5D26 CsrCreateProcess

00000005 75AA60E0 CsrCreateRemoteThread 看名字我有兴趣

00000006 75AA6010 CsrCreateThread 看名字我有兴趣

00000007 75AA63FE CsrCreateWait

00000008 75AA6362 CsrDebugProcess

00000009 75AA636F CsrDebugProcessStop

0000000A 75AA500B CsrDereferenceProcess

0000000B 75AA55AA CsrDereferenceThread

0000000C 75AA658B CsrDereferenceWait

0000000D 75AA5F58 CsrDestroyProcess

0000000E 75AA619A CsrDestroyThread

0000000F 75AA54C2 CsrExecServerThread

00000010 75AA508D CsrGetProcessLuid

00000011 75AA4EE9 CsrImpersonateClient

00000012 75AA535D CsrLockProcessByClientId

00000013 75AA53DD CsrLockThreadByClientId

00000014 75AA6615 CsrMoveSatisfiedWait

00000015 75AA652D CsrNotifyWait 看名字我有兴趣

00000016 75AA2A17 CsrPopulateDosDevices

00000017 75AA3FC3 CsrQueryApiPort 看名字我有兴趣

00000018 75AA4F9D CsrReferenceThread

00000019 75AA4F30 CsrRevertToSelf

0000001A 75AA305E CsrServerInitialization

0000001B 75AA4D1F CsrSetBackgroundPriority

0000001C 75AA516F CsrSetCallingSpooler

0000001D 75AA4CF8 CsrSetForegroundPriority

0000001E 75AA624F CsrShutdownProcesses

0000001F 75AA3204 CsrUnhandledExceptionFilter

00000020 75AA53BA CsrUnlockProcess

00000021 75AA622C CsrUnlockThread

00000022 75AA4421 CsrValidateMessageBuffer

00000023 75AA449D CsrValidateMessageString


我有兴趣的函数我都在reatos里面搜了。


当搜到CsrCreateRemoteThread函数的时候,一切都傻眼了。为什么呢?我们来看CsrCreateRemoteThread的代码。


路径如下:

subsystems\csr\csrsrv\thread.c


代码以及注释如下:


/*++

 * @name CsrCreateRemoteThread

 * @implemented NT4

 *

 * The CsrCreateRemoteThread routine creates a CSR Thread object for

 * an NT Thread which is not part of the current NT Process.

 *

 * @param hThread

 *        Handle to an existing NT Thread to which to associate this

 *        CSR Thread.

 *

 * @param ClientId

 *        Pointer to the Client ID structure of the NT Thread to associate

 *        with this CSR Thread.  

 *

 * @return STATUS_SUCCESS in case of success, STATUS_UNSUCCESSFUL

 *         othwerwise.

 *

 * @remarks None.

 *

 *--*/

NTSTATUS

NTAPI

CsrCreateRemoteThread(IN HANDLE hThread,

                      IN PCLIENT_ID ClientId)

{

    NTSTATUS Status;

    HANDLE ThreadHandle;

    PCSR_THREAD CsrThread;

    PCSR_PROCESS CsrProcess;

    KERNEL_USER_TIMES KernelTimes;


    DPRINT("CSRSRV: %s called\n", __FUNCTION__);


    /* Get the Thread Create Time */

    Status = NtQueryInformationThread(hThread,

                                      ThreadTimes,

                                      (PVOID)&KernelTimes,

                                      sizeof(KernelTimes),

                                      NULL);


    /* Lock the Owner Process */

    Status = CsrLockProcessByClientId(&ClientId->UniqueProcess,

                                      &CsrProcess);


    /* Make sure the thread didn't terminate */

    if (KernelTimes.ExitTime.QuadPart)

    {

        /* Unlock the process and return */

        CsrUnlockProcess(CsrProcess);

        return STATUS_THREAD_IS_TERMINATING;

    }


    /* Allocate a CSR Thread Structure */

    if (!(CsrThread = CsrAllocateThread(CsrProcess)))

    {

        DPRINT1("CSRSRV:%s: out of memory!\n", __FUNCTION__);

        CsrUnlockProcess(CsrProcess);

        return STATUS_NO_MEMORY;

    }


    /* Duplicate the Thread Handle */

    Status = NtDuplicateObject(NtCurrentProcess(),

                               hThread,

                               NtCurrentProcess(),

                               &ThreadHandle,

                               0,

                               0,

                               DUPLICATE_SAME_ACCESS);

    /* Allow failure */

    if (!NT_SUCCESS(Status)) ThreadHandle = hThread;


    /* Save the data we have */

    CsrThread->CreateTime = KernelTimes.CreateTime;

    CsrThread->ClientId = *ClientId;

    CsrThread->ThreadHandle = ThreadHandle;

    CsrThread->Flags = 0;


    /* Insert the Thread into the Process */

    CsrInsertThread(CsrProcess, CsrThread);


    /* Release the lock and return */

    CsrUnlockProcess(CsrProcess);

    return STATUS_SUCCESS;

}


CsrCreateRemoteThread(IN HANDLE hThread,IN PCLIENT_ID ClientId)


从注释和内容看,这明摆着不就是:把我们要注的目标进程的hThread和ClientId传给这个函数,这个函数


自动进行关联起来的内置操作吗?


又是一个挖槽。我们干嘛总是驱动里给他发通知,这不是自己自寻死路吗,我们为什么不

为什么不

为什么不

为什么不


太重要了这点信息,下面说的这点信息将是重点中的重点。通篇废话就是为了下面的这几句。


***************************************************************************


我们再来回想一下,我们通篇都在做什么?都在做一件事情,那就是 驱动(session=0) 通知 csrss(session=2)


我们从来就没想过往csrss(session=2)里面注,现在人家csrss(session=2)有个现成的函数自己实现这些。


我们现在就往csrss(session=2)里面注,让csrss自己通知自己。而我们驱动不等port的返回,因为所有的关于port等的信息都tmd与我们没关系了。我们只等着  注入csrss(session=2)里面的 的一点代码 把CsrCreateRemoteThread的返回值通过结束线程的接口给传回来。


来点片段代码说清楚上面的看似模糊的话。


片段1:r3里的

注入csrss(session=2)里面的 的一点代码,实际上重点就这两行,当然是r3的代码


//csrss自己去处理通知吧,我们什么也不管,我们只管驱动把这两参数给传好就行了。就让csrss自己通知自己

CsrCreateRemoteThread(ThreadHandle,UniqueProcess);

//如果执行到这里的话肯定上面的代码就已经完成了,下面的这行代码是为了让驱动知道(驱动里可以恢复真正的线程运行了)

NtTerminateThread(GetCurrentThread(),0);


片段2:r0里的


MyZwCreateThread()

{

//栈

//上先文

//ZwCreateThread目标线程挂起

//ZwCreateThread(&CsrssThreadHandle, THREAD_ALL_ACCESS...) 目标线程所在进程所在session的 csrss  不挂起(内容就是片段1里的)

//ZwWaitForSingleObject(CsrssThreadHandle...)

//恢复目标进程

}


已经非常清晰了,重点中的重点啊,真的是放血了。


这样做的好处太多了。简单明了,不用那个通知函数又复杂又麻烦,事情又多,弯路又多,网上又很多忽悠人的看似可以的文章。让大家更加迷茫。


简单几行替代上百行,又稳定又安全又有依据。


***************************************************************************


所以标题可以这么称呼:

驱动注入用户线程之跨session通知csrss之真正解决之CsrCreateRemoteThread大法之全球首创


哈哈,够霸气吧~~


感谢busy,感谢pediy,感谢所有帮助过我的朋友。


测试结果图~






本文由看雪论坛 mlgbwoai  原创

转载请注明来自看雪社区


热门阅读



点击阅读原文/read,

更多干货等着你~



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存