PE(Portable Executable)文件是Windows操作系统下可执行文件的标准格式,其设计目标是支持跨平台的可执行代码和动态链接。要解析或操作PE文件,需深入理解其二进制结构和运行时加载机制。
PE文件以.exe
、.dll
等为扩展名,其物理组织遵循以下层次:
IMAGE_NT_HEADERS
),包含文件的基本信息(如入口地址、段表位置)。.text
)、数据段(.data
)、资源段(.rsrc
)等逻辑分区的集合,每个节区有名称、大小、权限(如可读/可写/可执行)。静态解析指不加载文件到内存直接分析其二进制数据:
e_magic
字段(0x5A4D
)和PE头的Signature
(0x4550
)确认文件类型。IMAGE_SECTION_HEADER
结构,获取各节区的名称、虚拟地址(VA)、物理偏移量(File Offset)及内存属性(如IMAGE_SCN_MEM_EXECUTE
)。动态解析需模拟操作系统加载PE文件的过程:
.text
)插入机器码。需确保注入后的代码段属性(如可执行权限)正确。.rsrc
节区的资源数据(如图标、字符串),或删除不需要的资源以减小文件体积。VirtualProtectEx
修改内存页的访问权限(如将数据页设为可写)。IMAGE_DIRECTORY_ENTRY_SECURITY
中的数字签名,验证证书链和哈希值。CAT
目录和SIGNATURE
节区,需调整PE头的NumberOfSections
字段以保持文件结构完整。IMAGE_FILE_MACHINE_AMD64
)。OpenProcess
获取目标进程的访问权限,并分配注入代码的内存空间。CreateProcess
等关键系统调用的函数指针,监控程序启动行为。随着Windows安全机制的增强(如DEP、CFG),PE文件操作面临更多限制:
...
namespace mc_pe
{
constexpr static int MAX_DIRECTORY_COUNT = 16;
template<unsigned int>
class Image;
template<unsigned int>
class OptionalHeader;
class SectionHeader;
class FileHeader;
template<unsigned int bitsize = 32>
class PEHeader : pepp::msc::NonCopyable
{
friend class Image<bitsize>;
using ImageData_t = detail::Image_t<bitsize>;
Image<bitsize>* m_image;
ImageData_t::Header_t* m_PEHdr = nullptr;
FileHeader m_FileHeader;
OptionalHeader<bitsize> m_OptionalHeader;
private:
PEHeader();
public:
class FileHeader& getFileHdr() {
return m_FileHeader;
}
const class FileHeader& getFileHdr() const {
return m_FileHeader;
}
class OptionalHeader<bitsize>& getOptionalHdr() {
return m_OptionalHeader;
}
const class OptionalHeader<bitsize>& getOptionalHdr() const {
return m_OptionalHeader;
}
SectionHeader& getSectionHeader(std::uint16_t dwIndex) {
static SectionHeader dummy{};
if (dwIndex < m_image->getNumberOfSections())
return m_image->m_rawSectionHeaders[dwIndex];
return dummy;
}
SectionHeader& getSectionHeader(std::string_view name) {
static SectionHeader dummy{};
for (std::uint16_t n = 0; n < m_image->getNumberOfSections(); n++)
{
if (m_image->m_rawSectionHeaders[n].getName().compare(name) == 0) {
return m_image->m_rawSectionHeaders[n];
}
}
return dummy;
}
SectionHeader& getSectionHeaderFromVa(std::uint32_t va) {
static SectionHeader dummy{};
for (std::uint16_t n = 0; n < m_image->getNumberOfSections(); n++)
{
if (m_image->m_rawSectionHeaders[n].hasVirtualAddress(va)) {
return m_image->m_rawSectionHeaders[n];
}
}
return dummy;
}
SectionHeader& getSectionHeaderFromOffset(std::uint32_t offset) {
static SectionHeader dummy{};
for (std::uint16_t n = 0; n < m_image->getNumberOfSections(); n++)
{
if (m_image->m_rawSectionHeaders[n].hasOffset(offset)) {
return m_image->m_rawSectionHeaders[n];
}
}
return dummy;
}
std::uint32_t getDirectoryCount() const {
return getOptionalHdr().getDirectoryCount();
}
//将相对虚拟地址转换为文件偏移量
std::uint32_t rvaToOffset(std::uint32_t rva) {
SectionHeader const& sec { getSectionHeaderFromVa(rva) };
//
// Did we get one?
if (sec.getName() != ".dummy") {
return sec.getPtrToRawData() + rva - sec.getVirtualAddress();
}
return 0ul;
}
//! 将文件偏移量转换回相对虚拟地址
std::uint32_t offsetToRva(std::uint32_t offset) {
SectionHeader const& sec{ getSectionHeaderFromOffset(offset) };
//
// Did we get one?
if (sec.getName() != ".dummy") {
return (sec.getVirtualAddress() + offset) - sec.getPtrToRawData();
}
return 0ul;
}
// 将相对虚拟地址转换为虚拟地址
detail::Image_t<bitsize>::Address_t rvaToVa(std::uint32_t rva) const {
return m_OptionalHeader.getImageBase() + rva;
}
// 用于检查NT标签是否存在
bool isTaggedPE() const {
return m_PEHdr->Signature == IMAGE_NT_SIGNATURE;
}
std::uint8_t* base() const {
return (std::uint8_t*)m_PEHdr;
}
constexpr std::size_t size() const {
return sizeof(decltype(*m_PEHdr));
}
// 返回本机指针
detail::Image_t<bitsize>::Header_t* native() {
return m_PEHdr;
}
// 手动计算图像的大小
std::uint32_t calcSizeOfImage();
// 手动计算代码段的起始位置
std::uint32_t getStartOfCode();
// 计算下一节偏移量
std::uint32_t getNextSectionOffset();
std::uint32_t getNextSectionRva();
private:
// 设置标题
void _setup(Image<bitsize>* image) {
m_image = image;
m_PEHdr = reinterpret_cast<decltype(m_PEHdr)>(m_image->base() + m_image->m_MZHeader->e_lfanew);
m_FileHeader._setup(image);
m_OptionalHeader._setup(image);
}
};
}
...
namespace mc_pe
{
struct ExportData_t
{
std::string name{};
std::uint32_t rva = 0;
std::uint32_t base_ordinal = 0xffffffff;
std::uint32_t name_ordinal = 0xffffffff;
};
template<unsigned int bitsize>
class ExportDirectory : public pepp::msc::NonCopyable
{
friend class Image<32>;
friend class Image<64>;
Image<bitsize>* m_image;
detail::Image_t<>::ExportDirectory_t *m_base;
public:
ExportData_t getExport(std::uint32_t idx, bool demangle = true) const;
ExportData_t getExport(std::string_view name, bool demangle = true) const;
void add(std::string_view name, std::uint32_t rva);
void traverseExports(const std::function<void(ExportData_t*)>& cb_func, bool demangle = true);
bool isPresent() const noexcept;
void setNumberOfFunctions(std::uint32_t num) {
m_base->NumberOfFunctions = num;
}
std::uint32_t getNumberOfFunctions() const {
return m_base->NumberOfFunctions;
}
void setNumberOfNames(std::uint32_t num) {
m_base->NumberOfNames = num;
}
std::uint32_t getNumberOfNames() const {
return m_base->NumberOfNames;
}
void setCharacteristics(std::uint32_t chrs) {
m_base->Characteristics = chrs;
}
std::uint32_t getCharacteristics() const {
return m_base->Characteristics;
}
void setTimeDateStamp(std::uint32_t TimeDateStamp) {
m_base->TimeDateStamp = TimeDateStamp;
}
std::uint32_t getTimeDateStamp() const {
return m_base->TimeDateStamp;
}
void setAddressOfFunctions(std::uint32_t AddressOfFunctions) {
m_base->AddressOfFunctions = AddressOfFunctions;
}
std::uint32_t getAddressOfFunctions() const {
return m_base->AddressOfFunctions;
}
void setAddressOfNames(std::uint32_t AddressOfNames) {
m_base->AddressOfNames = AddressOfNames;
}
std::uint32_t getAddressOfNames() const {
return m_base->AddressOfNames;
}
void setAddressOfNameOrdinals(std::uint32_t AddressOfNamesOrdinals) {
m_base->AddressOfNameOrdinals = AddressOfNamesOrdinals;
}
std::uint32_t getAddressOfNameOrdinals() const {
return m_base->AddressOfNameOrdinals;
}
constexpr std::size_t size() const {
return sizeof(decltype(*m_base));
}
private:
//设置目录
void _setup(Image<bitsize>* image) {
m_image = image;
m_base = reinterpret_cast<decltype(m_base)>(
&image->base()[image->getPEHdr().rvaToOffset(
image->getPEHdr().getOptionalHdr().getDataDir(DIRECTORY_ENTRY_EXPORT).VirtualAddress)]);
}
};
}
...
namespace mc_pe
{
enum class PEMachine
{
MACHINE_I386 = 0x14c,
MACHINE_IA64 = 0x200,
MACHINE_AMD64 = 0x8664
};
class FileHeader : pepp::msc::NonCopyable
{
friend class PEHeader<32>;
friend class PEHeader<64>;
IMAGE_FILE_HEADER* m_base;
public:
FileHeader()
{
}
void setMachine(PEMachine machine) {
m_base->Machine = static_cast<std::uint16_t>(machine);
}
PEMachine getMachine() const {
return static_cast<PEMachine>(m_base->Machine);
}
void setNumberOfSections(std::uint16_t numSections) {
m_base->NumberOfSections = numSections;
}
std::uint16_t getNumberOfSections() const {
return m_base->NumberOfSections;
}
void setTimeDateStamp(std::uint32_t dwTimeDateStamp) {
m_base->TimeDateStamp = dwTimeDateStamp;
}
std::uint32_t getTimeDateStamp() const {
return m_base->TimeDateStamp;
}
void setPointerToSymbolTable(std::uint32_t dwPointerToSymbolTable) {
m_base->PointerToSymbolTable = dwPointerToSymbolTable;
}
std::uint32_t setPointerToSymbolTable() const {
return m_base->PointerToSymbolTable;
}
void setNumberOfSymbols(std::uint32_t numSymbols) {
m_base->NumberOfSymbols = numSymbols;
}
std::uint32_t getNumberOfSymbols() const {
return m_base->NumberOfSymbols;
}
void setSizeOfOptionalHeader(std::uint16_t size) {
m_base->SizeOfOptionalHeader = size;
}
std::uint16_t getSizeOfOptionalHeader() const {
return m_base->SizeOfOptionalHeader;
}
void setCharacteristics(std::uint16_t chars) {
m_base->Characteristics = chars;
}
std::uint16_t getCharacteristics() const {
return m_base->Characteristics;
}
IMAGE_FILE_HEADER* native() const {
return m_base;
}
private:
template<unsigned int bitsize>
void _setup(Image<bitsize>* image) {
m_base = &image->getPEHdr().native()->FileHeader;
}
};
}
If you need the complete source code, please add the WeChat number (c17865354792)
PE文件作为Windows生态的核心载体,其解析与操作技术深刻影响着逆向工程、安全防护和软件开发的多个领域。通过理解其底层原理并合理设计解析库,开发者能够高效应对复杂的应用场景和安全挑战。未来,随着操作系统和攻击手法的演进,这一领域的技术探索将持续活跃。
Welcome to follow WeChat official account【程序猿编码】