《逆向工程》通过修改PE注入DLL

通过修改PE加载DLL

TextView是一个文本查看工具,将文本文件拖入即可显示。

代码如下,注意按照32位编译,否则PEView不能分析64位IDT。

#include "windows.h"
#include "stdio.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szAppName[] = L"TextView";
TCHAR szFile[MAX_PATH] = { 0, };
TCHAR szMsg[2048] = { 0, };

#define MAX_BUF_SIZE (32768)

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wndclass;

	wndclass.cbSize = sizeof(wndclass);
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;
	wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

	RegisterClassEx(&wndclass);

	hwnd = CreateWindow(
		szAppName, szAppName,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndEdit;
	HFONT hFont;

	switch (iMsg)
	{
	case WM_CREATE:
		hwndEdit = CreateWindow(L"Edit", NULL,
			WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
			WS_BORDER | ES_LEFT | ES_MULTILINE |
			ES_AUTOHSCROLL | ES_AUTOVSCROLL,
			0, 0, 0, 0,
			hwnd, (HMENU)1,
			((LPCREATESTRUCT)lParam)->hInstance, NULL);

		hFont = CreateFont(16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Courier New");
		SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hFont, (LPARAM)FALSE);

		DragAcceptFiles(hwnd, TRUE);

		return 0;

	case WM_DROPFILES:
		if (DragQueryFile((HDROP)wParam, 0, szFile, MAX_PATH))
		{
			HANDLE hFile = CreateFile(szFile, GENERIC_READ, FILE_SHARE_READ,
				NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (hFile == INVALID_HANDLE_VALUE)
			{
				wsprintf(szMsg, L"file(\"%s\") open error!!! [%d]\n", szFile, GetLastError());
				MessageBox(hwndEdit, szMsg, szAppName, MB_OK);
				return 0;
			}

			DWORD dwBytesRead = 0;
			char *pBuf = new char[MAX_BUF_SIZE];
			ZeroMemory(pBuf, MAX_BUF_SIZE);

			ReadFile(hFile, pBuf, MAX_BUF_SIZE, &dwBytesRead, NULL);

			SetWindowTextA(hwndEdit, pBuf);

			wsprintf(szMsg, L"TextView (%s)", szFile);
			SetWindowText(hwnd, szMsg);

			delete[]pBuf;

			CloseHandle(hFile);
		}

		return 0;

	case WM_SETFOCUS:
		SetFocus(hwndEdit);
		return 0;

	case WM_SIZE:
		MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

PEView中可以看到.rdata的IDT中有4个dll:kernel32.dll, user32.dll, gdi32.dll, shell32.dll

下面是myhack3.cpp,用来生成dll。

#include "stdio.h"
#include "windows.h"
#include "shlobj.h"
#include "Wininet.h"
#include "tchar.h"

#pragma comment(lib, "Wininet.lib")

#define DEF_BUF_SIZE            (4096)
#define DEF_URL                 L"http://www.baidu.com/index.html"
#define DEF_INDEX_FILE          L"index.html"

HWND g_hWnd = NULL;

#ifdef __cplusplus
extern "C" {
#endif
	//  dummy export function in IDT
	__declspec(dllexport) void dummy()
	{
		return;
	}
#ifdef __cplusplus
}
#endif

BOOL DownloadURL(LPCTSTR szURL, LPCTSTR szFile)
{
	BOOL            bRet = FALSE;
	HINTERNET	    hInternet = NULL, hURL = NULL;
	BYTE            pBuf[DEF_BUF_SIZE] = { 0, };
	DWORD           dwBytesRead = 0;
	FILE            *pFile = NULL;
	errno_t         err = 0;

	hInternet = InternetOpen(L"ReverseCore",
		INTERNET_OPEN_TYPE_PRECONFIG,
		NULL,
		NULL,
		0);
	if (NULL == hInternet)
	{
		OutputDebugString(L"InternetOpen() failed!");
		return FALSE;
	}

	hURL = InternetOpenUrl(hInternet,
		szURL,
		NULL,
		0,
		INTERNET_FLAG_RELOAD,
		0);
	if (NULL == hURL)
	{
		OutputDebugString(L"InternetOpenUrl() failed!");
		goto _DownloadURL_EXIT;
	}

	if (err = _tfopen_s(&pFile, szFile, L"wt"))
	{
		OutputDebugString(L"fopen() failed!");
		goto _DownloadURL_EXIT;
	}

	while (InternetReadFile(hURL, pBuf, DEF_BUF_SIZE, &dwBytesRead))
	{
		if (!dwBytesRead)
			break;

		fwrite(pBuf, dwBytesRead, 1, pFile);
	}

	bRet = TRUE;

_DownloadURL_EXIT:
	if (pFile)
		fclose(pFile);

	if (hURL)
		InternetCloseHandle(hURL);

	if (hInternet)
		InternetCloseHandle(hInternet);

	return bRet;
}

BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
	DWORD dwPID = 0;

	GetWindowThreadProcessId(hWnd, &dwPID);

	if (dwPID == (DWORD)lParam)
	{
		g_hWnd = hWnd;
		return FALSE;
	}

	return TRUE;
}

HWND GetWindowHandleFromPID(DWORD dwPID)
{
	EnumWindows(EnumWindowsProc, dwPID);

	return g_hWnd;
}

BOOL DropFile(LPCTSTR wcsFile)
{
	HWND            hWnd = NULL;
	DWORD           dwBufSize = 0;
	BYTE            *pBuf = NULL;
	DROPFILES		*pDrop = NULL;
	char            szFile[MAX_PATH] = { 0, };
	HANDLE          hMem = 0;

	WideCharToMultiByte(CP_ACP, 0, wcsFile, -1,
		szFile, MAX_PATH, NULL, NULL);

	dwBufSize = sizeof(DROPFILES) + strlen(szFile) + 1;

	if (!(hMem = GlobalAlloc(GMEM_ZEROINIT, dwBufSize)))
	{
		OutputDebugString(L"GlobalAlloc() failed!!!");
		return FALSE;
	}

	pBuf = (LPBYTE)GlobalLock(hMem);

	pDrop = (DROPFILES*)pBuf;
	pDrop->pFiles = sizeof(DROPFILES);
	strcpy_s((char*)(pBuf + sizeof(DROPFILES)), strlen(szFile) + 1, szFile);

	GlobalUnlock(hMem);

	if (!(hWnd = GetWindowHandleFromPID(GetCurrentProcessId())))
	{
		OutputDebugString(L"GetWndHandleFromPID() failed!!!");
		return FALSE;
	}

	PostMessage(hWnd, WM_DROPFILES, (WPARAM)pBuf, NULL);

	return TRUE;
}

DWORD WINAPI ThreadProc(LPVOID lParam)
{
	TCHAR szPath[MAX_PATH] = { 0, };
	TCHAR *p = NULL;

	OutputDebugString(L"ThreadProc() start...");

	GetModuleFileName(NULL, szPath, sizeof(szPath));

	if (p = _tcsrchr(szPath, L'\\'))
	{
		_tcscpy_s(p + 1, wcslen(DEF_INDEX_FILE) + 1, DEF_INDEX_FILE);

		OutputDebugString(L"DownloadURL()");
		if (DownloadURL(DEF_URL, szPath))
		{
			OutputDebugString(L"DropFlie()");
			DropFile(szPath);
		}
	}

	OutputDebugString(L"ThreadProc() end...");

	return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL));
		break;
	}

	return TRUE;
}

