逆向 | 导入表注入代码示例

逆向 | 导入表注入代码示例

还是在编写书中的实验代码,冗余部分比较多可以优化,实验对象是32位pe文件。

// IID_Loader.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <stdio.h>
#include <Windows.h>
#include <string.h>

VOID hex_print(UCHAR* p, DWORD size) {
    DWORD i;
    printf("\n--------%p--------\n", p);
    for (i = 0; i < size; i++) {
        printf("0x%02X ", p[i]);
        if ((i + 1) % 16 == 0) {
            printf("\n");
        }
    }
    printf("\n----------------\n");
}

// 返回新section的FOA
DWORD64 add_section(WCHAR* pefilename, DWORD section_size) {
    // 映射PE文件进内存
    printf("add section: [%ls] \n", pefilename);
    HANDLE fp = CreateFile(pefilename,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);
    if (fp == NULL) {
        printf("err CreateFile \n");
        return NULL;
    }

    DWORD dwBytesInBlock = GetFileSize(fp, NULL); //文件长度
    printf(" > file size: %x \n", dwBytesInBlock);

    HANDLE hFileMapping = CreateFileMapping(fp,
        NULL,
        PAGE_READWRITE,
        0,//(DWORD)(dwBytesInBlock >> 16),
        dwBytesInBlock,//(DWORD)(dwBytesInBlock & 0x0000FFFF),
        NULL);

    int dwError = GetLastError();

    // 偏移地址
    DWORD64 qwFileOffset = 0;

    // 将文件数据映射到进程的地址空间
    LPVOID pbFile = (LPVOID)MapViewOfFile(hFileMapping,
        FILE_MAP_ALL_ACCESS,
        (DWORD)(qwFileOffset >> 32),
        (DWORD)(qwFileOffset & 0xFFFFFFFF),
        dwBytesInBlock);

    printf(" > map: %p \n", pbFile);


    // 检索PE文件信息
    if (((PIMAGE_DOS_HEADER)pbFile)->e_magic != IMAGE_DOS_SIGNATURE) {
        printf(" > IMAGE_DOS_SIGNATURE check fail \n");
        hex_print((UCHAR*)pbFile, 16);
        return NULL;
    }

    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD64)pbFile + ((PIMAGE_DOS_HEADER)pbFile)->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
        printf(" > IMAGE_NT_SIGNATURE check fail \n");
        hex_print((UCHAR*)pNtHeader, 16);
        return NULL;
    }

    // 判断是否有足够的空间增加一个节表项
    printf(" > NumberOfSections: %d \n", pNtHeader->FileHeader.NumberOfSections);
    printf(" > SizeOfHeaders: 0x%x \n", pNtHeader->OptionalHeader.SizeOfHeaders);
    if ((pNtHeader->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER) >
        (pNtHeader->OptionalHeader.SizeOfHeaders)) {
        printf("not enough space for a new section header! \n");
        return NULL;
    }

    // 获取新增节标项的内存位置和文件偏移
    PIMAGE_SECTION_HEADER pNewSection = (PIMAGE_SECTION_HEADER)((DWORD64)pNtHeader + 24/*标准PE头size*/ +
        pNtHeader->FileHeader.SizeOfOptionalHeader + pNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
    printf("> new section position: 0x%x \n", (DWORD64)pNewSection - (DWORD64)pbFile);

    PIMAGE_SECTION_HEADER pLastSection = (PIMAGE_SECTION_HEADER)((DWORD64)pNewSection - 40/*节表项的大小*/);

    // 新增一个节表项
    memcpy(pNewSection->Name, ".mz", strlen(".mz"));
    pNewSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA; // 设置节属性为可读可写
    pNewSection->PointerToRawData = pLastSection->PointerToRawData + pLastSection->SizeOfRawData;// 文件中的节偏移FOA(从最后一个节标项目获取到新增节的文件偏移)
    pNewSection->SizeOfRawData = section_size;    // 文件中的节大小
    pNewSection->Misc.VirtualSize = section_size;    // 在内存中的大小
    DWORD64 mem_size_last_section = 0;
    if (pLastSection->Misc.VirtualSize > pLastSection->SizeOfRawData) {
        if (pLastSection->Misc.VirtualSize % 0x1000 != 0) {
            mem_size_last_section = (pLastSection->Misc.VirtualSize / 0x1000/*内存对齐大小*/) * 0x1000 + 0x1000;
        }
        mem_size_last_section = pLastSection->Misc.VirtualSize;
    }
    else {
        mem_size_last_section = pLastSection->SizeOfRawData;
    }
    pNewSection->VirtualAddress = pLastSection->VirtualAddress + mem_size_last_section;
    printf("  > PointerToRawData: %p \n", pNewSection->PointerToRawData);
    printf("  > SizeOfRawData: %p \n", pNewSection->SizeOfRawData);
    printf("  > VirtualAddress: %p \n", pNewSection->VirtualAddress);
    printf("  > VirtualSize: %p \n", pNewSection->Misc.VirtualSize);

    // 输出新的节表项目
    hex_print((UCHAR*)pNewSection, 40);

    // 初始化节数据
    VOID* buf = malloc(section_size);
    DWORD wsize = 0;
    memset(buf, 0, section_size);
    SetFilePointer(fp, pNewSection->PointerToRawData, NULL, FILE_BEGIN);
    WriteFile(fp, buf, section_size, &wsize, NULL);

    // 更新PE头中接的数量和整个imagesize的大小
    pNtHeader->FileHeader.NumberOfSections += 1;
    pNtHeader->OptionalHeader.SizeOfImage = pNewSection->VirtualAddress + pNewSection->Misc.VirtualSize;

    DWORD64 ret = pNewSection->PointerToRawData;

    // 释放资源
    free(buf);
    UnmapViewOfFile(pbFile);
    CloseHandle(hFileMapping);
    CloseHandle(fp);
    return ret;
}


