windows.h: 2.DLL injection technology

Most of the applications in windows are based on the message mechanism and perform different functions according to different messages. The hook mechanism is provided by the windows operating system and can be used to intercept and monitor these messages in the system.

Hooks can be divided into local hooks and global hooks. The local hook is for a certain thread, and the global hook is a message-based application that acts on the entire system. The global hook needs to use the DLL file, and the corresponding hook function is implemented in the DLL.

1. Global hook injection

First introduce the functions used later

SetWindowsHookEX function

//将程序定义的钩子函数安装到挂钩链中,安装钩子程序可以监视系统是否存在某些类型的事件,这些事件
//和特定线程或调用线程所在的桌面所有线程相关联

HHOOK WINAPI SetWindowsHookEX(
    _In_ int idHook, //要安装的钩子程序的类型
    _In_ HOOKPROC lpfn, //一个指向钩子程序的指针,如果dwThreadId=0或者指定不同进程创建线程,则
                        //lpfn必须指向DLL中的钩子过程,否则lpfn可以指向与当前进程关联的代码函数
    _In_ HINSANCE hMod, //包含由lpfn参数指向的函数的DLL句柄,如果是当前进程则hMod必须为NULL
    _In_ DWORD dwThreadId) //与钩子程序关联的线程标识符。如果为0,则与所有系统中的线程相关联

//函数成功返回钩子函数的句柄
//失败返回NULL

The hook types (the first parameter idhook) are divided into the following types:

Hook type

The code of the global hook function needs to be in a separate DLL, so that the DLL can be loaded when a process is executed, and the hook function in it can be run. The process of loading a DLL into a specific process is DLL injection

In order to allow the DLL to be injected into all processes, set WH_GETMESSAGE to monitor the message queue. The windows system is based on message-driven, so all processes have their own message queue, which will load the global hook DLL of type WH_GETMESSAGE

The code to set the global hook:

//设置全局钩子
BOOL SetGlobalHook() {
	g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
	if (NULL == g_hHook) {
		return FALSE;
	}
	return TRUE;
}

Callback function code

//回调函数代码
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
	return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

The data types of the parameters and return values ​​of the callback function are fixed. callnexthookex means passing the current hook to the next hook in the hook chain.

Uninstall hook code:

//卸载钩子
BOOL UnsetGlobalHook() {
	if (g_hHook) {
		UnhookWindowsHookEx(g_hHook);
	}
	return TRUE;
}

The method used in this program is to create a shared memory in the DLL to be shared by multiple processes. The specific implementation method is as follows

//共享内存
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker,"/SECTION:mydata,RWS")

Use data_seg() to create a data segment, and then set the RWS parameter of the connector to a readable, writable, and shareable shared data segment.

achieve:

1. Create a DLL project

In the new project, select the windows desktop project wizard, select the dynamic link library dll

Define the dll entry function as follows

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "mydll.h"

HMODULE g_hDllModule = NULL;


BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	{
		g_hDllModule = hModule;
		break;
	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

After clicking Generate, generate the dll file in the debug directory

 DLL can be loaded and called in other programs

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

#include "stdafx.h"
#include <Windows.h>


int _tmain(int argc, _TCHAR* argv[])
{
	typedef BOOL(*typedef_SetGlobalHook)();
	typedef BOOL(*typedef_UnsetGlobalHook)();
	HMODULE hDll = NULL;
	typedef_SetGlobalHook SetGlobalHook = NULL;
	typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
	BOOL bRet = FALSE;

	do
	{
		hDll = ::LoadLibrary("GlobalHook_Test.dll");
		if (NULL == hDll)
		{
			printf("LoadLibrary Error[%d]\n", ::GetLastError());
			break;
		}
		SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetGlobalHook");
		if (NULL == SetGlobalHook)
		{
			printf("GetProcAddress Error[%d]\n", ::GetLastError());
			break;
		}
		bRet = SetGlobalHook();
		if (bRet)
		{
			printf("SetGlobalHook OK.\n");
		}
		else
		{
			printf("SetGlobalHook ERROR.\n");
		}

		system("pause");

		UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetGlobalHook");
		if (NULL == UnsetGlobalHook)
		{
			printf("GetProcAddress Error[%d]\n", ::GetLastError());
			break;
		}
		UnsetGlobalHook();
		printf("UnsetGlobalHook OK.\n");

	}while(FALSE);

	system("pause");
	return 0;
}

After the loading is completed, when other exe files are executed, the process viewer is used to find that the previously generated dll file is loaded in the loading module, indicating that the dll injection is successful.

After continuing to execute the uninstall hook, it is found that the corresponding dll has not been loaded, indicating that the hook has been successfully uninstalled:

 

2. Far thread injection

The so-called far thread injection refers to a process in which a process creates a thread in another process.

Function introduction:

OpenProcess function

//打开现有的本地进程对象
HANDLE WINAPI OpenProcess(
    _In_ DWORD dwDesiredAccess, //访问进程对象
    _In_ BOOL bInheritHandle,    //是否继承该进程的句柄
    _In_ DWORD dwProcessId)        //要打开的本地进程的PID

VirtualAllocEx function

//在指定进程的虚拟地址空间内保留,提交或更改内存的状态
LPVOID WINAPI VirtualAllocEx(
    _In_ HANDLE hProcess,  //过程句柄
    _In_opt_ LPVOID lpAddress,  //指定要分配页面所需的起始地址的指针
    _In_ SIZE_T dwSize,  //要分配的内存大小,以字节为单位
    _In_ DWORD flAllocationType,  //内存分配类型
    _In_ DWORD flProtect)  //要分配的页面区域的内存保护

//如果成功,返回分配页面的基址
//失败返回NULL

flAllocationType can take the following values:

MEM_COMMIT: allocate physical storage in memory or disk page file for a specific page area

MEM_PHYSICAL: Allocate physical memory (only used for address window expansion memory )

MEM_RESERVE: Reserve the virtual address space of the process without allocating any physical storage. Reserved pages can be occupied by continuing to call VirtualAlloc()

MEM_RESET: indicates that the data specified by the parameters lpAddress and dwSize in the memory is invalid

MEM_TOP_DOWN: allocate memory at the highest possible address (Windows 98 ignores this flag)

MEM_WRITE_WATCH: Must be specified together with MEM_RESERVE to enable the system to track the pages written to the allocated area (only for Windows 98)

flProtect can take the following values:

PAGE_READONLY: This area is read-only. If the application tries to access the page in the area, it will be denied access

PAGE_READWRITE area can be read and written by the application

PAGE_EXECUTE: The area contains code that can be executed by the system. Attempts to read or write to this area will be rejected.

PAGE_EXECUTE_READ: The area contains executable code, and the application can read this area.

PAGE_EXECUTE_READWRITE: The area contains executable code, and the application can read and write this area.

PAGE_GUARD: Enter a STATUS_GUARD_PAGE exception when the area is accessed for the first time. This flag should be combined with other protection flags to indicate the permission of the area to be accessed for the first time

PAGE_NOACCESS: Any access to this area will be denied

PAGE_NOCACHE: When the pages in RAM are mapped to this area, they will not be cached by the microprocessor (cached)

WriteProcessMemory function

//在指定进程中将数据写入内存区域,整个区域必须可访问
BOOL WINAPI WriteProcessMemory(
    _In_ HANDLE hProcess,  //要修改的进程内存的句柄
    _In_ LPVOID lpBaseAddress,  //指向指定进程中写入数据的基地址指针
    _In_ LPCVOID lpBuffer,  //指向缓冲区的指针,包含要写入指定进程地址空间的数据
    _In_ SIZE_T nSize,
    _In_ SIZE_T *lpNumberOfBytesWritten) //指向变量的指针
//函数成功返回值不为零
//失败返回值为零
    

CreateRemoteThread function

HANDLE WINAPI CreateRemoteThread(
    _In_ HANDLE hProcess,  //创建线程的进程句柄    
    _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //指向安全属性的指针
    _In_ SIZE_T dwStackSize,  //堆栈的初始大小
    _In_ LPTHREAD_START_ROUTING lpStartAddress,  //指向远程进程中线程的起始地址
    _In_ LPVOID lpParameter,  //传递给线程函数的变量的指针
    _In_ DWORD dwCreationFlags,  //控制线程创建的标志,若为0,表示线程在创建后立即运行
    _Out_ LPDWORD lpThreadId)  //指向接收线程标识符的变量的指针

//返回新线程句柄或者 NULL

Realization principle: The function used by the dynamic dll that the process needs to load itself is LoadLibrary(lpFilename); where the parameter is the path of the dll to be loaded

The createRemoteThread function needs to specify the address of the multi-threaded function and the address of the multi-threaded parameter. Because the loadLibrary function is bound to be executed by the target process, and the parameter is used as the dll to be loaded

Therefore, there is an instruction in the machine instruction, which starts from the entry address of the Loadlibrary function. If this function address is used as the address of the multi-thread to be created, and the dll path is used as the function parameter, you can use loadLibrary to load the corresponding dll Up

But windows uses base address randomization, so the system DLL load base address is different every time you turn on the system, so the LoadLibrary function base address is different, but some system DLLs require the address to be fixed after booting, so the LoadLibrary function address of your own program is different from other functions.

The addresses in the process are the same.

Code:

#include<Windows.h>
#include<iostream>
#include<tchar.h>
using namespace std;
//定义函数showError
void ShowError(const char* content) {
	cout << content << endl;
}
//使用CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjiectDll(DWORD dwProcessId, LPCWSTR pszDllFileName) {
	HANDLE hProcess = NULL;
	DWORD dwSize = 0;
	LPVOID pDllAddr = NULL;
	FARPROC pFuncProcAddr = NULL;
	//打开注入进程,获取进程句柄
	hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
	if (NULL == hProcess) {
		ShowError("OpenProcess");
		return FALSE;
	}
	//再注入进程中申请内存
	dwSize = 1 + ::lstrlen(pszDllFileName);
	pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
	if (NULL == pDllAddr) {
		ShowError("VirtualAllocEX");
		return FALSE;
	}
	//向申请的内存中写入数据
	if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) {
		ShowError("WriteProcessMemory");
		return FALSE;
	}
	//获取LoadLibrary函数地址
	pFuncProcAddr = ::GetProcAddress(::GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA");
	if (NULL == pFuncProcAddr) {
		ShowError("GetProcAddress_LoadLibraryA");
		return FALSE;
	}
	//使用CreateRemoteThread创建远线程,实现DLL注入
	HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr,pDllAddr,0,NULL);
	if (NULL == hRemoteThread) {
		ShowError("CreateRemoteThread");
		return FALSE;
	}
	//关闭句柄
	::CloseHandle(hProcess);
	return TRUE;
}

Let's do a test. When you open the computer qq, a box will automatically pop up with text content. The pop-up window needs to be written as a DLL module for the target process to load.

1. The first is to find the process number according to the program name "qq.exe"

Use the following program to pull out the process numbers of QQ and exe

#include"inject.h"
#include<tlhelp32.h>//声明快照bai函数的头文件
#include<stdio.h>


int main(int argc, char *argv[])
{
	DWORD QQid; //记录qq 的进程号

	PROCESSENTRY32 pe32;
	//在使用这个结构之前,先设置它的大小
	pe32.dwSize = sizeof(pe32);
	//给系统内的所有进程拍一个快照
	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	//遍历进程快照,轮流显示每个进程的信息
	BOOL bMore = ::Process32First(hProcessSnap, &pe32);
	while (bMore)
	{
		char output[256];
		const WCHAR* wc = pe32.szExeFile;
		sprintf_s(output, "%ws", wc);
		if (strcmp("QQ.exe", output) == 0)//如果找到进程名为abc.exe
		{
			
			QQid = pe32.th32ProcessID;
			break;
			//HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);//获取句柄
			/*这里已经打开那个进程的句柄了*/
		}
		printf("next  %ls\n",pe32.szExeFile);
	
		
		bMore = ::Process32Next(hProcessSnap, &pe32);//寻找下一个
	}
	// 远线程注入 DLL
#ifndef _WIN64
	BOOL bRet = CreateRemoteThreadInjectDll(QQid, "C:\\Users\\DemonGan\\Desktop\\CreateRemoteThread_Test\\Debug\\TestDll.dll");
#else 
	BOOL bRet = CreateRemoteThreadInjectDll(1144, "C:\\Users\\DemonGan\\Desktop\\CreateRemoteThread_Test\\x64\\Debug\\TestDll.dll");
#endif

	if (FALSE == bRet)
	{
		printf("Inject Dll Error.\n");
	}
	printf("Inject Dll OK.\n");
	system("pause");
	return 0;
	return 0;
}

