通过修改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 descriptorDWORD   OriginalFirstThunk;         // 包含指向IMAGE_DATA(输入名称表)RVA 的结构数组};DWORD   TimeDateStamp;                  //当可执行文件不与被导入的DLL进行绑定时,此字段为0DWORD   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. 运行程序
    在这里插入图片描述


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部