diff --git a/cxx-shared/cxx-shared.vcxitems b/cxx-shared/cxx-shared.vcxitems index 081badd..176a9e5 100644 --- a/cxx-shared/cxx-shared.vcxitems +++ b/cxx-shared/cxx-shared.vcxitems @@ -18,6 +18,7 @@ + \ No newline at end of file diff --git a/cxx-shared/shared/native.hpp b/cxx-shared/shared/native.hpp new file mode 100644 index 0000000..e7235b9 --- /dev/null +++ b/cxx-shared/shared/native.hpp @@ -0,0 +1,51 @@ +#pragma once +#include +#include +#include + +#pragma pack(push, 1) +namespace native { + class PEB { + public: + std::uint8_t inherited_address_space; + std::uint8_t read_image_file_exec_options; + /// - we don't need other fields + }; + + static_assert(offsetof(PEB, read_image_file_exec_options) == 1); + + template + inline Ty get_system_routine(const std::string_view module_name, const std::string_view function_name) { + const auto mod = GetModuleHandleA(module_name.data()); + if (mod == nullptr) { + throw std::runtime_error(std::format("unable to find module {}", module_name)); + } + + auto function = reinterpret_cast(GetProcAddress(mod, function_name.data())); + if (function == nullptr) { + throw std::runtime_error(std::format("unable to obtain {} from {}", module_name, function_name)); + } + return function; + } + + inline PEB* get_peb() { + static auto function = get_system_routine("ntdll.dll", "RtlGetCurrentPeb"); + static auto result = function(); + if (result == nullptr) [[unlikely]] { + throw std::runtime_error("no peb"); + } + + return result; + } + + inline bool debug_set_process_kill_on_exit(const bool value) { + static auto function = get_system_routine("kernel32.dll", "DebugSetProcessKillOnExit"); + return static_cast(function(static_cast(value))); + } + + inline bool debug_active_process_stop(const std::uint32_t process_id) { + static auto function = get_system_routine("kernel32.dll", "DebugActiveProcessStop"); + return static_cast(function(process_id)); + } +} // namespace native +#pragma pack(pop) diff --git a/defendnot-loader/core/inject.cpp b/defendnot-loader/core/inject.cpp index 6cf26fe..4834c97 100644 --- a/defendnot-loader/core/inject.cpp +++ b/defendnot-loader/core/inject.cpp @@ -1,6 +1,8 @@ #include "core/core.hpp" #include "shared/defer.hpp" +#include "shared/native.hpp" + #include #include @@ -19,11 +21,20 @@ namespace loader { .bInheritHandle = TRUE, }; + /// By setting ReadImageFileExecOptions to FALSE and attaching ourselves as a debugger we can skip the IFEO + /// \xref: https://github.com/es3n1n/defendnot/issues/7#issuecomment-2874903650 + native::get_peb()->read_image_file_exec_options = 0; + std::println("** booting {}", proc_name); - if (!CreateProcessA(nullptr, const_cast(proc_name.data()), &sa, &sa, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) { + const auto process_flags = CREATE_SUSPENDED | DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; + if (!CreateProcessA(nullptr, const_cast(proc_name.data()), &sa, &sa, FALSE, process_flags, nullptr, nullptr, &si, &pi)) { throw std::runtime_error(std::format("unable to create process: {}", GetLastError())); } + /// Detach + native::debug_set_process_kill_on_exit(false); + native::debug_active_process_stop(pi.dwProcessId); + defer->void { CloseHandle(pi.hThread); /// Not closing hProcess because we return it