参考课件 chap06.pdf 中“通过修改PE装载DLL”一节的内容,以及课本第 5.5节的 内容,实现对notepad.exe(修復並下載Notepad.exe)的修改,使得修改后的 notepad.exe 在双击运行时,能够自动隐式装载 MyDll3.dll,并进而 将一个网页下载到本地index.html 文件。

本上机作业的目的是让大家深入理解 PE 文件中的导入表、导入地址表等关键结构及相关的功能。

  1. 本实验所使用的notepad.exe 是 Windows 7系统(最好32位版)中的可执行程序,MyDll3.dll 也仅保证在 Windows 7下能正常工作,因此,建议在 Windows 7环境(可预先安装虚拟机)下进行修改。请在虚拟机中禁用所有杀毒软件,确保修改过程不会受到杀毒软件的干扰。保证虚拟机能联网,以便DLL装载后的网页下载动作能正常完成。
  2. 下载notepad.exe可能与课本中所描述的notepad.exe版本不一致,应主要参考课件中的修改流程。
  3. 使用PEview分析PE文件,使用HxD编辑器修改PE文件。
  4. 直接使用本书所附代码编译生成 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

  1. 查看IDT是否有足够空间

我们从Image_Optional_Header的IMPORT Table得到结构体数组的RVA和Size

image-20221129084743308

通过查看image_section_header,发现RVA84CC处于rdata区域

image-20221129084920604

计算偏移

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

image-20221129085530959

然后我们使用010 editor打开可以发现64Bytes的空间,有五个IDT结构体,最后一个为NULL,在我们的IDT之后紧贴着其它数据,我们没有足够的空间来添加一个0x14字节的结构体进去

image-20221129090351201

  1. 移动IDT

    从节区头信息可以得到,其内存virtual Size与文件的大小Size of Raw Data是不一样的

    image-20221129091817990

    .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字节

image-20221129092707789 修改后导入表位于 RVA: 8C80(RAW : 7E80)地址处

先使用010 Editor完全复制原IDT(RAW:76CC~772F),然后覆盖到IDT的新位置(RAW:7E80)

image-20221129093114593image-20221129170652737

在7ED0处写入IID

image-20221129151000219

然后在新IDT尾部(RAW:7ED0)添加与mydll3对应的IID

image-20221129170916339

转到 7F00 地址处,输入相应值

image-20221129171549719

3.修改 IAT 节区的属性值

向原属性(ChAracteristics)40000040 添加 IMAGE_SCN_MEM_WRITE(80000000)属性值

也就是C0000040

image-20221129183917794

image-20221129184116973

使用 PEView 工具打开修改后的 TextView.exe 文件,查看其 IDT,发现已经装载上了myhack3.dll

image-20221129184229569

image-20221129184625924

点开,我们发现它成功下载了百度某个cdn下的index.html,并展示在文字框中

image-20221129184740738

参考文献

[[原创]通过修改PE加载DLL]https://bbs.pediy.com/thread-267045-1.htm

[通过修改PE文件的方式导入DLL]https://blog.csdn.net/fanxiaoyao1/article/details/125379489

李承远 逆向工程核心原理