【Windows编程】Dll的注入与调用

去年的时候想做个脚本,开始是用python,但是有些实现想用Hook,然而自己本来python就是半桶水,而C++的window编程更是一窍不通,然而网上的教程又是零零散散
好吧,其实就是我没基础,大佬:接下来就不用我说了吧(超需要啊~~~)

注入需要准备一个dll,与配套的exe程序(当然,hook那么多种形式,我只是碰巧学了这么个半通不通的)
所以,在这个Demo里,有2个Dll:

  • Add.dll(假设的要hook的函数所在dll)
  • HookDll.dll(包含要注入用的函数所在dll)
  • 2个exe项目:One.exe(假设要Hook的程序)
  • Hook.exe(hook用的程序)

导包跟头文件我会发出来,但是函数修改的话,只会在修改部分发出

Add.dll

在本案例中,假设要hook程序中Add.dll中的ExportFunc(LPCTSTR pszContent)函数

这个dll只有一个ExportFunc函数,主体One.exe会通过该module函数弹出提示窗

add.h

#pragma once
#include <Windows.h>

 __declspec(dllexport) void ExportFunc(LPCTSTR pszContent);

dllmain.cpp

HMODULE g_hModule;
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:			    // 这里表示在dll加载的时候进行的操作
		g_hModule = (HMODULE)hModule;		//获取谁加载本dll,这样ExportFunc就能在对应的窗口弹出消息了
		break;
    }
    return TRUE;
}
//pszContent:要弹出的提示内容
void ExportFunc(LPCTSTR pszContent) {
	char sz[MAX_PATH];
	GetModuleFileNameA(g_hModule, sz, MAX_PATH);
	MessageBoxA(NULL, pszContent, strrchr(sz, '\\') + 1, MB_OK);
}

One.exe

在本案例中,One.exe就是要被hook的程序了

One在点击界面的时候,会调用Add.dll的ExportFunc,从而弹出窗口

One.h

#pragma once
#include "resource.h"
#include<windows.h>
#include<atlstr.h>
#include <TlHelp32.h>

HMODULE hModule;								//存放加载Add.dll
typedef void (*PFNEXPORTFUNC) (LPCTSTR);		//声明类型(?c++真心搞不懂)
PFNEXPORTFUNC mef;								//存放加载ExportFunc

One.cpp

#include "framework.h"
#include "One.h"
#include <string>
using namespace std;

void OnOk();

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_LBUTTONDOWN:
		OnOk();			//在点击界面的时候触发该函数
		break;
    return 0;
}

//这个是点击响应函数
void OnOk() {
	/*
	mef是ExportFunc的函数参数,加载过dll后,读取到的函数都会有个句柄(?应该是这么叫吧)
	这个名称不重要,之所以是PFNEXPORTFUNC是因为我们这个ExportFunc返回值是void
	大概就是这么理解吧=3=
	
	这里简单的说就是,如果ExportFunc还没加载
		就去看看Add.dll加载了没,没有就去加载
		有就从Add.dll读取ExportFunc
	然后就去调用这个ExportFunc然后弹出个success
	当然,这些根本不关我们事,毕竟One.exe模拟的是被我们hook的程序,又不是我们自己的程序=3=
	*/
	if (mef == NULL) {
		if (hModule == NULL) {
			hModule = ::LoadLibrary("D:/workspace/c/yysv3/Add/Debug/Add.dll");
		}
		if (hModule != NULL) {
			mef = (PFNEXPORTFUNC)::GetProcAddress(hModule, "ExportFunc");
		}
		else {
			MessageBox(NULL, "加载ADD失败", "", NULL);
		}

	}

	if (mef != NULL) {
		mef("success");
	}
}

效果大概就是这样
在这里插入图片描述

上面的在实际的Hook中就是现成的程序了,我们只需要了解我们要Hook什么dll中的什么函数,这些就不是我这没入门的能理解的范畴了
下面这就是Hook的重头戏了~~

HookDll.dll

HookDll.dll就是我们要用来注入的dll了
在这里,我们也定义了一个void ExportFunc(LPCTSTR pszContent);这里这个函数名称不重要,重要的是,参数必须跟我们要hook的函数参数保持一致(个人理解,,)

说一下个人对于Hook的理解(markdown绘制的,将就看看吧=3=)

