首页
论坛
课程
招聘
Vista&Win7下CreateRemoteThread应用的若干问题和解决方案
2009-11-18 15:43 35716

Vista&Win7下CreateRemoteThread应用的若干问题和解决方案

2009-11-18 15:43
35716
Vista&Win7下CreateRemoteThread应用的若干问题和解决方案

在xp、03下面,用CreateRemoteThread往别的进程注入shellcode或者是dll是一件非常容易的事情,甚至是往系统进程里面注入(如svchost、winlogon等),但在vista下,你会发现事情没有那么容易了,就算是在xp下一模一样的代码,在vista下面都有可能给你一个ERROR_NOT_ENOUGH_MEMORY ,error Id是8,中文解释是“存储空间不足,无法处理此命令。”通常发生的情况是在2个进程的sessionid不一样的时候。搜索网上的资料,原来早有前辈解决过这个问题。
链接一:http://www.cnasm.com/view.asp?classid=51&newsid=292  从无花果前辈的分析来看,他分析的是vista版本的。我们暂且以此为例来进行说明。
.text:7C8105B0                 cmp     [ebp-3ACh], ebx//这里发生了跳转,引起CreateRemoteThread失败。
.text:7C8105B6                 jl      loc_7C83ABD3 

这里进行的比较和跳转要处理的方式有很多中,MJ0011大牛也给了一种办法,原文链接在这里 http://bbs.pediy.com/showthread.php?t=88454 从文件MircoCode.rar-》xxload.exe里面可以找到。

.text:00401685                 lea     edx, [ebp+Buffer]
.text:0040168B                 push    ecx
.text:0040168C                 push    edx
.text:0040168D                 call    sub_4011B0
sub_4011B0函数里面有对上述比较地方进行处理的代码,我们跟进看看

.text:004013D4                 mov     eax, ds:CreateRemoteThread
.text:004013D9                 cmp     word ptr [eax+edi], 1D38h
.text:004013DF                 jnz     short loc_4013F2
.text:004013E1                 mov     edx, [eax+edi+2]
.text:004013E5                 push    1               ; ucb
.text:004013E7                 push    edx             ; lp
.text:004013E8                 call    ds:IsBadWritePtr
.text:004013EE                 test    eax, eax
.text:004013F0                 jz      short loc_401407
.text:004013F2
.text:004013F2 loc_4013F2:                             ; CODE XREF: sub_4011B0+22F j
.text:004013F2                 inc     edi
.text:004013F3                 cmp     edi, 300h
.text:004013F9                 jb      short loc_4013D4
.text:004013FB                 mov     edx, [esp+44h+dwSize]
.text:004013FF                 push    4000h
.text:00401404                 push    edx
.text:00401405                 jmp     short loc_40147F
.text:00401407 ; ---------------------------------------------------------------------------
.text:00401407
.text:00401407 loc_401407:                             ; CODE XREF: sub_4011B0+240 j
.text:00401407                 mov     eax, ds:CreateRemoteThread
.text:0040140C                 mov     edi, [eax+edi+2]
.text:00401410                 test    edi, edi
.text:00401412                 mov     cl, [edi]
.text:00401414                 mov     byte ptr [edi], 1
.text:00401417                 mov     byte ptr [esp+44h+arg_4], cl
这段是进行处理的过程,如果你觉得这样不好看,下面我们简单把它翻译成C语言的。

BOOL ModifyCreateRemoteThreadByMJ0011()
{
  //for vista 
  //win7 disable
  DWORD index = 0;
  PCHAR StartAddress = (PCHAR)CreateRemoteThread;
  for(index = 0;index<0x300;index++)
  {
    if(*(PUSHORT)(StartAddress+index)==0x1D38
      &&!IsBadWritePtr(*(PVOID*)(StartAddress+index+2),1))//此处已修改。打屁股
    {
      printf("OK:%d!\n",index);
      *((PCHAR)(*(PDWORD)(StartAddress+index+2))) = 1;
      return TRUE;
    }
  }
  printf("没有找到!\n");
  return FALSE;
};

