//--------------------
// PROGRAM: PEDUMP
// FILE:    COMMON.C
// AUTHOR:  Matt Pietrek - 1993
//--------------------
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "common.h"

PIMAGE_SYMBOL PCOFFSymbolTable = 0;	// RVA to COFF symbol table (if present)
DWORD COFFSymbolCount = 0;			// Number of symbols in COFF symbol table

typedef struct
{
	WORD	flag;
	PSTR	name;
} WORD_FLAG_DESCRIPTIONS;

typedef struct
{
	DWORD	flag;
	PSTR	name;
} DWORD_FLAG_DESCRIPTIONS;

// Bitfield values and names for the IMAGE_FILE_HEADER flags
WORD_FLAG_DESCRIPTIONS ImageFileHeaderCharacteristics[] = 
{
{ IMAGE_FILE_RELOCS_STRIPPED, "RELOCS_STRIPPED" },
{ IMAGE_FILE_EXECUTABLE_IMAGE, "EXECUTABLE_IMAGE" },
{ IMAGE_FILE_LINE_NUMS_STRIPPED, "LINE_NUMS_STRIPPED" },
{ IMAGE_FILE_LOCAL_SYMS_STRIPPED, "LOCAL_SYMS_STRIPPED" },
{ IMAGE_FILE_MINIMAL_OBJECT, "MINIMAL_OBJECT" },
{ IMAGE_FILE_UPDATE_OBJECT, "UPDATE_OBJECT" },
{ IMAGE_FILE_16BIT_MACHINE, "16BIT_MACHINE" },
{ IMAGE_FILE_BYTES_REVERSED_LO, "BYTES_REVERSED_LO" },
{ IMAGE_FILE_32BIT_MACHINE, "32BIT_MACHINE" },
{ IMAGE_FILE_PATCH, "PATCH" },
{ IMAGE_FILE_SYSTEM, "SYSTEM" },
{ IMAGE_FILE_DLL, "DLL" },
{ IMAGE_FILE_BYTES_REVERSED_HI, "BYTES_REVERSED_HI" }
};

#define NUMBER_IMAGE_HEADER_FLAGS \
    (sizeof(ImageFileHeaderCharacteristics) / sizeof(WORD_FLAG_DESCRIPTIONS))

//
// Dump the IMAGE_FILE_HEADER for a PE file or an OBJ
//
void DumpHeader(PIMAGE_FILE_HEADER pImageFileHeader)
{
	UINT headerFieldWidth = 30;
	UINT i;
	char *szMachine;
	
	printf("File Header\n");

	switch( pImageFileHeader->Machine )
	{
		case IMAGE_FILE_MACHINE_I386:	szMachine = "i386"; break;
		case IMAGE_FILE_MACHINE_I860:	szMachine = "i860"; break;
		case IMAGE_FILE_MACHINE_R3000:	szMachine = "R3000"; break;
		case IMAGE_FILE_MACHINE_R4000:	szMachine = "R4000"; break;
		case IMAGE_FILE_MACHINE_ALPHA:	szMachine = "alpha"; break;
		default:    szMachine = "unknown"; break;
	}

	printf("  %-*s%04X (%s)\n", headerFieldWidth, "Machine:", 
				pImageFileHeader->Machine, szMachine);
	printf("  %-*s%04X\n", headerFieldWidth, "Number of Sections:",
				pImageFileHeader->NumberOfSections);
	printf("  %-*s%08X\n", headerFieldWidth, "TimeDateStamp:",
				pImageFileHeader->TimeDateStamp);
	printf("  %-*s%08X\n", headerFieldWidth, "PointerToSymbolTable:",
				pImageFileHeader->PointerToSymbolTable);
	printf("  %-*s%08X\n", headerFieldWidth, "NumberOfSymbols:",
				pImageFileHeader->NumberOfSymbols);
	printf("  %-*s%04X\n", headerFieldWidth, "SizeOfOptionalHeader:",
				pImageFileHeader->SizeOfOptionalHeader);
	printf("  %-*s%04X\n", headerFieldWidth, "Characteristics:",
				pImageFileHeader->Characteristics);
	for ( i=0; i < NUMBER_IMAGE_HEADER_FLAGS; i++ )
	{
		if ( pImageFileHeader->Characteristics & 
			 ImageFileHeaderCharacteristics[i].flag )
			printf( "    %s\n", ImageFileHeaderCharacteristics[i].name );
	}
}

