给PE文件增加可执行代码

最近想写一个软件壳,需要给PE文件增加一个区段(Section),来执行一些初始化的工作。只要先学习一下PE文件的格式和结构,有个大概了解后,我想就能很容易写出相关代码。网上有很多PE格式的介绍,这里就不多介绍,直入主题。

增加的执行代码有两个地方可以放,一个是现有PE文件区段在数据对齐时有一些空隙,这样的空隙一般只能存放比较小块的代码,优点是对原有的PE文件数据改动比较小,大小也不会改变;另一个就是增加一个新的区段,这样可以放任意你想放置的代码,发挥的空间也大一些。本文的方法是先判断是否有足够的空隙来存放代码,如果没有,就增加一个Section来存放。

下面这张图,详细说明这个过程

给PE文件增加可执行代码

相关代码如下,加了简要的注释
给PE文件增加可执行代码给PE文件增加可执行代码

#pragma once

#ifdef UNICODE
typedef  fstream tfstream;
#else
typedef wfstream twfstream;
#endif // UNICODE


class CPEUtil
{
protected:
    vector<char> m_vBuf;

public:
    class CHeader
    {
    public:
        CHeader(char* pBase)
        {
            Reset(pBase);
        }
        ~CHeader()
        {

        }
        void Reset(char* pBase)
        {
            pDosHeader = (IMAGE_DOS_HEADER*)pBase;
            pNTHeader = (IMAGE_NT_HEADERS*)(pBase + pDosHeader->e_lfanew);
            pOp = &pNTHeader->OptionalHeader;
        }

    public:
        IMAGE_DOS_HEADER* pDosHeader;
        IMAGE_NT_HEADERS* pNTHeader;
        IMAGE_OPTIONAL_HEADER* pOp;
    };

public:
    CPEUtil();
    ~CPEUtil();

    bool Load(LPCTSTR szFile);
    bool Save(LPCTSTR szFile);
    bool AddCode(byte* pCode, DWORD dwSize);
    DWORD Rva2Fva(DWORD dwRva);
    DWORD AlignUp(DWORD x, DWORD v);
};

View Code给PE文件增加可执行代码给PE文件增加可执行代码

#include "stdafx.h"
#include "PEUtil.h"
#include <assert.h>

CPEUtil::CPEUtil()
{
}


CPEUtil::~CPEUtil()
{
}

DWORD CPEUtil::AlignUp(DWORD x, DWORD v)
{
    if (x%v == 0)
        return x;
    return x + v - x%v;
}

bool CPEUtil::Load(LPCTSTR szFile)
{
    tfstream f(szFile, ios::in | ios::binary);
    if (!f.is_open())
        return false;
    f.seekg(0, ios::end);
    m_vBuf.assign(f.tellg(), 0);
    f.seekg(0, ios::beg);

    f.read(m_vBuf.data(), m_vBuf.size());

    f.close();

    return true;
}

bool CPEUtil::Save(LPCTSTR szFile)
{
    if (m_vBuf.empty())
        return false;

    tfstream f(szFile, ios::out | ios::binary);

    if (!f.is_open())
        return false;

    f.write(m_vBuf.data(), m_vBuf.size());
    f.close();

    return true;
}

bool CPEUtil::AddCode(byte * pCode, DWORD dwSize)
{
    CHeader h(m_vBuf.data());
    DWORD dwBase = 0;
    auto pSec = IMAGE_FIRST_SECTION(h.pNTHeader);

    if (h.pNTHeader->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
        return false;
    // 找空隙
    for (size_t i = 0; i < h.pNTHeader->FileHeader.NumberOfSections; i++, pSec++)
    {
        if (pSec->SizeOfRawData > 0 &&
            pSec->SizeOfRawData > (pSec->Misc.VirtualSize + dwSize))
        {
            dwBase = pSec->VirtualAddress + pSec->Misc.VirtualSize;
            break;
        }
    }
    if (!dwBase)
    {
        // 空隙不够大,添加一个新的区段
        pSec = IMAGE_FIRST_SECTION(h.pNTHeader) +h.pNTHeader->FileHeader.NumberOfSections;
        memset(pSec, 0, sizeof(pSec));
        pSec->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE;
        strcpy_s((char*)pSec->Name, sizeof(pSec->Name), ".add");

        auto pSecPrev = pSec - 1;
        pSec->SizeOfRawData = AlignUp(dwSize, h.pOp->FileAlignment);
        pSec->PointerToRawData = AlignUp(pSecPrev->PointerToRawData + pSecPrev->SizeOfRawData, h.pOp->FileAlignment);
        pSec->VirtualAddress = AlignUp(pSecPrev->VirtualAddress + max(pSecPrev->SizeOfRawData, pSecPrev->Misc.VirtualSize), h.pOp->SectionAlignment);
        dwBase = pSec->VirtualAddress;
        //
        m_vBuf.insert(m_vBuf.end(), pSec->SizeOfRawData, 0);
        h.Reset(m_vBuf.data());
        pSec = IMAGE_FIRST_SECTION(h.pNTHeader) + h.pNTHeader->FileHeader.NumberOfSections;
        h.pNTHeader->FileHeader.NumberOfSections += 1; // 增加一个区段
        h.pOp->SizeOfImage += AlignUp(pSec->SizeOfRawData, h.pOp->SectionAlignment); // 修改映象文件大小
    }

    byte* pWrite = (byte*)(m_vBuf.data() + Rva2Fva(dwBase));
    DWORD dwJmp = h.pNTHeader->OptionalHeader.AddressOfEntryPoint - dwBase - dwSize;
    memcpy(pWrite, pCode, dwSize-sizeof(DWORD)); // pCode 最后5个字节为 jmp 00000000 (0xe9,0x00,0x00,0x00,0x00),作跳转用
    memcpy(pWrite + dwSize - sizeof(DWORD), &dwJmp, sizeof(DWORD));

    pSec->Misc.VirtualSize += dwSize;
    pSec->Characteristics |= IMAGE_SCN_MEM_EXECUTE;
    h.pNTHeader->OptionalHeader.AddressOfEntryPoint = dwBase;

    return true;
}

DWORD CPEUtil::Rva2Fva(DWORD dwRva)
{
    CHeader h(&m_vBuf[0]);
    auto pSec = IMAGE_FIRST_SECTION(h.pNTHeader);

    pSec += h.pNTHeader->FileHeader.NumberOfSections-1;

    assert(dwRva <= (pSec->VirtualAddress + pSec->SizeOfRawData));

    for (size_t i = 0; i < h.pNTHeader->FileHeader.NumberOfSections ; i++, pSec--)
    {
        if (dwRva >= pSec->VirtualAddress)
            return pSec->PointerToRawData + dwRva - pSec->VirtualAddress;
    }

    return 0;
}

PEUtil.cpp
测试用例:

PETest.rar(加载一个dll,并调用导出函数)
原文链接: https://www.cnblogs.com/qyy1415/p/pe_add_code.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/227562

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 下午1:38
下一篇 2023年2月13日 下午1:38

相关推荐