1. 程式人生 > >第四課 通過修改PE載入DLL

第四課 通過修改PE載入DLL

下面通過例項來講解

下面分析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"

HWND g_hWnd = NULL;

#ifdef __cplusplus
extern "C" {
#endif
// IDT 형식을 위한 dummy export function...
__declspec(dllexport) void dummy()
{
    return;
}
#ifdef __cplusplus
}
#endif

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;
}

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 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;
}

下面看一下DropFile()

複習一下IID結構體的定義

typedef struct _IMAGE_IMPORT_DESCRIPTOR {

    union {

        DWORD   Characteristics;     // 0 for terminating null import descriptor

        DWORD   OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA)

    };

DWORD   TimeDateStamp;   

    DWORD   ForwarderChain;     // -1 if no forwarders

    DWORD   Name;            //dll 名稱

    DWORD   FirstThunk;         //指向引入函式真實地址單元處的RVA  IAT

} IMAGE_IMPORT_DESCRIPTOR;

typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

首先在空白位置建立新的IDT

刪除繫結匯入表

IAT中儲存的函式地址是dll未載入的地址,當PE檔案中不存在繫結匯入表時,IAT就與INT一樣,此時匯入表中的時間戳就為0;否則匯入表中的時間戳為-1時,dll的真正時間戳存放於繫結匯入表中(繫結匯入表地址存放在資料目錄的第12項,IAT是第13項)。

現在大多數情況,匯入表的TimeDateStamp都為0,而Windows早期的自帶軟體(如WinXP的notepad.exe)基本都採用了TimeDateStamp為-1的情況即包含繫結匯入表的情況。PE中包含匯入表的優點是程式啟動快,但是其缺點也十分明顯,當存在dll地址重定位和dll修改更新,則繫結匯入表也需要修改更新。

//最後一個結構全0表示繫結匯入表結束

typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {

    DWORD   TimeDateStamp;      //表示繫結的時間戳,如果和PE頭中的TimeDateStamp不同則可能被修改過

    WORD    OffsetModuleName;   //dll名稱地址

    WORD    NumberOfModuleForwarderRefs;    //依賴dll個數

// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows

} IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;

NumberOfModuleForwarderRefs是指該dll自身依賴的dll的個數。值為n代表該結構後面緊跟了n個IMAGE_BOUND_FORWARDER_REF結構。之後才是匯入表匯入的下一個dll的結構。而IMAGE_BOUND_FORWARDER_REF結構體如下所示:

typedef struct _IMAGE_BOUND_FORWARDER_REF {

    DWORD   TimeDateStamp;  //時間戳,同樣的作用檢查更新情況

    WORD    OffsetModuleName;   //dll名稱地址

    WORD    Reserved;   //保留

} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;

注意:這兩個結構體中所有的OffsetModuleName均不是相對於ImageBaseRVA也不是FOA,而是相對於繫結匯入表首地址的偏移地址,即:繫結匯入表首地址 + OffsetModuleName= RVA

繫結匯入表結構圖解如下所示:

建立新的IDT:

先使用Hex Editor完全複製原IDT(RAW:76CC~772F),然後覆蓋到IDT的新位置(RAW:7E80)

複製:

覆蓋:

在7ED0處寫入IID

詳解如下圖

修改IAT節區的屬性

三天才整完這一課,中途在使用winhex的時候出了點小差錯弄了很久,只要有耐心,成功之後會非常有成就感的