【原创】从内核创建用户态线程

  • Post author:
  • Post category:其他


http://bbs.pediy.com/showthread.php?p=1304480#post1304480


标 题:


【原创】从内核创建用户态线程




作 者:


xSpy



时 间:

2014-07-29,11:56:33


链 接:

http://bbs.pediy.com/showthread.php?t=190599







简单方法从内核创建用户态线程.






在内核想要执行用户态的代码,通常的方式有.apc, usermodecallback等.




但是都各有缺点.






APC.




1.  apc的分发必须不被禁用,




2.  目标进程必须有处于alertable的线程.




特别是后者这个条件,很多时候不一定有.




比如explorer进程有很多线程,通常能找到.




但是像记事本这种单线程程序,就找不到.







UserModeCallback.




必须在目标进程空间调用,不能是attach.的.




必须加载过User32的.,这样才有Kernelcallbacktable






在某些特定的时机,我们是有机会执行的.




比如在进程刚刚创建的时候,我们可以修改OEP,修改IAT等加载我们的dll




在第一个线程创建之后,我们可以插入apc.这些条件都很好满足.






还有WOW64的兼容处理,在另一篇文章里说明.






但是如果任何时候.不限制调用的时机,比如在进程正常运行之后,这个时候,这些条件都不满足了.






虽然我们可以构造出场景,




比如,如果你是一个过滤驱动或者hook型的,那么总是有机会会切换到目标进程上空的,这个时候就有机会可以UserModeCallback.






现在要说的就是没有这些限制的做法.可以在任意时机,任意进程空间在任意进程中执行代码.




那就是直接在内核态给一个用户态进程创建一个用户态的线程.






模拟用户态进程给自己创建一个非远程线程的基本流程.






1.创建线程初始的栈,分配和保留栈空间.设置栈保护页.实现栈的自动增长.
















2设置线程上下文,各个段寄存器和基本寄存器,设置eip指向Kernel32!BaseThreadTrunk






.text:7C810473




.text:7C810473                         ; =============== S U B R O U T I N E =======================================




.text:7C810473




.text:7C810473                         ; Attributes: bp-based frame




.text:7C810473




.text:7C810473                         ; int __stdcall BaseInitializeContext(PCONTEXT Context, PVOID Parameter, PVOID StartAddress, PVOID StackAddress, ULONG ContextType)




.text:7C810473                         _BaseInitializeContext@20 proc near     ; CODE XREF: CreateRemoteThread(x,x,x,x,x,x,x)+84↓p




.text:7C810473                                                                 ; CreateProcessInternalW(x,x,x,x,x,x,x,x,x,x,x,x)+690↓p …




.text:7C810473




.text:7C810473                         Context         = dword ptr  8




.text:7C810473                         Parameter       = dword ptr  0Ch




.text:7C810473                         StartAddress    = dword ptr  10h




.text:7C810473                         StackAddress    = dword ptr  14h




.text:7C810473                         ContextType     = dword ptr  18h




.text:7C810473




.text:7C810473                         ; FUNCTION CHUNK AT .text:7C81F10A SIZE 00000019 BYTES




.text:7C810473                         ; FUNCTION CHUNK AT .text:7C8316A2 SIZE 0000000F BYTES




.text:7C810473




.text:7C810473 8B FF                                   mov     edi, edi




.text:7C810475 55                                      push    ebp




.text:7C810476 8B EC                                   mov     ebp, esp




.text:7C810478 8B 45 08                                mov     eax, [ebp+Context]




.text:7C81047B 8B 4D 10                                mov     ecx, [ebp+StartAddress]




.text:7C81047E 83 A0 8C 00 00 00 00                    and     [eax+CONTEXT.SegGs], 0




.text:7C810485 83 7D 18 01                             cmp     [ebp+ContextType], 1




.text:7C810489 89 88 B0 00 00 00                       mov     [eax+CONTEXT._Eax], ecx




.text:7C81048F 8B 4D 0C                                mov     ecx, [ebp+Parameter]




.text:7C810492 89 88 A4 00 00 00                       mov     [eax+CONTEXT._Ebx], ecx




.text:7C810498 6A 20                                   push    20h




