使用远程线程进行DLL注入的介绍

首先声明,我是一个初学者,这篇文章只是我学习编程过程中的一个记录 ,仅供大家参考。文章中涉及的源代码是我在Windows核心编程一书中和网上的一些资料里学习到的,在此向那些前辈表示感谢。本源代码没有版权,网友可随意复制修改,但请注明引用出处 http://blog.csdn.net/scienwu。此代码仅供学习参考之用,用此代码修改而成的程序所造成的损失作者ScienWu不承担任何责任。

由于作者水平有限,难免有疏忽错误之处,欢迎各位同道给予批评指正,也希望能和各位多交流,谢谢。

这篇文章介绍我在学习Windows核心编程时的一些心得,主要内容是关于使用远程线程进行Dll注入。

关于使用远程线程进行DLL注入和API挂接并不是什么新鲜技术,网上的介绍有很多,但或是粗略带过或是比较高深,让我这样的初学者学习起来有些吃力,最近在看Windows核心编程这本书,虽然是本老书,但却讲得由浅入深而且很详实,还有例程,让我学习起来省力不少。为了使向我这样的初学者少走弯路,在此把使用远程线程进行DLL注入方法做一个的详细介绍,供大家参考交流。高手若觉得此篇文章肤浅请不要扔西红柿,因为我的确是一个菜鸟

详细的实现过程。我使用的是VS.NET2005,如果您使用的是其他IDE,流程是一样的,但因为不同编译器之间的细微差别,请自行做相应的调整。

使用远程线程进行DLL注入的目的在于要在目标程序的领空内执行你自己的程序代码,主要用于监控修改目标程序的某些消息或数据(如汉化补丁、外挂、插件等),而且也是进行API挂接的前提条件。

要进行远程线程的DLL注入首先要有一个目标程序(1),一个包含你要执行代码的DLL文件(2)和一个将你的DLL文件注入到目标程序的引导程序(3)。将这三个文件放在同一个目录下执行引导程序。过程是这样的:引导程序使用CreateProcess建立一个新的目标程序的进程,这样我们可以很容易的得到进程句柄,或者用FindWindow获取已执行进程的进程句柄,有了进程句柄后就可以对进程进行操作了。然后使用VirtualAllocExWriteProcessMemory将必要的参数信息等写入到目标进程的内存空间,最后使用CreateRemoteThread在目标程序的进程中建立远程线程,并让远程线程执行LoadLibraryA载入我们自己的DLL,使得代码得以在目标进程的领空内执行。因为Kernel32.dll是每一个应用程序都必须加载的系统DLL,所以LoadLibrary函数可以在任何一个程序的进程中被调用,因此我们选择让远程线程启动时执行LoadLibrary来加载我们自己的DLL是再合适不过的了。

1)目标程序:当然这个目标程序可以而且绝大部分情况不是你写的程序,你可能没有源代码。在这个例子中,我所使用的目标程序是一个已经编译好的空白MFC程序Test.exe,仅仅显示一个Windows窗口没有其他作用。

2)包含代码的DLL文件myDll.dll:接下来你要是用IDE建立一个包含你自己要执行的代码的DLL项目。打开IDE-File-New-Project,在打开的对话框里选择Visual C++ Win32类型下的Win32 Project程序,命名为myDll,选择打开,在设置向导中的Application Settings处将Application Type选成DLL,其他使用默认选项,完成,这样会建立一个空的DLL项目,我们就在这个项目中书写需要注入进目标程序执行的程序代码。

myDll.cpp中已经有Dll文件的启动函数         DllMain

可能因为VS.NET2005默认使用的是UNICODE,为了适应UNICODEANSI我们需要在Stdafx.h文件中加入:#include <tchar.h> 或者使用所有WindowsAPIANSI版本。

 

这里我们完成一个简单的功能,就是在目标程序执行前弹出一个对话框。所以我们在myDll.cpp中添加如下的代码:

void myTestFun()

{

     MessageBoxEx(NULL,_TEXT("测试文本"),_TEXT("测试标题"),MB_OK,0);

 }

一个做测试用的非常简单的函数。为了能让我们自己的代码获得执行机会,我们把这个函数添加到DllMain中:

BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)

{

    }

