多线程注入

来源:互联网 发布:淘宝上二手电脑怎么样 编辑:程序博客网 时间:2024/06/05 02:08

Windows是一种多任务的操作系统,一方面是指它支持多进程,每个进程有独立的地址空间,另一方面则在于Windows的多线程支持。尽管多线程常常带来很多问题,并且在UN*X世界从来没有成为主流,但是在并发计算越来越流行的时代,多线程还是比较有前景的。在Windows系统中,应用程序通过调用 CreateThread 就可以创建一个线程,这个线程和主线程同属一个进程,共享着进程的所有资源,但是具有自己的堆栈和局部存储,可以平等的获得CPU时间,和主线程同时运行着。

远程线程注入技术是通过调用 CreateRemoteThread 函数在别的进程中创建一个线程,因此这个线程可以称为远程线程,而被注入的远程进程又称为宿主进程。远程线程就像是宿主进程自己调用 CreateThread 创建的线程一样,能够访问宿主进程的所有资源。计算机病毒通过向宿主进程注入一个线程,就可以以宿主进程的身份访问计算机系统,达到控制计算机的目的。
第一步,提升本进程的系统权限。
因为我们要操作的是系统中的其他进程,没有足够的系统权限是无法读取甚至写入其他进程的内存地址的。提升进程权限可能用到以下的函数:

1.函数OpenProcessToken(
HANDLE ProcessHandle, // 进程的句柄
DWORD DesiredAccess, // 对进程的访问描述
PHANDLE TokenHandle // 打开进程令牌的句柄指针
);
这个函数的作用是打开进程令牌。

2.函数LookupPrivilegeValue(
LPCTSTR lpSystemName, //系统名称
LPCTSTR lpName, // 特权名称
PLUID lpLuid // 本地系统唯一的ID号
);
这个函数将会返回一个本地系统内独一无二的ID,来用于系统权限的更改,它的第1个参数是系统名,nil表示本系统。第2个参数是特权的名字。第3个参数用来接收函数返回的ID

3.函数AdjustTokenPrivileges(
HANDLE TokenHandle, //更改权限的令牌环句柄
BOOL DisableAllPrivileges, //是否修改所有权限的标志位
PTOKEN_PRIVILEGES NewState, //新的系统权限信息
DWORD BufferLength, //上一个参数的长度
PTOKEN_PRIVILEGES PreviousState, // 返回更改系统特权以前的权限
PDWORD ReturnLength //上一个参数的长度
);
这个函数用于更改进程的系统权限 ,第1个参数是要更改权限的令牌环句柄。第2个参数如果为true表示更改所有的系统权限 ,false表示更改部分。第3个参数是要更改的系统特权的值。第4个参数是第3个参数的大小。第5个参数返回更改系统特权以前的权限,我们不需要就设为nil。第6个参数是第5个参数的大小。
第二步,进入宿主的内存空间
在拥有了进入宿主程序内存的权限之后,我们所要做的是在其内存空间加入一些新的程序代码,或者是让其载入一个Dll文件里面的函数并运行起来。加入新的代码可以省掉一个dll文件,而加载dll文件可以安装一些系统级的钩子,如果我们的隐形程序是一个截获密码的程序,加载dll就是一个很好的选择。
Kernel32.dll中的函数LoadLibraryW可以加载dll,它只需要dll文件的文件路径就可以完成操作,我们可以很容易的在程序代码中实现取出一个文件路径的操作。但是,我们希望在宿主程序中加载,而我们取出的dll文件路径并不存在于宿主程序的内存空间里面,所以我们需要把dll的文件路径写入宿主的内存空间。这些操作可能用到以下的函数:
这个函数用于修改我们宿主进程的一些属性,这些属性放在第一个参数里面,比如PROCESS_VM_OPERATION就是允许远程VM操作,即允许VirtualProtectEx和WriteProcessMemory函数操作本进程内存空间。PROCESS_CREATE_THREAD 就是允许远程创建线程。PROCESS_VM_WRITE就是允许远程VM写,即允许 WriteProcessMemory函数访问本进程的内存空间。第二个参数是一个标志参数,用来确定返回的句柄是否可以被新的进程继承。我们的程序中设为False。第三个参数需要操作的进程Id,也就是我们的宿主进程的Id。
2.函数VirtualAllocEx(
HANDLE hProcess, //要进行操作的进程句柄,当然是我们的宿主了
LPVOID lpAddress, //分配空间的起始地址
DWORD dwSize, //分配空间的大小
DWORD flAllocationType, // 分配空间的类型
DWORD flProtect // 访问保护类型
);
我们使用 VirtualAllocEx函数在宿主进程中开辟一块内存空间,用于存放dll的文件名。VirtualAllocEx的第1个参数是要操作的进程,第2个是起始地址,第3个是长度,第4,5个是操作参数。其中MEM_COMMIT表示本函数分配的是物理内存或者是内存的页面文件,PAGE_READWRITE表示分配的区域内允许读写。
3.函数WriteProcessMemory (
HANDLE hProcess, //所要操作进程的句柄
LPVOID lpBaseAddress, //开始进行些操作的起始地址
LPVOID lpBuffer, //要写入数据的缓冲区指针
DWORD nSize, // 要写的bytes数
LPDWORD lpNumberOfBytesWritten // 实际写入的bytes数
);
前面在宿主内存中创建好空间后,现在往里面写入dll的名称,而我们的WriteProcessMemory函数就可以胜任这一项工作。WriteProcessMemory函数的第一个参数 是需要往其内存里面写入dd的进程句柄,第二个参数是 “要进行写操作”的目标内存起始地址,第三个参数是 “需要被写入的数据”的地址,第四个参数是准备要写入的长度,第五个参数是实际操作中写的长度,这个参数是被函数输出的。到这里我们就已经能成功把dll的路径名称写进了宿主的内存空间。