2.想去找
6.所以真正找到的是
4.而它hook了A
5.A入口变成了B
7.重新去找到函数A
7.或直接不执行A<一般来说不选择这种>
8.A傻傻执行执行
1.X逻辑调用了A
函数A
函数B
3.HookDll
6.执行Hook的代码完了
9.X逻辑继续进行

所以,我们需要重新写一个函数B,用B去替换原本的正主A,这时候函数的参数要跟原来的保持一致,因为其他的代码并不知道这个A已经被掉包了,所以还是带着那么些参数过来了(这里函数名无所谓是因为,函数名只是一个指代一个入口,所以叫啥最后指向的入口对了就OK了)

最开始是想着怎么注入dll,到注入后一直在找资料说怎么调用怎么调用。后来才大概明白俩点:

  • 我们写的Hook.exe的注入操作,只是为了把HookDll.dll送到目标程序里面,其他的跳转什么的,全靠HookDll.dll自己去行动了
  • Dll在被加载的时候,会触发一个操作,就是默认的DllMain::DLL_PROCESS_ATTACH,也就是说在注入的时候,我们就该把所有事情安排好了(也就是函数B入口替换A的操作要完成),完了之后,只要逻辑X过来,它走的就是函数B(也就是我们的逻辑了),这时候就是目标程序主动来调用我们的函数了,而不用我们去控制了

简单说,就像一个特工要潜入目标,Hook.exe就是带路党把他带到目标营地里,而至于要搞破坏还是投资料,全都得这位特工去完成了,全凭他当前的得到的命令去做事情了。这时候,虽然说不是联系不上他,但是要重新给他发布指令也就是说外部指挥部去传递信息进去,肯定是相当麻烦且危险的事情了

dllmain.cpp

#include "pch.h"
HMODULE g_hModule;
HANDLE cur;
PROC OldProc;
unsigned long* lpAddr;
bool m_bInjected = false;
bool bHook;
DWORD dwPid;

//这里是用来保存我们hook的函数原地址(原函数)以及新地址(我们的函数)的
BYTE NewCode[5];
BYTE OldCode[5];

void ExportFunc(LPCTSTR pszContent);
typedef void( *ExportProc)(LPCSTR pszContent);
ExportProc proc;
//这里除了上面定义的用来加载HookDll中的ExportFunc外,还需要一个FARPROC,个人理解是用来记录远程的ExporProc
FARPROC fproc;

//hook操作函数
void HookOn();
void HookOff();
void Inject();
void printf(const char* msg, ...);


BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    //这里是加载了dll后就会执行的
    case DLL_PROCESS_ATTACH:
    	//拿到当前的进程号
		dwPid = ::GetCurrentProcessId();
		cur = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
		printf("Hook 注入");
		g_hModule = GetModuleHandle("Add.dll");
		//所以我们需要在这里偷天换日
		Inject();
		break;
    case DLL_PROCESS_DETACH:	//这个是dll卸载前会执行的
   		HookOff();				//所以在这里就要回复原状了(重要)
		MessageBoxA(NULL, "Hook 卸载", "", MB_OK);
        break;
    }
    return TRUE;
}

/**
 * dll注入之后通过dll的加载机制,调用了该函数
 * 首先,是先进行判断我们要hook的目标add.dll是不是加载了,避免搞错目标
 * 确认了dll的位置后,就通过dll去找到我们真正要替换的目标ExportFunc
 * 然后就将我们拿到的正主A转成FPROC(这个我也不大懂它的意义,不过能通过它去确定A的位置就是了)
 * 然后就把目标的地址跟自身地址记录好
 * 简单说,我们的特工潜入后,首先是去找找这里是不是有XX房间(避免潜入错营地的尴尬)
 * 找房间后又去房间里确认A是不是有来工作(dll加载后,函数不一定有加载调用)
 * 确认有后,就偷偷记录A跟自己的铭牌了
 */
