实验四十七:远程注入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注入的方法不需要对代码和数据进行重定位(由操作系统自动完成),省去了很多不必要的麻烦,因此也是我们常用的方法。