diff --git a/core/codegen/include/codegen/CodegenTypes.hpp b/core/codegen/include/codegen/CodegenTypes.hpp index 5f5e971c6..ba15b2a08 100644 --- a/core/codegen/include/codegen/CodegenTypes.hpp +++ b/core/codegen/include/codegen/CodegenTypes.hpp @@ -41,6 +41,7 @@ enum class ControlFlowKind : std::uint8_t { Return, IndirectJump, IndirectCall, + Trap, }; struct DecodedInstructionInfo { diff --git a/core/codegen/src/x86/X64InstructionDecoder.cpp b/core/codegen/src/x86/X64InstructionDecoder.cpp index c367e3577..c78851947 100644 --- a/core/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/codegen/src/x86/X64InstructionDecoder.cpp @@ -440,6 +440,10 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( } } } else { + if (op == 0x0B) { + info.FlowKind = ControlFlowKind::Trap; + return info; + } if (op >= TwoByteJccRel32Min && op <= TwoByteJccRel32Max) { std::int32_t disp = 0; std::memcpy(&disp, data + pos, 4); diff --git a/core/main.cpp b/core/main.cpp index da642f5c0..0f56a3ab1 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -26,7 +26,8 @@ int main(const int argc, char* argv[]) { bool skipSyscallCheck = false; bool toIntel = false; - bool skipUnusedNidFilter = false; + std::uint32_t unusedFilterLevel = 1; + bool unusedFilterSpecified = false; bool writeRegistry = false; bool toWindows = false; @@ -40,8 +41,14 @@ int main(const int argc, char* argv[]) { skipSyscallCheck = true; } else if (arg == "--to-intel") { toIntel = true; - } else if (arg == "--skip-unused-nid-filter") { - skipUnusedNidFilter = true; + } else if (arg.rfind("unused-filter=", 0) == 0) { + const std::string value = arg.substr(14); + if (unusedFilterSpecified || value.size() != 1 || value[0] < '0' || value[0] > '2') { + std::cerr << "FAIL: unused-filter must be specified once with a value of 0, 1 or 2\n"; + return 1; + } + unusedFilterLevel = static_cast(value[0] - '0'); + unusedFilterSpecified = true; } else if (arg == "--registry") { writeRegistry = true; } else if (arg == "--rpath") { @@ -52,6 +59,9 @@ int main(const int argc, char* argv[]) { runPath = argv[++i]; } else if (arg == "--windows") { toWindows = true; + } else if (arg.rfind("--", 0) == 0 || arg == "unused-filter") { + std::cerr << "FAIL: unknown option: " << arg << "\n"; + return 1; } else if (inputPath.empty()) { inputPath = arg; } else if (outputPath.empty()) { @@ -63,7 +73,7 @@ int main(const int argc, char* argv[]) { } if (inputPath.empty() || outputPath.empty()) { - std::cerr << "Usage: relinker [--windows] [--skip-syscall-check] [--to-intel] [--skip-unused-nid-filter] [--registry] [--rpath ] \n" + std::cerr << "Usage: relinker [--windows] [--skip-syscall-check] [--to-intel] [unused-filter=0|1|2] [--registry] [--rpath ] \n" "Example: relinker input.elf output.elf\n"; return 1; } @@ -75,6 +85,7 @@ int main(const int argc, char* argv[]) { auto sourceBytes = fileReader.Read(inputPath); if (toIntel) { + std::cout << "Mode: Intel instruction conversion; system unchanged; unused-filter=" << unusedFilterLevel << " (not applied)\n"; const Relinker::ElfReader elfReader(sourceBytes); const auto converter = Codegen::MakeAmd64OnlyConverter(); auto result = converter->Convert(std::move(sourceBytes), elfReader.ReadCodeSegments()); @@ -96,9 +107,10 @@ int main(const int argc, char* argv[]) { std::make_shared(), std::make_shared(), Relinker::MakeUnusedNidFilter(), - !skipUnusedNidFilter + unusedFilterLevel ); + std::cout << "System: " << (toWindows ? "Windows" : "Linux") << "; unused-filter=" << unusedFilterLevel << "\n"; auto result = pipeline->Relink(sourceBytes); if (writeRegistry) { @@ -126,6 +138,7 @@ int main(const int argc, char* argv[]) { } auto patched = patcher->Patch(sourceBytes, result.OriginalHeaders, result.DynamicSection, result.OriginalPltGotVaddr, runPath); fileWriter.Write(outputPath, patched); + std::cout << "OK: " << result.RegistryEntries.size() << " NID references processed; output written\n"; } catch (const Domain::RelinkerException& e) { std::cerr << "FAIL: " << e.what(); diff --git a/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp b/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp index e82c84c51..67be9ac4b 100644 --- a/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp +++ b/core/relinker/include/relinker/pipeline/RelinkerPipeline.hpp @@ -14,15 +14,7 @@ namespace Relinker { class RelinkerPipeline : public IRelinkerPipeline { public: - RelinkerPipeline( - std::shared_ptr elfReader, - std::shared_ptr syscallScanner, - std::shared_ptr callSiteResolver, - std::shared_ptr validationPolicy, - std::shared_ptr dynamicSectionBuilder, - std::shared_ptr unusedNidFilter, - bool filterUnusedNids - ); + RelinkerPipeline(std::shared_ptr elfReader, std::shared_ptr syscallScanner, std::shared_ptr callSiteResolver, std::shared_ptr validationPolicy, std::shared_ptr dynamicSectionBuilder, std::shared_ptr unusedNidFilter, std::uint32_t unusedFilterLevel); RelinkResult Relink(const std::vector& sourceElf) override; @@ -33,7 +25,7 @@ private: std::shared_ptr _validationPolicy; std::shared_ptr _dynamicSectionBuilder; std::shared_ptr _unusedNidFilter; - bool _filterUnusedNids; + std::uint32_t unusedFilterLevel; static constexpr std::uint32_t PT_LOAD = 1; static constexpr std::uint32_t PT_DYNAMIC = 2; diff --git a/core/relinker/src/analysis/UnusedNidFilter/ControlFlowGraph.cpp b/core/relinker/src/analysis/UnusedNidFilter/ControlFlowGraph.cpp index ce1c4a906..bb98c8277 100644 --- a/core/relinker/src/analysis/UnusedNidFilter/ControlFlowGraph.cpp +++ b/core/relinker/src/analysis/UnusedNidFilter/ControlFlowGraph.cpp @@ -98,6 +98,7 @@ std::unique_ptr BuildControlFlowGraph( break; case Return: case IndirectJump: + case Trap: break; } } diff --git a/core/relinker/src/pipeline/RelinkerPipeline.cpp b/core/relinker/src/pipeline/RelinkerPipeline.cpp index ef484e6c7..ff0bdefdb 100644 --- a/core/relinker/src/pipeline/RelinkerPipeline.cpp +++ b/core/relinker/src/pipeline/RelinkerPipeline.cpp @@ -6,22 +6,17 @@ namespace Relinker { -RelinkerPipeline::RelinkerPipeline( - std::shared_ptr elfReader, - std::shared_ptr syscallScanner, - std::shared_ptr callSiteResolver, - std::shared_ptr validationPolicy, - std::shared_ptr dynamicSectionBuilder, - std::shared_ptr unusedNidFilter, - bool filterUnusedNids) +RelinkerPipeline::RelinkerPipeline(std::shared_ptr elfReader, std::shared_ptr syscallScanner, std::shared_ptr callSiteResolver, std::shared_ptr validationPolicy, std::shared_ptr dynamicSectionBuilder, std::shared_ptr unusedNidFilter, std::uint32_t unusedFilterLevel) : _elfReader(std::move(elfReader)) , _syscallScanner(std::move(syscallScanner)) , _callSiteResolver(std::move(callSiteResolver)) , _validationPolicy(std::move(validationPolicy)) , _dynamicSectionBuilder(std::move(dynamicSectionBuilder)) , _unusedNidFilter(std::move(unusedNidFilter)) - , _filterUnusedNids(filterUnusedNids) -{} + , unusedFilterLevel(unusedFilterLevel) +{ + if (unusedFilterLevel > 2) throw RelinkerException("Unused NID filter level must be 0, 1 or 2"); +} std::string RelinkerPipeline::_relocationTypeName(std::uint32_t type) { switch (type) { @@ -201,7 +196,10 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf static constexpr std::uint32_t R_X86_64_JUMP_SLOT = 7; - if (_filterUnusedNids) { + const std::size_t originalNidCount = nidRefs.size(); + std::cout << "NID input: " << originalNidCount << " references\n"; + + if (unusedFilterLevel != 0) { std::vector pltRefs; std::vector nonPltRefs; for (const auto& ref : nidRefs) { @@ -211,14 +209,25 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf nonPltRefs.push_back(ref); } + std::cout << "PLT preservation: " << pltRefs.size() << " -> " << pltRefs.size() << "; filtered=0\n"; + const std::size_t nonPltCount = nonPltRefs.size(); nonPltRefs = _unusedNidFilter->Filter(nonPltRefs, raw, textSection, textVAddr); + if (nonPltRefs.size() > nonPltCount) throw RelinkerException("Unused NID filter increased the reference count"); + std::cout << "CFG/GOT filtering: " << nonPltCount << " -> " << nonPltRefs.size() << "; filtered=" << nonPltCount - nonPltRefs.size() << "\n"; + + if (unusedFilterLevel == 2) + std::cout << "Strict filtering: " << nonPltRefs.size() << " -> " << nonPltRefs.size() << "; filtered=0 (reserved; currently identical to level 1)\n"; nidRefs.clear(); nidRefs.reserve(pltRefs.size() + nonPltRefs.size()); for (auto& ref : pltRefs) nidRefs.push_back(std::move(ref)); for (auto& ref : nonPltRefs) nidRefs.push_back(std::move(ref)); + } else { + std::cout << "Unused NID filtering: disabled; filtered=0\n"; } + std::cout << "NID total: " << originalNidCount << " -> " << nidRefs.size() << "; filtered=" << originalNidCount - nidRefs.size() << "\n"; + auto dynSection = _dynamicSectionBuilder->BuildDynamicSection( nidRefs, neededLibraries, dynJmpRelOffset, static_cast(dynJmpRelSize / relaEntSize)); @@ -271,8 +280,6 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf entries.push_back(std::move(entry)); } - std::cout << "OK: " << entries.size() << " NID references processed\n"; - return RelinkResult{std::move(entries), std::move(programHeaders), std::move(dynSection), gotVAddr}; }