// Bitfield values and names for the DllCharacteritics flags
WORD_FLAG_DESCRIPTIONS DllCharacteristics[] = 
{
{ IMAGE_LIBRARY_PROCESS_INIT, "PROCESS_INIT" },
{ IMAGE_LIBRARY_PROCESS_TERM, "PROCESS_TERM" },
{ IMAGE_LIBRARY_THREAD_INIT, "THREAD_INIT" },
{ IMAGE_LIBRARY_THREAD_TERM, "THREAD_TERM" },
};
#define NUMBER_DLL_CHARACTERISTICS \
	(sizeof(DllCharacteristics) / sizeof(WORD_FLAG_DESCRIPTIONS))

// Bitfield values and names for the LoaderFlags flags
DWORD_FLAG_DESCRIPTIONS LoaderFlags[] = 
{
{ IMAGE_LOADER_FLAGS_BREAK_ON_LOAD, "BREAK_ON_LOAD" },
{ IMAGE_LOADER_FLAGS_DEBUG_ON_LOAD, "DEBUG_ON_LOAD" }
};
#define NUMBER_LOADER_FLAGS \
	(sizeof(LoaderFlags) / sizeof(DWORD_FLAG_DESCRIPTIONS))
	
// Names of the data directory elements that are defined
char *ImageDirectoryNames[] = {
	"EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC",
	"DEBUG", "COPYRIGHT", "GLOBALPTR", "TLS", "LOAD_CONFIG" };

#define NUMBER_IMAGE_DIRECTORY_ENTRYS \
	(sizeof(ImageDirectoryNames)/sizeof(char *))

