Skip to content

Commit

Permalink
xex1tool: add param to convert between file & virtual (memory) addresses
Browse files Browse the repository at this point in the history
There's not really any simple "subtract 0x823FF.." way to convert addresses, each XEX block has to be looped over to calculate the addr using each blocks proper sizes.

Only works with uncompressed files, type of conversion (to/from) is decided by the given address.

eg. "-a 0x82000004" may return "0x3004" as the file offset.
  • Loading branch information
emoose committed Feb 7, 2021
1 parent 6e10e2c commit 10ec0dd
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
84 changes: 84 additions & 0 deletions xex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,90 @@ bool XEXFile::read_basefile_compressed(void* file, bool encrypted)
return retcode == 0;
}

// TODO: fix this to work with older XEX formats
uint32_t XEXFile::xex_va_to_offset(void* file, uint32_t va)
{
if (!data_descriptor_)
return false;

xex_opt::XexDataFormat comp_format = (xex_opt::XexDataFormat)(uint16_t)data_descriptor_->Format;

if (va >= base_address())
va -= base_address();

if (comp_format == xex_opt::XexDataFormat::None)
return xex_header_.SizeOfHeaders + va;

if (comp_format != xex_opt::XexDataFormat::Raw)
return 0; // can't get offset for compressed XEXs

int num_blocks = (data_descriptor_->Size - 8) / 8;
auto xex_blocks = std::make_unique<xex_opt::XexRawDataDescriptor[]>(num_blocks);

seek(file, directory_entries_[XEX_FILE_DATA_DESCRIPTOR_HEADER] + 8, 0);
read(xex_blocks.get(), sizeof(xex_opt::XexRawDataDescriptor), num_blocks, file);

// Uncompressed blocks can have any number of zeroes appended to them, instead of these zeroes being stored in the XEX
// To locate the RVA just track the block size + zero size together, and then return its proper address without zeroes.
uint32_t position = 0;
uint32_t real_position = 0;
for (int i = 0; i < num_blocks; i++)
{
const auto& block = xex_blocks[i];
int block_end = position + block.DataSize + block.ZeroSize;
if (va >= position && va < block_end)
return xex_header_.SizeOfHeaders + real_position + (va - position);

position += block.DataSize + block.ZeroSize;
real_position += block.DataSize;
}

return 0;
}

// TODO: fix this to work with older XEX formats
uint32_t XEXFile::xex_offset_to_va(void* file, uint32_t offset)
{
if (!data_descriptor_)
return false;

xex_opt::XexDataFormat comp_format = (xex_opt::XexDataFormat)(uint16_t)data_descriptor_->Format;

if (xex_header_.SizeOfHeaders > offset)
return 0;

offset -= xex_header_.SizeOfHeaders;

if (comp_format == xex_opt::XexDataFormat::None)
return base_address() + offset;

if (comp_format != xex_opt::XexDataFormat::Raw)
return 0; // can't get offset for compressed XEXs

int num_blocks = (data_descriptor_->Size - 8) / 8;
auto xex_blocks = std::make_unique<xex_opt::XexRawDataDescriptor[]>(num_blocks);

seek(file, directory_entries_[XEX_FILE_DATA_DESCRIPTOR_HEADER] + 8, 0);
read(xex_blocks.get(), sizeof(xex_opt::XexRawDataDescriptor), num_blocks, file);

// Uncompressed blocks can have any number of zeroes appended to them, instead of these zeroes being stored in the XEX
// To locate the RVA just track the block size + zero size together, and then return its proper address without zeroes.
uint32_t position = 0;
uint32_t real_position = 0;
for (int i = 0; i < num_blocks; i++)
{
const auto& block = xex_blocks[i];
int block_end = real_position + block.DataSize;
if (offset >= real_position && offset < block_end)
return base_address() + position + (offset - real_position);

position += block.DataSize + block.ZeroSize;
real_position += block.DataSize;
}

return 0;
}

// Reads import libraries & function info from PE headers
bool XEXFile::pe_load_imports(const uint8_t* data)
{
Expand Down
3 changes: 3 additions & 0 deletions xex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class XEXFile

uint32_t pe_rva_to_offset(uint32_t rva);

uint32_t xex_va_to_offset(void* file, uint32_t va);
uint32_t xex_offset_to_va(void* file, uint32_t offset);

// Length of the pe_data member, not the same as image_size!
uint32_t pe_data_length() { return pe_data_.size(); }

Expand Down
41 changes: 41 additions & 0 deletions xex1tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@ int main(int argc, char* argv[])
("b,basefile", "Dump basefile from XEX", cxxopts::value<std::string>())
("d,dumpres", "Dump all resources to a dir (can be '.')", cxxopts::value<std::string>())
("v,verbose", "Enables verbose XEXFile debug output")
("a,address", "Convert a virtual memory address to file offset, or vice-versa", cxxopts::value<uint32_t>())
("positional", "Positional parameters",
cxxopts::value<std::vector<std::string>>())
;
Expand Down Expand Up @@ -847,6 +848,46 @@ int main(int argc, char* argv[])
return 0;
}

if (result.count("a"))
{
uint32_t rva = result["a"].as<uint32_t>();
printf("\n");

bool convert = true;
auto* data_descriptor = xex.data_descriptor();
if (!data_descriptor)
convert = false;
else
{
xex_opt::XexDataFormat comp_format = (xex_opt::XexDataFormat)(uint16_t)data_descriptor->Format;
convert = comp_format == xex_opt::XexDataFormat::None || comp_format == xex_opt::XexDataFormat::Raw;
}

if (!convert)
printf("XEX isn't uncompressed, unable to convert address with -a!\n");
else
{
// TODO: fix these functions to work with older formats
if (xex.header().Magic != MAGIC_XEX2)
printf("XEX isn't XEX2, addresses might not be correct!\n");

if (rva >= xex.base_address())
{
printf("Virtual Address -> File Offset\n");
printf("Virtual Addr: 0x%X\n", rva);
printf("File Offset: 0x%X\n", xex.xex_va_to_offset(file, rva));
}
else
{
printf("File Offset -> Virtual Address\n");
printf("File Offset: 0x%X\n", rva);
printf("Virtual Addr: 0x%X\n", xex.xex_offset_to_va(file, rva));
}
}

printf("\n");
}

if (result.count("b"))
{
auto& basefile = result["b"].as<std::string>();
Expand Down

0 comments on commit 10ec0dd

Please sign in to comment.