Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

768 lines
20 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. ImportTableHash.c
  5. Abstract:
  6. This module contains hash computation routine
  7. RtlComputeImportTableHash to compute the hash
  8. based on the import table of an exe.
  9. Author:
  10. Vishnu Patankar (VishnuP) 31-May-2001
  11. Revision History:
  12. --*/
  13. #include "ImportTableHash.h"
  14. NTSTATUS
  15. RtlComputeImportTableHash(
  16. IN HANDLE hFile,
  17. IN PCHAR Hash,
  18. IN ULONG ImportTableHashRevision
  19. )
  20. /*++
  21. Routine Description:
  22. This routine computes the limited MD5 hash.
  23. First, the image is memory mapped and a canonical
  24. sorted list of module name and function name is created
  25. from the exe's import table.
  26. Second, the hash value is computed using the canonical
  27. information.
  28. Arguments:
  29. hFile - the handle of the file to compute the hash for
  30. Hash - the hash value returned - this has to be atleast 16 bytes long
  31. ImportTableHashRevision - the revision of the computation method for compatibility
  32. only ITH_REVISION_1 is supported today
  33. Return Value:
  34. The status of the hash computation.
  35. --*/
  36. {
  37. PIMPORTTABLEP_SORTED_LIST_ENTRY pTemp = NULL;
  38. PIMPORTTABLEP_SORTED_LIST_ENTRY ListEntry = NULL;
  39. PIMPORTTABLEP_SORTED_LIST_ENTRY ImportedNameList = NULL;
  40. PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY FunctionListEntry;
  41. ULONG ImportDescriptorSize = 0;
  42. HANDLE hMap = INVALID_HANDLE_VALUE;
  43. LPVOID FileMapping = NULL;
  44. PIMAGE_THUNK_DATA OriginalFirstThunk;
  45. PIMAGE_IMPORT_BY_NAME AddressOfData;
  46. PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  47. ACCESS_MASK DesiredAccess;
  48. ULONG AllocationAttributes;
  49. DWORD flProtect = PAGE_READONLY;
  50. LARGE_INTEGER SectionOffset;
  51. SIZE_T ViewSize;
  52. NTSTATUS Status = STATUS_SUCCESS;
  53. if ( ITH_REVISION_1 != ImportTableHashRevision ) {
  54. Status = STATUS_UNKNOWN_REVISION;
  55. goto ExitHandler;
  56. }
  57. //
  58. // Unwrap CreateFileMappingW (since that API is not available in ntdll.dll)
  59. //
  60. DesiredAccess = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ;
  61. AllocationAttributes = flProtect & (SEC_FILE | SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | SEC_NOCACHE);
  62. flProtect ^= AllocationAttributes;
  63. if (AllocationAttributes == 0) {
  64. AllocationAttributes = SEC_COMMIT;
  65. }
  66. Status = NtCreateSection(
  67. &hMap,
  68. DesiredAccess,
  69. NULL,
  70. NULL,
  71. flProtect,
  72. AllocationAttributes,
  73. hFile
  74. );
  75. if ( hMap == INVALID_HANDLE_VALUE || !NT_SUCCESS(Status) ) {
  76. Status = STATUS_INVALID_HANDLE;
  77. goto ExitHandler;
  78. }
  79. SectionOffset.LowPart = 0;
  80. SectionOffset.HighPart = 0;
  81. ViewSize = 0;
  82. Status = NtMapViewOfSection(
  83. hMap,
  84. NtCurrentProcess(),
  85. &FileMapping,
  86. 0L,
  87. 0L,
  88. &SectionOffset,
  89. &ViewSize,
  90. ViewShare,
  91. 0L,
  92. PAGE_READONLY
  93. );
  94. if (FileMapping == NULL || !NT_SUCCESS(Status) ) {
  95. Status = STATUS_NOT_MAPPED_VIEW;
  96. goto ExitHandler;
  97. }
  98. ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData (
  99. FileMapping,
  100. FALSE,
  101. IMAGE_DIRECTORY_ENTRY_IMPORT,
  102. &ImportDescriptorSize
  103. );
  104. if (ImportDescriptor == NULL) {
  105. Status = STATUS_RESOURCE_DATA_NOT_FOUND;
  106. goto ExitHandler;
  107. }
  108. //
  109. // outer loop that iterates over all modules in the import table of the exe
  110. //
  111. while ((ImportDescriptor->Name != 0) && (ImportDescriptor->FirstThunk != 0)) {
  112. PSZ ImportName = (PSZ)RtlAddressInSectionTable(
  113. RtlImageNtHeader(FileMapping),
  114. FileMapping,
  115. ImportDescriptor->Name
  116. );
  117. if ( ImportName == NULL ) {
  118. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  119. goto ExitHandler;
  120. }
  121. ListEntry = (PIMPORTTABLEP_SORTED_LIST_ENTRY)RtlAllocateHeap(RtlProcessHeap(), 0, sizeof( IMPORTTABLEP_SORTED_LIST_ENTRY ));
  122. if ( ListEntry == NULL ) {
  123. Status = STATUS_NO_MEMORY;
  124. goto ExitHandler;
  125. }
  126. ListEntry->String = ImportName;
  127. ListEntry->FunctionList = NULL;
  128. ListEntry->Next = NULL;
  129. ImportTablepInsertModuleSorted( ListEntry, &ImportedNameList );
  130. OriginalFirstThunk = (PIMAGE_THUNK_DATA)RtlAddressInSectionTable(
  131. RtlImageNtHeader(FileMapping),
  132. FileMapping,
  133. ImportDescriptor->OriginalFirstThunk
  134. );
  135. //
  136. // inner loop that iterates over all functions for a given module
  137. //
  138. while (OriginalFirstThunk->u1.Ordinal) {
  139. if (!IMAGE_SNAP_BY_ORDINAL( OriginalFirstThunk->u1.Ordinal)) {
  140. AddressOfData = (PIMAGE_IMPORT_BY_NAME)RtlAddressInSectionTable(
  141. RtlImageNtHeader(FileMapping),
  142. FileMapping,
  143. (ULONG)OriginalFirstThunk->u1.AddressOfData
  144. );
  145. if ( AddressOfData == NULL || AddressOfData->Name == NULL ) {
  146. Status = STATUS_RESOURCE_NAME_NOT_FOUND;
  147. goto ExitHandler;
  148. }
  149. FunctionListEntry = (PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY)RtlAllocateHeap(RtlProcessHeap(), 0, sizeof( IMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY ));
  150. if (FunctionListEntry == NULL ) {
  151. Status = STATUS_NO_MEMORY;
  152. goto ExitHandler;
  153. }
  154. FunctionListEntry->Next = NULL;
  155. FunctionListEntry->String = (PSZ)AddressOfData->Name;
  156. ImportTablepInsertFunctionSorted( FunctionListEntry, &ListEntry->FunctionList );
  157. }
  158. OriginalFirstThunk++;
  159. }
  160. ImportDescriptor++;
  161. }
  162. //
  163. // finally hash the canonical information (sorted module and sorted function list)
  164. //
  165. Status = ImportTablepHashCanonicalLists( ImportedNameList, Hash );
  166. ExitHandler:
  167. ImportTablepFreeModuleSorted( ImportedNameList );
  168. if (FileMapping) {
  169. NTSTATUS StatusUnmap;
  170. //
  171. // unwrap UnmapViewOfFile (since that API is not available in ntdll.dll)
  172. //
  173. StatusUnmap = NtUnmapViewOfSection(NtCurrentProcess(),(PVOID)FileMapping);
  174. if ( !NT_SUCCESS(StatusUnmap) ) {
  175. if (StatusUnmap == STATUS_INVALID_PAGE_PROTECTION) {
  176. //
  177. // Unlock any pages that were locked with MmSecureVirtualMemory.
  178. // This is useful for SANs.
  179. //
  180. if (RtlFlushSecureMemoryCache((PVOID)FileMapping, 0)) {
  181. StatusUnmap = NtUnmapViewOfSection(NtCurrentProcess(),
  182. (PVOID)FileMapping
  183. );
  184. }
  185. }
  186. }
  187. }
  188. if (hMap != INVALID_HANDLE_VALUE ) {
  189. NtClose(hMap);
  190. }
  191. return Status;
  192. }
  193. VOID
  194. ImportTablepInsertFunctionSorted(
  195. IN PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pFunctionName,
  196. OUT PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY * ppFunctionNameList
  197. )
  198. /*++
  199. Routine Description:
  200. This routine inserts a function name in a sorted order.
  201. Arguments:
  202. pFunctionName - name of the function
  203. ppFunctionNameList - pointer to the head of the function list to be updated
  204. Return Value:
  205. None:
  206. --*/
  207. {
  208. PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pPrev;
  209. PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pTemp;
  210. //
  211. // Special case, list is empty, insert at the front.
  212. //
  213. if (*ppFunctionNameList == NULL
  214. || _stricmp((*ppFunctionNameList)->String, pFunctionName->String) > 0) {
  215. pFunctionName->Next = *ppFunctionNameList;
  216. *ppFunctionNameList = pFunctionName;
  217. return;
  218. }
  219. pPrev = *ppFunctionNameList;
  220. pTemp = (*ppFunctionNameList)->Next;
  221. while (pTemp) {
  222. if (_stricmp(pTemp->String, pFunctionName->String) >= 0) {
  223. pFunctionName->Next = pTemp;
  224. pPrev->Next = pFunctionName;
  225. return;
  226. }
  227. pPrev = pTemp;
  228. pTemp = pTemp->Next;
  229. }
  230. pFunctionName->Next = NULL;
  231. pPrev->Next = pFunctionName;
  232. return;
  233. }
  234. VOID
  235. ImportTablepInsertModuleSorted(
  236. IN PIMPORTTABLEP_SORTED_LIST_ENTRY pImportName,
  237. OUT PIMPORTTABLEP_SORTED_LIST_ENTRY * ppImportNameList
  238. )
  239. /*++
  240. Routine Description:
  241. This routine inserts a module name (dll) in a sorted order.
  242. Arguments:
  243. pImportName - the import name that needs to be inserted
  244. ppImportNameList - pointer to the head of the list to be updated
  245. Return Value:
  246. None:
  247. --*/
  248. {
  249. PIMPORTTABLEP_SORTED_LIST_ENTRY pPrev;
  250. PIMPORTTABLEP_SORTED_LIST_ENTRY pTemp;
  251. //
  252. // Special case, list is empty, insert at the front.
  253. //
  254. if (*ppImportNameList == NULL
  255. || _stricmp((*ppImportNameList)->String, pImportName->String) > 0) {
  256. pImportName->Next = *ppImportNameList;
  257. *ppImportNameList = pImportName;
  258. return;
  259. }
  260. pPrev = *ppImportNameList;
  261. pTemp = (*ppImportNameList)->Next;
  262. while (pTemp) {
  263. if (_stricmp(pTemp->String, pImportName->String) >= 0) {
  264. pImportName->Next = pTemp;
  265. pPrev->Next = pImportName;
  266. return;
  267. }
  268. pPrev = pTemp;
  269. pTemp = pTemp->Next;
  270. }
  271. pImportName->Next = NULL;
  272. pPrev->Next = pImportName;
  273. return;
  274. }
  275. static HANDLE AdvApi32ModuleHandle = (HANDLE) (ULONG_PTR) -1;
  276. NTSTATUS
  277. ImportTablepHashCanonicalLists(
  278. IN PIMPORTTABLEP_SORTED_LIST_ENTRY ImportedNameList,
  279. OUT PBYTE Hash
  280. )
  281. /*++
  282. Routine Description:
  283. This routine computes the hash values from a given import list.
  284. advapi32.dll is dynamically loaded - once only per process,
  285. and the crypto APIs are used to compute the hash value.
  286. Arguments:
  287. ImportedNameList - the head of the module name/function name list
  288. Hash - the buffer to use to fill in the hash value
  289. Return Value:
  290. STATUS_SUCCESS if the hash value is calculated, otherwise the error status
  291. --*/
  292. {
  293. NTSTATUS Status = STATUS_SUCCESS;
  294. DWORD dwHashedDataLength = 0;
  295. PIMPORTTABLEP_SORTED_LIST_ENTRY pTemp;
  296. HCRYPTHASH hHash;
  297. HCRYPTPROV hProvVerify;
  298. typedef BOOL (WINAPI *CryptAcquireContextW) (
  299. HCRYPTPROV *phProv,
  300. LPCWSTR szContainer,
  301. LPCWSTR szProvider,
  302. DWORD dwProvType,
  303. DWORD dwFlags
  304. );
  305. typedef BOOL (WINAPI *CryptCreateHash) (
  306. HCRYPTPROV hProv,
  307. ALG_ID Algid,
  308. HCRYPTKEY hKey,
  309. DWORD dwFlags,
  310. HCRYPTHASH *phHash
  311. );
  312. typedef BOOL (WINAPI *CryptHashData) (
  313. HCRYPTHASH hHash,
  314. CONST BYTE *pbData,
  315. DWORD dwDataLen,
  316. DWORD dwFlags
  317. );
  318. typedef BOOL (WINAPI *CryptGetHashParam) (
  319. HCRYPTHASH hHash,
  320. DWORD dwParam,
  321. BYTE *pbData,
  322. DWORD *pdwDataLen,
  323. DWORD dwFlags
  324. );
  325. const static UNICODE_STRING ModuleName =
  326. RTL_CONSTANT_STRING(L"ADVAPI32.DLL");
  327. const static ANSI_STRING ProcedureNameCryptAcquireContext =
  328. RTL_CONSTANT_STRING("CryptAcquireContextW");
  329. const static ANSI_STRING ProcedureNameCryptCreateHash =
  330. RTL_CONSTANT_STRING("CryptCreateHash");
  331. const static ANSI_STRING ProcedureNameCryptHashData =
  332. RTL_CONSTANT_STRING("CryptHashData");
  333. const static ANSI_STRING ProcedureNameCryptGetHashParam =
  334. RTL_CONSTANT_STRING("CryptGetHashParam");
  335. static CryptAcquireContextW lpfnCryptAcquireContextW;
  336. static CryptCreateHash lpfnCryptCreateHash;
  337. static CryptHashData lpfnCryptHashData;
  338. static CryptGetHashParam lpfnCryptGetHashParam;
  339. if (AdvApi32ModuleHandle == NULL) {
  340. //
  341. // We tried to load ADVAPI32.DLL once before, but failed.
  342. //
  343. Status = STATUS_ENTRYPOINT_NOT_FOUND;
  344. goto ExitHandler;
  345. }
  346. if (AdvApi32ModuleHandle == LongToHandle(-1)) {
  347. HANDLE TempModuleHandle;
  348. //
  349. // Load advapi32.dll for crypt functions. We'll pass a special flag in
  350. // DllCharacteristics to eliminate WinSafer checking on advapi.
  351. //
  352. {
  353. ULONG DllCharacteristics = IMAGE_FILE_SYSTEM;
  354. Status = LdrLoadDll(UNICODE_NULL,
  355. &DllCharacteristics,
  356. (PUNICODE_STRING) &ModuleName,
  357. &TempModuleHandle);
  358. if (!NT_SUCCESS(Status)) {
  359. Status = STATUS_DLL_NOT_FOUND;
  360. AdvApi32ModuleHandle = NULL;
  361. goto ExitHandler;
  362. }
  363. }
  364. //
  365. // Get function pointers to the APIs that we'll need. If we fail
  366. // to get pointers for any of them, then just unload advapi and
  367. // ignore all future attempts to load it within this process.
  368. //
  369. Status = LdrGetProcedureAddress(
  370. TempModuleHandle,
  371. (PANSI_STRING) &ProcedureNameCryptAcquireContext,
  372. 0,
  373. (PVOID*)&lpfnCryptAcquireContextW);
  374. if (!NT_SUCCESS(Status) || !lpfnCryptAcquireContextW) {
  375. //
  376. // Couldn't get the fn ptr. Make sure we won't try again
  377. //
  378. AdvapiLoadFailure:
  379. LdrUnloadDll(TempModuleHandle);
  380. AdvApi32ModuleHandle = NULL;
  381. Status = STATUS_ENTRYPOINT_NOT_FOUND;
  382. goto ExitHandler;
  383. }
  384. Status = LdrGetProcedureAddress(
  385. TempModuleHandle,
  386. (PANSI_STRING) &ProcedureNameCryptCreateHash,
  387. 0,
  388. (PVOID*)&lpfnCryptCreateHash);
  389. if (!NT_SUCCESS(Status) || !lpfnCryptCreateHash) {
  390. goto AdvapiLoadFailure;
  391. }
  392. Status = LdrGetProcedureAddress(
  393. TempModuleHandle,
  394. (PANSI_STRING) &ProcedureNameCryptHashData,
  395. 0,
  396. (PVOID*)&lpfnCryptHashData);
  397. if (!NT_SUCCESS(Status) || !lpfnCryptHashData) {
  398. goto AdvapiLoadFailure;
  399. }
  400. Status = LdrGetProcedureAddress(
  401. TempModuleHandle,
  402. (PANSI_STRING) &ProcedureNameCryptGetHashParam,
  403. 0,
  404. (PVOID*)&lpfnCryptGetHashParam);
  405. if (!NT_SUCCESS(Status) || !lpfnCryptGetHashParam) {
  406. goto AdvapiLoadFailure;
  407. }
  408. AdvApi32ModuleHandle = TempModuleHandle;
  409. }
  410. ASSERT(lpfnCryptAcquireContextW != NULL);
  411. if (!lpfnCryptAcquireContextW(&hProvVerify,
  412. NULL,
  413. MS_DEF_PROV_W,
  414. PROV_RSA_FULL,
  415. CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
  416. Status = STATUS_NOT_IMPLEMENTED;
  417. goto ExitHandler;
  418. }
  419. ASSERT(lpfnCryptCreateHash != NULL);
  420. if (!lpfnCryptCreateHash( hProvVerify,
  421. CALG_MD5,
  422. 0,
  423. 0,
  424. &hHash )) {
  425. Status = STATUS_NOT_IMPLEMENTED;
  426. goto ExitHandler;
  427. }
  428. //
  429. // Loop though all of the module names and function names and create a hash
  430. //
  431. pTemp = ImportedNameList;
  432. //
  433. // loop through each module
  434. //
  435. while (pTemp != NULL) {
  436. PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pTemp2 = pTemp->FunctionList;
  437. ASSERT(lpfnCryptHashData != NULL);
  438. if (!lpfnCryptHashData( hHash,
  439. (PBYTE)(pTemp->String),
  440. strlen( pTemp->String ),
  441. 0)) {
  442. Status = STATUS_NOT_IMPLEMENTED;
  443. goto ExitHandler;
  444. }
  445. //
  446. // loop through each function
  447. //
  448. while (pTemp2 != NULL) {
  449. ASSERT(lpfnCryptHashData != NULL);
  450. if (!lpfnCryptHashData( hHash,
  451. (PBYTE)(pTemp2->String),
  452. strlen( pTemp2->String ),
  453. 0)) {
  454. Status = STATUS_NOT_IMPLEMENTED;
  455. goto ExitHandler;
  456. }
  457. pTemp2 = pTemp2->Next;
  458. }
  459. pTemp = pTemp->Next;
  460. }
  461. dwHashedDataLength = IMPORT_TABLE_MAX_HASH_SIZE;
  462. ASSERT(lpfnCryptGetHashParam != NULL);
  463. if (!lpfnCryptGetHashParam( hHash,
  464. HP_HASHVAL,
  465. Hash,
  466. &dwHashedDataLength,
  467. 0 )) {
  468. Status = STATUS_NOT_IMPLEMENTED;
  469. }
  470. ExitHandler:
  471. return Status;
  472. }
  473. VOID
  474. ImportTablepFreeModuleSorted(
  475. IN PIMPORTTABLEP_SORTED_LIST_ENTRY pImportNameList
  476. )
  477. /*++
  478. Routine Description:
  479. This routine frees the entire module/function list.
  480. Arguments:
  481. pImportNameList - head of the two level singly linked list
  482. Return Value:
  483. None:
  484. --*/
  485. {
  486. PIMPORTTABLEP_SORTED_LIST_ENTRY pToFree, pTemp;
  487. if ( !pImportNameList ) {
  488. return;
  489. }
  490. pToFree = pImportNameList;
  491. pTemp = pToFree->Next;
  492. while ( pToFree ) {
  493. ImportTablepFreeFunctionSorted( pToFree->FunctionList );
  494. RtlFreeHeap(RtlProcessHeap(), 0, pToFree);
  495. pToFree = pTemp;
  496. if ( pTemp ) {
  497. pTemp = pTemp->Next;
  498. }
  499. }
  500. return;
  501. }
  502. VOID
  503. ImportTablepFreeFunctionSorted(
  504. IN PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pFunctionNameList
  505. )
  506. /*++
  507. Routine Description:
  508. This routine frees function list.
  509. Arguments:
  510. pFunctionNameList - head of function name list
  511. Return Value:
  512. None:
  513. --*/
  514. {
  515. PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pToFree, pTemp;
  516. if ( !pFunctionNameList ) {
  517. return;
  518. }
  519. pToFree = pFunctionNameList;
  520. pTemp = pToFree->Next;
  521. while ( pToFree ) {
  522. RtlFreeHeap(RtlProcessHeap(), 0, pToFree);
  523. pToFree = pTemp;
  524. if ( pTemp ) {
  525. pTemp = pTemp->Next;
  526. }
  527. }
  528. return;
  529. }