通过修改PE加载DLL

练习文件

  1. 直接修改TextView.exe文件,使其在运行时自动加载myhack3.dll文件。
  2. TextView.exe是一个非常简单的文本查看程序,只要用鼠标将要查看的文本文件(myhack3.cpp)拖动到其中,即可通过它查看文本文件的内容。
    在这里插入图片描述
  3. 使用PEView工具查看TextView.exe可执行文件的IDT(import Directory Table,导入目录表),TexeView.exe中直接导入的DLL文件为KERNEL32.dll、USER32.dll、GDI32.dll、SHELL32.dll.
    在这里插入图片描述
  4. TextView_patched.exe是修改TextView.exe文件的IDT后得到的文件,即在IDT中添加了导入myhack3.dll的部分,运行时会自动导入myhack3.dll文件,使用PEView工具查看TextView_patched.exe的IDT。
    在这里插入图片描述
  5. 运行程序,程序会自动加载myhack3.dll,尝试连接Google网站,下载网站的index.html,并将其放到TextView_Patched.exe程序。
    在这里插入图片描述
    在这里插入图片描述

源代码 - myhack3.cpp

  1. 分析myhack3.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.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 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;
}
  • DllMain()函数的功能非常简单,创建线程运行指定的线程过程,在线程过程(ThreadProc)中调用DownLoadURL()与DropFIle()函数,下载指定网页并将其拖放到文本查看程序。
  1. DownloadURL()
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;
}
  • DownloadURL()函数会下载参数szURL中指定的网页文件,并将其保存到szFile目录。
  • 上述示例中DownloadURL()函数使用internetOpen()、InternetOpenUrl()、InternetReadFile() API对URLDownloadToFile() API的简单实现。
  1. DropFile()
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;
}
  • DropFile()函数的主要功能是,使用PID获取窗口句柄,在调用postMessage(WM_DROPFILES)API将消息放入消息队列。
  1. dummy()
__declspec(dllexport) void dummy()
{
    return;
}
#ifdef __cplusplus
}
#endif
  • dummy()函数是myhack3.dll文件项外部提供服务的导出函数,它没有任何功能,为了保持形式上的完整性,是myhack3.dll能够顺利添加到TextView.exe文件的导入表。
  • 在PE文件中导入某个DLL,实质就是在文件伪造内调用该DLL提供的导出函数,PE文件头记录着DLL名称、函数名称等信息,因此,myhack3.dll至少要提供1个以上的导出函数才能保持形式上的完整性。

修改TextView.exe文件的准备工作

  1. PE文件中导入的DLL信息以结构体列表形式存储在IDT中,只要将myhack3.dll添加到列表末尾就可以了,当然,此前要确认一下IDT中有无足够空间。
  2. 使用PEView查看TextView.exe的IDT地址,PE文件头的IMAGE_OPTIONAL_HEADER结构体中导入表的RVA值即为IDT的RVA,IDT的地址为84CC,接下来,在PEView中直接查看IDT。
    在这里插入图片描述
  3. TextView.exe的IDT存在于.rdata节区,IDT是由IMAGE_IMPORT_DESCRIPTOR(简称IID)结构体组成的数组,且数组末尾以NULL结构体结束,由于每个导入的DLL文件都对应一个IID结构体,每个IID结构图大小为14个字节.
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // 包含指向IMAGE_DATA(输入名称表)RVA 的结构数组
    };
    DWORD   TimeDateStamp;                  //当可执行文件不与被导入的DLL进行绑定时,此字段为0
    DWORD   ForwarderChain;                 //第一个被转向的API索引
    DWORD   Name;                           //指向被导入的DLL 名称
    DWORD   FirstThunk;                     //指向输入地址表(IAT)RVA,IAT是一个IMAGE_THUNK_DATA结构的数组
} IMAGE_IMPORT_DESCRIPTOR;
  1. 使用PEView工具栏将视图改为File Offset,可以看到IDT的文件偏移为76CC.
    在这里插入图片描述
    在这里插入图片描述
  • IDT的文件偏移为76CC~772F,整个大小为64(十六进制)字节,共有5个IID结构体,其中最后一个为NULL结构体,从图中可以看出IDT尾部存在其他数据,没有足够空间来添加myhack3.dll的IID结构体。
  1. 先把整个IDT转移到其他更广阔的位置,然后再添加新的IID,确定移动的目标位置时,可以使用下main三种方式:
  • 查找文件中的空白区域
  • 增加文件最后一个节区的大小。
  • 在文件末尾添加新节区。
  1. .rdata节区尾部恰好存在大片空白区域,一般来说,节区或文件末尾都存在空白区域,PE文件这种空白区域称为Null-Padding区域。
    在这里插入图片描述
  • 把原IDT移动到该Null-Padding区域(RVA:8C60~8DFF)中合适位置就行了,在此之前,先要确认一下该区域是否全是空白可用区域(Null-padding区域),并不是文件中的所有区域都会被无条件加载到进程的虚拟内存,只有节区头中明确记录的区域才会被加载。
    在这里插入图片描述
    在这里插入图片描述
  • .rdata节区在磁盘文件中的大小为2E00,而文件执行后被加载到内存时,程序实际使用的数据大小为2C56,剩余未被使用的区域大小为1AA,在这段空白区域创建IDT是不会由什么问题的。
  • 我们要在RVA:8C80(RAW:7E80)位置创建IDT。

