当前位置: 首页 > article >正文

《Windows PE》6.4.2 远程注入DLL

实验四十七:远程注入DLL

写一个窗口程序,将一个dll通过远程注入的方法,注入到第三章的示例程序PEHeader.exe中,支持32位和64位PE。

       ●dll.c

/*------------------------------------------------------------------------
 FileName:dll.c
 实验47:远程注入(DLL)
 (c) bcdaren, 2024
-----------------------------------------------------------------------*/
#include <Windows.h>

//入口和退出点
int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
	{
		MessageBox(NULL, L"Welcome to PE!", L"Hello", MB_OK);
		break;
	}
	case DLL_THREAD_ATTACH:
		break;

	case DLL_THREAD_DETACH:
		break;

	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;  
}

resource.h(略)

remoteThread.rc(略)

●remoteThread.c

/*------------------------------------------------------------------------
 FileName:remoteThread.c
 实验46:远程线程注入演示程序
 功能:目标是在进程PEHeader.exe中远程注入一个DLL,运行并显示"Welcome to PE!"对话框。
 测试步骤:当PEHeader.exe运行时,运行remoteThread.exe,文件菜单---插入到PEHeader.exe
 (c) bcdaren, 2024
-----------------------------------------------------------------------*/
#include <windows.h>
#include <strsafe.h>	//StringCchCopy
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")
#include <Richedit.h>
#include <tchar.h>
#include <string.h>
#include "resource.h"

//使用typedef给函数指针类型一个别名,函数指针存储函数地址
typedef FARPROC(__stdcall *_ApiSuspend)(HANDLE);
typedef HMODULE(__stdcall *_ApiResume)(HANDLE);
_ApiSuspend _suspendProcess;
_ApiResume _resumeProcess;

BOOL CALLBACK ProcDlgMain(HWND, UINT, WPARAM, LPARAM);
BOOL _patchPEInfo();
void _Init();
void ShowErrMsg();

HANDLE hInstance;
HWND hWinMain, hWinEdit;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
	const TCHAR	szDllEdit[] = TEXT("RichEd20.dll");
	const TCHAR	szClassEdit[] = TEXT("RichEdit20W");//peinfo.rc中定义
	const TCHAR	szNtdll[] = TEXT("ntdll.dll");
	const CHAR	szSuspend[] = "ZwSuspendProcess";//函数名需要定义为ASCII字符
	const CHAR	szResume[] = "ZwResumeProcess";
	const TCHAR	szErr3[] = TEXT("Error happend when getting address.");
	HMODULE hRichEdit, hNtdll;

	hRichEdit = LoadLibrary((LPCWSTR)&szDllEdit);
	hNtdll = LoadLibrary((LPCWSTR)&szNtdll);
	_suspendProcess = (_ApiSuspend)GetProcAddress(hNtdll, szSuspend);
	if (!_suspendProcess)
		MessageBox(NULL, szErr3, NULL, MB_OK);
	_resumeProcess = (_ApiResume)GetProcAddress(hNtdll, szResume);
	if (!_resumeProcess)
		MessageBox(NULL, szErr3, NULL, MB_OK);

	hInstance = GetModuleHandle(NULL);
	DialogBoxParam(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)ProcDlgMain, (LPARAM)0);
	FreeLibrary(hRichEdit);
	return 0;
}

BOOL CALLBACK ProcDlgMain(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	switch (wMsg)
	{
	case WM_CLOSE:
		EndDialog(hWnd, 0);
		return TRUE;

	case WM_INITDIALOG:
		hWinMain = hWnd;
		_Init();	//初始化
		return TRUE;

	case WM_COMMAND:
		switch (wParam)
		{
		case IDM_EXIT:
			EndDialog(hWnd, 0);
			return TRUE;

		case IDM_OPEN:		//停止
			_patchPEInfo();
			return TRUE;
		}
	}
	return FALSE;
}

