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 va_end(ap); 24 } 25 26 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 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 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 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 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 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 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 */ 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 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 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