You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
6.1 KiB
228 lines
6.1 KiB
|
|
#include "ExeReader.hpp" |
|
|
|
#include <iostream> |
|
#include <iomanip> |
|
#include <algorithm> |
|
#include <cstring> |
|
|
|
#include "Types.h" |
|
#include "ExeFormat.hpp" |
|
#include "Utils.hpp" |
|
#include "Output.hpp" |
|
|
|
using std::cout; |
|
using std::string; |
|
using std::endl; |
|
using std::setw; |
|
using std::setfill; |
|
using std::hex; |
|
using std::dec; |
|
|
|
size_t ExeReader::findResourceEntry(std::istream & ifs, int id) { |
|
|
|
CoffResourceTable table; |
|
if(read(ifs, table).fail()) { |
|
error << "error reading resource table"; |
|
return 0; |
|
} |
|
|
|
cout << "[table] char: " << table.characteristics << " time: " << table.timestamp << " version: " << table.majorVersion << '.' << table.minorVersion << " names: " << table.nbnames << " ids: " << table.nbids << endl; |
|
|
|
ifs.seekg(table.nbnames * sizeof(CoffResourceEntry), strm::cur); |
|
|
|
size_t offset = 0; |
|
|
|
for(size_t i = 0; i < table.nbids; i++) { |
|
|
|
CoffResourceEntry entry; |
|
if(read(ifs, entry).fail()) { |
|
error << "error reading resource table entry"; |
|
return 0; |
|
} |
|
|
|
cout << "[entry] id: " << entry.id << " address: " << hex << (entry.offset & ~(1 << 31)) << dec << " type: " << ((entry.offset & (1 << 31)) ? "table" : "leaf" ) << endl; |
|
|
|
if(entry.id == id) { |
|
//ifs.seekg((table.nbids - i - 1) * sizeof(ResourceEntry)); |
|
offset = entry.offset; |
|
// break; |
|
} |
|
} |
|
|
|
return offset; |
|
} |
|
|
|
bool ExeReader::loadSectionTable(std::istream & ifs, size_t peOffset, const CoffFileHeader & coff, CoffSectionTable & table) { |
|
|
|
size_t sectionTableOffset = peOffset + 4 + sizeof(CoffFileHeader) + coff.optionalHeaderSize; |
|
|
|
table.resize(coff.nsections); |
|
|
|
ifs.seekg(sectionTableOffset); |
|
if(ifs.read(reinterpret_cast<char *>(table.data()), sizeof(CoffSection) * table.size()).fail()) { |
|
error << "error coff loading section table"; |
|
return false; |
|
} |
|
|
|
for(CoffSectionTable::const_iterator i = table.begin(); i != table.end(); ++i) { |
|
cout << "[section] \"" << safestring(i->name) << "\" virtual=" << hex << i->virtualAddress << '+' << i->virtualSize << " raw=" << i->rawAddress << '+' << i->rawSize << dec << endl; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
size_t ExeReader::memoryAddressToFileOffset(const CoffSectionTable & sections, size_t memory) { |
|
|
|
for(CoffSectionTable::const_iterator i = sections.begin(); i != sections.end(); ++i) { |
|
|
|
const CoffSection & section = *i; |
|
|
|
if(memory >= section.virtualAddress && memory < section.virtualAddress + section.virtualSize) { |
|
return memory + section.rawAddress - section.virtualAddress; |
|
} |
|
|
|
} |
|
|
|
return 0; |
|
} |
|
|
|
ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int type, int language) { |
|
|
|
Resource result; |
|
result.offset = result.size = 0; |
|
|
|
u16 peOffset; |
|
if(read(is.seekg(0x3c), peOffset).fail()) { |
|
error << "error reading PE signature offset"; |
|
return result; |
|
} |
|
cout << "PE signature is @ " << hex << peOffset << dec << endl; |
|
|
|
char magic[4]; |
|
if(is.seekg(peOffset).read(magic, 4).fail()) { |
|
error << "error reading PE signature"; |
|
return result; |
|
} |
|
static const char expectedMagic[] = { 'P', 'E', 0, 0 }; |
|
if(std::memcmp(magic, expectedMagic, 4)) { |
|
error << "wrong PE signature - not an exe file"; |
|
return result; |
|
} |
|
|
|
CoffFileHeader coff; |
|
if(read(is, coff).fail()) { |
|
error << "error reading COFF file header"; |
|
return result; |
|
} |
|
|
|
u16 optionalHeaderMagic; |
|
if(read(is, optionalHeaderMagic).fail()) { |
|
error << "error reading the optional header magic number"; |
|
return result; |
|
} |
|
|
|
// skip the optional header |
|
if(optionalHeaderMagic == 0x20b) { // PE32+ |
|
is.seekg(106, strm::cur); |
|
} else { |
|
is.seekg(90, strm::cur); |
|
} |
|
|
|
u32 ndirectories; |
|
if(read(is, ndirectories).fail()) { |
|
error << "error reading number of data directories"; |
|
return result; |
|
} |
|
cout << "number of directories is " << ndirectories << endl; |
|
if(ndirectories < 3) { |
|
error << "no resource directory found"; |
|
return result; |
|
} |
|
|
|
CoffDataDirectory resources; |
|
if(read(is.seekg(16, strm::cur), resources).fail()) { |
|
error << "error reading resource directory offset"; |
|
return result; |
|
} |
|
if(!resources.address || !resources.size) { |
|
error << "missing resource directory"; |
|
return result; |
|
} |
|
|
|
CoffSectionTable sections; |
|
if(!loadSectionTable(is, peOffset, coff, sections)) { |
|
return result; |
|
} |
|
|
|
size_t resourceOffset = memoryAddressToFileOffset(sections, resources.address); |
|
if(!resourceOffset) { |
|
error << "error mapping virtual resource address " << hex << resources.address << dec << " to file offset"; |
|
return result; |
|
} |
|
cout << "resource table is @ RVA " << hex << resources.address << " -> @ " << resourceOffset << dec << endl; |
|
|
|
is.seekg(resourceOffset); |
|
|
|
u32 typeOffset = findResourceEntry(is, type); |
|
if(!typeOffset) { |
|
error << "missing data resource entry"; |
|
return result; |
|
} |
|
if(!(typeOffset & (1 << 31))) { |
|
error << "unexpected resource leaf for data"; |
|
return result; |
|
} |
|
typeOffset &= ~(1 << 31), typeOffset += resourceOffset; |
|
|
|
cout << "data resource table is @ " << hex << typeOffset << dec << endl; |
|
|
|
is.seekg(typeOffset); |
|
|
|
u32 nameOffset = findResourceEntry(is, name); |
|
if(!nameOffset) { |
|
error << "missing installer resource entry"; |
|
return result; |
|
} |
|
if(!(nameOffset & (1 << 31))) { |
|
error << "unexpected resource leaf for installer"; |
|
return result; |
|
} |
|
nameOffset &= ~(1 << 31), nameOffset += resourceOffset; |
|
|
|
cout << "installer resource table is @ " << hex << nameOffset << dec << endl; |
|
|
|
is.seekg(nameOffset); |
|
|
|
u32 finalOffset = findResourceEntry(is, language); |
|
if(!finalOffset) { |
|
error << "missing final resource entry"; |
|
return result; |
|
} |
|
if(finalOffset & (1 << 31)) { |
|
error << "unexpected table for final resource entry"; |
|
return result; |
|
} |
|
finalOffset += resourceOffset; |
|
|
|
cout << "final resource entry is @ " << hex << finalOffset << dec << endl; |
|
|
|
CoffResourceLeaf leaf; |
|
if(read(is.seekg(finalOffset), leaf).fail()) { |
|
error << "error loading final resource entry"; |
|
return result; |
|
} |
|
|
|
cout << "[resource] address: " << hex << leaf.address << dec << " size: " << leaf.size << " codepage: " << leaf.codepage << endl; |
|
|
|
size_t dataOffset = memoryAddressToFileOffset(sections, leaf.address); |
|
if(!dataOffset) { |
|
error << "error mapping final virtual resource address " << hex << leaf.address << dec << " to file offset"; |
|
return result; |
|
} |
|
|
|
result.offset = dataOffset; |
|
result.size = leaf.size; |
|
|
|
return result; |
|
}
|
|
|