Dll注入学习笔记

    实现DLL注入的思路是让进程运行LoadLibrary函数,把我们的DLL装载进去,怎样让进程调用LoadLibrary函数呢?首先想到的办法就是在目标进程中创建一个新的线程来调用。比如:

HANDLE WINAPI CreateRemoteThread(
  __in          HANDLE hProcess,                          //线程被创建的进程句柄
  __in          LPSECURITY_ATTRIBUTES lpThreadAttributes, //可设置为0,表示取默认值
  __in          SIZE_T dwStackSize,                       //可设置为0,表示取默认值
  __in          LPTHREAD_START_ROUTINE lpStartAddress,    //线程函数指针,必须存在于远程进程地址空间中
  __in          LPVOID lpParameter,                       //传给线程函数的参数
  __in          DWORD dwCreationFlags,                    //可设置为0,表示线程创建后立刻执行
  __out         LPDWORD lpThreadId                        //返回线程ID,可设置为0,表示不返回
);


通过函数的参数可以看出,我们需要得到三个参数来创建线程以达到注入DLL的目的。下面一一介绍。


一.hProcess进程的句柄

   要获得进程的句柄,有几种方法可以选择
   1) 使用FindWindow获得窗口句柄,接着调用GetWindowThreadProcessId来获得进程ID,最后调用OpenProcess来获得进程句柄
   2) 使用EnumWindows来枚举所有窗口,并通过GetWindowText来获得窗口标题,从而找到自己需要的窗口句柄,接下来的操作与1)相同。
   3) 使用CreateToolhelp32Snapshot获得进程快照,使用Process32First和Process32Next来检索每个具体的进程,并获得我们需要的进程ID,最后调用OpenProcess来获   得进程句柄。
   上述几个函数的功能和参数都很简单,可以参考MSDN说明。我们采用的是第3中方式,因为有些程序会屏蔽掉前两种方法中的API。获得进程句柄的代码如下:

   
if(!(ProcessID=GetGameProcessName("notepad.exe")))
{
	cout<<"进程没用启动"<<endl;
	return 0;
}
	
if(!(ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessID)))
{
	cout<<"打开进程失败"<<endl;
	return 0;
}
int GetGameProcessName(char *GameName)
{
	PROCESSENTRY32 stProcess;
	HANDLE hProcessShot;

	hProcessShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

	stProcess.dwSize=sizeof(PROCESSENTRY32);

	Process32First(hProcessShot,&stProcess);
	do 
	{
		if (!strcmp(GameName,stProcess.szExeFile))
		{
			return stProcess.th32ProcessID;
		}
	} while (Process32Next(hProcessShot,&stProcess));

	CloseHandle(hProcessShot);
	return FALSE;

}

二、线程函数

    这里线程函数我们使用LoadLibrary函数,但是这里有个问题,我们调用LoadLibrary时,并不是直接调用kernel32.dll中的LoadLibry函数,而要经过一个跳转。这样我们调用的LoadLibrary函数在不同的进程中地址空间可能不一样,因为不能直接把LoadLibrary函数地址传递给CreateRometeThread函数,但是可以把kernel32.dll中的LoadLibrary函数地址传递给CreateRometeThread函数,因为几乎所有进程都会加载kernel32.dll,而且会映射到同一地址。
    
    但是这里又有新的问题,怎样才能获得kernel32.dll中的函数呢?很容易想到GetProcAddress函数,我们看关于这个函数的说明:

    FARPROC GetProcAddress( 
  HMODULE hModule,    //DLL模块句柄
  LPCWSTR lpProcName  //函数名称
    ); 
    第一个参数需要我们获得模块句柄,这里肯定不能用LoadLibrary来获得了,但是还有一个函数可以获得GetModuleHandle。DLL名称作为参数传递给他即可。
    第二个参数不能使用“LoadLibrary”,而应该使用“LoadLibraryA”(ANSI类型)或者“LoadLibraryW”(unicode类型)。

     代码如下:

hKernelModule=GetModuleHandle("kernel32.dll");

LPTHREAD_START_ROUTINE start_routine=(LPTHREAD_START_ROUTINE)GetProcAddress(hKernelModule,"LoadLibraryA");
    

三、线程参数

    LoadLibrary函数的参数是Dll的名字,Dll的名字是字符串,它也必须存在于远程进程的地址空间中,所以我们不能直接使用字符串作为参数传递给线程,这里可以使用函数WriteProcessMemory来通过向指定的进程写内存将Dll的路径名字写进远程进程的地址空间中,我们看一看这个函数的参数:
  BOOL WriteProcessMemory( 
       HANDLE hProcess,               //进程句柄,由OpenProcess获得
       LPVOID lpBaseAddress,          //指定进程的内存起始地址
       LPVOID lpBuffer,               //提供数据的缓冲区指针,它将被写入指定进程的地址空间中
       DWORD nSize,                   //写入的字节数
       LPDWORD lpNumberOfBytesWritten //输出参数,指向实际输出的字节数,这里可设置为NULL,表示忽略
    );
    通过函数的参数我们可以看到,这里还有一个问题,我们怎么获得指定进程的起始地址呢?这里肯定不能用new或者malloc,因为他们分配的是当前进程的内存,这就需要另外一个函数VirtualAllocEx,该函数在指定进程的虚拟地址空间中提交或保留一个区域的内存,对于的函数为VirtualFreeEx释放回收指定进程的地址空间。代码如下:

addrStart=VirtualAllocEx(ProcessHandle,0,1024,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if (!addrStart)
{
	cout<<"申请内存失败"<<endl;
	return 0;
}
WriteProcessMemory(ProcessHandle,addrStart,dll_name,1024,0);


    

四、函数返回

    以上几个参数获得成功之后,就可以通过CreateRemoteThread函数实现DLL注入的目的了,但是远程线程什么时候结束呢?即LoadLibrary函数什么时候返回呢?我们可能需要等到LoadLibrary返回之后才能进行下一步操作。这里可以使用WaitForSingleObject来检测信号对象的情况
    DWORD WaitForSingleObject(   
        HANDLE hHandle,       //对象句柄,这里为CreateRemoteThread的返回值
        DWORD dwMilliseconds  //等待时间,单位ms,设置为INFINITE表示一直等待
    ); 

五、注入总结

以上几个函数就是实现Dll注入需要的API函数,下面进行一下总结,按函数执行的流程进行说明,并直接设置好需要的参数:
1.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)获得进程快照


2.Process32First(hProcessShot,&stProcess)、Process32Next(hProcessShot,&stProcess)查找获取我们需要进程的ID


3.ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessID)获得进程句柄
4.addrStart=VirtualAllocEx(ProcessHandle,0,1024,MEM_COMMIT,PAGE_EXECUTE_READWRITE)申请指定进程的内存空间
5.WriteProcessMemory(ProcessHandle,addrStart,dll_name,1024,0)向指定空间中写入dll名字
6.hModule=GetModuleHandle("kernel32.dll")获得模块的模块句柄
7.start_routine=(LPTHREAD_START_ROUTINE)GetProcAddress(hModule,"LoadLibraryA")获得模块中LoadLibrary函数地址
8.ThreadHandle=CreateRemoteThread(ProcessHandle,0,0,start_routine,addrStart,0,0)创建远程线程
9.WaitForSingleObject(ThreadHandle,INFINITE)等待线程结束
10.VirtualFreeEx(ProcessHandle,addrStart,0,MEM_RELEASE)释放进程地址空间
11.CloseHandle(ProcessHandle);关闭句柄


六、DLL卸载

