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.
301 lines
9.4 KiB
301 lines
9.4 KiB
#include "linker.hpp"
|
|
|
|
namespace lnk
|
|
{
|
|
auto get_symbol_size(symbol_t& sym, obj_buffer_t& obj) -> std::uint32_t
|
|
{
|
|
const auto coff_header =
|
|
reinterpret_cast<PIMAGE_FILE_HEADER>(obj.data());
|
|
|
|
const auto section_headers =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
obj.data() + sizeof IMAGE_FILE_HEADER);
|
|
|
|
const auto symbol_table =
|
|
reinterpret_cast<PIMAGE_SYMBOL>(
|
|
coff_header->PointerToSymbolTable + obj.data());
|
|
|
|
std::uint32_t result =
|
|
section_headers[sym.section_number - 1].SizeOfRawData;
|
|
|
|
// loop over all symbols in this object...
|
|
// find the next symbol inside of the same section...
|
|
for (auto idx = 0u; idx < coff_header->NumberOfSymbols; ++idx)
|
|
if (symbol_table[idx].SectionNumber == sym.section_number)
|
|
if (symbol_table[idx].Value > sym.section_offset && symbol_table[idx].Value < result)
|
|
result = symbol_table[idx].Value;
|
|
|
|
return result - sym.section_offset;
|
|
}
|
|
|
|
auto get_map_symbols(std::string map_path) -> map_symbols_t
|
|
{
|
|
std::ifstream map_file(map_path);
|
|
|
|
if (!map_file.is_open())
|
|
return { {}, {} };
|
|
|
|
std::string line;
|
|
map_symbols_t result;
|
|
while (std::getline(map_file, line))
|
|
{
|
|
const auto colon_index = line.find(":");
|
|
if (colon_index == std::string::npos)
|
|
break;
|
|
|
|
const auto section_number =
|
|
std::strtoul(line.substr(1,
|
|
colon_index).c_str(), NULL, 16);
|
|
|
|
const auto section_offset =
|
|
std::strtoull(line.substr(
|
|
colon_index + 1, 16).c_str(), NULL, 16);
|
|
|
|
auto symbol = line.substr(
|
|
colon_index + 16 + 8,
|
|
line.length() - (colon_index + 16 + 7));
|
|
|
|
symbol[symbol.length()] = '\0';
|
|
result[symbol] = { section_number, section_offset };
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto get_objs(std::string lib_path, std::vector<obj_buffer_t>& objs) -> bool
|
|
{
|
|
std::vector<std::uint8_t> lib_file;
|
|
utils::open_binary_file(lib_path, lib_file);
|
|
|
|
// archive header magic bytes are not correct...
|
|
if (strncmp((char*)lib_file.data(),
|
|
IMAGE_ARCHIVE_START, sizeof IMAGE_ARCHIVE_START - 1))
|
|
return false;
|
|
|
|
auto archive_headers =
|
|
reinterpret_cast<PIMAGE_ARCHIVE_MEMBER_HEADER>(
|
|
lib_file.data() + sizeof IMAGE_ARCHIVE_START - 1);
|
|
|
|
while (reinterpret_cast<std::uint8_t*>(archive_headers) < lib_file.data() + lib_file.size())
|
|
{
|
|
// some absolutely fucked up shit I have to
|
|
// do since the linker is off by 1 sometimes...
|
|
// this sounds weird... I know it sounds weird... its weird...
|
|
if (archive_headers->Name[0] == '\n')
|
|
archive_headers = reinterpret_cast<PIMAGE_ARCHIVE_MEMBER_HEADER>(
|
|
reinterpret_cast<std::uintptr_t>(archive_headers) + 1);
|
|
|
|
// refer to https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#archive-member-headers
|
|
// for details on the "name" field... in short `/` means its a linker header
|
|
// `//` means that it is the string table, and finally `/n` n being the index into the string table...
|
|
// so in short: if the first byte isnt a `/` and the second byte is not a space or the first byte
|
|
// is not a forward slash then this archive header is for an obj...
|
|
if ((archive_headers->Name[0] == '/' &&
|
|
archive_headers->Name[1] != '\x20' &&
|
|
archive_headers->Name[1] != '/') || archive_headers->Name[0] != '/')
|
|
{
|
|
const auto obj_size = std::atoi(
|
|
reinterpret_cast<const char*>(archive_headers->Size));
|
|
|
|
if (archive_headers->EndHeader[0] != 0x60 || archive_headers->EndHeader[1] != 0x0A)
|
|
break;
|
|
|
|
if (!obj_size)
|
|
break;
|
|
|
|
const auto obj_begin = reinterpret_cast<void*>(
|
|
reinterpret_cast<std::uintptr_t>(archive_headers) +
|
|
sizeof IMAGE_ARCHIVE_MEMBER_HEADER);
|
|
|
|
std::vector<std::uint8_t> obj{};
|
|
obj.resize(obj_size);
|
|
|
|
memcpy(obj.data(), obj_begin, obj_size);
|
|
objs.push_back(obj);
|
|
}
|
|
|
|
archive_headers = reinterpret_cast<PIMAGE_ARCHIVE_MEMBER_HEADER>(
|
|
reinterpret_cast<std::uintptr_t>(archive_headers) +
|
|
std::atoi(reinterpret_cast<char*>(
|
|
archive_headers->Size)) + sizeof IMAGE_ARCHIVE_MEMBER_HEADER);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
namespace section
|
|
{
|
|
auto get_header(obj_buffer_t& obj, const char* section_name) -> PIMAGE_SECTION_HEADER
|
|
{
|
|
const auto coff_header =
|
|
reinterpret_cast<PIMAGE_FILE_HEADER>(obj.data());
|
|
|
|
const auto section_headers =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
obj.data() + sizeof IMAGE_FILE_HEADER);
|
|
|
|
for (auto idx = 0u; idx < coff_header->NumberOfSections; ++idx)
|
|
if (!strncmp((char*)section_headers[idx].Name, section_name, strlen(section_name) - 1))
|
|
return section_headers + idx;
|
|
|
|
return {};
|
|
}
|
|
|
|
auto for_each(section_callback_t callback, obj_buffer_t& obj) -> void
|
|
{
|
|
const auto coff_header =
|
|
reinterpret_cast<PIMAGE_FILE_HEADER>(obj.data());
|
|
|
|
const auto section_headers =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
obj.data() + sizeof IMAGE_FILE_HEADER);
|
|
|
|
for (auto idx = 0u; idx < coff_header->NumberOfSections; ++idx)
|
|
if (!callback(section_headers + idx, obj))
|
|
break;
|
|
}
|
|
}
|
|
|
|
namespace sym
|
|
{
|
|
auto get_relocs(obj_buffer_t& obj) -> std::vector<image_reloc_t>
|
|
{
|
|
const auto coff_header =
|
|
reinterpret_cast<PIMAGE_FILE_HEADER>(obj.data());
|
|
|
|
const auto section_headers =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
obj.data() + sizeof IMAGE_FILE_HEADER);
|
|
|
|
const auto symbol_table =
|
|
reinterpret_cast<PIMAGE_SYMBOL>(
|
|
coff_header->PointerToSymbolTable + obj.data());
|
|
|
|
const auto string_table =
|
|
reinterpret_cast<const char*>(
|
|
reinterpret_cast<std::uintptr_t>(symbol_table) +
|
|
(coff_header->NumberOfSymbols * sizeof IMAGE_SYMBOL));
|
|
|
|
std::vector<image_reloc_t> result;
|
|
for (auto idx = 0u; idx < coff_header->NumberOfSections; ++idx)
|
|
{
|
|
if (section_headers[idx].PointerToRelocations)
|
|
{
|
|
// for some reason the compiler makes some empty sections...
|
|
if (!section_headers[idx].SizeOfRawData)
|
|
continue;
|
|
|
|
// skip over sections that we will not be using...
|
|
if (section_headers[idx].Characteristics & IMAGE_SCN_LNK_REMOVE)
|
|
continue;
|
|
|
|
// skip over discardable sections...
|
|
if (section_headers[idx].Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
|
|
continue;
|
|
|
|
// skip both the .pdata and the .xdata sections... these are used for exceptions...
|
|
if (!strncmp((char*)section_headers[idx].Name, ".pdata", strlen(".pdata") - 1))
|
|
continue;
|
|
|
|
if (!strncmp((char*)section_headers[idx].Name, ".xdata", strlen(".xdata") - 1))
|
|
continue;
|
|
|
|
const auto reloc_dir =
|
|
reinterpret_cast<PIMAGE_RELOCATION>(
|
|
section_headers[idx].PointerToRelocations + obj.data());
|
|
|
|
for (auto reloc_idx = 0u; reloc_idx <
|
|
section_headers[idx].NumberOfRelocations; ++reloc_idx)
|
|
{
|
|
image_reloc_t entry;
|
|
entry.file_offset =
|
|
reloc_dir[reloc_idx].VirtualAddress +
|
|
section_headers[idx].PointerToRawData;
|
|
|
|
if (symbol_table[reloc_dir[reloc_idx].SymbolTableIndex].N.Name.Short)
|
|
entry.resolve_symbol_name =
|
|
std::string((char*)symbol_table[reloc_dir[
|
|
reloc_idx].SymbolTableIndex].N.ShortName);
|
|
else
|
|
entry.resolve_symbol_name = std::string(
|
|
string_table + symbol_table[reloc_dir[
|
|
reloc_idx].SymbolTableIndex].N.Name.Long);
|
|
|
|
entry.type = reloc_dir[reloc_idx].Type;
|
|
result.push_back(entry);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
auto get_all(obj_buffer_t& obj) -> std::vector<symbol_t>
|
|
{
|
|
const auto coff_header =
|
|
reinterpret_cast<PIMAGE_FILE_HEADER>(obj.data());
|
|
|
|
const auto section_headers =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
obj.data() + sizeof IMAGE_FILE_HEADER);
|
|
|
|
const auto symbol_table =
|
|
reinterpret_cast<PIMAGE_SYMBOL>(
|
|
coff_header->PointerToSymbolTable + obj.data());
|
|
|
|
const auto string_table =
|
|
reinterpret_cast<const char*>(
|
|
reinterpret_cast<std::uintptr_t>(symbol_table) +
|
|
(coff_header->NumberOfSymbols * sizeof IMAGE_SYMBOL));
|
|
|
|
std::vector<symbol_t> result;
|
|
for (auto idx = 0u; idx < coff_header->NumberOfSymbols; ++idx)
|
|
{
|
|
// skip section symbols... we only want
|
|
// .data, .rdata, and executable (function) symbols...
|
|
if (symbol_table[idx].StorageClass != IMAGE_SYM_CLASS_EXTERNAL
|
|
|| !symbol_table[idx].SectionNumber)
|
|
{
|
|
if (symbol_table[idx].NumberOfAuxSymbols)
|
|
idx += symbol_table[idx].NumberOfAuxSymbols;
|
|
|
|
continue;
|
|
}
|
|
|
|
symbol_t symbol;
|
|
if (symbol_table[idx].N.Name.Short)
|
|
symbol.symbol_name =
|
|
std::string((char*)symbol_table[idx].N.ShortName);
|
|
else
|
|
symbol.symbol_name =
|
|
std::string(string_table +
|
|
symbol_table[idx].N.Name.Long);
|
|
|
|
symbol.file_offset = section_headers[symbol_table[
|
|
idx].SectionNumber - 1].PointerToRawData + symbol_table[idx].Value;
|
|
|
|
symbol.section_number = symbol_table[idx].SectionNumber;
|
|
symbol.section_offset = symbol_table[idx].Value;
|
|
symbol.type = symbol_table[idx].Type;
|
|
symbol.size = get_symbol_size(symbol, obj);
|
|
|
|
const auto section_name =
|
|
reinterpret_cast<const char*>(
|
|
section_headers[symbol_table[idx].SectionNumber - 1].Name);
|
|
|
|
if (!strncmp(section_name, ".theo2", sizeof(".theo2") - 1))
|
|
symbol.obfuscate_type = theo_type::encrypt;
|
|
else if (!strncmp(section_name, ".theo1", sizeof(".theo1") - 1))
|
|
symbol.obfuscate_type = theo_type::mutate;
|
|
else if (!strncmp(section_name, ".theo", sizeof(".theo") - 1))
|
|
symbol.obfuscate_type = theo_type::obfuscate;
|
|
else
|
|
symbol.obfuscate_type = (theo_type)NULL;
|
|
|
|
// there can be more then one aux symbols...
|
|
if (symbol_table[idx].NumberOfAuxSymbols)
|
|
idx += symbol_table[idx].NumberOfAuxSymbols;
|
|
|
|
result.push_back(symbol);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
} |