第三步,在宿主中启动新的线程!
刚才我们已经在宿主程序中创建了一个用于存放一个dll文件路径的缓冲区,现在我们就要让这个dll在宿主的内存空间中运行起来。我们是用LoadLibraryW函数来加载的,而使用LoadLibraryW,又需要知道LoadLibraryW函数的入口地址。所以在加载dll之前,我们要用GetProcAddress来得到LoadLibraryW的入口地址。我们来看看这几个函数的使用方法:
1.GetProcAddress(
HMODULE hModule, //dll模块的句柄
LPCSTR lpProcName // 函数名称
);
我们用这个函数主要想得到kernel32.dll中的函数LoadLibraryW的入口地址,所以
GetProcAddress(GetModuleHandle('Kernel32'), 'LoadLibraryW')就可以了,当然有些细节得符合程序编译器的要求,VC下使用就要改成
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW")的形式。

2.CreateRemoteThread (
HANDLE hProcess, //要进行操作的进程句柄,也就是我们的宿主句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性的指针
DWORD dwStackSize, //初始化堆(stack)的大小
LPTHREAD_START_ROUTINE lpStartAddress,//新建线程函数的指针,或叫做地址
LPVOID lpParameter, //新建线程函数的参数
DWORD dwCreationFlags, //标志位
LPDWORD lpThreadId //线程返回值
);
这个函数就是本文的点睛之笔了,我们之前所做所有的一切,都是在为它做准备工作,它的功能就是在其他任何进程中创建新的线程,让其他的程序或进程附加执行我们的代码。
CreateRemoteThread函数的第一个参数是要操作的宿主进程句柄;第二个参数为线程安全参数的指针,这里设为nil;第三个参数为初始化堆(stack)的大小,这里设0;第四个参数为新建线程函数的指针或叫做地址或叫入口;第五个参数为新建线程函数的参数,这里就是我们的dll路径名称;第六个参数是标志位,这里设0;第七个参数是线程返回值。
到这里,我们基本上把如何编写一个能够寄生于其他进程之中运行的API函数都依次讲完了,如果读者有兴趣亲手试一下的话,在光盘中可以找到已经编好的程序完整代码和详细的程序注释。当然这个寄生于进程中的dll文件还需要自己编写,不过我写的内容是弹出一个“Helloworld”,而寄生的宿主是我们常用的QQ。我们打开Windows的任务管理器,看到只有QQ在运行,而我们看不到其他的东西。

 

原创粉丝点击