windows-nt/Source/XPSP1/NT/base/ntdll/importtablehash.c
2020-09-26 16:20:57 +08:00

769 lines
20 KiB
C

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
ImportTableHash.c
Abstract:
This module contains hash computation routine
RtlComputeImportTableHash to compute the hash
based on the import table of an exe.
Author:
Vishnu Patankar (VishnuP) 31-May-2001
Revision History:
--*/
#include "ImportTableHash.h"
NTSTATUS
RtlComputeImportTableHash(
IN HANDLE hFile,
IN PCHAR Hash,
IN ULONG ImportTableHashRevision
)
/*++
Routine Description:
This routine computes the limited MD5 hash.
First, the image is memory mapped and a canonical
sorted list of module name and function name is created
from the exe's import table.
Second, the hash value is computed using the canonical
information.
Arguments:
hFile - the handle of the file to compute the hash for
Hash - the hash value returned - this has to be atleast 16 bytes long
ImportTableHashRevision - the revision of the computation method for compatibility
only ITH_REVISION_1 is supported today
Return Value:
The status of the hash computation.
--*/
{
PIMPORTTABLEP_SORTED_LIST_ENTRY pTemp = NULL;
PIMPORTTABLEP_SORTED_LIST_ENTRY ListEntry = NULL;
PIMPORTTABLEP_SORTED_LIST_ENTRY ImportedNameList = NULL;
PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY FunctionListEntry;
ULONG ImportDescriptorSize = 0;
HANDLE hMap = INVALID_HANDLE_VALUE;
LPVOID FileMapping = NULL;
PIMAGE_THUNK_DATA OriginalFirstThunk;
PIMAGE_IMPORT_BY_NAME AddressOfData;
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
ACCESS_MASK DesiredAccess;
ULONG AllocationAttributes;
DWORD flProtect = PAGE_READONLY;
LARGE_INTEGER SectionOffset;
SIZE_T ViewSize;
NTSTATUS Status = STATUS_SUCCESS;
if ( ITH_REVISION_1 != ImportTableHashRevision ) {
Status = STATUS_UNKNOWN_REVISION;
goto ExitHandler;
}
//
// Unwrap CreateFileMappingW (since that API is not available in ntdll.dll)
//
DesiredAccess = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ;
AllocationAttributes = flProtect & (SEC_FILE | SEC_IMAGE | SEC_RESERVE | SEC_COMMIT | SEC_NOCACHE);
flProtect ^= AllocationAttributes;
if (AllocationAttributes == 0) {
AllocationAttributes = SEC_COMMIT;
}
Status = NtCreateSection(
&hMap,
DesiredAccess,
NULL,
NULL,
flProtect,
AllocationAttributes,
hFile
);
if ( hMap == INVALID_HANDLE_VALUE || !NT_SUCCESS(Status) ) {
Status = STATUS_INVALID_HANDLE;
goto ExitHandler;
}
SectionOffset.LowPart = 0;
SectionOffset.HighPart = 0;
ViewSize = 0;
Status = NtMapViewOfSection(
hMap,
NtCurrentProcess(),
&FileMapping,
0L,
0L,
&SectionOffset,
&ViewSize,
ViewShare,
0L,
PAGE_READONLY
);
if (FileMapping == NULL || !NT_SUCCESS(Status) ) {
Status = STATUS_NOT_MAPPED_VIEW;
goto ExitHandler;
}
ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData (
FileMapping,
FALSE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ImportDescriptorSize
);
if (ImportDescriptor == NULL) {
Status = STATUS_RESOURCE_DATA_NOT_FOUND;
goto ExitHandler;
}
//
// outer loop that iterates over all modules in the import table of the exe
//
while ((ImportDescriptor->Name != 0) && (ImportDescriptor->FirstThunk != 0)) {
PSZ ImportName = (PSZ)RtlAddressInSectionTable(
RtlImageNtHeader(FileMapping),
FileMapping,
ImportDescriptor->Name
);
if ( ImportName == NULL ) {
Status = STATUS_RESOURCE_NAME_NOT_FOUND;
goto ExitHandler;
}
ListEntry = (PIMPORTTABLEP_SORTED_LIST_ENTRY)RtlAllocateHeap(RtlProcessHeap(), 0, sizeof( IMPORTTABLEP_SORTED_LIST_ENTRY ));
if ( ListEntry == NULL ) {
Status = STATUS_NO_MEMORY;
goto ExitHandler;
}
ListEntry->String = ImportName;
ListEntry->FunctionList = NULL;
ListEntry->Next = NULL;
ImportTablepInsertModuleSorted( ListEntry, &ImportedNameList );
OriginalFirstThunk = (PIMAGE_THUNK_DATA)RtlAddressInSectionTable(
RtlImageNtHeader(FileMapping),
FileMapping,
ImportDescriptor->OriginalFirstThunk
);
//
// inner loop that iterates over all functions for a given module
//
while (OriginalFirstThunk->u1.Ordinal) {
if (!IMAGE_SNAP_BY_ORDINAL( OriginalFirstThunk->u1.Ordinal)) {
AddressOfData = (PIMAGE_IMPORT_BY_NAME)RtlAddressInSectionTable(
RtlImageNtHeader(FileMapping),
FileMapping,
(ULONG)OriginalFirstThunk->u1.AddressOfData
);
if ( AddressOfData == NULL || AddressOfData->Name == NULL ) {
Status = STATUS_RESOURCE_NAME_NOT_FOUND;
goto ExitHandler;
}
FunctionListEntry = (PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY)RtlAllocateHeap(RtlProcessHeap(), 0, sizeof( IMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY ));
if (FunctionListEntry == NULL ) {
Status = STATUS_NO_MEMORY;
goto ExitHandler;
}
FunctionListEntry->Next = NULL;
FunctionListEntry->String = (PSZ)AddressOfData->Name;
ImportTablepInsertFunctionSorted( FunctionListEntry, &ListEntry->FunctionList );
}
OriginalFirstThunk++;
}
ImportDescriptor++;
}
//
// finally hash the canonical information (sorted module and sorted function list)
//
Status = ImportTablepHashCanonicalLists( ImportedNameList, Hash );
ExitHandler:
ImportTablepFreeModuleSorted( ImportedNameList );
if (FileMapping) {
NTSTATUS StatusUnmap;
//
// unwrap UnmapViewOfFile (since that API is not available in ntdll.dll)
//
StatusUnmap = NtUnmapViewOfSection(NtCurrentProcess(),(PVOID)FileMapping);
if ( !NT_SUCCESS(StatusUnmap) ) {
if (StatusUnmap == STATUS_INVALID_PAGE_PROTECTION) {
//
// Unlock any pages that were locked with MmSecureVirtualMemory.
// This is useful for SANs.
//
if (RtlFlushSecureMemoryCache((PVOID)FileMapping, 0)) {
StatusUnmap = NtUnmapViewOfSection(NtCurrentProcess(),
(PVOID)FileMapping
);
}
}
}
}
if (hMap != INVALID_HANDLE_VALUE ) {
NtClose(hMap);
}
return Status;
}
VOID
ImportTablepInsertFunctionSorted(
IN PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pFunctionName,
OUT PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY * ppFunctionNameList
)
/*++
Routine Description:
This routine inserts a function name in a sorted order.
Arguments:
pFunctionName - name of the function
ppFunctionNameList - pointer to the head of the function list to be updated
Return Value:
None:
--*/
{
PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pPrev;
PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pTemp;
//
// Special case, list is empty, insert at the front.
//
if (*ppFunctionNameList == NULL
|| _stricmp((*ppFunctionNameList)->String, pFunctionName->String) > 0) {
pFunctionName->Next = *ppFunctionNameList;
*ppFunctionNameList = pFunctionName;
return;
}
pPrev = *ppFunctionNameList;
pTemp = (*ppFunctionNameList)->Next;
while (pTemp) {
if (_stricmp(pTemp->String, pFunctionName->String) >= 0) {
pFunctionName->Next = pTemp;
pPrev->Next = pFunctionName;
return;
}
pPrev = pTemp;
pTemp = pTemp->Next;
}
pFunctionName->Next = NULL;
pPrev->Next = pFunctionName;
return;
}
VOID
ImportTablepInsertModuleSorted(
IN PIMPORTTABLEP_SORTED_LIST_ENTRY pImportName,
OUT PIMPORTTABLEP_SORTED_LIST_ENTRY * ppImportNameList
)
/*++
Routine Description:
This routine inserts a module name (dll) in a sorted order.
Arguments:
pImportName - the import name that needs to be inserted
ppImportNameList - pointer to the head of the list to be updated
Return Value:
None:
--*/
{
PIMPORTTABLEP_SORTED_LIST_ENTRY pPrev;
PIMPORTTABLEP_SORTED_LIST_ENTRY pTemp;
//
// Special case, list is empty, insert at the front.
//
if (*ppImportNameList == NULL
|| _stricmp((*ppImportNameList)->String, pImportName->String) > 0) {
pImportName->Next = *ppImportNameList;
*ppImportNameList = pImportName;
return;
}
pPrev = *ppImportNameList;
pTemp = (*ppImportNameList)->Next;
while (pTemp) {
if (_stricmp(pTemp->String, pImportName->String) >= 0) {
pImportName->Next = pTemp;
pPrev->Next = pImportName;
return;
}
pPrev = pTemp;
pTemp = pTemp->Next;
}
pImportName->Next = NULL;
pPrev->Next = pImportName;
return;
}
static HANDLE AdvApi32ModuleHandle = (HANDLE) (ULONG_PTR) -1;
NTSTATUS
ImportTablepHashCanonicalLists(
IN PIMPORTTABLEP_SORTED_LIST_ENTRY ImportedNameList,
OUT PBYTE Hash
)
/*++
Routine Description:
This routine computes the hash values from a given import list.
advapi32.dll is dynamically loaded - once only per process,
and the crypto APIs are used to compute the hash value.
Arguments:
ImportedNameList - the head of the module name/function name list
Hash - the buffer to use to fill in the hash value
Return Value:
STATUS_SUCCESS if the hash value is calculated, otherwise the error status
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD dwHashedDataLength = 0;
PIMPORTTABLEP_SORTED_LIST_ENTRY pTemp;
HCRYPTHASH hHash;
HCRYPTPROV hProvVerify;
typedef BOOL (WINAPI *CryptAcquireContextW) (
HCRYPTPROV *phProv,
LPCWSTR szContainer,
LPCWSTR szProvider,
DWORD dwProvType,
DWORD dwFlags
);
typedef BOOL (WINAPI *CryptCreateHash) (
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTKEY hKey,
DWORD dwFlags,
HCRYPTHASH *phHash
);
typedef BOOL (WINAPI *CryptHashData) (
HCRYPTHASH hHash,
CONST BYTE *pbData,
DWORD dwDataLen,
DWORD dwFlags
);
typedef BOOL (WINAPI *CryptGetHashParam) (
HCRYPTHASH hHash,
DWORD dwParam,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwFlags
);
const static UNICODE_STRING ModuleName =
RTL_CONSTANT_STRING(L"ADVAPI32.DLL");
const static ANSI_STRING ProcedureNameCryptAcquireContext =
RTL_CONSTANT_STRING("CryptAcquireContextW");
const static ANSI_STRING ProcedureNameCryptCreateHash =
RTL_CONSTANT_STRING("CryptCreateHash");
const static ANSI_STRING ProcedureNameCryptHashData =
RTL_CONSTANT_STRING("CryptHashData");
const static ANSI_STRING ProcedureNameCryptGetHashParam =
RTL_CONSTANT_STRING("CryptGetHashParam");
static CryptAcquireContextW lpfnCryptAcquireContextW;
static CryptCreateHash lpfnCryptCreateHash;
static CryptHashData lpfnCryptHashData;
static CryptGetHashParam lpfnCryptGetHashParam;
if (AdvApi32ModuleHandle == NULL) {
//
// We tried to load ADVAPI32.DLL once before, but failed.
//
Status = STATUS_ENTRYPOINT_NOT_FOUND;
goto ExitHandler;
}
if (AdvApi32ModuleHandle == LongToHandle(-1)) {
HANDLE TempModuleHandle;
//
// Load advapi32.dll for crypt functions. We'll pass a special flag in
// DllCharacteristics to eliminate WinSafer checking on advapi.
//
{
ULONG DllCharacteristics = IMAGE_FILE_SYSTEM;
Status = LdrLoadDll(UNICODE_NULL,
&DllCharacteristics,
(PUNICODE_STRING) &ModuleName,
&TempModuleHandle);
if (!NT_SUCCESS(Status)) {
Status = STATUS_DLL_NOT_FOUND;
AdvApi32ModuleHandle = NULL;
goto ExitHandler;
}
}
//
// Get function pointers to the APIs that we'll need. If we fail
// to get pointers for any of them, then just unload advapi and
// ignore all future attempts to load it within this process.
//
Status = LdrGetProcedureAddress(
TempModuleHandle,
(PANSI_STRING) &ProcedureNameCryptAcquireContext,
0,
(PVOID*)&lpfnCryptAcquireContextW);
if (!NT_SUCCESS(Status) || !lpfnCryptAcquireContextW) {
//
// Couldn't get the fn ptr. Make sure we won't try again
//
AdvapiLoadFailure:
LdrUnloadDll(TempModuleHandle);
AdvApi32ModuleHandle = NULL;
Status = STATUS_ENTRYPOINT_NOT_FOUND;
goto ExitHandler;
}
Status = LdrGetProcedureAddress(
TempModuleHandle,
(PANSI_STRING) &ProcedureNameCryptCreateHash,
0,
(PVOID*)&lpfnCryptCreateHash);
if (!NT_SUCCESS(Status) || !lpfnCryptCreateHash) {
goto AdvapiLoadFailure;
}
Status = LdrGetProcedureAddress(
TempModuleHandle,
(PANSI_STRING) &ProcedureNameCryptHashData,
0,
(PVOID*)&lpfnCryptHashData);
if (!NT_SUCCESS(Status) || !lpfnCryptHashData) {
goto AdvapiLoadFailure;
}
Status = LdrGetProcedureAddress(
TempModuleHandle,
(PANSI_STRING) &ProcedureNameCryptGetHashParam,
0,
(PVOID*)&lpfnCryptGetHashParam);
if (!NT_SUCCESS(Status) || !lpfnCryptGetHashParam) {
goto AdvapiLoadFailure;
}
AdvApi32ModuleHandle = TempModuleHandle;
}
ASSERT(lpfnCryptAcquireContextW != NULL);
if (!lpfnCryptAcquireContextW(&hProvVerify,
NULL,
MS_DEF_PROV_W,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
Status = STATUS_NOT_IMPLEMENTED;
goto ExitHandler;
}
ASSERT(lpfnCryptCreateHash != NULL);
if (!lpfnCryptCreateHash( hProvVerify,
CALG_MD5,
0,
0,
&hHash )) {
Status = STATUS_NOT_IMPLEMENTED;
goto ExitHandler;
}
//
// Loop though all of the module names and function names and create a hash
//
pTemp = ImportedNameList;
//
// loop through each module
//
while (pTemp != NULL) {
PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pTemp2 = pTemp->FunctionList;
ASSERT(lpfnCryptHashData != NULL);
if (!lpfnCryptHashData( hHash,
(PBYTE)(pTemp->String),
strlen( pTemp->String ),
0)) {
Status = STATUS_NOT_IMPLEMENTED;
goto ExitHandler;
}
//
// loop through each function
//
while (pTemp2 != NULL) {
ASSERT(lpfnCryptHashData != NULL);
if (!lpfnCryptHashData( hHash,
(PBYTE)(pTemp2->String),
strlen( pTemp2->String ),
0)) {
Status = STATUS_NOT_IMPLEMENTED;
goto ExitHandler;
}
pTemp2 = pTemp2->Next;
}
pTemp = pTemp->Next;
}
dwHashedDataLength = IMPORT_TABLE_MAX_HASH_SIZE;
ASSERT(lpfnCryptGetHashParam != NULL);
if (!lpfnCryptGetHashParam( hHash,
HP_HASHVAL,
Hash,
&dwHashedDataLength,
0 )) {
Status = STATUS_NOT_IMPLEMENTED;
}
ExitHandler:
return Status;
}
VOID
ImportTablepFreeModuleSorted(
IN PIMPORTTABLEP_SORTED_LIST_ENTRY pImportNameList
)
/*++
Routine Description:
This routine frees the entire module/function list.
Arguments:
pImportNameList - head of the two level singly linked list
Return Value:
None:
--*/
{
PIMPORTTABLEP_SORTED_LIST_ENTRY pToFree, pTemp;
if ( !pImportNameList ) {
return;
}
pToFree = pImportNameList;
pTemp = pToFree->Next;
while ( pToFree ) {
ImportTablepFreeFunctionSorted( pToFree->FunctionList );
RtlFreeHeap(RtlProcessHeap(), 0, pToFree);
pToFree = pTemp;
if ( pTemp ) {
pTemp = pTemp->Next;
}
}
return;
}
VOID
ImportTablepFreeFunctionSorted(
IN PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pFunctionNameList
)
/*++
Routine Description:
This routine frees function list.
Arguments:
pFunctionNameList - head of function name list
Return Value:
None:
--*/
{
PIMPORTTABLEP_SORTED_FUNCTION_LIST_ENTRY pToFree, pTemp;
if ( !pFunctionNameList ) {
return;
}
pToFree = pFunctionNameList;
pTemp = pToFree->Next;
while ( pToFree ) {
RtlFreeHeap(RtlProcessHeap(), 0, pToFree);
pToFree = pTemp;
if ( pTemp ) {
pTemp = pTemp->Next;
}
}
return;
}