mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1617 lines
40 KiB
1617 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mach.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the x86 specific code for dealing with
|
|
machine dependent issues that invlove registers, instruction
|
|
disassembly, function calling and other interesting things.
|
|
|
|
Author:
|
|
|
|
Jim Schaad (jimsch)
|
|
|
|
Environment:
|
|
|
|
Win32 - User
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
extern CRITICAL_SECTION csContinueQueue;
|
|
|
|
/**********************************************************************/
|
|
|
|
extern LPDM_MSG LpDmMsg;
|
|
|
|
/**********************************************************************/
|
|
|
|
#ifdef WIN32S
|
|
|
|
extern BOOL fCanGetThreadContext;
|
|
|
|
typedef struct tagWin32sSystemDll {
|
|
struct tagWin32sSystemDll * pNext;
|
|
DWORD dwStart;
|
|
DWORD dwEnd;
|
|
} WIN32S_SYSTEM_DLL, * LPWIN32S_SYSTEM_DLL;
|
|
|
|
// sorted list of win32s system dll names. NULL terminated.
|
|
UCHAR * szWin32sSystemDllTable[] = {
|
|
"advapi32.dll",
|
|
"comdlg32.dll",
|
|
"gdi32.dll",
|
|
"kernel32.dll",
|
|
"lz32.dll",
|
|
"ntdll.dll",
|
|
"olecli32.dll",
|
|
"olesrv32.dll",
|
|
"shell32.dll",
|
|
"user32.dll",
|
|
"version.dll",
|
|
"w32skrnl.dll",
|
|
NULL
|
|
};
|
|
|
|
#define WIN32S_LOAD_EPSILON 15 // 15 bytes min between sections
|
|
|
|
DWORD Win32sSystemDllFirst = 0xFFFFFFFF;
|
|
DWORD Win32sSystemDllLast = 0;
|
|
|
|
LPWIN32S_SYSTEM_DLL pWin32sSystemDlls = NULL; // start list empty
|
|
FARPROC Win32sBackTo32 = NULL; // BackTo32 thunk return address
|
|
|
|
#endif // WIN32S
|
|
|
|
#if DBG
|
|
static char * rgszTrace[] = {
|
|
"Trace", "BreakPoint", "Cannot Trace", "Soft Int", "Call"
|
|
};
|
|
#endif // DBG
|
|
|
|
#define MAXL 20L
|
|
|
|
#define BIT20(b) (b & 0x07)
|
|
#define BIT53(b) (b >> 3 & 0x07)
|
|
#define BIT76(b) (b >> 6 & 0x03)
|
|
|
|
static int mod; /* mod of mod/rm byte */
|
|
|
|
|
|
|
|
//
|
|
// Stuff for debug registers
|
|
//
|
|
|
|
|
|
typedef struct _DR7 *PDR7;
|
|
typedef struct _DR7 {
|
|
DWORD L0 : 1;
|
|
DWORD G0 : 1;
|
|
DWORD L1 : 1;
|
|
DWORD G1 : 1;
|
|
DWORD L2 : 1;
|
|
DWORD G2 : 1;
|
|
DWORD L3 : 1;
|
|
DWORD G3 : 1;
|
|
DWORD LE : 1;
|
|
DWORD GE : 1;
|
|
DWORD Pad1 : 3;
|
|
DWORD GD : 1;
|
|
DWORD Pad2 : 1;
|
|
DWORD Pad3 : 1;
|
|
DWORD Rwe0 : 2;
|
|
DWORD Len0 : 2;
|
|
DWORD Rwe1 : 2;
|
|
DWORD Len1 : 2;
|
|
DWORD Rwe2 : 2;
|
|
DWORD Len2 : 2;
|
|
DWORD Rwe3 : 2;
|
|
DWORD Len3 : 2;
|
|
} DR7;
|
|
|
|
|
|
#define RWE_EXEC 0x00
|
|
#define RWE_WRITE 0x01
|
|
#define RWE_RESERVED 0x02
|
|
#define RWE_READWRITE 0x03
|
|
|
|
|
|
DWORD LenMask[ MAX_DEBUG_REG_DATA_SIZE + 1 ] = DEBUG_REG_LENGTH_MASKS;
|
|
|
|
|
|
BOOL
|
|
IsRet(
|
|
HTHDX hthd,
|
|
LPADDR addr
|
|
)
|
|
{
|
|
BYTE instr;
|
|
DWORD cBytes;
|
|
if (!AddrReadMemory( hthd->hprc, hthd, addr, &instr, 1, &cBytes )) {
|
|
return FALSE;
|
|
}
|
|
return ((instr == 0xc2) || (instr == 0xc3) || (instr == 0xca) || (instr == 0xcb));
|
|
}
|
|
|
|
void
|
|
IsCall(
|
|
HTHDX hthd,
|
|
LPADDR lpaddr,
|
|
LPINT lpf,
|
|
BOOL fStepOver
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks to see if the specified instruction is
|
|
a call instruction.
|
|
|
|
Arguments:
|
|
|
|
hthd - Supplies the handle to the current thread
|
|
|
|
lpaddr - Supplies the address to check for the call instruction at
|
|
|
|
lpf - Returns TRUE if is a call instruction
|
|
|
|
fStepOver - Supplies TRUE if doing a step over
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
int mode_32;
|
|
int opsize_32;
|
|
DWORD cBytes;
|
|
DWORD cb;
|
|
char membuf [ MAXL ];
|
|
char *pMem;
|
|
UCHAR opcode;
|
|
int fPrefix;
|
|
int fRepPrefix;
|
|
int ttt;
|
|
int mode;
|
|
int rm;
|
|
BOOL fAddrSet;
|
|
ULONG rgul[2];
|
|
USHORT rgus[2];
|
|
ADDR addrSp;
|
|
|
|
/*
|
|
* If we have already done this work and cached the answer then
|
|
* pick up the answer from the cache. The cache marker is cleared
|
|
* at the start of ProcessDebugEvent.
|
|
*/
|
|
|
|
if (hthd->fIsCallDone) {
|
|
*lpaddr = hthd->addrIsCall;
|
|
*lpf = hthd->iInstrIsCall;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* local addressing mode
|
|
*/
|
|
|
|
mode_32 = opsize_32 = hthd->fAddrOff32;
|
|
|
|
/*
|
|
* Read enough bytes to get the longest possible instruction
|
|
*/
|
|
|
|
if (!AddrReadMemory( hthd->hprc,
|
|
hthd,
|
|
lpaddr,
|
|
membuf,
|
|
MAXL,
|
|
&cBytes) ||
|
|
(cBytes == 0)) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
goto done;
|
|
}
|
|
|
|
DPRINT(1, ("(IsCall?) EIP=%08x Type=", *lpaddr));
|
|
|
|
/*
|
|
* point to begin of instruction
|
|
*/
|
|
|
|
pMem = membuf;
|
|
|
|
/*
|
|
* read and process any prefixes first
|
|
*/
|
|
|
|
fPrefix = TRUE;
|
|
fRepPrefix = FALSE;
|
|
|
|
do {
|
|
opcode = (UCHAR) *pMem++; /* get opcode */
|
|
|
|
/*
|
|
* Operand size prefix
|
|
*/
|
|
|
|
if (opcode == 0x66) {
|
|
opsize_32 = !opsize_32;
|
|
}
|
|
/*
|
|
* Address size prefix
|
|
*/
|
|
|
|
else if (opcode == 0x67) {
|
|
mode_32 = !mode_32;
|
|
}
|
|
/*
|
|
* REP and REPNE prefix
|
|
*/
|
|
else if ((opcode & ~1) == 0xf2) {
|
|
fRepPrefix = TRUE;
|
|
}
|
|
/*
|
|
* LOCK prefix (0xf0)
|
|
* Segment Override (0x26, 0x36, 0x2e, 0x3e, 0x64, 0x65)
|
|
*/
|
|
else if ( opcode != 0xf0 &&
|
|
(opcode & ~0x18) != 0x26 &&
|
|
(opcode & ~1) != 0x64 ) {
|
|
fPrefix = FALSE;
|
|
}
|
|
} while ( fPrefix );
|
|
|
|
/*
|
|
* Now start checking for the instructions which must be treated
|
|
* in a special manner. Any instruction which does not respect
|
|
* the trace bit (either due to flag munging or faulting) needs
|
|
* to be treated specially. Also all call ops need to be treated
|
|
* specially (step in vs step over). Finally interupts need to
|
|
* be treated specially since they could cause faults in 16-bit mode
|
|
*/
|
|
|
|
fAddrSet = FALSE;
|
|
|
|
/*
|
|
* Break point instruction
|
|
*/
|
|
if (opcode == 0xcc) {
|
|
*lpf = INSTR_BREAKPOINT;
|
|
}
|
|
// NOTENOTE -- missing the INTO instruction
|
|
/*
|
|
* all other interrrupt instructions
|
|
*/
|
|
else if (opcode == 0xcd) {
|
|
opcode = (UCHAR) *pMem++;
|
|
|
|
/*
|
|
* Is this really a 2-byte version of INT 3 ?
|
|
*/
|
|
if (opcode == 0x3) {
|
|
*lpf = INSTR_BREAKPOINT;
|
|
}
|
|
/*
|
|
* Is this a funky 16-bit floating point instruction? if so then
|
|
* we need to make sure and step over it
|
|
*/
|
|
else if (!ADDR_IS_FLAT(*lpaddr) &&
|
|
(0x34 <= opcode) && (opcode <= 0x3c)) {
|
|
if (opcode == 0x3C) {
|
|
pMem++;
|
|
}
|
|
opcode = *pMem++;
|
|
mode = opcode & 0xc0;
|
|
rm = opcode & 0x03;
|
|
switch ( mode) {
|
|
case 0:
|
|
if (rm == 0x6) {
|
|
pMem += 2;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
pMem += 1;
|
|
break;
|
|
|
|
case 2:
|
|
pMem += 2;
|
|
break;
|
|
}
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
GetAddrOff(*lpaddr) += pMem - membuf;
|
|
fAddrSet = TRUE;
|
|
}
|
|
/*
|
|
* This is an FWAIT instr -- 2 bytes long
|
|
*/
|
|
else if (!ADDR_IS_FLAT(*lpaddr) && opcode == 0x3d) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
GetAddrOff(*lpaddr) += 2;
|
|
fAddrSet = TRUE;
|
|
}
|
|
/*
|
|
* This is a 0x3f interrupt -- I think this is for
|
|
* overlays in dos
|
|
*/
|
|
else if (!ADDR_IS_FLAT(*lpaddr) && (opcode == 0x3f)) {
|
|
if (fStepOver) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
AddrInit(&addrSp, 0, SsSegOfHthdx(hthd), STACK_POINTER(hthd),
|
|
FALSE, FALSE, FALSE, hthd->fAddrIsReal);
|
|
if (!AddrReadMemory(hthd->hprc,
|
|
hthd,
|
|
&addrSp,
|
|
rgus,
|
|
4,
|
|
&cb) ||
|
|
(cb != 4) ) {
|
|
goto done;
|
|
}
|
|
AddrInit(lpaddr, 0, rgus[1], (UOFF32) rgus[0], FALSE, FALSE,
|
|
FALSE, hthd->fAddrIsReal);
|
|
fAddrSet = TRUE;
|
|
}
|
|
}
|
|
/*
|
|
* OK its really an interrupt --- deal with it
|
|
*/
|
|
else {
|
|
if (!fStepOver && hthd->fAddrIsReal) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
AddrInit(&addrSp, 0, 0, opcode*4, FALSE, FALSE, FALSE, TRUE);
|
|
if (!AddrReadMemory(hthd->hprc,
|
|
hthd,
|
|
&addrSp,
|
|
rgus,
|
|
4,
|
|
&cb) ||
|
|
(cb != 4) ) {
|
|
goto done;
|
|
}
|
|
AddrInit(lpaddr, 0, rgus[1], (UOFF32) rgus[0], FALSE, FALSE,
|
|
FALSE, TRUE);
|
|
fAddrSet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Now check for various call instructions
|
|
*/
|
|
else if (opcode == 0xe8) { /* near direct call */
|
|
*lpf = INSTR_IS_CALL;
|
|
pMem += (1 + opsize_32)*2;
|
|
} else if (opcode == 0x9a) { /* far direct call */
|
|
*lpf = INSTR_IS_CALL;
|
|
pMem += (2 + opsize_32)*2;
|
|
} else if (opcode == 0xff) {
|
|
opcode = *pMem++; /* compute the modRM bits for instruction */
|
|
ttt = BIT53(opcode);
|
|
if ((ttt & ~1) == 2) { /* indirect call */
|
|
*lpf = INSTR_IS_CALL;
|
|
|
|
mod = BIT76(opcode);
|
|
if (mod != 3) { /* non-register operand */
|
|
rm = BIT20( opcode );
|
|
if (mode_32) {
|
|
if (rm == 4) {
|
|
rm = BIT20(*pMem++); /* get base from SIB */
|
|
}
|
|
|
|
if (mod == 0) {
|
|
if (rm == 5) {
|
|
pMem += 4; /* long direct address */
|
|
}
|
|
} else if (mod == 1) {
|
|
pMem++; /* register with byte offset */
|
|
} else {
|
|
pMem += 4; /* register with long offset */
|
|
}
|
|
} else { /* 16-bit mode */
|
|
if (mod == 0) {
|
|
if (rm == 6) {
|
|
pMem += 2; /* short direct address */
|
|
}
|
|
} else {
|
|
pMem += mod; /* reg, byte, word offset */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Now catch all of the repeated instructions
|
|
*
|
|
* INSB (0x6c) INSW (0x6d) OUTSB (0x6e) OUTSW (0x6f)
|
|
* MOVSB (0xa4) MOVSW (0xa5) CMPSB (0xa6) CMPSW (0xa7)
|
|
* STOSB (0xaa) STOSW (0xab)
|
|
* LODSB (0xac) LODSW (0xad) SCASB (0xae) SCASW (0xaf)
|
|
*/
|
|
else if (fRepPrefix && (((opcode & ~3) == 0x6c) ||
|
|
((opcode & ~3) == 0xa4) ||
|
|
((opcode & ~1) == 0xaa) ||
|
|
((opcode & ~3) == 0xac))) {
|
|
if (fStepOver) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
} else {
|
|
/*
|
|
* Cannot trace the ins/outs instructions
|
|
*/
|
|
if ((opcode & ~3) == 0x6c) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Now catch IO instructions -- these will generally fault and
|
|
* be interpreted.
|
|
*/
|
|
else if ((opcode & ~3) == 0x6c) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
}
|
|
/*
|
|
* Now catch other instructions which change registers
|
|
*/
|
|
else if ((opcode == 0xfa) || (opcode == 0xfb) ||
|
|
(opcode == 0x9d) || (opcode == 0x9c)) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
}
|
|
/*
|
|
* Now catch irets
|
|
*/
|
|
else if (opcode == 0xcf) {
|
|
*lpf = INSTR_CANNOT_TRACE;
|
|
AddrInit(&addrSp, 0, SsSegOfHthdx(hthd), STACK_POINTER(hthd),
|
|
hthd->fAddrIsFlat, hthd->fAddrOff32, FALSE,
|
|
hthd->fAddrIsReal);
|
|
if (opsize_32) {
|
|
if (!AddrReadMemory(hthd->hprc,
|
|
hthd,
|
|
&addrSp,
|
|
rgul,
|
|
8,
|
|
&cb) ||
|
|
(cb != 8) ) {
|
|
goto done;
|
|
}
|
|
AddrInit(lpaddr, 0, (SEGMENT) rgul[1], rgul[0],
|
|
hthd->fAddrIsFlat, TRUE, FALSE, FALSE);
|
|
} else {
|
|
if (!AddrReadMemory(hthd->hprc,
|
|
hthd,
|
|
&addrSp,
|
|
rgus,
|
|
4,
|
|
&cb) ||
|
|
(cb != 4) ) {
|
|
goto done;
|
|
}
|
|
AddrInit(lpaddr, 0, rgus[1], (UOFF32) rgus[0], FALSE, FALSE,
|
|
FALSE, hthd->fAddrIsReal);
|
|
}
|
|
fAddrSet = TRUE;
|
|
}
|
|
/*
|
|
* Assume that we want to just trace the instruction
|
|
*/
|
|
else {
|
|
*lpf = INSTR_TRACE_BIT;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
DPRINT(1, ("%s", rgszTrace[*lpf]));
|
|
|
|
/*
|
|
* Have read enough bytes? no -- expect somebody else to blow up
|
|
*/
|
|
|
|
if (cBytes < (DWORD)(pMem - membuf)) {
|
|
*lpf = INSTR_TRACE_BIT;
|
|
goto done;
|
|
}
|
|
|
|
if (!fAddrSet) {
|
|
GetAddrOff(*lpaddr) += pMem - membuf;
|
|
}
|
|
|
|
/*
|
|
* Dump out the bytes for later checking
|
|
*/
|
|
|
|
#if DBG
|
|
if (FVerbose) {
|
|
DWORD i;
|
|
DPRINT(1, ("length = %d bytes=", cBytes & 0xff));
|
|
for (i=0; i<cBytes; i++) {
|
|
DPRINT(1, (" %02x", membuf[i]));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
done:
|
|
hthd->fIsCallDone = TRUE;
|
|
hthd->addrIsCall = *lpaddr;
|
|
hthd->iInstrIsCall = *lpf;
|
|
return;
|
|
} /* IsCall() */
|
|
|
|
|
|
|
|
|
|
XOSD
|
|
SetupFunctionCall(
|
|
LPEXECUTE_OBJECT_DM lpeo,
|
|
LPEXECUTE_STRUCT lpes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function contains the machine dependent code for initializing
|
|
the function call system.
|
|
|
|
Arguments:
|
|
|
|
lpeo - Supplies a pointer to the Execute Object for the Function call
|
|
|
|
lpes - Supplies a pointer to the Execute Struct from the DM
|
|
|
|
Return Value:
|
|
|
|
XOSD error code
|
|
|
|
--*/
|
|
|
|
{
|
|
CONTEXT context;
|
|
OFFSET off;
|
|
int cb;
|
|
ULONG ul;
|
|
HPRCX hprc = lpeo->hthd->hprc;
|
|
ADDR addr;
|
|
|
|
/*
|
|
* Can only execute functions on the current stopped thread. Therefore
|
|
* assert that the current thread is stopped.
|
|
*/
|
|
|
|
assert(lpeo->hthd->tstate & ts_stopped);
|
|
if (!(lpeo->hthd->tstate & ts_stopped)) {
|
|
return xosdInvalidThread;
|
|
}
|
|
|
|
/*
|
|
* Can copy the context from the cached context in the thread structure
|
|
*/
|
|
|
|
context = lpeo->hthd->context;
|
|
|
|
/*
|
|
* Now get the current stack offset
|
|
*/
|
|
|
|
lpeo->addrStack.addr.off = context.Esp;
|
|
lpeo->addrStack.addr.seg = (SEGMENT) context.SegSs;
|
|
if (!lpeo->hthd->fAddrOff32) {
|
|
lpeo->addrStack.addr.off &= 0xffff;
|
|
}
|
|
|
|
/*
|
|
* Put the return address onto the stack. If this is a far address
|
|
* then it needs to be a far return address. Else it must be a
|
|
* near return address.
|
|
*/
|
|
|
|
if (lpeo->hthd->fAddrOff32) {
|
|
if (lpes->fFar) {
|
|
assert(FALSE); /* Not used for Win32 */
|
|
}
|
|
|
|
off = context.Esp - 4;
|
|
if (DbgWriteMemory(hprc, (char *) off, &lpeo->addrStart.addr.off,
|
|
4, &cb) == 0 ||
|
|
(cb != 4)) {
|
|
return xosdUnknown;
|
|
}
|
|
} else {
|
|
if (lpes->fFar) {
|
|
off = context.Esp - 4;
|
|
ul = (lpeo->addrStart.addr.seg << 16) | lpeo->addrStart.addr.off;
|
|
addr = lpeo->addrStack;
|
|
GetAddrOff(addr) -= 4;
|
|
TranslateAddress(hprc, lpeo->hthd, &addr, TRUE);
|
|
if ((DbgWriteMemory(hprc, (char *) GetAddrOff(addr),
|
|
&ul, 4, &cb) == 0) ||
|
|
(cb != 4)) {
|
|
return xosdUnknown;
|
|
}
|
|
} else {
|
|
off = context.Esp & 0xffff - 2;
|
|
addr = lpeo->addrStack;
|
|
GetAddrOff(addr) -= 2;
|
|
TranslateAddress(hprc, lpeo->hthd, &addr, TRUE);
|
|
if ((DbgWriteMemory(hprc, (char *) GetAddrOff(addr),
|
|
&lpeo->addrStart.addr.off, 2, &cb) == 0) ||
|
|
(cb != 2)) {
|
|
return xosdUnknown;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the new stack pointer and starting address in the context and
|
|
* write them back to the thread.
|
|
*/
|
|
|
|
lpeo->hthd->context.Esp = off;
|
|
lpeo->hthd->context.Eip = lpeo->addrStart.addr.off;
|
|
|
|
lpeo->hthd->fContextDirty = TRUE;
|
|
|
|
return xosdNone;
|
|
} /* SetupFunctionCall() */
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
CompareStacks(
|
|
LPEXECUTE_OBJECT_DM lpeo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to determine if the stack pointers are currect
|
|
for terminating function evaluation.
|
|
|
|
Arguments:
|
|
|
|
lpeo - Supplies the pointer to the DM Execute Object description
|
|
|
|
Return Value:
|
|
|
|
TRUE if the evaluation is to be terminated and FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
if (lpeo->hthd->fAddrOff32) {
|
|
if (lpeo->addrStack.addr.off <= lpeo->hthd->context.Esp) {
|
|
return TRUE;
|
|
}
|
|
} else if ((lpeo->addrStack.addr.off <= (lpeo->hthd->context.Esp & 0xffff)) &&
|
|
(lpeo->addrStack.addr.seg == (SEGMENT) lpeo->hthd->context.SegSs)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
} /* CompareStacks() */
|
|
|
|
#ifdef WIN32S
|
|
/*
|
|
* IsWin32sSystemDll
|
|
*
|
|
* INPUTS szImageName = asciiz name of dll (may include path)
|
|
* OUTPUTS returns TRUE if the dll name is in the set of Win32s system dlls.
|
|
* SUMMARY Simple table search. The table of names is sorted and NULL
|
|
* terminated.
|
|
*/
|
|
BOOL IsWin32sSystemDll(UCHAR * szImageName) {
|
|
USHORT i = 0;
|
|
int cmp;
|
|
UCHAR * pTemp;
|
|
|
|
|
|
if (! szImageName) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// break off path
|
|
pTemp = szImageName + strlen(szImageName) - 1; // point to end of string
|
|
while ((pTemp > szImageName) && (*pTemp != ':') && (*pTemp != '\\')) {
|
|
pTemp--;
|
|
}
|
|
if (*pTemp == ':' || *pTemp == '\\') {
|
|
pTemp++;
|
|
}
|
|
|
|
// linear search in sorted table (NULL terminated)
|
|
while (szWin32sSystemDllTable[i] &&
|
|
((cmp = _stricmp(szWin32sSystemDllTable[i], pTemp)) < 0)) {
|
|
i++;
|
|
}
|
|
|
|
return(cmp == 0); // _stricmp --> 0 on match
|
|
}
|
|
|
|
|
|
/*
|
|
* IsWin32sSystemDllAddr
|
|
*
|
|
* INPUTS dwOffset
|
|
* OUTPUTS TRUE if dwOffset fits into a range found in the system dll table.
|
|
* SUMMARY Search the ranges in the table until the Start value is > dwOffset.
|
|
*/
|
|
BOOL
|
|
IsWin32sSystemDllAddr(
|
|
DWORD dwOffset
|
|
)
|
|
{
|
|
LPWIN32S_SYSTEM_DLL pNode = pWin32sSystemDlls;
|
|
if (dwOffset < Win32sSystemDllFirst || dwOffset > Win32sSystemDllLast) {
|
|
return(FALSE); // quick check
|
|
}
|
|
|
|
// otherwise, do the search
|
|
while (pNode && (pNode->dwStart <= dwOffset)) {
|
|
if (dwOffset <= pNode->dwEnd && dwOffset >= pNode->dwStart) {
|
|
return(TRUE); // in range
|
|
}
|
|
pNode = pNode->pNext;
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
* AddWin32sSystemDllAddr
|
|
*
|
|
* INPUTS dwOffset = start address for object
|
|
* cbObject = size of object
|
|
* OUTPUTS none
|
|
* SUMMARY Add the addresses in between {offset, offset+cbObject} to the
|
|
* Win32s System Dlls list. If the offset is within
|
|
* WIN32S_LOAD_EPSILON of another entry, tack them together. (The
|
|
* idea is that the loader won't load any of the user's code in
|
|
* there anyway and we want to speed up list searches.) The list
|
|
* will be maintained in sorted order. We will also maintain the
|
|
* Win32sSystemDllFirst and Win32sSystemDllLast offsets for a quick
|
|
* elimination of most list searches.
|
|
*/
|
|
void
|
|
AddWin32sSystemDllAddr(
|
|
DWORD dwOffset,
|
|
DWORD cbObject
|
|
)
|
|
{
|
|
DWORD dwEnd = dwOffset + cbObject - 1; // ending offset
|
|
LPWIN32S_SYSTEM_DLL pNode, pPrev, pNew;
|
|
|
|
assert(dwOffset < dwEnd);
|
|
|
|
// Update Win32sSystemDllFirst/Last
|
|
|
|
Win32sSystemDllFirst = min(Win32sSystemDllFirst, dwOffset);
|
|
Win32sSystemDllLast = max(Win32sSystemDllLast, dwEnd);
|
|
|
|
// find insert point and insert new node for section
|
|
pPrev = NULL;
|
|
pNode = pWin32sSystemDlls; // head of list
|
|
|
|
while (pNode && pNode->dwStart < dwOffset) {
|
|
pPrev = pNode;
|
|
pNode = pNode->pNext;
|
|
}
|
|
|
|
if ((pNew = malloc(sizeof(WIN32S_SYSTEM_DLL))) == NULL) {
|
|
assert(FALSE);
|
|
return; //error, no memory
|
|
}
|
|
|
|
if (pPrev) { // insert point is middle or end, not head
|
|
pPrev->pNext = pNew;
|
|
} else { // insert at head, update global head pointer
|
|
pWin32sSystemDlls = pNew;
|
|
}
|
|
|
|
// fill in new node
|
|
pNew->dwStart = dwOffset;
|
|
pNew->dwEnd = dwEnd;
|
|
pNew->pNext = pNode;
|
|
|
|
// Remove overlap. It can only happen near the new node.
|
|
// merge with prev
|
|
if (pPrev && pPrev->dwEnd + WIN32S_LOAD_EPSILON >= pNew->dwStart) {
|
|
pPrev->dwEnd = max(pNew->dwEnd, pPrev->dwEnd);
|
|
pPrev->pNext = pNew->pNext;
|
|
free(pNew);
|
|
pNew = pPrev;
|
|
}
|
|
|
|
// merge with next (may be more than one if we have large dwEnd)
|
|
while (pNode && pNode->dwStart <= pNew->dwEnd + WIN32S_LOAD_EPSILON) {
|
|
pNew->dwEnd = max(pNode->dwEnd, pNew->dwEnd);
|
|
pNew->pNext = pNode->pNext;
|
|
free(pNode);
|
|
pNode = pNew->pNext;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* FreeWin32sDllList
|
|
*
|
|
* INPUTS none
|
|
* OUTPUTS none
|
|
* SUMMARY free all the memory in the win32s system dll list and set the
|
|
* global head pointer to null.
|
|
*/
|
|
void
|
|
FreeWin32sDllList(
|
|
void
|
|
)
|
|
{
|
|
|
|
LPWIN32S_SYSTEM_DLL pNode, pNext;
|
|
|
|
pNode = pWin32sSystemDlls;
|
|
pWin32sSystemDlls = NULL;
|
|
|
|
while (pNode) {
|
|
pNext = pNode->pNext;
|
|
free(pNode);
|
|
pNode = pNext;
|
|
}
|
|
}
|
|
|
|
WIN32S_TRACE_CHECK
|
|
IsWin32sSystemThunk(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
DWORD currAddr,
|
|
DWORD stackPtr
|
|
)
|
|
/*
|
|
* IsWin32sSystemThunk
|
|
*
|
|
* INPUTS rwHand = handle to the process
|
|
* currAddr = EIP to check for a thunk call
|
|
* stackPtr = current ESP
|
|
* OUTPUT WIN32S_TRACE_OK: not a jump or call to win32s code
|
|
* WIN32S_THUNK_CALL: the instr is a call to win32s code
|
|
* WIN32S_THUNK_JUMP: the instr is a jump or ret to win32s code
|
|
* SUMMARY This is kinda twisted: Thunks look like this:
|
|
*
|
|
* ApiCall(p1, p2, etc);
|
|
* push
|
|
* push
|
|
* etc.
|
|
* call _ApiCall@8
|
|
* e8 {offset} where offset is the distance from the start
|
|
* of the next instruction. The destination of
|
|
* the call is eip+5+offset.
|
|
*
|
|
* _ApiCall@8:
|
|
* jmp dword ptr [system call address]
|
|
* ff 25 {address} where address is the address to read the
|
|
* destination from.
|
|
*
|
|
* Anyway, we want to recognize this situation and check the final
|
|
* destination against the known address space of the system dlls.
|
|
* For this, we will look in the table compiled during dll loads.
|
|
*
|
|
* WARNWARN This code is very i386 specific, but that should be ok since it is
|
|
* only intended to run on Win32s machines.
|
|
*
|
|
*/
|
|
|
|
{
|
|
WIN32S_TRACE_CHECK wtcReturn = WIN32S_TRACE_OK;
|
|
BYTE InstructionBuffer[6] = {0};
|
|
DWORD dwDestination;
|
|
DWORD * pdwDestination;
|
|
DWORD dwIndirect;
|
|
DWORD * pdwIndirect;
|
|
DWORD dwBytesRead;
|
|
ADDR addr;
|
|
|
|
|
|
|
|
DEBUG_PRINT_1("\r\nIsWin32sSystemThunk(0x%x)\r\n", currAddr);
|
|
|
|
try { // in case instruction would generate gp-fault
|
|
|
|
// read the current instruction
|
|
AddrInit(&addr, 0, 0, (OFFSET)currAddr, TRUE, TRUE, FALSE, FALSE);
|
|
if (AddrReadMemory(hprc, hthd, &addr, InstructionBuffer, 5,
|
|
&dwBytesRead)) {
|
|
|
|
// got the current instruction. What is it?
|
|
switch (InstructionBuffer[0]) {
|
|
case (BYTE)0xe8: // Near call
|
|
// call to where?
|
|
pdwDestination = (DWORD *)(&InstructionBuffer[1]);
|
|
dwDestination = *pdwDestination + currAddr + 5;
|
|
DEBUG_PRINT_1("Found a call to 0x%x\r\n", dwDestination);
|
|
AddrInit(&addr, 0, 0, (OFFSET)dwDestination, TRUE, TRUE, FALSE, FALSE);
|
|
if (AddrReadMemory(hprc, hthd, &addr, InstructionBuffer, 6,
|
|
&dwBytesRead)) {
|
|
// Got the destination instruction, is it a jmp
|
|
// indirect?
|
|
if (InstructionBuffer[0] == (BYTE)0xff &&
|
|
InstructionBuffer[1] == (BYTE)0x25) {
|
|
// Jump indirect to where?
|
|
pdwIndirect = (DWORD *)(&InstructionBuffer[2]);
|
|
dwIndirect = *pdwIndirect;
|
|
DEBUG_PRINT_1(
|
|
"Found a call -> jump indirect to 0x%x\r\n",
|
|
dwIndirect);
|
|
AddrInit(&addr, 0, 0, (OFFSET)dwIndirect, TRUE, TRUE, FALSE, FALSE);
|
|
if (AddrReadMemory(hprc, hthd, &addr,
|
|
InstructionBuffer, 4, &dwBytesRead)) {
|
|
pdwDestination = (DWORD *)InstructionBuffer;
|
|
if (IsWin32sSystemDllAddr(*pdwDestination)) {
|
|
wtcReturn = WIN32S_THUNK_CALL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case (BYTE)0xc2: // retn N
|
|
case (BYTE)0xc3: // retn
|
|
// check if we are about to return to win32s system code...
|
|
// look at [esp]. If it is in system dll, do a Go
|
|
// Special case: If the current address is Win32sBackTo32+4
|
|
// don't do the check, we want to allow the trace back to
|
|
// User32.dll.
|
|
if (currAddr == (DWORD)Win32sBackTo32) {
|
|
break;
|
|
}
|
|
// read return address from stack
|
|
AddrInit(&addr, 0, 0, (OFFSET)stackPtr, TRUE, TRUE, FALSE, FALSE);
|
|
if (AddrReadMemory(hprc, hthd, &addr, InstructionBuffer, 4,
|
|
&dwBytesRead)) {
|
|
pdwDestination = (DWORD *)InstructionBuffer;
|
|
if (IsWin32sSystemDllAddr(*pdwDestination)) {
|
|
wtcReturn = WIN32S_THUNK_JUMP;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// BRUCEK: should also check for JUMP FAR to 16-bit. What does
|
|
// that look like?
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
} except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|
}
|
|
|
|
switch (wtcReturn) {
|
|
case WIN32S_THUNK_CALL:
|
|
DEBUG_PRINT("IsWin32sSystemThunk --> WIN32S_THUNK_CALL\r\n");
|
|
break;
|
|
case WIN32S_THUNK_JUMP:
|
|
DEBUG_PRINT("IsWin32sSystemThunk --> WIN32S_THUNK_JUMP\r\n");
|
|
break;
|
|
case WIN32S_TRACE_OK:
|
|
DEBUG_PRINT("IsWin32sSystemThunk --> WIN32S_TRACE_OK\r\n");
|
|
break;
|
|
default:
|
|
DEBUG_PRINT_1("ERROR: IsWin32sSystemThunk --> UNKNOWN %u\r\n",
|
|
wtcReturn);
|
|
}
|
|
|
|
return(wtcReturn);
|
|
}
|
|
|
|
#endif // WIN32S
|
|
|
|
#ifndef KERNEL
|
|
|
|
void
|
|
ProcessGetDRegsCmd(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
{
|
|
LPDWORD lpdw = (LPDWORD)LpDmMsg->rgb;
|
|
CONTEXT cxt;
|
|
int rs = 0;
|
|
|
|
DEBUG_PRINT( "ProcessGetDRegsCmd :\n");
|
|
|
|
|
|
#ifdef WIN32S
|
|
// Can't yet get thread context within a non-exception event.
|
|
if (hthd == 0 || ! fCanGetThreadContext) {
|
|
DEBUG_PRINT("\r\nProcessGetDRegsCmd\r\n");
|
|
// DebugBreak();
|
|
#else
|
|
if (hthd == 0) {
|
|
#endif
|
|
rs = 0;
|
|
} else {
|
|
cxt.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
if (!GetThreadContext(hthd->rwHand, &cxt)) {
|
|
LpDmMsg->xosdRet = xosdUnknown;
|
|
rs = 0;
|
|
} else {
|
|
lpdw[0] = hthd->context.Dr0;
|
|
lpdw[1] = hthd->context.Dr1;
|
|
lpdw[2] = hthd->context.Dr2;
|
|
lpdw[3] = hthd->context.Dr3;
|
|
lpdw[4] = hthd->context.Dr6;
|
|
lpdw[5] = hthd->context.Dr7;
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
rs = sizeof(CONTEXT);
|
|
}
|
|
}
|
|
|
|
Reply( rs, LpDmMsg, lpdbb->hpid );
|
|
return;
|
|
} /* ProcessGetDRegsCmd() */
|
|
|
|
|
|
void
|
|
ProcessSetDRegsCmd(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
{
|
|
LPDWORD lpdw = (LPDWORD)(lpdbb->rgbVar);
|
|
XOSD_ xosd = xosdNone;
|
|
|
|
Unreferenced(hprc);
|
|
|
|
DPRINT(5, ("ProcessSetDRegsCmd : "));
|
|
|
|
hthd->context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
|
|
hthd->context.Dr0 = lpdw[0];
|
|
hthd->context.Dr1 = lpdw[1];
|
|
hthd->context.Dr2 = lpdw[2];
|
|
hthd->context.Dr3 = lpdw[3];
|
|
hthd->context.Dr6 = lpdw[4];
|
|
hthd->context.Dr7 = lpdw[5];
|
|
|
|
|
|
if (hthd->fWowEvent) {
|
|
WOWSetThreadContext(hthd, &hthd->context);
|
|
} else {
|
|
SetThreadContext(hthd->rwHand, &hthd->context);
|
|
}
|
|
|
|
Reply(0, &xosd, lpdbb->hpid);
|
|
|
|
return;
|
|
} /* ProcessSetDRegsCmd() */
|
|
|
|
|
|
|
|
VOID
|
|
MakeThreadSuspendItselfHelper(
|
|
HTHDX hthd,
|
|
FARPROC lpSuspendThread
|
|
)
|
|
{
|
|
HANDLE h;
|
|
DWORD dw;
|
|
|
|
//
|
|
// set up the args to SuspendThread
|
|
//
|
|
|
|
hthd->context.Esp -= 4;
|
|
h = GetCurrentThread();
|
|
WriteProcessMemory(hthd->hprc->rwHand,
|
|
(PVOID)hthd->context.Esp,
|
|
&h,
|
|
sizeof(h),
|
|
&dw);
|
|
|
|
hthd->context.Esp -= 4;
|
|
WriteProcessMemory(hthd->hprc->rwHand,
|
|
(PVOID)hthd->context.Esp,
|
|
&(PC(hthd)),
|
|
sizeof(DWORD),
|
|
&dw);
|
|
|
|
PC(hthd) = (DWORD)lpSuspendThread;
|
|
hthd->fContextDirty = TRUE;
|
|
}
|
|
|
|
#endif // !KERNEL
|
|
|
|
BOOL
|
|
ProcessFrameStackWalkNextCmd(HPRCX hprc,
|
|
HTHDX hthd,
|
|
PCONTEXT context,
|
|
LPVOID pctxPtrs)
|
|
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
XOSD disasm ( LPSDI lpsdi, void*Memory, int Size );
|
|
BOOL ParseAddr ( char*, ADDR* );
|
|
BOOL ParseNumber( char*, DWORD*, int );
|
|
|
|
|
|
typedef struct _BTNODE {
|
|
char *Name;
|
|
BOOL IsCall;
|
|
BOOL TargetAvail;
|
|
} BTNODE;
|
|
|
|
|
|
BTNODE BranchTable[] = {
|
|
{ "call" , TRUE , TRUE },
|
|
{ "ja" , FALSE , TRUE },
|
|
{ "jae" , FALSE , TRUE },
|
|
{ "jb" , FALSE , TRUE },
|
|
{ "jbe" , FALSE , TRUE },
|
|
{ "jcxz" , FALSE , TRUE },
|
|
{ "je" , FALSE , TRUE },
|
|
{ "jecxz" , FALSE , TRUE },
|
|
{ "jg" , FALSE , TRUE },
|
|
{ "jge" , FALSE , TRUE },
|
|
{ "jl" , FALSE , TRUE },
|
|
{ "jle" , FALSE , TRUE },
|
|
{ "jmp" , FALSE , TRUE },
|
|
{ "jne" , FALSE , TRUE },
|
|
{ "jno" , FALSE , TRUE },
|
|
{ "jnp" , FALSE , TRUE },
|
|
{ "jns" , FALSE , TRUE },
|
|
{ "jo" , FALSE , TRUE },
|
|
{ "jp" , FALSE , TRUE },
|
|
{ "js" , FALSE , TRUE },
|
|
{ "loop" , FALSE , FALSE },
|
|
{ "loope" , FALSE , FALSE },
|
|
{ "loopne" , FALSE , FALSE },
|
|
{ "loopnz" , FALSE , FALSE },
|
|
{ "loopz" , FALSE , FALSE },
|
|
{ "ret" , FALSE , FALSE },
|
|
{ "retf" , FALSE , FALSE },
|
|
{ "retn" , FALSE , FALSE },
|
|
{ NULL , FALSE , FALSE }
|
|
};
|
|
|
|
|
|
|
|
DWORD
|
|
BranchUnassemble(
|
|
void *Memory,
|
|
ADDR *Addr,
|
|
BOOL *IsBranch,
|
|
BOOL *TargetKnown,
|
|
BOOL *IsCall,
|
|
BOOL *IsTable,
|
|
ADDR *Target
|
|
)
|
|
{
|
|
XOSD xosd;
|
|
SDI Sdi;
|
|
DWORD Consumed = 0;
|
|
DWORD i;
|
|
int s;
|
|
char *p;
|
|
ADDR Trgt;
|
|
|
|
AddrInit( &Trgt, 0, 0, 0, TRUE, TRUE, FALSE, FALSE );
|
|
|
|
*IsBranch = FALSE;
|
|
*IsTable = FALSE;
|
|
|
|
Sdi.dop = dopOpcode| dopOperands | dopEA;
|
|
Sdi.addr = *Addr;
|
|
|
|
xosd = disasm( &Sdi, Memory, 16 );
|
|
|
|
if ( xosd == xosdNone ) {
|
|
|
|
*IsTable = Sdi.fJumpTable;
|
|
|
|
for ( i=0; BranchTable[i].Name != NULL; i++ ) {
|
|
|
|
s = strcmp( Sdi.lpch, BranchTable[i].Name );
|
|
|
|
if ( s == 0 ) {
|
|
|
|
*IsBranch = TRUE;
|
|
*IsCall = BranchTable[i].IsCall;
|
|
if (*IsTable) {
|
|
*Target = Sdi.addrEA0;
|
|
//
|
|
// We might know the target, but for this
|
|
// purpose, we don't want to deal with it.
|
|
//
|
|
*TargetKnown = FALSE;
|
|
}
|
|
else if (BranchTable[i].TargetAvail &&
|
|
(p = Sdi.lpch) &&
|
|
*(p += (strlen(p)+1)) ) {
|
|
|
|
Trgt = *Addr;
|
|
if ( ParseAddr( p, &Trgt ) ) {
|
|
*TargetKnown = TRUE;
|
|
} else {
|
|
AddrInit( &Trgt, 0, 0, 0, TRUE, TRUE, FALSE, FALSE );
|
|
*TargetKnown = FALSE;
|
|
}
|
|
*Target = Trgt;
|
|
}
|
|
else {
|
|
*Target = Trgt;
|
|
*TargetKnown = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
} else if ( s < 0 ) {
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Consumed = GetAddrOff( Sdi.addr ) - GetAddrOff(*Addr);
|
|
}
|
|
|
|
return Consumed;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
ParseAddr (
|
|
char *szAddr,
|
|
ADDR *Addr
|
|
)
|
|
{
|
|
|
|
char *p;
|
|
BOOL fParsed;
|
|
SEGMENT Segment;
|
|
UOFF16 Off16;
|
|
UOFF32 Off32;
|
|
DWORD Dword;
|
|
|
|
assert( szAddr );
|
|
assert( Addr );
|
|
|
|
fParsed = FALSE;
|
|
|
|
p = strchr( szAddr, ':' );
|
|
|
|
if ( p ) {
|
|
|
|
*p = '\0';
|
|
p++;
|
|
|
|
if ( ParseNumber( szAddr, &Dword, 16 ) ) {
|
|
|
|
Segment = (SEGMENT)Dword;
|
|
|
|
if ( ParseNumber( p, &Dword, 16 ) ) {
|
|
|
|
Off16 = (UOFF16)Dword;
|
|
fParsed = TRUE;
|
|
|
|
GetAddrSeg(*Addr) = Segment;
|
|
GetAddrOff(*Addr) = Off16;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if ( ParseNumber( szAddr, &Dword, 16 ) ) {
|
|
|
|
Off32 = (UOFF32)Dword;
|
|
fParsed = TRUE;
|
|
|
|
GetAddrOff(*Addr) = Off32;
|
|
}
|
|
}
|
|
|
|
return fParsed;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ParseNumber (
|
|
char *szNumber,
|
|
DWORD *Number,
|
|
int Radix
|
|
)
|
|
{
|
|
BOOL fParsed = FALSE;
|
|
char *p = szNumber;
|
|
char *q;
|
|
|
|
assert( szNumber );
|
|
assert( Number );
|
|
|
|
if ( strlen(p) > 2 &&
|
|
p[0]=='0' &&
|
|
(p[1]=='x' || p[1]=='X') ) {
|
|
|
|
p+=2;
|
|
assert( Radix == 16 );
|
|
}
|
|
|
|
q = p;
|
|
while ( *q && isxdigit(*q) ) {
|
|
q++;
|
|
}
|
|
|
|
if ( !*q ) {
|
|
*Number = strtoul( p, NULL, Radix );
|
|
fParsed = TRUE;
|
|
}
|
|
|
|
return fParsed;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
SetupDebugRegister(
|
|
HTHDX hthd,
|
|
int Register,
|
|
int DataSize,
|
|
DWORD DataAddr,
|
|
DWORD BpType
|
|
)
|
|
{
|
|
DWORD Len;
|
|
DWORD rwMask;
|
|
|
|
#ifdef KERNEL
|
|
KSPECIAL_REGISTERS ksr;
|
|
PDWORD Dr0 = &ksr.KernelDr0;
|
|
PDWORD Dr1 = &ksr.KernelDr1;
|
|
PDWORD Dr2 = &ksr.KernelDr2;
|
|
PDWORD Dr3 = &ksr.KernelDr3;
|
|
PDR7 Dr7 = (PDR7)&(ksr.KernelDr7);
|
|
#else
|
|
CONTEXT Context;
|
|
PDWORD Dr0 = &Context.Dr0;
|
|
PDWORD Dr1 = &Context.Dr1;
|
|
PDWORD Dr2 = &Context.Dr2;
|
|
PDWORD Dr3 = &Context.Dr3;
|
|
PDR7 Dr7 = (PDR7)&(Context.Dr7);
|
|
#endif
|
|
|
|
|
|
#ifdef KERNEL
|
|
if (!GetExtendedContext(hthd, &ksr))
|
|
#else
|
|
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
if (!GetThreadContext(hthd->rwHand, &Context))
|
|
#endif
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
Len = LenMask[ DataSize ];
|
|
|
|
switch ( BpType ) {
|
|
case bptpDataR:
|
|
rwMask = RWE_READWRITE;
|
|
break;
|
|
|
|
case bptpDataW:
|
|
case bptpDataC:
|
|
rwMask = RWE_WRITE;
|
|
break;
|
|
|
|
case bptpDataExec:
|
|
rwMask = RWE_EXEC;
|
|
//
|
|
// length must be 0 for exec bp
|
|
//
|
|
Len = 0;
|
|
break;
|
|
|
|
default:
|
|
assert(!"Invalid BpType!!");
|
|
break;
|
|
}
|
|
|
|
|
|
switch( Register ) {
|
|
case 0:
|
|
*Dr0 = DataAddr;
|
|
Dr7->Len0 = Len;
|
|
Dr7->Rwe0 = rwMask;
|
|
Dr7->L0 = 0x01;
|
|
break;
|
|
case 1:
|
|
*Dr1 = DataAddr;
|
|
Dr7->Len1 = Len;
|
|
Dr7->Rwe1 = rwMask;
|
|
Dr7->L1 = 0x01;
|
|
break;
|
|
case 2:
|
|
*Dr2 = DataAddr;
|
|
Dr7->Len2 = Len;
|
|
Dr7->Rwe2 = rwMask;
|
|
Dr7->L2 = 0x01;
|
|
break;
|
|
case 3:
|
|
*Dr3 = DataAddr;
|
|
Dr7->Len3 = Len;
|
|
Dr7->Rwe3 = rwMask;
|
|
Dr7->L3 = 0x01;
|
|
break;
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
ksr.KernelDr6 = 0;
|
|
return SetExtendedContext(hthd, &ksr);
|
|
#else
|
|
Context.Dr6 = 0;
|
|
return SetThreadContext(hthd->rwHand, &Context);
|
|
#endif
|
|
|
|
}
|
|
|
|
VOID
|
|
ClearDebugRegister(
|
|
HTHDX hthd,
|
|
int Register
|
|
)
|
|
{
|
|
#ifdef KERNEL
|
|
KSPECIAL_REGISTERS ksr;
|
|
PDWORD Dr0 = &ksr.KernelDr0;
|
|
PDWORD Dr1 = &ksr.KernelDr1;
|
|
PDWORD Dr2 = &ksr.KernelDr2;
|
|
PDWORD Dr3 = &ksr.KernelDr3;
|
|
PDR7 Dr7 = (PDR7)&(ksr.KernelDr7);
|
|
#else
|
|
CONTEXT Context;
|
|
PDWORD Dr0 = &Context.Dr0;
|
|
PDWORD Dr1 = &Context.Dr1;
|
|
PDWORD Dr2 = &Context.Dr2;
|
|
PDWORD Dr3 = &Context.Dr3;
|
|
PDR7 Dr7 = (PDR7)&(Context.Dr7);
|
|
#endif
|
|
|
|
|
|
#ifdef KERNEL
|
|
if (GetExtendedContext(hthd, &ksr))
|
|
#else
|
|
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
if (GetThreadContext(hthd->rwHand, &Context))
|
|
#endif
|
|
{
|
|
|
|
switch( Register ) {
|
|
case 0:
|
|
*Dr0 = 0;
|
|
Dr7->Len0 = 0;
|
|
Dr7->Rwe0 = 0;
|
|
Dr7->L0 = 0;
|
|
break;
|
|
case 1:
|
|
*Dr1 = 0;
|
|
Dr7->Len1 = 0;
|
|
Dr7->Rwe1 = 0;
|
|
Dr7->L1 = 0;
|
|
break;
|
|
case 2:
|
|
*Dr2 = 0;
|
|
Dr7->Len2 = 0;
|
|
Dr7->Rwe2 = 0;
|
|
Dr7->L2 = 0;
|
|
break;
|
|
case 3:
|
|
*Dr3 = 0;
|
|
Dr7->Len3 = 0;
|
|
Dr7->Rwe3 = 0;
|
|
Dr7->L3 = 0;
|
|
break;
|
|
}
|
|
|
|
#ifdef KERNEL
|
|
ksr.KernelDr6 = 0;
|
|
SetExtendedContext(hthd, &ksr);
|
|
#else
|
|
Context.Dr6 = 0;
|
|
SetThreadContext( hthd->rwHand, &Context );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DecodeSingleStepEvent(
|
|
HTHDX hthd,
|
|
DEBUG_EVENT *de,
|
|
PDWORD eventCode,
|
|
PDWORD subClass
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
hthd - Supplies thread that has a single step exception pending
|
|
|
|
de - Supplies the DEBUG_EVENT structure for the exception
|
|
|
|
eventCode - Returns the remapped debug event id
|
|
|
|
subClass - Returns the remapped subClass id
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if event was a real single step or was successfully mapped
|
|
to a breakpoint. FALSE if a register breakpoint occurred which was
|
|
not expected.
|
|
|
|
--*/
|
|
{
|
|
DWORD dr6;
|
|
PBREAKPOINT bp;
|
|
|
|
#ifdef KERNEL
|
|
|
|
KSPECIAL_REGISTERS ksr;
|
|
|
|
GetExtendedContext( hthd, &ksr);
|
|
dr6 = ksr.KernelDr6;
|
|
|
|
#else
|
|
|
|
CONTEXT Context;
|
|
|
|
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
|
DbgGetThreadContext( hthd, &Context);
|
|
dr6 = Context.Dr6;
|
|
|
|
#endif
|
|
|
|
//
|
|
// if it was a single step, look no further:
|
|
//
|
|
|
|
if ((dr6 & 0x4000) != 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Search for a matching walk...
|
|
//
|
|
|
|
bp = GetWalkBPFromBits(hthd, (dr6 & 0xf));
|
|
|
|
if (bp) {
|
|
de->dwDebugEventCode = *eventCode = BREAKPOINT_DEBUG_EVENT;
|
|
de->u.Exception.ExceptionRecord.ExceptionCode = *subClass = (DWORD)bp;
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|