您的位置:首页 > 其它

利用远程线程实现dll的注入

2014-08-20 21:07 666 查看
一、基本思路

基本思路就是让远程进程载入一个我们的dll,这样我们只要在dll里面写入相关代码,基本就可以为所欲为了。但问题是远程进程显然不可能自己调用LoadLibrary来加载我们的dll,我们就要想办法“让”它来调用。我们用CreateRemoteThread函数在远程进程中创建一个线程,线程函数的地址设为LoadLibrary的地址,线程函数参数设为“Mydll.dll”。即让远程进程调用LoadLibrary载入我们的dll,就可以了。

二、CreateRemoteThread函数

HANDLE CreateRemoteThread(
  HANDLE hProcess,                          // 远程进程句柄
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性描述符
  SIZE_T dwStackSize,                       // 栈大小
  LPTHREAD_START_ROUTINE lpStartAddress,    // 线程函数
  LPVOID lpParameter,                       // 线程函数的参数
  DWORD dwCreationFlags,                    // 一些标志
  LPDWORD lpThreadId                        // 保存创建的线程的ID
);
hProcess:远程进程的句柄,通过OpenProcess等获得

lpThreadAttributes:指向一个SECURITY_ATTRIBUTES结构的指针,用以描述新线程的安全性等内容。一般传入NULL,表示使用默认的安全性。

dwStackSize:线程栈的初始大小。一般传入0,表示使用默认值。

lpStartAddress:线程函数,线程函数格式为:

DWORD WINAPI ThreadProc(
  LPVOID lpParameter   // 线程函数的参数
);
lpParameter:传给线程函数的参数。

dwCreationFlags:如果值为CREATE_SUSPENDED,则新线程创建后会直接挂起。如果值为0,则新线程创建后会立即运行。

lpThreadId:一个指针,用以保存新创建的线程的ID。也可以传入NULL,不接受ID号。

不要以为这个函数很容易完成。问题就在于我们的调用进程(A进程)和远程进程(B进程)是在两个完全不搭界的地址空间中。A进程想在B进程中创建一个新线程,这个并不容易。因为我们传入的参数lpStartAddress及lpParameter只是两个地址,这两个地址在A进程中。而在B进程中,这两个地址值当然是存在的,但是这个地址上保存的是什么玩意就不得而知了。为此,我们不得不做些事情,来保证在B进程中,lpStartAddress和lpParameter两个地址含有我们所需要的内容。

首先lpStartAddress很容易搞定。我们只要在自己的进程中这么写:

PTHREAD_START_ROUTINE pfhThreadRtn=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")),"LoadLibraryW");


这样我们就可以获取LoadLibraryW的地址了。

首先我们看到我们获取的是LoadLibraryW,为什么不是LoadLibrary?事实根本就没有LoadLibrary函数,LoadLibrary是一个宏,它分别被定义为LoadLibraryA和LoadLibraryW,即一个ANSI版本和一个Unicode版本。我们如果想用ANSI版本,也可以获取LoadLibraryA的地址。

然后可能有人要问,我们确实获得了LoadLibraryW的地址,但这个地址是LoadLibraryW在A进程中的地址,我们可以把这个地址给B进程吗?可以!这是因为Kernel32.dll在所有的应用程序中被载入到地址空间中的地址都是相同的,所以LoadLibraryW函数的地址也是相同的!正因为这个“相同”,我们才可以直接把这个地址传给远程进程,否则是无论如何也不能这样做的!
这时可能有人会想,那么这样,我们直接调用:

CreateRemoteThread(hProcessRemote,NULL,0,pfhThreadRtn,"d:\\Mydll.dll",0,NULL);
是不是就可以了呢?

很遗憾,不行!关键在于“d:\\Mydll.dll”这几个字符是在A进程的空间中,我们传的只是这个字符串的地址。但是,在B进程中,该地址处的内容不是这个字符串,天晓得这个地址处会是什么内容!那怎么办呢,还好我们有下面两个函数。

三、VirtualAllocEx和WriteProcessMemory函数。