DWORD64 RVA2FOA(PIMAGE_NT_HEADERS32 pNtHeader, DWORD64 dwRva) {
    DWORD64 dwFoa;
    DWORD dwNumberOfSections = pNtHeader->FileHeader.NumberOfSections;
    PIMAGE_SECTION_HEADER pImageSectionHeaders = (PIMAGE_SECTION_HEADER)((DWORD64)pNtHeader + 24/*标准PE头size*/ +
        pNtHeader->FileHeader.SizeOfOptionalHeader);    // 指向节表开头
    for (DWORD i = 0; i < dwNumberOfSections; i++) {
        // 输出节表的rva和size
        printf("    > [%s] RVA: %p - size: %p \n", (pImageSectionHeaders + i)->Name, (pImageSectionHeaders + i)->VirtualAddress, (pImageSectionHeaders + i)->Misc.VirtualSize);
        // 判断这个地址属不属于这个节
        if (dwRva >= (pImageSectionHeaders + i)->VirtualAddress && dwRva < (pImageSectionHeaders + i)->VirtualAddress + (pImageSectionHeaders + i)->Misc.VirtualSize) {
            printf("   >>> 属于这个section! \n");
            // 计算FOA
            dwFoa = (pImageSectionHeaders + i)->PointerToRawData + (dwRva - (pImageSectionHeaders + i)->VirtualAddress);
            break;
        }
    }
    return dwFoa;
}

DWORD64 FOA2RVA(PIMAGE_NT_HEADERS32 pNtHeader, DWORD64 dwFoa) {
    DWORD64 dwRva;
    DWORD dwNumberOfSections = pNtHeader->FileHeader.NumberOfSections;
    PIMAGE_SECTION_HEADER pImageSectionHeaders = (PIMAGE_SECTION_HEADER)((DWORD64)pNtHeader + 24/*标准PE头size*/ +
        pNtHeader->FileHeader.SizeOfOptionalHeader);    // 指向节表开头
    for (DWORD i = 0; i < dwNumberOfSections; i++) {
        // 输出节表的foa和size
        printf("    > [%s] foa: %p - size: %p \n", (pImageSectionHeaders + i)->Name, (pImageSectionHeaders + i)->PointerToRawData, (pImageSectionHeaders + i)->SizeOfRawData);
        // 判断这个地址属不属于这个节
        if (dwFoa >= (pImageSectionHeaders + i)->PointerToRawData && dwFoa < (pImageSectionHeaders + i)->PointerToRawData + (pImageSectionHeaders + i)->SizeOfRawData) {
            printf("   >>> 属于这个section! \n");
            // 计算FOA
            dwRva = (pImageSectionHeaders + i)->VirtualAddress + (dwFoa - (pImageSectionHeaders + i)->PointerToRawData);
            break;
        }
    }
    return dwRva;
}