void Inject()
{
	if (m_bInjected == false)
	{
		//保证只调用1次
		m_bInjected = true;

		//获取add.dll中的add()函数
		HMODULE hmod = GetModuleHandle("Add.dll");
		if (hmod != NULL) {
			proc = (ExportProc)::GetProcAddress(hmod, "ExportFunc");
			//procEndScene = (EndSceneProc)::GetProcAddress(hmod, "EndScene");
		}
		else {
			printf("fail to found add.dll");
		}
		fproc = (FARPROC)proc;
		if (fproc == NULL)
		{
			printf("fail to load farproc");
		}

		// 将add()中的入口代码保存入OldCode[]
		_asm
		{
			lea edi, OldCode
			mov esi, fproc
			cld
			movsd
			movsb
		}

		NewCode[0] = 0xe9;//实际上0xe9就相当于jmp指令
		//获取Myadd()的相对地址
		_asm
		{
			lea eax, ExportFunc
			mov ebx, fproc
			sub eax, ebx
			sub eax, 5
			mov dword ptr[NewCode + 1], eax
		}
		//填充完毕,现在NewCode[]里的指令相当于Jmp Myadd
		HookOn(); //可以开启钩子了
	}
}

/**
 * HookOn跟HookOff类似
 * 就是修改程序的内存,然后变更函数的入口了
 * 这一步相当于我们的特工B潜入目标阵营后确认A的存在后,将自己 铭牌跟A的偷换了
 * 当某人C通过铭牌来找A的时候,B接过信息并顺手转交给了A
 * 由于信息格式是符合规定的,A也没怀疑就做好处理并返回给了C
 * 这时候原本天知地知,AC知的事,就被B知道甚至可能偷偷做了一点手脚了
 */
void HookOn()
{
	DWORD dwTemp = 0;
	DWORD dwOldProtect;

	//将内存保护模式改为可写,老模式保存入dwOldProtect
	VirtualProtectEx(cur, fproc, 5, PAGE_READWRITE, &dwOldProtect);
	//将所属进程中add()的前5个字节改为Jmp Myadd
	WriteProcessMemory(cur, fproc, NewCode, 5, 0);
	//将内存保护模式改回为dwOldProtect
	VirtualProtectEx(cur, fproc, 5, dwOldProtect, &dwTemp);

	bHook = true;
}

//将所属进程中add()的入口代码恢复
void HookOff()
{
	DWORD dwTemp = 0;
	DWORD dwOldProtect;

	VirtualProtectEx(cur, fproc, 5, PAGE_READWRITE, &dwOldProtect);
	WriteProcessMemory(cur, fproc, OldCode, 5, 0);
	VirtualProtectEx(cur, fproc, 5, dwOldProtect, &dwTemp);
	bHook = false;
}

/**
 * 这个函数就是我们自己的函数B
 * 因为我们Inject后就HookOn()在监听着进入的函数了
 * 也就是在逻辑走到原程序的ExportFunc就会进入到这里了
 * 所以执行完我们自己的逻辑就该将入口恢复,然后调用真正的A函数
 * 而A函数运行完会返回什么就返回什么(这里是void自然就是运行完就结束了)
 * 然后重新修改A的入口到我们的B上面
 */
void ExportFunc(LPCTSTR pszContent) {
	printf("Hook!!!!");
	HookOff();
	proc(pszContent);
	HookOn();
}

//封装的弹框函数,没啥特别重点的
void printf(const char* msg, ...)
{
	va_list args;
	const char* params;
	va_start(args, msg);
	params = va_arg(args, const char*);
	va_end(args);

	TCHAR str[MAX_PATH];
	wsprintf(str, msg, params);
	MessageBox(NULL, str, "", NULL);
}

Hook.exe

Hook.exe就是我们用来注入跟卸载HookDll.dll用的,这里主要是的功能就是点击时,如果One.exe存在Add.dll就去判断HooDll.dll是否注入了,是就卸载,否则注入

这一步内容可能会稍微多一些,主要是多了许多的步骤判断,最开始是因为为了确认错误所在,后来也就没修改了,核心内容倒是不多,跟网上的其他说明就大同小异了

  • 判断进程中是否存在目标
  • 是的话获取进程操作句柄
  • 然后开辟内存(这一步可能会失败,具体自己网上找解决)
  • 将我们的dll路径赋值进去(到这里其实就是相当于给我们的特工在目标营地创建了一个身份档案)
  • 然后去找LoadLibrary(这里可理解为这个世界的所有营地人事都是同一家人力公司派遣的,他们只认身份档案不认人的,然后人事部就派车出来接特工进营地了)

Hook.cpp

#include "framework.h"
#include "Hook.h"