//
// Dump the IMAGE_OPTIONAL_HEADER from a PE file
//
void DumpOptionalHeader(PIMAGE_OPTIONAL_HEADER optionalHeader)
{
	UINT width = 30;
	char *s;
	UINT i;
	
	printf("Optional Header\n");
	
	printf("  %-*s%04X\n", width, "Magic", optionalHeader->Magic);
	printf("  %-*s%u.%02u\n", width, "linker version",
		optionalHeader->MajorLinkerVersion,
		optionalHeader->MinorLinkerVersion);
	printf("  %-*s%X\n", width, "size of code", optionalHeader->SizeOfCode);
	printf("  %-*s%X\n", width, "size of initialized data",
		optionalHeader->SizeOfInitializedData);
	printf("  %-*s%X\n", width, "size of uninitialized data",
		optionalHeader->SizeOfUninitializedData);
	printf("  %-*s%X\n", width, "entrypoint RVA",
		optionalHeader->AddressOfEntryPoint);
	printf("  %-*s%X\n", width, "base of code", optionalHeader->BaseOfCode);
	printf("  %-*s%X\n", width, "base of data", optionalHeader->BaseOfData);
	printf("  %-*s%X\n", width, "image base", optionalHeader->ImageBase);

	printf("  %-*s%X\n", width, "section align",
		optionalHeader->SectionAlignment);
	printf("  %-*s%X\n", width, "file align", optionalHeader->FileAlignment);
	printf("  %-*s%u.%02u\n", width, "required OS version",
		optionalHeader->MajorOperatingSystemVersion,
		optionalHeader->MinorOperatingSystemVersion);
	printf("  %-*s%u.%02u\n", width, "image version",
		optionalHeader->MajorImageVersion,
		optionalHeader->MinorImageVersion);
	printf("  %-*s%u.%02u\n", width, "subsystem version",
		optionalHeader->MajorSubsystemVersion,
		optionalHeader->MinorSubsystemVersion);
	printf("  %-*s%X\n", width, "Reserved1", optionalHeader->Reserved1);
	printf("  %-*s%X\n", width, "size of image", optionalHeader->SizeOfImage);
	printf("  %-*s%X\n", width, "size of headers",
			optionalHeader->SizeOfHeaders);
	printf("  %-*s%X\n", width, "checksum", optionalHeader->CheckSum);
	switch( optionalHeader->Subsystem )
	{
		case IMAGE_SUBSYSTEM_NATIVE: s = "Native"; break;
		case IMAGE_SUBSYSTEM_WINDOWS_GUI: s = "Windows GUI"; break;
		case IMAGE_SUBSYSTEM_WINDOWS_CUI: s = "Windows character"; break;
		case IMAGE_SUBSYSTEM_OS2_CUI: s = "OS/2 character"; break;
		case IMAGE_SUBSYSTEM_POSIX_CUI: s = "Posix character"; break;
		default: s = "unknown";
	}
	printf("  %-*s%04X (%s)\n", width, "Subsystem",
			optionalHeader->Subsystem, s);

	printf("  %-*s%04X\n", width, "DLL flags",
			optionalHeader->DllCharacteristics);
	for ( i=0; i < NUMBER_DLL_CHARACTERISTICS; i++ )
	{
		if ( optionalHeader->DllCharacteristics & 
			 DllCharacteristics[i].flag )
			printf( "  %s", DllCharacteristics[i].name );
	}
	if ( optionalHeader->DllCharacteristics )
		printf("\n");
	
	printf("  %-*s%X\n", width, "stack reserve size",
		optionalHeader->SizeOfStackReserve);
	printf("  %-*s%X\n", width, "stack commit size",
		optionalHeader->SizeOfStackCommit);
	printf("  %-*s%X\n", width, "heap reserve size",
		optionalHeader->SizeOfHeapReserve);
	printf("  %-*s%X\n", width, "heap commit size",
		optionalHeader->SizeOfHeapCommit);

	printf("  %-*s%08X\n", width, "loader flags",
		optionalHeader->LoaderFlags);

	for ( i=0; i < NUMBER_LOADER_FLAGS; i++ )
	{
		if ( optionalHeader->LoaderFlags & 
			 LoaderFlags[i].flag )
			printf( "  %s", LoaderFlags[i].name );
	}
	if ( optionalHeader->LoaderFlags )
		printf("\n");

	printf("  %-*s%X\n", width, "RVAs & sizes",
		optionalHeader->NumberOfRvaAndSizes);

	printf("\nData Directory\n");
	for ( i=0; i < optionalHeader->NumberOfRvaAndSizes; i++)
	{
		printf( "  %-12s rva: %08X  size: %08X\n",
			(i >= NUMBER_IMAGE_DIRECTORY_ENTRYS)
				? "unused" : ImageDirectoryNames[i], 
			optionalHeader->DataDirectory[i].VirtualAddress,
			optionalHeader->DataDirectory[i].Size);
	}
}

// Bitfield values and names for the IMAGE_SECTION_HEADER flags
DWORD_FLAG_DESCRIPTIONS SectionCharacteristics[] = 
{
{ IMAGE_SCN_CNT_CODE, "CODE" },
{ IMAGE_SCN_CNT_INITIALIZED_DATA, "INITIALIZED_DATA" },
{ IMAGE_SCN_CNT_UNINITIALIZED_DATA, "UNINITIALIZED_DATA" },
{ IMAGE_SCN_LNK_INFO, "LNK_INFO" },
{ IMAGE_SCN_LNK_OVERLAY, "LNK_OVERLAY" },
{ IMAGE_SCN_LNK_REMOVE, "LNK_REMOVE" },
{ IMAGE_SCN_LNK_COMDAT, "LNK_COMDAT" },
{ IMAGE_SCN_MEM_DISCARDABLE, "MEM_DISCARDABLE" },
{ IMAGE_SCN_MEM_NOT_CACHED, "MEM_NOT_CACHED" },
{ IMAGE_SCN_MEM_NOT_PAGED, "MEM_NOT_PAGED" },
{ IMAGE_SCN_MEM_SHARED, "MEM_SHARED" },
{ IMAGE_SCN_MEM_EXECUTE, "MEM_EXECUTE" },
{ IMAGE_SCN_MEM_READ, "MEM_READ" },
{ IMAGE_SCN_MEM_WRITE, "MEM_WRITE" },
};

