驅動注入使用者執行緒之跨session通知csrss之真正解決

Editor發表於2018-03-12


驅動注入使用者執行緒之跨session通知csrss之真正解決


首先這事得搜尋網路,發現網路上的看起來很牛的文章,我們們得承認在之前是還算不錯的,但是也沒有真正實現。


或許有實現的,但是網上肯定是搜不到具體的,肯定都當私貨處理了,拿到現在這年代來說,都算是被淘汰了。


為什麼這麼說呢,因為這得分具體情況。在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,感謝所有幫助過我的朋友。


測試結果圖~


驅動注入使用者執行緒之跨session通知csrss之真正解決


本文由看雪論壇 mlgbwoai  原創 轉載請註明來自看雪社群

相關文章