远程线程注入——相信对Windows底层编程和系统安全熟悉的人并不陌生,其主要核心在于一个Windows API函数CreateRemoteThread,通过它可以在另外一个进程中注入一个线程并执行。在提供便利的同时,正是因为如此,使得系统内部出现了安全隐患。常用的注入手段有两种:一种是远程的dll的注入,另一种是远程代码的注入。后者相对起来更加隐蔽,也更难被杀软检测。
顾名思义,远程线程注入就是在非本地进程中创建一个新的线程。相比而言,本地创建线程的方法很简单,系统API函数CreateThread可以在本地创建一个新的线程,其函数声明如下:
HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
PDWORD lpThreadId
);
这里最关心的两个参数是lpStartAddress和lpParameter,它们分别代表线程函数的入口和参数,其他参数一般设置为0即可。由于参数的类型是LPVOID,因此传入的参数数据需要用户自己定义,而入口函数地址类型必须是LPTHREAD_START_ROUTINE类型。LPTHREAD_START_ROUTINE类型定义为:
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
按照上述定义声明的函数都可以作为线程函数的入口,和CreateThread类似,CreateRemoteThread的声明如下:
HANDLE WINAPI CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
可见该函数就是比CreateThread多了一个参数用于传递远程进程的打开句柄,而我们知道打开一个进程需要函数OpenProcess,其函数声明为:
HANDLE WINAPI OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);
第一个参数表示打开进程所要的访问权限,一般使用PROCESS_ALL_ACCESS来获得所有权限,第二个参数表示进程的继承属性,这里设置为false,最关键的参数是第三个参数——进程的ID。因此在此之前必须获得进程名字和PID的对应关系,TlHelp32.h库内提供的函数CreateToolhelp32Snapshot、Process32First、Process32Next提供了对当前进程的遍历访问。
通过调用EnableDebugPrivilege(SE_DEBUG_NAME)提高本地程序权限后就可以打开系统进程了。然后传入进程句柄到CreateRemoteThread注入远程进程,但是遗憾的是远程线程无法运行,这里就引发了第二个问题。CreateRemoteThread和CreateThread并不仅仅是多了一个进程句柄参数那么简单,其中更大的区别是它们的函数入口和参数的区别。CreateThread是创建本地线程,函数入口地址和参数都在本地进程,这很好理解,但是CreateRemoteThread创建的是其他进程的线程,它的入口地址和参数就该在其他进程中。如果强行把本地地址和参数传入,虽然编译上能通过,但是运行时侯被注入的进程会查找和本地进程相同值的地址和参数地址,当然结果可想而知,这就像拿着一号公寓201的钥匙去开二号公寓201的门一样。(或许在这里读者会有这个想法,可不可以远程注入本地进程呢?虽然这么做没什么意义,希望有兴趣的读者可以试一试,看看能否成功。)
既然这样,那么如何告诉远程线程需要执行的代码和地址呢?继续上边那个例子,假设在一号公寓201房间内可以使用高功率电器,但是一号公寓检查严格,一旦有此情况立马被禁止。而二号公寓戒备很松,所以有人想办法在二号公寓新准备一个空的房间专门使用高功率电器,这样即回避了检查,也达到了目的。这里一号公寓相当于本地进程,二号公寓相当于系统进程,使用高功率电器相当于黑客的行为,准备新的房间相当于开辟新的存储空间,禁止使用高功率电器相当于杀软的查杀。那么这里就需要关心如何在二号公寓新建一个房间,这里系统有两个API函数VirtualAllocEx和WriteProcessMemory,顾名思义,前者在远程进程中申请一段内存用于存储数据或者代码——准备房间,后者在申请的空间内写入数据或者代码——准备高功率电器。这就完美地诠释了远程线程的注入原理。
尽管我们详细地说明了远程线程注入的基本原理,但是理解起来还是需要有一定Java基础和理解能力的,我们可以到动力节点在线免费学习Java基础的视频课程,打好坚实的Java基础为后面深入学习Java的各种理论知识埋下伏笔。
提枪策马乘胜追击04-21 20:01
代码小兵92504-17 16:07
代码小兵98804-25 13:57
杨晶珍05-11 14:54