From 28b985a783a24eed5ecc907b48c173294b778103 Mon Sep 17 00:00:00 2001 From: Richard Obermayr <6305520+oberrich@users.noreply.github.com> Date: Fri, 19 Aug 2022 01:11:04 +0200 Subject: [PATCH] RELEASE --- .gitignore | 38 +++++++++ LICENSE | 21 +++++ README.md | 28 +++++++ main.cpp | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++ meson.build | 27 ++++++ 5 files changed, 344 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 main.cpp create mode 100644 meson.build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23d10df --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +/.vs +/.idea +/build/ +/deps/ + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f06675d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 oberrich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e15e97a --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# win11-toggle-rounded-corners +A simple utility that does **NOT** patch dwm (uDWM.dll) in order to disable window rounded corners in Windows 11 + +No system files are being replaced so you **won't** brick your system. All this tool really does is setting a bool inside the heap of the Desktop Window Manager (DWM), to be more precise inside the `udwm.dll`s singleton instance of `CDesktopManager`. + +**Demonstration** +
+ +**Download** + +Precompiled binaries are available [**here**](https://github.com/oberrich/win11-toggle-rounded-corners/releases) *(Some Anti-Virus products may block the access to `dwm.exe` in which case you have to disable them temporarily)*. + +To permanently disable rounded corners put the app into your auto-start (Task Manager > Startup apps > Run new task > `path/to/win11-toggle-rounded-edges.exe`). + +**Build** + +First clone the repo **recursive**ly +``` +git clone --recursive 'https://github.com/oberrich/win11-toggle-rounded-corners.git' +``` + +Then simply build it with meson +``` +meson setup build +meson compile -C build +``` + +If you find any bugs or issues feel free to [open an issue](https://github.com/oberrich/win11-toggle-rounded-corners/issues/new). diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1264de6 --- /dev/null +++ b/main.cpp @@ -0,0 +1,230 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#define CALLBACK_ASSERT(cond) \ + if (!(cond)) \ + return DisassembleStatus::kFailed; + +enum class DisassembleStatus +{ + kContinue, + kFailed, + kSuccess, + kFollow, + kZydisError +}; + +using DisassembleCallbackT = DisassembleStatus(ZydisDecodedInstruction &, ZydisDecodedOperand *, uint64_t &); + +ZydisDecoder decoder; +ZydisFormatter formatter; + +void zydis_init() +{ + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); + ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); + ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_IMM_SIGNEDNESS, ZYDIS_SIGNEDNESS_AUTO); +} + +void zydis_print_instrn(ZydisDecodedInstruction const &instrn, ZydisDecodedOperand const *operands, uint64_t address) noexcept +{ + printf(" %016" PRIX64 " ", address); + char buffer[256]; + ZydisFormatterFormatInstruction(&formatter, &instrn, operands, instrn.operand_count_visible, buffer, sizeof(buffer), address, ZYAN_NULL); + puts(buffer); +} + +DisassembleStatus zydis_disassemble(uint64_t address, const std::function &callback) noexcept +{ + CALLBACK_ASSERT(address != 0); + + ZydisDecodedInstruction instrn{}; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE]; + + while (ZYAN_SUCCESS(ZydisDecoderDecodeFull(&decoder, reinterpret_cast(address), 32, &instrn, operands, ZYDIS_MAX_OPERAND_COUNT_VISIBLE, ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY))) { + if (auto status = callback(instrn, operands, address); status != DisassembleStatus::kContinue) { + if (status == DisassembleStatus::kFollow) { + CALLBACK_ASSERT(ZydisCalcAbsoluteAddress(&instrn, operands, address, &address)); + continue; + } + return status; + } + address += instrn.length; + } + + return DisassembleStatus::kZydisError; +} + +struct desktop_manager_proto +{ + void *unknown0[3]; + uint8_t unknown1[2]; + bool rounded_shadow_enabled; + bool enable_sharp_corners; + bool enable_rounded_corners; +}; + +static_assert(offsetof(desktop_manager_proto, enable_rounded_corners) == 0x1C, + "alignment issues (wrong arch)"); + +std::optional locate_udwm_desktop_manager() noexcept +{ + auto const udwm_dll = LoadLibraryExA("udwm.dll", nullptr, DONT_RESOLVE_DLL_REFERENCES); + if (!udwm_dll) + return {}; + + auto const dwm_client_startup = reinterpret_cast(GetProcAddress(udwm_dll, MAKEINTRESOURCE(101))); + if (!dwm_client_startup) + return {}; + + struct Context { + uint64_t dm_instance; + bool found_dm_create; + }; + + Context ctx{}; + auto callback = [&ctx](auto const &instrn, auto const operands, auto &address) noexcept -> DisassembleStatus { + CALLBACK_ASSERT(instrn.mnemonic != ZYDIS_MNEMONIC_RET); + zydis_print_instrn(instrn, operands, address); + + if (!ctx.found_dm_create && instrn.mnemonic == ZYDIS_MNEMONIC_CALL) { + ctx.found_dm_create = true; + return DisassembleStatus::kFollow; + } + + auto const &lhs = operands[0]; + if (instrn.mnemonic == ZYDIS_MNEMONIC_MOV && lhs.type == ZYDIS_OPERAND_TYPE_MEMORY && lhs.mem.segment == ZYDIS_REGISTER_DS) { + ZydisCalcAbsoluteAddress(&instrn, operands, address, &ctx.dm_instance); + return DisassembleStatus::kSuccess; + } + + return DisassembleStatus::kContinue; + }; + + if (zydis_disassemble(dwm_client_startup, callback) != DisassembleStatus::kSuccess || !ctx.dm_instance) + return {}; + + return { ctx.dm_instance - (uint64_t)udwm_dll }; +} + +std::optional find_module_base(DWORD pid, std::string_view module_name) noexcept +{ + auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); + if (snapshot != INVALID_HANDLE_VALUE) { + auto entry = MODULEENTRY32{ .dwSize = sizeof(MODULEENTRY32) }; + + if (Module32First(snapshot, &entry)) { + do { + if (module_name == entry.szModule) { + CloseHandle(snapshot); + return { (uint64_t)entry.modBaseAddr }; + } + } while (Module32Next(snapshot, &entry)); + } + } + CloseHandle(snapshot); + return {}; +} + +bool enable_privilege(LPCTSTR name) noexcept +{ + TOKEN_PRIVILEGES privilege{}; + privilege.PrivilegeCount = 1; + privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!LookupPrivilegeValue(nullptr, name, &privilege.Privileges[0].Luid)) + return false; + + HANDLE token{}; + if (!OpenProcessToken(reinterpret_cast(-1), TOKEN_ADJUST_PRIVILEGES, &token)) + return false; + + if (!AdjustTokenPrivileges(token, FALSE, &privilege, sizeof privilege, nullptr, nullptr)) { + CloseHandle(token); + return false; + } + + CloseHandle(token); + return true; +} + +int main() try +{ + if (!enable_privilege(SE_DEBUG_NAME)) + throw std::runtime_error(std::format("Failed enable {}!", SE_DEBUG_NAME)); + + auto const dwm_hwnd = FindWindowA("Dwm", nullptr); + DWORD dwm_pid = 0u; + if (!dwm_hwnd || !GetWindowThreadProcessId(dwm_hwnd, &dwm_pid)) + throw std::runtime_error("Failed to find dwm process.\n"); + + std::cout << std::format("Found dwm.exe process [window handle: {}, pid: {}].\n", static_cast(dwm_hwnd), dwm_pid); + + auto const dwm_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwm_pid); + if (!dwm_process) + throw std::runtime_error(std::format("Failed to open dwm.exe process, status: {:#x}!", GetLastError())); + + std::cout << std::format("Opened process handle {:#x} to dwm.exe.\nLocating CDesktopManager *g_pdmInstance:\n", (uint64_t)dwm_process); + + zydis_init(); + + auto const desktop_manager_rva = locate_udwm_desktop_manager(); + if (!desktop_manager_rva) + throw std::runtime_error("Failed to locate g_pdmInstance RVA inside udwm.dll."); + + std::cout << std::format("Found g_pdmInstance at RVA {:#x}.\n", desktop_manager_rva.value()); + + auto const dwm_base = find_module_base(dwm_pid, std::string_view{"udwm.dll"}); + if (!dwm_base) + throw std::runtime_error("Failed to find udwm.dll module inside dwm.exe process!"); + + std::cout << std::format("Found udwm.dll mapped at {:#x}.\n", dwm_base.value()); + + auto desktop_manager_ptr = reinterpret_cast(dwm_base.value() + desktop_manager_rva.value()); + uint64_t desktop_manager_inst{}; + SIZE_T out_size{}; + if (!ReadProcessMemory(dwm_process, desktop_manager_ptr, &desktop_manager_inst, sizeof(void *), &out_size) || !desktop_manager_inst) + throw std::runtime_error(std::format("Failed to read value of g_pdmInstance from dwm.exe , status: {:#x}.\n", GetLastError())); + + auto desktop_manager = reinterpret_cast(desktop_manager_inst); + std::cout << std::format(" g_pdmInstance = (CDesktopManager *){:#x};\n\n", desktop_manager_inst); + + bool enable_sharp_corners = true; + out_size = {}; + if (!ReadProcessMemory(dwm_process, &desktop_manager->enable_sharp_corners, &enable_sharp_corners, 1, &out_size) || out_size != 1) + std::cerr << std::format("Failed to read 'enable_sharp_corners' from dwm.exe, status: {:#x}.\n", GetLastError()); + + constexpr std::array boolean_values { + "disabled", + "enabled" + }; + + std::cout << std::format("Your rounded corners were '{}', they are now being {}...\n", boolean_values[enable_sharp_corners], boolean_values[(!enable_sharp_corners)]); + enable_sharp_corners ^= true; + + out_size = {}; + if (!WriteProcessMemory(dwm_process, &desktop_manager->enable_sharp_corners, &enable_sharp_corners, 1, &out_size) || out_size != 1) + throw std::runtime_error(std::format("Failed to write 'enable_sharp_corners' to dwm.exe, status: {:#x}.\n", GetLastError())); + + std::cout << "Success!"; + + if (enable_sharp_corners) + std::cout << " Your Windows 11 experience is now enhanced!\n"; + + return 0; +} catch (std::exception const &e) { + std::cerr << e.what() << '\n'; + return 1; +} \ No newline at end of file diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..9dde1d1 --- /dev/null +++ b/meson.build @@ -0,0 +1,27 @@ +project('win11-toggle-rounded-corners', ['c', 'cpp'], + version : '0.x', + default_options : [ + 'warning_level=3', + 'cpp_std=c++latest', + 'buildtype=release', + 'prefer_static=true', + 'default_library=static', + 'b_vscrt=static_from_buildtype' + ], + subproject_dir : 'deps') + +cmake = import('cmake') +zydis_opts = cmake.subproject_options() +zydis_opts.add_cmake_defines({'ZYDIS_BUILD_TOOLS': false, 'ZYDIS_BUILD_EXAMPLES': false}) + + +cc = meson.get_compiler('c') +if cc.get_id() == 'msvc' + zydis_opts.append_compile_args(['c', 'cpp'], ['/MT', '/DEBUG:NONE']) + zydis_opts.append_link_args(['c', 'cpp'], ['/EMITPOGOPHASEINFO', '/ASSEMBLYDEBUG:DISABLE']) +endif + +zydis_proj = cmake.subproject('zydis', options : zydis_opts) +zydis_depends = zydis_proj.dependency('Zydis') + +executable('win11-toggle-rounded-corners', 'main.cpp', install : true, dependencies: [zydis_depends])