#define NUMBER_SECTION_CHARACTERISTICS \
	(sizeof(SectionCharacteristics) / sizeof(DWORD_FLAG_DESCRIPTIONS))

//
// Dump the section table from a PE file or an OBJ
//
void DumpSectionTable(PIMAGE_SECTION_HEADER section,
					  unsigned cSections,
					  BOOL IsEXE)
{
	unsigned i, j;

	printf("Section Table\n");
	
	for ( i=1; i <= cSections; i++, section++ )
	{
		printf( "  %02X %-8.8s  %s: %08X  VirtAddr:  %08X\n",
				i, section->Name,
				IsEXE ? "VirtSize" : "PhysAddr",
				section->Misc.VirtualSize, section->VirtualAddress);
		printf( "    raw data offs:   %08X  raw data size: %08X\n",
				section->PointerToRawData, section->SizeOfRawData );
		printf( "    relocation offs: %08X  relocations:   %08X\n",
				section->PointerToRelocations, section->NumberOfRelocations );
		printf( "    line # offs:     %08X  line #'s:      %08X\n",
				section->PointerToLinenumbers, section->NumberOfLinenumbers );
		printf( "    characteristics: %08X\n", section->Characteristics);

		printf("    ");
		for ( j=0; j < NUMBER_SECTION_CHARACTERISTICS; j++ )
		{
			if ( section->Characteristics & 
				SectionCharacteristics[j].flag )
				printf( "  %s", SectionCharacteristics[j].name );
		}
		printf("\n\n");
	}
}

//
// Used by the DumpSymbolTable() routine.  It purpose is to filter out
// the non-normal section numbers and give them meaningful names.
//
void GetSectionName(WORD section, PSTR buffer, unsigned cbBuffer)
{
	char tempbuffer[10];
	
	switch ( (SHORT)section )
	{
		case IMAGE_SYM_UNDEFINED: strcpy(tempbuffer, "UNDEF"); break;
		case IMAGE_SYM_ABSOLUTE:  strcpy(tempbuffer, "ABS  "); break;
		case IMAGE_SYM_DEBUG:	  strcpy(tempbuffer, "DEBUG"); break;
		default: wsprintf(tempbuffer, "%-5X", section);
	}
	
	strncpy(buffer, tempbuffer, cbBuffer-1);
}

// The names of the first group of possible symbol table storage classes
char * SzStorageClass1[] = {
"NULL","AUTOMATIC","EXTERNAL","STATIC","REGISTER","EXTERNAL_DEF","LABEL",
"UNDEFINED_LABEL","MEMBER_OF_STRUCT","ARGUMENT","STRUCT_TAG",
"MEMBER_OF_UNION","UNION_TAG","TYPE_DEFINITION","UNDEFINED_STATIC",
"ENUM_TAG","MEMBER_OF_ENUM","REGISTER_PARAM","BIT_FIELD"
};

// The names of the second group of possible symbol table storage classes
char * SzStorageClass2[] = {
"BLOCK","FUNCTION","END_OF_STRUCT","FILE","SECTION","WEAK_EXTERNAL"
};

//
// Given a symbol storage class value, return a descriptive ASCII string
//
PSTR GetSZStorageClass(BYTE storageClass)
{
	if ( storageClass <= IMAGE_SYM_CLASS_BIT_FIELD )
		return SzStorageClass1[storageClass];
	else if ( (storageClass >= IMAGE_SYM_CLASS_BLOCK)
		      && (storageClass <= IMAGE_SYM_CLASS_WEAK_EXTERNAL) )
		return SzStorageClass2[storageClass-IMAGE_SYM_CLASS_BLOCK];
	else
		return "???";
}

//
// Dumps the auxillary symbol for a regular symbol.  It takes a pointer
// to the regular symbol, since the routine needs the information in
// that record.
//
void DumpAuxSymbols(PIMAGE_SYMBOL pSymbolTable)
{
	PIMAGE_AUX_SYMBOL auxSym;
	
	auxSym = (PIMAGE_AUX_SYMBOL)(pSymbolTable+1);
	
	if ( pSymbolTable->StorageClass == IMAGE_SYM_CLASS_FILE )
		printf("     * %s\n", auxSym);
	else if ( (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_EXTERNAL) )
	{
		if ( (pSymbolTable->Type & 0xF0) == (IMAGE_SYM_DTYPE_FUNCTION << 4))
		{	
		printf("     * tag: %04X  size: %04X  Line #'s: %08X  next fn: %04X\n",
			auxSym->Sym.TagIndex, auxSym->Sym.Misc.TotalSize,
			auxSym->Sym.FcnAry.Function.PointerToLinenumber,
			auxSym->Sym.FcnAry.Function.PointerToNextFunction);
		}
	}
	else if ( (pSymbolTable->StorageClass == IMAGE_SYM_CLASS_STATIC) )
	{
		printf(
			"     * Section: %04X  Len: %05X  Relocs: %04X  LineNums: %04X\n",
			auxSym->Section.Number, auxSym->Section.Length,
			auxSym->Section.NumberOfRelocations,
			auxSym->Section.NumberOfLinenumbers);
	}
}

//
// Given a COFF symbol table index, look up its name.  This function assumes
// that the COFFSymbolCount and PCOFFSymbolTable variables have been already
// set up.
//
BOOL LookupSymbolName(DWORD index, PSTR buffer, UINT length)
{
	PSTR stringTable;

	if ( index >= COFFSymbolCount )
		return FALSE;
	
	if ( PCOFFSymbolTable == 0 )
		return FALSE;
	
	if ( PCOFFSymbolTable[index].N.Name.Short != 0 )
	{
		strncpy(buffer, PCOFFSymbolTable[index].N.ShortName, min(8,length));
		buffer[8] = 0;
	}
	else
	{
		stringTable = (PSTR)&PCOFFSymbolTable[COFFSymbolCount]; 
		strncpy(buffer,
				stringTable + PCOFFSymbolTable[index].N.Name.Long, length);
		buffer[length-1] = 0;
	}
	
	return TRUE;
}

//
// Dumps a COFF symbol table from an EXE or OBJ
//
void DumpSymbolTable(PIMAGE_SYMBOL pSymbolTable, unsigned cSymbols)
{
	unsigned i;
	PSTR stringTable;
	char sectionName[10];
	
	printf("Symbol Table - %X entries  (* = auxillary symbol)\n", cSymbols);

	printf(
    "Indx Name                 Value    Section    cAux  Type    Storage\n"
    "---- -------------------- -------- ---------- ----- ------- --------\n");

	// The string table apparently starts right after the symbol table
	stringTable = (PSTR)&pSymbolTable[cSymbols]; 
		
	for ( i=0; i < cSymbols; i++ )
	{
		printf("%04X ", i);
		
		if ( pSymbolTable->N.Name.Short != 0 )
			printf("%-20.8s", pSymbolTable->N.ShortName);
		else
			printf("%-20s", stringTable + pSymbolTable->N.Name.Long);
		
		printf(" %08X", pSymbolTable->Value);
	
		GetSectionName(pSymbolTable->SectionNumber, sectionName,
						sizeof(sectionName));
		printf(" sect:%s aux:%X type:%02X st:%s\n",
				sectionName,
				pSymbolTable->NumberOfAuxSymbols,
				pSymbolTable->Type,
				GetSZStorageClass(pSymbolTable->StorageClass) );
		
		if ( pSymbolTable->NumberOfAuxSymbols )
			DumpAuxSymbols(pSymbolTable);

		// Take into account any aux symbols
		i += pSymbolTable->NumberOfAuxSymbols;
		pSymbolTable += pSymbolTable->NumberOfAuxSymbols;
		pSymbolTable++;
	}
}

//
// Given a section name, look it up in the section table and return a
// pointer to the start of its raw data area.
//
LPVOID GetSectionPtr(PSTR name, PIMAGE_NT_HEADERS pNTHeader, DWORD imageBase)
{
	PIMAGE_SECTION_HEADER section;
	unsigned i;
	
	section = (PIMAGE_SECTION_HEADER)(pNTHeader+1);
	
	for ( i=0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )
	{
		if ( strnicmp(section->Name, name, IMAGE_SIZEOF_SHORT_NAME) == 0 )
			return (LPVOID)(section->PointerToRawData + imageBase);
	}
	
	return 0;
}

