windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/dbgext/ver.cxx
2020-09-26 16:20:57 +08:00

781 lines
15 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995-1997 Microsoft Corporation
Module Name:
ver.cxx
Abstract:
This module contains an NTSD debugger extension for dumping module
version resources.
Author:
Keith Moore (keithmo) 16-Sep-1997
Revision History:
--*/
#include "inetdbgp.h"
PSTR VersionLabels[] =
{
"CompanyName",
"FileDescription",
"FileVersion",
"InternalName",
"LegalCopyright",
"OriginalFilename",
"ProductName",
"ProductVersion"
};
#define NUM_LABELS ( sizeof(VersionLabels) / sizeof(VersionLabels[0]) )
typedef struct _ENUM_CONTEXT {
PSTR ModuleName;
INT NameLength;
} ENUM_CONTEXT, *PENUM_CONTEXT;
/************************************************************
* Dump File Version Info
************************************************************/
PIMAGE_RESOURCE_DIRECTORY
FindResourceDir(
IN PIMAGE_RESOURCE_DIRECTORY BaseResourceDir,
IN PIMAGE_RESOURCE_DIRECTORY TargetResourceDir,
IN USHORT ResourceId
)
/*++
Routine Description:
Finds the specified resource directory.
Arguments:
BaseResourceDir - The (remote) address of the *start* of the resource
section.
TargetResourceDir - The (remote) address of the resource directory
to search.
ResourceId - The resource ID we're looking for.
Return Value:
PIMAGE_RESOURCE_DIRECTORY - Pointer to the resource directory
corresponding to ResourceId if successful, NULL otherwise.
--*/
{
IMAGE_RESOURCE_DIRECTORY localDir;
IMAGE_RESOURCE_DIRECTORY_ENTRY localEntry;
PIMAGE_RESOURCE_DIRECTORY_ENTRY remoteEntry;
USHORT i;
//
// Read the target resource directory.
//
if( !ReadMemory(
(ULONG_PTR)TargetResourceDir,
&localDir,
sizeof(localDir),
NULL
) ) {
return NULL;
}
//
// Scan it.
//
remoteEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)( TargetResourceDir + 1 );
for( i = localDir.NumberOfNamedEntries + localDir.NumberOfIdEntries ;
i > 0 ;
i--, remoteEntry++ ) {
//
// Read the directory entry.
//
if( !ReadMemory(
(ULONG_PTR)remoteEntry,
&localEntry,
sizeof(localEntry),
NULL
) ) {
return NULL;
}
//
// If the entry is a directory and the IDs match, then return it.
//
if( localEntry.DataIsDirectory == 0 ) {
continue;
}
if( localEntry.NameIsString == 0 &&
localEntry.Id == ResourceId ) {
return (PIMAGE_RESOURCE_DIRECTORY)
( (ULONG_PTR)BaseResourceDir + localEntry.OffsetToDirectory );
}
}
return NULL;
} // FindResourceDir
PIMAGE_RESOURCE_DATA_ENTRY
FindResourceData(
IN PIMAGE_RESOURCE_DIRECTORY BaseResourceDir,
IN PIMAGE_RESOURCE_DIRECTORY TargetResourceDir,
IN USHORT ResourceId
)
/*++
Routine Description:
Finds the specified resource data item.
Arguments:
BaseResourceDir - The (remote) address of the *start* of the resource
section.
TargetResourceDir - The (remote) address of the resource directory
to search.
ResourceId - The resource ID we're looking for. This may be zero
to return any resource.
Return Value:
PIMAGE_RESOURCE_DATA_ENTRY - Pointer to the resource data entry
corresponding to ResourceId if successful, NULL otherwise.
--*/
{
IMAGE_RESOURCE_DIRECTORY localDir;
IMAGE_RESOURCE_DIRECTORY_ENTRY localEntry;
PIMAGE_RESOURCE_DIRECTORY_ENTRY remoteEntry;
USHORT i;
//
// Read the target resource directory.
//
if( !ReadMemory(
(ULONG_PTR)TargetResourceDir,
&localDir,
sizeof(localDir),
NULL
) ) {
return NULL;
}
//
// Scan it.
//
remoteEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)( TargetResourceDir + 1 );
for( i = localDir.NumberOfNamedEntries + localDir.NumberOfIdEntries ;
i > 0 ;
i--, remoteEntry++ ) {
//
// Read the directory entry.
//
if( !ReadMemory(
(ULONG_PTR)remoteEntry,
&localEntry,
sizeof(localEntry),
NULL
) ) {
return NULL;
}
//
// If the entry is not a directory and the IDs match (or the
// requested ID is zero, meaning any ID) then return it.
//
if( localEntry.DataIsDirectory != 0 ) {
continue;
}
if( localEntry.NameIsString == 0 &&
( localEntry.Id == ResourceId ||
ResourceId == 0 ) ) {
return (PIMAGE_RESOURCE_DATA_ENTRY)
( (ULONG_PTR)BaseResourceDir + localEntry.OffsetToDirectory );
}
}
return NULL;
} // FindResourceData
BOOL
DumpVersionResource(
IN PVOID VersionResource
)
/*++
Routine Description:
Dumps a version resource block.
Arguments:
VersionResource - The version resource to dump.
Return Value:
BOOL - TRUE if successful, FALSE if the version resource block
was corrupt or unreadable.
--*/
{
ULONG charSet;
LPVOID version;
UINT versionLength;
INT i;
VS_FIXEDFILEINFO * fixedFileInfo;
CHAR label[MAX_PATH];
//
// Get the language/character-set pair.
//
if( !VerQueryValueA(
VersionResource,
"\\VarFileInfo\\Translation",
&version,
&versionLength
) ) {
return FALSE;
}
charSet = *(LPDWORD)version;
charSet = (DWORD)MAKELONG( HIWORD(charSet), LOWORD(charSet) );
//
// Get the root block so we can determine if this is a free or
// checked build.
//
if( !VerQueryValue(
VersionResource,
"\\",
&version,
&versionLength
) ) {
return FALSE;
}
fixedFileInfo = (VS_FIXEDFILEINFO *)version;
dprintf(
"%-19s = 0x%08lx (%s)\n",
"dwFileFlags",
fixedFileInfo->dwFileFlags,
( ( fixedFileInfo->dwFileFlags & VS_FF_DEBUG ) != 0 )
? "CHECKED"
: "FREE"
);
//
// Dump the various version strings.
//
for( i = 0 ; i < NUM_LABELS ; i++ ) {
wsprintfA(
label,
"\\StringFileInfo\\%08lX\\%s",
charSet,
VersionLabels[i]
);
if( VerQueryValue(
VersionResource,
label,
&version,
&versionLength
) ) {
dprintf(
"%-19s = %s\n",
VersionLabels[i],
version
);
}
}
dprintf( "\n" );
return TRUE;
} // DumpVersionResource
VOID
FindAndDumpVersionResourceByAddress(
IN ULONG_PTR ModuleAddress,
IN PSTR ModuleName
)
/*++
Routine Description:
Locates and dumps the version resource for the module based at
the specified address.
Arguments:
ModuleAddress - The base address of the module to dump.
ModuleName - The module name, for display purposes.
Return Value:
None.
--*/
{
IMAGE_DOS_HEADER dosHeader;
IMAGE_NT_HEADERS ntHeaders;
PIMAGE_OPTIONAL_HEADER optionalHeader;
PIMAGE_DATA_DIRECTORY dataDir;
PIMAGE_RESOURCE_DIRECTORY baseResourceDir;
PIMAGE_RESOURCE_DIRECTORY tmpResourceDir;
PIMAGE_RESOURCE_DATA_ENTRY dataEntry;
IMAGE_RESOURCE_DATA_ENTRY localDataEntry;
PVOID versionResource;
//
// Setup locals so we know how to cleanup on exit.
//
versionResource = NULL;
//
// Read & validate the image headers.
//
if( !ReadMemory(
ModuleAddress,
&dosHeader,
sizeof(dosHeader),
NULL
) ) {
dprintf(
"inetdbg.ver: cannot read DOS header @ 0x%p\n",
ModuleAddress
);
goto cleanup;
}
if( dosHeader.e_magic != IMAGE_DOS_SIGNATURE ) {
dprintf(
"inetdbg.ver: module @ 0x%p has invalid DOS header\n",
ModuleAddress
);
goto cleanup;
}
if( !ReadMemory(
ModuleAddress + dosHeader.e_lfanew,
&ntHeaders,
sizeof(ntHeaders),
NULL
) ) {
dprintf(
"inetdbg.ver: cannot read NT headers @ 0x%p\n",
ModuleAddress
);
goto cleanup;
}
if( ntHeaders.Signature != IMAGE_NT_SIGNATURE ) {
dprintf(
"inetdbg.ver: module @ 0x%p has invalid NT headers\n",
ModuleAddress
);
goto cleanup;
}
optionalHeader = &ntHeaders.OptionalHeader;
if( optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ) {
dprintf(
"inetdbg.ver: module @ 0x%p has invalid optional header\n",
ModuleAddress
);
goto cleanup;
}
//
// Locate the resource.
//
dataDir = &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
if( dataDir->VirtualAddress == 0 ||
dataDir->Size == 0 ) {
dprintf(
"inetdbg.ver: module @ 0x%p has no resource information\n",
ModuleAddress
);
goto cleanup;
}
baseResourceDir = (PIMAGE_RESOURCE_DIRECTORY)
( ModuleAddress + dataDir->VirtualAddress );
//
// Now go and find the resource in the image. Since resources are
// stored heirarchally, we're basically for the resource path:
//
// VS_FILE_INFO\VS_VERSION_INFO\LanguageId
//
// For the language ID, we'll first try 0x409 (English) and if
// that fails, we'll take any language.
//
dataEntry = NULL;
tmpResourceDir = FindResourceDir(
baseResourceDir,
baseResourceDir,
(USHORT)VS_FILE_INFO
);
if( tmpResourceDir != NULL ) {
tmpResourceDir = FindResourceDir(
baseResourceDir,
tmpResourceDir,
(USHORT)VS_VERSION_INFO
);
if( tmpResourceDir != NULL ) {
dataEntry = FindResourceData(
baseResourceDir,
tmpResourceDir,
0x409
);
if( dataEntry == NULL ) {
dataEntry = FindResourceData(
baseResourceDir,
tmpResourceDir,
0
);
}
}
}
if( dataEntry == NULL ) {
dprintf(
"inetdbg.ver: cannot find version resource\n"
);
goto cleanup;
}
//
// Actually read the dir entry.
//
if( !ReadMemory(
(ULONG_PTR)dataEntry,
&localDataEntry,
sizeof(localDataEntry),
NULL
) ) {
dprintf(
"inetdbg.ver: error reading resource\n"
);
goto cleanup;
}
//
// Now we can allocate & read the resource.
//
versionResource = malloc( localDataEntry.Size );
if( versionResource == NULL ) {
dprintf(
"inetdbg.ver: not enough memory\n"
);
goto cleanup;
}
if( !ReadMemory(
ModuleAddress + localDataEntry.OffsetToData,
versionResource,
localDataEntry.Size,
NULL
) ) {
dprintf(
"inetdbg.ver: error reading resource\n"
);
goto cleanup;
}
//
// Dump it.
//
dprintf(
"Module @ 0x%p = %s\n",
ModuleAddress,
ModuleName
);
if( !DumpVersionResource( versionResource ) ) {
dprintf(
"Cannot interpret version resource\n"
);
goto cleanup;
}
cleanup:
if( versionResource != NULL ) {
free( versionResource );
}
} // FindAndDumpVersionResourceByAddress
BOOLEAN
CALLBACK
VerpEnumProc(
IN PVOID Param,
IN PMODULE_INFO ModuleInfo
)
{
PENUM_CONTEXT context;
INT baseNameLength;
context = (PENUM_CONTEXT)Param;
baseNameLength = strlen( ModuleInfo->BaseName );
//
// If the user wants all modules, or if the specified module matches
// the "tail" of the module name, dump it.
//
if( context->ModuleName == NULL ||
( baseNameLength >= context->NameLength &&
!_stricmp(
context->ModuleName,
ModuleInfo->BaseName + baseNameLength - context->NameLength
) ) ) {
FindAndDumpVersionResourceByAddress(
ModuleInfo->DllBase,
ModuleInfo->BaseName
);
}
return TRUE;
} // VerpEnumProc
VOID
FindAndDumpVersionResourceByName(
IN PSTR ModuleName
)
/*++
Routine Description:
Locates and dumps the version resource for the specified module.
Arguments:
ModuleName - The name of the module to dump. If this is NULL then
all modules are dumped.
Return Value:
None.
--*/
{
ENUM_CONTEXT context;
context.ModuleName = ModuleName;
if( ModuleName == NULL ) {
context.NameLength = 0;
} else {
context.NameLength = strlen( ModuleName );
}
if( !EnumModules(
VerpEnumProc,
(PVOID)&context
) ) {
dprintf( "error retrieving module list\n" );
}
} // FindAndDumpVersionResourceByName
DECLARE_API( ver )
/*++
Routine Description:
This function is called as an NTSD extension to format and dump
module version info.
Arguments:
hCurrentProcess - Supplies a handle to the current process (at the
time the extension was called).
hCurrentThread - Supplies a handle to the current thread (at the
time the extension was called).
CurrentPc - Supplies the current pc at the time the extension is
called.
lpExtensionApis - Supplies the address of the functions callable
by this extension.
lpArgumentString - Supplies the asciiz string that describes the
ansi string to be dumped.
Return Value:
None.
--*/
{
ULONG module;
PSTR endPointer;
INIT_API();
//
// Skip leading blanks.
//
while( *lpArgumentString == ' ' ||
*lpArgumentString == '\t' ) {
lpArgumentString++;
}
if( *lpArgumentString == '\0' ) {
//
// No argument passed, dump all modules.
//
FindAndDumpVersionResourceByName( NULL );
} else {
module = strtoul( lpArgumentString, &endPointer, 16 );
if( *endPointer != ' ' && *endPointer != '\t' && *endPointer != '\0' ) {
//
// Assume the argument is actually a module name, not
// a base address.
//
FindAndDumpVersionResourceByName( lpArgumentString );
} else {
FindAndDumpVersionResourceByAddress( module, NULL );
}
}
} // DECLARE_API( ver )