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