LPVOID VirtualAllocEx(
  HANDLE hProcess,          //进程句柄
  LPVOID lpAddress,         //起始地址 
  SIZE_T dwSize,            //申请的大小
  DWORD flAllocationType,   //申请内存类型
  DWORD flProtect           //保护属性
);
VirtualAllocEx函数可以让我没在指定的进程的地址空间中申请一块虚拟内存,并且映射到物理内存上。

hProcess:要申请内存的进程的句柄

lpAddress:想要申请内存的起始地址。该地址必须为分配粒度(通常为64K)的整数倍,如果不是,系统会自动为其向下取整到64K的倍数。如果在该地址,系统能够成功地分配出我们指定大小的内存,则成功返回该基地址(基地址=lpAddress向下取整到64K的整数倍);如果系统发现在该地址不能分配出指定的大小,则返回NULL。通常我们会给这个参数传入NULL,这是系统会自动搜索进程的地址空间,并为我们分配出需要的内存。

dwSize:我们想要分配的内存的大小,必须是页面大小的整数倍。在x86和x64的机器上,页面大小都是4K。

flAllocationType:可以是MEM_RESERVE或者MEM_COMMIT。前者表示预订(不映射物理内存),后者表示调拨物理内存。一般我们可以同时预订和调拨,这是我们传入

MEM_RESERVE | MEM_COMMIT

flProtect:页面保护属性。可以为PAGE_READONLY(只读),PAGE_READWRITE(可读写),PAGE_EXECUTE(可执行),PAGE_EXECUTE_READ(可读可执行),PAGE_EXECUTE_READWRITE(可读写可执行)等。

BOOL WriteProcessMemory(
  HANDLE hProcess,                // 进程句柄
  LPVOID lpBaseAddress,           // 起始地址
  LPCVOID lpBuffer,               // 数据缓存
  SIZE_T nSize,                   // 要写入数据的大小,以字节为单位
  SIZE_T * lpNumberOfBytesWritten // 返回实际写入的数据大小,以字节为单位
);
该函数可以让我们在指定的进程的地址空间中写入一些东西。

hProcess:进程句柄

lpBaseAddress:要写入数据的地址。

lpBuffer:要写入的数据。

nSize:要写入的数据的大小,以字节为单位

lpNumberofBytesWritten:一个地址,返回实际写入的字节数。

有函数VirtualAllocEx,就有函数VirtualFreeEx与之对应。

BOOL VirtualFreeEx(
  HANDLE hProcess,   // 进程句柄
  LPVOID lpAddress,  // 起始地址
  SIZE_T dwSize,     // 大小
  DWORD dwFreeType   // 类型
);
hProcess:进程句柄

lpAddress:要释放内存的地址。当dwFreeType为MEM_RELEASE时,该值必须为VIrtualAllocEx的返回值。

dwSize:要释放的大小。当dwFreeType为MEM_RELEASE时,必须为0,表示把整个区域释放。当dwFreeType为MEM_DECOMMIT时,系统把进程从lpAddress到lpAddress+dwSize的这一段内存撤销调拨。

dwFreeType:可为MEM_RELEASE(释放整个区域)或MEM_DECOMMIT(撤销调拨,仍处于预订状态,还可以重新调拨)。

四、一个例子。

<span style="white-space:pre">	</span>HANDLE hProcessRemote;
<span style="white-space:pre">	</span>hProcessRemote=OpenProcess(PROCESS_ALL_ACCESS,FALSE,3720);//获得PID为3720的进程句柄
<span style="white-space:pre">	</span>LPVOID pAddress=VirtualAllocEx(hProcessRemote,0,4*1024,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
<span style="white-space:pre">	</span>TCHAR buffer[]=TEXT("d:\\dll.dll");
<span style="white-space:pre">	</span>WriteProcessMemory(hProcessRemote,pAddress,buffer,(_tcslen(buffer)+1)*2,NULL);
<span style="white-space:pre">	</span>PTHREAD_START_ROUTINE pfhThreadRtn=(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")),"LoadLibraryW");
<span style="white-space:pre">	</span>CreateRemoteThread(hProcessRemote,NULL,0,pfhThreadRtn,pAddress,0,NULL);
注:这只是一个基本示例,没有做任何错误检查。

最后,我们注入成功了。我们退出的时候,还需要把那个dll可释放掉。我们同样需要CreateRemoteThread,并传入FreeLibrary。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: