实验四十七:远程注入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);//通过标题获得进程的handleparent = 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;}}//根据窗口句柄获取进程IDGetWindowThreadProcessId(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注入的方法不需要对代码和数据进行重定位(由操作系统自动完成),省去了很多不必要的麻烦,因此也是我们常用的方法。