.text:7C81049A 59                                      pop     ecx




.text:7C81049B 89 88 94 00 00 00                       mov     [eax+CONTEXT.SegEs], ecx




.text:7C8104A1 89 88 98 00 00 00                       mov     [eax+CONTEXT.SegDs], ecx




.text:7C8104A7 89 88 C8 00 00 00                       mov     [eax+CONTEXT.SegSs], ecx




.text:7C8104AD 8B 4D 14                                mov     ecx, [ebp+StackAddress]




.text:7C8104B0 C7 80 90 00 00 00 38 00+                mov     [eax+CONTEXT.SegFs], 38h




.text:7C8104BA C7 80 BC 00 00 00 18 00+                mov     [eax+CONTEXT.SegCs], 18h




.text:7C8104C4 C7 80 C0 00 00 00 00 30+                mov     [eax+CONTEXT.EFlags], 3000h




.text:7C8104CE 89 88 C4 00 00 00                       mov     [eax+CONTEXT._Esp], ecx




.text:7C8104D4 0F 85 30 EC 00 00                       jnz     loc_7C81F10A




.text:7C8104DA C7 80 B8 00 00 00 29 07+                mov     [eax+CONTEXT._Eip], offset _BaseThreadStartThunk@8 ; BaseThreadStartThunk(x,x)




.text:7C8104E4




.text:7C8104E4                         loc_7C8104E4:                           ; CODE XREF: BaseInitializeContext(x,x,x,x,x)+ECAB↓j




.text:7C8104E4                                                                 ; BaseInitializeContext(x,x,x,x,x)+21239↓j




.text:7C8104E4 83 C1 FC                                add     ecx, 0FFFFFFFCh




.text:7C8104E7 C7 00 07 00 01 00                       mov     [eax+CONTEXT.ContextFlags], 10007h




.text:7C8104ED 89 88 C4 00 00 00                       mov     [eax+CONTEXT._Esp], ecx




.text:7C8104F3 5D                                      pop     ebp




.text:7C8104F4 C2 14 00                                retn    14h




.text:7C8104F4                         _BaseInitializeContext@20 endp




.text:7C8104F4




.text:7C8104F4                         ; —————————————————————————














3对于vista以后,还得分配TEB的ActiveContextStackPointer.要不然执行某些用户态的API的时候,那些API没有检查TEB的ActiveContextStackPointer是否为NULL就从中取值,造成崩溃.




windows的CreateThread也做了这些事.












.text:0DCEBD8A 23 4D 10                                and     ecx, [ebp+dwStackSize]




.text:0DCEBD8D 51                                      push    ecx             ; MaximumStackSize




.text:0DCEBD8E F7 D8                                   neg     eax




.text:0DCEBD90 1B C0                                   sbb     eax, eax




.text:0DCEBD92 23 45 10                                and     eax, [ebp+dwStackSize]




.text:0DCEBD95 50                                      push    eax             ; StackSize




.text:0DCEBD96 53                                      push    ebx             ; ZeroBits




.text:0DCEBD97 56                                      push    esi             ; CreateThreadFlags




.text:0DCEBD98 FF B5 B8 FD FF FF                       push    [ebp+StartContext] ; StartContext




.text:0DCEBD9E FF B5 D0 FD FF FF                       push    [ebp+StartRoutine] ; StartRoutine




.text:0DCEBDA4 FF B5 CC FD FF FF                       push    [ebp+ProcessHandle] ; ProcessHandle




.text:0DCEBDAA FF B5 BC FD FF FF                       push    [ebp+ObjectAttributes] ; ObjectAttributes




.text:0DCEBDB0 68 FF FF 1F 00                          push    1FFFFFh         ; DesiredAccess




.text:0DCEBDB5 8D 85 E4 FD FF FF                       lea     eax, [ebp+hThread]




.text:0DCEBDBB 50                                      push    eax             ; ThreadHandle




.text:0DCEBDBC FF 15 74 13 CE 0D                       call    ds:__imp__NtCreateThreadEx@44 ; NtCreateThreadEx(x,x,x,x,x,x,x,x,x,x,x)




.text:0DCEBDC2 89 85 E8 FD FF FF                       mov     [ebp+var_218], eax