BOOL CheckDllInProcess(DWORD dwPID, LPCTSTR szDllPath);		//检查指定dll是否存在目标进程
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath);
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath);
void d_printf(const char* msg, ...);
bool InitalizeInject();


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_LBUTTONDOWN:		//点击监听
	
		if (CheckDllInProcess(dwPID, orignDllName))
		{
			if (CheckDllInProcess(dwPID, dllName))
			{
				//已经注入就卸载
				EjectDll(dwPID, dllName);
			}
			else
			{
				//还没注入就加载
				InjectDll(dwPID, dllPath);
			}
		}
		else 
		{
			d_printf("不存在");
		}
		break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

/**
 * 检测进程中是否包含对应的Dll
 * @param dwPID		进程ID
 * @param szDllPath	所要注入Dll名称
 *
 * 创建内存快照
 * 遍历比较
 */
BOOL CheckDllInProcess(DWORD dwPID, LPCTSTR szDllPath) 
{
	BOOL bMore = FALSE;
	HANDLE hSnapshot = INVALID_HANDLE_VALUE;
	MODULEENTRY32 me = { sizeof(me), };

	if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))
	{
		DWORD err = GetLastError();
		d_printf("CheckDllInProcess():CreateToolHelp32Snapshot(%d)failed!!![%d]\n", dwPID, err);
		return FALSE;
	}

	bMore = Module32First(hSnapshot, &me);
	FILE* out;
	errno_t err;
	err = fopen_s(&out, "D:\\a.txt", "a");
	for (; bMore; bMore = Module32Next(hSnapshot, &me))
	{
		if (out != NULL)
		{
			fprintf(out, "%s\t", me.szModule);
		}
		if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szModule, szDllPath))
		{
			CloseHandle(hSnapshot);
			if (out != NULL)
			{
				fprintf(out, "\n-------------\n");
				fclose(out);
			}
			return TRUE;
		}
	}
	if (out != NULL)
	{
		fprintf(out, "\n-------------\n");
		fclose(out);
	}
	CloseHandle(hSnapshot);
	//d_printf("找不到%s", szDllPath);
	return FALSE;
}

//向指定的进程注入相应的模块
//dwPID         目标进程的PID
//szDllPath     被注入的dll的完整路径
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
	HANDLE                  hProcess = NULL;//保存目标进程的句柄
	LPVOID                  pRemoteBuf = NULL;//目标进程开辟的内存的起始地址
	DWORD                   dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);//开辟的内存的大小
	LPTHREAD_START_ROUTINE  pThreadProc = NULL;//loadLibreayW函数的起始地址
	HMODULE                 hMod = NULL;//kernel32.dll模块的句柄
	BOOL                    bRet = FALSE;
	HANDLE                  hThread = NULL;
	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))//打开目标进程,获得句柄
	{
		d_printf("InjectDll() : OpenProcess(%d) failed!!! [%d]\n",
			dwPID, GetLastError());
		goto INJECTDLL_EXIT;
	}
	pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
		MEM_COMMIT, PAGE_READWRITE);//在目标进程空间开辟一块内存
	if (pRemoteBuf == NULL)
	{
		d_printf("InjectDll() : VirtualAllocEx() failed!!! [%d]\n",
			GetLastError());
		goto INJECTDLL_EXIT;
	}
	if (!WriteProcessMemory(hProcess, pRemoteBuf,
		(LPVOID)szDllPath, dwBufSize, NULL))//向开辟的内存复制dll的路径
	{
		d_printf("InjectDll() : WriteProcessMemory() failed!!! [%d]\n",
			GetLastError());
		goto INJECTDLL_EXIT;
	}
	hMod = GetModuleHandle("kernel32.dll");//获得本进程kernel32.dll的模块句柄
	if (hMod == NULL)
	{
		d_printf("InjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n",
			GetLastError());
		goto INJECTDLL_EXIT;
	}

	//获得LoadLibrary函数的起始地址,这个要区分对方程序是否是宽字符的
	if (isUnicode) 
	{
		pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");
	}
	else
	{
		pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
	}
	
	if (pThreadProc == NULL)
	{
		d_printf("InjectDll() : GetProcAddress(\"LoadLibraryW\") failed!!! [%d]\n",
			GetLastError());
		goto INJECTDLL_EXIT;
	}
	hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
	if (!hThread)//执行远程线程
	{
		d_printf("InjectDll() : MyCreateRemoteThread() failed!!!\n");
		goto INJECTDLL_EXIT;
	}
