diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 875a463f4..208dae4bf 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -269,6 +269,15 @@ if(BUILD_TESTING) ) add_test(NAME windows_icon_resource COMMAND windows_icon_resource_tests) + add_test_executable(entry_stub_tests + ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/tests/EntryStubTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/general/EntryStubBuilder.cpp + ) + target_include_directories(entry_stub_tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include + ) + add_test(NAME entry_stub COMMAND entry_stub_tests) + add_test_executable(strict_nid_filter_tests ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/StrictNidFilterTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/UnusedNidFilter/StrictNidFilter.cpp diff --git a/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp b/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp index 51857f69b..bdb56e31a 100644 --- a/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp +++ b/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp @@ -166,6 +166,8 @@ inline constexpr std::uint32_t kDynamicLinkToStrTab = 2; inline constexpr std::uint8_t kStubOpAndRsp0xf0[] = {0x48, 0x83, 0xe4, 0xf0}; inline constexpr std::uint8_t kStubOpMovRdiRsp[] = {0x48, 0x89, 0xe7}; inline constexpr std::uint8_t kStubOpXorRsiRsi[] = {0x48, 0x31, 0xf6}; +inline constexpr std::uint8_t kStubOpPushZero[] = {0x6a, 0x00}; +inline constexpr std::uint8_t kStubOpMovRbpRsp[] = {0x48, 0x89, 0xe5}; inline constexpr std::uint8_t kStubOpCallRel32 = 0xe8; inline constexpr std::size_t kStubCallInstructionSize = 5; inline constexpr std::uint8_t kStubOpUd2[] = {0x0f, 0x0b}; diff --git a/core/relinker/elfpatcher/src/general/EntryStubBuilder.cpp b/core/relinker/elfpatcher/src/general/EntryStubBuilder.cpp index 21bc763d8..09f8f6794 100644 --- a/core/relinker/elfpatcher/src/general/EntryStubBuilder.cpp +++ b/core/relinker/elfpatcher/src/general/EntryStubBuilder.cpp @@ -17,6 +17,9 @@ std::vector EntryStubBuilder::BuildEntryStub( std::vector s; _appendBytes(s, kStubOpMovRdiRsp, sizeof(kStubOpMovRdiRsp)); _appendBytes(s, kStubOpAndRsp0xf0, sizeof(kStubOpAndRsp0xf0)); + _appendBytes(s, kStubOpPushZero, sizeof(kStubOpPushZero)); + _appendBytes(s, kStubOpPushZero, sizeof(kStubOpPushZero)); + _appendBytes(s, kStubOpMovRbpRsp, sizeof(kStubOpMovRbpRsp)); _appendBytes(s, kStubOpXorRsiRsi, sizeof(kStubOpXorRsiRsi)); const std::uint64_t callInsnVaddr = stubVaddr + s.size(); const std::uint64_t callNextVaddr = callInsnVaddr + kStubCallInstructionSize; diff --git a/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp index 068954f28..2efcc5e42 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsEntryStubBuilder.cpp @@ -426,6 +426,7 @@ WindowsEntryStub WindowsEntryStubBuilder::Build(const std::uint32_t dataRva, con call("FreeLibrary"); code.Rip({0x48, 0x8b, 0x3d}, argumentBlock); const auto exitCallback = code.Branch({0x48, 0x8d, 0x35}); + code.Emit({0x48, 0xc7, 0x44, 0x24, 0x40, 0, 0, 0, 0, 0x48, 0xc7, 0x44, 0x24, 0x48, 0, 0, 0, 0, 0x48, 0x8d, 0x6c, 0x24, 0x40}); code.Rip({0xe8}, entryRva); code.Emit({0x89, 0x44, 0x24, 0x58}); guestStartup.Finalize(code, guestModules, handles, guestFinished); diff --git a/core/relinker/elfpatcher/tests/EntryStubTests.cpp b/core/relinker/elfpatcher/tests/EntryStubTests.cpp new file mode 100644 index 000000000..cd3737ed0 --- /dev/null +++ b/core/relinker/elfpatcher/tests/EntryStubTests.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace { + +void require(const bool condition, const std::string& message) { + if (!condition) throw std::runtime_error(message); +} + +bool bytesAt(const std::vector& stub, std::size_t& offset, const std::vector& expected) { + if (offset + expected.size() > stub.size() || std::memcmp(stub.data() + offset, expected.data(), expected.size()) != 0) return false; + offset += expected.size(); + return true; +} + +void stubTerminatesTheFrameChain() { + constexpr std::uint64_t stubVaddr = 0xb32db98; + constexpr std::uint64_t entryVaddr = 0x80; + const auto stub = Elfpatcher::EntryStubBuilder().BuildEntryStub(stubVaddr, entryVaddr); + std::size_t offset = 0; + require(bytesAt(stub, offset, {0x48, 0x89, 0xe7}), "the stub does not pass the initial stack pointer in rdi"); + require(bytesAt(stub, offset, {0x48, 0x83, 0xe4, 0xf0}), "the stub does not align the stack to 16 bytes"); + require(bytesAt(stub, offset, {0x6a, 0x00, 0x6a, 0x00}), "the stub does not push a zero frame record"); + require(bytesAt(stub, offset, {0x48, 0x89, 0xe5}), "the stub does not point rbp at the zero frame record"); + require(bytesAt(stub, offset, {0x48, 0x31, 0xf6}), "the stub does not clear rsi"); + require(offset < stub.size() && stub[offset] == 0xe8, "the stub does not call the entry point"); + std::int32_t rel32 = 0; + std::memcpy(&rel32, stub.data() + offset + 1, sizeof(rel32)); + offset += 5; + require(stubVaddr + offset + static_cast(rel32) == entryVaddr, "the stub calls the wrong address"); + require(bytesAt(stub, offset, {0x0f, 0x0b}) && offset == stub.size(), "the stub does not end with ud2 after the call"); +} + +} + +int main() { + try { + stubTerminatesTheFrameChain(); + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } + return 0; +}