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)
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
16 void xprintf(const char *fmt
, ...)
22 vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL
, fmt
, ap
);
27 void CallApphelp(APPHELPCACHESERVICECLASS Service
,
28 PAPPHELP_CACHE_SERVICE_LOOKUP CacheEntry
)
30 NTSTATUS Status
= NtApphelpCacheControl(Service
, CacheEntry
);
31 xprintf("NtApphelpCacheControl returned 0x%x\n", (unsigned int)Status
);
34 HANDLE
MapFile(char* filename
, UNICODE_STRING
* PathName
, int MapIt
)
36 OBJECT_ATTRIBUTES LocalObjectAttributes
;
37 IO_STATUS_BLOCK IoStatusBlock
;
39 HANDLE FileHandle
= NULL
;
40 RtlCreateUnicodeStringFromAsciiz(PathName
, filename
);
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
))
52 xprintf("Failed opening the file, using a NULL handle\n");
59 void CallApphelpWithImage(char* filename
, int MapIt
,
60 APPHELPCACHESERVICECLASS Service
, char* ServiceName
)
62 UNICODE_STRING PathName
= {0};
63 APPHELP_CACHE_SERVICE_LOOKUP CacheEntry
;
65 HANDLE FileHandle
= MapFile(filename
, &PathName
, MapIt
);
67 xprintf("Calling %s %s mapping\n", ServiceName
, (MapIt
? "with" : "without"));
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);
77 RtlFreeUnicodeString(&PathName
);
80 int IsOpt(char* argv
, const char* check
)
82 if( argv
&& (argv
[0] == '-' || argv
[0] == '/') ) {
83 return !_strnicmp(argv
+ 1, check
, strlen(check
));
88 int HandleImageArg(int argc
, char* argv
[], int* pn
, char MapItChar
,
89 APPHELPCACHESERVICECLASS Service
, char* ServiceName
)
94 int MapIt
= argv
[n
][1] == MapItChar
;
95 CallApphelpWithImage(argv
[n
+1], MapIt
, Service
, ServiceName
);
99 xprintf("Error: no image name specified\n");
104 typedef UINT64 QWORD
;
106 #define TAG_TYPE_MASK 0xF000
107 #define TAG_TYPE_DWORD 0x4000
108 #define TAG_TYPE_QWORD 0x5000
109 #define TAG_TYPE_STRINGREF 0x6000
111 #define ATTRIBUTE_AVAILABLE 0x1
112 #define ATTRIBUTE_FAILED 0x2
114 typedef struct tagATTRINFO
117 DWORD flags
; /* ATTRIBUTE_AVAILABLE, ATTRIBUTE_FAILED */
124 } ATTRINFO
, *PATTRINFO
;
127 static LPCWSTR (WINAPI
*pSdbTagToString
)(TAG
);
128 static BOOL (WINAPI
*pSdbGetFileAttributes
)(LPCWSTR
, PATTRINFO
*, LPDWORD
);
129 static BOOL (WINAPI
*pSdbFreeFileAttributes
)(PATTRINFO
);
131 static BOOL
InitApphelp()
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
)))
141 xprintf("Unable to load apphelp.dll\n");
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
)))
150 xprintf("Unable to resolve functions\n");
158 int HandleDumpAttributes(int argc
, char* argv
[], int* pn
, const char* opt
)
160 UNICODE_STRING FileName
;
169 if (strlen(argv
[argn
]) > (strlen(opt
)+1))
171 arg
= argv
[argn
] + strlen(opt
);
173 else if (argn
+1 >= argc
)
175 xprintf("Error: no image name specified\n");
184 RtlCreateUnicodeStringFromAsciiz(&FileName
, arg
);
186 if (pSdbGetFileAttributes(FileName
.Buffer
, &attr
, &num_attr
))
188 xprintf("Dumping attributes for %s\n", arg
);
189 for (n
= 0; n
< num_attr
; ++n
)
193 if (attr
[n
].flags
!= ATTRIBUTE_AVAILABLE
)
196 tagName
= pSdbTagToString(attr
[n
].type
);
198 tagType
= attr
[n
].type
& TAG_TYPE_MASK
;
202 xprintf("<%ls>0x%lx</%ls>\n", tagName
, attr
[n
].dwattr
, tagName
);
204 case TAG_TYPE_STRINGREF
:
205 xprintf("<%ls>%ls</%ls>\n", tagName
, attr
[n
].lpattr
, tagName
);
208 xprintf("<%ls>0x%I64x</%ls>\n", tagName
, attr
[n
].qwattr
, tagName
);
211 xprintf("<!-- Unknown tag type: 0x%x (from 0x%x)\n", tagType
, attr
[n
].type
);
219 xprintf("Unable to get attributes from %s\n", arg
);
223 RtlFreeUnicodeString(&FileName
);
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
233 /* produce a hex dump, stolen from rdesktop.c */
234 void hexdump(unsigned char *p
, unsigned int len
)
236 unsigned char *line
= p
;
237 unsigned int i
, thisline
, offset
= 0;
241 xprintf("%04x ", offset
);
242 thisline
= len
- offset
;
246 for (i
= 0; i
< thisline
; i
++)
247 xprintf("%02x ", line
[i
]);
252 for (i
= 0; i
< thisline
; i
++)
253 xprintf("%c", (line
[i
] >= 0x20 && line
[i
] < 0x7f) ? line
[i
] : '.');
261 void DumpRegistryData(int IncludeDump
)
265 KEY_VALUE_PARTIAL_INFORMATION KeyValueObject
;
266 PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation
= &KeyValueObject
;
267 ULONG KeyInfoSize
, ResultSize
;
269 xprintf("Dumping AppCompatCache registry key\n");
271 Status
= NtOpenKey(&KeyHandle
, KEY_QUERY_VALUE
, &AppCompatKeyAttributes
);
273 Status
= NtQueryValueKey(KeyHandle
, &AppCompatCacheValue
,
274 KeyValuePartialInformation
, KeyValueInformation
,
275 sizeof(KeyValueObject
), &ResultSize
);
277 if (Status
== STATUS_BUFFER_OVERFLOW
)
279 KeyInfoSize
= sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + KeyValueInformation
->DataLength
;
280 KeyValueInformation
= malloc(KeyInfoSize
);
281 if (KeyValueInformation
!= NULL
)
283 Status
= NtQueryValueKey(KeyHandle
, &AppCompatCacheValue
,
284 KeyValuePartialInformation
, KeyValueInformation
,
285 KeyInfoSize
, &ResultSize
);
289 if (NT_SUCCESS(Status
) && KeyValueInformation
->Type
== REG_BINARY
)
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
);
299 xprintf("Failed reading AppCompatCache from registry (0x%lx)\n", Status
);
302 if (KeyValueInformation
!= &KeyValueObject
)
303 free(KeyValueInformation
);
311 int main(int argc
, char* argv
[])
313 int n
, unhandled
= 0, keepopen
= 0;
315 for (n
= 1; n
< argc
; ++n
)
320 xprintf("Calling ApphelpCacheServiceDump\n");
321 CallApphelp(ApphelpCacheServiceDump
, NULL
);
324 else if (IsOpt(arg
, "h"))
326 DumpRegistryData(arg
[1] == 'h');
329 else if (IsOpt(arg
, "f"))
331 xprintf("Calling ApphelpCacheServiceFlush\n");
332 CallApphelp(ApphelpCacheServiceFlush
, NULL
);
335 else if (IsOpt(arg
, "z"))
337 xprintf("Calling ApphelpDBGReadRegistry\n");
338 CallApphelp(ApphelpDBGReadRegistry
, NULL
);
341 else if (IsOpt(arg
, "x"))
343 xprintf("Calling ApphelpDBGWriteRegistry\n");
344 CallApphelp(ApphelpDBGWriteRegistry
, NULL
);
347 else if (IsOpt(arg
, "l"))
349 unhandled
|= HandleImageArg(argc
, argv
, &n
, 'l',
350 ApphelpCacheServiceLookup
, "ApphelpCacheServiceLookup");
352 else if (IsOpt(arg
, "u"))
354 unhandled
|= HandleImageArg(argc
, argv
, &n
, 'u',
355 ApphelpCacheServiceUpdate
, "ApphelpCacheServiceUpdate");
357 else if (IsOpt(arg
, "r"))
359 unhandled
|= HandleImageArg(argc
, argv
, &n
, 'r',
360 ApphelpCacheServiceRemove
, "ApphelpCacheServiceRemove");
362 else if (IsOpt(arg
, "a"))
364 unhandled
|= HandleDumpAttributes(argc
, argv
, &n
, "a");
366 else if (IsOpt(arg
, "k"))
375 if (unhandled
|| argc
== 1)
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");