.text:0DCEBDC8 3B C3                                   cmp     eax, ebx




.text:0DCEBDCA 0F 8C A5 F8 01 00                       jl      loc_DD0B675




.text:0DCEBDD0 89 5D FC                                mov     [ebp+ms_exc.disabled], ebx




.text:0DCEBDD3 64 A1 18 00 00 00                       mov     eax, large fs:18h




.text:0DCEBDD9 8B 8D C0 FD FF FF                       mov     ecx, [ebp+var_240]




.text:0DCEBDDF 3B 48 20                                cmp     ecx, [eax+20h]




.text:0DCEBDE2 75 73                                   jnz     short loc_DCEBE57




.text:0DCEBDE4 8D 85 E0 FD FF FF                       lea     eax, [ebp+var_220]




.text:0DCEBDEA 50                                      push    eax




.text:0DCEBDEB FF 15 70 13 CE 0D                       call    ds:__imp__RtlAllocateActivationContextStack@4 ; RtlAllocateActivationContextStack(x)




.text:0DCEBDF1 89 85 E8 FD FF FF                       mov     [ebp+var_218], eax




.text:0DCEBDF7 3B C3                                   cmp     eax, ebx




.text:0DCEBDF9 0F 8C B2 F8 01 00                       jl      loc_DD0B6B1




.text:0DCEBDFF 8B 85 E0 FD FF FF                       mov     eax, [ebp+var_220]




.text:0DCEBE05 8B 8D D4 FD FF FF                       mov     ecx, [ebp+var_22C]




.text:0DCEBE0B 89 81 A8 01 00 00                       mov     [ecx+1A8h], eax




.text:0DCEBE11 53                                      push    ebx




.text:0DCEBE12 6A 08                                   push    8




.text:0DCEBE14 8D 85 D8 FD FF FF                       lea     eax, [ebp+var_228]




.text:0DCEBE1A 50                                      push    eax




.text:0DCEBE1B 56                                      push    esi








4获取当前进程的BaseObject目录,可以是默认的








5 ZwCreateThread创建线程对象了.挂起的










6最重要的一点了.通知csrss进程,有新线程创建了.






.text:0DCEBE65 8B 85 E4 FD FF FF                       mov     eax, [ebp+hThread]




.text:0DCEBE6B 89 85 18 FE FF FF                       mov     [ebp+var_1E8], eax




.text:0DCEBE71 8B 85 C0 FD FF FF                       mov     eax, [ebp+var_240]




.text:0DCEBE77 89 85 1C FE FF FF                       mov     [ebp+var_1E4], eax




.text:0DCEBE7D 8B 85 C4 FD FF FF                       mov     eax, [ebp+var_23C]




.text:0DCEBE83 89 85 20 FE FF FF                       mov     [ebp+var_1E0], eax




.text:0DCEBE89 6A 0C                                   push    0Ch




.text:0DCEBE8B 68 01 00 01 00                          push    10001h




.text:0DCEBE90 53                                      push    ebx




.text:0DCEBE91 8D 85 F0 FD FF FF                       lea     eax, [ebp+var_210]




.text:0DCEBE97 50                                      push    eax




.text:0DCEBE98 FF 15 F0 11 CE 0D                       call    ds:__imp__CsrClientCallServer@16 ; CsrClientCallServer(x,x,x,x)




.text:0DCEBE9E 8B 85 10 FE FF FF                       mov     eax, [ebp+var_1F0]




.text:0DCEBEA4




.text:0DCEBEA4                         loc_DCEBEA4:                            ; CODE XREF: GetDiskFreeSpaceExA(x,x,x,x)+2FB9↓j




.text:0DCEBEA4 89 85 E8 FD FF FF                       mov     [ebp+var_218], eax






7恢复线程的执行.






.text:0DCEBEC8




.text:0DCEBEC8                         loc_DCEBEC8:                            ; CODE XREF: CreateRemoteThreadEx(x,x,x,x,x,x,x,x)+22A↑j




.text:0DCEBEC8 F6 45 1C 04                             test    byte ptr [ebp+dwCreationFlags], 4




.text:0DCEBECC 75 13                                   jnz     short loc_DCEBEE1




