#include #include "loader/component_loader.hpp" #include "scheduler.hpp" #include namespace arxan { namespace { DWORD get_steam_pid() { static auto steam_pid = [] { HKEY reg_key; DWORD pid{}; if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Valve\\Steam\\ActiveProcess", 0, KEY_QUERY_VALUE, ®_key) != ERROR_SUCCESS) return pid; DWORD length = sizeof(pid); RegQueryValueExA(reg_key, "pid", nullptr, nullptr, reinterpret_cast(&pid), &length); RegCloseKey(reg_key); return pid; }(); return steam_pid; } utils::hook::detour nt_close_hook; utils::hook::detour nt_query_system_information_hook; utils::hook::detour nt_query_information_process_hook; utils::hook::detour create_mutex_ex_a_hook; utils::hook::detour get_window_text_a_hook; utils::hook::detour get_window_text_w_hook; utils::hook::detour get_class_name_a_hook; utils::hook::detour get_class_name_w_hook; HANDLE create_mutex_ex_a_stub(const LPSECURITY_ATTRIBUTES attributes, const LPCSTR name, const DWORD flags, const DWORD access) { if (name == "$ IDA trusted_idbs"s || name == "$ IDA registry mutex $"s) { return nullptr; } return create_mutex_ex_a_hook.invoke(attributes, name, flags, access); } bool remove_evil_keywords_from_string(const UNICODE_STRING& string) { static const std::wstring evil_keywords[] = { L"IDA", L"ida", L"HxD", L"cheatengine", L"Cheat Engine", }; if (!string.Buffer || !string.Length) { return false; } std::wstring_view path(string.Buffer, string.Length / sizeof(string.Buffer[0])); bool modified = false; for (const auto& keyword : evil_keywords) { while (true) { const auto pos = path.find(keyword); if (pos == std::wstring::npos) { break; } modified = true; for (size_t i = 0; i < keyword.size(); ++i) { string.Buffer[pos + i] = L'a'; } } } return modified; } bool remove_evil_keywords_from_string(wchar_t* str, const size_t length) { UNICODE_STRING unicode_string{}; unicode_string.Buffer = str; unicode_string.Length = static_cast(length); unicode_string.MaximumLength = unicode_string.Length; return remove_evil_keywords_from_string(unicode_string); } bool remove_evil_keywords_from_string(char* str, const size_t length) { std::string_view str_view(str, length); std::wstring wstr(str_view.begin(), str_view.end()); if (!remove_evil_keywords_from_string(&wstr[0], wstr.size())) { return false; } const std::string regular_str(wstr.begin(), wstr.end()); memcpy(str, regular_str.data(), length); return true; } int WINAPI get_window_text_w_stub(const HWND wnd, const LPWSTR str, const int max_count) { const auto res = get_window_text_w_hook.invoke(wnd, str, max_count); if (res) { remove_evil_keywords_from_string(str, res); } return res; } int WINAPI get_window_text_a_stub(const HWND wnd, const LPSTR str, const int max_count) { const auto res = get_window_text_a_hook.invoke(wnd, str, max_count); if (res) { remove_evil_keywords_from_string(str, res); } return res; } int WINAPI get_class_name_a_stub(const HWND wnd, const LPSTR class_name, const int max_count) { const auto res = get_class_name_a_hook.invoke(wnd, class_name, max_count); if (res) { remove_evil_keywords_from_string(class_name, res); } return res; } int WINAPI get_class_name_w_stub(const HWND wnd, const LPWSTR class_name, const int max_count) { const auto res = get_class_name_w_hook.invoke(wnd, class_name, max_count); if (res) { remove_evil_keywords_from_string(class_name, res); } return res; } NTSTATUS NTAPI nt_query_system_information_stub(const SYSTEM_INFORMATION_CLASS system_information_class, const PVOID system_information, const ULONG system_information_length, const PULONG return_length) { const auto status = nt_query_system_information_hook.invoke( system_information_class, system_information, system_information_length, return_length); if (NT_SUCCESS(status)) { if (system_information_class == SystemProcessInformation) { auto addr = static_cast(system_information); while (true) { const auto info = reinterpret_cast(addr); remove_evil_keywords_from_string(info->ImageName); if (!info->NextEntryOffset) { break; } addr = addr + info->NextEntryOffset; } } } return status; } NTSTATUS WINAPI nt_query_information_process_stub(HANDLE handle, const PROCESSINFOCLASS info_class, const PVOID info, const ULONG info_length, const PULONG ret_length) { auto* orig = static_cast(nt_query_information_process_hook. get_original()); const auto status = orig(handle, info_class, info, info_length, ret_length); if (NT_SUCCESS(status)) { if (info_class == ProcessBasicInformation) { static_cast(info)->Reserved3 = PVOID(DWORD64(get_steam_pid())); } else if (info_class == 30) // ProcessDebugObjectHandle { *static_cast(info) = nullptr; return 0xC0000353; } else if (info_class == ProcessImageFileName || info_class == 43) { remove_evil_keywords_from_string(*static_cast(info)); } else if (info_class == 7) // ProcessDebugPort { *static_cast(info) = nullptr; } else if (info_class == 31) { *static_cast(info) = 1; } } return status; } NTSTATUS NTAPI nt_close_stub(const HANDLE handle) { char info[16]; if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345) { auto* orig = static_cast(nt_close_hook.get_original()); return orig(handle); } return STATUS_INVALID_HANDLE; } LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info) { if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE) { return EXCEPTION_CONTINUE_EXECUTION; } if (info->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) { //MessageBoxA(0, 0, "AV", 0); } return EXCEPTION_CONTINUE_SEARCH; } void hide_being_debugged() { auto* const peb = PPEB(__readgsqword(0x60)); peb->BeingDebugged = false; *reinterpret_cast(LPSTR(peb) + 0xBC) &= ~0x70; } void restore_debug_functions() { static const char* functions[] = { "DbgBreakPoint", "DbgUserBreakPoint", "DbgUiConnectToDbg", "DbgUiContinue", "DbgUiConvertStateChangeStructure", "DbgUiDebugActiveProcess", "DbgUiGetThreadDebugObject", "DbgUiIssueRemoteBreakin", "DbgUiRemoteBreakin", "DbgUiSetThreadDebugObject", "DbgUiStopDebugging", "DbgUiWaitStateChange", "DbgPrintReturnControlC", "DbgPrompt", }; using buffer = uint8_t[15]; static buffer buffers[ARRAYSIZE(functions)] = {}; static bool loaded = false; const utils::nt::library ntdll("ntdll.dll"); for (int i = 0; i < ARRAYSIZE(functions); ++i) { const auto func = ntdll.get_proc(functions[i]); if (!loaded) { memcpy(buffers[i], func, sizeof(buffer)); } else { utils::hook::copy(func, buffers[i], sizeof(buffer)); } } loaded = true; } } class component final : public component_interface { public: void post_load() override { hide_being_debugged(); scheduler::loop(hide_being_debugged, scheduler::pipeline::async); //restore_debug_functions(); create_mutex_ex_a_hook.create(CreateMutexExA, create_mutex_ex_a_stub); const utils::nt::library ntdll("ntdll.dll"); nt_close_hook.create(ntdll.get_proc("NtClose"), nt_close_stub); const auto nt_query_information_process = ntdll.get_proc("NtQueryInformationProcess"); nt_query_information_process_hook.create(nt_query_information_process, nt_query_information_process_stub); utils::hook::move_hook(nt_query_information_process); const auto nt_query_system_information = ntdll.get_proc("NtQuerySystemInformation"); nt_query_system_information_hook.create(nt_query_system_information, nt_query_system_information_stub); utils::hook::move_hook(nt_query_system_information); // Satisfy arxan /*get_window_text_a_hook.create(GetWindowTextA, get_window_text_a_stub); get_window_text_w_hook.create(GetWindowTextW, get_window_text_w_stub); get_class_name_a_hook.create(GetClassNameA, get_class_name_a_stub); get_class_name_w_hook.create(GetClassNameW, get_class_name_w_stub); // Satisfy arxan utils::hook::move_hook(GetWindowTextA); utils::hook::move_hook(GetWindowTextW); utils::hook::move_hook(GetClassNameA); utils::hook::move_hook(GetClassNameW);*/ AddVectoredExceptionHandler(1, exception_filter); } }; } REGISTER_COMPONENT(arxan::component)