我们将Dll成功注入到远程进程中了,现在来说说卸载的问题。
关于Dll的卸载需要函数Freelibrary,现在存在两个问题:
1.FreeLibrary需要以Dll的模块句柄作为参数,我们怎么获得Dll的模块句柄?
2.FreeLibrary不支持卸载本程序以外的Dll,该怎么办呢?
关于第二个问题很好解决,像LoadLibrary那样将FreeLibrary也注入到指定进程中就可以了。
现在考虑第一个问题,怎么获得Dll的模块句柄呢?我们还记得在注入Dll时使用GetModuleHandle来获得kernel32.dll的模块句柄,很容易想到这里依然使用GetModuleHandle函数,但是我们程序中并没有加载我们的DLL,不能直接调用GetModuleHandle来获得模块句柄,那怎么办?同样的将GetModuleHandle注入到指定进程中,但是怎么获得GetModuleHandle的返回值,也就是怎么获得DLL的模块句柄呢?这里要接受一个新的函数GetExitCodeThread,从指定的线程中获得退出的状态码,该函数的说明如下:

BOOL GetExitCodeThread( 
  HANDLE hThread,      //线程句柄
  LPDWORD lpExitCode   //输出返回值
);

实现源码如下:

// RegeditDllOK.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include "Tlhelp32.h"
#include <iostream>
using namespace std;


DWORD ProcessID;
HANDLE ProcessHandle;
LPVOID addrStart;
HANDLE ThreadHandle;
HANDLE hDll;
HMODULE hKernelModule;
char *dll_name="RegeditDll.dll";

int GetGameProcessName(char *GameName);

void UnRegisterDll()
{
	LPTHREAD_START_ROUTINE start_routine=(LPTHREAD_START_ROUTINE)GetProcAddress(hKernelModule,"GetModuleHandleA");

	ThreadHandle=CreateRemoteThread(ProcessHandle,0,0,start_routine,addrStart,0,0);
	WaitForSingleObject(ThreadHandle,INFINITE);
	GetExitCodeThread(ThreadHandle,(LPDWORD)(&hDll));

	start_routine=(LPTHREAD_START_ROUTINE)GetProcAddress(hKernelModule,"FreeLibrary");

	ThreadHandle=CreateRemoteThread(ProcessHandle,0,0,start_routine,hDll,0,0);
	WaitForSingleObject(ThreadHandle,INFINITE);
}	

void RegisterDll()
{
	LPTHREAD_START_ROUTINE start_routine=(LPTHREAD_START_ROUTINE)GetProcAddress(hKernelModule,"LoadLibraryA");

	ThreadHandle=CreateRemoteThread(ProcessHandle,0,0,start_routine,addrStart,0,0);

	WaitForSingleObject(ThreadHandle,INFINITE);

}

int _tmain(int argc, _TCHAR* argv[])
{
	if(!(ProcessID=GetGameProcessName("notepad.exe")))
	{
		cout<<"进程没用启动"<<endl;
		return 0;
	}
	
	if(!(ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,ProcessID)))
	{
		cout<<"打开进程失败"<<endl;
		return 0;
	}

	addrStart=VirtualAllocEx(ProcessHandle,0,1024,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
	if (!addrStart)
	{
		cout<<"申请内存失败"<<endl;
		return 0;
	}
	WriteProcessMemory(ProcessHandle,addrStart,dll_name,1024,0);


	hKernelModule=GetModuleHandle("kernel32.dll");

	RegisterDll();
	
	UnRegisterDll();

	VirtualFreeEx(ProcessHandle,addrStart,0,MEM_RELEASE);
	CloseHandle(ProcessHandle);
	return 0;
}

int GetGameProcessName(char *GameName)
{
	PROCESSENTRY32 stProcess;
	HANDLE hProcessShot;

	hProcessShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

	stProcess.dwSize=sizeof(PROCESSENTRY32);

	Process32First(hProcessShot,&stProcess);
	do 
	{
		if (!strcmp(GameName,stProcess.szExeFile))
		{
			return stProcess.th32ProcessID;
		}
	} while (Process32Next(hProcessShot,&stProcess));

	CloseHandle(hProcessShot);
	return FALSE;

}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值