将任意一个文本文件拖入TextView中测试,随后使用PEView文件查看TextView.exe可执行文件的IDT(导入目录表)。从图中可以看出,TextView.exe中直接导入的DLL文件为KERNEL32.dll、USER32.dll、GDI32.dll、SHELL32.dll。
7]4X%NWG7M~B30O_VE1II_N.png
TextView_patched.exe是修改TextView.exe文件的IDT后得到的文件,即在IDT中添加了导入myhack3.dll的部分,运行时会自动导入myhack3.dll文件。查看IDT结果如图。
PT9OY}W$Z(6W7@U6V90%@%3.png

DllMain

  1. #include "stdio.h"
  2. #include "windows.h"
  3. #include "shlobj.h"
  4. #include "Wininet.h"
  5. #include "tchar.h"
  6. #pragma comment(lib, "Wininet.lib")
  7. #define DEF_BUF_SIZE (4096)
  8. #define DEF_URL L"http://www.google.com/index.html"
  9. #define DEF_INDEX_FILE L"index.html"
  10. DWORD WINAPI ThreadProc(LPVOID lParam)
  11. {
  12. TCHAR szPath[MAX_PATH] = {0,};
  13. TCHAR *p = NULL;
  14. OutputDebugString(L"ThreadProc() start...");
  15. GetModuleFileName(NULL, szPath, sizeof(szPath));
  16. if( p = _tcsrchr(szPath, L'\\') )
  17. {
  18. _tcscpy_s(p+1, wcslen(DEF_INDEX_FILE)+1, DEF_INDEX_FILE);
  19. OutputDebugString(L"DownloadURL()");
  20. if( DownloadURL(DEF_URL, szPath) )
  21. {
  22. OutputDebugString(L"DropFlie()");
  23. DropFile(szPath);
  24. }
  25. }
  26. OutputDebugString(L"ThreadProc() end...");
  27. return 0;
  28. }
  29. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  30. {
  31. switch( fdwReason )
  32. {
  33. case DLL_PROCESS_ATTACH :
  34. CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL));
  35. break;
  36. }
  37. return TRUE;
  38. }

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是否有足够空间

![KJP31E(8`ZH780P6S(XE{M.png
由图可知IDT的地址(RVA)为84CC,TextView.exe的IDT存在于.rdata节区。由于每个导入的DLL文件都对应1个IID结构体,每个IID结构体的大小为14个字节,所以图中整个IID区域为RVA:84CC~852F(整体大小为14*5=64)。
![SDCR5G~H09WY[$0@TVFS.png](https://cdn.nlark.com/yuque/0/2020/png/554486/1584527152485-9c0951b3-bdd4-422d-ab81-e066d161dedb.png#align=left&display=inline&height=242&name=SDCR5G~_H09WY%5B%240%40TV_FS.png&originHeight=242&originWidth=678&size=130387&status=done&style=none&width=678)
![089~LY7GRD7AM96]G1FG9S.png
IDT文件偏移为76CC~772F,整个大小为64字节,共有5个IID结构体,其中最后一个为NULL结构体。从图中可以看出IDT尾部存在其他数据,没有足够空间来添加myhack3.dll的IID结构体。

移动IDT

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