|
|
/*++
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; } }
|