switch (ul_reason_for_call)

     {

     case DLL_PROCESS_ATTACH:

         myTestFun();

         break;

     case DLL_THREAD_ATTACH:

     case DLL_THREAD_DETACH:

     case DLL_PROCESS_DETACH:

         break;

     }

    return TRUE;

 

这样只要我们的Dll被加载就会执行我们写的这段代码。这样DLL工程中的工作就完成了。编译会生成一个myDll.dll文件。

     3)将你的DLL注入到目标程序的引导程序DllLoader.exe:建立一个新的工程,因为只是一个引导程序,为了简单我选择了Win32 console application,命名为DllLoader,完成,这样我们就建立了一个控制台程序。

     需要在StdAfx.h文件中加入

#include <tchar.h>

 

     接着我们在_tmain函数里书写引导代码。

     //   我们自己的DLL文件的文件名,如果不在同一目录下,需要指定完成路径

     TCHAR * pcFileName = _TEXT("TestDll.dll");

     //   计算文件名字符串的长度

     int iSize = ( _tcslen(pcFileName) + 1 ) * sizeof(TCHAR);

     //   要写入目标进程的字符串参数的地址

     TCHAR * pcLibFileRemote = NULL;

     //   目标程序的文件名

     TCHAR * pcTargetFileName=_TEXT("ollyDbgTest.exe");

 

     //   创建进程所需的进程信息结构体

     PROCESS_INFORMATION ProcessInfo;

     //   创建进程所需的启动信息结构体

     STARTUPINFO StartupInfo;

     ZeroMemory(&StartupInfo, sizeof(StartupInfo)); //   初始化工作

     StartupInfo.cb = sizeof StartupInfo;           //   计算大小

 

     //   开始为目标程序建立进程

     if(CreateProcess(pcTargetFileName, NULL,NULL,NULL,FALSE,0,NULL,NULL,&StartupInfo,&ProcessInfo))

     {

         //   打开进程,并获得所有权限

         HANDLE handleTarget=OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessInfo.dwProcessId);

         //   在目标进程中分配iSize大小的空间以便将我们的DLL的文件名作为参数写入目标进程

         pcLibFileRemote=(TCHAR*)VirtualAllocEx(handleTarget,NULL,iSize,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);

         //   分配成功

         if(NULL != pcLibFileRemote)

         {

              //   把我们的DLL文件名写入目标进程的内存中

              if(WriteProcessMemory(handleTarget,pcLibFileRemote,(LPVOID)pcFileName,iSize,NULL))

              {

                   //   我们要在内存中执行的LoadLibrary函数所在的系统DLL

                   HMODULE hmoduleKernel32 = LoadLibrary(_TEXT("Kernel32"));

//   获取Kernel32.dll中的Loadlibrary函数的地址

#ifdef  UNICODE        //   为了适应UNICODEANSI两种编码方式

                        PTHREAD_START_ROUTINE pStart = (PTHREAD_START_ROUTINE)GetProcAddress(hmoduleKernel32,"LoadLibraryW");

#else

                        PTHREAD_START_ROUTINE pStart = (PTHREAD_START_ROUTINE)GetProcAddress(hmoduleKernel32,"LoadLibraryA");

#endif

                   /*

建立远程线程进行注入。这里多说几句CreateRemoteThread函数是在我们的引导程序DllLoader.exe中执行的,如果lpParamter参数直接传递字符串的话 这个字符串的内存是在DllLoader.exe中的,目标进程是无法访问这个地址的,这就是之所以前面很麻烦的把我们的DLL的文件名用WriteProcessMemory写入到目标进程的内存中的原因,只有这样目标进程才能正确的得到文件名参数。

                   */

                   CreateRemoteThread(handleTarget,NULL,0,pStart,pcLibFileRemote,0,NULL);

              }

         }

     }

这段代码就能在建立的目标进程中创建远程线程,并让远程线程一启动就执行LoadLibrary函数加载我们的myDll.dll,使得我们写的代码得以执行。其效果就是在目标程序启动之前,先弹出了一个对话框,确定了之后才启动目标程序。某些汉化补丁或打包程序的LOGO界面就是用类似的方法实现的。此时我们的DLL就注入到了目标程序的进程中,可以访问目标进程中的任意内存,这样进行API的挂接和监视修改目标进程中内存的工作也变得非常简单了,可以用于制作外挂程序、插件、或者游戏的修改器等等,这些属于以后讨论的内容,这篇文章暂不涉及。

                                                                  ScienWu

                                                                 2006-11-22

 

 

