windows-nt/Source/XPSP1/NT/sdktools/debuggers/imagehlp/bindi.c
2020-09-26 16:20:57 +08:00

1457 lines
51 KiB
C

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
bindi.c
Abstract:
Implementation for the BindImage API
Author:
Revision History:
--*/
#include <private.h>
typedef struct _BOUND_FORWARDER_REFS {
struct _BOUND_FORWARDER_REFS *Next;
ULONG TimeDateStamp;
LPSTR ModuleName;
} BOUND_FORWARDER_REFS, *PBOUND_FORWARDER_REFS;
typedef struct _IMPORT_DESCRIPTOR {
struct _IMPORT_DESCRIPTOR *Next;
LPSTR ModuleName;
ULONG TimeDateStamp;
USHORT NumberOfModuleForwarderRefs;
PBOUND_FORWARDER_REFS Forwarders;
} IMPORT_DESCRIPTOR, *PIMPORT_DESCRIPTOR;
typedef struct _BINDP_PARAMETERS {
DWORD Flags;
BOOLEAN fNoUpdate;
BOOLEAN fNewImports;
LPSTR ImageName;
LPSTR DllPath;
LPSTR SymbolPath;
PIMAGEHLP_STATUS_ROUTINE StatusRoutine;
} BINDP_PARAMETERS, *PBINDP_PARAMETERS;
BOOL
BindpLookupThunk(
PBINDP_PARAMETERS Parms,
PIMAGE_THUNK_DATA ThunkName,
PLOADED_IMAGE Image,
PIMAGE_THUNK_DATA SnappedThunks,
PIMAGE_THUNK_DATA FunctionAddress,
PLOADED_IMAGE Dll,
PIMAGE_EXPORT_DIRECTORY Exports,
PIMPORT_DESCRIPTOR NewImport,
LPSTR DllPath,
PULONG *ForwarderChain
);
PVOID
BindpRvaToVa(
PBINDP_PARAMETERS Parms,
ULONG Rva,
PLOADED_IMAGE Image
);
VOID
BindpWalkAndProcessImports(
PBINDP_PARAMETERS Parms,
PLOADED_IMAGE Image,
LPSTR DllPath,
PBOOL ImageModified
);
BOOL
BindImage(
IN LPSTR ImageName,
IN LPSTR DllPath,
IN LPSTR SymbolPath
)
{
return BindImageEx( 0,
ImageName,
DllPath,
SymbolPath,
NULL
);
}
UCHAR BindpCapturedModuleNames[4096];
LPSTR BindpEndCapturedModuleNames;
LPSTR
BindpCaptureImportModuleName(
LPSTR DllName
)
{
LPSTR s;
s = (LPSTR) BindpCapturedModuleNames;
if (BindpEndCapturedModuleNames == NULL) {
*s = '\0';
BindpEndCapturedModuleNames = s;
}
while (*s) {
if (!_stricmp(s, DllName)) {
return s;
}
s += strlen(s)+1;
}
strcpy(s, DllName);
BindpEndCapturedModuleNames = s + strlen(s) + 1;
*BindpEndCapturedModuleNames = '\0';
return s;
}
PIMPORT_DESCRIPTOR
BindpAddImportDescriptor(
PBINDP_PARAMETERS Parms,
PIMPORT_DESCRIPTOR *NewImportDescriptor,
PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor,
LPSTR ModuleName,
PLOADED_IMAGE Dll
)
{
PIMPORT_DESCRIPTOR p, *pp;
if (!Parms->fNewImports) {
return NULL;
}
pp = NewImportDescriptor;
while (p = *pp) {
if (!_stricmp( p->ModuleName, ModuleName )) {
return p;
}
pp = &p->Next;
}
p = (PIMPORT_DESCRIPTOR) MemAlloc( sizeof( *p ) );
if (p != NULL) {
if (Dll != NULL) {
p->TimeDateStamp = ((PIMAGE_NT_HEADERS32)Dll->FileHeader)->FileHeader.TimeDateStamp;
}
p->ModuleName = BindpCaptureImportModuleName( ModuleName );
*pp = p;
}
else
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindOutOfMemory, NULL, NULL, 0, sizeof( *p ) );
}
return p;
}
PUCHAR
BindpAddForwarderReference(
PBINDP_PARAMETERS Parms,
LPSTR ImageName,
LPSTR ImportName,
PIMPORT_DESCRIPTOR NewImportDescriptor,
LPSTR DllPath,
PUCHAR ForwarderString,
PBOOL BoundForwarder
)
{
CHAR DllName[ MAX_PATH ];
PUCHAR s;
PLOADED_IMAGE Dll;
ULONG cb;
USHORT OrdinalNumber;
USHORT HintIndex;
ULONG ExportSize;
PIMAGE_EXPORT_DIRECTORY Exports;
PULONG NameTableBase;
PUSHORT NameOrdinalTableBase;
PULONG FunctionTableBase;
LPSTR NameTableName;
ULONG64 ForwardedAddress;
PBOUND_FORWARDER_REFS p, *pp;
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
PIMAGE_FILE_HEADER FileHeader;
*BoundForwarder = FALSE;
BindAnotherForwarder:
s = ForwarderString;
while (*s && *s != '.') {
s++;
}
if (*s != '.') {
return ForwarderString;
}
cb = (ULONG) (s - ForwarderString);
if (cb >= MAX_PATH) {
return ForwarderString;
}
strncpy( DllName, (LPSTR) ForwarderString, cb );
DllName[ cb ] = '\0';
strcat( DllName, ".DLL" );
Dll = ImageLoad( DllName, DllPath );
if (!Dll) {
return ForwarderString;
}
s += 1;
Exports = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(
(PVOID)Dll->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
);
if (!Exports) {
return ForwarderString;
}
FileHeader = &((PIMAGE_NT_HEADERS32)Dll->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Dll->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
if ( *s == '#' ) {
// Binding for ordinal forwarders
OrdinalNumber = (atoi((PCHAR)s + 1)) - (USHORT)Exports->Base;
if (OrdinalNumber >= Exports->NumberOfFunctions) {
return ForwarderString;
}
} else {
// Regular binding for named forwarders
OrdinalNumber = 0xFFFF;
}
NameTableBase = (PULONG) BindpRvaToVa( Parms, Exports->AddressOfNames, Dll );
NameOrdinalTableBase = (PUSHORT) BindpRvaToVa( Parms, Exports->AddressOfNameOrdinals, Dll );
FunctionTableBase = (PULONG) BindpRvaToVa( Parms, Exports->AddressOfFunctions, Dll );
if (OrdinalNumber == 0xFFFF) {
for ( HintIndex = 0; HintIndex < Exports->NumberOfNames; HintIndex++){
NameTableName = (LPSTR) BindpRvaToVa( Parms, NameTableBase[HintIndex], Dll );
if ( NameTableName ) {
OrdinalNumber = NameOrdinalTableBase[HintIndex];
if (!strcmp((PCHAR)s, NameTableName)) {
break;
}
}
}
if (HintIndex >= Exports->NumberOfNames) {
return ForwarderString;
}
}
do {
ForwardedAddress = FunctionTableBase[OrdinalNumber] +
OPTIONALHEADER(ImageBase);
pp = &NewImportDescriptor->Forwarders;
while (p = *pp) {
if (!_stricmp(DllName, p->ModuleName)) {
break;
}
pp = &p->Next;
}
if (p == NULL) {
p = (PBOUND_FORWARDER_REFS) MemAlloc( sizeof( *p ) );
if (p == NULL) {
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindOutOfMemory, NULL, NULL, 0, sizeof( *p ) );
}
break;
}
p->ModuleName = BindpCaptureImportModuleName( DllName );
*pp = p;
NewImportDescriptor->NumberOfModuleForwarderRefs += 1;
}
p->TimeDateStamp = FileHeader->TimeDateStamp;
if (Parms->StatusRoutine != NULL)
{
(Parms->StatusRoutine)( BindForwarder,
ImageName,
ImportName,
(ULONG_PTR)ForwardedAddress,
(ULONG_PTR)ForwarderString
);
}
Exports = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(
(PVOID)Dll->MappedAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
);
Exports = (PIMAGE_EXPORT_DIRECTORY) ((ULONG_PTR)Exports -
(ULONG_PTR) Dll->MappedAddress +
OPTIONALHEADER(ImageBase));
if ((ForwardedAddress >= (ULONG_PTR)Exports) &&
(ForwardedAddress < ((ULONG_PTR)Exports + ExportSize)))
{
ForwarderString = (PUCHAR)BindpRvaToVa(Parms,
FunctionTableBase[OrdinalNumber],
Dll);
goto BindAnotherForwarder;
} else {
ForwarderString = (PUCHAR)ForwardedAddress;
*BoundForwarder = TRUE;
break;
}
}
while (0);
return ForwarderString;
}
PIMAGE_BOUND_IMPORT_DESCRIPTOR
BindpCreateNewImportSection(
PBINDP_PARAMETERS Parms,
PIMPORT_DESCRIPTOR *NewImportDescriptor,
PULONG NewImportsSize
)
{
ULONG cbString, cbStruct;
PIMPORT_DESCRIPTOR p, *pp;
PBOUND_FORWARDER_REFS p1, *pp1;
LPSTR CapturedStrings;
PIMAGE_BOUND_IMPORT_DESCRIPTOR NewImports, NewImport;
PIMAGE_BOUND_FORWARDER_REF NewForwarder;
*NewImportsSize = 0;
cbString = 0;
cbStruct = 0;
pp = NewImportDescriptor;
while (p = *pp) {
cbStruct += sizeof( IMAGE_BOUND_IMPORT_DESCRIPTOR );
pp1 = &p->Forwarders;
while (p1 = *pp1) {
cbStruct += sizeof( IMAGE_BOUND_FORWARDER_REF );
pp1 = &p1->Next;
}
pp = &p->Next;
}
if (cbStruct == 0) {
BindpEndCapturedModuleNames = NULL;
return NULL;
}
cbStruct += sizeof(IMAGE_BOUND_IMPORT_DESCRIPTOR); // Room for terminating zero entry
cbString = (ULONG) (BindpEndCapturedModuleNames - (LPSTR) BindpCapturedModuleNames);
BindpEndCapturedModuleNames = NULL;
*NewImportsSize = cbStruct+((cbString + sizeof(ULONG) - 1) & ~(sizeof(ULONG)-1));
NewImports = (PIMAGE_BOUND_IMPORT_DESCRIPTOR) MemAlloc( *NewImportsSize );
if (NewImports != NULL) {
CapturedStrings = (LPSTR)NewImports + cbStruct;
memcpy(CapturedStrings, BindpCapturedModuleNames, cbString);
NewImport = NewImports;
pp = NewImportDescriptor;
while (p = *pp) {
NewImport->TimeDateStamp = p->TimeDateStamp;
NewImport->OffsetModuleName = (USHORT)(cbStruct + (p->ModuleName - (LPSTR) BindpCapturedModuleNames));
NewImport->NumberOfModuleForwarderRefs = p->NumberOfModuleForwarderRefs;
NewForwarder = (PIMAGE_BOUND_FORWARDER_REF)(NewImport+1);
pp1 = &p->Forwarders;
while (p1 = *pp1) {
NewForwarder->TimeDateStamp = p1->TimeDateStamp;
NewForwarder->OffsetModuleName = (USHORT)(cbStruct + (p1->ModuleName - (LPSTR) BindpCapturedModuleNames));
NewForwarder += 1;
pp1 = &p1->Next;
}
NewImport = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)NewForwarder;
pp = &p->Next;
}
}
else
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindOutOfMemory, NULL, NULL, 0, *NewImportsSize );
}
pp = NewImportDescriptor;
while ((p = *pp) != NULL) {
*pp = p->Next;
pp1 = &p->Forwarders;
while ((p1 = *pp1) != NULL) {
*pp1 = p1->Next;
MemFree(p1);
}
MemFree(p);
}
return NewImports;
}
BOOL
BindpExpandImageFileHeaders(
PBINDP_PARAMETERS Parms,
PLOADED_IMAGE Dll,
ULONG NewSizeOfHeaders
)
{
HANDLE hMappedFile;
LPVOID lpMappedAddress;
DWORD dwFileSizeLow, dwOldFileSize;
DWORD dwFileSizeHigh;
DWORD dwSizeDelta;
PIMAGE_SECTION_HEADER Section;
ULONG SectionNumber;
PIMAGE_DEBUG_DIRECTORY DebugDirectories;
ULONG DebugDirectoriesSize;
ULONG OldSizeOfHeaders;
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
PIMAGE_FILE_HEADER FileHeader;
dwFileSizeLow = GetFileSize( Dll->hFile, &dwFileSizeHigh );
if (dwFileSizeLow == 0xFFFFFFFF || dwFileSizeHigh != 0) {
return FALSE;
}
FileHeader = &((PIMAGE_NT_HEADERS32)Dll->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Dll->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
if (!OptionalHeader32 && !OptionalHeader64) {
return FALSE;
}
OldSizeOfHeaders = OPTIONALHEADER(SizeOfHeaders);
dwOldFileSize = dwFileSizeLow;
dwSizeDelta = NewSizeOfHeaders - OldSizeOfHeaders;
dwFileSizeLow += dwSizeDelta;
hMappedFile = CreateFileMapping(Dll->hFile,
NULL,
PAGE_READWRITE,
dwFileSizeHigh,
dwFileSizeLow,
NULL
);
if (!hMappedFile) {
return FALSE;
}
FlushViewOfFile(Dll->MappedAddress, Dll->SizeOfImage);
UnmapViewOfFile(Dll->MappedAddress);
lpMappedAddress = MapViewOfFileEx(hMappedFile,
FILE_MAP_WRITE,
0,
0,
0,
Dll->MappedAddress
);
if (!lpMappedAddress) {
lpMappedAddress = MapViewOfFileEx(hMappedFile,
FILE_MAP_WRITE,
0,
0,
0,
0
);
}
CloseHandle(hMappedFile);
if (lpMappedAddress != Dll->MappedAddress) {
Dll->MappedAddress = (PUCHAR) lpMappedAddress;
CalculateImagePtrs(Dll);
FileHeader = &((PIMAGE_NT_HEADERS32)Dll->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Dll->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
}
if (Dll->SizeOfImage != dwFileSizeLow) {
Dll->SizeOfImage = dwFileSizeLow;
}
DebugDirectories = (PIMAGE_DEBUG_DIRECTORY)ImageDirectoryEntryToData(
(PVOID)Dll->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_DEBUG,
&DebugDirectoriesSize
);
if (DebugDirectoryIsUseful(DebugDirectories, DebugDirectoriesSize)) {
while (DebugDirectoriesSize != 0) {
DebugDirectories->PointerToRawData += dwSizeDelta;
DebugDirectories += 1;
DebugDirectoriesSize -= sizeof( *DebugDirectories );
}
}
OPTIONALHEADER_LV(SizeOfHeaders) = NewSizeOfHeaders;
if (FileHeader->PointerToSymbolTable != 0) {
// Only adjust if it's already set
FileHeader->PointerToSymbolTable += dwSizeDelta;
}
Section = Dll->Sections;
for (SectionNumber=0; SectionNumber<FileHeader->NumberOfSections; SectionNumber++) {
if (Section->PointerToRawData != 0) {
Section->PointerToRawData += dwSizeDelta;
}
if (Section->PointerToRelocations != 0) {
Section->PointerToRelocations += dwSizeDelta;
}
if (Section->PointerToLinenumbers != 0) {
Section->PointerToLinenumbers += dwSizeDelta;
}
Section += 1;
}
memmove((LPSTR)lpMappedAddress + NewSizeOfHeaders,
(LPSTR)lpMappedAddress + OldSizeOfHeaders,
dwOldFileSize - OldSizeOfHeaders
);
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindExpandFileHeaders, Dll->ModuleName, NULL, 0, NewSizeOfHeaders );
}
return TRUE;
}
BOOL
BindImageEx(
IN DWORD Flags,
IN LPSTR ImageName,
IN LPSTR DllPath,
IN LPSTR SymbolPath,
IN PIMAGEHLP_STATUS_ROUTINE StatusRoutine
)
{
BINDP_PARAMETERS Parms;
LOADED_IMAGE LoadedImageBuffer;
PLOADED_IMAGE LoadedImage;
ULONG CheckSum;
ULONG HeaderSum;
BOOL fSymbolsAlreadySplit, fRC;
SYSTEMTIME SystemTime;
FILETIME LastWriteTime;
BOOL ImageModified;
DWORD OldChecksum;
CHAR DebugFileName[ MAX_PATH ];
CHAR DebugFilePath[ MAX_PATH ];
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
PIMAGE_FILE_HEADER FileHeader;
Parms.Flags = Flags;
if (Flags & BIND_NO_BOUND_IMPORTS) {
Parms.fNewImports = FALSE;
} else {
Parms.fNewImports = TRUE;
}
if (Flags & BIND_NO_UPDATE) {
Parms.fNoUpdate = TRUE;
} else {
Parms.fNoUpdate = FALSE;
}
Parms.ImageName = ImageName;
Parms.DllPath = DllPath;
Parms.SymbolPath = SymbolPath;
Parms.StatusRoutine = StatusRoutine;
fRC = FALSE; // Assume we'll fail to bind
__try {
// Map and load the image
LoadedImage = &LoadedImageBuffer;
memset( LoadedImage, 0, sizeof( *LoadedImage ) );
if (MapAndLoad( ImageName, DllPath, LoadedImage, TRUE, Parms.fNoUpdate )) {
LoadedImage->ModuleName = ImageName;
//
// Now locate and walk through and process the images imports
//
if (LoadedImage->FileHeader != NULL &&
((Flags & BIND_ALL_IMAGES) || (!LoadedImage->fSystemImage)) ) {
FileHeader = &((PIMAGE_NT_HEADERS32)LoadedImage->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)LoadedImage->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
if (OPTIONALHEADER(DllCharacteristics) & IMAGE_DLLCHARACTERISTICS_NO_BIND) {
goto NoBind;
}
{
DWORD dwDataSize;
PVOID pData = ImageDirectoryEntryToData(
LoadedImage->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_SECURITY,
&dwDataSize
);
if (pData || dwDataSize) {
// Signed - can't bind it.
goto NoBind;
}
pData = ImageDirectoryEntryToData(
LoadedImage->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
&dwDataSize
);
if (pData || dwDataSize) {
// COR header found - see if it's strong signed
if (((IMAGE_COR20_HEADER *)pData)->StrongNameSignature.VirtualAddress &&
((IMAGE_COR20_HEADER *)pData)->StrongNameSignature.Size)
{
goto NoBind;
}
}
}
BindpWalkAndProcessImports(
&Parms,
LoadedImage,
DllPath,
&ImageModified
);
//
// If the file is being updated, then recompute the checksum.
// and update image and possibly stripped symbol file.
//
if (!Parms.fNoUpdate && ImageModified &&
(LoadedImage->hFile != INVALID_HANDLE_VALUE)) {
// The image may have been moved as part of remapping it to add space for the
// bound imports. Recalculate the file and optional headers.
FileHeader = &((PIMAGE_NT_HEADERS32)LoadedImage->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)LoadedImage->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
if ( (FileHeader->Characteristics & IMAGE_FILE_DEBUG_STRIPPED) &&
(SymbolPath != NULL) ) {
PIMAGE_DEBUG_DIRECTORY DebugDirectories;
ULONG DebugDirectoriesSize;
PIMAGE_DEBUG_MISC MiscDebug;
fSymbolsAlreadySplit = TRUE;
strcpy( DebugFileName, ImageName );
DebugDirectories = (PIMAGE_DEBUG_DIRECTORY)ImageDirectoryEntryToData(
LoadedImage->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_DEBUG,
&DebugDirectoriesSize
);
if (DebugDirectoryIsUseful(DebugDirectories, DebugDirectoriesSize)) {
while (DebugDirectoriesSize != 0) {
if (DebugDirectories->Type == IMAGE_DEBUG_TYPE_MISC) {
MiscDebug = (PIMAGE_DEBUG_MISC)
((PCHAR)LoadedImage->MappedAddress +
DebugDirectories->PointerToRawData
);
strcpy( DebugFileName, (PCHAR) MiscDebug->Data );
break;
} else {
DebugDirectories += 1;
DebugDirectoriesSize -= sizeof( *DebugDirectories );
}
}
}
} else {
fSymbolsAlreadySplit = FALSE;
}
OldChecksum = OPTIONALHEADER(CheckSum);
CheckSumMappedFile(
(PVOID)LoadedImage->MappedAddress,
GetFileSize(LoadedImage->hFile, NULL),
&HeaderSum,
&CheckSum
);
OPTIONALHEADER_LV(CheckSum) = CheckSum;
FlushViewOfFile(LoadedImage->MappedAddress, LoadedImage->SizeOfImage);
if (fSymbolsAlreadySplit) {
if ( UpdateDebugInfoFileEx(ImageName,
SymbolPath,
DebugFilePath,
(PIMAGE_NT_HEADERS32)(LoadedImage->FileHeader),
OldChecksum)) {
if (GetLastError() == ERROR_INVALID_DATA) {
if (Parms.StatusRoutine != NULL) {
(Parms.StatusRoutine)( BindMismatchedSymbols,
LoadedImage->ModuleName,
NULL,
0,
(ULONG_PTR)DebugFileName
);
}
}
} else {
if (Parms.StatusRoutine != NULL) {
(Parms.StatusRoutine)( BindSymbolsNotUpdated,
LoadedImage->ModuleName,
NULL,
0,
(ULONG_PTR)DebugFileName
);
}
}
}
GetSystemTime(&SystemTime);
if (SystemTimeToFileTime( &SystemTime, &LastWriteTime )) {
SetFileTime( LoadedImage->hFile, NULL, NULL, &LastWriteTime );
}
}
}
NoBind:
fRC = TRUE;
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
// Nothing to do...
}
if (LoadedImage->MappedAddress) {
UnmapViewOfFile( LoadedImage->MappedAddress );
}
if (LoadedImage->hFile != INVALID_HANDLE_VALUE) {
CloseHandle( LoadedImage->hFile );
}
if (!(Flags & BIND_CACHE_IMPORT_DLLS)) {
UnloadAllImages();
}
return (fRC);
}
BOOL
BindpLookupThunk(
PBINDP_PARAMETERS Parms,
PIMAGE_THUNK_DATA ThunkName,
PLOADED_IMAGE Image,
PIMAGE_THUNK_DATA SnappedThunks,
PIMAGE_THUNK_DATA FunctionAddress,
PLOADED_IMAGE Dll,
PIMAGE_EXPORT_DIRECTORY Exports,
PIMPORT_DESCRIPTOR NewImport,
LPSTR DllPath,
PULONG *ForwarderChain
)
{
BOOL Ordinal;
USHORT OrdinalNumber;
PULONG NameTableBase;
PUSHORT NameOrdinalTableBase;
PULONG FunctionTableBase;
PIMAGE_IMPORT_BY_NAME ImportName;
USHORT HintIndex;
LPSTR NameTableName;
ULONG64 ExportsBase;
ULONG ExportSize;
UCHAR NameBuffer[ 32 ];
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
PIMAGE_OPTIONAL_HEADER32 DllOptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 DllOptionalHeader64 = NULL;
NameTableBase = (PULONG) BindpRvaToVa( Parms, Exports->AddressOfNames, Dll );
if (!NameTableBase)
return FALSE;
NameOrdinalTableBase = (PUSHORT) BindpRvaToVa( Parms, Exports->AddressOfNameOrdinals, Dll );
FunctionTableBase = (PULONG) BindpRvaToVa( Parms, Exports->AddressOfFunctions, Dll );
if (!FunctionTableBase) {
return FALSE;
}
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Image->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Dll->FileHeader,
&DllOptionalHeader32,
&DllOptionalHeader64);
//
// Determine if snap is by name, or by ordinal
//
Ordinal = (BOOL)IMAGE_SNAP_BY_ORDINAL(ThunkName->u1.Ordinal);
if (Ordinal) {
UCHAR szOrdinal[8];
OrdinalNumber = (USHORT)(IMAGE_ORDINAL(ThunkName->u1.Ordinal) - Exports->Base);
if ( (ULONG)OrdinalNumber >= Exports->NumberOfFunctions ) {
return FALSE;
}
ImportName = (PIMAGE_IMPORT_BY_NAME)NameBuffer;
// Can't use sprintf w/o dragging in more CRT support than we want... Must run on Win95.
strcpy((PCHAR) ImportName->Name, "Ordinal");
strcat((PCHAR) ImportName->Name, _ultoa((ULONG) OrdinalNumber, (LPSTR) szOrdinal, 16));
}
else {
ImportName = (PIMAGE_IMPORT_BY_NAME)BindpRvaToVa(
Parms,
(ULONG)(ULONG_PTR)(ThunkName->u1.AddressOfData),
Image
);
if (!ImportName) {
return FALSE;
}
//
// now check to see if the hint index is in range. If it
// is, then check to see if it matches the function at
// the hint. If all of this is true, then we can snap
// by hint. Otherwise need to scan the name ordinal table
//
OrdinalNumber = (USHORT)(Exports->NumberOfFunctions+1);
HintIndex = ImportName->Hint;
if ((ULONG)HintIndex < Exports->NumberOfNames ) {
NameTableName = (LPSTR) BindpRvaToVa( Parms, NameTableBase[HintIndex], Dll );
if ( NameTableName ) {
if ( !strcmp((PCHAR)ImportName->Name, NameTableName) ) {
OrdinalNumber = NameOrdinalTableBase[HintIndex];
}
}
}
if ((ULONG)OrdinalNumber >= Exports->NumberOfFunctions) {
for (HintIndex = 0; HintIndex < Exports->NumberOfNames; HintIndex++) {
NameTableName = (LPSTR) BindpRvaToVa( Parms, NameTableBase[HintIndex], Dll );
if (NameTableName) {
if (!strcmp( (PCHAR)ImportName->Name, NameTableName )) {
OrdinalNumber = NameOrdinalTableBase[HintIndex];
break;
}
}
}
if ((ULONG)OrdinalNumber >= Exports->NumberOfFunctions) {
return FALSE;
}
}
}
(FunctionAddress->u1.Function) = (ULONG)(FunctionTableBase[OrdinalNumber] +
(DllOptionalHeader32 ?
DllOptionalHeader32->ImageBase :
DllOptionalHeader64->ImageBase)
);
ExportsBase = (ULONG64)ImageDirectoryEntryToData(
(PVOID)Dll->MappedAddress,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
) - (ULONG_PTR)Dll->MappedAddress;
ExportsBase += (DllOptionalHeader32 ?
DllOptionalHeader32->ImageBase :
DllOptionalHeader64->ImageBase);
if ((ULONG64)FunctionAddress->u1.Function > (ULONG64)ExportsBase &&
(ULONG64)FunctionAddress->u1.Function < ((ULONG64)ExportsBase + ExportSize)
) {
BOOL BoundForwarder;
BoundForwarder = FALSE;
if (NewImport != NULL) {
(FunctionAddress->u1.ForwarderString) = (ULONG_PTR)BindpAddForwarderReference(Parms,
Image->ModuleName,
(LPSTR) ImportName->Name,
NewImport,
DllPath,
(PUCHAR) BindpRvaToVa( Parms, FunctionTableBase[OrdinalNumber], Dll ),
&BoundForwarder
);
}
if (!BoundForwarder) {
**ForwarderChain = (ULONG) (FunctionAddress - SnappedThunks);
*ForwarderChain = (ULONG *)&FunctionAddress->u1.Ordinal;
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindForwarderNOT,
Image->ModuleName,
Dll->ModuleName,
(ULONG_PTR)FunctionAddress->u1.Function,
(ULONG_PTR)(ImportName->Name)
);
}
}
}
else {
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindImportProcedure,
Image->ModuleName,
Dll->ModuleName,
(ULONG_PTR)FunctionAddress->u1.Function,
(ULONG_PTR)(ImportName->Name)
);
}
}
return TRUE;
}
PVOID
BindpRvaToVa(
PBINDP_PARAMETERS Parms,
ULONG Rva,
PLOADED_IMAGE Image
)
{
PVOID Va;
Va = ImageRvaToVa( Image->FileHeader,
Image->MappedAddress,
Rva,
&Image->LastRvaSection
);
if (!Va && Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindRvaToVaFailed,
Image->ModuleName,
NULL,
(ULONG)Rva,
0
);
}
return Va;
}
VOID
SetIdataToRo(
PLOADED_IMAGE Image
)
{
PIMAGE_SECTION_HEADER Section;
ULONG i;
for(Section = Image->Sections,i=0; i<Image->NumberOfSections; i++,Section++) {
if (!_stricmp((PCHAR) Section->Name, ".idata")) {
if (Section->Characteristics & IMAGE_SCN_MEM_WRITE) {
Section->Characteristics &= ~IMAGE_SCN_MEM_WRITE;
Section->Characteristics |= IMAGE_SCN_MEM_READ;
}
break;
}
}
}
VOID
BindpWalkAndProcessImports(
PBINDP_PARAMETERS Parms,
PLOADED_IMAGE Image,
LPSTR DllPath,
PBOOL ImageModified
)
{
ULONG ForwarderChainHead;
PULONG ForwarderChain;
ULONG ImportSize;
ULONG ExportSize;
PIMPORT_DESCRIPTOR NewImportDescriptorHead, NewImportDescriptor;
PIMAGE_BOUND_IMPORT_DESCRIPTOR PrevNewImports, NewImports;
ULONG PrevNewImportsSize, NewImportsSize;
PIMAGE_IMPORT_DESCRIPTOR Imports;
PIMAGE_EXPORT_DIRECTORY Exports;
LPSTR ImportModule;
PLOADED_IMAGE Dll;
PIMAGE_THUNK_DATA tname,tsnap;
PIMAGE_THUNK_DATA ThunkNames;
PIMAGE_THUNK_DATA SnappedThunks;
PIMAGE_IMPORT_BY_NAME ImportName;
ULONG NumberOfThunks;
ULONG i, cb;
BOOL Ordinal, BindThunkFailed, NoErrors;
USHORT OrdinalNumber;
UCHAR NameBuffer[ 32 ];
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
PIMAGE_FILE_HEADER FileHeader;
NoErrors = FALSE;
*ImageModified = FALSE;
//
// Locate the import array for this image/dll
//
NewImportDescriptorHead = NULL;
Imports = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
(PVOID)Image->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ImportSize
);
if (Imports == NULL) {
//
// Nothing to bind if no imports
//
return;
}
FileHeader = &((PIMAGE_NT_HEADERS32)Image->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Image->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
PrevNewImports = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
(PVOID)Image->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
&PrevNewImportsSize
);
// If the user asked for an old style bind and there are new style bind records
// already in the image, zero them out first. This is the fix the problem where
// you bind on NT (creating new import descriptors), boot Win95 and bind there
// (creating old bind format), and then reboot to NT (the loader will only check
// the BOUND_IMPORT array.
if (PrevNewImports &&
(Parms->fNewImports == FALSE) &&
(Parms->fNoUpdate == FALSE ))
{
OPTIONALHEADER_LV(DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ].VirtualAddress) = 0;
OPTIONALHEADER_LV(DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ].Size) = 0;
PrevNewImports = 0;
PrevNewImportsSize = 0;
*ImageModified = TRUE;
}
//
// For each import record
//
for(;Imports;Imports++) {
if ( !Imports->Name ) {
break;
}
//
// Locate the module being imported and load the dll
//
ImportModule = (LPSTR)BindpRvaToVa( Parms, Imports->Name, Image );
if (ImportModule) {
Dll = ImageLoad( ImportModule, DllPath );
if (!Dll) {
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindImportModuleFailed,
Image->ModuleName,
ImportModule,
0,
0
);
}
//
// Unless specifically told not to, generate the new style
// import descriptor.
//
BindpAddImportDescriptor(Parms,
&NewImportDescriptorHead,
Imports,
ImportModule,
Dll
);
continue;
}
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindImportModule,
Image->ModuleName,
ImportModule,
0,
0
);
}
//
// If we can load the DLL, locate the export section and
// start snapping the thunks
//
Exports = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(
(PVOID)Dll->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
);
if ( !Exports ) {
continue;
}
//
// assert that the export directory addresses can be translated
//
if ( !BindpRvaToVa( Parms, Exports->AddressOfNames, Dll ) ) {
continue;
}
if ( !BindpRvaToVa( Parms, Exports->AddressOfNameOrdinals, Dll ) ) {
continue;
}
if ( !BindpRvaToVa( Parms, Exports->AddressOfFunctions, Dll ) ) {
continue;
}
//
// For old style bind, bypass the bind if it's already bound.
// New style binds s/b looked up in PrevNewImport.
//
if ( Parms->fNewImports == FALSE &&
Imports->TimeDateStamp &&
Imports->TimeDateStamp == FileHeader->TimeDateStamp ) {
continue;
}
//
// Now we need to size our thunk table and
// allocate a buffer to hold snapped thunks. This is
// done instead of writting to the mapped view so that
// thunks are only updated if we find all the entry points
//
ThunkNames = (PIMAGE_THUNK_DATA) BindpRvaToVa( Parms, Imports->OriginalFirstThunk, Image );
if (!ThunkNames || ThunkNames->u1.Function == 0) {
//
// Skip this one if no thunks or first thunk is the terminating null thunk
//
continue;
}
//
// Unless specifically told not to, generate the new style
// import descriptor.
//
NewImportDescriptor = BindpAddImportDescriptor(Parms,
&NewImportDescriptorHead,
Imports,
ImportModule,
Dll
);
NumberOfThunks = 0;
tname = ThunkNames;
while (tname->u1.AddressOfData) {
NumberOfThunks++;
tname++;
}
SnappedThunks = (PIMAGE_THUNK_DATA) MemAlloc( NumberOfThunks*sizeof(*SnappedThunks) );
if ( !SnappedThunks ) {
continue;
}
tname = ThunkNames;
tsnap = SnappedThunks;
NoErrors = TRUE;
ForwarderChainHead = (ULONG)-1;
ForwarderChain = &ForwarderChainHead;
for(i=0;i<NumberOfThunks;i++) {
BindThunkFailed = FALSE;
__try {
if (!BindpLookupThunk( Parms,
tname,
Image,
SnappedThunks,
tsnap,
Dll,
Exports,
NewImportDescriptor,
DllPath,
&ForwarderChain
)
) {
BindThunkFailed = TRUE;
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
BindThunkFailed = TRUE;
}
if (BindThunkFailed) {
if (NewImportDescriptor != NULL) {
NewImportDescriptor->TimeDateStamp = 0;
}
if (Parms->StatusRoutine != NULL) {
Ordinal = (BOOL)IMAGE_SNAP_BY_ORDINAL(tname->u1.Ordinal);
if (Ordinal) {
UCHAR szOrdinal[8];
OrdinalNumber = (USHORT)(IMAGE_ORDINAL(tname->u1.Ordinal) - Exports->Base);
ImportName = (PIMAGE_IMPORT_BY_NAME)NameBuffer;
// Can't use sprintf w/o dragging in more CRT support than we want... Must run on Win95.
strcpy((PCHAR) ImportName->Name, "Ordinal");
strcat((PCHAR) ImportName->Name, _ultoa((ULONG) OrdinalNumber, (LPSTR)szOrdinal, 16));
}
else {
ImportName = (PIMAGE_IMPORT_BY_NAME)BindpRvaToVa(
Parms,
(ULONG)(ULONG_PTR)(tname->u1.AddressOfData),
Image
);
}
(Parms->StatusRoutine)( BindImportProcedureFailed,
Image->ModuleName,
Dll->ModuleName,
(ULONG_PTR)tsnap->u1.Function,
(ULONG_PTR)(ImportName->Name)
);
}
break;
}
tname++;
tsnap++;
}
tname = (PIMAGE_THUNK_DATA) BindpRvaToVa( Parms, Imports->FirstThunk, Image );
if ( !tname ) {
NoErrors = FALSE;
}
//
// If we were able to locate all of the entrypoints in the
// target dll, then copy the snapped thunks into the image,
// update the time and date stamp, and flush the image to
// disk
//
if ( NoErrors && Parms->fNoUpdate == FALSE ) {
if (ForwarderChainHead != -1) {
*ImageModified = TRUE;
*ForwarderChain = -1;
}
if (Imports->ForwarderChain != ForwarderChainHead) {
Imports->ForwarderChain = ForwarderChainHead;
*ImageModified = TRUE;
}
cb = NumberOfThunks*sizeof(*SnappedThunks);
if (memcmp(tname,SnappedThunks,cb)) {
MoveMemory(tname,SnappedThunks,cb);
*ImageModified = TRUE;
}
if (NewImportDescriptorHead == NULL) {
if (Imports->TimeDateStamp != FileHeader->TimeDateStamp) {
Imports->TimeDateStamp = FileHeader->TimeDateStamp;
*ImageModified = TRUE;
}
}
else
if (Imports->TimeDateStamp != 0xFFFFFFFF) {
Imports->TimeDateStamp = 0xFFFFFFFF;
*ImageModified = TRUE;
}
}
MemFree(SnappedThunks);
}
}
NewImports = BindpCreateNewImportSection(Parms, &NewImportDescriptorHead, &NewImportsSize);
if (PrevNewImportsSize != NewImportsSize ||
memcmp( PrevNewImports, NewImports, NewImportsSize )
) {
*ImageModified = TRUE;
}
if (!*ImageModified) {
return;
}
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindImageModified,
Image->ModuleName,
NULL,
0,
0
);
}
if (NewImports != NULL) {
ULONG cbFreeFile, cbFreeHeaders, OffsetHeaderFreeSpace, cbFreeSpaceOnDisk;
if (NoErrors && Parms->fNoUpdate == FALSE) {
OPTIONALHEADER_LV(DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ].VirtualAddress) = 0;
OPTIONALHEADER_LV(DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ].Size) = 0;
}
OffsetHeaderFreeSpace = GetImageUnusedHeaderBytes( Image, &cbFreeFile );
cbFreeHeaders = Image->Sections->VirtualAddress -
OPTIONALHEADER(SizeOfHeaders) +
cbFreeFile;
// FreeSpace on Disk may be larger that FreeHeaders in the headers (the linker
// can start the first section on a page boundary already)
cbFreeSpaceOnDisk = Image->Sections->PointerToRawData -
OPTIONALHEADER(SizeOfHeaders) +
cbFreeFile;
if (NewImportsSize > cbFreeFile) {
if (NewImportsSize > cbFreeHeaders) {
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindNoRoomInImage,
Image->ModuleName,
NULL,
0,
0
);
}
NoErrors = FALSE;
}
else
if (NoErrors && (Parms->fNoUpdate == FALSE)) {
if (NewImportsSize <= cbFreeSpaceOnDisk) {
// There's already space on disk. Just adjust the header size.
OPTIONALHEADER_LV(SizeOfHeaders) =
(OPTIONALHEADER(SizeOfHeaders) -
cbFreeFile + NewImportsSize + (OPTIONALHEADER(FileAlignment)-1)
) & ~(OPTIONALHEADER(FileAlignment)-1);
} else {
NoErrors = BindpExpandImageFileHeaders( Parms,
Image,
(OPTIONALHEADER(SizeOfHeaders) -
cbFreeFile +
NewImportsSize +
(OPTIONALHEADER(FileAlignment)-1)
) &
~(OPTIONALHEADER(FileAlignment)-1)
);
// Expand may have remapped the image. Recalc the header ptrs.
FileHeader = &((PIMAGE_NT_HEADERS32)Image->FileHeader)->FileHeader;
OptionalHeadersFromNtHeaders((PIMAGE_NT_HEADERS32)Image->FileHeader,
&OptionalHeader32,
&OptionalHeader64);
}
}
}
if (Parms->StatusRoutine != NULL) {
(Parms->StatusRoutine)( BindImageComplete,
Image->ModuleName,
NULL,
(ULONG_PTR)NewImports,
NoErrors
);
}
if (NoErrors && Parms->fNoUpdate == FALSE) {
OPTIONALHEADER_LV(DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ].VirtualAddress) = OffsetHeaderFreeSpace;
OPTIONALHEADER_LV(DataDirectory[ IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT ].Size) = NewImportsSize;
memcpy( (LPSTR)(Image->MappedAddress) + OffsetHeaderFreeSpace,
NewImports,
NewImportsSize
);
}
MemFree(NewImports);
}
if (NoErrors && Parms->fNoUpdate == FALSE) {
SetIdataToRo( Image );
}
}
DWORD
GetImageUnusedHeaderBytes(
PLOADED_IMAGE LoadedImage,
LPDWORD SizeUnusedHeaderBytes
)
{
DWORD OffsetFirstUnusedHeaderByte;
DWORD i;
DWORD OffsetHeader;
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
PIMAGE_NT_HEADERS32 NtHeaders;
NtHeaders = (PIMAGE_NT_HEADERS32)LoadedImage->FileHeader;
//
// this calculates an offset, not an address, so DWORD is correct
//
OffsetFirstUnusedHeaderByte = (DWORD)
(((LPSTR)NtHeaders - (LPSTR)LoadedImage->MappedAddress) +
(FIELD_OFFSET( IMAGE_NT_HEADERS32, OptionalHeader ) +
NtHeaders->FileHeader.SizeOfOptionalHeader +
(NtHeaders->FileHeader.NumberOfSections *
sizeof(IMAGE_SECTION_HEADER)
)
)
);
OptionalHeadersFromNtHeaders(NtHeaders,
&OptionalHeader32,
&OptionalHeader64);
for ( i=0; i<OPTIONALHEADER(NumberOfRvaAndSizes); i++ ) {
OffsetHeader = OPTIONALHEADER(DataDirectory[i].VirtualAddress);
if (OffsetHeader < OPTIONALHEADER(SizeOfHeaders)) {
if (OffsetHeader >= OffsetFirstUnusedHeaderByte) {
OffsetFirstUnusedHeaderByte = OffsetHeader +
OPTIONALHEADER(DataDirectory[i].Size);
}
}
}
*SizeUnusedHeaderBytes = OPTIONALHEADER(SizeOfHeaders) -
OffsetFirstUnusedHeaderByte;
return OffsetFirstUnusedHeaderByte;
}