首页
社区
课程
招聘
[分享]通过修改PE文件加载DLL
发表于: 2021-8-13 17:07 11478

[分享]通过修改PE文件加载DLL

2021-8-13 17:07
11478

说明:

TextView.exe是一个非常简单的文本查看程序,只要用鼠标将要查看的文本文件拖动(Drop)到其中,即可通过它查看文本文件的内容。如下

1.jpg

使用 PEView 工具查看TextView.exe可执行文件的 IDT(Import directory Table,导入目录表)。

2.jpg

从上图可以看到,TextView.exe中直接导入的 DLL 文件为 KERNEL32.dllUSER32.dllGDI32.dllSHELL32.dll

这一节用来分析 myhack3.dll的源代码。

DLLMain()函数的功能非常简单,创建线程运行指定的线程过程(ThreadProc) 中调用DownloadURL()DropFile()函数。

DownloadURL()函数会下载参数szURL中指定的网页文件,并将其保存到szFile目录。

DropFile()函数将下载的index.html文件拖到TextView_Patch.exe进程并显示其内容。

dummy()导出函数,但是没有任何功能,仅仅保持dll文件的形式上完整。

在PE文件中导入某个 DLL ,实质就是在文件代码内调用该 DLL 提供的导出函数。PE 文件头中记录着 DLL 名称、函数名称等信息。因此,myhack3.dll至少需要向外提供一个以上的导出函数才能保持形式上的完整性。

PE文件中导入的 dll 信息以结构体列表的形式存储在 IDT 中。重要将 myhack3.dll 添加到列表尾部就可以了。 当然,此前需要确认一下 IDT 中有无足够的空间。

首先,使用 PEView 查看 TextView.exe 的 IDT 地址(PE 文件头的 IMAGE_OPTIONAL_HEADER 结构体中导入表 RVA 值即为 IDT 的RVA值)。

从下图可以看出,IDT 的地址(RVA)为 84CC 。 接下来在 PEView 中直接查看 IDT (在 PEView
工具栏中设置地址视图选项为 RVA )。

3.jpg

从上图可以看出,TextView.exe 的 IDT 存在与 .rdata 节区。 IDT 是由 IMAGE_IMPORT_DESCRIPTOR(以下简称 IID) 结构体组成的数组,且数组的末尾以 NULL 结构体结束。由于每个导入的 DLL 文件都对应 1 个 IID 结构体(每个 IID 结构体的大小为 0*14字节),所以下图中的整个 IID 区域为RVA:84CC ~ 852F(整体大小为:0*14*5 = 0*64字节)

4.jpg

IID 结构体的定义

在 PEView 工具栏将视图改为 File Offset,可以看到 IDT 的文件偏移为 76CC,如下图所示

5.jpg

使用 HxD 使用工具打开 TextView文件,找到 76CC 地址,如下图所示

6.jpg

IDT 的文件偏移为 76CC ~ 772F ,整个大小为64字节,共有5个 IID 结构体,其中最后一个为 NULL
结构体。从图中可以看出 IDT 尾部存在其它数据,没有足够的空间来添加 myhack3.dll 的 IID 结构体。

在这种情况下,我们首先要把整个 IDT 转移到其他更广阔的位置。然后在添加新的 IID。确定移动的目标位置时,可以使用下面三种方式:

首先第一种,即查找文件中的空白区域(程序运行时未使用的区域)。正如下图所示,.rdata节区尾部恰好存在大片空白区域(一般来说,节区或文件末尾都存在空白区域,PE 文件中这种空白区域称为 Null-Padding 区域)。

7.jpg

下面,就需要把原来的 IDT 移动到该 Null-Padding 区域(RVA:8C60 ~ 8DFF)中合适的位置即可。在此之前需要确认一下该区域(RVA:8C60 ~ 8DFF)是否是空白空用区域(Null-Padding区域)。

需要注意的是,并不是文件中所有区域都会被无条件加载到进程的虚拟内存,只有节区头中明确记录的区域才会被加载。使用 PEView 工具查看 TextView.exe 文件的 .rdata 节区头,如下所示:

8.jpg

节区头中存储着对应节区的位置,大小,属性等信息。整理.rdata节区头中信息,如下表所示

从节区头中信息可以看出,.rdata节区在磁盘文件与内存中的大小是不同的。

.rdata 节区在磁盘文件中的大小为 2E00,而文件执行后被加载到内存时,程序实际使用的数据大小(映射大小)仅为 2C56 ,剩余未被使用的区域大小为 1AA (2E00 - 2C56)。在这段空白区域创建 IDT 是不会有什么问题的。

空白区域
RVA:8C56 - 8E00 --> RAW : 7E56 - 8000

故下图的 Null-Padding 区域是可以使用的,接下来我们在 RVA:8C80(RAW:7E80)位置创建 IDT。

9.jpg

先把 TextView.exe 复制到工作文件夹,重命名为TextView_Patch.exe。下面使用TextView_Patch.exe文件进行练习打补丁。基本操作步骤是:先使用 PEView 打开 TextView.exe 原文件,查看各种 PE 信息,然后使用 HxD 打开 TextView_Patch 文件进行修改。