VOID cpy_iid(WCHAR* pefilename, DWORD64 new_space) {
    // 映射PE文件进内存
    printf("cpy_iid: [%ls] \n", pefilename);
    HANDLE fp = CreateFile(pefilename,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);
    if (fp == NULL) {
        printf("err CreateFile \n");
        return;
    }

    DWORD dwBytesInBlock = GetFileSize(fp, NULL); //文件长度
    printf(" > file size: %x \n", dwBytesInBlock);

    HANDLE hFileMapping = CreateFileMapping(fp,
        NULL,
        PAGE_READWRITE,
        0,//(DWORD)(dwBytesInBlock >> 16),
        dwBytesInBlock,//(DWORD)(dwBytesInBlock & 0x0000FFFF),
        NULL);

    int dwError = GetLastError();

    // 偏移地址
    DWORD64 qwFileOffset = 0;

    // 将文件数据映射到进程的地址空间
    LPVOID pbFile = (LPVOID)MapViewOfFile(hFileMapping,
        FILE_MAP_ALL_ACCESS,
        (DWORD)(qwFileOffset >> 32),
        (DWORD)(qwFileOffset & 0xFFFFFFFF),
        dwBytesInBlock);

    printf(" > map: %p \n", pbFile);


    // 检索PE文件信息
    if (((PIMAGE_DOS_HEADER)pbFile)->e_magic != IMAGE_DOS_SIGNATURE) {
        printf(" > IMAGE_DOS_SIGNATURE check fail \n");
        hex_print((UCHAR*)pbFile, 16);
        return;
    }
    // 这里要根据pe文件的位数选择相应的结构体,这里例子中使用的是32位的版本
    PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD64)pbFile + ((PIMAGE_DOS_HEADER)pbFile)->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
        printf(" > IMAGE_NT_SIGNATURE check fail \n");
        hex_print((UCHAR*)pNtHeader, 16);
        return;
    }
    // 检索导入表信息
    IMAGE_DATA_DIRECTORY IDEI =  pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    DWORD IID_size = IDEI.Size;
    printf("  > IMAGE_DIRECTORY_ENTRY_IMPORT: %p - size: %p \n", IDEI.VirtualAddress, IDEI.Size);
    hex_print((UCHAR*) & pNtHeader->OptionalHeader.DataDirectory, 16);
    // 找到IID数组 这里需要RVA转FOA
    DWORD64 dwRva = IDEI.VirtualAddress;
    DWORD64 dwFoa = RVA2FOA(pNtHeader, dwRva);
    printf("  > FOA: %p \n", dwFoa);
    ////////////////

    PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD64)pbFile + dwFoa);
    printf("%p \n", pIID);
    // 计算导入表条目数量
    DWORD IID_num = IID_size / sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1;
    printf("  > 有%d张导入表 \n", IID_num);

    // 迁移导入表
    DWORD64 new_IID = new_space + (DWORD64)pbFile;
    printf("  > cpy导入表到: %p \n", new_IID);
    memcpy((VOID*)(new_IID), pIID, IID_size);

    // 修改导入表指针和大小
    dwFoa = new_space;
    dwRva = FOA2RVA(pNtHeader,dwRva);    
    printf("  > RVA: %p \n", dwRva);
    ////////////////
    pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = dwRva;
    // size暂时不需要修改

    // 释放资源
    UnmapViewOfFile(pbFile);
    CloseHandle(hFileMapping);
    CloseHandle(fp);
}

