From 2acdf7724ab028d870aa35cb958f4d37d5bca15b Mon Sep 17 00:00:00 2001 From: Arsenii es3n1n Date: Sat, 15 Aug 2026 16:37:25 +0200 Subject: [PATCH] feat: add --firewall (#57) --- .github/workflows/build.yml | 2 +- README.md | 3 +- cxx-shared/shared/ctx.hpp | 3 ++ defendnot-loader/core/core.hpp | 10 +++++++ defendnot-loader/main.cpp | 5 +++- defendnot/bootstrap/bootstrap.cpp | 46 +++++++++++++++++++++---------- defendnot/core/com.hpp | 10 +++++++ 7 files changed, 61 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ecc5123..2472ff7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ permissions: jobs: build: - runs-on: windows-latest + runs-on: windows-2022 strategy: matrix: platform: [x64, x86, ARM64] diff --git a/README.md b/README.md index fa7168e..84c6a7b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Download the [latest](https://github.com/es3n1n/defendnot/releases/latest) relea ## Usage ```commandline -Usage: defendnot-loader [--help] [--version] [--name VAR] [--disable] [--verbose] [--silent] [--autorun-as-user] [--disable-autorun] +Usage: defendnot-loader [--help] [--version] [--name VAR] [--disable] [--verbose] [--firewall] [--silent] [--autorun-as-user] [--disable-autorun] Optional arguments: -h, --help prints help message and exits @@ -51,6 +51,7 @@ Optional arguments: -n, --name av display name [default: "dnot.sh"] -d, --disable disable defendnot -v, --verbose verbose logging + --firewall also register a fake firewall --silent do not allocate console --autorun-as-user create autorun task as currently logged in user --disable-autorun disable autorun task creation diff --git a/cxx-shared/shared/ctx.hpp b/cxx-shared/shared/ctx.hpp index 2e439b8..eeecf48 100644 --- a/cxx-shared/shared/ctx.hpp +++ b/cxx-shared/shared/ctx.hpp @@ -6,6 +6,7 @@ #include "shared/util.hpp" +#pragma pack(push, 1) namespace shared { constexpr std::size_t kMaxNameLength = 128; constexpr std::string_view kCtxPath = "ctx.bin"; @@ -28,6 +29,7 @@ namespace shared { State state = State::ON; bool verbose = false; std::array name = {0}; // +1 for the nullterm + bool register_firewall = false; void serialize() const { std::ofstream stream(detail::ctx_path(), std::ios::binary); @@ -50,3 +52,4 @@ namespace shared { static_assert(std::is_trivially_copyable_v); } // namespace shared +#pragma pack(pop) diff --git a/defendnot-loader/core/core.hpp b/defendnot-loader/core/core.hpp index fe60f75..7572f72 100644 --- a/defendnot-loader/core/core.hpp +++ b/defendnot-loader/core/core.hpp @@ -2,6 +2,8 @@ #include #include +#include "shared/ctx.hpp" + #include namespace loader { @@ -16,9 +18,17 @@ namespace loader { bool disable; bool alloc_console; bool verbose; + bool register_firewall; bool from_autorun; AutorunType autorun_type; bool enable_autorun; + + Config& operator=(const shared::Context& ctx) { + verbose = ctx.verbose; + register_firewall = ctx.register_firewall; + name = ctx.name.data(); + return *this; + } }; [[nodiscard]] HANDLE inject(std::string_view dll_path, std::string_view proc_name); diff --git a/defendnot-loader/main.cpp b/defendnot-loader/main.cpp index 4cf7975..bf3572d 100644 --- a/defendnot-loader/main.cpp +++ b/defendnot-loader/main.cpp @@ -25,6 +25,7 @@ namespace { shared::ctx.state = config.disable ? shared::State::OFF : shared::State::ON; shared::ctx.verbose = config.verbose; + shared::ctx.register_firewall = config.register_firewall; std::ranges::copy(config.name, shared::ctx.name.data()); /// No need to overwrite ctx if we are called from autorun @@ -100,6 +101,7 @@ int main(int argc, char* argv[]) try { program.add_argument("-n", "--name").help("av display name").default_value(std::string(strings::kDefaultAVName)).nargs(1); program.add_argument("-d", "--disable").help(std::format("disable {}", strings::kProjectName)).default_value(false).implicit_value(true); program.add_argument("-v", "--verbose").help("verbose logging").default_value(false).implicit_value(true); + program.add_argument("--firewall").help("also register a fake firewall").default_value(false).implicit_value(true); program.add_argument("--silent").help("do not allocate console").default_value(false).implicit_value(true); program.add_argument("--autorun-as-user").help("create autorun task as currently logged in user").default_value(false).implicit_value(true); program.add_argument("--disable-autorun").help("disable autorun task creation").default_value(false).implicit_value(true); @@ -120,6 +122,7 @@ int main(int argc, char* argv[]) try { .disable = program.get("-d"), .alloc_console = !program.get("--silent"), .verbose = program.get("-v"), + .register_firewall = program.get("--firewall"), .from_autorun = program.get("--from-autorun"), .autorun_type = program.get("--autorun-as-user") ? /// As system on boot is the default value loader::AutorunType::AS_CURRENT_USER_ON_LOGIN : @@ -130,7 +133,7 @@ int main(int argc, char* argv[]) try { /// When running from autorun, we'll be missing all the cli arguments, so lets load some relevant ones if (config.from_autorun) { shared::ctx.deserialize(); - config.verbose = shared::ctx.verbose; + config = shared::ctx; } if (!config.alloc_console && config.verbose) { diff --git a/defendnot/bootstrap/bootstrap.cpp b/defendnot/bootstrap/bootstrap.cpp index 096f3ee..4bf9c56 100644 --- a/defendnot/bootstrap/bootstrap.cpp +++ b/defendnot/bootstrap/bootstrap.cpp @@ -8,27 +8,41 @@ namespace defendnot { namespace { + void activate(const auto& step, IWscASStatus* inst) { + step("update", inst->UpdateStatus(WSCSecurityProductState::ON, TRUE)); + } + + void activate(const auto& step, IWscAVStatus4* inst) { + step("update", inst->UpdateStatus(WSCSecurityProductState::ON, TRUE)); + step("scan_update", inst->UpdateScanSubstatus(WSCSecurityProductSubStatus::NO_ACTION)); + step("settings_update", inst->UpdateSettingsSubstatus(WSCSecurityProductSubStatus::NO_ACTION)); + step("prot_update", inst->UpdateProtectionUpdateSubstatus(WSCSecurityProductSubStatus::NO_ACTION)); + } + + void activate(const auto& step, IWscFWStatus2* inst) { + step("update", inst->UpdateStatus(WSCSecurityProductState::ON)); + step("domain_update", inst->UpdateDomainProfileSubstatus(WSCSecurityProductSubStatus::NO_ACTION)); + step("private_update", inst->UpdatePrivateProfileSubstatus(WSCSecurityProductSubStatus::NO_ACTION)); + step("public_update", inst->UpdatePublicProfileSubstatus(WSCSecurityProductSubStatus::NO_ACTION)); + } + template - void apply(const std::string_view log_prefix, const BSTR name) { + void apply(const std::string_view log_prefix, const BSTR name, const bool should_register) { /// Get the WSC interface auto inst = com::query(); - /// This can fail if we dont have any products registered so no com_checked + /// This can fail if we dont have any products registered so no com::checked logln("{}_unregister: {:#x}", log_prefix, com::retry_while_pending([&inst]() -> HRESULT { return inst->Unregister(); }) & 0xFFFFFFFF); - if (shared::ctx.state == shared::State::OFF) { + if (!should_register) { return; } /// Register and activate - logln("{}_register: {:#x}", log_prefix, com::checked(inst->Register(name, name, 0, 0))); - logln("{}_update: {:#x}", log_prefix, com::checked(inst->UpdateStatus(WSCSecurityProductState::ON, static_cast(true)))); - - /// Update the substatuses, if the interface supports this - if constexpr (std::is_same_v) { - logln("{}_scan_update: {:#x}", log_prefix, com::checked(inst->UpdateScanSubstatus(WSCSecurityProductSubStatus::NO_ACTION))); - logln("{}_settings_update: {:#x}", log_prefix, com::checked(inst->UpdateSettingsSubstatus(WSCSecurityProductSubStatus::NO_ACTION))); - logln("{}_prot_update: {:#x}", log_prefix, com::checked(inst->UpdateProtectionUpdateSubstatus(WSCSecurityProductSubStatus::NO_ACTION))); - } + const auto step = [&](const std::string_view what, const HRESULT hr) -> void { + logln("{}_{}: {:#x}", log_prefix, what, com::checked(hr)); + }; + step("register", inst->Register(name, name, 0, 0)); + activate(step, inst.get()); } } // namespace @@ -45,12 +59,14 @@ namespace defendnot { /// Convert to BSTR auto name = SysAllocString(name_w.c_str()); - defer->void { + defer { SysFreeString(name); }; /// Register our stuff in the WSC interfaces - apply("IWscASStatus", name); - apply("IWscAVStatus4", name); + const bool enabled = shared::ctx.state != shared::State::OFF; + apply("IWscASStatus", name, enabled); + apply("IWscAVStatus4", name, enabled); + apply("IWscFWStatus2", name, enabled && shared::ctx.register_firewall); } } // namespace defendnot diff --git a/defendnot/core/com.hpp b/defendnot/core/com.hpp index 0e73888..56b484d 100644 --- a/defendnot/core/com.hpp +++ b/defendnot/core/com.hpp @@ -69,4 +69,14 @@ namespace defendnot { virtual HRESULT COM_CALLCONV Unregister() = 0; virtual HRESULT COM_CALLCONV UpdateStatus(WSCSecurityProductState state, BOOL unk) = 0; }; + + class IWscFWStatus2 : public com::IBaseObject { + public: + virtual HRESULT COM_CALLCONV Register(BSTR path_to_signed_product_exe, BSTR display_name, std::uint32_t, std::uint32_t) = 0; + virtual HRESULT COM_CALLCONV Unregister() = 0; + virtual HRESULT COM_CALLCONV UpdateStatus(WSCSecurityProductState state) = 0; + virtual HRESULT COM_CALLCONV UpdateDomainProfileSubstatus(WSCSecurityProductSubStatus status) = 0; + virtual HRESULT COM_CALLCONV UpdatePrivateProfileSubstatus(WSCSecurityProductSubStatus status) = 0; + virtual HRESULT COM_CALLCONV UpdatePublicProfileSubstatus(WSCSecurityProductSubStatus status) = 0; + }; } // namespace defendnot