修改TexTView.exe

  1. 将TextView.exe复制一份,重命名为TextView_Patch.exe。
  2. IMAGE_OPTIONAL_HEADER的导入表结构成员用来指出IDT的位置与大小。
    在这里插入图片描述
  • TextView.exe文件中,导入表的RVA的值为84CC,接下来,将导入表的RVA值更改为新IDT的RVA值8C80,在Size原值64字节的基础上增加14个字节,修改为78个字节。
    在这里插入图片描述
  1. 删除绑定导入表
    在这里插入图片描述
  • BOUND IMPORT TABLE是一种提高DLL加载速度的技术。
  • 若想正常导入myhack3.dll,需要项绑定导入表添加信息,但幸运的是,该绑定导入表是个可选项,不是必须存在的,所以可删除(修改其值为0)以获取更大便利。绑定导入表完全不存在也没关系,但若存在,且其内信息记录错误,则会在程序运行时引发错误。
  • 本实例TextView.exe,绑定导入表各项的值均为0,不需要在修改。
  1. 创建新的IDT
  • 复写原来的数据到新的位置RAW:7E80,如图所示:
    在这里插入图片描述
  • RAW:7ED0添加与myhack3.dll对应的IID:
    在这里插入图片描述
  1. 设置Name、INT、IAT
  • 前面添加的IID结构体成员拥有指向其他数据结构(INT、Name、IAT)的RVA的值,因此,必须准确设置这些数据结构才能保证TextView_Patch.exe文件正常运行。
    在这里插入图片描述
  • 这些地址(RVA:8D00,8D10,8D20)就位于新创建的IDT(RVA:8C80)下方,我们输入相应的值:
    在这里插入图片描述
  • 8CD0地址处存在myhack3.dll的IID结构体,其中3个主要成员(RVA of INT、RVA of Name、RVA of IAT)的值分别是实际INT、Name、IAT的指针。
  • INT是RVA数组,数组的各个元素都是一个RVA地址,该地址由导入函数的Ordinal(2个字节)+Func Name String结构体组成,数组的末尾为NULL。
  • Name包含导入函数文件名称字符串,在8D10地址处可以看到“myhack3.dll"字符串。
  • IAT也是RVA数组,各元素既可以拥有与INT相同的值,也可以拥有其他不同值,反正实际运行时,PE装载器会将虚拟内存中的IAT替换为实际函数的地址。
  1. 修改IAT节区的属性值
  • 加载PE文件到内存时,PE装载器会修改IAT,写入函数的实际地址,所以相关节区一定要拥有WRITE属性,只要这样,PE装载器才能正常写入操作。
  • 使用PEView查看.rdata节区头:
    在这里插入图片描述
  • 向原属性值40000040添加IMAGE_SCN_MEM_WRITE(80000000)属性值,执行bit OR运算,最终属性值变为C0000040.
    在这里插入图片描述

检测验证

  1. 使用PEView打开修改后的TextView_Patch.exe文件,查看IDT。
    在这里插入图片描述
  2. 向IDT导入myhack3.dll的IID结构已设置正常,myhack3.dll的dummy()函数被添加到INT。
    在这里插入图片描述
  3. 运行程序
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值