.text:0DCEBECE 8D 85 AC FD FF FF                       lea     eax, [ebp+var_254]




.text:0DCEBED4 50                                      push    eax




.text:0DCEBED5 FF B5 E4 FD FF FF                       push    [ebp+hThread]




.text:0DCEBEDB FF 15 3C 13 CE 0D                       call    ds:__imp__NtResumeThread@8 ; NtResumeThread(x,x)




.text:0DCEBEE1




.text:0DCEBEE1                         loc_DCEBEE1:                            ; CODE XREF: CreateRemoteThreadEx(x,x,x,x,x,x,x,x)+238↑j




.text:0DCEBEE1                                                                 ; GetDiskFreeSpaceExA(x,x,x,x)+2F6E↓j …




.text:0DCEBEE1 C7 45 FC FE FF FF FF                    mov     [ebp+ms_exc.disabled], 0FFFFFFFEh




.text:0DCEBEE8 E8 34 00 00 00                          call    sub_DCEBF21




.text:0DCEBEED 8B 85 E4 FD FF FF                       mov     eax, [ebp+hThread]




.text:0DCEBEF3




.text:0DCEBEF3                         loc_DCEBEF3:                            ; CODE XREF: GetDiskFreeSpaceExA(x,x,x,x)+2F27↓j




.text:0DCEBEF3 E8 A1 AD FF FF                          call    __SEH_epilog4_GS




.text:0DCEBEF8 C2 20 00                                retn    20h




.text:0DCEBEF8                         _CreateRemoteThreadEx@32 endp




.text:0DCEBEF8




.text:0DCEBEF8                         ; —————————————————————————








对于windows的CreateThread还有一些其他的操作,比如判断是否是csrss进程自己在创建线程.




vista以后对于远程的线程,还有session的检查等.








听起来很麻烦的一件事情,其实我们可以简化问题.




在我的实现里,不考虑csrss自己给自己创建线程的情况,




实际上我们创建的都是普通的线程,非远程的,






很多同学尝试过模拟这个过程,大部分都在第6步卡住了,.这一步比较麻烦.






每个用户态进程在创建的时候,都会连接 \\Windows\\ApiPort ,




但是发现,如果我们在内核直接连接csrss的这个port,是连不上的.需要patch.






其实可以不用patch.,直接切换到csrss空间,自己来操作CsrProcessTable等内置数据结构,但是不同意.






我用到的办法比较简单.




因为目标进程已经连接过了.这个句柄还是有符号的, CsrPortHandle.




既然从内核连接不上,我们可以在系统句柄表里去搜索这个句柄,






搜索所有的 LpcPort或 AlpcPort类型的句柄,




判断是否是我们需要的进程,




然后判断他们的ConnectionPort是否是\\Windows\\ApiPort.




找到句柄之后,duplicate到当前进程,






就可以ZwRequestWaitReplyPort 或ZwAlpcSendWaitReceivePort通知csrss了.








关于 Kernel32!BaseThreadTrunk 我并没有直接把eip指向这个地方,这个函数没有导出.






.text:7C810729




.text:7C810729                         ; =============== S U B R O U T I N E =======================================




.text:7C810729




.text:7C810729                         ; Attributes: noreturn




.text:7C810729




.text:7C810729                         ; int __stdcall BaseThreadStartThunk(int, int)




.text:7C810729                         _BaseThreadStartThunk@8 proc near       ; DATA XREF: BaseInitializeContext(x,x,x,x,x)+67↑o




.text:7C810729




.text:7C810729                         arg_0           = dword ptr  4




.text:7C810729                         arg_4           = dword ptr  8




.text:7C810729




.text:7C810729 33 ED                                   xor     ebp, ebp




.text:7C81072B 53                                      push    ebx             ; Param




.text:7C81072C 50                                      push    eax             ; StartAddress




.text:7C81072D 6A 00                                   push    0




.text:7C81072F E9 BE AF FF FF                          jmp     _BaseThreadStart@8 ; BaseThreadStart(x,x)




.text:7C81072F                         _BaseThreadStartThunk@8 endp




.text:7C81072F




.text:7C81072F                         ; —————————————————————————








.text:7C80B6F2