2. Make the dll file ready to run the effect as shown

The following is the dll pop-up code:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include<tchar.h>
HMODULE g_hDllModule = NULL;


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	{
		g_hDllModule = hModule;
		::MessageBox(NULL, _T("hhh."), _T("OK"), MB_OK);
		break;
	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

Break through sission 0 isolation:

When injecting the system process, you will encounter an isolation mechanism called session 0. At this time, you need to use the lower-level function ZwCreateThreadEx to create a remote thread. The specific principle is the same as CreateRemoteThread.

3. APC injection

apc is an asynchronous procedure call. In the windows system, each thread has an APC queue, and we can get the execution opportunity by pressing our self-defined apc function into this queue

Function introduction:

QueueUserAPC function

DWORD WINAPI QueueUserAPC(
    _In_ PAPCFUNC pfnAPC, //当指定线程执行可警告的等待操作时,指向应用程序提供的APC函数指针
    _In_ HANDLE hThread,  //线程句柄,该句柄必须有THREAD_SET_CONTEXT访问权限
    _In_ ULONG_PTR dwData) //传递有pfnAPC参数指向的APC函数的单个值

Function implementation:

#include"inject.h"

//定义函数showError
void ShowError(const char* content) {
	printf("%s\n", content);
}
//使用CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjiectDll(DWORD dwProcessId, LPCWSTR pszDllFileName) {
	HANDLE hProcess = NULL;
	DWORD dwSize = 0;
	LPVOID pDllAddr = NULL;
	FARPROC pFuncProcAddr = NULL;
	//打开注入进程,获取进程句柄
	hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
	if (NULL == hProcess) {
		ShowError("OpenProcess");
		return FALSE;
	}
	
	//再注入进程中申请内存
	dwSize = 1 + ::lstrlen(pszDllFileName);
	pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
	if (NULL == pDllAddr) {
		ShowError("VirtualAllocEX");
		return FALSE;
	}
	//向申请的内存中写入数据
	if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) {
		ShowError("WriteProcessMemory");
		return FALSE;
	}
	//获取LoadLibrary函数地址
	pFuncProcAddr = ::GetProcAddress(::GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA");
	if (NULL == pFuncProcAddr) {
		ShowError("GetProcAddress_LoadLibraryA");
		return FALSE;
	}
	//使用CreateRemoteThread创建远线程,实现DLL注入
	HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr,pDllAddr,0,NULL);
	if (NULL == hRemoteThread) {
		ShowError("CreateRemoteThread");
		return FALSE;
	}
	//关闭句柄
	::CloseHandle(hProcess);
	return TRUE;
}
// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(LPCWSTR pszProcessName)
{
	DWORD dwProcessId = 0;
	PROCESSENTRY32 pe32 = { 0 };
	HANDLE hSnapshot = NULL;
	BOOL bRet = FALSE;
	::RtlZeroMemory(&pe32, sizeof(pe32));
	pe32.dwSize = sizeof(pe32);

	// 获取进程快照
	hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (NULL == hSnapshot)
	{
		ShowError("CreateToolhelp32Snapshot");
		return dwProcessId;
	}

	// 获取第一条进程快照信息
	bRet = ::Process32First(hSnapshot, &pe32);
	while (bRet)
	{
		// 获取快照信息
		if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))
		{
			dwProcessId = pe32.th32ProcessID;
			break;
		}

		// 遍历下一个进程快照信息
		bRet = ::Process32Next(hSnapshot, &pe32);
	}

	return dwProcessId;
}