附:主要文件源程序代码

DLL工程myDll

头文件myDll.cpp

//   myDll.cpp开头

#include "stdafx.h"

 

 

#ifdef _MANAGED

#pragma managed(push, off)

#endif

void myTestFun();

BOOL APIENTRY DllMain( HMODULE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                        )

{

     switch (ul_reason_for_call)

     {

     case DLL_PROCESS_ATTACH:

         myTestFun();

         break;

     case DLL_THREAD_ATTACH:

         break;

     case DLL_THREAD_DETACH:

         break;

     case DLL_PROCESS_DETACH:

         break;

     }

    return TRUE;

}

 

#ifdef _MANAGED

#pragma managed(pop)

#endif

 

void myTestFun()

{

     MessageBoxEx(NULL,_TEXT("测试文本"),_TEXT("测试标题"),MB_OK,0);

}

//   myDll.cpp结尾

 

 

 

Exe工程DllLoader.exe

//   DllLoader.cpp开头

#include "stdafx.h"

 

int _tmain(int argc, _TCHAR* argv[])

{   

     //   我们自己的DLL文件的文件名,如果不在同一目录下,需要指定完成路径

     TCHAR * pcFileName = _TEXT("myDll.dll");

     //   计算文件名字符串的长度

     int iSize = ( _tcslen(pcFileName) + 1 ) * sizeof(TCHAR);

     //   要写入目标进程的字符串参数的地址

     TCHAR * pcLibFileRemote = NULL;

     //   目标程序的文件名

     TCHAR * pcTargetFileName=_TEXT("ollyDbgTest.exe");

 

     //   创建进程所需的进程信息结构体

     PROCESS_INFORMATION ProcessInfo;

     //   创建进程所需的启动信息结构体

     STARTUPINFO StartupInfo;

     ZeroMemory(&StartupInfo, sizeof(StartupInfo)); //   初始化工作

     StartupInfo.cb = sizeof StartupInfo;           //   计算大小

 

     //   开始为目标程序建立进程

     if(CreateProcess(pcTargetFileName, NULL,NULL,NULL,FALSE,0,NULL,NULL,&StartupInfo,&ProcessInfo))

     {

         //   打开进程,并获得所有权限

         HANDLE handleTarget=OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessInfo.dwProcessId);

         //   在目标进程中分配iSize大小的空间以便将我们的DLL的文件名作为参数写入目标进程

         pcLibFileRemote=(TCHAR*)VirtualAllocEx(handleTarget,NULL,iSize,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);

         //   分配成功

         if(NULL != pcLibFileRemote)

         {

              //   把我们的DLL文件名写入目标进程的内存中

              if(WriteProcessMemory(handleTarget,pcLibFileRemote,(LPVOID)pcFileName,iSize,NULL))

              {

                   //   我们要在内存中执行的LoadLibrary函数所在的系统DLL

                   HMODULE hmoduleKernel32 = LoadLibrary(_TEXT("Kernel32"));

                   //   获取Kernel32.dll中的Loadlibrary函数的地址

                   #ifdef  UNICODE        //   为了适应UNICODEANSI两种编码方式

                       PTHREAD_START_ROUTINE pStart = (PTHREAD_START_ROUTINE)GetProcAddress(hmoduleKernel32,"LoadLibraryW");

                   #else

                       PTHREAD_START_ROUTINE pStart = (PTHREAD_START_ROUTINE)GetProcAddress(hmoduleKernel32,"LoadLibraryA");

                   #endif

 

                   /*   建立远程线程进行注入。这里多说几句CreateRemoteThread函数是在我们的引导程序DllLoader.exe中执行的,如果lpParamter参数直接传递字符串的话这个字符串的内存是在DllLoader.exe中的,目标进程是无法访问这个地址的,这就是之所以前面很麻烦的把我们的DLL的文件名用WriteProcessMemory写入到目标进程的内存中的原因,只有这样目标进程才能正确的得到文件名参数。 */

                   CreateRemoteThread(handleTarget,NULL,0,pStart,pcLibFileRemote,0,NULL);

              }

         }

     }

     return 0;

}

//   DllLoader.cpp结尾

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值