da1117036ce33c8732f70e9435ce979bd38c1c64
[reactos.git] / modules / rosapps / applications / devutils / shimdbg / shimdbg.c
1 /*
2 * PROJECT: shimdbg
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Test tool for SHIM engine caching
5 * COPYRIGHT: Copyright 2016-2017 Mark Jansen (mark.jansen@reactos.org)
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <ctype.h>
11 #include <ntndk.h>
12
13 NTSYSAPI ULONG NTAPI vDbgPrintEx(_In_ ULONG ComponentId, _In_ ULONG Level, _In_z_ PCCH Format, _In_ va_list ap);
14 #define DPFLTR_ERROR_LEVEL 0
15
16 void xprintf(const char *fmt, ...)
17 {
18 va_list ap;
19
20 va_start(ap, fmt);
21 vprintf(fmt, ap);
22 vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL, fmt, ap);
23 }
24
25
26 void CallApphelp(APPHELPCACHESERVICECLASS Service,
27 PAPPHELP_CACHE_SERVICE_LOOKUP CacheEntry)
28 {
29 NTSTATUS Status = NtApphelpCacheControl(Service, CacheEntry);
30 xprintf("NtApphelpCacheControl returned 0x%x\n", (unsigned int)Status);
31 }
32
33 HANDLE MapFile(char* filename, UNICODE_STRING* PathName, int MapIt)
34 {
35 OBJECT_ATTRIBUTES LocalObjectAttributes;
36 IO_STATUS_BLOCK IoStatusBlock;
37 NTSTATUS Status;
38 HANDLE FileHandle = NULL;
39 RtlCreateUnicodeStringFromAsciiz(PathName, filename);
40 if (MapIt)
41 {
42 InitializeObjectAttributes(&LocalObjectAttributes, PathName,
43 OBJ_CASE_INSENSITIVE, NULL, NULL);
44 Status = NtOpenFile(&FileHandle,
45 SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_EXECUTE,
46 &LocalObjectAttributes, &IoStatusBlock,
47 FILE_SHARE_READ | FILE_SHARE_DELETE,
48 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
49 if (!NT_SUCCESS(Status))
50 {
51 xprintf("Failed opening the file, using a NULL handle\n");
52 FileHandle = NULL;
53 }
54 }
55 return FileHandle;
56 }
57
58 void CallApphelpWithImage(char* filename, int MapIt,
59 APPHELPCACHESERVICECLASS Service, char* ServiceName)
60 {
61 UNICODE_STRING PathName = {0};
62 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry;
63
64 HANDLE FileHandle = MapFile(filename, &PathName, MapIt);
65
66 xprintf("Calling %s %s mapping\n", ServiceName, (MapIt ? "with" : "without"));
67
68 RtlInitUnicodeString(&CacheEntry.ImageName, PathName.Buffer);
69 CacheEntry.ImageHandle = FileHandle ? FileHandle : (HANDLE)-1;
70 CallApphelp(Service, &CacheEntry);
71 // we piggy-back on the PathName, so let the Cleanup take care of the string
72 //RtlFreeUnicodeString(&CacheEntry.ImageName);
73
74 if (FileHandle)
75 NtClose(FileHandle);
76 RtlFreeUnicodeString(&PathName);
77 }
78
79 int IsOpt(char* argv, const char* check)
80 {
81 if( argv && (argv[0] == '-' || argv[0] == '/') ) {
82 return !_strnicmp(argv + 1, check, strlen(check));
83 }
84 return 0;
85 }
86
87 int HandleImageArg(int argc, char* argv[], int* pn, char MapItChar,
88 APPHELPCACHESERVICECLASS Service, char* ServiceName)
89 {
90 int n = *pn;
91 if (n+1 < argc)
92 {
93 int MapIt = argv[n][1] == MapItChar;
94 CallApphelpWithImage(argv[n+1], MapIt, Service, ServiceName);
95 (*pn) += 1;
96 return 0;
97 }
98 xprintf("Error: no image name specified\n");
99 return 1;
100 }
101
102 typedef WORD TAG;
103 typedef UINT64 QWORD;
104
105 #define TAG_TYPE_MASK 0xF000
106 #define TAG_TYPE_DWORD 0x4000
107 #define TAG_TYPE_QWORD 0x5000
108 #define TAG_TYPE_STRINGREF 0x6000
109
110 #define ATTRIBUTE_AVAILABLE 0x1
111 #define ATTRIBUTE_FAILED 0x2
112
113 typedef struct tagATTRINFO
114 {
115 TAG type;
116 DWORD flags; /* ATTRIBUTE_AVAILABLE, ATTRIBUTE_FAILED */
117 union
118 {
119 QWORD qwattr;
120 DWORD dwattr;
121 WCHAR *lpattr;
122 };
123 } ATTRINFO, *PATTRINFO;
124
125 static PVOID hdll;
126 static LPCWSTR (WINAPI *pSdbTagToString)(TAG);
127 static BOOL (WINAPI *pSdbGetFileAttributes)(LPCWSTR, PATTRINFO *, LPDWORD);
128 static BOOL (WINAPI *pSdbFreeFileAttributes)(PATTRINFO);
129
130 static BOOL InitApphelp()
131 {
132 if (!hdll)
133 {
134 static UNICODE_STRING DllName = RTL_CONSTANT_STRING(L"apphelp.dll");
135 static ANSI_STRING SdbTagToString = RTL_CONSTANT_STRING("SdbTagToString");
136 static ANSI_STRING SdbGetFileAttributes = RTL_CONSTANT_STRING("SdbGetFileAttributes");
137 static ANSI_STRING SdbFreeFileAttributes = RTL_CONSTANT_STRING("SdbFreeFileAttributes");
138 if (!NT_SUCCESS(LdrLoadDll(NULL, NULL, &DllName, &hdll)))
139 {
140 xprintf("Unable to load apphelp.dll\n");
141 return FALSE;
142 }
143 if (!NT_SUCCESS(LdrGetProcedureAddress(hdll, &SdbTagToString, 0, (PVOID)&pSdbTagToString)) ||
144 !NT_SUCCESS(LdrGetProcedureAddress(hdll, &SdbGetFileAttributes, 0, (PVOID)&pSdbGetFileAttributes)) ||
145 !NT_SUCCESS(LdrGetProcedureAddress(hdll, &SdbFreeFileAttributes, 0, (PVOID)&pSdbFreeFileAttributes)))
146 {
147 LdrUnloadDll(hdll);
148 hdll = NULL;
149 xprintf("Unable to resolve functions\n");
150 return FALSE;
151 }
152 }
153 return TRUE;
154 }
155
156
157 int HandleDumpAttributes(int argc, char* argv[], int* pn, const char* opt)
158 {
159 UNICODE_STRING FileName;
160 PATTRINFO attr;
161 DWORD num_attr, n;
162 int argn = *pn;
163 const char* arg;
164
165 if (!InitApphelp())
166 return 1;
167
168 if (strlen(argv[argn]) > (strlen(opt)+1))
169 {
170 arg = argv[argn] + strlen(opt);
171 }
172 else if (argn+1 >= argc)
173 {
174 xprintf("Error: no image name specified\n");
175 return 1;
176 }
177 else
178 {
179 arg = argv[argn+1];
180 (*pn) += 1;
181 }
182
183 RtlCreateUnicodeStringFromAsciiz(&FileName, arg);
184
185 if (pSdbGetFileAttributes(FileName.Buffer, &attr, &num_attr))
186 {
187 xprintf("Dumping attributes for %s\n", arg);
188 for (n = 0; n < num_attr; ++n)
189 {
190 TAG tagType;
191 LPCWSTR tagName;
192 if (attr[n].flags != ATTRIBUTE_AVAILABLE)
193 continue;
194
195 tagName = pSdbTagToString(attr[n].type);
196
197 tagType = attr[n].type & TAG_TYPE_MASK;
198 switch (tagType)
199 {
200 case TAG_TYPE_DWORD:
201 xprintf("<%ls>0x%lx</%ls>\n", tagName, attr[n].dwattr, tagName);
202 break;
203 case TAG_TYPE_STRINGREF:
204 xprintf("<%ls>%ls</%ls>\n", tagName, attr[n].lpattr, tagName);
205 break;
206 case TAG_TYPE_QWORD:
207 xprintf("<%ls>0x%I64x</%ls>\n", tagName, attr[n].qwattr, tagName);
208 break;
209 default:
210 xprintf("<!-- Unknown tag type: 0x%x (from 0x%x)\n", tagType, attr[n].type);
211 break;
212 }
213 }
214 xprintf("Done\n");
215 }
216 else
217 {
218 xprintf("Unable to get attributes from %s\n", arg);
219 }
220
221
222 RtlFreeUnicodeString(&FileName);
223 return 0;
224 }
225
226 UNICODE_STRING AppCompatCacheKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCompatCache");
227 OBJECT_ATTRIBUTES AppCompatKeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&AppCompatCacheKey, OBJ_CASE_INSENSITIVE);
228 UNICODE_STRING AppCompatCacheValue = RTL_CONSTANT_STRING(L"AppCompatCache");
229 #define REG_BINARY ( 3 ) // Free form binary
230
231
232 /* produce a hex dump, stolen from rdesktop.c */
233 void hexdump(unsigned char *p, unsigned int len)
234 {
235 unsigned char *line = p;
236 unsigned int i, thisline, offset = 0;
237
238 while (offset < len)
239 {
240 xprintf("%04x ", offset);
241 thisline = len - offset;
242 if (thisline > 16)
243 thisline = 16;
244
245 for (i = 0; i < thisline; i++)
246 xprintf("%02x ", line[i]);
247
248 for (; i < 16; i++)
249 xprintf(" ");
250
251 for (i = 0; i < thisline; i++)
252 xprintf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
253
254 xprintf("\n");
255 offset += thisline;
256 line += thisline;
257 }
258 }
259
260 void DumpRegistryData(int IncludeDump)
261 {
262 HANDLE KeyHandle;
263 NTSTATUS Status;
264 KEY_VALUE_PARTIAL_INFORMATION KeyValueObject;
265 PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = &KeyValueObject;
266 ULONG KeyInfoSize, ResultSize;
267
268 xprintf("Dumping AppCompatCache registry key\n");
269
270 Status = NtOpenKey(&KeyHandle, KEY_QUERY_VALUE, &AppCompatKeyAttributes);
271
272 Status = NtQueryValueKey(KeyHandle, &AppCompatCacheValue,
273 KeyValuePartialInformation, KeyValueInformation,
274 sizeof(KeyValueObject), &ResultSize);
275
276 if (Status == STATUS_BUFFER_OVERFLOW)
277 {
278 KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + KeyValueInformation->DataLength;
279 KeyValueInformation = malloc(KeyInfoSize);
280 if (KeyValueInformation != NULL)
281 {
282 Status = NtQueryValueKey(KeyHandle, &AppCompatCacheValue,
283 KeyValuePartialInformation, KeyValueInformation,
284 KeyInfoSize, &ResultSize);
285 }
286 }
287
288 if (NT_SUCCESS(Status) && KeyValueInformation->Type == REG_BINARY)
289 {
290 ULONG crc;
291 if (IncludeDump)
292 hexdump(KeyValueInformation->Data, KeyValueInformation->DataLength);
293 crc = RtlComputeCrc32(0, KeyValueInformation->Data, KeyValueInformation->DataLength);
294 xprintf("Len: %lu, Crc: 0x%lx\n", KeyValueInformation->DataLength, crc);
295 }
296 else
297 {
298 xprintf("Failed reading AppCompatCache from registry (0x%lx)\n", Status);
299 }
300
301 if (KeyValueInformation != &KeyValueObject)
302 free(KeyValueInformation);
303
304 if (KeyHandle)
305 NtClose(KeyHandle);
306 }
307
308 int _getch();
309
310 int main(int argc, char* argv[])
311 {
312 int n, unhandled = 0, keepopen = 0;
313
314 for (n = 1; n < argc; ++n)
315 {
316 char* arg = argv[n];
317 if (IsOpt(arg, "d"))
318 {
319 xprintf("Calling ApphelpCacheServiceDump\n");
320 CallApphelp(ApphelpCacheServiceDump, NULL);
321 unhandled = 0;
322 }
323 else if (IsOpt(arg, "h"))
324 {
325 DumpRegistryData(arg[1] == 'h');
326 unhandled = 0;
327 }
328 else if (IsOpt(arg, "f"))
329 {
330 xprintf("Calling ApphelpCacheServiceFlush\n");
331 CallApphelp(ApphelpCacheServiceFlush, NULL);
332 unhandled = 0;
333 }
334 else if (IsOpt(arg, "z"))
335 {
336 xprintf("Calling ApphelpDBGReadRegistry\n");
337 CallApphelp(ApphelpDBGReadRegistry, NULL);
338 unhandled = 0;
339 }
340 else if (IsOpt(arg, "x"))
341 {
342 xprintf("Calling ApphelpDBGWriteRegistry\n");
343 CallApphelp(ApphelpDBGWriteRegistry, NULL);
344 unhandled = 0;
345 }
346 else if (IsOpt(arg, "l"))
347 {
348 unhandled |= HandleImageArg(argc, argv, &n, 'l',
349 ApphelpCacheServiceLookup, "ApphelpCacheServiceLookup");
350 }
351 else if (IsOpt(arg, "u"))
352 {
353 unhandled |= HandleImageArg(argc, argv, &n, 'u',
354 ApphelpCacheServiceUpdate, "ApphelpCacheServiceUpdate");
355 }
356 else if (IsOpt(arg, "r"))
357 {
358 unhandled |= HandleImageArg(argc, argv, &n, 'r',
359 ApphelpCacheServiceRemove, "ApphelpCacheServiceRemove");
360 }
361 else if (IsOpt(arg, "a"))
362 {
363 unhandled |= HandleDumpAttributes(argc, argv, &n, "a");
364 }
365 else if (IsOpt(arg, "k"))
366 {
367 keepopen = 1;
368 }
369 else
370 {
371 unhandled = 1;
372 }
373 }
374 if (unhandled || argc == 1)
375 {
376 xprintf("Usage: %s [-d|-z|-x|-h|-H|-f|-[l|L] <image>|-[u|U] <image>|-[r|R] <image>|-k]\n", argv[0]);
377 xprintf(" -d: Dump shim cache over debug output\n");
378 xprintf(" -z: DEBUG Read shim cache from registry\n");
379 xprintf(" -x: DEBUG Write shim cache to registry\n");
380 xprintf(" -h: Hexdump shim registry key\n");
381 xprintf(" -H: Crc + Length from shim registry key only\n");
382 xprintf(" -f: Flush (clear) the shim cache\n");
383 xprintf(" -l: Lookup <image> in the shim cache\n");
384 xprintf(" -L: Lookup <image> in the shim cache without mapping it\n");
385 xprintf(" -u: Update (insert) <image> in the shim cache\n");
386 xprintf(" -U: Update (insert) <image> in the shim cache without mapping it\n");
387 xprintf(" -r: Remove <image> from the shim cache\n");
388 xprintf(" -R: Remove <image> from the shim cache without mapping it\n");
389 xprintf(" -a: Dump file attributes as used in the appcompat database\n");
390 xprintf(" -k: Keep the console open\n");
391 }
392 if (keepopen)
393 {
394 _getch();
395 }
396 return unhandled;
397 }