#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(obj.data()); const auto section_headers = reinterpret_cast( obj.data() + sizeof IMAGE_FILE_HEADER); const auto symbol_table = reinterpret_cast( 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& objs) -> bool { std::vector 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( lib_file.data() + sizeof IMAGE_ARCHIVE_START - 1); while (reinterpret_cast(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( reinterpret_cast(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(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( reinterpret_cast(archive_headers) + sizeof IMAGE_ARCHIVE_MEMBER_HEADER); std::vector obj{}; obj.resize(obj_size); memcpy(obj.data(), obj_begin, obj_size); objs.push_back(obj); } archive_headers = reinterpret_cast( reinterpret_cast(archive_headers) + std::atoi(reinterpret_cast( 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(obj.data()); const auto section_headers = reinterpret_cast( 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(obj.data()); const auto section_headers = reinterpret_cast( 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 { const auto coff_header = reinterpret_cast(obj.data()); const auto section_headers = reinterpret_cast( obj.data() + sizeof IMAGE_FILE_HEADER); const auto symbol_table = reinterpret_cast( coff_header->PointerToSymbolTable + obj.data()); const auto string_table = reinterpret_cast( reinterpret_cast(symbol_table) + (coff_header->NumberOfSymbols * sizeof IMAGE_SYMBOL)); std::vector 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( 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 { const auto coff_header = reinterpret_cast(obj.data()); const auto section_headers = reinterpret_cast( obj.data() + sizeof IMAGE_FILE_HEADER); const auto symbol_table = reinterpret_cast( coff_header->PointerToSymbolTable + obj.data()); const auto string_table = reinterpret_cast( reinterpret_cast(symbol_table) + (coff_header->NumberOfSymbols * sizeof IMAGE_SYMBOL)); std::vector 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( 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; } } }