资源表、重定位表
// win原理Day003.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
//1 把文件读到内存中
char* ReadFileToMemory(char* pFilePath)
{
//1 获取文件句柄
HANDLE hFile = CreateFileA(pFilePath,
GENERIC_READ | GENERIC_WRITE,
FALSE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("文件打开失败\n");
return 0;
}
//2.获取文件大小
DWORD dwFileSize = GetFileSize(hFile, NULL);
//3.申请内存空间
char* pBuf = new char[dwFileSize]{};
if (!pBuf)
{
CloseHandle(hFile);
printf("内存申请失败\n");
return 0;
}
//4.读取文件内容到内存空间
DWORD dwRead;
ReadFile(hFile, pBuf, dwFileSize, &dwRead, NULL);
//5. 返回内存地址
return pBuf;
}
//2 是否是PE文件
bool IsPeFile(char* pBuf)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE)
{
printf("不是PE文件\n");
return false;
}
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)
(pDos->e_lfanew + pBuf);
if (pNt->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是PE文件\n");
return false;
}
return true;
}
//RVA to FOA
DWORD RVAtoFOA(DWORD dwRVA, char* pBuf)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);
//区段个数(文件头第二个字段)
DWORD dwCount = pNt->FileHeader.NumberOfSections;
//区段首地址(IMAGE_FIRST_SECTION)
PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(pNt);
for (DWORD i = 0; i < dwCount; i++)
{
//FOA = RVA - 内存中区段首地址 + 文件中区段首地址
if (dwRVA >= pSec->VirtualAddress &&
dwRVA < pSec->VirtualAddress + pSec->SizeOfRawData)
{
return dwRVA - pSec->VirtualAddress + pSec->PointerToRawData;
}
//下一个区段表
pSec++;
}
return 0;
}
/*
//低16位是其ID
char* arryResType[] = { "", "鼠标指针(Cursor)", "位图(Bitmap)", "图标(Icon)", "菜单(Menu)"
, "对话框(Dialog)", "字符串列表(String Table)", "字体目录(Font Directory)", "字体(Font)", "快捷键(Accelerators)"
, "非格式化资源(Unformatted)", "消息列表(Message Table)", "鼠标指针组(Croup Cursor)", "", "图标组(Group Icon)", ""
, "版本信息(Version Information)" };
*/
//解析资源表
void ShowResouceTable(char* pBuf)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);
//找到资源表(数据目录表的第三项)
DWORD dwResRVA = pNt->OptionalHeader.DataDirectory[2].VirtualAddress;
//资源表在文件中的具体文件地址
DWORD dwResRoot = (DWORD)(RVAtoFOA(dwResRVA, pBuf) + pBuf);
//开始解析
//第一层地址
PIMAGE_RESOURCE_DIRECTORY pRes1 = (PIMAGE_RESOURCE_DIRECTORY)dwResRoot;
//第一层总个数
DWORD dwCount_1 = pRes1->NumberOfIdEntries + pRes1->NumberOfNamedEntries;
//第一层资源项的起始地址
PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry1 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes1 + 1);
for (DWORD i = 0; i < dwCount_1; i++)
{
//判断资源命名方式
if (pEntry1->NameIsString)
{
//字符串命名
PIMAGE_RESOURCE_DIR_STRING_U pName =
(PIMAGE_RESOURCE_DIR_STRING_U)
(pEntry1->NameOffset + dwResRoot);
//资源字符是WCHAR类型
//在文件中并不是以0为结尾存储的
WCHAR * wcName = new WCHAR[pName->Length+1]{};
memcpy(wcName, pName->NameString, pName->Length * 2);
printf("资源种类名称:【%S】\n", wcName);
delete wcName;
}
else
{
//ID命名
//低16位是其ID
char* arryResType[] = { "", "鼠标指针(Cursor)", "位图(Bitmap)", "图标(Icon)", "菜单(Menu)"
, "对话框(Dialog)", "字符串列表(String Table)", "字体目录(Font Directory)", "字体(Font)", "快捷键(Accelerators)"
, "非格式化资源(Unformatted)", "消息列表(Message Table)", "鼠标指针组(Croup Cursor)", "", "图标组(Group Icon)", ""
, "版本信息(Version Information)" };
if (pEntry1->Id > 17)
{
printf("资源种类ID:【%d】\n", pEntry1->Id);
}
else
{
printf("资源种类ID:【%s】\n", arryResType[pEntry1->Id]);
}
}
//判断是否有下一层
if (pEntry1->DataIsDirectory)
{
//第二层起始地址
PIMAGE_RESOURCE_DIRECTORY pRes2 =
(PIMAGE_RESOURCE_DIRECTORY)
(pEntry1->OffsetToDirectory + dwResRoot);
//第二层资源个数
DWORD dwCount2 = pRes2->NumberOfIdEntries + pRes2->NumberOfNamedEntries;
//第二层资源项的起始地址
PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry2 =
(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes2 + 1);
for (DWORD i2 = 0; i2 < dwCount2; i2++)
{
//判断资源命名方式
if (pEntry2->NameIsString)
{
//字符串命名
PIMAGE_RESOURCE_DIR_STRING_U pName =
(PIMAGE_RESOURCE_DIR_STRING_U)
(pEntry2->NameOffset + dwResRoot);
//资源字符是WCHAR类型
WCHAR * wcName = new WCHAR[pName->Length + 1]{};
memcpy(wcName, pName->NameString, pName->Length * 2);
printf("\t资源名称:【%S】\n", wcName);
delete wcName;
}
else
{
//ID命名
printf("\t资源ID:【%d】\n", pEntry2->Id);
}
//判断是否有下一层
if (pEntry2->DataIsDirectory)
{
//第三层
PIMAGE_RESOURCE_DIRECTORY pRes3 =
(PIMAGE_RESOURCE_DIRECTORY)
(pEntry2->OffsetToDirectory + dwResRoot);
//资源个数
DWORD dwCount3 = pRes3->NumberOfIdEntries + pRes3->NumberOfNamedEntries;
PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes3 + 1);
//资源类别(英文的或者中文的等等)
//pEntry3->Name;
PIMAGE_RESOURCE_DATA_ENTRY pData = (PIMAGE_RESOURCE_DATA_ENTRY)(pEntry3->OffsetToData + dwResRoot);
//资源的数据大小
DWORD dwDataSize = pData->Size;
//pData->OffsetToData,资源数据的RVA
DWORD dwDataFOA = RVAtoFOA(pData->OffsetToData, pBuf);
PBYTE pByteData = (PBYTE)(dwDataFOA + pBuf);
//这里输出前10个字节
printf("\t\t");
for (int n = 0; n < 10; n++)
{
printf("%02X ", pByteData[n]);
}
printf("\n");
}
//下一个资源
pEntry2++;
}
}
//下一种资源
pEntry1++;
}
}
//解析重定位表
void ShowRelocTable(char* pBuf)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);
//找到重定位表(数据目录表第六项)
//解析
DWORD dwRelocRVA = pNt->OptionalHeader.DataDirectory[5].VirtualAddress;
//得到重定位表在文件中的地址
PIMAGE_BASE_RELOCATION pReloc =
(PIMAGE_BASE_RELOCATION)
(RVAtoFOA(dwRelocRVA, pBuf) + pBuf);
struct TypeOffset //重定位项结构
{
WORD offset : 12; //低12位表示相对于本块记录的内存页的偏移量
WORD type : 4; //高4位表示需要修正几个字节(0:不需要修正;3:修正4个字节;0x10:修正8个字节)
};
int n = 1;//重定位块个数
while (pReloc->SizeOfBlock)
{
printf("第%d块【0x%08X】\n", n++, pReloc->VirtualAddress);
//计算重定位项的个数
DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
//重定位项数组的首地址
TypeOffset* pOffset = (TypeOffset*)(pReloc + 1);
for (int i = 0; i < dwCount; i++)
{
if (pOffset->type == 3)
{
DWORD dwDataRVA = pOffset->offset + pReloc->VirtualAddress;
DWORD* pRelocData = (DWORD*)(RVAtoFOA(dwDataRVA, pBuf) + pBuf);
printf("\t需要修正的数据:0x%08X\n", *pRelocData);
//如果程序运行起来,修正的方式
//*pRelocData = *pRelocData - 默认加载基址 + 新的加载基址;
}
//下一个重定位项
pOffset++;
}
//下一个重定位块的地址
pReloc = (PIMAGE_BASE_RELOCATION)
((DWORD)pReloc + pReloc->SizeOfBlock);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
char* pBuf = ReadFileToMemory("123.exe");
if (IsPeFile(pBuf))
{
//ShowResouceTable(pBuf);
ShowRelocTable(pBuf);
}
//释放内存
delete pBuf;
return 0;
}