void _Init()
{
	CHARFORMAT stCf;
	static TCHAR szClassEdit[] = TEXT("RichEdit20W");	//UNICODE版本
	//static TCHAR szClassEdit[] = TEXT("RichEdit20A");	//ASCII码版本
	static TCHAR szFont[] = TEXT("宋体");
	//设置编辑控件
	hWinEdit = GetDlgItem(hWinMain, IDC_INFO);
	SendMessage(hWinEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
	RtlZeroMemory(&stCf, sizeof(stCf));
	stCf.cbSize = sizeof(stCf);
	stCf.yHeight = 9 * 20;
	stCf.dwMask = CFM_FACE | CFM_SIZE | CFM_BOLD;
	StringCchCopy((LPTSTR)&stCf.szFaceName, lstrlen(szFont) + 1, (LPCTSTR)&szFont);
	SendMessage(hWinEdit, EM_SETCHARFORMAT, 0, (LPARAM)&stCf);
	SendMessage(hWinEdit, EM_EXLIMITTEXT, 0, -1);
}

/*
;--------------------
; 将远程线程打到进程PEHeader.exe中
; 测试方法:首先运行PEHeader.exe
; 启动该程序,单击第一个菜单的第一项
; 会发现桌面上弹出"Welcome to PE!"对话框
;--------------------
*/
BOOL _patchPEInfo()
{
	HANDLE phwnd, hProcess, hCreate;
	DWORD parent, hProcessID;
	static TCHAR strTitle[256];
	static TCHAR szBuffer[256];
	const TCHAR szTitle[] = TEXT("PE文件头中几个关键地址的定位:");
	const TCHAR szErr1[] = TEXT("Error happend when openning.");
	const TCHAR szErr2[] = TEXT("Error happend when VirtualAllocEx.");

	static int dwProcessID, dwThreadID;
	PVOID lpLoadLibrary, lpDllName;
	static CHAR szMyDllFull[MAX_PATH];//注意:写入内存中的字符都是ANSI字符

	const TCHAR szDllKernel[] = TEXT("Kernel32.dll");
	const CHAR szLoadLibrary[] = "LoadLibraryA";//注意:写入内存中的字符都是ANSI字符
	const CHAR szMyDll[] = "\\DLL.dll";//注意:写入内存中的字符都是ANSI字符

	//准备工作:获取dll的全路径文件名、获取LoadLibrary函数地址等
	GetCurrentDirectoryA(MAX_PATH, szMyDllFull);//获取应用程序的当前工作目录
	strcat_s(szMyDllFull, MAX_PATH, szMyDll);//添加后缀名
	int nLen = sizeof(CHAR)*(strlen(szMyDllFull) + 1);

	//获取Kernel32.dll句柄,LoadLibrary函数地址
	lpLoadLibrary = GetProcAddress(GetModuleHandle(szDllKernel), (LPCSTR)szLoadLibrary);

	//通过标题获得进程的handle
	parent = 0;	//复位标志
	phwnd = GetWindow(GetWindow(GetDesktopWindow(), GW_CHILD), GW_HWNDFIRST);
	if (!GetParent(phwnd))
		parent = 1;

	while (phwnd)
	{
		if (parent)
		{
			parent = 0;	//复位标志
			//得到窗口标题文字
			GetWindowText(phwnd, strTitle, sizeof(strTitle));
			if (!_tcscmp(szTitle, strTitle))
				break;
		}
		//寻找这个窗口的下一个兄弟窗口
		phwnd = GetWindow(phwnd, GW_HWNDNEXT);
		if (!GetParent(phwnd))
		{
			if (IsWindowVisible(phwnd))
				parent = 1;
		}
	}
	//根据窗口句柄获取进程ID
	GetWindowThreadProcessId(phwnd, &hProcessID);

	//1.打开本地进程
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, hProcessID);
	if (!hProcess)
	{
		MessageBox(NULL, szErr1, NULL, MB_OK);
		return FALSE;
	}

	//2.分配空间,以页为单位
	lpDllName = VirtualAllocEx(hProcess, NULL, nLen, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (!lpDllName)
	{
		MessageBox(NULL, szErr2, NULL, MB_OK);
		return FALSE;
	}
	//3.写入线程代码,注意:写入内存中的字符都是ANSI字符
	SIZE_T dwWrite = 0;
	if (!WriteProcessMemory(hProcess, lpDllName, szMyDllFull, nLen, &dwWrite))
	{
		MessageBox(NULL, L"写入内存失败!\n", NULL, MB_OK);
		return FALSE;
	}

	//4.创建一个在另一个进程的虚拟地址空间中运行的线程
	hCreate = CreateRemoteThread(hProcess, NULL, 0, lpLoadLibrary, lpDllName, 0, NULL);

	//5.等待线程结束返回,释放资源
	WaitForSingleObject(hCreate, -1);
	CloseHandle(hCreate);
	VirtualFreeEx(hProcess, lpDllName, 0, MEM_FREE);
	CloseHandle(hProcess);
	return TRUE;
}

运行

      

                     图6-5远程注入DLL

  总结

上述示例程序的功能是在进程PEHeader.exe中远程注入一个DLL,运行并显示"Welcome to PE!"对话框。

测试步骤:当PEHeader.exe运行时,运行remoteThread.exe,点击文件菜单”插入到PEHeader.exe”,就可以将DLL远程注入到进程PEHeader.exe中了。

远程注入的步骤:

1.打开本地进程(调用OpenProcess函数)。

2.目标进程分配空间,以页为单位(调用VirtualAllocEx函数)。

3.写入目标进程完整的DLL文件名,(调用WriteProcessMemory函数)。注意:写入内存中的字符都是ANSI字符。

4.创建远程线程(调用CreateRemoteThread函数)。

5.等待线程结束返回,释放资源(调用WaitForSingleObject函数)。

在VS CreateRemoteThread函数调用处下断点,远程注入参数如下所示:

图6-6 远程注入的参数

我们可以使用windbg调试器附加进程PEHeader.exe,查看进程地址0x04dc0000处的数据就是我们已经注入的DLL完整名称。

0:009> db 0x04dc0000

04dc0000  44 3a 5c 63 6f 64 65 5c-77 69 6e 70 65 5c 63 68  D:\code\winpe\ch

04dc0010  30 36 5c 72 65 6d 6f 74-65 54 68 72 65 61 64 5c  06\remoteThread\

04dc0020  44 4c 4c 2e 64 6c 6c 00-00 00 00 00 00 00 00 00  DLL.dll.........

       对比无DLL注入的方法,使用DLL注入的方法不需要对代码和数据进行重定位(由操作系统自动完成),省去了很多不必要的麻烦,因此也是我们常用的方法。


http://www.kler.cn/news/351074.html

相关文章:

  • 安全见闻(4)——开阔眼界,不做井底之蛙
  • GIS常见前端开发框架
  • HbuilderX 连接 Genymotion 模拟器
  • MongoDB的常用语句
  • [Hbase]一 HBase基础
  • 输入输出--I/O流【C++提升】
  • MySQL联合索引中不同区分度列的顺序对查询性能的影响
  • Spring Boot知识管理系统:敏捷开发实践
  • Spring Boot 自动配置与 Starter POMs 深度解析
  • Excel:Cells(Rows.Count, 1).End(xlUp).Row和Cells(Rows.Count, 1).End(xlUp)有什么区别
  • Git 查看当前分支是基于哪个分支拉取(源头分支)
  • 产品开发历程|共享空间系统小程序界面风格切换
  • 开放式蓝牙耳机哪个品牌好用?开放式耳机排行榜测评!
  • 转型AI产品经理需要掌握的硬知识(三):2B和2C类AI产品公司脑洞
  • JVM篇(运行时数据区(实战课程学习总结)
  • Windows:在WPS或者Word中添加Latex公式编辑器
  • 记录Centos7 漫漫配置路
  • Apache Doris介绍
  • 物联网的应用以及优势
  • webpack和vite的区别?
  • 实时语音转文字(基于NAudio+Whisper+VOSP+Websocket)
  • 通过激酶找到ChEMBL数据库数据的步骤与代码实现
  • day36 56.合并区间 738.单调递增的数字
  • Elasticsearch文档操作
  • 如何保证Redis和数据库的数据一致性
  • 从一个事故中理解 Redis(几乎)所有知识点