.text:7C80B6F2                         ; =============== S U B R O U T I N E =======================================




.text:7C80B6F2




.text:7C80B6F2                         ; Attributes: noreturn bp-based frame




.text:7C80B6F2




.text:7C80B6F2                         ; int __stdcall BaseThreadStart(int StartAddress, int ThreadParam)




.text:7C80B6F2                         _BaseThreadStart@8 proc near            ; CODE XREF: BaseThreadStartThunk(x,x)+6↓j




.text:7C80B6F2                                                                 ; BaseFiberStart()+12↓p




.text:7C80B6F2




.text:7C80B6F2                         Teb             = dword ptr -20h




.text:7C80B6F2                         ms_exc          = CPPEH_RECORD ptr -18h




.text:7C80B6F2                         StartAddress    = dword ptr  8




.text:7C80B6F2                         ThreadParam     = dword ptr  0Ch




.text:7C80B6F2




.text:7C80B6F2 6A 10                                   push    10h




.text:7C80B6F4 68 30 B7 80 7C                          push    offset stru_7C80B730




.text:7C80B6F9 E8 D8 6D FF FF                          call    __SEH_prolog




.text:7C80B6FE 83 65 FC 00                             and     [ebp+ms_exc.disabled], 0




.text:7C80B702 64 A1 18 00 00 00                       mov     eax, large fs:18h




.text:7C80B708 89 45 E0                                mov     [ebp+Teb], eax




.text:7C80B70B 81 78 10 00 1E 00 00                    cmp     dword ptr [eax+10h], 1E00h




.text:7C80B712 75 0F                                   jnz     short loc_7C80B723




.text:7C80B714 80 3D 08 50 88 7C 00                    cmp     _BaseRunningInServerProcess, 0




.text:7C80B71B 75 06                                   jnz     short loc_7C80B723




.text:7C80B71D FF 15 F8 12 80 7C                       call    ds:__imp__CsrNewThread@0 ; CsrNewThread()




.text:7C80B723




.text:7C80B723                         loc_7C80B723:                           ; CODE XREF: BaseThreadStart(x,x)+20↑j




.text:7C80B723                                                                 ; BaseThreadStart(x,x)+29↑j




.text:7C80B723 FF 75 0C                                push    [ebp+ThreadParam]




.text:7C80B726 FF 55 08                                call    [ebp+StartAddress]




.text:7C80B729 50                                      push    eax             ; dwExitCode




.text:7C80B72A




.text:7C80B72A                         loc_7C80B72A:                           ; CODE XREF: .text:7C83AB3B↓j




.text:7C80B72A E8 C9 09 00 00                          call    _ExitThread@4   ; ExitThread(x)




.text:7C80B72A                         _BaseThreadStart@8 endp




.text:7C80B72A




.text:7C80B72A                         ; —————————————————————————








而且我还需要分配ActiveContextStackPointer,.




所以新线程的eip实际上是指向一段stub,




在stub里分配ActiveContextStackPointer,然后模拟的call 线程的起始地址,




然后调用RtlExitUserThread,确保在StartAddress ret的时候,可以自行退出.




就像系统做的那样.






mov edi,API_RtlExitUserThread




test edi,edi




je _DirectRet








;调用用户提供的线程函数地址




mov eax,var_StartAddress




mov ebx,var_ThreadParam








push ebx  ;线程的参数




call eax  ;线程的起始地址








;是的用户线程函数返回时,我们可以让线程退出




push eax




call API_RtlExitUserThread












流程说完了.现在我们已经在内核模拟一个用户态线程给自己创建了一个线程.




非远程的,支持WOW64.






没有那么多限制条件执行用户态代码之后,可以做的事情就只局限于你的想象力了.




给目标进程注入一个dll简直是一个小意思了.






如果有事先执行的机会,就可以伪造各个杀毒软件或者系统进程的身份了.









附一些代码,因为依赖比较多,只贴关键的说明问题.






全文代码在下列系统测试通过.






xp/2003 32




win7/8/8.1 32/64






xSpy@binvul.com




xSpy@vxjump.net






排版的问题,代码还是传word吧.





CreateUserModeThreadFromKernelLand.doc


.