将任意一个文本文件拖入TextView中测试,随后使用PEView文件查看TextView.exe可执行文件的IDT(导入目录表)。从图中可以看出,TextView.exe中直接导入的DLL文件为KERNEL32.dll、USER32.dll、GDI32.dll、SHELL32.dll。
TextView_patched.exe是修改TextView.exe文件的IDT后得到的文件,即在IDT中添加了导入myhack3.dll的部分,运行时会自动导入myhack3.dll文件。查看IDT结果如图。
DllMain
#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)中调用DownloaadURL()与DropFile()函数,下载指定的网页并将其拖放到文本查看程序。
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目录。示例中该函数用来连接谷歌网站(www.google.com),并下载网站的index.html文件。
提示:该函数是使用InternerOpen()、InternetOpenUrl()、InternetReadFile() API对URLDownloadToFile() API的简单实现。InternetOpen()、InternetOpenUrl()、InternetReadFile() API均在wininet.dll中提供、而URLDownloadToFile() API在urlmon.dll中提供
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;
}
dummy()
#ifdef __cplusplus
extern "C" {
#endif
// 出现在IDT中的dump export function...
__declspec(dllexport) void dummy()
{
return;
}
#ifdef __cplusplus
}
#endif
修改TextView.exe文件
修改思路
PE文件中导入的DLL信息以结构体列表形式存储在IDT中。只要将myhack.dll添加到列表尾部即可。
查看IDT是否有足够空间

![089~LY7GRD7AM96]G1FG9S.png
IDT文件偏移为76CC~772F,整个大小为64字节,共有5个IID结构体,其中最后一个为NULL结构体。从图中可以看出IDT尾部存在其他数据,没有足够空间来添加myhack3.dll的IID结构体。
移动IDT
这种情况下,要先把整个IDT转移到其他更广阔的位置,然后再添加新的IID。确定移动的目标位置时可以使用:
#查找文件中的空白区域
#增加文件最后一个节区的大小
#在文件末尾添加新节区
首先尝试第一种方法,即查找文件中的空白区域(程序运行时未使用的区域)。如图,.rdata节区尾恰好存在大片空白区域(一般来说,节区或文件末尾都存在空白区域,PE文件中这种空白区域称为Null-Padding区域)。
把原IDT移动到该空白区域(RVA:8C60~8DFF)中合适位置。首先确认该区域是否拳师空白可用区域。并非文件中所有的区域都会被无条件加载到进程的虚拟内存,只有节区头中明确记录的区域才会被加载。
节区头中存储着对应节区的位置、大小、属性等信息。整理.rdata节区头中信息如表。
.rdata节区在磁盘文件中的大小为2E00,而文件执行后被加载到内存时,程序实际使用数据大小(映射大小)仅为2C56,剩余未被使用的区域大小为1AA(2E00~2C56)。在这段空白区域创建IDT不会有什么问题。