744 lines
17 KiB
C
744 lines
17 KiB
C
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
uwdump.c
|
|
|
|
Abstract:
|
|
|
|
This module implements a program which dumps the function table and
|
|
unwind data for a specified executable file. It is an AMD64 specific
|
|
program.
|
|
|
|
Author:
|
|
|
|
David N. Cutler (davec) 6-Feb-2001
|
|
|
|
Environment:
|
|
|
|
User mode.
|
|
|
|
Revision History:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
//
|
|
// Define AMD64 exception handling structures and function prototypes.
|
|
//
|
|
// Define unwind operation codes.
|
|
//
|
|
|
|
typedef enum _UNWIND_OP_CODES {
|
|
UWOP_PUSH_NONVOL = 0,
|
|
UWOP_ALLOC_LARGE,
|
|
UWOP_ALLOC_SMALL,
|
|
UWOP_SET_FPREG,
|
|
UWOP_SAVE_NONVOL,
|
|
UWOP_SAVE_NONVOL_FAR,
|
|
UWOP_SAVE_XMM,
|
|
UWOP_SAVE_XMM_FAR,
|
|
UWOP_SAVE_XMM128,
|
|
UWOP_SAVE_XMM128_FAR,
|
|
UWOP_PUSH_MACHFRAME
|
|
} UNWIND_OP_CODES, *PUNWIND_OP_CODES;
|
|
|
|
//
|
|
// Define unwind code structure.
|
|
//
|
|
|
|
typedef union _UNWIND_CODE {
|
|
struct {
|
|
UCHAR CodeOffset;
|
|
UCHAR UnwindOp : 4;
|
|
UCHAR OpInfo : 4;
|
|
};
|
|
|
|
USHORT FrameOffset;
|
|
} UNWIND_CODE, *PUNWIND_CODE;
|
|
|
|
//
|
|
// Define unwind information flags.
|
|
//
|
|
|
|
#define UNW_FLAG_NHANDLER 0x0
|
|
#define UNW_FLAG_EHANDLER 0x1
|
|
#define UNW_FLAG_UHANDLER 0x2
|
|
#define UNW_FLAG_CHAININFO 0x4
|
|
|
|
//
|
|
// Define unwind information structure.
|
|
//
|
|
|
|
typedef struct _UNWIND_INFO {
|
|
UCHAR Version : 3;
|
|
UCHAR Flags : 5;
|
|
UCHAR SizeOfProlog;
|
|
UCHAR CountOfCodes;
|
|
UCHAR FrameRegister : 4;
|
|
UCHAR FrameOffset : 4;
|
|
UNWIND_CODE UnwindCode[1];
|
|
|
|
//
|
|
// The unwind codes are followed by an optional DWORD aligned field that
|
|
// contains the exception handler address or the address of chained unwind
|
|
// information. If an exception handler address is specified, then it is
|
|
// followed by the language specified exception handler data.
|
|
//
|
|
// union {
|
|
// ULONG ExceptionHandler;
|
|
// ULONG FunctionEntry;
|
|
// };
|
|
//
|
|
// ULONG ExceptionData[];
|
|
//
|
|
|
|
} UNWIND_INFO, *PUNWIND_INFO;
|
|
|
|
//
|
|
// Define function table entry - a function table entry is generated for
|
|
// each frame function.
|
|
//
|
|
|
|
typedef struct _RUNTIME_FUNCTION {
|
|
ULONG BeginAddress;
|
|
ULONG EndAddress;
|
|
ULONG UnwindData;
|
|
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
|
|
|
|
//
|
|
// Scope table structure definition.
|
|
//
|
|
|
|
typedef struct _SCOPE_ENTRY {
|
|
ULONG BeginAddress;
|
|
ULONG EndAddress;
|
|
ULONG HandlerAddress;
|
|
ULONG JumpTarget;
|
|
} SCOPE_ENTRY;
|
|
|
|
typedef struct _SCOPE_TABLE {
|
|
ULONG Count;
|
|
struct
|
|
{
|
|
ULONG BeginAddress;
|
|
ULONG EndAddress;
|
|
ULONG HandlerAddress;
|
|
ULONG JumpTarget;
|
|
} ScopeRecord[1];
|
|
} SCOPE_TABLE, *PSCOPE_TABLE;
|
|
|
|
//
|
|
// Define register names.
|
|
//
|
|
|
|
PCHAR Register[] = {"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xxm6",
|
|
"xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12",
|
|
"xmm13", "xxm14", "xmm15"};
|
|
|
|
//
|
|
// Define the sector size and header buffer.
|
|
//
|
|
|
|
#define SECTOR_SIZE 512
|
|
CHAR LocalBuffer[SECTOR_SIZE * 2];
|
|
|
|
//
|
|
// Define input file stream.
|
|
//
|
|
|
|
FILE * InputFile;
|
|
|
|
//
|
|
// This gobal indicates whether we are processing an executable or an obj.
|
|
//
|
|
|
|
BOOLEAN IsObj;
|
|
|
|
//
|
|
// Define forward referenced prototypes.
|
|
//
|
|
|
|
VOID
|
|
DumpPdata (
|
|
IN ULONG NumberOfSections,
|
|
IN PIMAGE_SECTION_HEADER SectionHeaders,
|
|
IN PIMAGE_SECTION_HEADER PdataHeader
|
|
);
|
|
|
|
VOID
|
|
DumpUData (
|
|
IN ULONG NumberOfSections,
|
|
IN PIMAGE_SECTION_HEADER SectionHeaders,
|
|
IN ULONG Virtual
|
|
);
|
|
|
|
PIMAGE_SECTION_HEADER
|
|
FindSectionHeader (
|
|
IN ULONG NumberOfSections,
|
|
IN PIMAGE_SECTION_HEADER SectionHeaders,
|
|
IN PCHAR SectionName
|
|
);
|
|
|
|
VOID
|
|
ReadData (
|
|
IN ULONG Position,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Count
|
|
);
|
|
|
|
USHORT
|
|
ReadWord (
|
|
IN ULONG Position
|
|
);
|
|
|
|
ULONG
|
|
ReadDword (
|
|
IN ULONG Position
|
|
);
|
|
|
|
//
|
|
// Main program.
|
|
//
|
|
|
|
int
|
|
__cdecl
|
|
main(
|
|
int argc,
|
|
char **argv
|
|
)
|
|
|
|
{
|
|
|
|
PIMAGE_FILE_HEADER FileHeader;
|
|
PCHAR FileName;
|
|
ULONG Index;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
ULONG NumberOfSections;
|
|
PIMAGE_SECTION_HEADER PDataHeader;
|
|
PIMAGE_SECTION_HEADER SectionHeaders;
|
|
|
|
if (argc < 2) {
|
|
printf("no executable file specified\n");
|
|
|
|
} else {
|
|
|
|
//
|
|
// Open the input file.
|
|
//
|
|
|
|
FileName = argv[1];
|
|
InputFile = fopen(FileName, "rb");
|
|
if (InputFile != NULL) {
|
|
|
|
//
|
|
// Read the file header.
|
|
//
|
|
|
|
if (fread(&LocalBuffer[0],
|
|
sizeof(CHAR),
|
|
SECTOR_SIZE * 2,
|
|
InputFile) == (SECTOR_SIZE * 2)) {
|
|
|
|
//
|
|
// Get the NT header address.
|
|
//
|
|
|
|
NtHeaders = RtlImageNtHeader(&LocalBuffer[0]);
|
|
if (NtHeaders != NULL) {
|
|
IsObj = FALSE;
|
|
FileHeader = &NtHeaders->FileHeader;
|
|
} else {
|
|
IsObj = TRUE;
|
|
FileHeader = (PIMAGE_FILE_HEADER)LocalBuffer;
|
|
}
|
|
|
|
printf("FileHeader->Machine %d\n",FileHeader->Machine);
|
|
|
|
if (FileHeader->Machine == IMAGE_FILE_MACHINE_AMD64) {
|
|
|
|
//
|
|
// Look up the .pdata section.
|
|
//
|
|
|
|
NumberOfSections = FileHeader->NumberOfSections;
|
|
|
|
SectionHeaders =
|
|
(PIMAGE_SECTION_HEADER)((PUCHAR)(FileHeader + 1) +
|
|
FileHeader->SizeOfOptionalHeader);
|
|
|
|
PDataHeader = FindSectionHeader(NumberOfSections,
|
|
SectionHeaders,
|
|
".pdata");
|
|
|
|
if (PDataHeader != NULL) {
|
|
printf("Dumping Unwind Information for file %s\n\n", FileName);
|
|
DumpPdata(NumberOfSections,
|
|
&SectionHeaders[0],
|
|
PDataHeader);
|
|
|
|
return 0;
|
|
}
|
|
|
|
printf("no .pdata section in image\n");
|
|
|
|
} else {
|
|
printf("the specified file is not an amd64 executable\n");
|
|
}
|
|
|
|
} else {
|
|
printf("premature end of file encountered on input file\n");
|
|
}
|
|
|
|
fclose(InputFile);
|
|
|
|
} else {
|
|
printf("can't open input file %s\n", FileName);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID
|
|
DumpPdata (
|
|
IN ULONG NumberOfSections,
|
|
IN PIMAGE_SECTION_HEADER SectionHeaders,
|
|
IN PIMAGE_SECTION_HEADER PdataHeader
|
|
)
|
|
|
|
{
|
|
|
|
RUNTIME_FUNCTION Entry;
|
|
ULONG Number;
|
|
ULONG Offset;
|
|
ULONG SectionSize;
|
|
|
|
//
|
|
// Dump a .pdata function table entry and then dump the associated
|
|
// unwind data.
|
|
//
|
|
|
|
if (IsObj == FALSE) {
|
|
SectionSize = PdataHeader->Misc.VirtualSize;
|
|
} else {
|
|
SectionSize = PdataHeader->SizeOfRawData;
|
|
}
|
|
|
|
Number = 1;
|
|
Offset = 0;
|
|
do {
|
|
|
|
//
|
|
// Read and dump the next function table entry.
|
|
//
|
|
|
|
ReadData(PdataHeader->PointerToRawData + Offset,
|
|
&Entry,
|
|
sizeof(RUNTIME_FUNCTION));
|
|
|
|
printf(".pdata entry %d 0x%08lX 0x%08lX\n",
|
|
Number,
|
|
Entry.BeginAddress,
|
|
Entry.EndAddress);
|
|
|
|
//
|
|
// Dump the unwind data assoicated with the function table entry.
|
|
//
|
|
|
|
DumpUData(NumberOfSections, SectionHeaders, Entry.UnwindData);
|
|
|
|
//
|
|
// Increment the entry number and update the offset to the next
|
|
// function table entry.
|
|
//
|
|
|
|
Number += 1;
|
|
Offset += sizeof(RUNTIME_FUNCTION);
|
|
} while (Offset < SectionSize);
|
|
|
|
//
|
|
// Function offset and size of raw data should be equal if there is
|
|
// the correct amount of data in the .pdata section.
|
|
//
|
|
|
|
if (Offset != SectionSize) {
|
|
printf("incorrect size of raw data in .pdata, 0x%lx\n",
|
|
PdataHeader->SizeOfRawData);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
DumpUData (
|
|
IN ULONG NumberOfSections,
|
|
IN PIMAGE_SECTION_HEADER SectionHeaders,
|
|
IN ULONG Virtual
|
|
)
|
|
|
|
{
|
|
|
|
ULONG Allocation;
|
|
ULONG Count;
|
|
ULONG Displacement;
|
|
ULONG FrameOffset = 0;
|
|
ULONG FrameRegister = 0;
|
|
ULONG Handler;
|
|
ULONG Index;
|
|
ULONG Offset;
|
|
SCOPE_ENTRY ScopeEntry;
|
|
UNWIND_CODE UnwindCode;
|
|
UNWIND_INFO UnwindInfo;
|
|
PIMAGE_SECTION_HEADER XdataHeader;
|
|
|
|
//
|
|
// Locate the section that contains the unwind data.
|
|
//
|
|
|
|
printf("\n");
|
|
printf(" Unwind data: 0x%08lX\n\n", Virtual);
|
|
|
|
if (IsObj == FALSE) {
|
|
XdataHeader = SectionHeaders;
|
|
for (Index = 0; Index < NumberOfSections; Index += 1) {
|
|
if ((XdataHeader->VirtualAddress <= Virtual) &&
|
|
(Virtual < (XdataHeader->VirtualAddress + XdataHeader->Misc.VirtualSize))) {
|
|
break;
|
|
}
|
|
|
|
XdataHeader += 1;
|
|
}
|
|
|
|
if (Index == NumberOfSections) {
|
|
printf(" unwind data address outside of image\n\n");
|
|
return;
|
|
}
|
|
|
|
Offset = Virtual -
|
|
XdataHeader->VirtualAddress +
|
|
XdataHeader->PointerToRawData;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is an .obj, so there is only one Xdata header
|
|
//
|
|
|
|
XdataHeader = FindSectionHeader(NumberOfSections,
|
|
SectionHeaders,
|
|
".xdata");
|
|
|
|
Offset = Virtual + XdataHeader->PointerToRawData;
|
|
}
|
|
|
|
//
|
|
// Read unwind information.
|
|
//
|
|
|
|
ReadData(Offset,
|
|
&UnwindInfo,
|
|
sizeof(UNWIND_INFO) - sizeof(UNWIND_CODE));
|
|
|
|
//
|
|
// Dump unwind version.
|
|
//
|
|
|
|
printf(" Unwind version: %d\n", UnwindInfo.Version);
|
|
|
|
//
|
|
// Dump unwind flags.
|
|
//
|
|
|
|
printf(" Unwind Flags: ");
|
|
if ((UnwindInfo.Flags & UNW_FLAG_EHANDLER) != 0) {
|
|
printf("EHANDLER ");
|
|
}
|
|
|
|
if ((UnwindInfo.Flags & UNW_FLAG_UHANDLER) != 0) {
|
|
printf("UHANDLER ");
|
|
}
|
|
|
|
if ((UnwindInfo.Flags & UNW_FLAG_CHAININFO) != 0) {
|
|
printf("CHAININFO");
|
|
}
|
|
|
|
if (UnwindInfo.Flags == 0) {
|
|
printf("None");
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
//
|
|
// Dump size of prologue.
|
|
//
|
|
|
|
printf(" Size of prologue: 0x%02lX\n", UnwindInfo.SizeOfProlog);
|
|
|
|
//
|
|
// Dump number of unwind codes.
|
|
//
|
|
|
|
printf(" Count of codes: %d\n", UnwindInfo.CountOfCodes);
|
|
|
|
//
|
|
// Dump frame register if specified.
|
|
//
|
|
|
|
if (UnwindInfo.FrameRegister != 0) {
|
|
FrameOffset = UnwindInfo.FrameOffset * 16;
|
|
FrameRegister = UnwindInfo.FrameRegister;
|
|
printf(" Frame register: %s\n", Register[FrameRegister]);
|
|
printf(" Frame offset: 0x%lx\n", FrameOffset);
|
|
}
|
|
|
|
//
|
|
// Dump the unwind codes.
|
|
//
|
|
|
|
Offset += sizeof(UNWIND_INFO) - sizeof(UNWIND_CODE);
|
|
if (UnwindInfo.CountOfCodes != 0) {
|
|
printf(" Unwind codes:\n\n");
|
|
Count = UnwindInfo.CountOfCodes;
|
|
do {
|
|
Count -= 1;
|
|
UnwindCode.FrameOffset = ReadWord(Offset);
|
|
Offset += sizeof(USHORT);
|
|
printf(" Code offset: 0x%02lX, ", UnwindCode.CodeOffset);
|
|
switch (UnwindCode.UnwindOp) {
|
|
case UWOP_PUSH_NONVOL:
|
|
printf("PUSH_NONVOL, register=%s\n", Register[UnwindCode.OpInfo]);
|
|
break;
|
|
|
|
case UWOP_ALLOC_LARGE:
|
|
Count -= 1;
|
|
Allocation = ReadWord(Offset);
|
|
Offset += sizeof(USHORT);
|
|
if (UnwindCode.OpInfo == 0) {
|
|
Allocation *= 8;
|
|
|
|
} else {
|
|
Count -= 1;
|
|
Allocation = (Allocation << 16) + ReadWord(Offset);
|
|
Offset += sizeof(USHORT);
|
|
}
|
|
|
|
printf("ALLOC_LARGE, size=0x%lX\n", Allocation);
|
|
break;
|
|
|
|
case UWOP_ALLOC_SMALL:
|
|
Allocation = (UnwindCode.OpInfo * 8) + 8;
|
|
printf("ALLOC_SMALL, size=0x%lX\n", Allocation);
|
|
break;
|
|
|
|
case UWOP_SET_FPREG:
|
|
printf("SET_FPREG, register=%s, offset=0x%02lX\n",
|
|
Register[FrameRegister], FrameOffset);
|
|
break;
|
|
|
|
case UWOP_SAVE_NONVOL:
|
|
Count -= 1;
|
|
Displacement = ReadWord(Offset) * 8;
|
|
Offset += sizeof(USHORT);
|
|
printf("SAVE_NONVOL, register=%s offset=0x%lX\n",
|
|
Register[UnwindCode.OpInfo],
|
|
Displacement);
|
|
break;
|
|
|
|
case UWOP_SAVE_NONVOL_FAR:
|
|
Count -= 2;
|
|
Displacement = ReadWord(Offset) << 16;
|
|
Offset += sizeof(USHORT);
|
|
Displacement = Displacement + ReadWord(Offset);
|
|
Offset += sizeof(USHORT);
|
|
printf("SAVE_NONVOL_FAR, register=%s offset=0x%lX\n",
|
|
Register[UnwindCode.OpInfo],
|
|
Displacement);
|
|
break;
|
|
|
|
case UWOP_SAVE_XMM:
|
|
Count -= 1;
|
|
Displacement = ReadWord(Offset) * 8;
|
|
Offset += sizeof(USHORT);
|
|
printf("SAVE_XMM, register=%s offset=0x%lX\n",
|
|
Register[UnwindCode.OpInfo + 16],
|
|
Displacement);
|
|
break;
|
|
|
|
case UWOP_SAVE_XMM_FAR:
|
|
Count -= 2;
|
|
Displacement = ReadWord(Offset) << 16;
|
|
Offset += sizeof(USHORT);
|
|
Displacement = Displacement + ReadWord(Offset);
|
|
Offset += sizeof(USHORT);
|
|
printf("SAVE_XMM_FAR, register=%s offset=0x%lX\n",
|
|
Register[UnwindCode.OpInfo + 16],
|
|
Displacement);
|
|
break;
|
|
|
|
case UWOP_SAVE_XMM128:
|
|
Count -= 1;
|
|
Displacement = ReadWord(Offset) * 16;
|
|
Offset += sizeof(USHORT);
|
|
printf("SAVE_XMM128, register=%s offset=0x%lX\n",
|
|
Register[UnwindCode.OpInfo + 16],
|
|
Displacement);
|
|
break;
|
|
|
|
case UWOP_SAVE_XMM128_FAR:
|
|
Count -= 2;
|
|
Displacement = ReadWord(Offset) << 16;
|
|
Offset += sizeof(USHORT);
|
|
Displacement = Displacement + ReadWord(Offset);
|
|
Offset += sizeof(USHORT);
|
|
printf("SAVE_XMM128_FAR, register=%s offset=0x%lX\n",
|
|
Register[UnwindCode.OpInfo + 16],
|
|
Displacement);
|
|
break;
|
|
|
|
case UWOP_PUSH_MACHFRAME:
|
|
if (UnwindCode.OpInfo == 0) {
|
|
printf("PUSH_MACHFRAME without error code\n");
|
|
|
|
} else {
|
|
printf("PUSH_MACHFRAME with error code\n");
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
} while (Count != 0);
|
|
}
|
|
|
|
//
|
|
// Dump exception data if there is an excpetion or termination
|
|
// handler.
|
|
//
|
|
|
|
if (((UnwindInfo.Flags & UNW_FLAG_EHANDLER) != 0) ||
|
|
((UnwindInfo.Flags & UNW_FLAG_UHANDLER) != 0)) {
|
|
|
|
if ((UnwindInfo.CountOfCodes & 1) != 0) {
|
|
Offset += sizeof(USHORT);
|
|
}
|
|
|
|
Handler = ReadDword(Offset);
|
|
Offset += sizeof(ULONG);
|
|
Count = ReadDword(Offset);
|
|
Offset += sizeof(ULONG);
|
|
printf("\n");
|
|
printf(" Language specific handler: 0x%08lX\n", Handler);
|
|
printf(" Count of scope table entries: %d\n\n", Count);
|
|
if (Count != 0) {
|
|
printf(" Begin End Handler Target\n");
|
|
do {
|
|
ReadData(Offset, &ScopeEntry, sizeof(SCOPE_ENTRY));
|
|
printf(" 0x%08lX 0x%08lX 0x%08lX 0x%08lX\n",
|
|
ScopeEntry.BeginAddress,
|
|
ScopeEntry.EndAddress,
|
|
ScopeEntry.HandlerAddress,
|
|
ScopeEntry.JumpTarget);
|
|
|
|
Count -= 1;
|
|
Offset += sizeof(SCOPE_ENTRY);
|
|
} while (Count != 0);
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
return;
|
|
}
|
|
|
|
PIMAGE_SECTION_HEADER
|
|
FindSectionHeader (
|
|
IN ULONG NumberOfSections,
|
|
IN PIMAGE_SECTION_HEADER SectionHeaders,
|
|
IN PCHAR SectionName
|
|
)
|
|
{
|
|
ULONG RemainingSections;
|
|
PIMAGE_SECTION_HEADER SectionHeader;
|
|
|
|
SectionHeader = SectionHeaders;
|
|
RemainingSections = NumberOfSections;
|
|
|
|
while (RemainingSections > 0) {
|
|
|
|
if (strncmp(SectionHeader->Name,
|
|
SectionName,
|
|
IMAGE_SIZEOF_SHORT_NAME) == 0) {
|
|
|
|
return SectionHeader;
|
|
}
|
|
|
|
RemainingSections -= 1;
|
|
SectionHeader += 1;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
ReadData (
|
|
IN ULONG Position,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Count
|
|
)
|
|
|
|
{
|
|
|
|
if (fseek(InputFile,
|
|
Position,
|
|
SEEK_SET) == 0) {
|
|
|
|
if (fread((PCHAR)Buffer,
|
|
1,
|
|
Count,
|
|
InputFile) == Count) {
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf("premature end of file encounterd on inpout file\n");
|
|
exit(0);
|
|
}
|
|
|
|
USHORT
|
|
ReadWord (
|
|
IN ULONG Position
|
|
)
|
|
|
|
{
|
|
|
|
USHORT Buffer;
|
|
|
|
ReadData(Position, &Buffer, sizeof(USHORT));
|
|
return Buffer;
|
|
}
|
|
|
|
ULONG
|
|
ReadDword (
|
|
IN ULONG Position
|
|
)
|
|
|
|
{
|
|
|
|
ULONG Buffer;
|
|
|
|
ReadData(Position, &Buffer, sizeof(ULONG));
|
|
return Buffer;
|
|
}
|