INJECTDLL_EXIT:
	if (hThread != NULL) 
	{
		WaitForSingleObject(hThread, INFINITE);
	}
	bRet = CheckDllInProcess(dwPID, dllName);				//确认结果
	if (pRemoteBuf)
		VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
	if (hProcess)
		CloseHandle(hProcess);
	return bRet;
}

//让指定的进程卸载相应的模块
//dwPID         目标进程的PID
//szDllPath     被注入的dll的完整路径,注意:路径不要用“/”来代替“\\”
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
	BOOL                    bMore = FALSE, bFound = FALSE, bRet = FALSE;
	HANDLE                  hSnapshot = INVALID_HANDLE_VALUE;
	HANDLE                  hProcess = NULL;
	MODULEENTRY32           me = { sizeof(me), };
	LPTHREAD_START_ROUTINE  pThreadProc = NULL;
	HMODULE                 hMod = NULL;
	TCHAR                   szProcName[MAX_PATH] = { 0, };
	if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))
	{
		d_printf("EjectDll() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n",
			dwPID, GetLastError());
		goto EJECTDLL_EXIT;
	}
	bMore = Module32First(hSnapshot, &me);
	for (; bMore; bMore = Module32Next(hSnapshot, &me))//查找模块句柄
	{
		if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath))
		{
			bFound = TRUE;
			break;
		}
	}
	if (!bFound)
	{
		d_printf("EjectDll() : There is not %s module in process(%d) memory!!!\n", szDllPath, dwPID);
		goto EJECTDLL_EXIT;
	}
	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
	{
		d_printf("EjectDll() : OpenProcess(%d) failed!!! [%d]\n",
			dwPID, GetLastError());
		goto EJECTDLL_EXIT;
	}
	hMod = GetModuleHandle("kernel32.dll");
	if (hMod == NULL)
	{
		d_printf("EjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n",
			GetLastError());
		goto EJECTDLL_EXIT;
	}
	pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "FreeLibrary");
	if (pThreadProc == NULL)
	{
		d_printf("EjectDll() : GetProcAddress(\"FreeLibrary\") failed!!! [%d]\n",
			GetLastError());
		goto EJECTDLL_EXIT;
	}
	if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL))
	{
		d_printf("EjectDll() : MyCreateRemoteThread() failed!!!\n");
		goto EJECTDLL_EXIT;
	}
	bRet = TRUE;
EJECTDLL_EXIT:
	if (hProcess)
		CloseHandle(hProcess);
	if (hSnapshot != NULL && hSnapshot != INVALID_HANDLE_VALUE)
		CloseHandle(hSnapshot);
	return bRet;
}

void d_printf(const char* msg,...)
{
	va_list args;
	const char* params;
	va_start(args, msg);
	params = va_arg(args, const char*);
	va_end(args);

	TCHAR str[MAX_PATH];
	wsprintf(str, msg,params);
	MessageBox(NULL, str, "", NULL);
}

/**
 * 初始化注入操作
 *
 *
 * 判断是否取得窗口句柄hHwnd
 *		获取窗口句柄
 *		获取线程号、进程号
 *		判断是否多字符类型:A/W 
 * 
 * 所要注入dll完整路径,以及对应的dll名称
 * 
 * 验证初始化内容是否完成,否则返回失败
 */
bool InitalizeInject() 
{
	if (hHwnd == NULL) {
		hHwnd = FindWindow("One", NULL);
		dwTID = GetWindowThreadProcessId(hHwnd, &dwPID);
		isUnicode = IsWindowUnicode(hHwnd);
	}

	if (dllPath == NULL) {
		dllPath = "D:\\workspace\\c\\yysv3\\HookDll\\Debug\\HookDll.dll";	//这是要Inject的dll的绝对路径
		dllName = "HookDll.dll";											//这是要Inject的dll名称
		orignDllName = "Add.dll";											//这是要hook的dll名称
	}
	if (hHwnd == NULL) 
	{
		d_printf("Get HWND faild!");
		return false;
	}
	if (dwTID == NULL)
	{
		d_printf("Get TID&PID faild!");
		return false;
	}
	if (dllPath == NULL)
	{
		d_printf("Get DLL faild!");
		return false;
	}
	return TRUE;
}

最终效果:
在这里插入图片描述
可以看到,One的弹框变化(虽然没写好弹框定位,但是通过任务栏确实能确定弹框的归属)

远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理器是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值