DownLoadURL()下载szURL网页文件并保存到szFile目录。大概流程是利用wininet.dllInternetOpen(), InternetOpenUrl(), InternetReadFile(), 也可以用urlmon.dllURLDownloadToFile()

DropFile()将网页文件拖到TextView_Patch.exe(载入myhack3.dll),所以要通过pid获取TextView的句柄,再传送WM_DROPFILES消息到消息队列。

dummy()的存在是为了形式上的完整,使myhack3.dll顺利添加到TextView的导入表。因为在PE文件中导入一个dll,实质就是调用该dll的导出函数。

我们使用PE Viewer和Hex Editor来修改TextView的导入表。

修改导入表

dll以struct _IMAGE_IMPORT_DESCRIPTOR结构体形式存在于exe的导入表中,只要将myhack3.dll添加到列表尾部就可以了。

查看IDT空间是否足够

PEView中查看TextView的IMAGE_OPTIONAL_HEADER64,找到导入表的_IMAGE_DATA_DIRECTORY

pFileRVASize
0x1700x384c0xdc

我这里显示RVA为0x384c,位于.rdata,大小为0xdc。所以导入表RVA范围为0x384c - 0x3928

转到RVA为0x384c的地方,然后将视图改为pFile(file offset),显示文件偏移为0x1e4c,所以文件偏移范围为0x1e4c - 0x1f27

