最近一直在研究关于代码注入的问题,看到最经典的文章应该就是《Three Ways to Inject Your Code into Another Process》,如果想看细节可以直接去看这篇文章。本文主要讨论关于在Win Vista/7等系统中将代码注入系统进程的方法,在WinXP以及之前的版本中,利用CreateRemoteThread就可以不受限制地注入进程,然而到了WinVista/7系统中,在注入系统进程的时候会失败,查看失败代码为8,进一步查MSDN的描述为:ERROR_NOT_ENOUGH_MEMORY。实际意思就是没有足够的存储可用来处理此命令,可以这和线程注入有什么关系呢,因为之前已经使用了VirtualAllocEx来申请了内存,怎么会没有足够的存储呢?想不通的时候只能仔细去看MSDN,果然看到了关键性的一句话:

Terminal Services isolates each terminal session by design. Therefore, CreateRemoteThread fails if the target process is in a different session than the calling process.

意思就是终端服务采用会话(session)分离设计,那么如果注入进程和目标进程在不同的会话(session)中的时候,CreateRemoteThread调用就会失败。那么所谓的会话(session)分离又是什么意思呢?其实在微软的公开文档中说的很清楚,下面引用文中的两幅图来简要说明一下会话(session)分离。

Figure 1

Figure 2
  
Figure 1显示的是3个用户登录系统后的情况,Session 0是控制台的Session,Session 1和2是远程登录用户的Session,第一个登录的用户被分配到Session 0, 而Session 1和Session 2分别代表其他用户登录到本地系统。 Figure 2显示的同样是3个用户登录系统后的情况,不同的是现在只有系统服务运行在Sessions 0,而后登录的用户一次被分配Session1、Session2和Session3。
微软这样做应该是出于安全的考虑,微软公开的文档中还提到在Session分离后,哪些操作应是避免的以及服务进程如何与用户进程通信等内容,如果以后经常涉及到这方面的开发工作,建议认真看一下这篇文章。

现在继续讨论CreateRemoteThread的失败,既然微软在设计上不允许用户进程往系统进程中注入代码,那么通过微软公开的API肯定很难达到目的,Google一下,发现还是有很多前辈在研究这个问题:

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

(国内牛人写的,汇编分析的,对于我这种没有太多汇编基础的说实话没看懂,直接用他的API注入总是失败,感兴趣的朋友可以去看一下);

  1. Remote Thread Execution in System Process using NtCreateThreadEx for Vista & Windows7
    (国外的一篇文章,说的非常通俗易懂,采用了未文档化的API NtCreateThreadEx,英文不好的朋友借着谷歌翻译应该也可以看明白了,毕竟代码非常清晰。)

采用第二篇文章说的方法,我尝试自己写了个服务程序,实际就相当于从Session 0 往Session 1(或2,3..)的进程注入代码,我注入的是explorer.exe进程,成功。另外我又写了一个注入代码到系统进程winlogin.exe的程序,相当于从Session 1(或2,3..) 往 Session 0注入,成功。 这里就不把代码贴出来了,实际就是结合《Three Ways to Inject Your Code into Another Process》和《Remote Thread Execution in System Process using NtCreateThreadEx for Vista & Windows7》两篇文章中说的代码,读者完全可以自己尝试做到。记得要判断操作系统版本,XP以及XP之前的系统版本是不能采用NtCreateThreadEx函数的,直接采用CreateRemoteThread即可。 我在代码注入中还遇到过一个问题,因为之前写的程序都是32位的,今天放在64位的Win 7下尝试注入winlogin.exe程序总是失败,用NtCreateThreadEx函数返回错误,错误代码是0xC0000022(STATUS\_ACCESS\_DENIED),是权限不足,于是我尝试使用CreateRemoteThread函数,依旧返回错误,错误代码是5(ERROR\_ACCESS\_DENIED),表达的是一个意思,都是没有权限,这里在调用这些函数的时候程序已经经过提权操作,那么应该不是本程序没有权限。在我Google了很久之后搞明白了,原来windows在设计的时候是将32位程序和64位程序完全隔离的,很多朋友想要在64位的系统中注入DLL到系统进程中,系统进程必然是64位的,那么按照我的理解(也许有大牛有未公开的其他方法),首先这个DLL必须要是64位,然后注入的程序也要是64位的,那么有没有办法写一个同时兼容32位和64位的注入代码的程序呢? 我在Stackoverflow看过一篇提问《C++: Injecting 32 bit targets from 64 bit process》,虽然没达到我的目的,但仍旧是值得一看的内容。

技术研究本身就是一个不断挑战的过程,Session隔离的安全保护被突破,那么32位和64位的设计隔离应该也是可以突破的,最终是可以完成一个32位程序将代码注入到32位或者64位的进程的(至少在现阶段这样的程序还是需求的),我将继续研究这方面内容,也希望能与技术达人交流:<zjlover0608@gmail.com>。