mirror of
https://github.com/hasherezade/bearparser
synced 2026-06-08 14:32:38 +00:00
330 lines
8.6 KiB
C++
330 lines
8.6 KiB
C++
#include "pe/RelocDirWrapper.h"
|
|
#include "pe/PEFile.h"
|
|
|
|
/*
|
|
// Based relocation format.
|
|
|
|
typedef struct _IMAGE_BASE_RELOCATION {
|
|
DWORD VirtualAddress;
|
|
DWORD SizeOfBlock;
|
|
// WORD TypeOffset[1];
|
|
} IMAGE_BASE_RELOCATION;
|
|
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
|
|
|
|
|
|
//Based relocation types.
|
|
|
|
enum reloc_based {
|
|
RELB_ABSOLUTE = 0,
|
|
RELB_HIGH = 1,
|
|
RELB_LOW = 2,
|
|
RELB_HIGHLOW = 3,
|
|
RELB_HIGHADJ = 4,
|
|
RELB_MIPS_JMPADDR = 5,
|
|
RELB_SECTION = 6,
|
|
RELB_REL32 = 7,
|
|
RELB_MIPS_JMPADDR16 = 9,
|
|
RELB_IA64_IMM64 = 9,
|
|
RELB_DIR64 = 10,
|
|
RELB_HIGH3ADJ = 11
|
|
};
|
|
|
|
*/
|
|
|
|
bool RelocDirWrapper::wrap()
|
|
{
|
|
clear();
|
|
this->parsedSize = 0;
|
|
|
|
bufsize_t maxSize = getDirEntrySize(true);
|
|
size_t entryId = 0;
|
|
while (parsedSize < maxSize) {
|
|
RelocBlockWrapper* entry = new RelocBlockWrapper(this->m_Exe, this, entryId++);
|
|
if (!entry) break;
|
|
|
|
bool isOk1 = false;
|
|
const bufsize_t blockSize = (bufsize_t) entry->getNumValue(RelocBlockWrapper::BLOCK_SIZE, &isOk1);
|
|
if (!isOk1 || !blockSize || !entry->getPtr()) {
|
|
delete entry;
|
|
break;
|
|
}
|
|
this->parsedSize += blockSize;
|
|
this->entries.push_back(entry);
|
|
if (!entry->isValid()) {
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
IMAGE_BASE_RELOCATION* RelocDirWrapper::reloc()
|
|
{
|
|
offset_t rva = getDirEntryAddress();
|
|
|
|
BYTE *ptr = m_Exe->getContentAt(rva, Executable::RVA, sizeof(IMAGE_BASE_RELOCATION));
|
|
if (ptr == NULL) return NULL;
|
|
|
|
IMAGE_BASE_RELOCATION *reloc = (IMAGE_BASE_RELOCATION*) ptr;
|
|
return reloc;
|
|
}
|
|
|
|
//----------------
|
|
|
|
bool RelocBlockWrapper::wrap()
|
|
{
|
|
clear();
|
|
parsedSize = 0;
|
|
|
|
IMAGE_BASE_RELOCATION* reloc = myReloc();
|
|
if (!reloc) return false;
|
|
|
|
validatePage();
|
|
if (!this->isValidPage) return false;
|
|
|
|
size_t maxSize = reloc->SizeOfBlock;
|
|
parsedSize = sizeof(IMAGE_BASE_RELOCATION); // the block begins with IMAGE_BASE_RELOCATION record
|
|
size_t entryId = 0;
|
|
|
|
while (parsedSize < maxSize) {
|
|
RelocEntryWrapper* entry = new RelocEntryWrapper(this->m_Exe, this, entryId++);
|
|
|
|
if (!entry->getPtr()) {
|
|
delete entry;
|
|
break;
|
|
}
|
|
this->parsedSize += sizeof(pe::BASE_RELOCATION_ENTRY);
|
|
this->entries.push_back(entry);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RelocBlockWrapper::validatePage()
|
|
{
|
|
this->isValidPage = false;
|
|
if (!parentDir) return;
|
|
PEFile *m_PE = parentDir->m_PE;
|
|
if (!m_PE) return;
|
|
|
|
bool isOk = false;
|
|
const offset_t pageRva = this->getNumValue(RelocBlockWrapper::PAGE_VA, &isOk);
|
|
if (!isOk) return;
|
|
|
|
this->isValidPage = (pageRva < m_PE->getImageSize()) ? true : false;
|
|
}
|
|
|
|
bool RelocBlockWrapper::isValid()
|
|
{
|
|
return this->isValidPage;
|
|
}
|
|
|
|
void* RelocBlockWrapper::getPtr()
|
|
{
|
|
if (this->parentDir == NULL) return NULL;
|
|
IMAGE_BASE_RELOCATION* reloc = this->parentDir->reloc();
|
|
if (!reloc) return NULL;
|
|
|
|
offset_t raw = INVALID_ADDR;
|
|
BYTE *ptr = NULL;
|
|
|
|
// use my cached:
|
|
if (this->cachedRaw != INVALID_ADDR) {
|
|
ptr = m_Exe->getContentAt(this->cachedRaw, Executable::RAW, sizeof(IMAGE_BASE_RELOCATION));
|
|
return ptr;
|
|
}
|
|
|
|
// use previous cached to calculate my cached
|
|
size_t prevNum = this->entryNum - 1;
|
|
|
|
RelocBlockWrapper *prevEntry = dynamic_cast<RelocBlockWrapper*> (this->parentDir->getEntryAt(prevNum));
|
|
if (prevEntry) {
|
|
offset_t prevRaw = prevEntry->cachedRaw;
|
|
|
|
IMAGE_BASE_RELOCATION* prevReloc = (IMAGE_BASE_RELOCATION*) prevEntry->getPtr();
|
|
raw = prevRaw + prevReloc->SizeOfBlock;
|
|
|
|
if (prevRaw != INVALID_ADDR) {
|
|
ptr = m_Exe->getContentAt(raw, Executable::RAW, sizeof(IMAGE_BASE_RELOCATION));
|
|
|
|
if (ptr != NULL) {
|
|
this->cachedRaw = raw;
|
|
return ptr;
|
|
}
|
|
}
|
|
}
|
|
// previous cached not avaliable, calculate...
|
|
offset_t firstRaw = this->getOffset(reloc);
|
|
offset_t blockSize = reloc->SizeOfBlock;
|
|
|
|
raw = firstRaw;
|
|
ptr = (BYTE*) reloc;
|
|
|
|
for ( size_t i = 0; i < this->entryNum; i++) { //TODO: make caching
|
|
raw += blockSize;
|
|
|
|
ptr = m_Exe->getContentAt(raw, Executable::RAW, sizeof(IMAGE_BASE_RELOCATION));
|
|
if (!ptr) return NULL;
|
|
|
|
reloc = (IMAGE_BASE_RELOCATION*) ptr;
|
|
blockSize = reloc->SizeOfBlock;
|
|
}
|
|
|
|
this->cachedRaw = raw;
|
|
return ptr;
|
|
}
|
|
|
|
bufsize_t RelocBlockWrapper::getSize()
|
|
{
|
|
if (this->parentDir == NULL) return 0;
|
|
IMAGE_BASE_RELOCATION* reloc = (IMAGE_BASE_RELOCATION*) this->getPtr();
|
|
if (!reloc) return 0;
|
|
|
|
if (reloc->SizeOfBlock > 0) return reloc->SizeOfBlock;
|
|
|
|
return sizeof(IMAGE_BASE_RELOCATION);
|
|
}
|
|
|
|
void* RelocBlockWrapper::getFieldPtr(size_t fieldId, size_t subField)
|
|
{
|
|
IMAGE_BASE_RELOCATION* reloc = (IMAGE_BASE_RELOCATION*) this->getPtr();
|
|
if (!reloc) return NULL;
|
|
|
|
switch (fieldId) {
|
|
case PAGE_VA: return (void*) &reloc->VirtualAddress;
|
|
case BLOCK_SIZE : return (void*) &reloc->SizeOfBlock;
|
|
case ENTRIES_PTR :
|
|
{
|
|
BYTE *blockSizePtr = (BYTE*) &reloc->SizeOfBlock;
|
|
return blockSizePtr + sizeof(DWORD);
|
|
}
|
|
}
|
|
return getPtr();
|
|
}
|
|
|
|
QString RelocBlockWrapper::getFieldName(size_t fieldId)
|
|
{
|
|
switch (fieldId) {
|
|
case BLOCK_SIZE : return "Block Size";
|
|
case PAGE_VA: return "Page RVA";
|
|
case ENTRIES_PTR: return "Entries";
|
|
}
|
|
return getName();
|
|
}
|
|
|
|
Executable::addr_type RelocBlockWrapper::containsAddrType(size_t fieldId, size_t subField)
|
|
{
|
|
switch (fieldId) {
|
|
case PAGE_VA:
|
|
return Executable::RVA;
|
|
}
|
|
return Executable::NOT_ADDR;
|
|
}
|
|
|
|
WrappedValue::data_type RelocBlockWrapper::containsDataType(size_t fieldId, size_t subField)
|
|
{
|
|
if (fieldId == ENTRIES_PTR){
|
|
return WrappedValue::COMPLEX;
|
|
}
|
|
return WrappedValue::INT;
|
|
}
|
|
|
|
void* RelocBlockWrapper::getEntriesPtr()
|
|
{
|
|
void *entriesPtr = getFieldPtr(ENTRIES_PTR);
|
|
size_t entriesSize = getFieldSize(ENTRIES_PTR);
|
|
|
|
if (entriesPtr == NULL || entriesSize == 0) return NULL;
|
|
|
|
offset_t entriesOffset = getFieldOffset(ENTRIES_PTR);
|
|
void *ptr = this->m_Exe->getContentAt(entriesOffset, Executable::RAW, sizeof(WORD));
|
|
|
|
return ptr;
|
|
}
|
|
|
|
size_t RelocBlockWrapper::maxEntriesNumInBlock()
|
|
{
|
|
if (this->cachedMaxNum > 0) return this->cachedMaxNum;
|
|
|
|
IMAGE_BASE_RELOCATION* reloc = (IMAGE_BASE_RELOCATION*) this->getPtr();
|
|
if (!reloc) return 0;
|
|
|
|
bufsize_t entriesSize = getFieldSize(ENTRIES_PTR);
|
|
offset_t entriesOffset = getFieldOffset(ENTRIES_PTR);
|
|
|
|
offset_t fileSize = m_Exe->getRawSize();
|
|
if (entriesOffset + entriesSize > fileSize) {
|
|
entriesSize = fileSize - entriesOffset; // truncate to fileSize
|
|
}
|
|
|
|
void *ptr = this->m_Exe->getContentAt(entriesOffset, Executable::RAW, entriesSize);
|
|
|
|
bufsize_t entriesNum = 0;
|
|
if (ptr) {
|
|
entriesNum = entriesSize / sizeof(WORD); //sizeof(BASE_RELOCATION_ENTRY);
|
|
}
|
|
this->cachedMaxNum = entriesNum;
|
|
return entriesNum;
|
|
}
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
void* RelocEntryWrapper::getPtr()
|
|
{
|
|
if (this->parentDir == NULL) return NULL;
|
|
|
|
size_t maxNum = this->parentDir->maxEntriesNumInBlock();
|
|
if (this->entryNum >= maxNum) return NULL;
|
|
|
|
WORD* entriesPtr = (WORD* ) parentDir->getEntriesPtr();
|
|
if (entriesPtr == NULL) return NULL;
|
|
|
|
WORD* ptr = &entriesPtr[this->entryNum];
|
|
return ptr;
|
|
}
|
|
|
|
bufsize_t RelocEntryWrapper::getSize()
|
|
{
|
|
if (this->parentDir == NULL) return 0;
|
|
return sizeof(WORD);
|
|
}
|
|
|
|
WORD RelocEntryWrapper::getType(WORD relocEntryVal)
|
|
{
|
|
pe::BASE_RELOCATION_ENTRY* entry = (pe::BASE_RELOCATION_ENTRY*) &relocEntryVal;
|
|
return entry->Type;
|
|
}
|
|
|
|
WORD RelocEntryWrapper::getDelta(WORD relocEntryVal)
|
|
{
|
|
pe::BASE_RELOCATION_ENTRY* entry = (pe::BASE_RELOCATION_ENTRY*) &relocEntryVal;
|
|
return entry->Offset;
|
|
}
|
|
|
|
QString RelocEntryWrapper::translateType(WORD type)
|
|
{
|
|
switch (type) {
|
|
case 0 : return "Padding (skipped)";
|
|
case 1 : return "High WORD of 32-bit field";
|
|
case 2 : return "Low WORD of 32-bit field";
|
|
case 3 : return "32 bit field";
|
|
case 4 : return "HighAdj";
|
|
case 5 : return "MIPS JumpAddr";
|
|
case 6 : case 7 : return "Reserved";
|
|
case 9 : return "MIPS16 JumpAddr";
|
|
case 10 : return "64 bit field";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
offset_t RelocEntryWrapper::deltaToRVA(WORD delta)
|
|
{
|
|
if (this->parentDir == NULL) return INVALID_ADDR;
|
|
|
|
IMAGE_BASE_RELOCATION* reloc = parentDir->myReloc();
|
|
if (reloc == NULL) return INVALID_ADDR;
|
|
|
|
offset_t offset = static_cast<offset_t>(reloc->VirtualAddress + delta);
|
|
return offset;
|
|
}
|
|
|