Load DLL by modifying PE file

target

The goal of this experiment is to modify the TextView directly. Exe file to automatically load myhack at run time. DLL file.

TextView.exe

TextView.exe is a very simple text viewer that allows you to view the contents of a text file by dragging it (Drop) with the mouse.

View TextView using the PEView tool. IDT of exe executable (Import directory table, Import Directory table).

As you can see from the image above, TextView. The DLL file imported directly from exe is KERNEL32.dll, USER32.dll, GDI32.dll, SHELL32.dll.

myhack32.dll source code

This section is used to analyze myhack3.dll source code.

// dllmain.cpp: Defines the entry point for the DLL application.
#include "pch.h"
#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.baidu.com/index.html"
#define DEF_INDEX_FILE L"index.html"
HWND g_hWnd = NULL;
 
 
#ifdef __cplusplus
extern "C" {
#endif
    //  Export functions, but without any functionality, just keep the dll file intact in form.
    __declspec(dllexport) void dummy()
    {
        return;
    }
#ifdef __cplusplus
}
#endif
 
 
//Download URL downloads the files of the specified Web site in the szURL and saves them in the szFile directory.
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;
}
 
 
//The index that the DropFile function will download. Drag HTML file to TextView_Path.exe process and display its contents.
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 APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL));
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

DllMain()

#include "pch.h"
#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 APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        CloseHandle(CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL));
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

The DLLMain() function is very simple in that it creates a thread to run the specified thread process (ThreadProc) and calls the Download URL () and DropFile() functions.

DownloadURL()

//Download URL downloads the files of the specified Web site in the szURL and saves them in the szFile directory.
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;
}

The Download URL () function downloads the web page file specified in the parameter szURL and saves it to the szFile directory.

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;
}
 
 
//The index that the DropFile function will download. Drag HTML file to TextView_Path.exe process and display its contents.
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;
}

The DropFile() function will download the index. Drag HTML file to TextView_Patch.exe process and display its contents.

dummy()

#ifdef __cplusplus
extern "C" {
#endif
    //  Export functions, but without any functionality, just keep the dll file intact in form.
    __declspec(dllexport) void dummy()
    {
        return;
    }
#ifdef __cplusplus
}
#endif

dummy() exports the function, but does not have any function, just keeps the dll file intact in form.

Importing a DLL into a PE file essentially calls the export function provided by the DLL within the file code. The PE file header records information such as DLL name, function name, and so on. Therefore, myhack3.dll needs to provide at least one more export function outward to maintain formal integrity.

Modify TextView. Preparation of EXE file

Modify ideas

The dll information imported in the PE file is stored in the IDT as a list of structures. Important will be myhack3. Add dll to the end of the list. Of course, you need to confirm before that there is enough space in the IDT.

Check if IDT has enough space

First, use PEView to view TextView. IDT address of exe (the RVA value of the imported table in the IMAGE_OPTIONAL_HEADER structure of the PE header is the RVA value of the IDT).

As you can see from the figure below, the address of the IDT (RVA) is 84CC. Next, look directly at the IDT in PEView (in PEView)
Set the address view option to RVA in the toolbar.

From the image above, you can see that TextView. The IDT of exe exists with. rdata section. IDT is made by IMAGE_ IMPORT_ An array of DESCRIPTOR (hereinafter referred to as IID) structs ending with a NULL struct. Since each imported DLL file corresponds to an IID structure (each IID structure is 0*14 bytes in size), the entire IID region in the following figure is RVA:84CC~852F (overall size is 0*14*5 = 0*64 bytes)

Definition of IID Structures

IMAGE_IMPORT_DESCRIPTOR
 
typedef struct _IMAGE_IMPORT_DESCRIPTOR
{
    union{
        DWORD Characteristics;
        DWOED OriginalFirstThunk;   //RVA to INT(Import Name Table)
    };                         
    DWORD   TimeDataStamp;
    DWORD   ForwarderChain;        
    DWORD   Name;                   //RVA to DLL Name String   
    DWORD   FirstThunk;             //RVA to IAT(Import Address Table)
}IMAGE_IMPORT_DESCRIPTOR;

Change the view to File Offset in the PEView toolbar, and you can see that the file offset for IDT is 76CC, as shown in the following figure

Use HxD to open the TextView file with the tool and find the 76CC address, as shown in the following figure

The file offset for IDT is 76CC ~ 772F, the entire size is 64 bytes, there are five IID structures, the last of which is NULL
Structures. As you can see from the diagram, there is additional data at the end of the IDT and there is not enough room to add myhack3. IID structure of dll.

Mobile IDT

In this case, we first need to move the entire IDT to a wider location. Then add a new IID. There are three ways to determine a moving target location:

  • Find large white space in a file
  • Increase the size of the last section of the file
  • Add a new section at the end of the file

First, look for blank areas in the file (areas not used when the program is running). As shown in the following figure, there is a large amount of blank areas at the end of the.rdata section (generally, there are blank areas at the end of the section or file, which are called Null-Padding areas in PE files).

Next, you need to move the original IDT to the appropriate location in the Null-Padding area (RVA:8C60~8DFF). Before doing so, you need to confirm that the area (RVA:8C60~8DFF) is a blank area (Null-Padding area).

It is important to note that not all regions in the file are loaded unconditionally into the process's virtual memory, but only regions explicitly recorded in the section header are loaded. View TextView using the PEView tool. Exe file. rdata section header, as follows:

