参考课件 chap06.pdf 中“通过修改PE装载DLL”一节的内容,以及课本第 5.5节的 内容,实现对notepad.exe(修復並下載Notepad.exe)的修改,使得修改后的 notepad.exe 在双击运行时,能够自动隐式装载 MyDll3.dll,并进而 将一个网页下载到本地index.html 文件。
本上机作业的目的是让大家深入理解 PE 文件中的导入表、导入地址表等关键结构及相关的功能。
- 本实验所使用的notepad.exe 是 Windows 7系统(最好32位版)中的可执行程序,MyDll3.dll 也仅保证在 Windows 7下能正常工作,因此,建议在 Windows 7环境(可预先安装虚拟机)下进行修改。请在虚拟机中禁用所有杀毒软件,确保修改过程不会受到杀毒软件的干扰。保证虚拟机能联网,以便DLL装载后的网页下载动作能正常完成。
- 下载notepad.exe可能与课本中所描述的notepad.exe版本不一致,应主要参考课件中的修改流程。
- 使用PEview分析PE文件,使用HxD编辑器修改PE文件。
- 直接使用本书所附代码编译生成 MyDll3.dll。
PE文件静态注入
这里因为notepad一直注入不成功,换了个win7版本也不行,好像是安全策略的原因,就只好找另外一个textview.exe进行注入
代码是李承远老师的逆向工程核心原理当中的
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#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
// 导出函数,但是没有任何功能,仅仅保持dll文件的形式上完整。
__declspec(dllexport) void dummy()
{
return;
}
#ifdef __cplusplus
}
#endif
//DownloadURL 下载 szURL 中指定网站的文件,并将其保存在 szFile 目录。
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;
}
//DropFile 函数将下载的 index.html 文件 拖到 TextView_Path.exe进程并显示其内容。
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 APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL));
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
dummmy()
函数实际是dll文件向外部提供服务的导出函数,但正如所见,它没有任何功能。既然如此,为何还要将其导出呢?这是为了保持形式上的完整性,mydll3.dll能够顺利添加到notepad_patch.exe文件的导入表。
生成的myhack3.dll
- 查看IDT是否有足够空间
我们从Image_Optional_Header的IMPORT Table得到结构体数组的RVA和Size
通过查看image_section_header,发现RVA84CC处于rdata区域
计算偏移
0x6000-0x5200 = 0xE00
文件偏移=即0x84CC -0xE00 = 0x76CC
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA)
};
DWORD TimeDateStamp;
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name; //dll 名称
DWORD FirstThunk; //指向引入函数真实地址单元处的RVA IAT
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
也可以将PEview调成文件偏移视图查看,可以看到文件偏移确实是0x76CC
然后我们使用010 editor打开可以发现64Bytes的空间,有五个IDT结构体,最后一个为NULL,在我们的IDT之后紧贴着其它数据,我们没有足够的空间来添加一个0x14字节的结构体进去
-
移动IDT
从节区头信息可以得到,其内存virtual Size与文件的大小Size of Raw Data是不一样的
.rdata
节区在磁盘文件中的大小为2E00
,而文件执行后被加载到内存时,程序实际使用的数据大小(映射大小)仅为 2C56 ,剩余未被使用的区域大小为 1AA (2E00 - 2C56)足够放下(0x14 * 6 = 0x78)字节的数据可以先从0x8C80开始存放我们的IDT(转化为文件偏移为0x7E80)(新IDT: 0x7E80到(0x7E80+0x78))
TextView.exe 文件中,导入表的 RVA 值为 84CC 。接下来,将导入表的 RVA 值更改为新 IDT 的 RVA 值 8C80,在 Size 原值64字节的基础上再加 14字节(IID 结构体的大小),修改为78字节
修改后导入表位于 RVA: 8C80(RAW : 7E80)地址处
先使用010 Editor完全复制原IDT(RAW:76CC~772F),然后覆盖到IDT的新位置(RAW:7E80)
在7ED0处写入IID
然后在新IDT尾部(RAW:7ED0)添加与mydll3对应的IID
转到 7F00 地址处,输入相应值
3.修改 IAT 节区的属性值
向原属性(ChAracteristics)40000040 添加 IMAGE_SCN_MEM_WRITE(80000000)属性值
也就是C0000040
使用 PEView 工具打开修改后的 TextView.exe 文件,查看其 IDT,发现已经装载上了myhack3.dll
点开,我们发现它成功下载了百度某个cdn下的index.html,并展示在文字框中
参考文献
[[原创]通过修改PE加载DLL]https://bbs.pediy.com/thread-267045-1.htm
[通过修改PE文件的方式导入DLL]https://blog.csdn.net/fanxiaoyao1/article/details/125379489
李承远 逆向工程核心原理