//计算值将Rva转换成Foa
DWORD RVA2Offset(DWORD dwRVA);
//加载PE
BOOL LoadPe();
//判断是否为PE文件
BOOL IsPE_File();
//获取PE映像文件头
void GetFileHeader();
//获取PE映像扩展头
void GetOptionalHeader();
//获取PE区段信息
void GetSectionHeader();
//导出表
void GetExportDirectory();
//导入表
void GetImportDirectory();
//解析PE映像文件头
void GetFileHeader()
{
m_PeHead = m_pNT->FileHeader;
m_pImageHeaderNum = m_PeHead.NumberOfSections;
}
//获取PE映像扩展头
void GetOptionalHeader()
{
m_Optinal = m_pNT->OptionalHeader;
m_pDataDir =(PIMAGE_DATA_DIRECTORY)m_pNT->OptionalHeader.DataDirectory;
}
DWORD CPeHear::RVA2Offset( DWORD dwRVA )
{
for ( DWORD i=0; i<m_pNT->FileHeader.NumberOfSections; i++ )
{
if ( dwRVA>=m_pSection[i].VirtualAddress && dwRVA<m_pSection[i].VirtualAddress+m_pSection[i].Misc.VirtualSize )
{
DWORD dwR_Offset = dwRVA - m_pSection[i].VirtualAddress;
DWORD dwOffset = (DWORD)m_lpFileImage + dwR_Offset + m_pSection[i].PointerToRawData;
return dwOffset;
}
}
return 0;
}
typedef struct _SECTIONHEADER
{
CString NAME;
DWORD PointerToRawData; //区段在文件中的偏移
DWORD SizeOfRawData; //文件中区段对齐大小
DWORD MiscVirtualSize; //偏移大小
DWORD VirtualAddress; //区段的RVA地址
}SECTION,*PSECTION
typedef struct _EXPORT
{
DWORD ID; //导出函数的序号
DWORD EAT; //导出函数的地址RVA
DWORD EX_RVA; //导出函数的地址偏移
CString NAME; //导出的函数名称
}EXPORTA,*PEXPORT;
void GetExportDirectory()
{
PIMAGE_DATA_DIRECTORY pExportDir = &m_pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (pExportDir->Size == 0)
return;
DWORD dwExportOfffset = RVA2Offset(pExportDir->VirtualAddress);
m_pExport = (PIMAGE_EXPORT_DIRECTORY)dwExportOfffset;
m_ExportOffset = RVA2Offset(m_Optinal.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) - (DWORD)m_lpFileImage;
PDWORD pEAT = (PDWORD)RVA2Offset(m_pExport->AddressOfFunctions);
PDWORD pENT = (PDWORD)RVA2Offset(m_pExport->AddressOfNames);
PWORD pEIT = (PWORD) RVA2Offset(m_pExport->AddressOfNameOrdinals);
//m_ExportOffset =
for ( DWORD dwOrdinal=0; dwOrdinal<m_pExport->NumberOfFunctions; dwOrdinal++ )
{
if ( !pEAT[dwOrdinal] )
continue;
for ( DWORD dwIndex=0; dwIndex<m_pExport->NumberOfFunctions; dwIndex++ )
{
EXPORTA saveImport;
if ( pEIT[dwIndex] == dwOrdinal )
{
PCHAR pszFunName = (PCHAR)RVA2Offset(pENT[dwIndex]);
saveImport.NAME.Format(_T("%S"),pszFunName);
saveImport.EX_RVA = m_pExport->AddressOfFunctions;
saveImport.ID = m_pExport->Base+dwOrdinal;
saveImport.EAT = pEAT[dwOrdinal];
m_vExport.push_back(saveImport);
break;
}
else if ( dwIndex == m_pExport->NumberOfFunctions-1 )
{
saveImport.NAME.Format(_T("(Null)"));
saveImport.ID = m_pExport->Base+dwOrdinal;
saveImport.EX_RVA = m_pExport->AddressOfFunctions;
saveImport.EAT = pEAT[dwOrdinal];
m_vExport.push_back(saveImport);
break;
}
}
}
}
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // 指向输入名称表(IAT)的RVA
} DUMMYUNIONNAME;
DWORD TimeDateStamp; //时间标识
DWORD ForwarderChain; // 转发链如果不转发此值为0
DWORD Name; //指向导入映像文件的名字
DWORD FirstThunk; // 指向导入地址表(IAT)的RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // 转发字符的RVA
DWORD Function; // 被导入函数的地址
DWORD Ordinal; //被导入函数的序号
DWORD AddressOfData; // 指向输入名称
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //需导入函数序号
CHAR Name[1]; //导入函数名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
void CPeHear::GetImportDirectory()
{
PIMAGE_DATA_DIRECTORY pDir = (PIMAGE_DATA_DIRECTORY)m_pNT->OptionalHeader.DataDirectory;
PIMAGE_DATA_DIRECTORY pDataDir = pDir+IMAGE_DIRECTORY_ENTRY_IMPORT;
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)RVA2Offset(pDataDir->VirtualAddress);
while ( pImport->Name )
{
IMPORT saveImport;
saveImport.pszDllName = (PCHAR)RVA2Offset(pImport->Name);
PIMAGE_THUNK_DATA32 pINT = (PIMAGE_THUNK_DATA32)RVA2Offset(pImport->OriginalFirstThunk);
while ( pINT->u1.Ordinal )
{
if ( !IMAGE_SNAP_BY_ORDINAL32(pINT->u1.Ordinal) )
{
PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)RVA2Offset(pINT->u1.AddressOfData);
saveImport.NAME.Format(_T("%s"),pByName->Name);
saveImport.ID = pByName->Hint;
saveImport.IM_RVA = pImport->FirstThunk;
saveImport.Offset = RVA2Offset(pImport->FirstThunk);
m_vImport.push_back(saveImport);
pINT++;
continue;
}
// 2.4.2 如果最高位为1,则直接打印Ordinal部分
saveImport.NAME.Format(_T("Null"));
saveImport.ID = pINT->u1.Ordinal&0x0000FFFF;
saveImport.IM_RVA = pImport->FirstThunk;
saveImport.Offset = RVA2Offset(pImport->FirstThunk);
m_vImport.push_back(saveImport);
pINT++;
}
m_vpImport.push_back(pImport);
pImport++;
}
}
static PVOID m_lpFileImage; //文件内存中起始加载位置
static CString m_strInPath; //文件名
static IMAGE_FILE_HEADER m_PeHead; //PE映像文件头
static IMAGE_OPTIONAL_HEADER m_Optinal; //PE映像扩展头
static PIMAGE_SECTION_HEADER m_pSection; //获取PE区段信息
static PIMAGE_DOS_HEADER m_pDos; //DOS头地址
static PIMAGE_NT_HEADERS m_pNT; //PE头地址
static PIMAGE_DATA_DIRECTORY m_pDataDir; //PE数据目录
static PIMAGE_EXPORT_DIRECTORY m_pExport; //输出表
//static PIMAGE_IMPORT_DESCRIPTOR m_pImport; //输入表
static CString m_pszDllName;
static DWORD m_pImageHeaderNum; //区段数量
static DWORD m_NumberOfRvaAndSizes; //
static DWORD m_ExportOffset; //导出表的偏移
static vector<SECTION> m_vSection; //保存区段中的信息
static vector<EXPORTA> m_vExport; //保存导出表的信息
static vector<IMPORT> m_vImport; //保存导入表的信息
static vector<PIMAGE_IMPORT_DESCRIPTOR> m_vpImport;
Signature是PE文件头的标识,其值始终为0x00004550,ASCII码为“PE/0/0”,win32SDK使用#define IMAGE_NT_SIGNATURE定义了这个值,我们可以通过DOS和PE这两个标识来判断该文件是否为PE文件。
FileHeader是标准PE头
OptionalHeader是扩展PE头,重要的信息都存放在IMAGE_OPTIONAL_HEADER这个结构体中。
SizeOfCode 所有代码区段的总大小。
AddressOfEntryPoint 程序执行入口RVA。也就是我们所说的OEP。
ImageBase 文件在内存中的首选装入地址,默认加载基址,为PE文件的优先装载地址。比如,如果该值是400000h,PE装载器将尝试把文件装到虚拟地址空间的400000h处。字眼"优先"表示若该地址区域已被其他模块占用,那PE装载器会选用其他空闲地址。
SectionAlignment:映像文件在内存中的区段对齐大小。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)