//
// Given a section name, look it up in the section table and return a
// pointer to its IMAGE_SECTION_HEADER
//
PIMAGE_SECTION_HEADER GetSectionHeader(PSTR name, PIMAGE_NT_HEADERS pNTHeader)
{
	PIMAGE_SECTION_HEADER section;
	unsigned i;
	
	section = (PIMAGE_SECTION_HEADER)(pNTHeader+1);
	
	for ( i=0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++ )
	{
		if ( strnicmp(section->Name, name, IMAGE_SIZEOF_SHORT_NAME) == 0 )
			return section;
	}
	
	return 0;
}

//
// Do a hexadecimal dump of the raw data for all the sections.  You
// could just dump one section by adjusting the PIMAGE_SECTION_HEADER
// and cSections parameters
//
void DumpRawSectionData(PIMAGE_SECTION_HEADER section,
						PVOID base,
						unsigned cSections)
{
	unsigned i;
	char name[IMAGE_SIZEOF_SHORT_NAME + 1];

	printf("Section Hex Dumps\n");
	
	for ( i=0; i < cSections; i++, section++ )
	{
		// Make a copy of the section name so that we can ensure that
		// it's null-terminated
		memcpy(name, section->Name, IMAGE_SIZEOF_SHORT_NAME);
		name[IMAGE_SIZEOF_SHORT_NAME] = 0;

		// Don't dump sections that don't exist in the file!
		if ( section->PointerToRawData == 0 )
			continue;
		
		printf( "section %02X (%s)  size: %08X  file offs: %08X\n",
				i, name, section->SizeOfRawData, section->PointerToRawData);

		HexDump( MakePtr(PBYTE, base, section->PointerToRawData),
				 section->SizeOfRawData );
		printf("\n");
	}
}

//
// Dump a range of line numbers from the COFF debug information
//
void DumpLineNumbers(PIMAGE_LINENUMBER pln, DWORD count)
{
	char buffer[64];
	DWORD i;
	
	printf("Line Numbers\n");
	
	for (i=0; i < count; i++)
	{
		if ( pln->Linenumber == 0 )	// A symbol table index
		{
			buffer[0] = 0;
			LookupSymbolName(pln->Type.SymbolTableIndex, buffer,
							sizeof(buffer));
			printf("SymIndex: %X (%s)\n", pln->Type.SymbolTableIndex,
											 buffer);
		}
		else		// A regular line number
			printf(" Addr: %05X  Line: %04X\n",
				pln->Type.VirtualAddress, pln->Linenumber);
		pln++;
	}
}

// Number of hex values displayed per line
#define HEX_DUMP_WIDTH 16

//
// Dump a region of memory in a hexadecimal format
//
void HexDump(PBYTE ptr, DWORD length)
{
	char buffer[256];
	PSTR buffPtr;
	unsigned cOutput, i;
	DWORD bytesToGo=length;

	while ( bytesToGo  )
	{
		cOutput = bytesToGo >= HEX_DUMP_WIDTH ? HEX_DUMP_WIDTH : bytesToGo;

		buffPtr = buffer;
		buffPtr += wsprintf(buffPtr, "%08X:  ", length-bytesToGo );
		
		for ( i=0; i < HEX_DUMP_WIDTH; i++ )
		{
			buffPtr += wsprintf(buffPtr,
				i < cOutput ? "%02X " : "   ",
				*(ptr+i) );

			// Put an extra space between the 1st and 2nd half of the bytes
			// on each line.
			if ( i == (HEX_DUMP_WIDTH/2)-1 )
				buffPtr += wsprintf(buffPtr, " ");
		}

		for ( i=0; i < cOutput; i++ )
		{
			char c;
			
			c = '.';
			if ( isascii(*(ptr + i)) )
				c = isprint(*(ptr + i)) ? *(ptr + i) : '.';
			
			buffPtr += wsprintf(buffPtr, "%c", c);
		}
		puts(buffer);	// Can't use printf(), since there may be a '%'
						// in the string.

		bytesToGo -= cOutput;
		ptr += HEX_DUMP_WIDTH;
	}
}