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

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。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值