4513 lines
115 KiB
C
4513 lines
115 KiB
C
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
symbolsp.c
|
|
|
|
Abstract:
|
|
|
|
This function implements a generic simple symbol handler.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 1-Sep-1994
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntldr.h>
|
|
#include "private.h"
|
|
#include "symbols.h"
|
|
#include "globals.h"
|
|
#include "tlhelp32.h"
|
|
|
|
#include "fecache.hpp"
|
|
|
|
typedef BOOL (WINAPI *PMODULE32)(HANDLE, LPMODULEENTRY32);
|
|
typedef HANDLE (WINAPI *PCREATE32SNAPSHOT)(DWORD, DWORD);
|
|
|
|
typedef ULONG (NTAPI *PRTLQUERYPROCESSDEBUGINFORMATION)(HANDLE,ULONG,PRTL_DEBUG_INFORMATION);
|
|
typedef PRTL_DEBUG_INFORMATION (NTAPI *PRTLCREATEQUERYDEBUGBUFFER)(ULONG,BOOLEAN);
|
|
typedef NTSTATUS (NTAPI *PRTLDESTROYQUERYDEBUGBUFFER)(PRTL_DEBUG_INFORMATION);
|
|
typedef NTSTATUS (NTAPI *PNTQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS,PVOID,ULONG,PULONG);
|
|
typedef ULONG (NTAPI *PRTLNTSTATUSTODOSERROR)(NTSTATUS);
|
|
//typedef NTSTATUS (NTAPI *PNTQUERYINFORMATIONPROCESS)(UINT_PTR,PROCESSINFOCLASS,UINT_PTR,ULONG,UINT_PTR);
|
|
typedef NTSTATUS (NTAPI *PNTQUERYINFORMATIONPROCESS)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);
|
|
|
|
DWORD_PTR Win95GetProcessModules(HANDLE, PINTERNAL_GET_MODULE ,PVOID);
|
|
DWORD_PTR NTGetProcessModules(HANDLE, PINTERNAL_GET_MODULE ,PVOID);
|
|
DWORD64 miGetModuleBase(HANDLE hProcess, DWORD64 Address);
|
|
|
|
// private version of qsort used to avoid compat problems on NT4 and win2k.
|
|
// code is published from base\crts
|
|
extern
|
|
void __cdecl dbg_qsort(void *, size_t, size_t,
|
|
int (__cdecl *) (const void *, const void *));
|
|
|
|
|
|
typedef struct _SYMBOL_INFO_LOOKUP {
|
|
ULONG Segment;
|
|
ULONG64 Offset;
|
|
PCHAR NamePtr;
|
|
SYMBOL_INFO SymInfo;
|
|
} SYMBOL_INFO_LOOKUP;
|
|
|
|
|
|
BOOL
|
|
LoadSymbols(
|
|
HANDLE hp,
|
|
PMODULE_ENTRY mi,
|
|
DWORD flags
|
|
)
|
|
{
|
|
if (flags & LS_JUST_TEST) {
|
|
if ((mi->Flags & MIF_DEFERRED_LOAD) && !(mi->Flags & MIF_NO_SYMBOLS))
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
|
|
if (flags & LS_QUALIFIED) {
|
|
if (g.SymOptions & SYMOPT_NO_UNQUALIFIED_LOADS) {
|
|
if ((mi->Flags & MIF_DEFERRED_LOAD) && !(mi->Flags & MIF_NO_SYMBOLS))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ((mi->Flags & MIF_DEFERRED_LOAD) && !(mi->Flags & MIF_NO_SYMBOLS))
|
|
return load(hp, mi);
|
|
else if (flags & LS_FAIL_IF_LOADED)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the address form section no and offset in a PE file
|
|
//
|
|
ULONG
|
|
GetAddressFromOffset(
|
|
PMODULE_ENTRY mi,
|
|
ULONG section,
|
|
ULONG64 Offset,
|
|
PULONG64 pAddress
|
|
)
|
|
{
|
|
ULONG Bias;
|
|
if (section > mi->NumSections
|
|
|| !pAddress
|
|
|| !section
|
|
|| !mi
|
|
)
|
|
{
|
|
// Invalid !!
|
|
return FALSE;
|
|
}
|
|
|
|
*pAddress = mi->BaseOfDll + mi->OriginalSectionHdrs[section-1].VirtualAddress + Offset;
|
|
*pAddress = ConvertOmapFromSrc( mi, *pAddress, &Bias );
|
|
if (*pAddress) {
|
|
*pAddress += Bias;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* GetSymbolInfo
|
|
* This extracts useful information from a CV SYMBOl record into a generic
|
|
* SYMBOL_ENTRY structure.
|
|
*
|
|
*
|
|
*/
|
|
ULONG
|
|
GetSymbolInfo(
|
|
PMODULE_ENTRY me,
|
|
PCHAR pRawSym,
|
|
SYMBOL_INFO_LOOKUP *pSymEntry
|
|
)
|
|
{
|
|
PCHAR SymbolInfo = pRawSym;
|
|
ULONG symIndex, typeIndex=0, segmentNum=0;
|
|
ULONG64 Offset=0, Address=0, Value=0;
|
|
// ULONG Register=0, bpRel=0, BaseReg=0;
|
|
BOOL HasAddr=FALSE, HasValue=FALSE;
|
|
PSYMBOL_INFO pSymInfo = &pSymEntry->SymInfo;
|
|
|
|
if ((pRawSym != NULL) && (pSymEntry != NULL)) {
|
|
|
|
SymbolInfo = (PCHAR) pRawSym;
|
|
typeIndex = 0;
|
|
symIndex = ((SYMTYPE *) (pRawSym))->rectyp;
|
|
ZeroMemory(pSymEntry, sizeof(SYMBOL_INFO));
|
|
|
|
pSymInfo->ModBase = me->BaseOfDll;
|
|
|
|
|
|
#define ExtractSymName(from) (pSymEntry->NamePtr = ((PCHAR) from) + 1); pSymInfo->NameLen = (UCHAR) *((PUCHAR) from);
|
|
switch (symIndex) {
|
|
case S_COMPILE : // 0x0001 Compile flags symbol
|
|
case S_REGISTER_16t : { // 0x0002 Register variable
|
|
break;
|
|
}
|
|
case S_CONSTANT_16t : { // 0x0003 constant symbol
|
|
DWORD len=4;
|
|
CONSTSYM_16t *constSym;
|
|
|
|
constSym = (CONSTSYM_16t *) SymbolInfo;
|
|
typeIndex = constSym->typind;
|
|
// GetNumericValue((PCHAR)&constSym->value, &Value, &len);
|
|
|
|
pSymInfo->Flags |= IMAGEHLP_SYMBOL_INFO_VALUEPRESENT;
|
|
pSymInfo->Value = Value;
|
|
ExtractSymName((constSym->name + len));
|
|
break;
|
|
}
|
|
case S_UDT_16t : { // 0x0004 User defined type
|
|
UDTSYM_16t *udtSym;
|
|
|
|
udtSym = (UDTSYM_16t *) SymbolInfo;
|
|
typeIndex = udtSym->typind;
|
|
ExtractSymName(udtSym->name); // strncpy(name, (PCHAR)symReturned + 7, (UCHAR) symReturned[6]);
|
|
break;
|
|
}
|
|
case S_SSEARCH : // 0x0005 Start Search
|
|
case S_END : // 0x0006 Block, procedure, "with" or thunk end
|
|
case S_SKIP : // 0x0007 Reserve symbol space in $$Symbols table
|
|
case S_CVRESERVE : // 0x0008 Reserved symbol for CV internal use
|
|
case S_OBJNAME : // 0x0009 path to object file name
|
|
case S_ENDARG : // 0x000a end of argument/return list
|
|
case S_COBOLUDT_16t : // 0x000b special UDT for cobol that does not symbol pack
|
|
case S_MANYREG_16t : // 0x000c multiple register variable
|
|
case S_RETURN : // 0x000d return description symbol
|
|
case S_ENTRYTHIS : // 0x000e description of this pointer on entry
|
|
break;
|
|
|
|
case S_BPREL16 : // 0x0100 BP-relative
|
|
case S_LDATA16 : // 0x0101 Module-local symbol
|
|
case S_GDATA16 : // 0x0102 Global data symbol
|
|
case S_PUB16 : // 0x0103 a public symbol
|
|
case S_LPROC16 : // 0x0104 Local procedure start
|
|
case S_GPROC16 : // 0x0105 Global procedure start
|
|
case S_THUNK16 : // 0x0106 Thunk Start
|
|
case S_BLOCK16 : // 0x0107 block start
|
|
case S_WITH16 : // 0x0108 with start
|
|
case S_LABEL16 : // 0x0109 code label
|
|
case S_CEXMODEL16 : // 0x010a change execution model
|
|
case S_VFTABLE16 : // 0x010b address of virtual function table
|
|
case S_REGREL16 : // 0x010c register relative address
|
|
case S_BPREL32_16t : { // 0x0200 BP-relative
|
|
break;
|
|
}
|
|
case S_LDATA32_16t :// 0x0201 Module-local symbol
|
|
case S_GDATA32_16t :// 0x0202 Global data symbol
|
|
case S_PUB32_16t : { // 0x0203 a public symbol (CV internal reserved)
|
|
DATASYM32_16t *pData;
|
|
|
|
pData = (DATASYM32_16t *) SymbolInfo;
|
|
typeIndex = pData->typind;
|
|
Offset = pData->off; segmentNum = pData->seg;
|
|
HasAddr = TRUE;
|
|
ExtractSymName(pData->name);
|
|
// strncpy(name, (PCHAR)&pData->name[1], (UCHAR) pData->name[0]);
|
|
break;
|
|
}
|
|
case S_LPROC32_16t : // 0x0204 Local procedure start
|
|
case S_GPROC32_16t : { // 0x0205 Global procedure start
|
|
PROCSYM32_16t *procSym;
|
|
|
|
procSym = (PROCSYM32_16t *)SymbolInfo;
|
|
// CONTEXT-SENSITIVE
|
|
// Offset = procSym->off; segmentNum = procSym->seg;
|
|
typeIndex = procSym->typind;
|
|
ExtractSymName(procSym->name);
|
|
// strncpy(name, (PCHAR)symReturned + 36, (UCHAR) symReturned[35]);
|
|
break;
|
|
}
|
|
case S_THUNK32 : // 0x0206 Thunk Start
|
|
case S_BLOCK32 : // 0x0207 block start
|
|
case S_WITH32 : // 0x0208 with start
|
|
case S_LABEL32 : // 0x0209 code label
|
|
case S_CEXMODEL32 : // 0x020a change execution model
|
|
case S_VFTABLE32_16t : // 0x020b address of virtual function table
|
|
case S_REGREL32_16t : // 0x020c register relative address
|
|
case S_LTHREAD32_16t : // 0x020d local thread storage
|
|
case S_GTHREAD32_16t : // 0x020e global thread storage
|
|
case S_SLINK32 : // 0x020f static link for MIPS EH implementation
|
|
case S_LPROCMIPS_16t : // 0x0300 Local procedure start
|
|
case S_GPROCMIPS_16t : { // 0x0301 Global procedure start
|
|
break;
|
|
}
|
|
|
|
case S_PROCREF : { // 0x0400 Reference to a procedure
|
|
// typeIndex = ((PDWORD) symReturned) + 3;
|
|
// strncpy(name, symReturned + 13, (char) *(symReturned+12));
|
|
break;
|
|
}
|
|
case S_DATAREF : // 0x0401 Reference to data
|
|
case S_ALIGN : // 0x0402 Used for page alignment of symbols
|
|
case S_LPROCREF : // 0x0403 Local Reference to a procedure
|
|
|
|
// sym records with 32-bit types embedded instead of 16-bit
|
|
// all have 0x1000 bit set for easy identification
|
|
// only do the 32-bit target versions since we don't really
|
|
// care about 16-bit ones anymore.
|
|
case S_TI16_MAX : // 0x1000,
|
|
break;
|
|
|
|
case S_REGISTER : { // 0x1001 Register variable
|
|
REGSYM *regSym;
|
|
|
|
regSym = (REGSYM *)SymbolInfo;
|
|
typeIndex = regSym->typind;
|
|
pSymInfo->Flags = IMAGEHLP_SYMBOL_INFO_REGISTER;
|
|
LookupRegID((DWORD)regSym->reg, me->MachineType, &pSymInfo->Register);
|
|
ExtractSymName(regSym->name);
|
|
break;
|
|
}
|
|
|
|
case S_CONSTANT : { // 0x1002 constant symbol
|
|
CONSTSYM *constSym;
|
|
DWORD len=4, val;
|
|
|
|
constSym = (CONSTSYM *) SymbolInfo;
|
|
// GetNumericValue((PCHAR)&constSym->value, &Value, &len);
|
|
|
|
pSymInfo->Flags |= IMAGEHLP_SYMBOL_INFO_VALUEPRESENT;
|
|
pSymInfo->Value = Value;
|
|
typeIndex = constSym->typind;
|
|
ExtractSymName((constSym->name+len));
|
|
break;
|
|
}
|
|
case S_UDT : { // 0x1003 User defined type
|
|
UDTSYM *udtSym;
|
|
|
|
udtSym = (UDTSYM *) SymbolInfo;
|
|
typeIndex = udtSym->typind;
|
|
ExtractSymName(udtSym->name);
|
|
break;
|
|
}
|
|
|
|
case S_COBOLUDT : // 0x1004 special UDT for cobol that does not symbol pack
|
|
break;
|
|
|
|
case S_MANYREG : // 0x1005 multiple register variable
|
|
#if 0
|
|
typedef struct MANYREGSYM {
|
|
unsigned short reclen; // Record length
|
|
unsigned short rectyp; // S_MANYREG
|
|
CV_typ_t typind; // Type index
|
|
unsigned char count; // count of number of registers
|
|
unsigned char reg[1]; // count register enumerates followed by
|
|
// length-prefixed name. Registers are
|
|
// most significant first.
|
|
} MANYREGSYM;
|
|
|
|
typedef struct MANYREGSYM2 {
|
|
unsigned short reclen; // Record length
|
|
unsigned short rectyp; // S_MANYREG2
|
|
CV_typ_t typind; // Type index
|
|
unsigned short count; // count of number of registers
|
|
unsigned short reg[1]; // count register enumerates followed by
|
|
// length-prefixed name. Registers are
|
|
// most significant first.
|
|
} MANYREGSYM2;
|
|
#endif
|
|
break;
|
|
|
|
case S_BPREL32 : { // 0x1006 BP-relative
|
|
BPRELSYM32 *bprelSym;
|
|
|
|
bprelSym = (BPRELSYM32 *)SymbolInfo;
|
|
typeIndex = bprelSym->typind;
|
|
pSymInfo->Flags = IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE;
|
|
pSymInfo->Address = bprelSym->off;
|
|
ExtractSymName(bprelSym->name);
|
|
break;
|
|
}
|
|
|
|
case S_LDATA32 : // 0x1007 Module-local symbol
|
|
case S_GDATA32 : // 0x1008 Global data symbol
|
|
case S_PUB32 : { // 0x1009 a public symbol (CV internal reserved)
|
|
DATASYM32 *dataSym;
|
|
|
|
dataSym = (DATASYM32 *)SymbolInfo;
|
|
HasAddr = TRUE;
|
|
Offset = dataSym->off; segmentNum = dataSym->seg;
|
|
typeIndex = dataSym->typind; //(PDWORD) symReturned;
|
|
ExtractSymName(dataSym->name); // strncpy(name, (PCHAR)symReturned+11, (UCHAR) symReturned[10]);
|
|
|
|
break;
|
|
}
|
|
case S_LPROC32 : // 0x100a Local procedure start
|
|
case S_GPROC32 : { // 0x100b Global procedure start
|
|
PROCSYM32 *procSym;
|
|
|
|
procSym = (PROCSYM32 *) SymbolInfo;
|
|
// CONTEXT-SENSITIVE
|
|
HasAddr = TRUE;
|
|
Offset = procSym->off; segmentNum = procSym->seg;
|
|
typeIndex = procSym->typind;
|
|
ExtractSymName(procSym->name);
|
|
break;
|
|
}
|
|
|
|
case S_VFTABLE32 : // 0x100c address of virtual function table
|
|
break;
|
|
|
|
case S_REGREL32 : { // 0x100d register relative address
|
|
REGREL32 *regrelSym;
|
|
|
|
regrelSym = (REGREL32 *)SymbolInfo;
|
|
typeIndex = regrelSym->typind;
|
|
pSymInfo->Flags = IMAGEHLP_SYMBOL_INFO_REGRELATIVE;
|
|
pSymInfo->Address = regrelSym->off;
|
|
LookupRegID((DWORD)regrelSym->reg, me->MachineType, &pSymInfo->Register);
|
|
ExtractSymName(regrelSym->name);
|
|
break;
|
|
}
|
|
|
|
case S_LTHREAD32 : // 0x100e local thread storage
|
|
case S_GTHREAD32 : // 0x100f global thread storage
|
|
case S_LPROCMIPS : // 0x1010 Local procedure start
|
|
case S_GPROCMIPS : // 0x1011 Global procedure start
|
|
case S_FRAMEPROC : // 0x1012 extra frame and proc information
|
|
case S_COMPILE2 : // 0x1013 extended compile flags and info
|
|
case S_MANYREG2 : // 0x1014 multiple register variable
|
|
case S_LPROCIA64 : // 0x1015 Local procedure start (IA64)
|
|
case S_GPROCIA64 : // 0x1016 Global procedure start (IA64)
|
|
case S_RECTYPE_MAX :
|
|
default:
|
|
return FALSE;
|
|
} /* switch */
|
|
|
|
|
|
if (HasAddr && GetAddressFromOffset(me, segmentNum, Offset, &Address)) {
|
|
pSymInfo->Address = Address;
|
|
}
|
|
|
|
pSymInfo->TypeIndex = typeIndex;
|
|
pSymEntry->Offset = Offset;
|
|
pSymEntry->Segment = segmentNum;
|
|
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* cvExtractSymbolInfo
|
|
* This extracts useful information from a CV SYMBOl record into a generic
|
|
* SYMBOL_ENTRY structure.
|
|
*
|
|
*
|
|
*/
|
|
ULONG
|
|
cvExtractSymbolInfo(
|
|
PMODULE_ENTRY me,
|
|
PCHAR pRawSym,
|
|
PSYMBOL_ENTRY pSymEntry,
|
|
BOOL fCopyName
|
|
)
|
|
{
|
|
SYMBOL_INFO_LOOKUP SymInfoLookup={0};
|
|
ULONG reg;
|
|
|
|
pSymEntry->Size = 0;
|
|
pSymEntry->Flags = 0;
|
|
pSymEntry->Address = 0;
|
|
if (fCopyName)
|
|
*pSymEntry->Name = 0;
|
|
else
|
|
pSymEntry->Name = 0;
|
|
pSymEntry->NameLength = 0;
|
|
pSymEntry->Segment = 0;
|
|
pSymEntry->Offset = 0;
|
|
pSymEntry->TypeIndex = 0;
|
|
pSymEntry->ModBase = 0;
|
|
|
|
if (GetSymbolInfo(me, pRawSym, &SymInfoLookup)) {
|
|
LARGE_INTEGER li;
|
|
pSymEntry->NameLength = SymInfoLookup.SymInfo.NameLen;
|
|
pSymEntry->TypeIndex = SymInfoLookup.SymInfo.TypeIndex;
|
|
pSymEntry->Offset = SymInfoLookup.Offset;
|
|
pSymEntry->Segment = SymInfoLookup.Segment;
|
|
pSymEntry->ModBase = me->BaseOfDll;
|
|
// NOTE: this was implented as a mask - but used differently
|
|
switch (SymInfoLookup.SymInfo.Flags)
|
|
{
|
|
case IMAGEHLP_SYMBOL_INFO_REGISTER:
|
|
pSymEntry->Flags = SYMF_REGISTER;
|
|
pSymEntry->Address = SymInfoLookup.SymInfo.Register;
|
|
break;
|
|
|
|
case IMAGEHLP_SYMBOL_INFO_REGRELATIVE:
|
|
// DBGHELP_HACK - HiPart of Addr = RegId , LowPart = Pffset
|
|
pSymEntry->Flags = SYMF_REGREL;
|
|
//LookupRegID((DWORD)SymInfoLookup.SymInfo.Register, me->MachineType, &pSymEntry->Segment);
|
|
li.LowPart = (ULONG) SymInfoLookup.SymInfo.Address;
|
|
li.HighPart = SymInfoLookup.SymInfo.Register;
|
|
pSymEntry->Segment = SymInfoLookup.SymInfo.Register;
|
|
pSymEntry->Address = li.QuadPart;
|
|
break;
|
|
|
|
case IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE:
|
|
pSymEntry->Flags = SYMF_FRAMEREL;
|
|
pSymEntry->Address = SymInfoLookup.SymInfo.Address;
|
|
break;
|
|
|
|
case IMAGEHLP_SYMBOL_INFO_VALUEPRESENT:
|
|
default:
|
|
pSymEntry->Address = SymInfoLookup.SymInfo.Address;
|
|
break;
|
|
}
|
|
if (fCopyName) {
|
|
if (!pSymEntry->Name)
|
|
return FALSE;
|
|
*pSymEntry->Name = 0;
|
|
strncpy(pSymEntry->Name, SymInfoLookup.NamePtr ? SymInfoLookup.NamePtr : "", SymInfoLookup.SymInfo.NameLen);
|
|
} else {
|
|
pSymEntry->Name = SymInfoLookup.NamePtr;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD_PTR
|
|
NTGetPID(
|
|
HANDLE hProcess
|
|
)
|
|
{
|
|
HMODULE hModule;
|
|
PNTQUERYINFORMATIONPROCESS NtQueryInformationProcess;
|
|
PROCESS_BASIC_INFORMATION pi;
|
|
NTSTATUS status;
|
|
|
|
hModule = GetModuleHandle( "ntdll.dll" );
|
|
if (!hModule) {
|
|
return ERROR_MOD_NOT_FOUND;
|
|
}
|
|
|
|
NtQueryInformationProcess = (PNTQUERYINFORMATIONPROCESS)GetProcAddress(
|
|
hModule,
|
|
"NtQueryInformationProcess"
|
|
);
|
|
|
|
if (!NtQueryInformationProcess) {
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
|
|
status = NtQueryInformationProcess(hProcess,
|
|
ProcessBasicInformation,
|
|
&pi,
|
|
sizeof(pi),
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return 0;
|
|
|
|
return pi.UniqueProcessId;
|
|
}
|
|
|
|
|
|
//
|
|
// the block bounded by the #ifdef _X86_ statement
|
|
// contains the code for getting the PID from an
|
|
// HPROCESS when running under Win9X
|
|
//
|
|
|
|
#ifdef _X86_
|
|
|
|
#define HANDLE_INVALID ((HANDLE)0xFFFFFFFF)
|
|
#define HANDLE_CURRENT_PROCESS ((HANDLE)0x7FFFFFFF)
|
|
#define HANDLE_CURRENT_THREAD ((HANDLE)0xFFFFFFFE)
|
|
#define MAX_HANDLE_VALUE ((HANDLE)0x00FFFFFF)
|
|
|
|
|
|
// Thread Information Block.
|
|
|
|
typedef struct _TIB {
|
|
|
|
DWORD unknown[12];
|
|
DWORD_PTR ppdb;
|
|
|
|
} TIB, *PTIB;
|
|
|
|
// Task Data Block
|
|
|
|
typedef struct _TDB {
|
|
|
|
DWORD unknown[2];
|
|
TIB tib;
|
|
|
|
} TDB, *PTDB;
|
|
|
|
typedef struct _OBJ {
|
|
|
|
BYTE typObj; // object type
|
|
BYTE objFlags; // object flags
|
|
WORD cntUses; // count of this objects usage
|
|
|
|
} OBJ, *POBJ;
|
|
|
|
typedef struct _HTE {
|
|
|
|
DWORD flFlags;
|
|
POBJ pobj;
|
|
|
|
} HTE, *PHTE;
|
|
|
|
typedef struct _HTB {
|
|
|
|
DWORD chteMax;
|
|
HTE rghte[1];
|
|
|
|
} HTB, *PHTB;
|
|
|
|
typedef struct _W9XPDB {
|
|
|
|
DWORD unknown[17];
|
|
PHTB phtbHandles;
|
|
|
|
} W9XPDB, *PW9XPDB;
|
|
|
|
#pragma warning(disable:4035)
|
|
|
|
_inline struct _TIB * GetCurrentTib(void) { _asm mov eax, fs:[0x18] }
|
|
|
|
// stuff needed to convert local handle
|
|
|
|
#define IHTETOHANDLESHIFT 2
|
|
#define GLOBALHANDLEMASK (0x453a4d3cLU)
|
|
|
|
#define IHTEFROMHANDLE(hnd) ((hnd) == HANDLE_INVALID ? (DWORD)(hnd) : (((DWORD)(hnd)) >> IHTETOHANDLESHIFT))
|
|
|
|
#define IHTEISGLOBAL(ihte) \
|
|
(((ihte) >> (32 - 8 - IHTETOHANDLESHIFT)) == (((DWORD)GLOBALHANDLEMASK) >> 24))
|
|
|
|
#define IS_WIN32_PREDEFINED_HANDLE(hnd) \
|
|
((hnd == HANDLE_CURRENT_PROCESS)||(hnd == HANDLE_CURRENT_THREAD)||(hnd == HANDLE_INVALID))
|
|
|
|
DWORD
|
|
GetWin9xObsfucator(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
GetWin9xObsfucator()
|
|
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
|
|
Return Value:
|
|
|
|
Obsfucator key used by Windows9x to hide Process and Thread Id's
|
|
|
|
|
|
Notes:
|
|
|
|
The code has only been tested on Windows98SE and Millennium.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD ppdb = 0; // W9XPDB = Process Data Block
|
|
DWORD processId = (DWORD) GetCurrentProcessId();
|
|
|
|
// get PDB pointer
|
|
|
|
ppdb = GetCurrentTib()->ppdb;
|
|
|
|
return ppdb ^ processId;
|
|
}
|
|
|
|
|
|
DWORD_PTR
|
|
GetPtrFromHandle(
|
|
IN HANDLE Handle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
GetPtrFromHandle()
|
|
|
|
|
|
Arguments:
|
|
|
|
Handle - handle from Process handle table
|
|
|
|
|
|
Return Value:
|
|
|
|
Real Pointer to object
|
|
|
|
|
|
Notes:
|
|
|
|
The code has only been tested on Windows98SE and Millennium.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD_PTR ptr = 0;
|
|
DWORD ihte = 0;
|
|
PW9XPDB ppdb = 0;
|
|
|
|
ppdb = (PW9XPDB) GetCurrentTib()->ppdb;
|
|
|
|
// check for pre-defined handle values.
|
|
|
|
if (Handle == HANDLE_CURRENT_PROCESS) {
|
|
ptr = (DWORD_PTR) ppdb;
|
|
} else if (Handle == HANDLE_CURRENT_THREAD) {
|
|
ptr = (DWORD_PTR) CONTAINING_RECORD(GetCurrentTib(), TDB, tib);
|
|
} else if (Handle == HANDLE_INVALID) {
|
|
ptr = 0;
|
|
} else {
|
|
// not a special handle, we can perform our magic.
|
|
|
|
ihte = IHTEFROMHANDLE(Handle);
|
|
|
|
// if we have a global handle, it is only meaningful in the context
|
|
// of the kernel process's handle table...we don't currently deal with
|
|
// this type of handle
|
|
|
|
if (!(IHTEISGLOBAL(ihte))) {
|
|
ptr = (DWORD_PTR) ppdb->phtbHandles->rghte[ihte].pobj;
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
DWORD_PTR
|
|
Win9xGetPID(
|
|
IN HANDLE hProcess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Win9xGetPid()
|
|
|
|
|
|
Arguments:
|
|
|
|
hProcess - Process handle
|
|
|
|
|
|
Return Value:
|
|
|
|
Process Id
|
|
|
|
|
|
Notes:
|
|
|
|
The code has only been tested on Windows98SE and Millennium.
|
|
|
|
|
|
--*/
|
|
{
|
|
static DWORD dwObsfucator = 0;
|
|
|
|
// check to see that we have a predefined handle or an index into
|
|
// our local handle table.
|
|
|
|
if (IS_WIN32_PREDEFINED_HANDLE(hProcess) || (hProcess < MAX_HANDLE_VALUE)) {
|
|
if (!dwObsfucator) {
|
|
dwObsfucator = GetWin9xObsfucator();
|
|
assert(dwObsfucator != 0);
|
|
}
|
|
return dwObsfucator ^ GetPtrFromHandle(hProcess);
|
|
}
|
|
|
|
// don't know what we have here
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif // _X86_
|
|
|
|
|
|
DWORD_PTR
|
|
GetPID(
|
|
HANDLE hProcess
|
|
)
|
|
{
|
|
OSVERSIONINFO VerInfo;
|
|
|
|
if (hProcess == GetCurrentProcess())
|
|
return GetCurrentProcessId();
|
|
|
|
VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&VerInfo);
|
|
if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
return NTGetPID(hProcess);
|
|
} else {
|
|
#ifdef _X86_
|
|
return Win9xGetPID(hProcess);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
PMODULE_ENTRY
|
|
GetModFromAddr(
|
|
PPROCESS_ENTRY pe,
|
|
IN DWORD64 addr
|
|
)
|
|
{
|
|
PMODULE_ENTRY mi = NULL;
|
|
|
|
__try {
|
|
mi = GetModuleForPC(pe, addr, FALSE);
|
|
if (!mi) {
|
|
SetLastError(ERROR_MOD_NOT_FOUND);
|
|
return NULL;
|
|
}
|
|
|
|
if (!LoadSymbols(pe->hProcess, mi, 0)) {
|
|
SetLastError(ERROR_MOD_NOT_FOUND);
|
|
return NULL;
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
ImagepSetLastErrorFromStatus(GetExceptionCode());
|
|
return NULL;
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetProcessModules(
|
|
HANDLE hProcess,
|
|
PINTERNAL_GET_MODULE InternalGetModule,
|
|
PVOID Context
|
|
)
|
|
{
|
|
#ifdef _X86_
|
|
OSVERSIONINFO VerInfo;
|
|
|
|
VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
GetVersionEx(&VerInfo);
|
|
if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
return NTGetProcessModules(hProcess, InternalGetModule, Context);
|
|
} else {
|
|
return Win95GetProcessModules(hProcess, InternalGetModule, Context);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
Win95GetProcessModules(
|
|
HANDLE hProcess,
|
|
PINTERNAL_GET_MODULE InternalGetModule,
|
|
PVOID Context
|
|
)
|
|
{
|
|
MODULEENTRY32 mi;
|
|
PMODULE32 pModule32Next, pModule32First;
|
|
PCREATE32SNAPSHOT pCreateToolhelp32Snapshot;
|
|
HANDLE hSnapshot;
|
|
HMODULE hToolHelp;
|
|
DWORD pid;
|
|
|
|
// get the PID:
|
|
// this hack supports old bug workaround, in which callers were passing
|
|
// a pid, because an hprocess didn't work on W9X.
|
|
|
|
pid = GetPID(hProcess);
|
|
if (!pid)
|
|
pid = (DWORD)hProcess;
|
|
|
|
// get the module list from toolhelp apis
|
|
|
|
hToolHelp = GetModuleHandle("kernel32.dll");
|
|
if (!hToolHelp)
|
|
return ERROR_MOD_NOT_FOUND;
|
|
|
|
pModule32Next = (PMODULE32)GetProcAddress(hToolHelp, "Module32Next");
|
|
pModule32First = (PMODULE32)GetProcAddress(hToolHelp, "Module32First");
|
|
pCreateToolhelp32Snapshot = (PCREATE32SNAPSHOT)GetProcAddress(hToolHelp, "CreateToolhelp32Snapshot");
|
|
if (!pModule32Next || !pModule32First || !pCreateToolhelp32Snapshot)
|
|
return ERROR_MOD_NOT_FOUND;
|
|
|
|
hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
|
|
if (hSnapshot == (HANDLE)-1) {
|
|
return ERROR_MOD_NOT_FOUND;
|
|
}
|
|
|
|
mi.dwSize = sizeof(MODULEENTRY32);
|
|
|
|
if (pModule32First(hSnapshot, &mi)) {
|
|
do
|
|
{
|
|
if (!InternalGetModule(
|
|
hProcess,
|
|
mi.szModule,
|
|
(DWORD) mi.modBaseAddr,
|
|
mi.modBaseSize,
|
|
Context))
|
|
{
|
|
break;
|
|
}
|
|
|
|
} while ( pModule32Next(hSnapshot, &mi) );
|
|
}
|
|
|
|
CloseHandle(hSnapshot);
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
DWORD
|
|
NTGetProcessModules(
|
|
HANDLE hProcess,
|
|
PINTERNAL_GET_MODULE InternalGetModule,
|
|
PVOID Context
|
|
)
|
|
{
|
|
|
|
#endif // _X86_
|
|
|
|
PRTLQUERYPROCESSDEBUGINFORMATION RtlQueryProcessDebugInformation;
|
|
PRTLCREATEQUERYDEBUGBUFFER RtlCreateQueryDebugBuffer;
|
|
PRTLDESTROYQUERYDEBUGBUFFER RtlDestroyQueryDebugBuffer;
|
|
HMODULE hModule;
|
|
NTSTATUS Status;
|
|
PRTL_DEBUG_INFORMATION Buffer;
|
|
ULONG i;
|
|
DWORD_PTR ProcessId;
|
|
|
|
hModule = GetModuleHandle( "ntdll.dll" );
|
|
if (!hModule) {
|
|
return ERROR_MOD_NOT_FOUND;
|
|
}
|
|
|
|
RtlQueryProcessDebugInformation = (PRTLQUERYPROCESSDEBUGINFORMATION)GetProcAddress(
|
|
hModule,
|
|
"RtlQueryProcessDebugInformation"
|
|
);
|
|
|
|
if (!RtlQueryProcessDebugInformation) {
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
RtlCreateQueryDebugBuffer = (PRTLCREATEQUERYDEBUGBUFFER)GetProcAddress(
|
|
hModule,
|
|
"RtlCreateQueryDebugBuffer"
|
|
);
|
|
|
|
if (!RtlCreateQueryDebugBuffer) {
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
RtlDestroyQueryDebugBuffer = (PRTLDESTROYQUERYDEBUGBUFFER)GetProcAddress(
|
|
hModule,
|
|
"RtlDestroyQueryDebugBuffer"
|
|
);
|
|
|
|
if (!RtlDestroyQueryDebugBuffer) {
|
|
return ERROR_INVALID_FUNCTION;
|
|
}
|
|
|
|
Buffer = RtlCreateQueryDebugBuffer( 0, FALSE );
|
|
if (!Buffer) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
ProcessId = GetPID(hProcess);
|
|
|
|
// for backwards compatibility with an old bug
|
|
if (!ProcessId)
|
|
ProcessId = (DWORD_PTR)hProcess;
|
|
|
|
ULONG QueryFlags = RTL_QUERY_PROCESS_MODULES |
|
|
RTL_QUERY_PROCESS_NONINVASIVE;
|
|
|
|
if (g.SymOptions & SYMOPT_INCLUDE_32BIT_MODULES) {
|
|
QueryFlags |= RTL_QUERY_PROCESS_MODULES32;
|
|
}
|
|
|
|
Status = RtlQueryProcessDebugInformation(
|
|
(HANDLE)ProcessId,
|
|
QueryFlags,
|
|
Buffer
|
|
);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RtlDestroyQueryDebugBuffer( Buffer );
|
|
return(ImagepSetLastErrorFromStatus(Status));
|
|
}
|
|
|
|
for (i=0; i<Buffer->Modules->NumberOfModules; i++) {
|
|
PRTL_PROCESS_MODULE_INFORMATION Module = &Buffer->Modules->Modules[i];
|
|
if (!InternalGetModule(
|
|
hProcess,
|
|
(LPSTR) &Module->FullPathName[Module->OffsetToFileName],
|
|
(DWORD64)Module->ImageBase,
|
|
(DWORD)Module->ImageSize,
|
|
Context
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
RtlDestroyQueryDebugBuffer( Buffer );
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeModuleEntry(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
FunctionEntryCache* Cache;
|
|
|
|
if (pe && (Cache = GetFeCache(mi->MachineType, FALSE))) {
|
|
Cache->InvalidateProcessOrModule(pe->hProcess, mi->BaseOfDll);
|
|
}
|
|
if (pe && pe->ipmi == mi) {
|
|
pe->ipmi = NULL;
|
|
}
|
|
if (mi->symbolTable) {
|
|
MemFree( mi->symbolTable );
|
|
}
|
|
if (mi->SectionHdrs) {
|
|
MemFree( mi->SectionHdrs );
|
|
}
|
|
if (mi->OriginalSectionHdrs) {
|
|
MemFree( mi->OriginalSectionHdrs );
|
|
}
|
|
if (mi->pFpoData) {
|
|
VirtualFree( mi->pFpoData, 0, MEM_RELEASE );
|
|
}
|
|
if (mi->pFpoDataOmap) {
|
|
VirtualFree( mi->pFpoDataOmap, 0, MEM_RELEASE );
|
|
}
|
|
if (mi->pExceptionData) {
|
|
VirtualFree( mi->pExceptionData, 0, MEM_RELEASE );
|
|
}
|
|
if (mi->pPData) {
|
|
MemFree( mi->pPData );
|
|
}
|
|
if (mi->pXData) {
|
|
MemFree( mi->pXData );
|
|
}
|
|
if (mi->TmpSym.Name) {
|
|
MemFree( mi->TmpSym.Name );
|
|
}
|
|
if (mi->ImageName) {
|
|
MemFree( mi->ImageName );
|
|
}
|
|
if (mi->LoadedImageName) {
|
|
MemFree( mi->LoadedImageName );
|
|
}
|
|
if (mi->LoadedPdbName) {
|
|
MemFree( mi->LoadedPdbName );
|
|
}
|
|
if (mi->pOmapTo) {
|
|
MemFree( mi->pOmapTo );
|
|
}
|
|
if (mi->pOmapFrom) {
|
|
MemFree( mi->pOmapFrom );
|
|
}
|
|
if (mi->CallerData) {
|
|
MemFree( mi->CallerData );
|
|
}
|
|
if (mi->SourceFiles) {
|
|
PSOURCE_ENTRY Src, SrcNext;
|
|
|
|
for (Src = mi->SourceFiles; Src != NULL; Src = SrcNext) {
|
|
SrcNext = Src->Next;
|
|
MemFree(Src);
|
|
}
|
|
}
|
|
if (mi->dia) {
|
|
diaRelease(mi->dia);
|
|
}
|
|
|
|
MemFree( mi );
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
MatchSymbolName(
|
|
PSYMBOL_ENTRY sym,
|
|
LPSTR SymName
|
|
)
|
|
{
|
|
if (g.SymOptions & SYMOPT_CASE_INSENSITIVE) {
|
|
if (_stricmp( sym->Name, SymName ) == 0) {
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if (strcmp( sym->Name, SymName ) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PSYMBOL_ENTRY
|
|
HandleDuplicateSymbols(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
PSYMBOL_ENTRY sym
|
|
)
|
|
{
|
|
DWORD i;
|
|
DWORD Dups;
|
|
DWORD NameSize;
|
|
PIMAGEHLP_SYMBOL64 Syms64 = NULL;
|
|
PIMAGEHLP_SYMBOL Syms32 = NULL;
|
|
PIMAGEHLP_DUPLICATE_SYMBOL64 DupSym64 = NULL;
|
|
PIMAGEHLP_DUPLICATE_SYMBOL DupSym32 = NULL;
|
|
PULONG SymSave;
|
|
|
|
|
|
if (!pe->pCallbackFunction32 && !pe->pCallbackFunction64) {
|
|
return sym;
|
|
}
|
|
|
|
if (!(sym->Flags & SYMF_DUPLICATE)) {
|
|
return sym;
|
|
}
|
|
|
|
Dups = 0;
|
|
NameSize = 0;
|
|
for (i = 0; i < mi->numsyms; i++) {
|
|
if ((mi->symbolTable[i].NameLength == sym->NameLength) &&
|
|
(strcmp( mi->symbolTable[i].Name, sym->Name ) == 0)) {
|
|
Dups += 1;
|
|
NameSize += (mi->symbolTable[i].NameLength + 1);
|
|
}
|
|
}
|
|
|
|
if (pe->pCallbackFunction32) {
|
|
DupSym32 = (PIMAGEHLP_DUPLICATE_SYMBOL) MemAlloc( sizeof(IMAGEHLP_DUPLICATE_SYMBOL) );
|
|
if (!DupSym32) {
|
|
return sym;
|
|
}
|
|
|
|
Syms32 = (PIMAGEHLP_SYMBOL) MemAlloc( (sizeof(IMAGEHLP_SYMBOL) * Dups) + NameSize );
|
|
if (!Syms32) {
|
|
MemFree( DupSym32 );
|
|
return sym;
|
|
}
|
|
|
|
SymSave = (PULONG) MemAlloc( sizeof(ULONG) * Dups );
|
|
if (!SymSave) {
|
|
MemFree( Syms32 );
|
|
MemFree( DupSym32 );
|
|
return sym;
|
|
}
|
|
|
|
DupSym32->SizeOfStruct = sizeof(IMAGEHLP_DUPLICATE_SYMBOL);
|
|
DupSym32->NumberOfDups = Dups;
|
|
DupSym32->Symbol = Syms32;
|
|
DupSym32->SelectedSymbol = (ULONG) -1;
|
|
|
|
Dups = 0;
|
|
for (i = 0; i < mi->numsyms; i++) {
|
|
if ((mi->symbolTable[i].NameLength == sym->NameLength) &&
|
|
(strcmp( mi->symbolTable[i].Name, sym->Name ) == 0)) {
|
|
symcpy32( Syms32, &mi->symbolTable[i] );
|
|
Syms32 += (sizeof(IMAGEHLP_SYMBOL) + mi->symbolTable[i].NameLength + 1);
|
|
SymSave[Dups] = i;
|
|
Dups += 1;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
DupSym64 = (PIMAGEHLP_DUPLICATE_SYMBOL64) MemAlloc( sizeof(IMAGEHLP_DUPLICATE_SYMBOL64) );
|
|
if (!DupSym64) {
|
|
return sym;
|
|
}
|
|
|
|
Syms64 = (PIMAGEHLP_SYMBOL64) MemAlloc( (sizeof(IMAGEHLP_SYMBOL64) * Dups) + NameSize );
|
|
if (!Syms64) {
|
|
MemFree( DupSym64 );
|
|
return sym;
|
|
}
|
|
|
|
SymSave = (PULONG) MemAlloc( sizeof(ULONG) * Dups );
|
|
if (!SymSave) {
|
|
MemFree( Syms64 );
|
|
MemFree( DupSym64 );
|
|
return sym;
|
|
}
|
|
|
|
DupSym64->SizeOfStruct = sizeof(IMAGEHLP_DUPLICATE_SYMBOL64);
|
|
DupSym64->NumberOfDups = Dups;
|
|
DupSym64->Symbol = Syms64;
|
|
DupSym64->SelectedSymbol = (ULONG) -1;
|
|
|
|
Dups = 0;
|
|
for (i = 0; i < mi->numsyms; i++) {
|
|
if ((mi->symbolTable[i].NameLength == sym->NameLength) &&
|
|
(strcmp( mi->symbolTable[i].Name, sym->Name ) == 0)) {
|
|
symcpy64( Syms64, &mi->symbolTable[i] );
|
|
Syms64 += (sizeof(IMAGEHLP_SYMBOL64) + mi->symbolTable[i].NameLength + 1);
|
|
SymSave[Dups] = i;
|
|
Dups += 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sym = NULL;
|
|
|
|
__try {
|
|
|
|
if (pe->pCallbackFunction32) {
|
|
pe->pCallbackFunction32(
|
|
pe->hProcess,
|
|
CBA_DUPLICATE_SYMBOL,
|
|
(PVOID) DupSym32,
|
|
(PVOID) pe->CallbackUserContext
|
|
);
|
|
|
|
if (DupSym32->SelectedSymbol != (ULONG) -1) {
|
|
if (DupSym32->SelectedSymbol < DupSym32->NumberOfDups) {
|
|
sym = &mi->symbolTable[SymSave[DupSym32->SelectedSymbol]];
|
|
}
|
|
}
|
|
} else {
|
|
pe->pCallbackFunction64(
|
|
pe->hProcess,
|
|
CBA_DUPLICATE_SYMBOL,
|
|
(ULONG64) &DupSym64,
|
|
pe->CallbackUserContext
|
|
);
|
|
|
|
if (DupSym64->SelectedSymbol != (ULONG) -1) {
|
|
if (DupSym64->SelectedSymbol < DupSym64->NumberOfDups) {
|
|
sym = &mi->symbolTable[SymSave[DupSym64->SelectedSymbol]];
|
|
}
|
|
}
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
;
|
|
}
|
|
|
|
if (DupSym32) {
|
|
MemFree( DupSym32 );
|
|
}
|
|
if (DupSym64) {
|
|
MemFree( DupSym64 );
|
|
}
|
|
if (Syms32) {
|
|
MemFree( Syms32 );
|
|
}
|
|
if (Syms64) {
|
|
MemFree( Syms64 );
|
|
}
|
|
MemFree( SymSave );
|
|
|
|
return sym;
|
|
}
|
|
|
|
|
|
PSYMBOL_ENTRY
|
|
FindSymbolByName(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
LPSTR SymName
|
|
)
|
|
{
|
|
DWORD hash;
|
|
PSYMBOL_ENTRY sym;
|
|
DWORD i;
|
|
|
|
if (!mi || mi->dia)
|
|
return diaFindSymbolByName(pe, mi, SymName);
|
|
|
|
hash = ComputeHash( SymName, strlen(SymName) );
|
|
sym = mi->NameHashTable[hash];
|
|
|
|
if (sym) {
|
|
//
|
|
// there are collision(s) so lets walk the
|
|
// collision list and match the names
|
|
//
|
|
while( sym ) {
|
|
if (MatchSymbolName( sym, SymName )) {
|
|
sym = HandleDuplicateSymbols( pe, mi, sym );
|
|
return sym;
|
|
}
|
|
sym = sym->Next;
|
|
}
|
|
}
|
|
|
|
//
|
|
// the symbol did not hash to anything valid
|
|
// this is possible if the caller passed an undecorated name
|
|
// now we must look linearly thru the list
|
|
//
|
|
for (i=0; i<mi->numsyms; i++) {
|
|
sym = &mi->symbolTable[i];
|
|
if (MatchSymbolName( sym, SymName )) {
|
|
sym = HandleDuplicateSymbols( pe, mi, sym );
|
|
return sym;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
IMGHLP_RVA_FUNCTION_DATA *
|
|
SearchRvaFunctionTable(
|
|
IMGHLP_RVA_FUNCTION_DATA *FunctionTable,
|
|
LONG High,
|
|
LONG Low,
|
|
DWORD dwPC
|
|
)
|
|
{
|
|
LONG Middle;
|
|
IMGHLP_RVA_FUNCTION_DATA *FunctionEntry;
|
|
|
|
// Perform binary search on the function table for a function table
|
|
// entry that subsumes the specified PC.
|
|
|
|
while (High >= Low) {
|
|
|
|
// Compute next probe index and test entry. If the specified PC
|
|
// is greater than of equal to the beginning address and less
|
|
// than the ending address of the function table entry, then
|
|
// return the address of the function table entry. Otherwise,
|
|
// continue the search.
|
|
|
|
Middle = (Low + High) >> 1;
|
|
FunctionEntry = &FunctionTable[Middle];
|
|
if (dwPC < FunctionEntry->rvaBeginAddress) {
|
|
High = Middle - 1;
|
|
|
|
} else if (dwPC >= FunctionEntry->rvaEndAddress) {
|
|
Low = Middle + 1;
|
|
|
|
} else {
|
|
return FunctionEntry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PIMGHLP_RVA_FUNCTION_DATA
|
|
GetFunctionEntryFromDebugInfo (
|
|
PPROCESS_ENTRY pe,
|
|
DWORD64 ControlPc
|
|
)
|
|
{
|
|
PMODULE_ENTRY mi;
|
|
IMGHLP_RVA_FUNCTION_DATA *FunctionTable;
|
|
|
|
mi = GetModuleForPC( pe, ControlPc, FALSE );
|
|
if (mi == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!GetPData(pe->hProcess, mi)) {
|
|
return NULL;
|
|
}
|
|
|
|
FunctionTable = (IMGHLP_RVA_FUNCTION_DATA *)mi->pExceptionData;
|
|
return SearchRvaFunctionTable(FunctionTable, mi->dwEntries - 1, 0,
|
|
(ULONG)(ControlPc - mi->BaseOfDll));
|
|
}
|
|
|
|
PIMAGE_FUNCTION_ENTRY
|
|
LookupFunctionEntryAxp32 (
|
|
HANDLE hProcess,
|
|
DWORD ControlPc
|
|
)
|
|
{
|
|
FunctionEntryCache* Cache;
|
|
FeCacheEntry* FunctionEntry;
|
|
|
|
if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_ALPHA, TRUE)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// Don't specify the function table access callback or it will
|
|
// cause recursion.
|
|
|
|
FunctionEntry = Cache->
|
|
Find(hProcess, (ULONG64)(LONG)ControlPc, ReadInProcMemory,
|
|
miGetModuleBase, NULL);
|
|
if ( FunctionEntry == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
// Alpha function entries are always stored as 64-bit
|
|
// so downconvert.
|
|
tlsvar(FunctionEntry32).StartingAddress =
|
|
(ULONG)FunctionEntry->Data.Axp64.BeginAddress;
|
|
tlsvar(FunctionEntry32).EndingAddress =
|
|
(ULONG)FunctionEntry->Data.Axp64.EndAddress;
|
|
tlsvar(FunctionEntry32).EndOfPrologue =
|
|
(ULONG)FunctionEntry->Data.Axp64.PrologEndAddress;
|
|
return &tlsvar(FunctionEntry32);
|
|
}
|
|
|
|
PIMAGE_FUNCTION_ENTRY64
|
|
LookupFunctionEntryAxp64 (
|
|
HANDLE hProcess,
|
|
DWORD64 ControlPc
|
|
)
|
|
{
|
|
FunctionEntryCache* Cache;
|
|
FeCacheEntry* FunctionEntry;
|
|
|
|
if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_ALPHA64, TRUE)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// Don't specify the function table access callback or it will
|
|
// cause recursion.
|
|
|
|
FunctionEntry = Cache->
|
|
Find(hProcess, ControlPc, ReadInProcMemory,
|
|
miGetModuleBase, NULL);
|
|
if ( FunctionEntry == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
tlsvar(FunctionEntry64).StartingAddress =
|
|
FunctionEntry->Data.Axp64.BeginAddress;
|
|
tlsvar(FunctionEntry64).EndingAddress =
|
|
FunctionEntry->Data.Axp64.EndAddress;
|
|
tlsvar(FunctionEntry64).EndOfPrologue =
|
|
FunctionEntry->Data.Axp64.PrologEndAddress;
|
|
return &tlsvar(FunctionEntry64);
|
|
}
|
|
|
|
// NTRAID#96939-2000/03/27-patst
|
|
//
|
|
// All the platform dependent "LookupFunctionEntryXxx" should be retyped as returning
|
|
// a PIMAGE_FUNCTION_ENTRY64. This would require a modification of the callers, especially
|
|
// the IA64 specific locations that assume that the returned function entries contains RVAs
|
|
// and not absolute addresses. I implemented a platform-independant
|
|
// "per address space / per module" cache of function entries - capable of supporting the
|
|
// dynamic function entries scheme but I fell short of time in delivering it.
|
|
|
|
PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY
|
|
LookupFunctionEntryIa64 (
|
|
HANDLE hProcess,
|
|
DWORD64 ControlPc
|
|
)
|
|
{
|
|
FunctionEntryCache* Cache;
|
|
FeCacheEntry* FunctionEntry;
|
|
|
|
if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_IA64, TRUE)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// IA64-NOTE 08/99: IA64 Function entries contain file offsets, not absolute relocated addresses.
|
|
// IA64 Callers assume this.
|
|
//
|
|
|
|
|
|
// Don't specify the function table access callback or it will
|
|
// cause recursion.
|
|
|
|
FunctionEntry = Cache->
|
|
Find(hProcess, ControlPc, ReadInProcMemory,
|
|
miGetModuleBase, NULL);
|
|
if ( FunctionEntry == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
tlsvar(Ia64FunctionEntry) = FunctionEntry->Data.Ia64;
|
|
return &tlsvar(Ia64FunctionEntry);
|
|
}
|
|
|
|
_PIMAGE_RUNTIME_FUNCTION_ENTRY
|
|
LookupFunctionEntryAmd64 (
|
|
HANDLE hProcess,
|
|
DWORD64 ControlPc
|
|
)
|
|
{
|
|
FunctionEntryCache* Cache;
|
|
FeCacheEntry* FunctionEntry;
|
|
|
|
if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_AMD64, TRUE)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// Don't specify the function table access callback or it will
|
|
// cause recursion.
|
|
|
|
FunctionEntry = Cache->
|
|
Find(hProcess, ControlPc, ReadInProcMemory,
|
|
miGetModuleBase, NULL);
|
|
if ( FunctionEntry == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
tlsvar(Amd64FunctionEntry) = FunctionEntry->Data.Amd64;
|
|
return &tlsvar(Amd64FunctionEntry);
|
|
}
|
|
|
|
PFPO_DATA
|
|
SwSearchFpoData(
|
|
DWORD key,
|
|
PFPO_DATA base,
|
|
DWORD num
|
|
)
|
|
{
|
|
PFPO_DATA lo = base;
|
|
PFPO_DATA hi = base + (num - 1);
|
|
PFPO_DATA mid;
|
|
DWORD half;
|
|
|
|
while (lo <= hi) {
|
|
if (half = num / 2) {
|
|
mid = lo + ((num & 1) ? half : (half - 1));
|
|
if ((key >= mid->ulOffStart)&&(key < (mid->ulOffStart+mid->cbProcSize))) {
|
|
return mid;
|
|
}
|
|
if (key < mid->ulOffStart) {
|
|
hi = mid - 1;
|
|
num = (num & 1) ? half : half-1;
|
|
} else {
|
|
lo = mid + 1;
|
|
num = half;
|
|
}
|
|
} else
|
|
if (num) {
|
|
if ((key >= lo->ulOffStart)&&(key < (lo->ulOffStart+lo->cbProcSize))) {
|
|
return lo;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
BOOL
|
|
DoSymbolCallback (
|
|
PPROCESS_ENTRY pe,
|
|
ULONG CallbackType,
|
|
IN PMODULE_ENTRY mi,
|
|
PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl64,
|
|
LPSTR FileName
|
|
)
|
|
{
|
|
BOOL Status;
|
|
IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl32;
|
|
|
|
Status = FALSE;
|
|
if (pe->pCallbackFunction32) {
|
|
idsl32.SizeOfStruct = sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD);
|
|
idsl32.BaseOfImage = (ULONG)mi->BaseOfDll;
|
|
idsl32.CheckSum = mi->CheckSum;
|
|
idsl32.TimeDateStamp = mi->TimeDateStamp;
|
|
idsl32.Reparse = FALSE;
|
|
idsl32.FileName[0] = 0;
|
|
if (FileName) {
|
|
strncat( idsl32.FileName, FileName, MAX_PATH - 1 );
|
|
}
|
|
|
|
__try {
|
|
|
|
Status = pe->pCallbackFunction32(
|
|
pe->hProcess,
|
|
CallbackType,
|
|
(PVOID)&idsl32,
|
|
(PVOID)pe->CallbackUserContext
|
|
);
|
|
idsl64->SizeOfStruct = sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64);
|
|
idsl64->BaseOfImage = idsl32.BaseOfImage;
|
|
idsl64->CheckSum = idsl32.CheckSum;
|
|
idsl64->TimeDateStamp = idsl32.TimeDateStamp;
|
|
idsl64->Reparse = idsl32.Reparse;
|
|
if (idsl32.FileName) {
|
|
strncpy( idsl64->FileName, idsl32.FileName, MAX_PATH );
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
} else
|
|
if (pe->pCallbackFunction64) {
|
|
idsl64->SizeOfStruct = sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64);
|
|
idsl64->BaseOfImage = mi->BaseOfDll;
|
|
idsl64->CheckSum = mi->CheckSum;
|
|
idsl64->TimeDateStamp = mi->TimeDateStamp;
|
|
idsl64->Reparse = FALSE;
|
|
idsl64->FileName[0] = 0;
|
|
if (FileName) {
|
|
strncat( idsl64->FileName, FileName, MAX_PATH );
|
|
}
|
|
|
|
__try {
|
|
|
|
Status = pe->pCallbackFunction64(
|
|
pe->hProcess,
|
|
CallbackType,
|
|
(ULONG64)(ULONG_PTR)idsl64,
|
|
pe->CallbackUserContext
|
|
);
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DoCallback(
|
|
PPROCESS_ENTRY pe,
|
|
ULONG type,
|
|
PVOID data
|
|
)
|
|
{
|
|
BOOL rc = TRUE;
|
|
|
|
__try {
|
|
|
|
// if we weren't passed a process entry, then call all processes
|
|
|
|
if (!pe) {
|
|
BOOL ret;
|
|
PLIST_ENTRY next;
|
|
|
|
next = g.ProcessList.Flink;
|
|
if (!next)
|
|
return FALSE;
|
|
|
|
while ((PVOID)next != (PVOID)&g.ProcessList) {
|
|
pe = CONTAINING_RECORD( next, PROCESS_ENTRY, ListEntry );
|
|
next = pe->ListEntry.Flink;
|
|
if (!pe)
|
|
return rc;
|
|
ret = DoCallback(pe, type, data);
|
|
if (!ret)
|
|
rc = ret;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
// otherwise call this process
|
|
|
|
if (pe->pCallbackFunction32) {
|
|
rc = pe->pCallbackFunction32(pe->hProcess,
|
|
type,
|
|
data,
|
|
(PVOID)pe->CallbackUserContext);
|
|
} else if (pe->pCallbackFunction64) {
|
|
rc = pe->pCallbackFunction64(pe->hProcess,
|
|
type,
|
|
(ULONG64)data,
|
|
pe->CallbackUserContext);
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = FALSE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
VOID
|
|
SympSendDebugString(
|
|
PPROCESS_ENTRY pe,
|
|
LPSTR String
|
|
)
|
|
{
|
|
__try {
|
|
if (!pe)
|
|
pe = FindFirstProcessEntry();
|
|
|
|
if (!pe) {
|
|
printf(String);
|
|
} else if (pe->pCallbackFunction32) {
|
|
pe->pCallbackFunction32(pe->hProcess,
|
|
CBA_DEBUG_INFO,
|
|
(PVOID)String,
|
|
(PVOID)pe->CallbackUserContext
|
|
);
|
|
} else if (pe->pCallbackFunction64) {
|
|
pe->pCallbackFunction64(pe->hProcess,
|
|
CBA_DEBUG_INFO,
|
|
(ULONG64)String,
|
|
pe->CallbackUserContext
|
|
);
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
}
|
|
}
|
|
|
|
int
|
|
WINAPIV
|
|
_pprint(
|
|
PPROCESS_ENTRY pe,
|
|
LPSTR Format,
|
|
...
|
|
)
|
|
{
|
|
static char buf[1000] = "DBGHELP: ";
|
|
va_list args;
|
|
|
|
va_start(args, Format);
|
|
_vsnprintf(buf+9, sizeof(buf)-9, Format, args);
|
|
va_end(args);
|
|
SympSendDebugString(pe, buf);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
WINAPIV
|
|
_peprint(
|
|
PPROCESS_ENTRY pe,
|
|
LPSTR Format,
|
|
...
|
|
)
|
|
{
|
|
static char buf[1000] = "";
|
|
va_list args;
|
|
|
|
va_start(args, Format);
|
|
_vsnprintf(buf, sizeof(buf), Format, args);
|
|
va_end(args);
|
|
SympSendDebugString(pe, buf);
|
|
return 1;
|
|
}
|
|
|
|
|
|
int
|
|
WINAPIV
|
|
_dprint(
|
|
LPSTR format,
|
|
...
|
|
)
|
|
{
|
|
static char buf[1000] = "DBGHELP: ";
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
_vsnprintf(buf+9, sizeof(buf)-9, format, args);
|
|
va_end(args);
|
|
SympSendDebugString(NULL, buf);
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
WINAPIV
|
|
_eprint(
|
|
LPSTR format,
|
|
...
|
|
)
|
|
{
|
|
static char buf[1000] = "";
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
_vsnprintf(buf, sizeof(buf), format, args);
|
|
va_end(args);
|
|
SympSendDebugString(NULL, buf);
|
|
return 1;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPIV
|
|
evtprint(
|
|
PPROCESS_ENTRY pe,
|
|
DWORD severity,
|
|
DWORD code,
|
|
PVOID object,
|
|
LPSTR format,
|
|
...
|
|
)
|
|
{
|
|
static char buf[1000] = "";
|
|
IMAGEHLP_CBA_EVENT evt;
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
_vsnprintf(buf, sizeof(buf), format, args);
|
|
va_end(args);
|
|
evt.severity = severity;
|
|
evt.code = code;
|
|
evt.desc = buf;
|
|
evt.object = object;
|
|
|
|
return DoCallback(pe, CBA_EVENT, &evt);
|
|
}
|
|
|
|
BOOL
|
|
traceAddr(
|
|
DWORD64 addr
|
|
)
|
|
{
|
|
DWORD64 taddr = 0;
|
|
|
|
if (!*g.DebugToken)
|
|
return FALSE;
|
|
sscanf(g.DebugToken, "0x%I64x", &taddr);
|
|
taddr = EXTEND64(taddr);
|
|
addr = EXTEND64(addr);
|
|
return (addr == taddr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
traceName(
|
|
PCHAR name
|
|
)
|
|
{
|
|
if (!*g.DebugToken)
|
|
return FALSE;
|
|
return !_strnicmp(name, g.DebugToken, strlen(g.DebugToken));
|
|
}
|
|
|
|
|
|
BOOL
|
|
traceSubName(
|
|
PCHAR name
|
|
)
|
|
{
|
|
char *lname;
|
|
BOOL rc;
|
|
|
|
if (!*g.DebugToken)
|
|
return FALSE;
|
|
lname = (char *)MemAlloc(sizeof(char) * (strlen(name) + 1));
|
|
if (!lname) {
|
|
return FALSE;
|
|
}
|
|
strcpy(lname, name);
|
|
if (!lname)
|
|
return FALSE;
|
|
_strlwr(lname);
|
|
rc = strstr(lname, g.DebugToken) ? TRUE : FALSE;
|
|
MemFree(lname);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
load(
|
|
IN HANDLE hProcess,
|
|
IN PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
|
|
PPROCESS_ENTRY pe;
|
|
ULONG i;
|
|
PIMGHLP_DEBUG_DATA pIDD;
|
|
ULONG bias;
|
|
PIMAGE_SYMBOL lpSymbolEntry;
|
|
PUCHAR lpStringTable;
|
|
PUCHAR p;
|
|
BOOL SymbolsLoaded = FALSE;
|
|
PCHAR CallbackFileName, ImageName;
|
|
ULONG Size;
|
|
|
|
g.LastSymLoadError = SYMLOAD_DEFERRED;
|
|
pe = FindProcessEntry( hProcess );
|
|
if (!pe) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return FALSE;
|
|
}
|
|
|
|
// if (traceName(mi->ModuleName)) // for setting debug breakpoints from DBGHELP_TOKEN
|
|
// pprint(pe, "debug(%s)\n", mi->ModuleName);
|
|
|
|
CallbackFileName = mi->LoadedImageName ? mi->LoadedImageName :
|
|
mi->ImageName ? mi->ImageName : mi->ModuleName;
|
|
|
|
DoSymbolCallback(
|
|
pe,
|
|
CBA_DEFERRED_SYMBOL_LOAD_START,
|
|
mi,
|
|
&idsl,
|
|
CallbackFileName
|
|
);
|
|
|
|
ImageName = mi->ImageName;
|
|
for (; ;) {
|
|
pIDD = GetDebugData(
|
|
hProcess,
|
|
mi->hFile,
|
|
ImageName,
|
|
pe->SymbolSearchPath,
|
|
mi->BaseOfDll,
|
|
&mi->mld,
|
|
0
|
|
);
|
|
mi->SymLoadError = g.LastSymLoadError;
|
|
|
|
if (pIDD) {
|
|
break;
|
|
}
|
|
|
|
pprint(pe, "GetDebugData(%p, %s, %s, %I64x, 0) failed\n",
|
|
mi->hFile,
|
|
ImageName,
|
|
pe->SymbolSearchPath,
|
|
mi->BaseOfDll
|
|
);
|
|
|
|
if (!DoSymbolCallback(
|
|
pe,
|
|
CBA_DEFERRED_SYMBOL_LOAD_FAILURE,
|
|
mi,
|
|
&idsl,
|
|
CallbackFileName
|
|
) || !idsl.Reparse)
|
|
{
|
|
mi->SymType = SymNone;
|
|
mi->Flags |= MIF_NO_SYMBOLS;
|
|
return FALSE;
|
|
}
|
|
|
|
ImageName = idsl.FileName;
|
|
CallbackFileName = idsl.FileName;
|
|
}
|
|
|
|
pIDD->flags = mi->Flags;
|
|
|
|
// The following code ONLY works if the dll wasn't rebased
|
|
// during install. Is it really useful?
|
|
|
|
if (!mi->BaseOfDll) {
|
|
//
|
|
// This case occurs when modules are loaded multiple times by
|
|
// name with no explicit base address.
|
|
//
|
|
if (GetModuleForPC( pe, pIDD->ImageBaseFromImage, TRUE )) {
|
|
if (pIDD->ImageBaseFromImage) {
|
|
pprint(pe, "GetModuleForPC(%p, %I64x, TRUE) failed\n",
|
|
pe,
|
|
pIDD->ImageBaseFromImage,
|
|
TRUE
|
|
);
|
|
} else {
|
|
pprint(pe, "No base address for %s: Please specify\n", ImageName);
|
|
}
|
|
return FALSE;
|
|
}
|
|
mi->BaseOfDll = pIDD->ImageBaseFromImage;
|
|
}
|
|
|
|
if (!mi->DllSize) {
|
|
mi->DllSize = pIDD->SizeOfImage;
|
|
}
|
|
|
|
mi->hProcess = pIDD->hProcess;
|
|
mi->InProcImageBase = pIDD->InProcImageBase;
|
|
|
|
mi->CheckSum = pIDD->CheckSum;
|
|
mi->TimeDateStamp = pIDD->TimeDateStamp;
|
|
mi->MachineType = pIDD->Machine;
|
|
|
|
mi->ImageType = pIDD->ImageType;
|
|
mi->PdbSrc = pIDD->PdbSrc;
|
|
mi->ImageSrc = pIDD->ImageSrc;
|
|
|
|
if (!mi->MachineType && g.MachineType) {
|
|
mi->MachineType = (USHORT) g.MachineType;
|
|
}
|
|
if (pIDD->dia) {
|
|
mi->LoadedPdbName = StringDup(pIDD->PdbFileName);
|
|
}
|
|
if (pIDD->DbgFileMap) {
|
|
mi->LoadedImageName = StringDup(pIDD->DbgFilePath);
|
|
} else if (*pIDD->ImageFilePath) {
|
|
mi->LoadedImageName = StringDup(pIDD->ImageFilePath);
|
|
} else if (pIDD->dia) {
|
|
mi->LoadedImageName = StringDup(pIDD->PdbFileName);
|
|
} else {
|
|
mi->LoadedImageName = StringDup("");
|
|
}
|
|
|
|
if (pIDD->fROM) {
|
|
mi->Flags |= MIF_ROM_IMAGE;
|
|
}
|
|
|
|
if (!mi->ImageName) {
|
|
mi->ImageName = StringDup(pIDD->OriginalImageFileName);
|
|
_splitpath( mi->ImageName, NULL, NULL, mi->ModuleName, NULL );
|
|
mi->AliasName[0] = 0;
|
|
}
|
|
|
|
mi->dsExceptions = pIDD->dsExceptions;
|
|
|
|
if (pIDD->cFpo) {
|
|
//
|
|
// use virtualalloc() because the rtf search function
|
|
// return a pointer into this memory. we want to make
|
|
// all of this memory read only so that callers cannot
|
|
// stomp on imagehlp's data
|
|
//
|
|
mi->pFpoData = (PFPO_DATA)VirtualAlloc(
|
|
NULL,
|
|
sizeof(FPO_DATA) * pIDD->cFpo,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (mi->pFpoData) {
|
|
mi->dwEntries = pIDD->cFpo;
|
|
CopyMemory(
|
|
mi->pFpoData,
|
|
pIDD->pFpo,
|
|
sizeof(FPO_DATA) * mi->dwEntries
|
|
);
|
|
VirtualProtect(
|
|
mi->pFpoData,
|
|
sizeof(FPO_DATA) * mi->dwEntries,
|
|
PAGE_READONLY,
|
|
&i
|
|
);
|
|
}
|
|
}
|
|
|
|
// copy the pdata block from the pdb
|
|
|
|
if (pIDD->pPData) {
|
|
mi->pPData = MemAlloc(pIDD->cbPData);
|
|
if (mi->pPData) {
|
|
mi->cPData = pIDD->cPData;
|
|
mi->cbPData = pIDD->cbPData;
|
|
CopyMemory(mi->pPData, pIDD->pPData, pIDD->cbPData);
|
|
}
|
|
}
|
|
|
|
if (pIDD->pXData) {
|
|
mi->pXData = MemAlloc(pIDD->cbXData);
|
|
if (mi->pXData) {
|
|
mi->cXData = pIDD->cXData;
|
|
mi->cbXData = pIDD->cbXData;
|
|
CopyMemory(mi->pXData, pIDD->pXData, pIDD->cbXData);
|
|
}
|
|
}
|
|
|
|
// now the sections
|
|
|
|
mi->NumSections = pIDD->cCurrentSections;
|
|
if (pIDD->fCurrentSectionsMapped) {
|
|
mi->SectionHdrs = (PIMAGE_SECTION_HEADER) MemAlloc(
|
|
sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
|
|
);
|
|
if (mi->SectionHdrs) {
|
|
CopyMemory(
|
|
mi->SectionHdrs,
|
|
pIDD->pCurrentSections,
|
|
sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
|
|
);
|
|
}
|
|
} else {
|
|
mi->SectionHdrs = pIDD->pCurrentSections;
|
|
}
|
|
|
|
if (pIDD->pOriginalSections) {
|
|
mi->OriginalNumSections = pIDD->cOriginalSections;
|
|
mi->OriginalSectionHdrs = pIDD->pOriginalSections;
|
|
} else {
|
|
mi->OriginalNumSections = mi->NumSections;
|
|
mi->OriginalSectionHdrs = (PIMAGE_SECTION_HEADER) MemAlloc(
|
|
sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
|
|
);
|
|
if (mi->OriginalSectionHdrs) {
|
|
CopyMemory(
|
|
mi->OriginalSectionHdrs,
|
|
pIDD->pCurrentSections,
|
|
sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
|
|
);
|
|
}
|
|
}
|
|
|
|
// symbols
|
|
|
|
mi->TmpSym.Name = (LPSTR) MemAlloc( TMP_SYM_LEN );
|
|
|
|
if (pIDD->dia) {
|
|
mi->SymType = SymDia;
|
|
SymbolsLoaded = TRUE;
|
|
} else {
|
|
if (pIDD->pMappedCv) {
|
|
SymbolsLoaded = LoadCodeViewSymbols(
|
|
hProcess,
|
|
mi,
|
|
pIDD
|
|
);
|
|
pprint(pe, "codeview symbols %sloaded\n", SymbolsLoaded?"":"not ");
|
|
}
|
|
if (!SymbolsLoaded && pIDD->pMappedCoff) {
|
|
SymbolsLoaded = LoadCoffSymbols(hProcess, mi, pIDD);
|
|
pprint(pe, "coff symbols %sloaded\n", SymbolsLoaded?"":"not ");
|
|
}
|
|
|
|
if (!SymbolsLoaded && pIDD->cExports) {
|
|
SymbolsLoaded = LoadExportSymbols( mi, pIDD );
|
|
if (SymbolsLoaded) {
|
|
mi->PdbSrc = srcNone;
|
|
}
|
|
pprint(pe, "export symbols %sloaded\n", SymbolsLoaded?"":"not ");
|
|
}
|
|
|
|
if (!SymbolsLoaded) {
|
|
mi->SymType = SymNone;
|
|
pprint(pe, "no symbols loaded\n");
|
|
}
|
|
}
|
|
|
|
mi->dia = pIDD->dia;
|
|
|
|
ProcessOmapForModule( mi, pIDD );
|
|
|
|
ReleaseDebugData(pIDD,
|
|
IMGHLP_FREE_FPO | IMGHLP_FREE_SYMPATH | IMGHLP_FREE_PDATA | IMGHLP_FREE_XDATA);
|
|
mi->Flags &= ~MIF_DEFERRED_LOAD;
|
|
|
|
DoSymbolCallback(pe,
|
|
CBA_DEFERRED_SYMBOL_LOAD_COMPLETE,
|
|
mi,
|
|
&idsl,
|
|
CallbackFileName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD64
|
|
InternalLoadModule(
|
|
IN HANDLE hProcess,
|
|
IN PSTR ImageName,
|
|
IN PSTR ModuleName,
|
|
IN DWORD64 BaseOfDll,
|
|
IN DWORD DllSize,
|
|
IN HANDLE hFile,
|
|
IN PMODLOAD_DATA data,
|
|
IN DWORD flags
|
|
)
|
|
{
|
|
IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
|
|
PPROCESS_ENTRY pe;
|
|
PMODULE_ENTRY mi;
|
|
LPSTR p;
|
|
DWORD64 ip;
|
|
|
|
// if (traceSubName(ImageName)) // for setting debug breakpoints from DBGHELP_TOKEN
|
|
// dprint("debug(%s)\n", ImageName);
|
|
|
|
if (BaseOfDll == (DWORD64)-1)
|
|
return 0;
|
|
|
|
__try {
|
|
CHAR c;
|
|
if (ImageName)
|
|
c = *ImageName;
|
|
if (ModuleName)
|
|
c = *ModuleName;
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
pe = FindProcessEntry( hProcess );
|
|
if (!pe) {
|
|
return 0;
|
|
}
|
|
|
|
if (BaseOfDll) {
|
|
mi = GetModuleForPC( pe, BaseOfDll, TRUE );
|
|
} else {
|
|
mi = NULL;
|
|
}
|
|
|
|
if (mi) {
|
|
//
|
|
// in this case the symbols are already loaded
|
|
// so the caller really wants the deferred
|
|
// symbols to be loaded
|
|
//
|
|
if ( (mi->Flags & MIF_DEFERRED_LOAD) &&
|
|
load( hProcess, mi )) {
|
|
|
|
return mi->BaseOfDll;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// look to see if there is an overlapping module entry
|
|
//
|
|
if (BaseOfDll) {
|
|
do {
|
|
mi = GetModuleForPC( pe, BaseOfDll, FALSE );
|
|
if (mi) {
|
|
RemoveEntryList( &mi->ListEntry );
|
|
|
|
DoSymbolCallback(
|
|
pe,
|
|
CBA_SYMBOLS_UNLOADED,
|
|
mi,
|
|
&idsl,
|
|
mi->LoadedImageName ? mi->LoadedImageName : mi->ImageName ? mi->ImageName : mi->ModuleName
|
|
);
|
|
|
|
FreeModuleEntry(pe, mi);
|
|
}
|
|
} while(mi);
|
|
}
|
|
|
|
mi = (PMODULE_ENTRY) MemAlloc( sizeof(MODULE_ENTRY) );
|
|
if (!mi) {
|
|
return 0;
|
|
}
|
|
InitModuleEntry(mi);
|
|
|
|
mi->BaseOfDll = BaseOfDll;
|
|
mi->DllSize = DllSize;
|
|
mi->hFile = hFile;
|
|
if (ImageName) {
|
|
char SplitMod[_MAX_FNAME];
|
|
|
|
mi->ImageName = StringDup(ImageName);
|
|
_splitpath( ImageName, NULL, NULL, SplitMod, NULL );
|
|
mi->ModuleName[0] = 0;
|
|
strncat(mi->ModuleName, SplitMod, sizeof(mi->ModuleName) - 1);
|
|
if (ModuleName && _stricmp( ModuleName, mi->ModuleName ) != 0) {
|
|
mi->AliasName[0] = 0;
|
|
strncat( mi->AliasName, ModuleName, sizeof(mi->AliasName) - 1 );
|
|
} else {
|
|
mi->AliasName[0] = 0;
|
|
}
|
|
} else {
|
|
|
|
if (ModuleName) {
|
|
mi->AliasName[0] = 0;
|
|
strncat( mi->AliasName, ModuleName, sizeof(mi->AliasName) - 1 );
|
|
}
|
|
|
|
}
|
|
mi->mod = NULL;
|
|
mi->cbPdbSymbols = 0;
|
|
mi->pPdbSymbols = NULL;
|
|
|
|
if (data) {
|
|
if (data->ssize != sizeof(MODLOAD_DATA)) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
memcpy(&mi->mld, data, data->ssize);
|
|
mi->CallerData = MemAlloc(mi->mld.size);
|
|
if (!mi->CallerData) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
mi->mld.data = mi->CallerData;
|
|
memcpy(mi->mld.data, data->data, mi->mld.size);
|
|
}
|
|
|
|
if ((g.SymOptions & SYMOPT_DEFERRED_LOADS) && BaseOfDll) {
|
|
mi->Flags |= MIF_DEFERRED_LOAD;
|
|
mi->SymType = SymDeferred;
|
|
} else if (!load( hProcess, mi )) {
|
|
FreeModuleEntry(pe, mi);
|
|
return 0;
|
|
}
|
|
|
|
pe->Count += 1;
|
|
|
|
InsertTailList( &pe->ModuleList, &mi->ListEntry);
|
|
|
|
ip = GetIP(pe);
|
|
if ((mi->BaseOfDll <= ip) && (mi->BaseOfDll + DllSize >= ip))
|
|
diaSetModFromIP(pe);
|
|
|
|
return mi->BaseOfDll;
|
|
}
|
|
|
|
PPROCESS_ENTRY
|
|
FindProcessEntry(
|
|
HANDLE hProcess
|
|
)
|
|
{
|
|
PLIST_ENTRY next;
|
|
PPROCESS_ENTRY pe;
|
|
DWORD count;
|
|
|
|
next = g.ProcessList.Flink;
|
|
if (!next) {
|
|
return NULL;
|
|
}
|
|
|
|
for (count = 0; (PVOID)next != (PVOID)&g.ProcessList; count++) {
|
|
assert(count < g.cProcessList);
|
|
if (count >= g.cProcessList)
|
|
return NULL;
|
|
pe = CONTAINING_RECORD( next, PROCESS_ENTRY, ListEntry );
|
|
next = pe->ListEntry.Flink;
|
|
if (pe->hProcess == hProcess) {
|
|
return pe;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PPROCESS_ENTRY
|
|
FindFirstProcessEntry(
|
|
)
|
|
{
|
|
return CONTAINING_RECORD(g.ProcessList.Flink, PROCESS_ENTRY, ListEntry);
|
|
}
|
|
|
|
|
|
PMODULE_ENTRY
|
|
FindModule(
|
|
HANDLE hProcess,
|
|
PPROCESS_ENTRY pe,
|
|
LPSTR ModuleName,
|
|
BOOL fLoad
|
|
)
|
|
{
|
|
PLIST_ENTRY next;
|
|
PMODULE_ENTRY mi;
|
|
|
|
if (!ModuleName || !*ModuleName)
|
|
return NULL;
|
|
|
|
next = pe->ModuleList.Flink;
|
|
if (next) {
|
|
while ((PVOID)next != (PVOID)&pe->ModuleList) {
|
|
mi = CONTAINING_RECORD( next, MODULE_ENTRY, ListEntry );
|
|
next = mi->ListEntry.Flink;
|
|
|
|
if ((_stricmp( mi->ModuleName, ModuleName ) == 0) ||
|
|
(mi->AliasName[0] &&
|
|
_stricmp( mi->AliasName, ModuleName ) == 0))
|
|
{
|
|
if (fLoad && !LoadSymbols(hProcess, mi, 0)) {
|
|
return NULL;
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
|
|
BOOL
|
|
SymCheckUserGenerated(
|
|
ULONG64 dwAddr,
|
|
PSYMBOL_ENTRY sym,
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY nextSym;
|
|
|
|
/*
|
|
// We do not know the size of a user generated symbol...
|
|
// This work because of the execution of CompleteSymbolTable() after AllocSym().
|
|
// Particularly, the size has been adjusted.
|
|
*/
|
|
|
|
if ( !(sym->Flags & SYMF_USER_GENERATED) ) {
|
|
dprint("SymCheckUserGenerated: We should not call this function. This is not a user generated symbol...\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (dwAddr == sym->Address) || (sym == &mi->symbolTable[mi->numsyms - 1]) ) {
|
|
return TRUE;
|
|
}
|
|
|
|
nextSym = sym + 1;
|
|
if ( dwAddr < nextSym->Address ) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
#endif // !_DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
|
|
PSYMBOL_ENTRY
|
|
GetSymFromAddr(
|
|
DWORD64 dwAddr,
|
|
PDWORD64 pqwDisplacement,
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY sym = NULL;
|
|
LONG High;
|
|
LONG Low;
|
|
LONG Middle;
|
|
|
|
if (mi == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (mi->dia)
|
|
return diaGetSymFromAddr(dwAddr, mi, pqwDisplacement);
|
|
|
|
//
|
|
// do a binary search to locate the symbol
|
|
//
|
|
|
|
Low = 0;
|
|
High = mi->numsyms - 1;
|
|
|
|
while (High >= Low) {
|
|
Middle = (Low + High) >> 1;
|
|
sym = &mi->symbolTable[Middle];
|
|
if (dwAddr < sym->Address) {
|
|
|
|
High = Middle - 1;
|
|
|
|
} else if (dwAddr >= sym->Address + sym->Size) {
|
|
|
|
#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
|
|
if ( (sym->Flags & SYMF_USER_GENERATED) && SymCheckUserGenerated( dwAddr, sym, mi ) ) {
|
|
if (pqwDisplacement) {
|
|
*pqwDisplacement = dwAddr - sym->Address;
|
|
}
|
|
return sym;
|
|
}
|
|
|
|
#endif // !_DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
|
|
Low = Middle + 1;
|
|
|
|
} else {
|
|
|
|
if (pqwDisplacement) {
|
|
*pqwDisplacement = dwAddr - sym->Address;
|
|
}
|
|
return sym;
|
|
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PMODULE_ENTRY
|
|
GetModuleForPC(
|
|
PPROCESS_ENTRY pe,
|
|
DWORD64 dwPcAddr,
|
|
BOOL ExactMatch
|
|
)
|
|
{
|
|
static PLIST_ENTRY next = NULL;
|
|
PMODULE_ENTRY mi;
|
|
|
|
if (dwPcAddr == (DWORD64)-1) {
|
|
if (!next)
|
|
return NULL;
|
|
if ((PVOID)next == (PVOID)&pe->ModuleList) {
|
|
// Reset to NULL so the list can be re-walked
|
|
next = NULL;
|
|
return NULL;
|
|
}
|
|
mi = CONTAINING_RECORD( next, MODULE_ENTRY, ListEntry );
|
|
next = mi->ListEntry.Flink;
|
|
return mi;
|
|
}
|
|
|
|
next = pe->ModuleList.Flink;
|
|
if (!next) {
|
|
return NULL;
|
|
}
|
|
|
|
while ((PVOID)next != (PVOID)&pe->ModuleList) {
|
|
mi = CONTAINING_RECORD( next, MODULE_ENTRY, ListEntry );
|
|
next = mi->ListEntry.Flink;
|
|
if (dwPcAddr == 0) {
|
|
return mi;
|
|
}
|
|
if (ExactMatch) {
|
|
if (dwPcAddr == mi->BaseOfDll) {
|
|
return mi;
|
|
}
|
|
} else
|
|
if ((dwPcAddr == mi->BaseOfDll && mi->DllSize == 0) ||
|
|
((dwPcAddr >= mi->BaseOfDll) &&
|
|
(dwPcAddr < mi->BaseOfDll + mi->DllSize))) {
|
|
return mi;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PSYMBOL_ENTRY
|
|
GetSymFromAddrAllContexts(
|
|
DWORD64 dwAddr,
|
|
PDWORD64 pqwDisplacement,
|
|
PPROCESS_ENTRY pe
|
|
)
|
|
{
|
|
PMODULE_ENTRY mi = GetModuleForPC( pe, dwAddr, FALSE );
|
|
if (mi == NULL) {
|
|
return NULL;
|
|
}
|
|
return GetSymFromAddr( dwAddr, pqwDisplacement, mi );
|
|
}
|
|
|
|
DWORD
|
|
ComputeHash(
|
|
LPSTR lpbName,
|
|
ULONG cb
|
|
)
|
|
{
|
|
ULONG UNALIGNED * lpulName;
|
|
ULONG ulEnd = 0;
|
|
int cul;
|
|
int iul;
|
|
ULONG ulSum = 0;
|
|
|
|
while (cb & 3) {
|
|
ulEnd |= (lpbName[cb - 1] & 0xdf);
|
|
ulEnd <<= 8;
|
|
cb -= 1;
|
|
}
|
|
|
|
cul = cb / 4;
|
|
lpulName = (ULONG UNALIGNED *) lpbName;
|
|
for (iul =0; iul < cul; iul++) {
|
|
ulSum ^= (lpulName[iul] & 0xdfdfdfdf);
|
|
ulSum = _lrotl( ulSum, 4);
|
|
}
|
|
ulSum ^= ulEnd;
|
|
return ulSum % HASH_MODULO;
|
|
}
|
|
|
|
PSYMBOL_ENTRY
|
|
AllocSym(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
LPSTR name
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY sym;
|
|
ULONG Length;
|
|
|
|
|
|
if (mi->numsyms == mi->MaxSyms) {
|
|
// dprint("AllocSym: ERROR - symbols Table overflow!\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!mi->StringSize) {
|
|
// dprint("AllocSym: ERROR - symbols strings not allocated for module!\n");
|
|
return NULL;
|
|
}
|
|
|
|
Length = strlen(name);
|
|
|
|
if ((Length + 1) > mi->StringSize) {
|
|
// dprint("AllocSym: ERROR - symbols strings buffer overflow!\n");
|
|
return NULL;
|
|
}
|
|
|
|
sym = &mi->symbolTable[mi->numsyms];
|
|
|
|
mi->numsyms += 1;
|
|
sym->Name = mi->SymStrings;
|
|
mi->SymStrings += (Length + 2);
|
|
mi->StringSize -= (Length + 2);
|
|
|
|
strcpy( sym->Name, name );
|
|
sym->Address = addr;
|
|
sym->Size = 0;
|
|
sym->Flags = 0;
|
|
sym->Next = NULL;
|
|
sym->NameLength = Length;
|
|
|
|
return sym;
|
|
}
|
|
|
|
int __cdecl
|
|
SymbolTableAddressCompare(
|
|
const void *e1,
|
|
const void *e2
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY sym1 = (PSYMBOL_ENTRY) e1;
|
|
PSYMBOL_ENTRY sym2 = (PSYMBOL_ENTRY) e2;
|
|
LONG64 diff;
|
|
|
|
if ( sym1 && sym2 ) {
|
|
diff = (sym1->Address - sym2->Address);
|
|
return (diff < 0) ? -1 : (diff == 0) ? 0 : 1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int __cdecl
|
|
SymbolTableNameCompare(
|
|
const void *e1,
|
|
const void *e2
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY sym1 = (PSYMBOL_ENTRY) e1;
|
|
PSYMBOL_ENTRY sym2 = (PSYMBOL_ENTRY) e2;
|
|
|
|
return strcmp( sym1->Name, sym2->Name );
|
|
}
|
|
|
|
VOID
|
|
CompleteSymbolTable(
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY sym;
|
|
PSYMBOL_ENTRY symH;
|
|
ULONG Hash;
|
|
ULONG i;
|
|
ULONG dups;
|
|
ULONG seq;
|
|
|
|
|
|
//
|
|
// sort the symbols by name
|
|
//
|
|
dbg_qsort(
|
|
mi->symbolTable,
|
|
mi->numsyms,
|
|
sizeof(SYMBOL_ENTRY),
|
|
SymbolTableNameCompare
|
|
);
|
|
|
|
//
|
|
// mark duplicate names
|
|
//
|
|
seq = 0;
|
|
for (i=0; i<mi->numsyms; i++) {
|
|
dups = 0;
|
|
while ((mi->symbolTable[i+dups].NameLength == mi->symbolTable[i+dups+1].NameLength) &&
|
|
(strcmp( mi->symbolTable[i+dups].Name, mi->symbolTable[i+dups+1].Name ) == 0)) {
|
|
mi->symbolTable[i+dups].Flags |= SYMF_DUPLICATE;
|
|
mi->symbolTable[i+dups+1].Flags |= SYMF_DUPLICATE;
|
|
dups += 1;
|
|
}
|
|
i += dups;
|
|
}
|
|
|
|
//
|
|
// sort the symbols by address
|
|
//
|
|
dbg_qsort(
|
|
mi->symbolTable,
|
|
mi->numsyms,
|
|
sizeof(SYMBOL_ENTRY),
|
|
SymbolTableAddressCompare
|
|
);
|
|
|
|
//
|
|
// calculate the size of each symbol
|
|
//
|
|
for (i=0; i<mi->numsyms; i++) {
|
|
mi->symbolTable[i].Next = NULL;
|
|
if (i+1 < mi->numsyms) {
|
|
mi->symbolTable[i].Size = (ULONG)(mi->symbolTable[i+1].Address - mi->symbolTable[i].Address);
|
|
}
|
|
}
|
|
|
|
//
|
|
// compute the hash for each symbol
|
|
//
|
|
ZeroMemory( mi->NameHashTable, sizeof(mi->NameHashTable) );
|
|
for (i=0; i<mi->numsyms; i++) {
|
|
sym = &mi->symbolTable[i];
|
|
|
|
Hash = ComputeHash( sym->Name, sym->NameLength );
|
|
|
|
if (mi->NameHashTable[Hash]) {
|
|
|
|
//
|
|
// we have a collision
|
|
//
|
|
symH = mi->NameHashTable[Hash];
|
|
while( symH->Next ) {
|
|
symH = symH->Next;
|
|
}
|
|
symH->Next = sym;
|
|
|
|
} else {
|
|
|
|
mi->NameHashTable[Hash] = sym;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CreateSymbolTable(
|
|
PMODULE_ENTRY mi,
|
|
DWORD SymbolCount,
|
|
SYM_TYPE SymType,
|
|
DWORD NameSize
|
|
)
|
|
{
|
|
//
|
|
// allocate the symbol table
|
|
//
|
|
NameSize += OMAP_SYM_STRINGS;
|
|
mi->symbolTable = (PSYMBOL_ENTRY) MemAlloc(
|
|
(sizeof(SYMBOL_ENTRY) * (SymbolCount + OMAP_SYM_EXTRA)) + NameSize + (SymbolCount * CPP_EXTRA)
|
|
);
|
|
if (!mi->symbolTable) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// initialize the relevant fields
|
|
//
|
|
mi->numsyms = 0;
|
|
mi->MaxSyms = SymbolCount + OMAP_SYM_EXTRA;
|
|
mi->SymType = SymType;
|
|
mi->StringSize = NameSize + (SymbolCount * CPP_EXTRA);
|
|
mi->SymStrings = (LPSTR)(mi->symbolTable + SymbolCount + OMAP_SYM_EXTRA);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PIMAGE_SECTION_HEADER
|
|
FindSection(
|
|
PIMAGE_SECTION_HEADER sh,
|
|
ULONG NumSections,
|
|
ULONG Address
|
|
)
|
|
{
|
|
ULONG i;
|
|
for (i=0; i<NumSections; i++) {
|
|
if (Address >= sh[i].VirtualAddress &&
|
|
Address < (sh[i].VirtualAddress + sh[i].Misc.VirtualSize)) {
|
|
return &sh[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
PVOID
|
|
GetSectionPhysical(
|
|
HANDLE hp,
|
|
ULONG64 base,
|
|
PIMGHLP_DEBUG_DATA pIDD,
|
|
ULONG Address
|
|
)
|
|
{
|
|
PIMAGE_SECTION_HEADER sh;
|
|
|
|
sh = FindSection( pIDD->pCurrentSections, pIDD->cCurrentSections, Address );
|
|
if (!sh) {
|
|
return 0;
|
|
}
|
|
|
|
return (PCHAR)pIDD->ImageMap + sh->PointerToRawData + (Address - sh->VirtualAddress);
|
|
}
|
|
|
|
BOOL
|
|
ReadSectionInfo(
|
|
HANDLE hp,
|
|
ULONG64 base,
|
|
PIMGHLP_DEBUG_DATA pIDD,
|
|
ULONG address,
|
|
PVOID buf,
|
|
DWORD size
|
|
)
|
|
{
|
|
PIMAGE_SECTION_HEADER sh;
|
|
DWORD_PTR status = TRUE;
|
|
|
|
sh = FindSection( pIDD->pCurrentSections, pIDD->cCurrentSections, address );
|
|
if (!sh)
|
|
return FALSE;
|
|
|
|
if (!hp) {
|
|
status = (DWORD_PTR)memcpy((PCHAR)buf,
|
|
(PCHAR)base + sh->PointerToRawData + (address - sh->VirtualAddress),
|
|
size);
|
|
} else {
|
|
status = ReadImageData(hp, base, address, buf, size);
|
|
}
|
|
if (!status)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PCHAR
|
|
expptr(
|
|
HANDLE hp,
|
|
ULONG64 base,
|
|
PIMGHLP_DEBUG_DATA pIDD,
|
|
ULONG address
|
|
)
|
|
{
|
|
PIMAGE_SECTION_HEADER sh;
|
|
DWORD_PTR status = TRUE;
|
|
|
|
if (hp)
|
|
return (PCHAR)base + address;
|
|
|
|
sh = FindSection( pIDD->pCurrentSections, pIDD->cCurrentSections, address );
|
|
if (!sh)
|
|
return FALSE;
|
|
|
|
return (PCHAR)base + sh->PointerToRawData + (address - sh->VirtualAddress);
|
|
}
|
|
|
|
|
|
ULONG
|
|
LoadExportSymbols(
|
|
PMODULE_ENTRY mi,
|
|
PIMGHLP_DEBUG_DATA pIDD
|
|
)
|
|
{
|
|
PULONG names;
|
|
PULONG addrs;
|
|
PUSHORT ordinals;
|
|
PUSHORT ordidx = NULL;
|
|
ULONG cnt;
|
|
ULONG idx;
|
|
PIMAGE_EXPORT_DIRECTORY expdir;
|
|
PCHAR expbuf = NULL;
|
|
ULONG i;
|
|
PSYMBOL_ENTRY sym;
|
|
ULONG NameSize;
|
|
HANDLE hp;
|
|
ULONG64 base;
|
|
CHAR name[2048];
|
|
BOOL rc;
|
|
DWORD64 endExports;
|
|
PCHAR p;
|
|
|
|
if (g.SymOptions & SYMOPT_EXACT_SYMBOLS)
|
|
return 0;
|
|
|
|
// setup pointers for grabing data
|
|
|
|
switch (pIDD->dsExports) {
|
|
case dsInProc:
|
|
hp = pIDD->hProcess;
|
|
expbuf = (PCHAR)MemAlloc(pIDD->cExports);
|
|
if (!expbuf)
|
|
goto cleanup;
|
|
if (!ReadImageData(hp, pIDD->InProcImageBase, pIDD->oExports, expbuf, pIDD->cExports))
|
|
goto cleanup;
|
|
base = (ULONG64)expbuf - pIDD->oExports;
|
|
expdir = (PIMAGE_EXPORT_DIRECTORY)expbuf;
|
|
break;
|
|
case dsImage:
|
|
hp = NULL;
|
|
expbuf = NULL;
|
|
if (!pIDD->ImageMap)
|
|
pIDD->ImageMap = MapItRO(pIDD->ImageFileHandle);
|
|
base = (ULONG64)pIDD->ImageMap;
|
|
expdir = &pIDD->expdir;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
cnt = 0;
|
|
|
|
names = (PULONG)expptr(hp, base, pIDD, expdir->AddressOfNames);
|
|
if (!names)
|
|
goto cleanup;
|
|
|
|
addrs = (PULONG)expptr(hp, base, pIDD, expdir->AddressOfFunctions);
|
|
if (!addrs)
|
|
goto cleanup;
|
|
|
|
ordinals = (PUSHORT)expptr(hp, base, pIDD, expdir->AddressOfNameOrdinals);
|
|
if (!ordinals)
|
|
goto cleanup;
|
|
|
|
ordidx = (PUSHORT) MemAlloc( max(expdir->NumberOfFunctions, expdir->NumberOfNames) * sizeof(USHORT) );
|
|
|
|
if (!ordidx)
|
|
goto cleanup;
|
|
|
|
cnt = 0;
|
|
NameSize = 0;
|
|
|
|
// count the symbols
|
|
|
|
for (i=0; i<expdir->NumberOfNames; i++) {
|
|
*name = 0;
|
|
p = expptr(hp, base, pIDD, names[i]);
|
|
if (!p)
|
|
continue;
|
|
strcpy(name, p);
|
|
if (!*name)
|
|
continue;
|
|
if (g.SymOptions & SYMOPT_UNDNAME) {
|
|
SymUnDNameInternal( mi->TmpSym.Name, TMP_SYM_LEN, name, strlen(name), mi->MachineType, TRUE );
|
|
NameSize += strlen(mi->TmpSym.Name);
|
|
cnt += 1;
|
|
} else {
|
|
NameSize += (strlen(name) + 2);
|
|
cnt += 1;
|
|
}
|
|
}
|
|
|
|
for (i=0,idx=expdir->NumberOfNames; i<expdir->NumberOfFunctions; i++) {
|
|
if (!ordidx[i]) {
|
|
NameSize += 16;
|
|
cnt += 1;
|
|
}
|
|
}
|
|
|
|
// allocate the symbol table
|
|
|
|
if (!CreateSymbolTable( mi, cnt, SymExport, NameSize )) {
|
|
cnt = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
// allocate the symbols
|
|
|
|
cnt = 0;
|
|
endExports = pIDD->oExports + pIDD->cExports;
|
|
|
|
for (i=0; i<expdir->NumberOfNames; i++) {
|
|
idx = ordinals[i];
|
|
ordidx[idx] = TRUE;
|
|
*name = 0;
|
|
p = expptr(hp, base, pIDD, names[i]);
|
|
if (!p)
|
|
continue;
|
|
strcpy(name, p);
|
|
if (!*name)
|
|
continue;
|
|
if (g.SymOptions & SYMOPT_UNDNAME) {
|
|
SymUnDNameInternal( mi->TmpSym.Name, TMP_SYM_LEN, (LPSTR)name, strlen(name), mi->MachineType, TRUE );
|
|
sym = AllocSym( mi, addrs[idx] + mi->BaseOfDll, mi->TmpSym.Name);
|
|
} else {
|
|
sym = AllocSym( mi, addrs[idx] + mi->BaseOfDll, name);
|
|
}
|
|
if (sym) {
|
|
cnt += 1;
|
|
}
|
|
if (pIDD->oExports <= addrs[idx]
|
|
&& addrs[idx] <= endExports)
|
|
{
|
|
sym->Flags |= SYMF_FORWARDER;
|
|
} else {
|
|
sym->Flags |= SYMF_EXPORT;
|
|
}
|
|
}
|
|
|
|
for (i=0,idx=expdir->NumberOfNames; i<expdir->NumberOfFunctions; i++) {
|
|
if (!ordidx[i]) {
|
|
CHAR NameBuf[sizeof("Ordinal99999") + 1]; // Ordinals are only 64k max.
|
|
strcpy( NameBuf, "Ordinal" );
|
|
_itoa( i+expdir->Base, &NameBuf[7], 10 );
|
|
sym = AllocSym( mi, addrs[i] + mi->BaseOfDll, NameBuf);
|
|
if (sym) {
|
|
cnt += 1;
|
|
}
|
|
idx += 1;
|
|
}
|
|
}
|
|
|
|
CompleteSymbolTable( mi );
|
|
|
|
cleanup:
|
|
if (expbuf) {
|
|
MemFree(expbuf);
|
|
}
|
|
if (ordidx) {
|
|
MemFree(ordidx);
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LoadCoffSymbols(
|
|
HANDLE hProcess,
|
|
PMODULE_ENTRY mi,
|
|
PIMGHLP_DEBUG_DATA pIDD
|
|
)
|
|
{
|
|
PIMAGE_COFF_SYMBOLS_HEADER pCoffHeader = (PIMAGE_COFF_SYMBOLS_HEADER)(pIDD->pMappedCoff);
|
|
PUCHAR stringTable;
|
|
PIMAGE_SYMBOL allSymbols;
|
|
DWORD numberOfSymbols;
|
|
PIMAGE_LINENUMBER LineNumbers;
|
|
PIMAGE_SYMBOL NextSymbol;
|
|
PIMAGE_SYMBOL Symbol;
|
|
PSYMBOL_ENTRY sym;
|
|
CHAR szSymName[256];
|
|
DWORD i;
|
|
DWORD64 addr;
|
|
DWORD CoffSymbols = 0;
|
|
DWORD NameSize = 0;
|
|
DWORD64 Bias;
|
|
|
|
allSymbols = (PIMAGE_SYMBOL)((PCHAR)pCoffHeader +
|
|
pCoffHeader->LvaToFirstSymbol);
|
|
|
|
stringTable = (PUCHAR)pCoffHeader +
|
|
pCoffHeader->LvaToFirstSymbol +
|
|
(pCoffHeader->NumberOfSymbols * IMAGE_SIZEOF_SYMBOL);
|
|
|
|
numberOfSymbols = pCoffHeader->NumberOfSymbols;
|
|
LineNumbers = (PIMAGE_LINENUMBER)(PCHAR)pCoffHeader +
|
|
pCoffHeader->LvaToFirstLinenumber;
|
|
|
|
//
|
|
// count the number of actual symbols
|
|
//
|
|
NextSymbol = allSymbols;
|
|
for (i= 0; i < numberOfSymbols; i++) {
|
|
Symbol = NextSymbol++;
|
|
if (Symbol->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
|
|
Symbol->SectionNumber > 0) {
|
|
GetSymName( Symbol, stringTable, szSymName, sizeof(szSymName) );
|
|
if (szSymName[0] == '?' && szSymName[1] == '?' &&
|
|
szSymName[2] == '_' && szSymName[3] == 'C' ) {
|
|
//
|
|
// ignore strings
|
|
//
|
|
} else if (g.SymOptions & SYMOPT_UNDNAME) {
|
|
SymUnDNameInternal(mi->TmpSym.Name,
|
|
TMP_SYM_LEN,
|
|
szSymName,
|
|
strlen(szSymName),
|
|
mi->MachineType,
|
|
TRUE);
|
|
NameSize += strlen(mi->TmpSym.Name);
|
|
CoffSymbols += 1;
|
|
} else {
|
|
CoffSymbols += 1;
|
|
NameSize += (strlen(szSymName) + 1);
|
|
}
|
|
}
|
|
|
|
NextSymbol += Symbol->NumberOfAuxSymbols;
|
|
i += Symbol->NumberOfAuxSymbols;
|
|
}
|
|
|
|
//
|
|
// allocate the symbol table
|
|
//
|
|
if (!CreateSymbolTable( mi, CoffSymbols, SymCoff, NameSize )) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// populate the symbol table
|
|
//
|
|
|
|
if (mi->Flags & MIF_ROM_IMAGE) {
|
|
Bias = mi->BaseOfDll & 0xffffffff00000000;
|
|
} else {
|
|
Bias = mi->BaseOfDll;
|
|
}
|
|
|
|
NextSymbol = allSymbols;
|
|
for (i= 0; i < numberOfSymbols; i++) {
|
|
Symbol = NextSymbol++;
|
|
if (Symbol->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
|
|
Symbol->SectionNumber > 0) {
|
|
GetSymName( Symbol, stringTable, szSymName, sizeof(szSymName) );
|
|
addr = Symbol->Value + Bias;
|
|
if (szSymName[0] == '?' && szSymName[1] == '?' &&
|
|
szSymName[2] == '_' && szSymName[3] == 'C' ) {
|
|
//
|
|
// ignore strings
|
|
//
|
|
} else if (g.SymOptions & SYMOPT_UNDNAME) {
|
|
SymUnDNameInternal(mi->TmpSym.Name,
|
|
TMP_SYM_LEN,
|
|
szSymName,
|
|
strlen(szSymName),
|
|
mi->MachineType,
|
|
TRUE);
|
|
AllocSym( mi, addr, mi->TmpSym.Name);
|
|
} else {
|
|
AllocSym( mi, addr, szSymName );
|
|
}
|
|
}
|
|
|
|
NextSymbol += Symbol->NumberOfAuxSymbols;
|
|
i += Symbol->NumberOfAuxSymbols;
|
|
}
|
|
|
|
CompleteSymbolTable( mi );
|
|
|
|
if (g.SymOptions & SYMOPT_LOAD_LINES) {
|
|
AddLinesForCoff(mi, allSymbols, numberOfSymbols, LineNumbers);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
LoadCodeViewSymbols(
|
|
HANDLE hProcess,
|
|
PMODULE_ENTRY mi,
|
|
PIMGHLP_DEBUG_DATA pIDD
|
|
)
|
|
{
|
|
DWORD i, j;
|
|
PPROCESS_ENTRY pe;
|
|
OMFSignature *omfSig;
|
|
OMFDirHeader *omfDirHdr;
|
|
OMFDirEntry *omfDirEntry;
|
|
OMFSymHash *omfSymHash;
|
|
DATASYM32 *dataSym;
|
|
DWORD64 addr;
|
|
DWORD CvSymbols;
|
|
DWORD NameSize;
|
|
SYMBOL_ENTRY SymEntry;
|
|
|
|
pe = FindProcessEntry( hProcess );
|
|
if (!pe) {
|
|
return FALSE;
|
|
}
|
|
|
|
pprint(pe, "LoadCodeViewSymbols:\n"
|
|
" hProcess %p\n"
|
|
" mi %p\n"
|
|
" pCvData %p\n"
|
|
" dwSize %x\n",
|
|
hProcess,
|
|
mi,
|
|
pIDD->pMappedCv,
|
|
pIDD->cMappedCv
|
|
);
|
|
|
|
omfSig = (OMFSignature*) pIDD->pMappedCv;
|
|
if ((*(DWORD *)(omfSig->Signature) != '80BN') &&
|
|
(*(DWORD *)(omfSig->Signature) != '90BN') &&
|
|
(*(DWORD *)(omfSig->Signature) != '11BN'))
|
|
{
|
|
if ((*(DWORD *)(omfSig->Signature) != '01BN') &&
|
|
(*(DWORD *)(omfSig->Signature) != 'SDSR'))
|
|
{
|
|
pprint(pe, "unrecognized OMF sig: %x\n", *(DWORD *)(omfSig->Signature));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// count the number of actual symbols
|
|
//
|
|
omfDirHdr = (OMFDirHeader*) ((ULONG_PTR)omfSig + (DWORD)omfSig->filepos);
|
|
omfDirEntry = (OMFDirEntry*) ((ULONG_PTR)omfDirHdr + sizeof(OMFDirHeader));
|
|
|
|
NameSize = 0;
|
|
CvSymbols = 0;
|
|
|
|
for (i=0; i<omfDirHdr->cDir; i++,omfDirEntry++) {
|
|
LPSTR SymbolName;
|
|
UCHAR SymbolLen;
|
|
SYMBOL_ENTRY SymEntry;
|
|
|
|
if (omfDirEntry->SubSection == sstGlobalPub) {
|
|
omfSymHash = (OMFSymHash*) ((ULONG_PTR)omfSig + omfDirEntry->lfo);
|
|
dataSym = (DATASYM32*) ((ULONG_PTR)omfSig + omfDirEntry->lfo + sizeof(OMFSymHash));
|
|
for (j=sizeof(OMFSymHash); j<=omfSymHash->cbSymbol; ) {
|
|
addr = 0;
|
|
cvExtractSymbolInfo(mi, (PCHAR) dataSym, &SymEntry, FALSE);
|
|
if (SymEntry.Segment && (SymEntry.Segment <= mi->OriginalNumSections))
|
|
{
|
|
addr = mi->OriginalSectionHdrs[SymEntry.Segment-1].VirtualAddress + SymEntry.Offset + mi->BaseOfDll;
|
|
SymbolName = SymEntry.Name;
|
|
SymbolLen = (UCHAR) SymEntry.NameLength;
|
|
if (SymbolName[0] == '?' &&
|
|
SymbolName[1] == '?' &&
|
|
SymbolName[2] == '_' &&
|
|
SymbolName[3] == 'C' )
|
|
{
|
|
//
|
|
// ignore strings
|
|
//
|
|
} else if (g.SymOptions & SYMOPT_UNDNAME) {
|
|
SymUnDNameInternal(mi->TmpSym.Name,
|
|
TMP_SYM_LEN,
|
|
SymbolName,
|
|
SymbolLen,
|
|
mi->MachineType,
|
|
TRUE);
|
|
NameSize += strlen(mi->TmpSym.Name);
|
|
CvSymbols += 1;
|
|
} else {
|
|
CvSymbols += 1;
|
|
NameSize += SymbolLen + 1;
|
|
}
|
|
}
|
|
j += dataSym->reclen + 2;
|
|
dataSym = (DATASYM32*) ((ULONG_PTR)dataSym + dataSym->reclen + 2);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// allocate the symbol table
|
|
//
|
|
if (!CreateSymbolTable( mi, CvSymbols, SymCv, NameSize )) {
|
|
pprint(pe, "CreateSymbolTable failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// populate the symbol table
|
|
//
|
|
omfDirHdr = (OMFDirHeader*) ((ULONG_PTR)omfSig + (DWORD)omfSig->filepos);
|
|
omfDirEntry = (OMFDirEntry*) ((ULONG_PTR)omfDirHdr + sizeof(OMFDirHeader));
|
|
for (i=0; i<omfDirHdr->cDir; i++,omfDirEntry++) {
|
|
LPSTR SymbolName;
|
|
if (omfDirEntry->SubSection == sstGlobalPub) {
|
|
omfSymHash = (OMFSymHash*) ((ULONG_PTR)omfSig + omfDirEntry->lfo);
|
|
dataSym = (DATASYM32*) ((ULONG_PTR)omfSig + omfDirEntry->lfo + sizeof(OMFSymHash));
|
|
for (j=sizeof(OMFSymHash); j<=omfSymHash->cbSymbol; ) {
|
|
addr = 0;
|
|
cvExtractSymbolInfo(mi, (PCHAR) dataSym, &SymEntry, FALSE);
|
|
|
|
|
|
if (SymEntry.Segment && (SymEntry.Segment <= mi->OriginalNumSections))
|
|
{
|
|
addr = mi->OriginalSectionHdrs[SymEntry.Segment-1].VirtualAddress + SymEntry.Offset + mi->BaseOfDll;
|
|
SymbolName = SymEntry.Name;
|
|
if (SymbolName[0] == '?' &&
|
|
SymbolName[1] == '?' &&
|
|
SymbolName[2] == '_' &&
|
|
SymbolName[3] == 'C' )
|
|
{
|
|
//
|
|
// ignore strings
|
|
//
|
|
} else if (g.SymOptions & SYMOPT_UNDNAME) {
|
|
SymUnDNameInternal(mi->TmpSym.Name,
|
|
TMP_SYM_LEN,
|
|
SymbolName,
|
|
SymEntry.NameLength,
|
|
mi->MachineType,
|
|
TRUE);
|
|
AllocSym( mi, addr, (LPSTR) mi->TmpSym.Name);
|
|
} else {
|
|
mi->TmpSym.NameLength = SymEntry.NameLength;
|
|
memcpy( mi->TmpSym.Name, SymbolName, mi->TmpSym.NameLength );
|
|
mi->TmpSym.Name[mi->TmpSym.NameLength] = 0;
|
|
AllocSym( mi, addr, mi->TmpSym.Name);
|
|
}
|
|
}
|
|
j += dataSym->reclen + 2;
|
|
dataSym = (DATASYM32*) ((ULONG_PTR)dataSym + dataSym->reclen + 2);
|
|
}
|
|
break;
|
|
}
|
|
else if (omfDirEntry->SubSection == sstSrcModule &&
|
|
(g.SymOptions & SYMOPT_LOAD_LINES)) {
|
|
AddLinesForOmfSourceModule(mi,
|
|
(PUCHAR)(pIDD->pMappedCv)+omfDirEntry->lfo,
|
|
(OMFSourceModule *)
|
|
((PCHAR)(pIDD->pMappedCv)+omfDirEntry->lfo),
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
CompleteSymbolTable( mi );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
GetSymName(
|
|
PIMAGE_SYMBOL Symbol,
|
|
PUCHAR StringTable,
|
|
LPSTR s,
|
|
DWORD size
|
|
)
|
|
{
|
|
DWORD i;
|
|
|
|
if (Symbol->n_zeroes) {
|
|
for (i=0; i<8; i++) {
|
|
if ((Symbol->n_name[i]>0x1f) && (Symbol->n_name[i]<0x7f)) {
|
|
*s++ = Symbol->n_name[i];
|
|
}
|
|
}
|
|
*s = 0;
|
|
}
|
|
else {
|
|
strncpy( s, (char *) &StringTable[Symbol->n_offset], size );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
ProcessOmapForModule(
|
|
PMODULE_ENTRY mi,
|
|
PIMGHLP_DEBUG_DATA pIDD
|
|
)
|
|
{
|
|
PSYMBOL_ENTRY sym;
|
|
PSYMBOL_ENTRY symN;
|
|
DWORD i;
|
|
ULONG64 addr;
|
|
DWORD bias;
|
|
PFPO_DATA fpo;
|
|
|
|
if (pIDD->cOmapTo && pIDD->pOmapTo) {
|
|
if (pIDD->fOmapToMapped || pIDD->dia) {
|
|
mi->pOmapTo = (POMAP)MemAlloc(pIDD->cOmapTo * sizeof(OMAP));
|
|
if (mi->pOmapTo) {
|
|
CopyMemory(
|
|
mi->pOmapTo,
|
|
pIDD->pOmapTo,
|
|
pIDD->cOmapTo * sizeof(OMAP)
|
|
);
|
|
}
|
|
} else {
|
|
mi->pOmapTo = pIDD->pOmapTo;
|
|
}
|
|
mi->cOmapTo = pIDD->cOmapTo;
|
|
}
|
|
|
|
if (pIDD->cOmapFrom && pIDD->pOmapFrom) {
|
|
if (pIDD->fOmapFromMapped) {
|
|
mi->pOmapFrom = (POMAP)MemAlloc(pIDD->cOmapFrom * sizeof(OMAP));
|
|
if (mi->pOmapFrom) {
|
|
CopyMemory(
|
|
mi->pOmapFrom,
|
|
pIDD->pOmapFrom,
|
|
pIDD->cOmapFrom * sizeof(OMAP)
|
|
);
|
|
}
|
|
} else {
|
|
mi->pOmapFrom = pIDD->pOmapFrom;
|
|
}
|
|
mi->cOmapFrom = pIDD->cOmapFrom;
|
|
}
|
|
|
|
if (mi->pFpoData) {
|
|
//
|
|
// if this module is BBT-optimized, then build
|
|
// another fpo table with omap transalation
|
|
//
|
|
mi->pFpoDataOmap = (PFPO_DATA)VirtualAlloc(
|
|
NULL,
|
|
sizeof(FPO_DATA) * mi->dwEntries,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (mi->pFpoDataOmap) {
|
|
CopyMemory(
|
|
mi->pFpoDataOmap,
|
|
pIDD->pFpo,
|
|
sizeof(FPO_DATA) * mi->dwEntries
|
|
);
|
|
for (i = 0, fpo = mi->pFpoDataOmap;
|
|
i < mi->dwEntries;
|
|
i++, fpo++) {
|
|
addr = ConvertOmapFromSrc(mi,
|
|
mi->BaseOfDll + fpo->ulOffStart,
|
|
&bias);
|
|
if (addr)
|
|
fpo->ulOffStart = (ULONG)(addr - mi->BaseOfDll) + bias;
|
|
}
|
|
VirtualProtect(
|
|
mi->pFpoData,
|
|
sizeof(FPO_DATA) * mi->dwEntries,
|
|
PAGE_READONLY,
|
|
&i
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!mi->pOmapFrom ||
|
|
!mi->symbolTable ||
|
|
((mi->SymType != SymCoff) && (mi->SymType != SymCv))
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<mi->numsyms; i++) {
|
|
ProcessOmapSymbol( mi, &mi->symbolTable[i] );
|
|
}
|
|
|
|
CompleteSymbolTable( mi );
|
|
}
|
|
|
|
|
|
BOOL
|
|
ProcessOmapSymbol(
|
|
PMODULE_ENTRY mi,
|
|
PSYMBOL_ENTRY sym
|
|
)
|
|
{
|
|
DWORD bias;
|
|
DWORD64 OptimizedSymAddr;
|
|
DWORD rvaSym;
|
|
POMAPLIST pomaplistHead;
|
|
DWORD64 SymbolValue;
|
|
DWORD64 OrgSymAddr;
|
|
POMAPLIST pomaplistNew;
|
|
POMAPLIST pomaplistPrev;
|
|
POMAPLIST pomaplistCur;
|
|
POMAPLIST pomaplistNext;
|
|
DWORD rva;
|
|
DWORD rvaTo;
|
|
DWORD cb;
|
|
DWORD end;
|
|
DWORD rvaToNext;
|
|
LPSTR NewSymName;
|
|
CHAR Suffix[32];
|
|
DWORD64 addrNew;
|
|
POMAP pomap;
|
|
PSYMBOL_ENTRY symOmap;
|
|
|
|
if ((sym->Flags & SYMF_OMAP_GENERATED) || (sym->Flags & SYMF_OMAP_MODIFIED)) {
|
|
return FALSE;
|
|
}
|
|
|
|
OrgSymAddr = SymbolValue = sym->Address;
|
|
|
|
OptimizedSymAddr = ConvertOmapFromSrc( mi, SymbolValue, &bias );
|
|
|
|
if (OptimizedSymAddr == 0) {
|
|
//
|
|
// No equivalent address
|
|
//
|
|
sym->Address = 0;
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// We have successfully converted
|
|
//
|
|
sym->Address = OptimizedSymAddr + bias;
|
|
|
|
rvaSym = (ULONG)(SymbolValue - mi->BaseOfDll);
|
|
SymbolValue = sym->Address;
|
|
|
|
pomap = GetOmapFromSrcEntry( mi, OrgSymAddr );
|
|
if (!pomap) {
|
|
goto exit;
|
|
}
|
|
|
|
pomaplistHead = NULL;
|
|
|
|
//
|
|
// Look for all OMAP entries belonging to SymbolEntry
|
|
//
|
|
|
|
end = (ULONG)(OrgSymAddr - mi->BaseOfDll + sym->Size);
|
|
|
|
while (pomap && (pomap->rva < end)) {
|
|
|
|
if (pomap->rvaTo == 0) {
|
|
pomap++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Allocate and initialize a new entry
|
|
//
|
|
pomaplistNew = (POMAPLIST) MemAlloc( sizeof(OMAPLIST) );
|
|
if (!pomaplistNew) {
|
|
return FALSE;
|
|
}
|
|
|
|
pomaplistNew->omap = *pomap;
|
|
pomaplistNew->cb = pomap[1].rva - pomap->rva;
|
|
|
|
pomaplistPrev = NULL;
|
|
pomaplistCur = pomaplistHead;
|
|
|
|
while (pomaplistCur != NULL) {
|
|
if (pomap->rvaTo < pomaplistCur->omap.rvaTo) {
|
|
//
|
|
// Insert between Prev and Cur
|
|
//
|
|
break;
|
|
}
|
|
pomaplistPrev = pomaplistCur;
|
|
pomaplistCur = pomaplistCur->next;
|
|
}
|
|
|
|
if (pomaplistPrev == NULL) {
|
|
//
|
|
// Insert in head position
|
|
//
|
|
pomaplistHead = pomaplistNew;
|
|
} else {
|
|
pomaplistPrev->next = pomaplistNew;
|
|
}
|
|
|
|
pomaplistNew->next = pomaplistCur;
|
|
|
|
pomap++;
|
|
}
|
|
|
|
if (pomaplistHead == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
pomaplistCur = pomaplistHead;
|
|
pomaplistNext = pomaplistHead->next;
|
|
|
|
//
|
|
// we do have a list
|
|
//
|
|
while (pomaplistNext != NULL) {
|
|
rva = pomaplistCur->omap.rva;
|
|
rvaTo = pomaplistCur->omap.rvaTo;
|
|
cb = pomaplistCur->cb;
|
|
rvaToNext = pomaplistNext->omap.rvaTo;
|
|
|
|
if (rvaToNext == sym->Address - mi->BaseOfDll) {
|
|
//
|
|
// Already inserted above
|
|
//
|
|
} else if (rvaToNext < (rvaTo + cb + 8)) {
|
|
//
|
|
// Adjacent to previous range
|
|
//
|
|
} else {
|
|
addrNew = mi->BaseOfDll + rvaToNext;
|
|
Suffix[0] = '_';
|
|
_ltoa( pomaplistNext->omap.rva - rvaSym, &Suffix[1], 10 );
|
|
memcpy( mi->TmpSym.Name, sym->Name, sym->NameLength );
|
|
strncpy( &mi->TmpSym.Name[sym->NameLength], Suffix, strlen(Suffix) + 1 );
|
|
symOmap = AllocSym( mi, addrNew, mi->TmpSym.Name);
|
|
if (symOmap) {
|
|
symOmap->Flags |= SYMF_OMAP_GENERATED;
|
|
}
|
|
}
|
|
|
|
MemFree(pomaplistCur);
|
|
|
|
pomaplistCur = pomaplistNext;
|
|
pomaplistNext = pomaplistNext->next;
|
|
}
|
|
|
|
MemFree(pomaplistCur);
|
|
|
|
exit:
|
|
if (sym->Address != OrgSymAddr) {
|
|
sym->Flags |= SYMF_OMAP_MODIFIED;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD64
|
|
ConvertOmapFromSrc(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
LPDWORD bias
|
|
)
|
|
{
|
|
DWORD rva;
|
|
DWORD comap;
|
|
POMAP pomapLow;
|
|
POMAP pomapHigh;
|
|
DWORD comapHalf;
|
|
POMAP pomapMid;
|
|
|
|
|
|
*bias = 0;
|
|
|
|
if (!mi->pOmapFrom) {
|
|
return addr;
|
|
}
|
|
|
|
rva = (DWORD)(addr - mi->BaseOfDll);
|
|
|
|
comap = mi->cOmapFrom;
|
|
pomapLow = mi->pOmapFrom;
|
|
pomapHigh = pomapLow + comap;
|
|
|
|
while (pomapLow < pomapHigh) {
|
|
|
|
comapHalf = comap / 2;
|
|
|
|
pomapMid = pomapLow + ((comap & 1) ? comapHalf : (comapHalf - 1));
|
|
|
|
if (rva == pomapMid->rva) {
|
|
if (pomapMid->rvaTo) {
|
|
return mi->BaseOfDll + pomapMid->rvaTo;
|
|
} else {
|
|
return(0); // No need adding the base. This address was discarded...
|
|
}
|
|
}
|
|
|
|
if (rva < pomapMid->rva) {
|
|
pomapHigh = pomapMid;
|
|
comap = (comap & 1) ? comapHalf : (comapHalf - 1);
|
|
} else {
|
|
pomapLow = pomapMid + 1;
|
|
comap = comapHalf;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no exact match, pomapLow points to the next higher address
|
|
//
|
|
if (pomapLow == mi->pOmapFrom) {
|
|
//
|
|
// This address was not found
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
if (pomapLow[-1].rvaTo == 0) {
|
|
//
|
|
// This address is in a discarded block
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Return the closest address plus the bias
|
|
//
|
|
*bias = rva - pomapLow[-1].rva;
|
|
|
|
return mi->BaseOfDll + pomapLow[-1].rvaTo;
|
|
}
|
|
|
|
|
|
DWORD64
|
|
ConvertOmapToSrc(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
LPDWORD bias,
|
|
BOOL fBackup
|
|
)
|
|
{
|
|
DWORD rva;
|
|
DWORD comap;
|
|
POMAP pomapLow;
|
|
POMAP pomapHigh;
|
|
DWORD comapHalf;
|
|
POMAP pomapMid;
|
|
|
|
*bias = 0;
|
|
|
|
if (!mi->pOmapTo) {
|
|
return addr;
|
|
}
|
|
|
|
rva = (DWORD)(addr - mi->BaseOfDll);
|
|
|
|
comap = mi->cOmapTo;
|
|
pomapLow = mi->pOmapTo;
|
|
pomapHigh = pomapLow + comap;
|
|
|
|
while (pomapLow < pomapHigh) {
|
|
|
|
comapHalf = comap / 2;
|
|
|
|
pomapMid = pomapLow + ((comap & 1) ? comapHalf : (comapHalf - 1));
|
|
|
|
if (rva == pomapMid->rva) {
|
|
if (pomapMid->rvaTo == 0) {
|
|
//
|
|
// We may be at the start of an inserted branch instruction
|
|
//
|
|
|
|
if (fBackup) {
|
|
//
|
|
// Return information about the next lower address
|
|
//
|
|
|
|
rva--;
|
|
pomapLow = pomapMid;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return mi->BaseOfDll + pomapMid->rvaTo;
|
|
}
|
|
|
|
if (rva < pomapMid->rva) {
|
|
pomapHigh = pomapMid;
|
|
comap = (comap & 1) ? comapHalf : (comapHalf - 1);
|
|
} else {
|
|
pomapLow = pomapMid + 1;
|
|
comap = comapHalf;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no exact match, pomapLow points to the next higher address
|
|
//
|
|
|
|
if (pomapLow == mi->pOmapTo) {
|
|
//
|
|
// This address was not found
|
|
//
|
|
return 0;
|
|
}
|
|
|
|
// find the previous valid item in the omap
|
|
|
|
do {
|
|
pomapLow--;
|
|
if (pomapLow->rvaTo)
|
|
break;
|
|
} while (pomapLow > mi->pOmapTo);
|
|
|
|
// should never occur
|
|
|
|
// assert(pomapLow->rvaTo);
|
|
if (pomapLow->rvaTo == 0) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Return the new address plus the bias
|
|
//
|
|
*bias = rva - pomapLow->rva;
|
|
|
|
return mi->BaseOfDll + pomapLow->rvaTo;
|
|
}
|
|
|
|
POMAP
|
|
GetOmapFromSrcEntry(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr
|
|
)
|
|
{
|
|
DWORD rva;
|
|
DWORD comap;
|
|
POMAP pomapLow;
|
|
POMAP pomapHigh;
|
|
DWORD comapHalf;
|
|
POMAP pomapMid;
|
|
|
|
|
|
if (mi->pOmapFrom == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
rva = (DWORD)(addr - mi->BaseOfDll);
|
|
|
|
comap = mi->cOmapFrom;
|
|
pomapLow = mi->pOmapFrom;
|
|
pomapHigh = pomapLow + comap;
|
|
|
|
while (pomapLow < pomapHigh) {
|
|
|
|
comapHalf = comap / 2;
|
|
|
|
pomapMid = pomapLow + ((comap & 1) ? comapHalf : (comapHalf - 1));
|
|
|
|
if (rva == pomapMid->rva) {
|
|
return pomapMid;
|
|
}
|
|
|
|
if (rva < pomapMid->rva) {
|
|
pomapHigh = pomapMid;
|
|
comap = (comap & 1) ? comapHalf : (comapHalf - 1);
|
|
} else {
|
|
pomapLow = pomapMid + 1;
|
|
comap = comapHalf;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpOmapForModule(
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
POMAP pomap;
|
|
DWORD i;
|
|
|
|
i = sizeof(ULONG_PTR);
|
|
i = sizeof(DWORD);
|
|
|
|
if (!mi->pOmapFrom)
|
|
return;
|
|
|
|
dprint("\nOMAP FROM:\n");
|
|
for(i = 0, pomap = mi->pOmapFrom;
|
|
i < 100; // mi->cOmapFrom;
|
|
i++, pomap++)
|
|
{
|
|
dprint("%8x %8x\n", pomap->rva, pomap->rvaTo);
|
|
}
|
|
|
|
if (!mi->pOmapTo)
|
|
return;
|
|
|
|
dprint("\nOMAP TO:\n");
|
|
for(i = 0, pomap = mi->pOmapTo;
|
|
i < 100; // mi->cOmapTo;
|
|
i++, pomap++)
|
|
{
|
|
dprint("%8x %8x\n", pomap->rva, pomap->rvaTo);
|
|
}
|
|
}
|
|
|
|
|
|
LPSTR
|
|
StringDup(
|
|
LPSTR str
|
|
)
|
|
{
|
|
LPSTR ds = (LPSTR) MemAlloc( strlen(str) + 1 );
|
|
if (ds) {
|
|
strcpy( ds, str );
|
|
}
|
|
return ds;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InternalGetModule(
|
|
HANDLE hProcess,
|
|
LPSTR ModuleName,
|
|
DWORD64 ImageBase,
|
|
DWORD ImageSize,
|
|
PVOID Context
|
|
)
|
|
{
|
|
InternalLoadModule(
|
|
hProcess,
|
|
ModuleName,
|
|
NULL,
|
|
ImageBase,
|
|
ImageSize,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LoadedModuleEnumerator(
|
|
HANDLE hProcess,
|
|
LPSTR ModuleName,
|
|
DWORD64 ImageBase,
|
|
DWORD ImageSize,
|
|
PLOADED_MODULE lm
|
|
)
|
|
{
|
|
if (lm->EnumLoadedModulesCallback64) {
|
|
return lm->EnumLoadedModulesCallback64( ModuleName, ImageBase, ImageSize, lm->Context );
|
|
} else {
|
|
return lm->EnumLoadedModulesCallback32( ModuleName, (DWORD)ImageBase, ImageSize, lm->Context );
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
ToggleFailCriticalErrors(
|
|
BOOL reset
|
|
)
|
|
{
|
|
static UINT oldmode = 0;
|
|
|
|
if (!(g.SymOptions & SYMOPT_FAIL_CRITICAL_ERRORS))
|
|
return FALSE;
|
|
|
|
if (reset)
|
|
SetErrorMode(oldmode);
|
|
else
|
|
oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
fnGetFileAttributes(
|
|
LPCTSTR lpFileName
|
|
)
|
|
{
|
|
DWORD rc;
|
|
|
|
SetCriticalErrorMode();
|
|
rc = GetFileAttributes(lpFileName);
|
|
ResetCriticalErrorMode();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
LPSTR
|
|
SymUnDNameInternal(
|
|
LPSTR UnDecName,
|
|
DWORD UnDecNameLength,
|
|
LPSTR DecName,
|
|
DWORD DecNameLength,
|
|
DWORD MachineType,
|
|
BOOL IsPublic
|
|
)
|
|
{
|
|
LPSTR p;
|
|
ULONG Suffix;
|
|
ULONG i;
|
|
LPSTR TmpDecName;
|
|
|
|
UnDecName[0] = 0;
|
|
|
|
if (DecName[0] == '?' || !strncmp(DecName, ".?", 2) || !strncmp(DecName, "..?", 3)) {
|
|
|
|
__try {
|
|
|
|
if (DecName[0] == '.') {
|
|
if (DecName[1] == '.') {
|
|
Suffix = 2;
|
|
UnDecName[0] = '.';
|
|
UnDecName[1] = '.';
|
|
} else { // DecName[1] = '?'
|
|
Suffix = 1;
|
|
UnDecName[0] = '.';
|
|
}
|
|
} else { // DecName[0] = '?'
|
|
Suffix = 0;
|
|
}
|
|
|
|
TmpDecName = (LPSTR)MemAlloc( 4096 );
|
|
if (!TmpDecName) {
|
|
strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );
|
|
return UnDecName;
|
|
}
|
|
TmpDecName[0] = 0;
|
|
strncat( TmpDecName, DecName+Suffix, DecNameLength );
|
|
|
|
if (UnDecorateSymbolName( TmpDecName,
|
|
UnDecName+Suffix,
|
|
UnDecNameLength-Suffix,
|
|
UNDNAME_NAME_ONLY ) == 0 ) {
|
|
strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );
|
|
}
|
|
|
|
MemFree( TmpDecName );
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
__try {
|
|
|
|
if ((IsPublic && (DecName[0] == '_' || DecName[0] == '.'))
|
|
|| DecName[0] == '@') {
|
|
DecName += 1;
|
|
DecNameLength -= 1;
|
|
}
|
|
|
|
p = 0;
|
|
for (i = 0; i < DecNameLength; i++) {
|
|
if (DecName [i] == '@') {
|
|
p = &DecName [i];
|
|
break;
|
|
}
|
|
}
|
|
if (p) {
|
|
i = (int)(p - DecName);
|
|
} else {
|
|
i = min(DecNameLength,UnDecNameLength);
|
|
}
|
|
|
|
strncat( UnDecName, DecName, i );
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );
|
|
|
|
}
|
|
}
|
|
|
|
if (g.SymOptions & SYMOPT_NO_CPP) {
|
|
while (p = strstr( UnDecName, "::" )) {
|
|
p[0] = '_';
|
|
p[1] = '_';
|
|
}
|
|
}
|
|
|
|
return UnDecName;
|
|
}
|
|
|
|
|
|
BOOL
|
|
MatchSymName(
|
|
LPSTR matchName,
|
|
LPSTR symName
|
|
)
|
|
{
|
|
assert(matchName && symName);
|
|
if (!*matchName || !*symName)
|
|
return FALSE;
|
|
|
|
if (g.SymOptions & SYMOPT_CASE_INSENSITIVE) {
|
|
if (!_strnicmp(matchName, symName, MAX_SYM_NAME))
|
|
return TRUE;
|
|
} else {
|
|
if (!strncmp(matchName, symName, MAX_SYM_NAME))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PIMAGEHLP_SYMBOL
|
|
symcpy32(
|
|
PIMAGEHLP_SYMBOL External,
|
|
PSYMBOL_ENTRY Internal
|
|
)
|
|
{
|
|
External->Address = (ULONG)Internal->Address;
|
|
External->Size = Internal->Size;
|
|
External->Flags = Internal->Flags;
|
|
|
|
External->Name[0] = 0;
|
|
strncat( External->Name, Internal->Name, External->MaxNameLength );
|
|
|
|
return External;
|
|
}
|
|
|
|
PIMAGEHLP_SYMBOL64
|
|
symcpy64(
|
|
PIMAGEHLP_SYMBOL64 External,
|
|
PSYMBOL_ENTRY Internal
|
|
)
|
|
{
|
|
External->Address = Internal->Address;
|
|
External->Size = Internal->Size;
|
|
External->Flags = Internal->Flags;
|
|
|
|
External->Name[0] = 0;
|
|
strncat( External->Name, Internal->Name, External->MaxNameLength );
|
|
|
|
return External;
|
|
}
|
|
|
|
BOOL
|
|
SympConvertSymbol64To32(
|
|
PIMAGEHLP_SYMBOL64 Symbol64,
|
|
PIMAGEHLP_SYMBOL Symbol32
|
|
)
|
|
{
|
|
Symbol32->Address = (DWORD)Symbol64->Address;
|
|
Symbol32->Size = Symbol64->Size;
|
|
Symbol32->Flags = Symbol64->Flags;
|
|
Symbol32->MaxNameLength = Symbol64->MaxNameLength;
|
|
Symbol32->Name[0] = 0;
|
|
strncat( Symbol32->Name, Symbol64->Name, Symbol32->MaxNameLength );
|
|
|
|
return (Symbol64->Address >> 32) == 0;
|
|
}
|
|
|
|
BOOL
|
|
SympConvertSymbol32To64(
|
|
PIMAGEHLP_SYMBOL Symbol32,
|
|
PIMAGEHLP_SYMBOL64 Symbol64
|
|
)
|
|
{
|
|
Symbol64->Address = Symbol32->Address;
|
|
Symbol64->Size = Symbol32->Size;
|
|
Symbol64->Flags = Symbol32->Flags;
|
|
Symbol64->MaxNameLength = Symbol32->MaxNameLength;
|
|
Symbol64->Name[0] = 0;
|
|
strncat( Symbol64->Name, Symbol32->Name, Symbol64->MaxNameLength );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
SympConvertLine64To32(
|
|
PIMAGEHLP_LINE64 Line64,
|
|
PIMAGEHLP_LINE Line32
|
|
)
|
|
{
|
|
Line32->Key = Line64->Key;
|
|
Line32->LineNumber = Line64->LineNumber;
|
|
Line32->FileName = Line64->FileName;
|
|
Line32->Address = (DWORD)Line64->Address;
|
|
|
|
return (Line64->Address >> 32) == 0;
|
|
}
|
|
|
|
BOOL
|
|
SympConvertLine32To64(
|
|
PIMAGEHLP_LINE Line32,
|
|
PIMAGEHLP_LINE64 Line64
|
|
)
|
|
{
|
|
Line64->Key = Line32->Key;
|
|
Line64->LineNumber = Line32->LineNumber;
|
|
Line64->FileName = Line32->FileName;
|
|
Line64->Address = Line32->Address;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
__stdcall
|
|
ReadInProcMemory(
|
|
HANDLE hProcess,
|
|
DWORD64 addr,
|
|
PVOID buf,
|
|
DWORD bytes,
|
|
DWORD *bytesread
|
|
)
|
|
{
|
|
DWORD rc;
|
|
PPROCESS_ENTRY pe;
|
|
IMAGEHLP_CBA_READ_MEMORY rm;
|
|
|
|
rm.addr = addr;
|
|
rm.buf = buf;
|
|
rm.bytes = bytes;
|
|
rm.bytesread = bytesread;
|
|
|
|
rc = FALSE;
|
|
|
|
__try {
|
|
pe = FindProcessEntry(hProcess);
|
|
if (!pe) {
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return FALSE;
|
|
}
|
|
|
|
if (pe->pCallbackFunction32) {
|
|
rc = pe->pCallbackFunction32(pe->hProcess,
|
|
CBA_READ_MEMORY,
|
|
(PVOID)&rm,
|
|
(PVOID)pe->CallbackUserContext);
|
|
|
|
} else if (pe->pCallbackFunction64) {
|
|
rc = pe->pCallbackFunction64(pe->hProcess,
|
|
CBA_READ_MEMORY,
|
|
(ULONG64)&rm,
|
|
pe->CallbackUserContext);
|
|
} else {
|
|
SIZE_T RealBytesRead=0;
|
|
rc = ReadProcessMemory(hProcess,
|
|
(LPVOID)(ULONG_PTR)addr,
|
|
buf,
|
|
bytes,
|
|
&RealBytesRead);
|
|
*bytesread = (DWORD)RealBytesRead;
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
rc = FALSE;
|
|
}
|
|
|
|
return (rc != FALSE);
|
|
}
|
|
|
|
DWORD64
|
|
miGetModuleBase(
|
|
HANDLE hProcess,
|
|
DWORD64 Address
|
|
)
|
|
{
|
|
IMAGEHLP_MODULE64 ModuleInfo = {0};
|
|
ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
|
|
|
|
if (SymGetModuleInfo64(hProcess, Address, &ModuleInfo)) {
|
|
return ModuleInfo.BaseOfImage;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
GetPData(
|
|
HANDLE hp,
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
BOOL status;
|
|
ULONG cb;
|
|
PCHAR pc;
|
|
BOOL fROM = FALSE;
|
|
IMAGE_DOS_HEADER DosHeader;
|
|
IMAGE_NT_HEADERS ImageNtHeaders;
|
|
PIMAGE_FILE_HEADER ImageFileHdr;
|
|
PIMAGE_OPTIONAL_HEADER ImageOptionalHdr;
|
|
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
|
|
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
|
|
ULONG feCount = 0;
|
|
ULONG i;
|
|
|
|
HANDLE fh = 0;
|
|
PCHAR base = NULL;
|
|
USHORT filetype;
|
|
PIMAGE_SEPARATE_DEBUG_HEADER sdh;
|
|
PIMAGE_DOS_HEADER dh;
|
|
PIMAGE_NT_HEADERS inth;
|
|
PIMAGE_OPTIONAL_HEADER32 ioh32;
|
|
PIMAGE_OPTIONAL_HEADER64 ioh64;
|
|
ULONG cdd;
|
|
PCHAR p;
|
|
PIMAGE_DEBUG_DIRECTORY dd;
|
|
ULONG cexp = 0;
|
|
ULONG tsize;
|
|
ULONG csize = 0;
|
|
|
|
// if the pdata is already loaded, return
|
|
|
|
if (mi->pExceptionData)
|
|
return TRUE;
|
|
|
|
if (!LoadSymbols(hp, mi, 0))
|
|
return FALSE;
|
|
|
|
// try to get pdata from dia
|
|
|
|
if (mi->dia) {
|
|
if ((mi->pPData) && (mi->dsExceptions == dsDia))
|
|
goto dia;
|
|
|
|
if (diaGetPData(mi)) {
|
|
p = (PCHAR)mi->pPData;
|
|
csize = mi->cbPData;
|
|
goto dia;
|
|
}
|
|
}
|
|
|
|
if (!mi->dsExceptions)
|
|
return FALSE;
|
|
|
|
// open the file and get the file type
|
|
|
|
SetCriticalErrorMode();
|
|
|
|
fh = CreateFile(mi->LoadedImageName,
|
|
GENERIC_READ,
|
|
g.OSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ? (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE) : (FILE_SHARE_READ | FILE_SHARE_WRITE),
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
ResetCriticalErrorMode();
|
|
|
|
if (fh == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
|
|
base = (PCHAR)MapItRO(fh);
|
|
if (!base)
|
|
goto cleanup;
|
|
p = base;
|
|
|
|
filetype = *(USHORT *)p;
|
|
if (filetype == IMAGE_DOS_SIGNATURE)
|
|
goto image;
|
|
if (filetype == IMAGE_SEPARATE_DEBUG_SIGNATURE)
|
|
goto dbg;
|
|
goto cleanup;
|
|
|
|
image:
|
|
|
|
// process disk-based image
|
|
|
|
dh = (PIMAGE_DOS_HEADER)p;
|
|
p += dh->e_lfanew;
|
|
inth = (PIMAGE_NT_HEADERS)p;
|
|
|
|
if (inth->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
ioh32 = (PIMAGE_OPTIONAL_HEADER32)&inth->OptionalHeader;
|
|
p = base + ioh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
|
|
csize = ioh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
|
|
}
|
|
else if (inth->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
ioh64 = (PIMAGE_OPTIONAL_HEADER64)&inth->OptionalHeader;
|
|
p = base + ioh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
|
|
csize = ioh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
|
|
}
|
|
|
|
dia:
|
|
|
|
if (!csize)
|
|
goto cleanup;
|
|
|
|
switch (mi->MachineType)
|
|
{
|
|
case IMAGE_FILE_MACHINE_ALPHA:
|
|
cexp = csize / sizeof(IMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_ALPHA64:
|
|
cexp = csize / sizeof(IMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
cexp = csize / sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY);
|
|
break;
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
cexp = csize / sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY);
|
|
break;
|
|
default:
|
|
goto cleanup;
|
|
}
|
|
|
|
goto table;
|
|
|
|
dbg:
|
|
|
|
// process dbg file
|
|
|
|
sdh = (PIMAGE_SEPARATE_DEBUG_HEADER)p;
|
|
cdd = sdh->DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
p += sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
|
|
(sdh->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)) +
|
|
sdh->ExportedNamesSize;
|
|
dd = (PIMAGE_DEBUG_DIRECTORY)p;
|
|
|
|
for (i = 0; i < cdd; i++, dd++) {
|
|
if (dd->Type == IMAGE_DEBUG_TYPE_EXCEPTION) {
|
|
p = base + dd->PointerToRawData;
|
|
cexp = dd->SizeOfData / sizeof(IMAGE_FUNCTION_ENTRY);
|
|
break;
|
|
}
|
|
}
|
|
|
|
table:
|
|
|
|
// parse the pdata into a table
|
|
|
|
if (!cexp)
|
|
goto cleanup;
|
|
|
|
tsize = cexp * sizeof(IMGHLP_RVA_FUNCTION_DATA);
|
|
|
|
mi->pExceptionData = (PIMGHLP_RVA_FUNCTION_DATA)VirtualAlloc( NULL, tsize, MEM_COMMIT, PAGE_READWRITE );
|
|
|
|
if (mi->pExceptionData) {
|
|
PIMGHLP_RVA_FUNCTION_DATA pIRFD = mi->pExceptionData;
|
|
switch (mi->MachineType) {
|
|
|
|
case IMAGE_FILE_MACHINE_ALPHA:
|
|
if (filetype == IMAGE_SEPARATE_DEBUG_SIGNATURE) {
|
|
// easy case. The addresses are already in rva format.
|
|
PIMAGE_FUNCTION_ENTRY pFE = (PIMAGE_FUNCTION_ENTRY)p;
|
|
for (i = 0; i < cexp; i++) {
|
|
pIRFD[i].rvaBeginAddress = pFE[i].StartingAddress;
|
|
pIRFD[i].rvaEndAddress = pFE[i].EndingAddress;
|
|
pIRFD[i].rvaPrologEndAddress = pFE[i].EndOfPrologue;
|
|
pIRFD[i].rvaExceptionHandler = 0;
|
|
pIRFD[i].rvaHandlerData = 0;
|
|
}
|
|
} else {
|
|
PIMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY pRFE = (PIMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY)p;
|
|
for (i = 0; i < cexp; i++) {
|
|
pIRFD[i].rvaBeginAddress = pRFE[i].BeginAddress - (ULONG)mi->BaseOfDll;
|
|
pIRFD[i].rvaEndAddress = pRFE[i].EndAddress - (ULONG)mi->BaseOfDll;
|
|
pIRFD[i].rvaPrologEndAddress = pRFE[i].PrologEndAddress - (ULONG)mi->BaseOfDll;
|
|
pIRFD[i].rvaExceptionHandler = pRFE[i].ExceptionHandler - (ULONG)mi->BaseOfDll;
|
|
pIRFD[i].rvaHandlerData = pRFE[i].HandlerData - (ULONG)mi->BaseOfDll;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_ALPHA64:
|
|
{
|
|
PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY pRFE = (PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY)p;
|
|
for (i = 0; i < cexp; i++) {
|
|
pIRFD[i].rvaBeginAddress = (DWORD)(pRFE[i].BeginAddress - mi->BaseOfDll);
|
|
pIRFD[i].rvaEndAddress = (DWORD)(pRFE[i].EndAddress - mi->BaseOfDll);
|
|
pIRFD[i].rvaPrologEndAddress = (DWORD)(pRFE[i].PrologEndAddress - mi->BaseOfDll);
|
|
pIRFD[i].rvaExceptionHandler = (DWORD)(pRFE[i].ExceptionHandler - mi->BaseOfDll);
|
|
pIRFD[i].rvaHandlerData = (DWORD)(pRFE[i].HandlerData - mi->BaseOfDll);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
{
|
|
PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY pRFE = (PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY)p;
|
|
for (i = 0; i < cexp; i++) {
|
|
pIRFD[i].rvaBeginAddress = pRFE[i].BeginAddress;
|
|
pIRFD[i].rvaEndAddress = pRFE[i].EndAddress;
|
|
pIRFD[i].rvaPrologEndAddress = pRFE[i].UnwindInfoAddress;
|
|
pIRFD[i].rvaExceptionHandler = 0;
|
|
pIRFD[i].rvaHandlerData = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
{
|
|
_PIMAGE_RUNTIME_FUNCTION_ENTRY pRFE = (_PIMAGE_RUNTIME_FUNCTION_ENTRY)p;
|
|
for (i = 0; i < cexp; i++) {
|
|
pIRFD[i].rvaBeginAddress = pRFE[i].BeginAddress;
|
|
pIRFD[i].rvaEndAddress = pRFE[i].EndAddress;
|
|
pIRFD[i].rvaPrologEndAddress = pRFE[i].UnwindInfoAddress;
|
|
pIRFD[i].rvaExceptionHandler = 0;
|
|
pIRFD[i].rvaHandlerData = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
VirtualProtect( mi->pExceptionData, tsize, PAGE_READONLY, &i );
|
|
|
|
mi->dwEntries = cexp;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (mi->pPData) {
|
|
MemFree(mi->pPData);
|
|
mi->pPData = NULL;
|
|
}
|
|
|
|
if (base)
|
|
UnmapViewOfFile(base);
|
|
|
|
if (fh)
|
|
CloseHandle(fh);
|
|
|
|
return (cexp) ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL
|
|
GetXData(
|
|
HANDLE hp,
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
if (mi->pXData)
|
|
return TRUE;
|
|
|
|
if (LoadSymbols(hp, mi, 0) && !mi->pXData && mi->dia && !diaGetXData(mi))
|
|
return FALSE;
|
|
|
|
return (mi->pXData != NULL);
|
|
}
|
|
|
|
PVOID
|
|
GetXDataFromBase(
|
|
HANDLE hp,
|
|
DWORD64 base,
|
|
ULONG_PTR* size
|
|
)
|
|
{
|
|
PPROCESS_ENTRY pe;
|
|
PMODULE_ENTRY mi;
|
|
|
|
pe = FindProcessEntry(hp);
|
|
if (!pe) {
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return NULL;
|
|
}
|
|
|
|
mi = GetModuleForPC(pe, base, FALSE);
|
|
if (!mi) {
|
|
SetLastError(ERROR_MOD_NOT_FOUND);
|
|
return NULL;
|
|
}
|
|
|
|
if (!GetXData(hp, mi))
|
|
return NULL;
|
|
|
|
if (size) *size = mi->cbXData;
|
|
return mi->pXData;
|
|
}
|
|
|
|
PVOID
|
|
GetUnwindInfoFromSymbols(
|
|
HANDLE hProcess,
|
|
DWORD64 ModuleBase,
|
|
ULONG UnwindInfoAddress,
|
|
ULONG_PTR* Size
|
|
)
|
|
{
|
|
ULONG_PTR XDataSize;
|
|
|
|
PBYTE pXData = (PBYTE)GetXDataFromBase(hProcess, ModuleBase, &XDataSize);
|
|
if (!pXData)
|
|
return NULL;
|
|
|
|
DWORD DataBase = *(DWORD*)pXData;
|
|
pXData += sizeof(DWORD);
|
|
|
|
if (DataBase > UnwindInfoAddress)
|
|
return NULL;
|
|
|
|
ULONG_PTR Offset = UnwindInfoAddress - DataBase;
|
|
|
|
if (Offset >= XDataSize)
|
|
return NULL;
|
|
|
|
if (Size) *Size = XDataSize - Offset;
|
|
return pXData + Offset;
|
|
}
|