Section headers store information about the location, size, attributes of corresponding sections. Arrangement. The information in the rdata section header is shown in the following table

You can see from the section header that. The rdata section size is different between disk file and memory.

. The rdata section is 2E00 in size in the disk file, and when the file is loaded into memory after execution, the actual data size (map size) used by the program is only 2C56, while the remaining unused area is 1AA (2E00 - 2C56). There is no problem creating IDT s in this empty area.

white space

RVA: 8C56 - 8E00 --> RAW : 7E56 - 8000

So the Null-Padding area in the figure below is available, so we'll create the IDT at RVA:8C80 (RAW:7E80).

Modify TextView.exe

First put TextView.exe copied to working folder, renamed TextView_Patch.exe. Use TextView_below Patch. Exe file to practice patching. The basic procedure is to first open TextView using PEView. Exe original file, view various PE information, and then use HxD to open TextView_Patch file to modify.

Modify the RVA value of the imported table

IMAGE_ OPTIONAL_ The import table structure members of HEADERD are used to indicate the location (RVA) and size of the IDT as follows

TextView. In the EXE file, the RVA value of the import table is 84CC. Next, change the RVA value of the imported table to 8C80 for the new IDT, and add 14 bytes (the size of the IID structure) to the original 64 bytes of the Size, as shown below.

From now on, the import table is located at the RVA: 8C80 (RAW: 7E80) address.

Delete Import Table

BOUND IMPORT TABLE (Binding Export Table) is a technique for increasing the speed of DLL loading as follows

If myhack3 is imported to normal. Dll, you need to add information to the binding import table. Fortunately, the binding import table is an optional option and does not have to exist. So you can delete it (Modify its value to 0) for greater convenience. Of course, it doesn't matter if the bound import table doesn't exist at all, but if it does exist and there are errors in its internal information records, errors will be raised when the program runs. In this example TextView.exe file, all values of the bound import table are 0 and need not be modified. When modifying the file, be careful to check the bound import table Data.

Create a new IDT

First use Hex Editor to completely copy the original IDT (RAW:76CC~772F), then overwrite (Past write) to the new location of the IDT (RAW:7E80), as shown in the following figure

Then add the IID corresponding to myhack3.dll at the end of the new IDT (RAW:7ED0) (the data for each member will be explained separately later)

//IMAGE_IMPORT_DESCRIPTOR
typedef struct _IMAGE_IMPORT_DESCRIPTOR{
    union{
        DWORD Characteristics;
        DWORD OriginalFirstThunk;   //00008D00  => RVA to INT
    };
    DWORD   TimeDataStamp;          //0
    DWORD   ForwarderChain;         //0
    DWORD   Name;                   //00008D10  => RVA to DLL Name
    DWORD   FirstThunk;             //00008D20  => RVA to IAT
} IMAGE_IMPORT_DESCRIPTOR;

Write relevant data in the exact location (RAW:7ED0), as shown below

Set Name, INT, IAT

The previously added members of the IID structure have RVA values pointing to other data structures (INT, Name, IAT). Therefore, these data structures must be set accurately to ensure the proper functioning of the TextView_Patch.exe file. The values of INT, Name, IAT's RVA/RAW are summarized as follows

These addresses (RVA:8D00, 8D10, 8D20) are located just below the newly created IDT (RVA:8C80). Of course, you can also select other eligible regions. Go to the 7F00 address in the HxD editor and enter the corresponding values as shown in the following figure

To better understand the above, use PEView to open TextView_ Patch. The EXE file looks at the same area, using the RVA view as shown below

Myhack3 is present at the 8CD0 address. The structure of the dll, where the values of three primary members (RVA of INT, RVA of Name, RVA of IAT) are the actual INT, Name, and IAT pointers, respectively.

Simply put, INT (Import Name Table) is an RVA array. Each element of the array is an RVA address, which consists of the Ordinal (2 bytes) + Func Name String structure of the import function and ends with NULL. In the figure above, INT has an element with a value of 8D30, which is the Ordinal address of the function to be imported. (2 bytes) and the name string of the function ("dummy").

Name is the DLL file name string that contains the import function and myhack3 is visible at the 8D10 address. DLL string.

IAT is also an RVA array, and each element can have either the same value as INT or other values that are not used (if the data in INT is accurate, IAT can have different values as well). When actually running, the PE loader replaces the IAT in virtual memory with the address of the actual function

Modify IAT Section Attribute Value

When loading PE files into memory, the PE loader modifies the IAT to write to the actual address of the function, so the section must have a WRITE property. Only then can the PE loader write properly. Use PEView to view the.rdata section header, as shown below

Add the IMAGE_SCN_MEM_WRITE(80000000) attribute value to the original attribute (ChAracteristics) 40000040, perform a bit OR operation, and the final attribute value changes to C0000040, as shown in the following figure

Detection Verification

First use the PEView tool to open the modified TextView_Patch.exe file, see its IDT, as shown below

Import myhack3 into IDT. The IID structure of the DLL is set properly. Myhack3. The dummy() function of the DLL is added to the INT.

Successfully modified from the file structure, and then run the file to see if the program works properly. Put TextView_Patch.exe and myhack3.dll into the same folder and run TextView_Patch.exe file, the result is as follows:

You can see TextView_Patch.exe successfully loaded myhack.dll file and downloaded index for the specified site. HTML file, and in TextView_ Patch. Displayed in exe.

 

Keywords: security Cyber Security

Added by youqing on Fri, 24 Dec 2021 15:03:12 +0200