8 static wind_config_t cfg
= {(void*)(-(LONG
)sizeof(cfg
))};
9 static KMUTEX ioctl_mutex
;
13 // What follows is the meat of the whole DSE bypass.
15 // We temporarily flip the ci_Options to not validate, load driver, flip
18 static void ci_restore()
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
));
25 static NTSTATUS
driver_sideload(PUNICODE_STRING svc
)
29 // Clear ci_Options. Daaaaanger zone.
33 status
= ZwLoadDriver(svc
);
35 // Restore ci_Options.
41 // The rest is just boring driver boilerplate...
42 static NTSTATUS NTAPI
dev_open(IN PDEVICE_OBJECT dev
, IN PIRP irp
)
44 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
45 irp
->IoStatus
.Information
= 0;
46 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
47 return STATUS_SUCCESS
;
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
)
55 DBG("unlock %d %p %p\n",lock
,tkb
,kb
);
59 if (tkb
->KeyControlBlock
!= kb
->KeyControlBlock
)
61 nb
= tkb
->NotifyBlock
;
71 if (nb
->KeyControlBlock
!= kb
->KeyControlBlock
)
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
) {
80 DBG("unlock: changing filter from %x to %x", nb
->Filter
, f
.n
);
84 if (lock
&& f
.high
&& !f
.low
) {
87 DBG("re-lock: changing filter from %x to %x", nb
->Filter
, f
.n
);
92 if (!nb
->HiveList
.Flink
)
94 nb
= CONTAINING_RECORD(nb
->HiveList
.Flink
,
95 CM_NOTIFY_BLOCK
, HiveList
);
100 static NTSTATUS
open_key(HANDLE
*h
, PUNICODE_STRING name
)
102 OBJECT_ATTRIBUTES attr
= {
103 .Length
= sizeof(attr
),
104 .Attributes
= OBJ_KERNEL_HANDLE
|OBJ_CASE_INSENSITIVE
,
107 return ZwOpenKey(h
, KEY_READ
, &attr
);
110 // Stop/reenable notifications on registry key.
111 static NTSTATUS
reg_set_notify(PUNICODE_STRING name
, int lock
)
113 #define KLOCK_FLAGS (CM_KCB_NO_DELAY_CLOSE|CM_KCB_READ_ONLY_KEY)
114 #define KUNLOCK_MARKER (1<<15)
117 CM_KEY_BODY
*kbs
[NSPAM
], *kb
;
119 CM_KEY_CONTROL_BLOCK
*cb
;
124 LIST_ENTRY KeyBodyListHead
;
125 CM_KEY_BODY
*KeyBodyArray
[4];
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
);
132 goto out_unspam_zwclose
;
134 for (i
= 0; i
< NSPAM
; i
++) {
135 st
= ObReferenceObjectByHandle(harr
[i
], KEY_WRITE
,
136 *CmKeyObjectType
, 0, (void*)&kbs
[i
], NULL
);
138 goto out_unspam_deref
;
142 cb
= kb
->KeyControlBlock
;
143 st
= STATUS_KEY_DELETED
;
144 if (!cb
|| cb
->Delete
)
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);
159 DBG("cbptr not found\n");
163 DBG("cbptr @ %p, offset %p\n", cbptr
, ((ULONG_PTR
)(((void*)cbptr
)-((void*)cb
))));
165 // Now process array area.
166 for (i
= 0; i
< 4; i
++)
167 if (notify_unlock(lock
, cbptr
->KeyBodyArray
[i
], kb
))
170 // And list area too.
171 kl
= cbptr
->KeyBodyListHead
.Flink
;
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
))
183 for (int j
= 0; j
< i
; j
++)
184 ObDereferenceObject(kbs
[j
]);
187 for (int j
= 0; j
< i
; j
++)
192 // Apply/remove hard lock.
193 static NTSTATUS
reg_set_lock(PUNICODE_STRING name
, int lock
)
197 CM_KEY_CONTROL_BLOCK
*cb
;
200 st
= open_key(&h
, name
);
203 st
= ObReferenceObjectByHandle(h
, KEY_WRITE
,
204 *CmKeyObjectType
, 0, (void*)&kb
, NULL
);
206 if (!NT_SUCCESS(st
)) {
210 cb
= kb
->KeyControlBlock
;
211 st
= STATUS_KEY_DELETED
;
212 if (!cb
|| cb
->Delete
)
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
);
220 cb
->ExtFlags
|= KLOCK_FLAGS
;
222 cb
->ExtFlags
&= ~KLOCK_FLAGS
;
225 ObDereferenceObject(kb
);
230 static NTSTATUS
regs_do(NTSTATUS (*fn
)(PUNICODE_STRING
,int), PUNICODE_STRING names
,
233 WCHAR
*p
= names
->Buffer
;
234 NTSTATUS status
= STATUS_SUCCESS
;
241 UNICODE_STRING split
;
242 NTSTATUS item_status
;
244 next
= wcschr(p
, L
';');
248 RtlInitUnicodeString(&split
, p
);
249 item_status
= fn(&split
, lock
);
250 if (NT_SUCCESS(status
) && !NT_SUCCESS(item_status
))
251 status
= item_status
;
259 static NTSTATUS
change_prot(wind_prot_t
*req
)
264 if ((getonly
= (req
->pid
< 0)))
265 req
->pid
= -req
->pid
;
266 status
= PsLookupProcessByProcessId((HANDLE
)(req
->pid
), (PEPROCESS
*)&proc
);
267 if (!NT_SUCCESS(status
))
269 if (cfg
.protbit
< 0) {
270 WIND_PS_PROTECTION save
, *prot
= proc
+ cfg
.protofs
- 2;
271 memcpy(&save
, prot
, sizeof(save
));
273 memcpy(prot
, &req
->prot
, sizeof(req
->prot
));
274 memcpy(&req
->prot
, &save
, sizeof(save
));
276 ULONG prev
, *prot
= proc
+ cfg
.protofs
;
279 *prot
= (prev
& (~(1<<cfg
.protbit
)))
280 | ((!!req
->prot
.Level
) << cfg
.protbit
);
281 memset(&req
->prot
, 0, sizeof(req
->prot
));
282 req
->prot
.Level
= (prev
>>cfg
.protbit
)&1;
284 ObDereferenceObject(proc
);
288 static NTSTATUS
regcb_set(int enable
)
290 static LIST_ENTRY saved_list
;
291 static int cleared
= 0;
293 return STATUS_NOT_SUPPORTED
;
294 if (cleared
^ enable
)
295 return STATUS_DEVICE_BUSY
;
297 saved_list
= *cfg
.cblist
;
298 InitializeListHead(cfg
.cblist
);
301 *cfg
.cblist
= saved_list
;
304 return STATUS_SUCCESS
;
307 // Helper scratch space for parser.
309 wind_pol_ent
*ents
[WIND_POL_MAX
];
310 UCHAR scratch
[65536], *p
;
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
)
318 parse_t
*parse
= pparse
;
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
);
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
);
336 // Allocate scratch space
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
) {
344 if (parse
->nent
== WIND_POL_MAX
)
345 return STATUS_SUCCESS
;
348 // Otherwise we'll overwrite previous entry, preserve flags.
349 e
->flags
= parse
->ents
[i
]->flags
;
351 // Fill in entry. Note that padding (as well as final size)
352 // is done via wind_pol_pack().
357 memcpy(e
->name
, name
, nlen
);
358 memcpy(e
->name
+ nlen
, data
, len
);
360 return STATUS_SUCCESS
;
363 // System policy has changed, apply our custom rules.
364 static NTAPI
void pol_arm_notify(HANDLE key
)
366 static WORK_QUEUE_ITEM it
;
367 static IO_STATUS_BLOCK io
;
369 KEY_VALUE_PARTIAL_INFORMATION v
;
372 static parse_t parse
;
373 static UCHAR buf
[65536];
374 ULONG got
= sizeof(vb
);
376 // Grab current view of policy and parse its entries.
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
,
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
);
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
);
400 DBG("Re-arming notification\n");
401 memset(&it
, 0, sizeof(it
));
402 it
.WorkerRoutine
= pol_arm_notify
;
404 ZwNotifyChangeKey(key
, NULL
, (void*)&it
, (void*)1,
405 &io
, 5, TRUE
, NULL
, 0, TRUE
);
408 static NTSTATUS NTAPI
dev_control(IN PDEVICE_OBJECT dev
, IN PIRP irp
)
410 PIO_STACK_LOCATION io_stack
;
412 NTSTATUS status
= STATUS_INVALID_PARAMETER
;
417 KeWaitForMutexObject(&ioctl_mutex
, UserRequest
, KernelMode
, FALSE
, NULL
);
419 io_stack
= IoGetCurrentIrpStackLocation(irp
);
423 buf
= irp
->AssociatedIrp
.SystemBuffer
;
424 len
= io_stack
->Parameters
.DeviceIoControl
.InputBufferLength
;
425 code
= io_stack
->Parameters
.DeviceIoControl
.IoControlCode
;
427 irp
->IoStatus
.Information
= 0;
429 if (!SeSinglePrivilegeCheck(LUID_SeLoadDriverPrivilege
, irp
->RequestorMode
)) {
430 status
= STATUS_PRIVILEGE_NOT_HELD
;
434 status
= STATUS_INVALID_BUFFER_SIZE
;
435 DBG("code=%08x\n",(unsigned)code
);
437 // codes 0x90x and 0x81x need buffer.
438 if ((code
& ((0x110)<<2)) && (!buf
))
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))
447 us
.Buffer
= (void*)buf
;
449 us
.MaximumLength
= len
;
454 case WIND_IOCTL_INSMOD
:
455 status
= driver_sideload(&us
);
457 case WIND_IOCTL_REGLOCKON
:
458 case WIND_IOCTL_REGLOCKOFF
:
459 status
= regs_do(reg_set_lock
, &us
, onoff
);
461 case WIND_IOCTL_REGNON
:
462 case WIND_IOCTL_REGNOFF
:
463 status
= regs_do(reg_set_notify
, &us
, onoff
);
465 case WIND_IOCTL_PROT
:
466 if (len
!= sizeof(wind_prot_t
))
468 status
= change_prot(buf
);
469 irp
->IoStatus
.Information
= len
;
471 case WIND_IOCTL_REGCBON
:
472 case WIND_IOCTL_REGCBOFF
:
473 status
= regcb_set(onoff
);
477 KeReleaseMutex(&ioctl_mutex
, 0);
478 irp
->IoStatus
.Status
= status
;
479 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
483 static VOID NTAPI
dev_unload(IN PDRIVER_OBJECT self
)
486 // Restore callbacks.
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
);
495 UCHAR
*p
= MmGetSystemRoutineAddress(&RTL_STRING(L
"MmMapViewInSessionSpace"));
496 DBG("marker at %p\n", p
);
498 for (int i
= 0; i
< 256*1024; i
++, p
--) {
500 if (EQUALS(p
+ 14,"\x48\x81\xec\xa0\x04\x00\x00") && p
[0] == 0x48)
502 if (EQUALS(p
,"\x68\x28\x04\x00\x00\x68"))
505 DBG("ExUpdateLicenseData guessed at %p\n", p
);
506 cfg
.pExUpdateLicenseData2
= (void*)p
;
510 DBG("Even the brute guess failed.\n");
514 static int k_analyze()
517 UCHAR
*p
= (void*)MmGetSystemRoutineAddress(&RTL_STRING(L
"PsGetProcessProtection"));
522 p
= (void*)MmGetSystemRoutineAddress(&RTL_STRING(L
"PsIsProtectedProcess"));
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)
529 // mov al, [anything+OFFSET]
530 for (i
=0 ; i
< 64; i
++, p
++)
531 if ((p
[-2] == 0x8a) && (!p
[2] && !p
[3]))
534 DBG("failed to find protbit\n");
537 cfg
.protofs
= *((ULONG
*)p
);
540 p
= (void*)MmGetSystemRoutineAddress(&RTL_STRING(L
"CmUnRegisterCallback"));
542 for (i
= 0; i
< 512; i
++, p
++) {
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
));
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
);
561 DBG("CallbackListHead @ %p", cfg
.cblist
);
562 cfg
.pExUpdateLicenseData
= MmGetSystemRoutineAddress(&RTL_STRING(L
"ExUpdateLicenseData"));
563 if (cfg
.pExUpdateLicenseData
)
570 NTSTATUS NTAPI
ENTRY(driver_entry
)(IN PDRIVER_OBJECT self
, IN PUNICODE_STRING reg
)
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
580 |RTL_QUERY_REGISTRY_DELETE
584 .EntryContext
= &cfg
,
585 .DefaultType
= (REG_BINARY
<<RTL_QUERY_REGISTRY_TYPECHECK_SHIFT
)
588 .Flags
= RTL_QUERY_REGISTRY_DIRECT
589 |RTL_QUERY_REGISTRY_TYPECHECK
,
590 .DefaultType
= (REG_SZ
<<RTL_QUERY_REGISTRY_TYPECHECK_SHIFT
),
592 .EntryContext
= regs
,
594 .Flags
= RTL_QUERY_REGISTRY_DIRECT
595 |RTL_QUERY_REGISTRY_TYPECHECK
,
596 .DefaultType
= (REG_SZ
<<RTL_QUERY_REGISTRY_TYPECHECK_SHIFT
),
598 .EntryContext
= regs
+1,
600 .Flags
= RTL_QUERY_REGISTRY_DIRECT
601 |RTL_QUERY_REGISTRY_TYPECHECK
,
602 .DefaultType
= (REG_SZ
<<RTL_QUERY_REGISTRY_TYPECHECK_SHIFT
),
604 .EntryContext
= regs
+2,
606 .Flags
= RTL_QUERY_REGISTRY_DIRECT
607 |RTL_QUERY_REGISTRY_TYPECHECK
,
608 .DefaultType
= (REG_SZ
<<RTL_QUERY_REGISTRY_TYPECHECK_SHIFT
),
610 .EntryContext
= regs
+3,
614 status
= RtlQueryRegistryValues(0, reg
->Buffer
, tab
, NULL
, NULL
);
615 if (!NT_SUCCESS(status
)) {
616 DBG("registry read failed=%x\n",(unsigned)status
);
619 self
->DriverUnload
= dev_unload
;
620 self
->MajorFunction
[IRP_MJ_CREATE
] = dev_open
;
621 self
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = dev_control
;
623 status
= IoCreateDevice(self
, 0, &RTL_STRING(L
"\\Device\\" WIND_DEVNAME
),
624 FILE_DEVICE_UNKNOWN
, 0, 0, &dev
);
626 if (!NT_SUCCESS(status
)) {
627 DBG("failed to create device=%08x\n",(unsigned)status
);
632 cfg
.ci_guess
= *cfg
.ci_orig
;
635 KeInitializeMutex(&ioctl_mutex
, 0);
636 KeWaitForMutexObject(&ioctl_mutex
, UserRequest
, KernelMode
, FALSE
, NULL
);
638 dev
->Flags
|= METHOD_BUFFERED
;
639 dev
->Flags
&= ~DO_DEVICE_INITIALIZING
;
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);
650 reg_set_notify(&RTL_STRING(POLICY_PATH
), 0);
651 if (NT_SUCCESS(open_key(&kh
, &RTL_STRING(POLICY_PATH
))))
655 DBG("initialized driver with:\n"
658 " .ci_guess = %02x\n"
660 " .protbit = %d\n", cfg
.ci_opt
, cfg
.ci_orig
, cfg
.ci_guess
,
661 cfg
.protofs
, cfg
.protbit
);
663 KeReleaseMutex(&ioctl_mutex
, 0);
664 DBG("loaded driver\n");