利用远程线程实现dll的注入
2014-08-20 21:07
666 查看
一、基本思路
基本思路就是让远程进程载入一个我们的dll,这样我们只要在dll里面写入相关代码,基本就可以为所欲为了。但问题是远程进程显然不可能自己调用LoadLibrary来加载我们的dll,我们就要想办法“让”它来调用。我们用CreateRemoteThread函数在远程进程中创建一个线程,线程函数的地址设为LoadLibrary的地址,线程函数参数设为“Mydll.dll”。即让远程进程调用LoadLibrary载入我们的dll,就可以了。
二、CreateRemoteThread函数
lpThreadAttributes:指向一个SECURITY_ATTRIBUTES结构的指针,用以描述新线程的安全性等内容。一般传入NULL,表示使用默认的安全性。
dwStackSize:线程栈的初始大小。一般传入0,表示使用默认值。
lpStartAddress:线程函数,线程函数格式为:
dwCreationFlags:如果值为CREATE_SUSPENDED,则新线程创建后会直接挂起。如果值为0,则新线程创建后会立即运行。
lpThreadId:一个指针,用以保存新创建的线程的ID。也可以传入NULL,不接受ID号。
不要以为这个函数很容易完成。问题就在于我们的调用进程(A进程)和远程进程(B进程)是在两个完全不搭界的地址空间中。A进程想在B进程中创建一个新线程,这个并不容易。因为我们传入的参数lpStartAddress及lpParameter只是两个地址,这两个地址在A进程中。而在B进程中,这两个地址值当然是存在的,但是这个地址上保存的是什么玩意就不得而知了。为此,我们不得不做些事情,来保证在B进程中,lpStartAddress和lpParameter两个地址含有我们所需要的内容。
首先lpStartAddress很容易搞定。我们只要在自己的进程中这么写:
这样我们就可以获取LoadLibraryW的地址了。
首先我们看到我们获取的是LoadLibraryW,为什么不是LoadLibrary?事实根本就没有LoadLibrary函数,LoadLibrary是一个宏,它分别被定义为LoadLibraryA和LoadLibraryW,即一个ANSI版本和一个Unicode版本。我们如果想用ANSI版本,也可以获取LoadLibraryA的地址。
然后可能有人要问,我们确实获得了LoadLibraryW的地址,但这个地址是LoadLibraryW在A进程中的地址,我们可以把这个地址给B进程吗?可以!这是因为Kernel32.dll在所有的应用程序中被载入到地址空间中的地址都是相同的,所以LoadLibraryW函数的地址也是相同的!正因为这个“相同”,我们才可以直接把这个地址传给远程进程,否则是无论如何也不能这样做的!
这时可能有人会想,那么这样,我们直接调用:
很遗憾,不行!关键在于“d:\\Mydll.dll”这几个字符是在A进程的空间中,我们传的只是这个字符串的地址。但是,在B进程中,该地址处的内容不是这个字符串,天晓得这个地址处会是什么内容!那怎么办呢,还好我们有下面两个函数。
三、VirtualAllocEx和WriteProcessMemory函数。
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(可读写可执行)等。
hProcess:进程句柄
lpBaseAddress:要写入数据的地址。
lpBuffer:要写入的数据。
nSize:要写入的数据的大小,以字节为单位
lpNumberofBytesWritten:一个地址,返回实际写入的字节数。
有函数VirtualAllocEx,就有函数VirtualFreeEx与之对应。
lpAddress:要释放内存的地址。当dwFreeType为MEM_RELEASE时,该值必须为VIrtualAllocEx的返回值。
dwSize:要释放的大小。当dwFreeType为MEM_RELEASE时,必须为0,表示把整个区域释放。当dwFreeType为MEM_DECOMMIT时,系统把进程从lpAddress到lpAddress+dwSize的这一段内存撤销调拨。
dwFreeType:可为MEM_RELEASE(释放整个区域)或MEM_DECOMMIT(撤销调拨,仍处于预订状态,还可以重新调拨)。
四、一个例子。
最后,我们注入成功了。我们退出的时候,还需要把那个dll可释放掉。我们同样需要CreateRemoteThread,并传入FreeLibrary。
基本思路就是让远程进程载入一个我们的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。
相关文章推荐
- 将注入进行到底--------远程线程实现 Dll 注入 汇编实现
- 利用VB远线程注入技术实现键盘拦截的例子(无DLL)
- 利用远程线程进行DLL的注入
- 利用HOOK技术实现DLL远程进程注入
- 利用远程线程无DLL直接注入
- 进程注入DLL实现(APC和远程线程创建)
- 转贴:利用VB远线程注入技术实现键盘拦截的例子(无DLL)
- Win32汇编实现DLL的远程注入及卸载
- C++ 实现远程注入DLL技术要点总结
- 谨慎使用DLL_THREAD_ATTACH,以及利用DLL_THREAD_ATTACH来阻止远程线程的创建执行
- 使用远程线程来注入DLL
- 外挂或病毒注入DLL到宿主进程,然后远程启动线程方式
- 利用c#实现远程注入非托管WIN32程序,并利用嵌入汇编调用非托管WIN32程序中的内部过程
- 无需远程注入DLL,几行代码实现War3魔兽地图全开
- 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。
- 使用远程线程来注入DLL
- 《windows核心编程系列》谈谈使用远程线程来注入DLL。
- Win32汇编实现DLL的远程注入及卸载
- 远程注入DLL实现进程隐藏以及键盘记录器
- 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。