VOID add_iid(WCHAR* pefilename, DWORD64 new_space,CHAR* dllname, CHAR* dllexport_function) {
    printf("add_iid: [%s] \n", dllname);
    HANDLE fp = CreateFile(pefilename,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);
    if (fp == NULL) {
        printf("err CreateFile \n");
        return;
    }

    DWORD dwBytesInBlock = GetFileSize(fp, NULL); //文件长度
    printf(" > file size: %x \n", dwBytesInBlock);

    HANDLE hFileMapping = CreateFileMapping(fp,
        NULL,
        PAGE_READWRITE,
        0,//(DWORD)(dwBytesInBlock >> 16),
        dwBytesInBlock,//(DWORD)(dwBytesInBlock & 0x0000FFFF),
        NULL);

    int dwError = GetLastError();

    // 偏移地址
    DWORD64 qwFileOffset = 0;

    // 将文件数据映射到进程的地址空间
    LPVOID pbFile = (LPVOID)MapViewOfFile(hFileMapping,
        FILE_MAP_ALL_ACCESS,
        (DWORD)(qwFileOffset >> 32),
        (DWORD)(qwFileOffset & 0xFFFFFFFF),
        dwBytesInBlock);

    printf(" > map: %p \n", pbFile);


    // 检索PE文件信息
    if (((PIMAGE_DOS_HEADER)pbFile)->e_magic != IMAGE_DOS_SIGNATURE) {
        printf(" > IMAGE_DOS_SIGNATURE check fail \n");
        hex_print((UCHAR*)pbFile, 16);
        return;
    }
    // 这里要根据pe文件的位数选择相应的结构体,这里例子中使用的是32位的版本
    PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD64)pbFile + ((PIMAGE_DOS_HEADER)pbFile)->e_lfanew);
    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
        printf(" > IMAGE_NT_SIGNATURE check fail \n");
        hex_print((UCHAR*)pNtHeader, 16);
        return;
    }

    //////////////
    // 新增导入表项目
    // 检索导入表信息
    IMAGE_DATA_DIRECTORY IDEI = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    DWORD IID_size = IDEI.Size;
    printf("  > IMAGE_DIRECTORY_ENTRY_IMPORT: %p - size: %p \n", IDEI.VirtualAddress, IDEI.Size);
    hex_print((UCHAR*)&pNtHeader->OptionalHeader.DataDirectory, 16);

    // 找到IID数组 这里需要RVA转FOA
    DWORD64 dwRva = IDEI.VirtualAddress;
    DWORD64 dwFoa = RVA2FOA(pNtHeader, dwRva);
    printf("  > FOA: %p \n", dwFoa);
    ////////////////
    IDEI.Size += sizeof(IMAGE_IMPORT_DESCRIPTOR); // 增加size

    // 添加条目,这里使用自己定义的结构体将这些数据结构串起来
    typedef struct _MzIID {
        IMAGE_IMPORT_DESCRIPTOR IID[2];   // 第一个用于存放新导入表,第二个用于置0
        CHAR dllname[16];
        IMAGE_THUNK_DATA32 INT_ITD[2];    // INT: 第一个指向IMAGE_IMPORT_BY_NAME 第二个置空
        IMAGE_THUNK_DATA32 IAT_ITD[2];    // IAT: 第一个指向IMAGE_IMPORT_BY_NAME 第二个置空
        /*IMAGE_IMPORT_BY_NAME组装成一个字节数组*/
        CHAR HintName[32];
    }MzIID;

    DWORD64 mzIID_FOA = dwFoa + 
        sizeof(IMAGE_IMPORT_DESCRIPTOR)*
        (IID_size/ sizeof(IMAGE_IMPORT_DESCRIPTOR) - 1);
    DWORD64 mzIID_RVA = FOA2RVA(pNtHeader, mzIID_FOA);
    printf("  > 插入新导入表位置FOA: %p - RVA: %p \n", mzIID_FOA, mzIID_RVA);
    MzIID mzIID;
    memset(&mzIID, 0, sizeof(MzIID));
    memcpy(mzIID.dllname, dllname, strlen(dllname));
    mzIID.IID[0].Name = mzIID_RVA + ((DWORD)&mzIID.dllname - (DWORD)&mzIID);
    printf("  > mzIID.IID[0].Name: %p \n", mzIID.IID[0].Name);
    mzIID.IID[0].TimeDateStamp = 0;
    mzIID.IID[0].ForwarderChain = 0;
    mzIID.IID[0].FirstThunk = mzIID_RVA + ((DWORD)&mzIID.IAT_ITD -(DWORD)&mzIID);
    mzIID.IID[0].OriginalFirstThunk = mzIID_RVA + ((DWORD)&mzIID.INT_ITD - (DWORD)&mzIID);
    printf("  > OriginalFirstThunk: %p - FirstThunk: %p \n",
        mzIID.IID[0].OriginalFirstThunk,
        mzIID.IID[0].FirstThunk);
    mzIID.HintName[0] = 0;
    mzIID.HintName[1] = 0;
    memcpy(&mzIID.HintName[2], dllexport_function, strlen(dllexport_function));
    mzIID.IAT_ITD[0].u1.AddressOfData = mzIID_RVA + ((DWORD)&mzIID.HintName - (DWORD)&mzIID);
    mzIID.INT_ITD[0].u1.AddressOfData = mzIID_RVA + ((DWORD)&mzIID.HintName - (DWORD)&mzIID);
    printf("  > INT_ITD[0].u1.AddressOfData: %p \n", mzIID.INT_ITD[0].u1.AddressOfData);
    // 将mzIID打进pe文件中
    memcpy((VOID*)(mzIID_FOA + (DWORD64)pbFile), &mzIID, sizeof(MzIID));
    printf("> over. \n");

    // 释放资源
    UnmapViewOfFile(pbFile);
    CloseHandle(hFileMapping);
    CloseHandle(fp);
}

int main()
{
    printf("> IID Loader(会覆盖输入文件,需要自行备份) \n");
    WCHAR fname[] = L"print.exe";
    /*
       1. 新增节
       2. 迁移导入表
       3. 新增导入项
    */
    DWORD64 new_section_foa = add_section(fname, 0x1000);
    if (new_section_foa == NULL) {
        printf("err \n");
        return -1;
    }
    cpy_iid(fname, new_section_foa);
    CHAR dllname[] = "bad.dll";
    CHAR dllexport_function[] = "_hi@0";
    add_iid(fname, new_section_foa, dllname, dllexport_function);
    return 0;
}


posted @ 2024-07-17 10:45  Mz1  阅读(21)  评论(0编辑  收藏  举报