pFile / RVAdata
0x1e4c / 0x384c0x3934

用hexeditor找到这块范围,然后发现没有空间添加myhack3.dll的结构体。

移动IDT

为了添加IID,可以把IDT转移到以下空旷的位置:

  • 文件的空白区域;
  • 增加文件最后一个节区的大小;
  • 文件末尾添加新节区。

法1:

  1. 修改导入表RVA和Size
  2. 删除绑定导入表
  3. 复制IDT到新位置
  4. 设置INT, name, IAT
  5. 修改IAT节区的属性

一般节区或文件末尾都会有空白的Null-Padding区域,比如.rdata,hexeditor中可以看到大片00。查看.rdata节区的IMAGE_SECTION_HEADER

datadescription
0x1046virtual size
0x3000RVA
0x1200size of raw data
0x1600pointer to raw data

我这里显示virtual size为0x1046,size of raw data为0x1200,差值0x1BA,有这么块区域未使用,在这里可以创建IDT。

00未必是Null-Padding区域,也不是所有Null-Padding区域会加载到内存。

目标RVA可以取0x3000 + 0x1046 + 0x2a == 0x4070,对应文件偏移0x1600 + 0x1046 + 0x2a == 0x2670

在hexeditor中,把导入表(文件偏移0x170)的_IMAGE_DATA_DIRECTORY.VirtualAddress从0x384c改为0x4070,大小改为0xdc + 0x14 == 0xf0

00000170 | 70 40 00 f0 00 00 00

PEView的_IMAGE_OPTIONAL_HEADER64部分可以看到bound import table,这是种提高dll加载速度的技术,是个可选项,我这里显示全0,不需要再修改。如果使用但信息错误,程序运行时就会出错。 修改其它文件时要注意检查这一项。

在hexeditor中把文件偏移0x1e4c - 0x1f27复制到0x2670 - 0x274b,然后在最后0x14字节的空IID结构体(IDT结尾)写入myhack3.dll数据。

INT, name, IAT的地址:

RVARAW
INT00x2770 -0x1600 + 0x3000 == 0x41700x2770
name0x41800x2780
IAT0x41900x2790

0x2770就在IDT下方,观察方便。

00002730    f8 3e 00 00 c4 30 00 00 70 41 00 00 00 00 00 00
00002740    00 00 00 00 80 41 00 00 90 41 00 00 00 00 00 00
    ...
00002770    a0 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00002780    6d 79 68 61 63 6b 33 2e 64 6c 6c 00 00 00 00 00
00002790    a0 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000027a0    00 00 64 75 6d 6d 79 00 00 00 00 00 00 00 00 00
	

0x2770为IID.OriginalFirstThunk,即INT的RVA;

0x2780为IID.name

0x2790为IID.FirstThunk,即IAT的RVA;

0x27a0处为IMAGE_IMPORT_BY_NAME结构体,包括导入函数的ordinal和函数名。

加载PE时,PE装载器会修改IAT,写入函数的实际地址,所以相关节区要拥有可写属性IMAGE_SCN_MEM_WRITE,其值为8,与IMAGE_SECTION_HEADER.Characteristics异或即可。

其实,.rdata本来就没有可写属性,但我们修改的PE必须有,否则程序无法运行,因为IMAGE_OPTIONAL_HEADER64的数据目录表里也有IAT,RVA为0x3000,Size为0x13c,在该范围内则不需要可写属性,显然我添加的IID不在此范围内。

修改完成后,PEView中IDT可以看到myhack3.dll,ProcessExplorer也可以看到,并且目录下有了百度网页。

起初我没有将myhack3.dll放在同目录,会弹框提示找不到myhack3.dll,无法运行。


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值