// 根据PID获取所有的相应线程ID
BOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD **ppThreadId, DWORD *pdwThreadIdLength)
{
	DWORD *pThreadId = NULL;
	DWORD dwThreadIdLength = 0;
	DWORD dwBufferLength = 1000;
	THREADENTRY32 te32 = { 0 };
	HANDLE hSnapshot = NULL;
	BOOL bRet = TRUE;

	do
	{
		// 申请内存
		pThreadId = new DWORD[dwBufferLength];
		if (NULL == pThreadId)
		{
			ShowError("new");
			bRet = FALSE;
			break;
		}
		::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));

		// 获取线程快照
		::RtlZeroMemory(&te32, sizeof(te32));
		te32.dwSize = sizeof(te32);
		hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (NULL == hSnapshot)
		{
			ShowError("CreateToolhelp32Snapshot");
			bRet = FALSE;
			break;
		}

		// 获取第一条线程快照信息
		bRet = ::Thread32First(hSnapshot, &te32);
		while (bRet)
		{
			// 获取进程对应的线程ID
			if (te32.th32OwnerProcessID == dwProcessId)
			{
				pThreadId[dwThreadIdLength] = te32.th32ThreadID;
				dwThreadIdLength++;
			}

			// 遍历下一个线程快照信息
			bRet = ::Thread32Next(hSnapshot, &te32);
		}

		// 返回
		*ppThreadId = pThreadId;
		*pdwThreadIdLength = dwThreadIdLength;
		bRet = TRUE;

	} while (FALSE);

	if (FALSE == bRet)
	{
		if (pThreadId)
		{
			delete[]pThreadId;
			pThreadId = NULL;
		}
	}

	return bRet;
}

//APC 注入
BOOL ApcInjectDll(LPCTSTR pszProcessName, LPCTSTR pszDLLName) {
	BOOL bRet = FALSE;
	DWORD dwProcessId = 0;
	DWORD *pThreadId = NULL;
	DWORD dwThreadIdLength = 0;

	HANDLE hProcess = NULL, hThread = NULL;
	PVOID pBaseAddress = NULL;
	PVOID pLoadLibraryAFunc = NULL;
	SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDLLName);
	DWORD i = 0;
	do {
		//根据进程名获取pid
		dwProcessId = GetProcessIdByProcessName(pszProcessName);
		if (0 >= dwProcessId) {
			//获取出错了
			bRet = FALSE;
			break;
		}
		//根据PID获取所有相应的线程ID
		bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);
		if (FALSE == bRet) {
			bRet = false;
			break;
		}

		//打开注入进程
		hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
		if (NULL == hProcess) {
			ShowError("OpenProcess");
			bRet = FALSE;
			break;
		}

		//在注入进程空间中申请内存
		pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		if (NULL == pBaseAddress) {
			ShowError("virtualAllocEX");
			bRet = FALSE;
			break;
		}
		//向申请的空间中写入DLL 路径数据
		::WriteProcessMemory(hProcess, pBaseAddress, pszDLLName, dwDllPathLen, &dwRet);
		if (dwRet != dwDllPathLen) {
			ShowError("WriteProcessMemory");
			bRet = FALSE;
			break;
		}
		//获取loadlibrarya地址
		pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
		if (NULL == pLoadLibraryAFunc) {
			ShowError("GetProcessAddress");
			bRet = FALSE;
			break;
		}

		//遍历线程,插入APC
		for (i = 0; i < dwThreadIdLength; i++) {
			//打开线程
			hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
			if (hThread) {
				//打开成功
				//插入APC 
				::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
				//关闭线程句柄
				::CloseHandle(hThread);
				hThread = NULL;
			}
		}
		bRet = TRUE;
	} while (FALSE);
	//释放内存
	if (hProcess)
	{
		::CloseHandle(hProcess);
		hProcess = NULL;
	}
	if (pThreadId)
	{
		delete[]pThreadId;
		pThreadId = NULL;
	}

	return bRet;
}

 

Guess you like

Origin blog.csdn.net/mid_Faker/article/details/112567506