IMAGE_OPTIONAL_HEADERD的导入表结构体成员用来指出 IDT 的位置(RVA)与大小,如下所示

10.jpg

TextView.exe 文件中,导入表的 RVA 值为 84CC 。接下来,将导入表的 RVA 值更改为新 IDT 的 RVA 值 8C80,在 Size 原值64字节的基础上再加 14字节(IID 结构体的大小),修改为78字节,如下所示。

11.jpg

从现在开始,导入表位于 RVA: 8C80(RAW : 7E80)地址出。

BOUND IMPORT TABLE(绑定导出表)是一种提高 DLL 加载速度的技术,如下

12.jpg

若向正常导入 myhack3.dll ,需要向绑定导入表添加信息。但是幸运的是,该绑定导入表是个可选选项,不是必须存在的,所以可以删除(修改其值为 0 即可)以获取更大便利。当然绑定导入表完全不存在也没有关系,但若存在,且其内信息记录错误,则会在程序运行时引发错误。本示例 TextView.exe 文件中,绑定导入表各项的值均为 0 ,不需要再修改。修改文件时,一定要注意检查绑定导入表中的数据。

先使用 Hex Editor 完全复制原 IDT(RAW:76CC ~ 772F),然后覆写(Past write)到 IDT 的新位置(RAW :7E80 ),如下图所示

13.jpg

然后在新的IDT 尾部(RAW:7ED0)添加与 myhack3.dll 对应的 IID (后面会单独讲解各个成员的数据)

在准确位置(RAW:7ED0)写入相关数据,如下所示

14.jpg

前面添加的 IID 结构体成员拥有指向其他数据结构(INT、Name、IAT)的RVA值。因此,必须准确设置这些数据结构才能保证 TextView_Patch.exe 文件的正常运行。由前面的设置可知 INT、Name、IAT的 RVA/RAW 的值整理如下

这些地址(RVA:8D00,8D10,8D20)就位于新建的IDT(RVA:8C80)下方。当然也可以选择其他符合条件的区域。在 HxD 编辑器中转到 7F00 地址处,输入相应值,如下图所示

15.jpg

为了更好地理解以上内容,使用 PEView 打开 TextView_Patch.exe 文件查看同一区域,查看时使用 RVA 视图方式,如下所示

16.jpg

8CD0地址处存在着 myhack3.dll 的结构体,其中3各主要成员(RVA of INT 、RVA of Name、RVA of IAT)的值分别时实际 INT 、Name、IAT的指针。

简单来说,INT(Import Name Table,导入名称表)是 RVA 数组,数组的各个元素都是一个 RVA 地址,该地址由导入函数的 Ordinal(2个字节)+ Func Name String 结构体构成,数组的末尾为 NULL。 上图中 INT 有一个元素,其值为 8D30,该地址是要导入的函数的 Ordinal(2字节)与函数的名称字符串(“dummy”)。

Name 是包含导入函数的 DLL 文件名称字符串,在 8D10 地址处可以看到myhack3.dll字符串。

IAT 也是RVA 数组,各元素既可以拥有与 INT 相同的值,也可以拥有其他不用的值(若 INT 中的数据准确,IAT 也可以拥有其他不同值)。反正实际运行时,PE 装载器会将虚拟内存中的 IAT 替换为实际函数的地址

加载 PE 文件到内存时,PE装载器会修改 IAT ,写入函数的实际地址,所以相关节区一定要拥有 WRITE(可写)属性。只有这样,PE 装载器才能正常进行写入操作。使用 PEView查看.rdata节区头,如下所示

17.jpg

向原属性(ChAracteristics)40000040 添加 IMAGE_SCN_MEM_WRITE(80000000)属性值,执行 bit OR 运算,最终属性值变为 C0000040 , 如下图所示

18.jpg

首先使用 PEView 工具打开修改后的 TextView_Patch.exe 文件,查看其 IDT ,如下所示

19.jpg

向 IDT 导入 myhack3.dll 的 IID 结构体已设置正常。如下所示, myhack3.dll 的 dummy() 函数被添加到 INT。

20.jpg

从文件结构来说修改成功,接下来运行文件看看程序是否能正常运行。将TextView_Patch.exe和myhack3.dll 放入相同文件夹,然后运行 TextView_Patch.exe 文件,结果如下所示:

21.jpg

可以看见 TextView_Patch.exe 成功加载了 myhack.dll 文件并下载了指定网站的 index.html 文件,并在 TextView_Patch.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;
}
// 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;
}
#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.google.com/index.html"
#define DEF_INDEX_FILE L"index.html"
 
 
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;
}
#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.google.com/index.html"
#define DEF_INDEX_FILE L"index.html"
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 2
支持
分享
最新回复 (3)
雪    币: 299
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
Mark
2021-8-13 18:27
0
雪    币: 0
活跃值: (954)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
导入表注入
2021-8-16 10:59
0
游客
登录 | 注册 方可回帖
返回
//