里面的printf是我加进去的。意思很简单,从CreateRemoteThread开始,最多找0x300个字节,找到0x1D38后,将某个值赋值为1,现在我们回头再看CreateRemoteThread里面的0x1D38是个什么语句。
76454826    8981 600F0000         mov     dword ptr [ecx+F60], eax
7645482C    381D 40EF4D76         cmp     byte ptr [764DEF40], bl
76454832  ^ 0F85 28E3FBFF         jnz     76412B60
76454838    8B45 B4               mov     eax, dword ptr [ebp-4C]
7645483B    8985 C0FEFFFF         mov     dword ptr [ebp-140], eax
76454841    8B45 94               mov     eax, dword ptr [ebp-6C]
76454844    8985 C4FEFFFF         mov     dword ptr [ebp-13C], eax
7645484A    8B45 98               mov     eax, dword ptr [ebp-68]
7645484D    8985 C8FEFFFF         mov     dword ptr [ebp-138], eax
76454853    6A 0C                 push    0C
76454855    68 01000100           push    10001
7645485A    53                    push    ebx
7645485B    8D85 98FEFFFF         lea     eax, dword ptr [ebp-168]
76454861    50                    push    eax
76454862    FF15 4C104176         call    dword ptr [<&ntdll.CsrClientCall>; ntdll.CsrClientCallServer

如果你仔细看的话,在第2句代码哪里就是381D ,MJ0011的代码就是将里面的数据0x764DEF40取出来,作为地址,赋值为1.从而实现远程线程的顺利注入。但是这段代码在win7下面却又行不通了,因为根本找不到0x1D38,就不能进行patch。那么win7下面的CreateRemoteThread又是个什么样子呢?
看下面。
.text:77E6F403                 mov     edi, edi
.text:77E6F405                 push    ebp
.text:77E6F406                 mov     ebp, esp
.text:77E6F408                 push    [ebp+lpThreadId]
.text:77E6F40B                 push    0
.text:77E6F40D                 push    [ebp+dwCreationFlags]
.text:77E6F410                 push    [ebp+lpParameter]
.text:77E6F413                 push    [ebp+lpStartAddress]
.text:77E6F416                 push    [ebp+dwStackSize]
.text:77E6F419                 push    [ebp+lpThreadAttributes]
.text:77E6F41C                 push    [ebp+hProcess]
.text:77E6F41F                 call    CreateRemoteThreadEx_0
.text:77E6F424                 pop     ebp
.text:77E6F425                 retn    1Ch

就这么一段,中间调用了CreateRemoteThreadEx,然后返回了。
而 CreateRemoteThreadEx仅仅是从API-MS-WIN-CORE-PROCESSTHREADS-L1-1-0.DLL(你的系统可能和我的不一样)引入的一个函数。所以MJ0011的那段代码在win7下是不能用了。

我们现在再回头看看无花果前辈给出的解决办法,自己用代码实现CreateRemoteThread的功能。测试一下。
在vista、win7下都能工作吧。不过就是一运行,目标程序就挂了,检查我们的代码并没有发现使你异常。那就来比较一下调用系统的CreateRemoteThread和调用 OsCreateRemoteThread(DWORD dwpid,StartAddress:pointer) 2个有什么不一样的地方,很快我们发现(以vista为例),系统的调用结束后是用ntdll.RtlExitUserThread来结束当前线程的,而我们用OsCreateRemoteThread创建的并没有调用这个函数,而是返回到了一个莫名其妙的地方。OK,那我们在代码的最后自己来调用ntdll.RtlExitUserThread结束,OK,正常了。
但是我们要用的地方非常多啊,如果都去修改的话,不但容易错,将来维护也麻烦啊。那我们就自己来实现这个桩函数吧。

  const CHAR myBaseThreadInitThunk[] = 
  {
    //   00830000    8BFF            mov     edi, edi
    '\x8B','\xFF',
    //   00830002    55              push    ebp
    '\x55',
    //   00830003    8BEC            mov     ebp, esp
    '\x8B','\xEC',
    //   00830005    51              push    ecx   //ntdll.RtlExitUserThread
    '\x51',
    //   00830006    53              push    ebx   //参数
    '\x53',
    //   00830007    FFD0            call    eax   //函数地址
    '\xFF','\xD0',
    //   00830009    59              pop     ecx   //恢复结束函数地址
    '\x59',
    //   0083000A    50              push    eax   //将刚才的结果压栈
    '\x50',
    //   0083000B    FFD1            call    ecx   //调用RtlExitUserThread 结束
    '\xFF','\xD1',
    //  0083000D    90              nop
    '\x90'
  };
根据以上的内容和参考无花果前辈的代码,我们可以实现这样一个函数。

HANDLE WINAPI OsCreateRemoteThread2(
              HANDLE hProcess,
              LPSECURITY_ATTRIBUTES lpThreadAttributes,
              SIZE_T dwStackSize,
              LPTHREAD_START_ROUTINE lpStartAddress,
              LPVOID lpParameter,
              DWORD dwCreationFlags,
              LPDWORD lpThreadId)
{
//by 80695073(QQ) 
//email kiss2008ufo@yahoo.com.cn
  CONTEXT    context = {CONTEXT_FULL}; 
  CLIENT_ID  cid; 
  DWORD    ret; 
  HANDLE    hThread = NULL;
  DWORD    StackReserve;
  DWORD    StackCommit = 0x1000;
  ULONG_PTR  Stack = 0;
  INITIAL_TEB InitialTeb;
  ULONG    x; 
  const CHAR myBaseThreadInitThunk[] = 
  {
    //   00830000    8BFF            mov     edi, edi
    '\x8B','\xFF',
    //   00830002    55              push    ebp
    '\x55',
    //   00830003    8BEC            mov     ebp, esp
    '\x8B','\xEC',
    //   00830005    51              push    ecx   //ntdll.RtlExitUserThread
    '\x51',
    //   00830006    53              push    ebx   //参数
    '\x53',
    //   00830007    FFD0            call    eax   //函数地址
    '\xFF','\xD0',
    //   00830009    59              pop     ecx   //恢复结束函数地址
    '\x59',
    //   0083000A    50              push    eax   //将刚才的结果压栈
    '\x50',
    //   0083000B    FFD1            call    ecx   //调用RtlExitUserThread 结束
    '\xFF','\xD1',
    //  0083000D    90              nop
    '\x90'
  };
  PVOID  pBaseThreadThunk = NULL; //不能释放

  //0、分配非OS的加载函数
  StackReserve = 0x1000;
  ret = ZwAllocateVirtualMemory(hProcess, 
    /*&stack.ExpandableStackBottom*/(PVOID*)&pBaseThreadThunk, 
    0, 
    &StackReserve,
    MEM_COMMIT, 
    PAGE_EXECUTE_READWRITE); 
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwAllocateVirtualMemory0 !\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  ret = ZwWriteVirtualMemory(hProcess,
    pBaseThreadThunk,
    (LPVOID)myBaseThreadInitThunk,
    sizeof(myBaseThreadInitThunk),&x);
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwAllocateVirtualMemory0 !\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  cid.UniqueProcess = hProcess;

  //1、准备堆栈
  StackReserve = 0x10000;
  ret = ZwAllocateVirtualMemory(hProcess, 
    /*&stack.ExpandableStackBottom*/(PVOID*)&Stack, 
    0, 
    &StackReserve,
    MEM_RESERVE, 
    PAGE_READWRITE); 
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwAllocateVirtualMemory1!\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  printf("OK OsCreateRemoteThread2:ZwAllocateVirtualMemory 0x%08x\n",Stack);

  InitialTeb.AllocatedStackBase = (PVOID)Stack;
    InitialTeb.StackBase = (PVOID)(Stack + StackReserve);

  /* Update the Stack Position */
    Stack += StackReserve - StackCommit;

  Stack -= 0x1000;
  StackCommit += 0x1000;

    /* Allocate memory for the stack */
    ret = ZwAllocateVirtualMemory(hProcess,
    (PVOID*)&Stack,
    0,
    &StackCommit,
    MEM_COMMIT,
        PAGE_READWRITE);
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwAllocateVirtualMemory2!\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  printf("OK OsCreateRemoteThread2:ZwAllocateVirtualMemory 2 0x%08x\n",Stack);
  InitialTeb.StackLimit = (PVOID)Stack;

  StackReserve = 0x1000; 
  ret = ZwProtectVirtualMemory(hProcess, (PVOID*)&Stack, &StackReserve, PAGE_READWRITE | PAGE_GUARD, &x); 
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwProtectVirtualMemory!\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  /* Update the Stack Limit keeping in mind the Guard Page */
    InitialTeb.StackLimit = (PVOID)((ULONG_PTR)InitialTeb.StackLimit - 0x1000);
  //2、准备CONTEXT
//  CONTEXT context = {CONTEXT_FULL}; 
  ret = ZwGetContextThread(GetCurrentThread(),&context); 
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwGetContextThread!\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  context.Esp = (ULONG)InitialTeb.StackBase; 
  context.Eip = (ULONG)pBaseThreadThunk; //这里填写需要加载的地址,不过需要自己终结自己
  context.Ebx = (ULONG)lpParameter;
  //other init
  //must
  context.Eax = (ULONG)lpStartAddress;
  context.Ecx = 0x778B0859;/*win7*///0x77AEEC01;/*vista*/ //ntdll.RtlExitUserThread
  context.Edx = 0x00000000; //nouse

  ret = ZwCreateThread(&hThread, THREAD_ALL_ACCESS, 0, hProcess, &cid, &context, &InitialTeb, TRUE); 
  if (ret >= 0x80000000)
  {
    //失败
    printf("Error IN OsCreateRemoteThread2 ZwCreateThread!\n");
    goto OsCreateRemoteThread2Ret;
    //end
  }
  if(lpThreadId)
  {
    *lpThreadId = (DWORD)cid.UniqueThread;
  }
  if (!(dwCreationFlags & CREATE_SUSPENDED))
    {
        ZwResumeThread(hThread, NULL);
    }
OsCreateRemoteThread2Ret:
  return hThread;
}
里面红色的部分是需要根据实际需要修改的,当然,你也可以这样
context.Ecx =(ULONG)GetProcAddress(GetModuleHandle("ntdll.dll"),"RtlExitUserThread");
简单解释一哈上面的代码。
//0 部分是准备桩函数用的。不详细解释
//1 部分是准备线程运行时候用的堆栈,其初始化的大小和方式来自ReactOS的CreateRemoteThread-》BasepCreateStack。
//2 部分是准备线程运行的上下文的。可以查看ReactOS的CreateRemoteThread-》BasepInitializeContext。这里为简便起见,获取了当前线程的CONTEXT,然后根据实际情况设置了ESP\EIP\EBP,EAX\ECX\EDX为其他初始化情况。在这里可以不用修改。
从myBaseThreadInitThunk的代码来看,其要求shellcode具有如下形式:
DWORD WINAPI  FuncName(PVOID lpParam){.......}形式。

到这里关于CreateRemoteThread的话题就说完了。
再说说在实际测试的时候发现的一些现象(问题?)吧。
1、我们调用 GetDesktopWindow获取窗口句柄,再GetWindowThreadProcessId获取进程ID得到的将是csrss.exe的PID,在VISTA(WIN7)下获取的就是当前会话的csrss.exe的PID,服务里面的就是sessionID为0 的csrss.exe的PID。但上述调用过程就是不能在csrss.exe自己内部调用,如果调用会导致csrss.exe异常退出。机器蓝屏。
2、OS 名称:          Microsoft Windows 7 旗舰版
OS 版本:          6.1.7600 暂缺 Build 7600
以下代码失效,常用于ShellCode中查找KERNEL32.DLL的基址
  HMODULE hKernel32;
  __asm 
  {
    mov eax, fs:[0x30];
    mov eax, [eax + 0x0C];
    mov eax, [eax + 0x1C];
    mov eax, [eax];
    mov eax, [eax + 0x08];
    mov hKernel32, eax;
  }
将找到 KernelBase.dll
有效系统2K\XP\03\VISTA。

BY 飞心
2009.11.18

附件是此文档的DOC和PDF版

恭喜ID[飞翔的猫咪]获看雪安卓应用安全能力认证高级安全工程师!!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (9)
雪    币: 225
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
longway 活跃值 2009-11-18 16:00
2
0
好帖。学习了,虽然还没搞过WIN7。
雪    币: 459
活跃值: 活跃值 (84)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
qihoocom 活跃值 9 2009-11-18 16:05
3
0
xxload都被你扒了啊~ 自己实现太麻烦了,比没必要,还是PATCH一下比较爽

其实这个没什么,微软本来也只是稍微提供下提高普通程序注服务的门槛,让大家不要乱注搞出来提权XX,懂行的稍微跟踪一下就知道怎么过了。

另外你逆的那个代码有问题,IsBadWritePtr是返回FLASE才说明可写的,你写反了。
雪    币: 285
活跃值: 活跃值 (34)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
飞心男孩 活跃值 2 2009-11-18 19:45
4
0
多谢指点。你原文件里的确不是那么写的。F5才发现确实错了。
雪    币: 319
活跃值: 活跃值 (10)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
DiYhAcK 活跃值 2 2009-11-18 21:28
5
0
根本不需要Patch。。。
雪    币: 459
活跃值: 活跃值 (84)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
qihoocom 活跃值 9 2009-11-18 21:40
6
0
欢迎楼上爆料 不过我也没有patch,改了一个常量值而已~
雪    币: 578
活跃值: 活跃值 (43)
能力值: ( LV9,RANK:780 )
在线值:
发帖
回帖
粉丝
非安全 活跃值 17 2009-11-19 04:51
7
0
膜拜下。。。。。。。。。。。。
雪    币: 285
活跃值: 活跃值 (34)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
飞心男孩 活跃值 2 2009-11-19 10:48
8
0
DiYhAcK料就是多。洒点出来撒。
我用了哈QueueUserAPC,总的来说和XP下没多大区别,不过就是一样的代码在对explorer.exe进行测试的时候,V下的死了,对记事本测试的是很就好好的。(初步测试结果)
雪    币: 259
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
szbigcat 活跃值 2009-11-30 17:33
9
0
if (!(dwCreationFlags & CREATE_SUSPENDED))
        {
                zwapi.ZwResumeThread(hThread, NULL);
        }

我在ZwResumeThread这句会出现一个 应用程序正常初始化错误 0xC000000D?
线程是创建了,但是执行的shellcode会出现这个错误,原w32 api CreateRemoteThread就没这个问题。。
难道两个函数会造成什么区别?
跟一下先。。
雪    币: 259
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
szbigcat 活跃值 2009-11-30 23:40
10
0
我在        const CHAR myBaseThreadInitThunk[] =
        {
        '\xCC',

这里加了个int3

问题还是在ZwResumeThread那里弹出被注入的目标进程初始化失败 0xC000000D
点了确认按钮之后才执行到int3,说明shellcode还没执行就出错了。。
这是怎么回事?大牛解释一下?xp系统

0:001> u RtlExitUserThread
ntdll!RtlExitUserThread:
7c98342b 8bff            mov     edi,edi
7c98342d 55              push    ebp
7c98342e 8bec            mov     ebp,esp
7c983430 e8a524fdff      call    ntdll!LdrShutdownThread (7c9558da)
7c983435 64a118000000    mov     eax,dword ptr fs:[00000018h]
7c98343b ff7508          push    dword ptr [ebp+8]
7c98343e c680750f000001  mov     byte ptr [eax+0F75h],1
7c983445 6afe            push    0FFFFFFFEh
0:001> g

/// 下面出错
(374.ea0): Unknown exception - code c000000d (first chance)
(374.ea0): Unknown exception - code c000000d (!!! second chance !!!)
eax=00affc58 ebx=00000003 ecx=00affc84 edx=7c92e514 esi=7ffd7000 edi=c000000d
eip=7c98671e esp=00affc58 ebp=00affca8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!RtlRaiseStatus+0x26:
7c98671e c9              leave
0:002> u
ntdll!RtlRaiseStatus+0x26:
7c98671e c9              leave
7c98671f c20400          ret     4
游客
登录 | 注册 方可回帖
返回