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

2707 lines
79 KiB
C

/*++
Copyright (c) 1993-2001 Microsoft Corporation
Module Name:
walkx86.c
Abstract:
This file implements the Intel x86 stack walking api. This api allows for
the presence of "real mode" stack frames. This means that you can trace
into WOW code.
Author:
Wesley Witt (wesw) 1-Oct-1993
Environment:
User Mode
--*/
#define _IMAGEHLP_SOURCE_
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include "private.h"
#define NOEXTAPI
#include "wdbgexts.h"
#include "ntdbg.h"
#include <objbase.h>
#include <wx86dll.h>
#include <symbols.h>
#include <globals.h>
#include "dia2.h"
#if 0
#define WDB(Args) dbPrint Args
#else
#define WDB(Args)
#endif
#define SAVE_EBP(f) (f->Reserved[0])
#define TRAP_TSS(f) (f->Reserved[1])
#define TRAP_EDITED(f) (f->Reserved[1])
#define SAVE_TRAP(f) (f->Reserved[2])
#define CALLBACK_STACK(f) (f->KdHelp.ThCallbackStack)
#define CALLBACK_NEXT(f) (f->KdHelp.NextCallback)
#define CALLBACK_FUNC(f) (f->KdHelp.KiCallUserMode)
#define CALLBACK_THREAD(f) (f->KdHelp.Thread)
#define CALLBACK_FP(f) (f->KdHelp.FramePointer)
#define CALLBACK_DISPATCHER(f) (f->KdHelp.KeUserCallbackDispatcher)
#define SYSTEM_RANGE_START(f) (f->KdHelp.SystemRangeStart)
#define STACK_SIZE (sizeof(DWORD))
#define FRAME_SIZE (STACK_SIZE * 2)
#define STACK_SIZE16 (sizeof(WORD))
#define FRAME_SIZE16 (STACK_SIZE16 * 2)
#define FRAME_SIZE1632 (STACK_SIZE16 * 3)
#define MAX_STACK_SEARCH 64 // in STACK_SIZE units
#define MAX_JMP_CHAIN 64 // in STACK_SIZE units
#define MAX_CALL 7 // in bytes
#define MIN_CALL 2 // in bytes
#define MAX_FUNC_PROLOGUE 64 // in bytes
#define PUSHBP 0x55
#define MOVBPSP 0xEC8B
ULONG g_vc7fpo = 1;
#define DoMemoryRead(addr,buf,sz,br) \
ReadMemoryInternal( Process, Thread, addr, buf, sz, \
br, ReadMemory, TranslateAddress )
BOOL
WalkX86Init(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
BOOL
WalkX86Next(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
BOOL
ReadMemoryInternal(
HANDLE Process,
HANDLE Thread,
LPADDRESS64 lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
BOOL
IsFarCall(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
BOOL *Ok,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
);
BOOL
ReadTrapFrame(
HANDLE Process,
DWORD64 TrapFrameAddress,
PX86_KTRAP_FRAME TrapFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
);
BOOL
TaskGate2TrapFrame(
HANDLE Process,
USHORT TaskRegister,
PX86_KTRAP_FRAME TrapFrame,
PULONG64 off,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
);
DWORD64
SearchForReturnAddress(
HANDLE Process,
DWORD64 uoffStack,
DWORD64 funcAddr,
DWORD funcSize,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
BOOL AcceptUnreadableCallSite
);
//----------------------------------------------------------------------------
//
// DIA IDiaStackWalkFrame implementation.
//
//----------------------------------------------------------------------------
class X86WalkFrame : public IDiaStackWalkFrame
{
public:
X86WalkFrame(HANDLE Process,
X86_CONTEXT* Context,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase)
{
m_Process = Process;
m_Context = Context;
m_ReadMemory = ReadMemory;
m_GetModuleBase = GetModuleBase;
m_Locals = 0;
m_Params = 0;
m_VirtFrame = Context->Ebp;
}
// IUnknown.
STDMETHOD(QueryInterface)(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
);
STDMETHOD_(ULONG, AddRef)(
THIS
);
STDMETHOD_(ULONG, Release)(
THIS
);
// IDiaStackWalkFrame.
STDMETHOD(get_registerValue)(DWORD reg, ULONGLONG* pValue);
STDMETHOD(put_registerValue)(DWORD reg, ULONGLONG value);
STDMETHOD(readMemory)(ULONGLONG va, DWORD cbData,
DWORD* pcbData, BYTE* data);
STDMETHOD(searchForReturnAddress)(IDiaFrameData* frame,
ULONGLONG* pResult);
STDMETHOD(searchForReturnAddressStart)(IDiaFrameData* frame,
ULONGLONG startAddress,
ULONGLONG* pResult);
private:
HANDLE m_Process;
X86_CONTEXT* m_Context;
PREAD_PROCESS_MEMORY_ROUTINE64 m_ReadMemory;
PGET_MODULE_BASE_ROUTINE64 m_GetModuleBase;
ULONGLONG m_Locals;
ULONGLONG m_Params;
ULONGLONG m_VirtFrame;
};
STDMETHODIMP
X86WalkFrame::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
HRESULT Status;
*Interface = NULL;
Status = E_NOINTERFACE;
if (IsEqualIID(InterfaceId, IID_IDiaStackWalkFrame)) {
*Interface = (IDiaStackWalkFrame*)this;
Status = S_OK;
}
return Status;
}
STDMETHODIMP_(ULONG)
X86WalkFrame::AddRef(
THIS
)
{
// Stack allocated, no refcount.
return 1;
}
STDMETHODIMP_(ULONG)
X86WalkFrame::Release(
THIS
)
{
// Stack allocated, no refcount.
return 0;
}
STDMETHODIMP
X86WalkFrame::get_registerValue( DWORD reg, ULONGLONG* pVal )
{
switch( reg ) {
// debug registers
case CV_REG_DR0:
*pVal = m_Context->Dr0;
break;
case CV_REG_DR1:
*pVal = m_Context->Dr1;
break;
case CV_REG_DR2:
*pVal = m_Context->Dr2;
break;
case CV_REG_DR3:
*pVal = m_Context->Dr3;
break;
case CV_REG_DR6:
*pVal = m_Context->Dr6;
break;
case CV_REG_DR7:
*pVal = m_Context->Dr7;
break;
// segment registers
case CV_REG_GS:
*pVal = m_Context->SegGs;
break;
case CV_REG_FS:
*pVal = m_Context->SegFs;
break;
case CV_REG_ES:
*pVal = m_Context->SegEs;
break;
case CV_REG_DS:
*pVal = m_Context->SegDs;
break;
// integer registers
case CV_REG_EDI:
*pVal = m_Context->Edi;
break;
case CV_REG_ESI:
*pVal = m_Context->Esi;
break;
case CV_REG_EBX:
*pVal = m_Context->Ebx;
break;
case CV_REG_EDX:
*pVal = m_Context->Edx;
break;
case CV_REG_ECX:
*pVal = m_Context->Ecx;
break;
case CV_REG_EAX:
*pVal = m_Context->Eax;
break;
// control registers
case CV_REG_EBP:
*pVal = m_Context->Ebp;
break;
case CV_REG_EIP:
*pVal = m_Context->Eip;
break;
case CV_REG_CS:
*pVal = m_Context->SegCs;
break;
case CV_REG_EFLAGS:
*pVal = m_Context->EFlags;
break;
case CV_REG_ESP:
*pVal = m_Context->Esp;
break;
case CV_REG_SS:
*pVal = m_Context->SegSs;
break;
case CV_ALLREG_LOCALS:
*pVal = m_Locals;
break;
case CV_ALLREG_PARAMS:
*pVal = m_Params;
break;
case CV_ALLREG_VFRAME:
*pVal = m_VirtFrame;
break;
default:
*pVal = 0;
return E_FAIL;
}
return S_OK;
}
STDMETHODIMP
X86WalkFrame::put_registerValue( DWORD reg, ULONGLONG LongVal )
{
ULONG val = (ULONG)LongVal;
switch( reg ) {
// debug registers
case CV_REG_DR0:
m_Context->Dr0 = val;
break;
case CV_REG_DR1:
m_Context->Dr1 = val;
break;
case CV_REG_DR2:
m_Context->Dr2 = val;
break;
case CV_REG_DR3:
m_Context->Dr3 = val;
break;
case CV_REG_DR6:
m_Context->Dr6 = val;
break;
case CV_REG_DR7:
m_Context->Dr7 = val;
break;
// segment registers
case CV_REG_GS:
m_Context->SegGs = val;
break;
case CV_REG_FS:
m_Context->SegFs = val;
break;
case CV_REG_ES:
m_Context->SegEs = val;
break;
case CV_REG_DS:
m_Context->SegDs = val;
break;
// integer registers
case CV_REG_EDI:
m_Context->Edi = val;
break;
case CV_REG_ESI:
m_Context->Esi = val;
break;
case CV_REG_EBX:
m_Context->Ebx = val;
break;
case CV_REG_EDX:
m_Context->Edx = val;
break;
case CV_REG_ECX:
m_Context->Ecx = val;
break;
case CV_REG_EAX:
m_Context->Eax = val;
break;
// control registers
case CV_REG_EBP:
m_Context->Ebp = val;
break;
case CV_REG_EIP:
m_Context->Eip = val;
break;
case CV_REG_CS:
m_Context->SegCs = val;
break;
case CV_REG_EFLAGS:
m_Context->EFlags = val;
break;
case CV_REG_ESP:
m_Context->Esp = val;
break;
case CV_REG_SS:
m_Context->SegSs = val;
break;
case CV_ALLREG_LOCALS:
m_Locals = val;
break;
case CV_ALLREG_PARAMS:
m_Params = val;
break;
case CV_ALLREG_VFRAME:
m_VirtFrame = val;
break;
default:
return E_FAIL;
}
return S_OK;
}
STDMETHODIMP
X86WalkFrame::readMemory(ULONGLONG va, DWORD cbData,
DWORD* pcbData, BYTE* data)
{
return m_ReadMemory( m_Process, va, data, cbData, pcbData ) != 0 ?
S_OK : E_FAIL;
}
STDMETHODIMP
X86WalkFrame::searchForReturnAddress(IDiaFrameData* frame,
ULONGLONG* pResult)
{
HRESULT Status;
DWORD LenLocals, LenRegs;
if ((Status = frame->get_lengthLocals(&LenLocals)) != S_OK ||
(Status = frame->get_lengthSavedRegisters(&LenRegs)) != S_OK) {
return Status;
}
return searchForReturnAddressStart(frame,
m_Context->Esp + LenLocals + LenRegs,
pResult);
}
STDMETHODIMP
X86WalkFrame::searchForReturnAddressStart(IDiaFrameData* DiaFrame,
ULONGLONG StartAddress,
ULONGLONG* Result)
{
HRESULT Status;
BOOL IsFuncStart;
IDiaFrameData* OrigFrame = DiaFrame;
IDiaFrameData* NextFrame;
//
// This frame data may be a subsidiary descriptor. Move up
// the parent chain to the true function start.
//
while (DiaFrame->get_functionParent(&NextFrame) == S_OK) {
if (DiaFrame != OrigFrame) {
DiaFrame->Release();
}
DiaFrame = NextFrame;
}
ULONGLONG FuncStart;
DWORD LenFunc;
if ((Status = DiaFrame->get_virtualAddress(&FuncStart)) == S_OK) {
Status = DiaFrame->get_lengthBlock(&LenFunc);
}
if (DiaFrame != OrigFrame) {
DiaFrame->Release();
}
if (Status != S_OK) {
return Status;
}
*Result = SearchForReturnAddress(m_Process,
StartAddress,
FuncStart,
LenFunc,
m_ReadMemory,
m_GetModuleBase,
FALSE);
return *Result != 0 ? S_OK : E_FAIL;
}
//----------------------------------------------------------------------------
//
// Walk functions.
//
//----------------------------------------------------------------------------
BOOL
WalkX86(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress,
DWORD flags
)
{
BOOL rval;
#if 0
WDB(("WalkX86 in: PC %X, SP %X, FP %X, RA %X\n",
(ULONG)StackFrame->AddrPC.Offset,
(ULONG)StackFrame->AddrStack.Offset,
(ULONG)StackFrame->AddrFrame.Offset,
(ULONG)StackFrame->AddrReturn.Offset));
#endif
if (StackFrame->Virtual) {
rval = WalkX86Next( Process,
Thread,
StackFrame,
(PX86_CONTEXT)ContextRecord,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
} else {
rval = WalkX86Init( Process,
Thread,
StackFrame,
(PX86_CONTEXT)ContextRecord,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
}
#if 0
WDB(("WalkX86 out: PC %X, SP %X, FP %X, RA %X\n",
(ULONG)StackFrame->AddrPC.Offset,
(ULONG)StackFrame->AddrStack.Offset,
(ULONG)StackFrame->AddrFrame.Offset,
(ULONG)StackFrame->AddrReturn.Offset));
#endif
// This hack fixes the fpo stack when ebp wasn't used.
// Don't put this fix into StackWalk() or it will break MSDEV.
#if 0
if (rval && (flags & WALK_FIX_FPO_EBP)) {
PFPO_DATA pFpo = (PFPO_DATA)StackFrame->FuncTableEntry;
if (pFpo && !pFpo->fUseBP) {
StackFrame->AddrFrame.Offset += 4;
}
}
#endif
return rval;
}
BOOL
ReadMemoryInternal(
HANDLE Process,
HANDLE Thread,
LPADDRESS64 lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
ADDRESS64 addr;
addr = *lpBaseAddress;
if (addr.Mode != AddrModeFlat) {
TranslateAddress( Process, Thread, &addr );
}
return ReadMemory( Process,
addr.Offset,
lpBuffer,
nSize,
lpNumberOfBytesRead
);
}
DWORD64
SearchForReturnAddress(
HANDLE Process,
DWORD64 uoffStack,
DWORD64 funcAddr,
DWORD funcSize,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
BOOL AcceptUnreadableCallSite
)
{
DWORD64 uoffRet;
DWORD64 uoffBestGuess = 0;
DWORD cdwIndex;
DWORD cdwIndexMax;
INT cbIndex;
INT cbLimit;
DWORD cBytes;
DWORD cJmpChain = 0;
DWORD64 uoffT;
DWORD cb;
BYTE jmpBuffer[ sizeof(WORD) + sizeof(DWORD) ];
LPWORD lpwJmp = (LPWORD)&jmpBuffer[0];
BYTE code[MAX_CALL];
DWORD stack [ MAX_STACK_SEARCH ];
BOPINSTR BopInstr;
WDB((" SearchForReturnAddress: start %X\n", (ULONG)uoffStack));
//
// this function is necessary for 4 reasons:
//
// 1) random compiler bugs where regs are saved on the
// stack but the fpo data does not account for them
//
// 2) inline asm code that does a push
//
// 3) any random code that does a push and it isn't
// accounted for in the fpo data
//
// 4) non-void non-fpo functions
// *** This case is not neccessary when the compiler
// emits FPO records for non-FPO funtions. Unfortunately
// only the NT group uses this feature.
//
if (!ReadMemory(Process,
uoffStack,
stack,
sizeof(stack),
&cb)) {
WDB((" can't read stack\n"));
return 0;
}
cdwIndexMax = cb / STACK_SIZE;
if ( !cdwIndexMax ) {
WDB((" can't read stack\n"));
return 0;
}
for ( cdwIndex=0; cdwIndex<cdwIndexMax; cdwIndex++,uoffStack+=STACK_SIZE ) {
uoffRet = (DWORD64)(LONG64)(LONG)stack[cdwIndex];
//
// Don't try looking for Code in the first 64K of an NT app.
//
if ( uoffRet < 0x00010000 ) {
continue;
}
//
// if it isn't part of any known address space it must be bogus
//
if (GetModuleBase( Process, uoffRet ) == 0) {
continue;
}
//
// Check for a BOP instruction.
//
if (ReadMemory(Process,
uoffRet - sizeof(BOPINSTR),
&BopInstr,
sizeof(BOPINSTR),
&cb)) {
if (cb == sizeof(BOPINSTR) &&
BopInstr.Instr1 == 0xc4 && BopInstr.Instr2 == 0xc4) {
WDB((" BOP, use %X\n", (ULONG)uoffStack));
return uoffStack;
}
}
//
// Read the maximum number of bytes a call could be from the istream
//
cBytes = MAX_CALL;
if (!ReadMemory(Process,
uoffRet - cBytes,
code,
cBytes,
&cb)) {
//
// if page is not present, we will ALWAYS mess up by
// continuing to search. If alloca was used also, we
// are toast. Too Bad.
//
if (cdwIndex == 0 && AcceptUnreadableCallSite) {
WDB((" unreadable call site, use %X\n",
(ULONG)uoffStack));
return uoffStack;
} else {
continue;
}
}
//
// With 32bit code that isn't FAR:32 we don't have to worry about
// intersegment calls. Check here to see if we had a call within
// segment. If it is we can later check it's full diplacement if
// necessary and see if it calls the FPO function. We will also have
// to check for thunks and see if maybe it called a JMP indirect which
// called the FPO function. We will fail to find the caller if it was
// a case of tail recursion where one function doesn't actually call
// another but rather jumps to it. This will only happen when a
// function who's parameter list is void calls another function who's
// parameter list is void and the call is made as the last statement
// in the first function. If the call to the first function was an
// 0xE8 call we will fail to find it here because it didn't call the
// FPO function but rather the FPO functions caller. If we don't get
// specific about our 0xE8 checks we will potentially see things that
// look like return addresses but aren't.
//
if (( cBytes >= 5 ) && ( ( code[ 2 ] == 0xE8 ) || ( code[ 2 ] == 0xE9 ) )) {
// We do math on 32 bit so we can ignore carry, and then sign extended
uoffT = (ULONG64)(LONG64)(LONG)((DWORD)uoffRet + *( (UNALIGNED DWORD *) &code[3] ));
//
// See if it calls the function directly, or into the function
//
if (( uoffT >= funcAddr) && ( uoffT < (funcAddr + funcSize) ) ) {
WDB((" found function, use %X\n", (ULONG)uoffStack));
return uoffStack;
}
while ( cJmpChain < MAX_JMP_CHAIN ) {
if (!ReadMemory(Process,
uoffT,
jmpBuffer,
sizeof(jmpBuffer),
&cb)) {
break;
}
if (cb != sizeof(jmpBuffer)) {
break;
}
//
// Now we are going to check if it is a call to a JMP, that may
// jump to the function
//
// If it is a relative JMP then calculate the destination
// and save it in uoffT. If it is an indirect JMP then read
// the destination from where the JMP is inderecting through.
//
if ( *(LPBYTE)lpwJmp == 0xE9 ) {
// We do math on 32 bit so we can ignore carry, and then
// sign extended
uoffT = (ULONG64)(LONG64)(LONG) ((ULONG)uoffT +
*(UNALIGNED DWORD *)( jmpBuffer + sizeof(BYTE) ) + 5);
} else if ( *lpwJmp == 0x25FF ) {
if ((!ReadMemory(Process,
(ULONG64)(LONG64)(LONG) (
*(UNALIGNED DWORD *)
((LPBYTE)lpwJmp+sizeof(WORD))),
&uoffT,
sizeof(DWORD),
&cb)) || (cb != sizeof(DWORD))) {
uoffT = 0;
break;
}
uoffT = (DWORD64)(LONG64)(LONG)uoffT;
} else {
break;
}
//
// If the destination is to the FPO function then we have
// found the return address and thus the vEBP
//
if ( uoffT == funcAddr ) {
WDB((" exact function, use %X\n",
(ULONG)uoffStack));
return uoffStack;
}
cJmpChain++;
}
//
// We cache away the first 0xE8 call or 0xE9 jmp that we find in
// the event we cant find anything else that looks like a return
// address. This is meant to protect us in the tail recursion case.
//
if ( !uoffBestGuess ) {
uoffBestGuess = uoffStack;
}
}
//
// Now loop backward through the bytes read checking for a multi
// byte call type from Grp5. If we find an 0xFF then we need to
// check the byte after that to make sure that the nnn bits of
// the mod/rm byte tell us that it is a call. It it is a call
// then we will assume that this one called us because we can
// no longer accurately determine for sure whether this did
// in fact call the FPO function. Since 0xFF calls are a guess
// as well we will not check them if we already have an earlier guess.
// It is more likely that the first 0xE8 called the function than
// something higher up the stack that might be an 0xFF call.
//
if ( !uoffBestGuess && cBytes >= MIN_CALL ) {
cbLimit = MAX_CALL - (INT)cBytes;
for (cbIndex = MAX_CALL - MIN_CALL;
cbIndex >= cbLimit; //MAX_CALL - (INT)cBytes;
cbIndex--) {
if ( ( code [ cbIndex ] == 0xFF ) &&
( ( code [ cbIndex + 1 ] & 0x30 ) == 0x10 )){
WDB((" found call, use %X\n", (ULONG)uoffStack));
return uoffStack;
}
}
}
}
//
// we found nothing that was 100% definite so we'll return the best guess
//
WDB((" best guess is %X\n", (ULONG)uoffBestGuess));
return uoffBestGuess;
}
DWORD64
SearchForFramePointer(
HANDLE Process,
DWORD64 StackAddr,
DWORD NumRegs,
DWORD64 FuncAddr,
DWORD FuncSize,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
)
{
BYTE Code[MAX_FUNC_PROLOGUE];
DWORD CodeLen;
DWORD i;
DWORD Depth;
WDB((" SearchForFramePointer: start %X, regs %d\n",
(ULONG)StackAddr, NumRegs));
//
// The compiler does not push registers in a consistent
// order and FPO information only indicates the total
// number of registers pushed, not their order. This
// function searches the stack locations where registers
// are stored and tries to find which one is EBP.
// It searches the function code for pushes and
// tries to use that information to help the stack
// analysis.
//
// If this routine fails it just returns the base
// of the register save area.
//
// Read the beginning of the function for code analysis.
if (sizeof(Code) < FuncSize)
{
CodeLen = sizeof(Code);
}
else
{
CodeLen = FuncSize;
}
if (!ReadMemory(Process, FuncAddr, Code, CodeLen, &CodeLen))
{
WDB((" unable to read code, use %X\n", (ULONG)StackAddr));
return StackAddr;
}
// Scan the code for normal prologue operations like
// sub esp, push reg and mov reg. This code only
// handles a very limited set of instructions.
Depth = 0;
for (i = 0; i < CodeLen; i++)
{
if (Code[i] == 0x83 && Code[i+1] == 0xec)
{
// sub esp, imm8
// Skip past.
i += 2;
}
else if (Code[i] == 0x8b)
{
BYTE Mod, Rm;
// mov reg32, r/m32
i++;
Mod = Code[i] >> 6;
Rm = Code[i] & 0x7;
switch(Mod)
{
case 0:
if (Rm == 4)
{
i++;
}
else if (Rm == 5)
{
i += 4;
}
break;
case 1:
i += 1 + (Rm == 4 ? 1 : 0);
break;
case 2:
i += 4 + (Rm == 4 ? 1 : 0);
break;
case 3:
// No extra bytes.
break;
}
}
else if (Code[i] >= 0x50 && Code[i] <= 0x57)
{
// push rd
if (Code[i] == 0x55)
{
// push ebp
// Found it.
StackAddr += (NumRegs - Depth - 1) * STACK_SIZE;
WDB((" found ebp at %X\n", (ULONG)StackAddr));
return StackAddr;
}
else
{
// Consumes a stack slot.
Depth++;
}
}
else
{
// Unhandled code, fail.
return StackAddr;
}
}
// Didn't find a push ebp, fail.
WDB((" no ebp, use %X\n", (ULONG)StackAddr));
return StackAddr;
}
BOOL
GetFpoFrameBase(
HANDLE Process,
LPSTACKFRAME64 StackFrame,
PFPO_DATA pFpoData,
BOOL fFirstFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase
)
{
DWORD Addr32;
X86_KTRAP_FRAME TrapFrame;
DWORD64 OldFrameAddr;
DWORD64 FrameAddr;
DWORD64 StackAddr;
DWORD64 ModuleBase;
DWORD64 FuncAddr;
DWORD cb;
DWORD64 StoredEbp;
PFPO_DATA PreviousFpoData = (PFPO_DATA)StackFrame->FuncTableEntry;
//
// calculate the address of the beginning of the function
//
ModuleBase = GetModuleBase( Process, StackFrame->AddrPC.Offset );
#ifdef NO_TEMPORARY_MODULE_BASE_HACK
if (!ModuleBase) {
return FALSE;
}
FuncAddr = ModuleBase+pFpoData->ulOffStart;
#else
// XXX drewb - Currently we have a couple of fake
// symbols for the new fast syscall code in the
// user shared data area. There is no module
// associated with that area but we still want
// to return FPO data for the stubs to get
// stack traces to work well, so hack this check.
// Later we'll do a full fake module.
if (!ModuleBase) {
FuncAddr = pFpoData->ulOffStart;
} else {
FuncAddr = ModuleBase+pFpoData->ulOffStart;
}
#endif
WDB((" GetFpoFrameBase: PC %X, Func %X, first %d, FPO %p [%d,%d,%d]\n",
(ULONG)StackFrame->AddrPC.Offset, (ULONG)FuncAddr,
fFirstFrame, pFpoData, pFpoData->cdwParams, pFpoData->cdwLocals,
pFpoData->cbRegs));
//
// If this isn't the first/current frame then we can add back the count
// bytes of locals and register pushed before beginning to search for
// vEBP. If we are beyond prolog we can add back the count bytes of locals
// and registers pushed as well. If it is the first frame and EIP is
// greater than the address of the function then the SUB for locals has
// been done so we can add them back before beginning the search. If we
// are right on the function then we will need to start our search at ESP.
//
if ( !fFirstFrame ) {
OldFrameAddr = StackFrame->AddrFrame.Offset;
FrameAddr = 0;
//
// if this is a non-fpo or trap frame, get the frame base now:
//
if (pFpoData->cbFrame != FRAME_FPO) {
if (!PreviousFpoData || PreviousFpoData->cbFrame == FRAME_NONFPO) {
//
// previous frame base is ebp and points to this frame's ebp
//
ReadMemory(Process,
OldFrameAddr,
&Addr32,
sizeof(DWORD),
&cb);
FrameAddr = (DWORD64)(LONG64)(LONG)Addr32;
}
//
// if that didn't work, try for a saved ebp
//
if (!FrameAddr && SAVE_EBP(StackFrame)) {
FrameAddr = SAVE_EBP(StackFrame);
WDB((" non-FPO using %X\n", (ULONG)FrameAddr));
}
//
// this is not an FPO frame, so the saved EBP can only have come
// from this or a lower frame.
//
SAVE_EBP(StackFrame) = 0;
}
//
// still no frame base - either this frame is fpo, or we couldn't
// follow the ebp chain.
//
if (FrameAddr == 0) {
FrameAddr = OldFrameAddr;
//
// skip over return address from prev frame
//
FrameAddr += FRAME_SIZE;
//
// skip over this frame's locals and saved regs
//
FrameAddr += ( pFpoData->cdwLocals * STACK_SIZE );
FrameAddr += ( pFpoData->cbRegs * STACK_SIZE );
if (PreviousFpoData) {
//
// if the previous frame had an fpo record, we can account
// for its parameters
//
FrameAddr += PreviousFpoData->cdwParams * STACK_SIZE;
}
}
//
// if this is an FPO frame
// and the previous frame was non-fpo,
// and this frame passed the inherited ebp to the previous frame,
// save its ebp
//
// (if this frame used ebp, SAVE_EBP will be set after verifying
// the frame base)
//
if (pFpoData->cbFrame == FRAME_FPO &&
(!PreviousFpoData || PreviousFpoData->cbFrame == FRAME_NONFPO) &&
!pFpoData->fUseBP) {
SAVE_EBP(StackFrame) = 0;
if (ReadMemory(Process,
OldFrameAddr,
&Addr32,
sizeof(DWORD),
&cb)) {
SAVE_EBP(StackFrame) = (DWORD64)(LONG64)(LONG)Addr32;
WDB((" pass-through FP %X\n", Addr32));
} else {
WDB((" clear ebp\n"));
}
}
} else {
OldFrameAddr = StackFrame->AddrFrame.Offset;
if (pFpoData->cbFrame == FRAME_FPO && !pFpoData->fUseBP) {
//
// this frame didn't use EBP, so it actually belongs
// to a non-FPO frame further up the stack. Stash
// it in the save area for the next frame.
//
SAVE_EBP(StackFrame) = StackFrame->AddrFrame.Offset;
WDB((" first non-ebp save %X\n", (ULONG)SAVE_EBP(StackFrame)));
}
if (pFpoData->cbFrame == FRAME_TRAP ||
pFpoData->cbFrame == FRAME_TSS) {
FrameAddr = StackFrame->AddrFrame.Offset;
} else if (StackFrame->AddrPC.Offset == FuncAddr) {
FrameAddr = StackFrame->AddrStack.Offset;
} else if (StackFrame->AddrPC.Offset >= FuncAddr+pFpoData->cbProlog) {
FrameAddr = StackFrame->AddrStack.Offset +
( pFpoData->cdwLocals * STACK_SIZE ) +
( pFpoData->cbRegs * STACK_SIZE );
} else {
FrameAddr = StackFrame->AddrStack.Offset +
( pFpoData->cdwLocals * STACK_SIZE );
}
}
if (pFpoData->cbFrame == FRAME_TRAP) {
//
// read a kernel mode trap frame from the stack
//
if (!ReadTrapFrame( Process,
FrameAddr,
&TrapFrame,
ReadMemory )) {
return FALSE;
}
SAVE_TRAP(StackFrame) = FrameAddr;
TRAP_EDITED(StackFrame) = TrapFrame.SegCs & X86_FRAME_EDITED;
StackFrame->AddrReturn.Offset = (DWORD64)(LONG64)(LONG)(TrapFrame.Eip);
StackFrame->AddrReturn.Mode = AddrModeFlat;
StackFrame->AddrReturn.Segment = 0;
return TRUE;
}
if (pFpoData->cbFrame == FRAME_TSS) {
//
// translate a tss to a kernel mode trap frame
//
StackAddr = FrameAddr;
TaskGate2TrapFrame( Process, X86_KGDT_TSS, &TrapFrame, &StackAddr, ReadMemory );
TRAP_TSS(StackFrame) = X86_KGDT_TSS;
SAVE_TRAP(StackFrame) = StackAddr;
StackFrame->AddrReturn.Offset = (DWORD64)(LONG64)(LONG)(TrapFrame.Eip);
StackFrame->AddrReturn.Mode = AddrModeFlat;
StackFrame->AddrReturn.Segment = 0;
return TRUE;
}
if ((pFpoData->cbFrame != FRAME_FPO) &&
(pFpoData->cbFrame != FRAME_NONFPO) ) {
//
// we either have a compiler or linker problem, or possibly
// just simple data corruption.
//
return FALSE;
}
//
// go look for a return address. this is done because, even though
// we have subtracted all that we can from the frame pointer it is
// possible that there is other unknown data on the stack. by
// searching for the return address we are able to find the base of
// the fpo frame.
//
FrameAddr = SearchForReturnAddress( Process,
FrameAddr,
FuncAddr,
pFpoData->cbProcSize,
ReadMemory,
GetModuleBase,
PreviousFpoData != NULL
);
if (!FrameAddr) {
return FALSE;
}
if (pFpoData->fUseBP && pFpoData->cbFrame == FRAME_FPO) {
//
// this function used ebp as a general purpose register, but
// before doing so it saved ebp on the stack.
//
// we must retrieve this ebp and save it for possible later
// use if we encounter a non-fpo frame
//
if (fFirstFrame && StackFrame->AddrPC.Offset < FuncAddr+pFpoData->cbProlog) {
SAVE_EBP(StackFrame) = OldFrameAddr;
WDB((" first use save FP %X\n", (ULONG)OldFrameAddr));
} else {
SAVE_EBP(StackFrame) = 0;
// FPO information doesn't indicate which of the saved
// registers is EBP and the compiler doesn't push in a
// consistent way. Scan the register slots of the
// stack for something that looks OK.
StackAddr = FrameAddr -
( ( pFpoData->cbRegs + pFpoData->cdwLocals ) * STACK_SIZE );
StackAddr = SearchForFramePointer( Process,
StackAddr,
pFpoData->cbRegs,
FuncAddr,
pFpoData->cbProcSize,
ReadMemory
);
if (StackAddr &&
ReadMemory(Process,
StackAddr,
&Addr32,
sizeof(DWORD),
&cb)) {
SAVE_EBP(StackFrame) = (DWORD64)(LONG64)(LONG)Addr32;
WDB((" use search save %X from %X\n", Addr32,
(ULONG)StackAddr));
} else {
WDB((" use clear ebp\n"));
}
}
}
//
// subtract the size for an ebp register if one had
// been pushed. this is done because the frames that
// are virtualized need to appear as close to a real frame
// as possible.
//
StackFrame->AddrFrame.Offset = FrameAddr - STACK_SIZE;
return TRUE;
}
BOOL
ReadTrapFrame(
HANDLE Process,
DWORD64 TrapFrameAddress,
PX86_KTRAP_FRAME TrapFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
)
{
DWORD cb;
if (!ReadMemory(Process,
TrapFrameAddress,
TrapFrame,
sizeof(*TrapFrame),
&cb)) {
return FALSE;
}
if (cb < sizeof(*TrapFrame)) {
if (cb < sizeof(*TrapFrame) - 20) {
//
// shorter then the smallest possible frame type
//
return FALSE;
}
if ((TrapFrame->SegCs & 1) && cb < sizeof(*TrapFrame) - 16 ) {
//
// too small for inter-ring frame
//
return FALSE;
}
if (TrapFrame->EFlags & X86_EFLAGS_V86_MASK) {
//
// too small for V86 frame
//
return FALSE;
}
}
return TRUE;
}
BOOL
GetSelector(
HANDLE Process,
USHORT Processor,
PX86_DESCRIPTOR_TABLE_ENTRY pDescriptorTableEntry,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
)
{
ULONG_PTR Address;
PVOID TableBase;
USHORT TableLimit;
ULONG Index;
X86_LDT_ENTRY Descriptor;
ULONG bytesread;
//
// Fetch the address and limit of the GDT
//
Address = (ULONG_PTR)&(((PX86_KSPECIAL_REGISTERS)0)->Gdtr.Base);
ReadMemory( Process, Address, &TableBase, sizeof(TableBase), (LPDWORD)-1 );
Address = (ULONG_PTR)&(((PX86_KSPECIAL_REGISTERS)0)->Gdtr.Limit);
ReadMemory( Process, Address, &TableLimit, sizeof(TableLimit), (LPDWORD)-1 );
//
// Find out whether this is a GDT or LDT selector
//
if (pDescriptorTableEntry->Selector & 0x4) {
//
// This is an LDT selector, so we reload the TableBase and TableLimit
// with the LDT's Base & Limit by loading the descriptor for the
// LDT selector.
//
if (!ReadMemory(Process,
(ULONG64)TableBase+X86_KGDT_LDT,
&Descriptor,
sizeof(Descriptor),
&bytesread)) {
return FALSE;
}
TableBase = (PVOID)(DWORD_PTR)((ULONG)Descriptor.BaseLow + // Sundown: zero-extension from ULONG to PVOID.
((ULONG)Descriptor.HighWord.Bits.BaseMid << 16) +
((ULONG)Descriptor.HighWord.Bytes.BaseHi << 24));
TableLimit = Descriptor.LimitLow; // LDT can't be > 64k
if(Descriptor.HighWord.Bits.Granularity) {
//
// I suppose it's possible, to have an
// LDT with page granularity.
//
TableLimit <<= X86_PAGE_SHIFT;
}
}
Index = (USHORT)(pDescriptorTableEntry->Selector) & ~0x7;
// Irrelevant bits
//
// Check to make sure that the selector is within the table bounds
//
if (Index >= TableLimit) {
//
// Selector is out of table's bounds
//
return FALSE;
}
if (!ReadMemory(Process,
(ULONG64)TableBase+Index,
&(pDescriptorTableEntry->Descriptor),
sizeof(pDescriptorTableEntry->Descriptor),
&bytesread)) {
return FALSE;
}
return TRUE;
}
BOOL
TaskGate2TrapFrame(
HANDLE Process,
USHORT TaskRegister,
PX86_KTRAP_FRAME TrapFrame,
PULONG64 off,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
)
{
X86_DESCRIPTOR_TABLE_ENTRY desc;
ULONG bytesread;
struct {
ULONG r1[8];
ULONG Eip;
ULONG EFlags;
ULONG Eax;
ULONG Ecx;
ULONG Edx;
ULONG Ebx;
ULONG Esp;
ULONG Ebp;
ULONG Esi;
ULONG Edi;
ULONG Es;
ULONG Cs;
ULONG Ss;
ULONG Ds;
ULONG Fs;
ULONG Gs;
} TaskState;
//
// Get the task register
//
desc.Selector = TaskRegister;
if (!GetSelector(Process, 0, &desc, ReadMemory)) {
return FALSE;
}
if (desc.Descriptor.HighWord.Bits.Type != 9 &&
desc.Descriptor.HighWord.Bits.Type != 0xb) {
//
// not a 32bit task descriptor
//
return FALSE;
}
//
// Read in Task State Segment
//
*off = ((ULONG)desc.Descriptor.BaseLow +
((ULONG)desc.Descriptor.HighWord.Bytes.BaseMid << 16) +
((ULONG)desc.Descriptor.HighWord.Bytes.BaseHi << 24) );
if (!ReadMemory(Process,
(ULONG64)(LONG64)(LONG)(*off),
&TaskState,
sizeof(TaskState),
&bytesread)) {
return FALSE;
}
//
// Move fields from Task State Segment to TrapFrame
//
ZeroMemory( TrapFrame, sizeof(*TrapFrame) );
TrapFrame->Eip = TaskState.Eip;
TrapFrame->EFlags = TaskState.EFlags;
TrapFrame->Eax = TaskState.Eax;
TrapFrame->Ecx = TaskState.Ecx;
TrapFrame->Edx = TaskState.Edx;
TrapFrame->Ebx = TaskState.Ebx;
TrapFrame->Ebp = TaskState.Ebp;
TrapFrame->Esi = TaskState.Esi;
TrapFrame->Edi = TaskState.Edi;
TrapFrame->SegEs = TaskState.Es;
TrapFrame->SegCs = TaskState.Cs;
TrapFrame->SegDs = TaskState.Ds;
TrapFrame->SegFs = TaskState.Fs;
TrapFrame->SegGs = TaskState.Gs;
TrapFrame->HardwareEsp = TaskState.Esp;
TrapFrame->HardwareSegSs = TaskState.Ss;
return TRUE;
}
BOOL
ProcessTrapFrame(
HANDLE Process,
LPSTACKFRAME64 StackFrame,
PFPO_DATA pFpoData,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess
)
{
X86_KTRAP_FRAME TrapFrame;
DWORD64 StackAddr;
if (((PFPO_DATA)StackFrame->FuncTableEntry)->cbFrame == FRAME_TSS) {
StackAddr = SAVE_TRAP(StackFrame);
TaskGate2TrapFrame( Process, X86_KGDT_TSS, &TrapFrame, &StackAddr, ReadMemory );
} else {
if (!ReadTrapFrame( Process,
SAVE_TRAP(StackFrame),
&TrapFrame,
ReadMemory)) {
SAVE_TRAP(StackFrame) = 0;
return FALSE;
}
}
pFpoData = (PFPO_DATA)
FunctionTableAccess(Process,
(DWORD64)(LONG64)(LONG)TrapFrame.Eip);
if (!pFpoData) {
StackFrame->AddrFrame.Offset = (DWORD64)(LONG64)(LONG)TrapFrame.Ebp;
SAVE_EBP(StackFrame) = 0;
} else {
if ((TrapFrame.SegCs & X86_MODE_MASK) ||
(TrapFrame.EFlags & X86_EFLAGS_V86_MASK)) {
//
// User-mode frame, real value of Esp is in HardwareEsp
//
StackFrame->AddrFrame.Offset = (DWORD64)(LONG64)(LONG)(TrapFrame.HardwareEsp - STACK_SIZE);
StackFrame->AddrStack.Offset = (DWORD64)(LONG64)(LONG)TrapFrame.HardwareEsp;
} else {
//
// We ignore if Esp has been edited for now, and we will print a
// separate line indicating this later.
//
// Calculate kernel Esp
//
if (((PFPO_DATA)StackFrame->FuncTableEntry)->cbFrame == FRAME_TRAP) {
//
// plain trap frame
//
if ((TrapFrame.SegCs & X86_FRAME_EDITED) == 0) {
StackFrame->AddrStack.Offset = (DWORD64)(LONG64)(LONG)TrapFrame.TempEsp;
} else {
StackFrame->AddrStack.Offset = (ULONG64)(LONG64)(LONG_PTR)
(& (((PX86_KTRAP_FRAME)SAVE_TRAP(StackFrame))->HardwareEsp) );
}
} else {
//
// tss converted to trap frame
//
StackFrame->AddrStack.Offset = (DWORD64)(LONG64)(LONG)TrapFrame.HardwareEsp;
}
}
}
StackFrame->AddrFrame.Offset = (DWORD64)(LONG64)(LONG)TrapFrame.Ebp;
StackFrame->AddrPC.Offset = (DWORD64)(LONG64)(LONG)TrapFrame.Eip;
SAVE_TRAP(StackFrame) = 0;
StackFrame->FuncTableEntry = pFpoData;
return TRUE;
}
BOOL
IsFarCall(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
BOOL *Ok,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
BOOL fFar = FALSE;
ULONG cb;
ADDRESS64 Addr;
*Ok = TRUE;
if (StackFrame->AddrFrame.Mode == AddrModeFlat) {
DWORD dwStk[ 3 ];
//
// If we are working with 32 bit offset stack pointers, we
// will say that the return address if far if the address
// treated as a FAR pointer makes any sense, if not then
// it must be a near return
//
if (StackFrame->AddrFrame.Offset &&
DoMemoryRead( &StackFrame->AddrFrame, dwStk, sizeof(dwStk), &cb )) {
//
// See if segment makes sense
//
Addr.Offset = (DWORD64)(LONG64)(LONG)(dwStk[1]);
Addr.Segment = (WORD)dwStk[2];
Addr.Mode = AddrModeFlat;
if (TranslateAddress( Process, Thread, &Addr ) && Addr.Offset) {
fFar = TRUE;
}
} else {
*Ok = FALSE;
}
} else {
WORD wStk[ 3 ];
//
// For 16 bit (i.e. windows WOW code) we do the following tests
// to check to see if an address is a far return value.
//
// 1. if the saved BP register is odd then it is a far
// return values
// 2. if the address treated as a far return value makes sense
// then it is a far return value
// 3. else it is a near return value
//
if (StackFrame->AddrFrame.Offset &&
DoMemoryRead( &StackFrame->AddrFrame, wStk, 6, &cb )) {
if ( wStk[0] & 0x0001 ) {
fFar = TRUE;
} else {
//
// See if segment makes sense
//
Addr.Offset = wStk[1];
Addr.Segment = wStk[2];
Addr.Mode = AddrModeFlat;
if (TranslateAddress( Process, Thread, &Addr ) && Addr.Offset) {
fFar = TRUE;
}
}
} else {
*Ok = FALSE;
}
}
return fFar;
}
BOOL
SetNonOff32FrameAddress(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
BOOL fFar;
WORD Stk[ 3 ];
ULONG cb;
BOOL Ok;
fFar = IsFarCall( Process, Thread, StackFrame, &Ok, ReadMemory, TranslateAddress );
if (!Ok) {
return FALSE;
}
if (!DoMemoryRead( &StackFrame->AddrFrame, Stk, (DWORD)(fFar ? FRAME_SIZE1632 : FRAME_SIZE16), &cb )) {
return FALSE;
}
if (SAVE_EBP(StackFrame) > 0) {
StackFrame->AddrFrame.Offset = SAVE_EBP(StackFrame) & 0xffff;
StackFrame->AddrPC.Offset = Stk[1];
if (fFar) {
StackFrame->AddrPC.Segment = Stk[2];
}
SAVE_EBP(StackFrame) = 0;
} else {
if (Stk[1] == 0) {
return FALSE;
} else {
StackFrame->AddrFrame.Offset = Stk[0];
StackFrame->AddrFrame.Offset &= 0xFFFFFFFE;
StackFrame->AddrPC.Offset = Stk[1];
if (fFar) {
StackFrame->AddrPC.Segment = Stk[2];
}
}
}
return TRUE;
}
VOID
X86ReadFunctionParameters(
HANDLE Process,
ULONG64 Offset,
LPSTACKFRAME64 Frame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
)
{
DWORD Params[4];
DWORD Done;
if (ReadMemory(Process, Offset, Params, sizeof(Params), &Done)) {
Frame->Params[0] = (DWORD64)(LONG64)(LONG)(Params[0]);
Frame->Params[1] = (DWORD64)(LONG64)(LONG)(Params[1]);
Frame->Params[2] = (DWORD64)(LONG64)(LONG)(Params[2]);
Frame->Params[3] = (DWORD64)(LONG64)(LONG)(Params[3]);
} else {
Frame->Params[0] =
Frame->Params[1] =
Frame->Params[2] =
Frame->Params[3] = 0;
}
}
VOID
GetFunctionParameters(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
BOOL Ok;
ADDRESS64 ParmsAddr;
ParmsAddr = StackFrame->AddrFrame;
//
// calculate the frame size
//
if (StackFrame->AddrPC.Mode == AddrModeFlat) {
ParmsAddr.Offset += FRAME_SIZE;
} else
if ( IsFarCall( Process, Thread, StackFrame, &Ok,
ReadMemory, TranslateAddress ) ) {
StackFrame->Far = TRUE;
ParmsAddr.Offset += FRAME_SIZE1632;
} else {
StackFrame->Far = FALSE;
ParmsAddr.Offset += STACK_SIZE;
}
//
// read the memory
//
if (ParmsAddr.Mode != AddrModeFlat) {
TranslateAddress( Process, Thread, &ParmsAddr );
}
X86ReadFunctionParameters(Process, ParmsAddr.Offset, StackFrame,
ReadMemory);
}
VOID
GetReturnAddress(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess
)
{
ULONG cb;
DWORD stack[1];
if (SAVE_TRAP(StackFrame)) {
//
// if a trap frame was encountered then
// the return address was already calculated
//
return;
}
WDB((" GetReturnAddress: SP %X, FP %X\n",
(ULONG)StackFrame->AddrStack.Offset,
(ULONG)StackFrame->AddrFrame.Offset));
if (StackFrame->AddrPC.Mode == AddrModeFlat) {
ULONG64 CallOffset;
PFPO_DATA CallFpo;
ADDRESS64 FrameRet;
FPO_DATA SaveCallFpo;
PFPO_DATA RetFpo;
//
// read the frame from the process's memory
//
FrameRet = StackFrame->AddrFrame;
FrameRet.Offset += STACK_SIZE;
if (!DoMemoryRead( &FrameRet, stack, STACK_SIZE, &cb ) ||
cb < STACK_SIZE) {
//
// if we could not read the memory then set
// the return address to zero so that the stack trace
// will terminate
//
stack[0] = 0;
}
StackFrame->AddrReturn.Offset = (DWORD64)(LONG64)(LONG)(stack[0]);
WDB((" read %X\n", stack[0]));
//
// Calls of __declspec(noreturn) functions may not have any
// code after them to return to since the compiler knows
// that the function will not return. This can confuse
// stack traces because the return address will lie outside
// of the function's address range and FPO data will not
// be looked up correctly. Check and see if the return
// address falls outside of the calling function and, if so,
// adjust the return address back by one byte. It'd be
// better to adjust it back to the call itself so that
// the return address points to valid code but
// backing up in X86 assembly is more or less impossible.
//
CallOffset = StackFrame->AddrReturn.Offset - 1;
CallFpo = (PFPO_DATA)FunctionTableAccess(Process, CallOffset);
if (CallFpo != NULL) {
SaveCallFpo = *CallFpo;
}
RetFpo = (PFPO_DATA)
FunctionTableAccess(Process, StackFrame->AddrReturn.Offset);
if (CallFpo != NULL) {
if (RetFpo == NULL ||
memcmp(&SaveCallFpo, RetFpo, sizeof(SaveCallFpo))) {
StackFrame->AddrReturn.Offset = CallOffset;
}
} else if (RetFpo != NULL) {
StackFrame->AddrReturn.Offset = CallOffset;
}
} else {
StackFrame->AddrReturn.Offset = StackFrame->AddrPC.Offset;
StackFrame->AddrReturn.Segment = StackFrame->AddrPC.Segment;
}
}
BOOL
WalkX86_Fpo_Fpo(
HANDLE Process,
HANDLE Thread,
PFPO_DATA pFpoData,
LPSTACKFRAME64 StackFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
BOOL rval;
WDB((" WalkFF:\n"));
rval = GetFpoFrameBase( Process,
StackFrame,
pFpoData,
FALSE,
ReadMemory,
GetModuleBase );
StackFrame->FuncTableEntry = pFpoData;
return rval;
}
BOOL
WalkX86_Fpo_NonFpo(
HANDLE Process,
HANDLE Thread,
PFPO_DATA pFpoData,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
DWORD stack[FRAME_SIZE+STACK_SIZE];
DWORD cb;
DWORD64 FrameAddr;
DWORD64 FuncAddr;
DWORD FuncSize;
BOOL AcceptUnreadableCallsite = FALSE;
WDB((" WalkFN:\n"));
//
// if the previous frame was an seh frame then we must
// retrieve the "real" frame pointer for this frame.
// the seh function pushed the frame pointer last.
//
if (((PFPO_DATA)StackFrame->FuncTableEntry)->fHasSEH) {
if (DoMemoryRead( &StackFrame->AddrFrame, stack, FRAME_SIZE+STACK_SIZE, &cb )) {
StackFrame->AddrFrame.Offset = (DWORD64)(LONG64)(LONG)(stack[2]);
StackFrame->AddrStack.Offset = (DWORD64)(LONG64)(LONG)(stack[2]);
WalkX86Init(Process,
Thread,
StackFrame,
ContextRecord,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress);
return TRUE;
}
}
//
// If a prior frame has stored this frame's EBP, just use it.
//
if (SAVE_EBP(StackFrame)) {
StackFrame->AddrFrame.Offset = SAVE_EBP(StackFrame);
FrameAddr = StackFrame->AddrFrame.Offset + 4;
AcceptUnreadableCallsite = TRUE;
WDB((" use %X\n", (ULONG)FrameAddr));
} else {
//
// Skip past the FPO frame base and parameters.
//
StackFrame->AddrFrame.Offset +=
(FRAME_SIZE + (((PFPO_DATA)StackFrame->FuncTableEntry)->cdwParams * 4));
//
// Now this is pointing to the bottom of the non-FPO frame.
// If the frame has an fpo record, use it:
//
if (pFpoData) {
FrameAddr = StackFrame->AddrFrame.Offset +
4* (pFpoData->cbRegs + pFpoData->cdwLocals);
AcceptUnreadableCallsite = TRUE;
} else {
//
// We don't know if the non-fpo frame has any locals, but
// skip past the EBP anyway.
//
FrameAddr = StackFrame->AddrFrame.Offset + 4;
}
WDB((" compute %X\n", (ULONG)FrameAddr));
}
//
// at this point we may not be sitting at the base of the frame
// so we now search for the return address and then subtract the
// size of the frame pointer and use that address as the new base.
//
if (pFpoData) {
FuncAddr = GetModuleBase(Process,StackFrame->AddrPC.Offset) + pFpoData->ulOffStart;
FuncSize = pFpoData->cbProcSize;
} else {
FuncAddr = StackFrame->AddrPC.Offset - MAX_CALL;
FuncSize = MAX_CALL;
}
FrameAddr = SearchForReturnAddress( Process,
FrameAddr,
FuncAddr,
FuncSize,
ReadMemory,
GetModuleBase,
AcceptUnreadableCallsite
);
if (FrameAddr) {
StackFrame->AddrFrame.Offset = FrameAddr - STACK_SIZE;
}
if (!DoMemoryRead( &StackFrame->AddrFrame, stack, FRAME_SIZE, &cb )) {
//
// a failure means that we likely have a bad address.
// returning zero will terminate that stack trace.
//
stack[0] = 0;
}
SAVE_EBP(StackFrame) = (DWORD64)(LONG64)(LONG)(stack[0]);
WDB((" save %X\n", stack[0]));
StackFrame->FuncTableEntry = pFpoData;
return TRUE;
}
BOOL
WalkX86_NonFpo_Fpo(
HANDLE Process,
HANDLE Thread,
PFPO_DATA pFpoData,
LPSTACKFRAME64 StackFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
BOOL rval;
WDB((" WalkNF:\n"));
rval = GetFpoFrameBase( Process,
StackFrame,
pFpoData,
FALSE,
ReadMemory,
GetModuleBase );
StackFrame->FuncTableEntry = pFpoData;
return rval;
}
BOOL
WalkX86_NonFpo_NonFpo(
HANDLE Process,
HANDLE Thread,
PFPO_DATA pFpoData,
LPSTACKFRAME64 StackFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
DWORD stack[FRAME_SIZE*4];
DWORD cb;
WDB((" WalkNN:\n"));
//
// a previous function in the call stack was a fpo function that used ebp as
// a general purpose register. ul contains the ebp value that was good before
// that function executed. it is that ebp that we want, not what was just read
// from the stack. what was just read from the stack is totally bogus.
//
if (SAVE_EBP(StackFrame)) {
StackFrame->AddrFrame.Offset = SAVE_EBP(StackFrame);
SAVE_EBP(StackFrame) = 0;
} else {
//
// read the first 2 dwords off the stack
//
if (!DoMemoryRead( &StackFrame->AddrFrame, stack, FRAME_SIZE, &cb )) {
return FALSE;
}
StackFrame->AddrFrame.Offset = (DWORD64)(LONG64)(LONG)(stack[0]);
}
StackFrame->FuncTableEntry = pFpoData;
return TRUE;
}
BOOL
X86ApplyFrameData(
HANDLE Process,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
BOOL FirstFrame,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase
)
{
IDiaFrameData* DiaFrame;
BOOL Succ = FALSE;
// If we can get VC7-style frame data just execute
// the frame data program to unwind the stack.
// If weren't given a context record we cannot use
// the new VC7 unwind information as we have nowhere
// to save intermediate context values.
if (StackFrame->AddrPC.Mode != AddrModeFlat ||
!g_vc7fpo ||
!ContextRecord ||
!diaGetFrameData(Process, StackFrame->AddrPC.Offset, &DiaFrame))
{
return FALSE;
}
if (FirstFrame)
{
ContextRecord->Ebp = (ULONG)StackFrame->AddrFrame.Offset;
ContextRecord->Esp = (ULONG)StackFrame->AddrStack.Offset;
ContextRecord->Eip = (ULONG)StackFrame->AddrPC.Offset;
}
WDB((" Applying frame data program for PC %X SP %X FP %X\n",
ContextRecord->Eip, ContextRecord->Esp, ContextRecord->Ebp));
//
// execute() does not currently work when the PC is
// within the function prologue. This should only
// happen on calls from WalkX86Init, in which case the
// normal failure path here where the non-frame-data
// code will be executed is correct as that will handle
// normal prologue code.
//
X86WalkFrame WalkFrame(Process, ContextRecord,
ReadMemory, GetModuleBase);
Succ = DiaFrame->execute(&WalkFrame) == S_OK;
if (Succ) {
WDB((" Result PC %X SP %X FP %X\n",
ContextRecord->Eip, ContextRecord->Esp, ContextRecord->Ebp));
StackFrame->AddrStack.Mode = AddrModeFlat;
StackFrame->AddrStack.Offset = EXTEND64(ContextRecord->Esp);
StackFrame->AddrFrame.Mode = AddrModeFlat;
// The frame value we want to return is the frame value
// used for the function that was just unwound, not
// the current value of EBP. After the unwind the current
// value of EBP is the caller's EBP, not the callee's
// frame. Instead we always set the callee's frame to
// the offset beyond where the return address would be
// as that's where the frame will be in a normal non-FPO
// function and where we fake it as being for FPO functions.
// We save the true EBP away for future frame use.
StackFrame->AddrFrame.Offset =
StackFrame->AddrStack.Offset - FRAME_SIZE;
StackFrame->AddrReturn.Offset = EXTEND64(ContextRecord->Eip);
SAVE_EBP(StackFrame) = ContextRecord->Ebp;
// Do not return a pointer to an FPO record as
// no such data was retrieved.
StackFrame->FuncTableEntry = NULL;
X86ReadFunctionParameters(Process, StackFrame->AddrStack.Offset,
StackFrame, ReadMemory);
} else {
WDB((" Apply failed\n"));
}
DiaFrame->Release();
return Succ;
}
VOID
X86UpdateContextFromFrame(
HANDLE Process,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
)
{
ULONG Ebp;
ULONG Done;
if (StackFrame->AddrPC.Mode != AddrModeFlat ||
!ContextRecord) {
return;
}
ContextRecord->Esp = (ULONG)StackFrame->AddrFrame.Offset + FRAME_SIZE;
ContextRecord->Eip = (ULONG)StackFrame->AddrReturn.Offset;
if (StackFrame->FuncTableEntry) {
PFPO_DATA pFpoData = (PFPO_DATA)StackFrame->FuncTableEntry;
ULONG64 FrameAddr;
Ebp = 0;
if (pFpoData->cbFrame == FRAME_NONFPO) {
FrameAddr = StackFrame->AddrFrame.Offset;
//
// frame base is ebp and points to next frame's ebp
//
ReadMemory(Process,
FrameAddr,
&Ebp,
sizeof(DWORD),
&Done);
if (Ebp == 0) {
Ebp = (ULONG) FrameAddr + STACK_SIZE;
}
} else if (pFpoData->fUseBP) {
Ebp = (ULONG)SAVE_EBP(StackFrame);
} else {
Ebp = ContextRecord->Esp;
Ebp += pFpoData->cdwParams * STACK_SIZE;
}
ContextRecord->Ebp = Ebp;
} else {
if (ReadMemory(Process, StackFrame->AddrFrame.Offset,
&Ebp, sizeof(Ebp), &Done) &&
Done == sizeof(Ebp)) {
ContextRecord->Ebp = Ebp;
}
}
}
BOOL
WalkX86Next(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
PFPO_DATA pFpoData = NULL;
BOOL rVal = TRUE;
DWORD64 Address;
DWORD cb;
DWORD64 ThisPC;
DWORD64 ModuleBase;
DWORD64 SystemRangeStart;
WDB(("WalkNext: PC %X, SP %X, FP %X\n",
(ULONG)StackFrame->AddrReturn.Offset,
(ULONG)StackFrame->AddrStack.Offset,
(ULONG)StackFrame->AddrFrame.Offset));
if (g.AppVersion.Revision >= 6) {
SystemRangeStart = (ULONG64)(LONG64)(LONG_PTR)(SYSTEM_RANGE_START(StackFrame));
} else {
//
// This might not really work right with old debuggers, but it keeps
// us from looking off the end of the structure anyway.
//
SystemRangeStart = 0xFFFFFFFF80000000;
}
ThisPC = StackFrame->AddrPC.Offset;
//
// the previous frame's return address is this frame's pc
//
StackFrame->AddrPC = StackFrame->AddrReturn;
if (StackFrame->AddrPC.Mode != AddrModeFlat) {
//
// the call stack is from either WOW or a DOS app
//
SetNonOff32FrameAddress( Process,
Thread,
StackFrame,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
goto exit;
}
//
// if the last frame was the usermode callback dispatcher,
// switch over to the kernel stack:
//
ModuleBase = GetModuleBase(Process, ThisPC);
if ((g.AppVersion.Revision >= 4) &&
(CALLBACK_STACK(StackFrame) != 0) &&
(pFpoData = (PFPO_DATA)StackFrame->FuncTableEntry) &&
(CALLBACK_DISPATCHER(StackFrame) == ModuleBase + pFpoData->ulOffStart) ) {
NextCallback:
rVal = FALSE;
//
// find callout frame
//
if ((ULONG64)(LONG64)(LONG_PTR)(CALLBACK_STACK(StackFrame)) >= SystemRangeStart) {
//
// it is the pointer to the stack frame that we want,
// or -1.
Address = (ULONG64)(LONG64)(LONG) CALLBACK_STACK(StackFrame);
} else {
//
// if it is below SystemRangeStart, it is the offset to
// the address in the thread.
// Look up the pointer:
//
rVal = ReadMemory(Process,
(CALLBACK_THREAD(StackFrame) +
CALLBACK_STACK(StackFrame)),
&Address,
sizeof(DWORD),
&cb);
Address = (ULONG64)(LONG64)(LONG)Address;
if (!rVal || Address == 0) {
Address = 0xffffffff;
CALLBACK_STACK(StackFrame) = 0xffffffff;
}
}
if ((Address == 0xffffffff) ||
!(pFpoData = (PFPO_DATA) FunctionTableAccess( Process,
CALLBACK_FUNC(StackFrame))) ) {
rVal = FALSE;
} else {
StackFrame->FuncTableEntry = pFpoData;
StackFrame->AddrPC.Offset = CALLBACK_FUNC(StackFrame) +
pFpoData->cbProlog;
StackFrame->AddrStack.Offset = Address;
ReadMemory(Process,
Address + CALLBACK_FP(StackFrame),
&StackFrame->AddrFrame.Offset,
sizeof(DWORD),
&cb);
StackFrame->AddrFrame.Offset = (ULONG64)(LONG64)(LONG)
StackFrame->AddrFrame.Offset;
ReadMemory(Process,
Address + CALLBACK_NEXT(StackFrame),
&CALLBACK_STACK(StackFrame),
sizeof(DWORD),
&cb);
SAVE_TRAP(StackFrame) = 0;
rVal = WalkX86Init(
Process,
Thread,
StackFrame,
ContextRecord,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
}
return rVal;
}
//
// if there is a trap frame then handle it
//
if (SAVE_TRAP(StackFrame)) {
rVal = ProcessTrapFrame(
Process,
StackFrame,
pFpoData,
ReadMemory,
FunctionTableAccess
);
if (!rVal) {
return rVal;
}
rVal = WalkX86Init(
Process,
Thread,
StackFrame,
ContextRecord,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
return rVal;
}
//
// if the PC address is zero then we're at the end of the stack
//
//if (GetModuleBase(Process, StackFrame->AddrPC.Offset) == 0)
if (StackFrame->AddrPC.Offset < 65536) {
//
// if we ran out of stack, check to see if there is
// a callback stack chain
//
if (g.AppVersion.Revision >= 4 && CALLBACK_STACK(StackFrame) != 0) {
goto NextCallback;
}
return FALSE;
}
//
// If the frame, pc and return address are all identical, then we are
// at the top of the idle loop
//
if ((StackFrame->AddrPC.Offset == StackFrame->AddrReturn.Offset) &&
(StackFrame->AddrPC.Offset == StackFrame->AddrFrame.Offset))
{
return FALSE;
}
if (X86ApplyFrameData(Process, StackFrame, ContextRecord, FALSE,
ReadMemory, GetModuleBase)) {
return TRUE;
}
//
// check to see if the current frame is an fpo frame
//
pFpoData = (PFPO_DATA) FunctionTableAccess(Process, StackFrame->AddrPC.Offset);
if (pFpoData && pFpoData->cbFrame != FRAME_NONFPO) {
if (StackFrame->FuncTableEntry && ((PFPO_DATA)StackFrame->FuncTableEntry)->cbFrame != FRAME_NONFPO) {
rVal = WalkX86_Fpo_Fpo( Process,
Thread,
pFpoData,
StackFrame,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
} else {
rVal = WalkX86_NonFpo_Fpo( Process,
Thread,
pFpoData,
StackFrame,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
}
} else {
if (StackFrame->FuncTableEntry && ((PFPO_DATA)StackFrame->FuncTableEntry)->cbFrame != FRAME_NONFPO) {
rVal = WalkX86_Fpo_NonFpo( Process,
Thread,
pFpoData,
StackFrame,
ContextRecord,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
} else {
rVal = WalkX86_NonFpo_NonFpo( Process,
Thread,
pFpoData,
StackFrame,
ReadMemory,
FunctionTableAccess,
GetModuleBase,
TranslateAddress
);
}
}
exit:
StackFrame->AddrFrame.Mode = StackFrame->AddrPC.Mode;
StackFrame->AddrReturn.Mode = StackFrame->AddrPC.Mode;
GetFunctionParameters( Process, Thread, StackFrame,
ReadMemory, GetModuleBase, TranslateAddress );
GetReturnAddress( Process, Thread, StackFrame,
ReadMemory, GetModuleBase, TranslateAddress,
FunctionTableAccess );
X86UpdateContextFromFrame(Process, StackFrame, ContextRecord, ReadMemory);
return rVal;
}
BOOL
WalkX86Init(
HANDLE Process,
HANDLE Thread,
LPSTACKFRAME64 StackFrame,
PX86_CONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccess,
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
)
{
UCHAR code[3];
DWORD stack[FRAME_SIZE*4];
PFPO_DATA pFpoData = NULL;
ULONG cb;
StackFrame->Virtual = TRUE;
StackFrame->Reserved[0] =
StackFrame->Reserved[1] =
StackFrame->Reserved[2] = 0;
StackFrame->AddrReturn = StackFrame->AddrPC;
if (StackFrame->AddrPC.Mode != AddrModeFlat) {
goto exit;
}
WDB(("WalkInit: PC %X, SP %X, FP %X\n",
(ULONG)StackFrame->AddrPC.Offset, (ULONG)StackFrame->AddrStack.Offset,
(ULONG)StackFrame->AddrFrame.Offset));
if (X86ApplyFrameData(Process, StackFrame, ContextRecord, TRUE,
ReadMemory, GetModuleBase)) {
return TRUE;
}
StackFrame->FuncTableEntry = pFpoData = (PFPO_DATA)
FunctionTableAccess(Process, StackFrame->AddrPC.Offset);
if (pFpoData && pFpoData->cbFrame != FRAME_NONFPO) {
GetFpoFrameBase( Process,
StackFrame,
pFpoData,
TRUE,
ReadMemory,
GetModuleBase );
} else {
//
// this code determines whether eip is in the function prolog
//
if (!DoMemoryRead( &StackFrame->AddrPC, code, 3, &cb )) {
//
// assume a call to a bad address if the memory read fails
//
code[0] = PUSHBP;
}
if ((code[0] == PUSHBP) || (*(LPWORD)&code[0] == MOVBPSP)) {
SAVE_EBP(StackFrame) = StackFrame->AddrFrame.Offset;
StackFrame->AddrFrame.Offset = StackFrame->AddrStack.Offset;
if (StackFrame->AddrPC.Mode != AddrModeFlat) {
StackFrame->AddrFrame.Offset &= 0xffff;
}
if (code[0] == PUSHBP) {
if (StackFrame->AddrPC.Mode == AddrModeFlat) {
StackFrame->AddrFrame.Offset -= STACK_SIZE;
} else {
StackFrame->AddrFrame.Offset -= STACK_SIZE16;
}
}
} else {
//
// read the first 2 dwords off the stack
//
if (DoMemoryRead( &StackFrame->AddrFrame, stack, FRAME_SIZE, &cb )) {
SAVE_EBP(StackFrame) = (ULONG64)(LONG64)(LONG)stack[0];
}
if (StackFrame->AddrPC.Mode != AddrModeFlat) {
StackFrame->AddrFrame.Offset &= 0x0000FFFF;
}
}
}
exit:
StackFrame->AddrFrame.Mode = StackFrame->AddrPC.Mode;
GetFunctionParameters( Process, Thread, StackFrame,
ReadMemory, GetModuleBase, TranslateAddress );
GetReturnAddress( Process, Thread, StackFrame,
ReadMemory, GetModuleBase, TranslateAddress,
FunctionTableAccess );
X86UpdateContextFromFrame(Process, StackFrame, ContextRecord, ReadMemory);
return TRUE;
}