/*++ Copyright (c) 1993 Microsoft Corporation Module Name: disasm.c Abstract: This file provides support disassembly ( x86 ). Author: Gerd Immeyer 19-Oct-1989 Wesley Witt (wesw) 1-May-1993 ( ported from ntsd to drwatson) Environment: User Mode --*/ #include #include #include #include "regs.h" #include "disasm.h" #include "drwatson.h" #include "proto.h" /***** macros and defines *****/ #define BIT20(b) (b & 0x07) #define BIT53(b) (b >> 3 & 0x07) #define BIT76(b) (b >> 6 & 0x03) #define MAXL 16 #define MAXOPLEN 10 #define OBOFFSET 26 #define OBOPERAND 34 #define OBLINEEND 77 /***** static tables and variables *****/ static char regtab[] = "alcldlblahchdhbhaxcxdxbxspbpsidi"; /* reg table */ static char *mrmtb16[] = { "bx+si", /* modRM string table (16-bit) */ "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx" }; static char *mrmtb32[] = { "eax", /* modRM string table (32-bit) */ "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" }; static char seg16[8] = { REGDS, REGDS, REGSS, REGSS, REGDS, REGDS, REGSS, REGDS }; static char reg16[8] = { REGEBX, REGEBX, REGEBP, REGEBP, REGESI, REGEDI, REGEBP, REGEBX }; static char reg16_2[4] = { REGESI, REGEDI, REGESI, REGEDI }; static char seg32[8] = { REGDS, REGDS, REGDS, REGDS, REGSS, REGSS, REGDS, REGDS }; static char reg32[8] = { REGEAX, REGECX, REGEDX, REGEBX, REGESP, REGEBP, REGESI, REGEDI }; static char sregtab[] = "ecsdfg"; // first letter of ES, CS, SS, DS, FS, GS char hexdigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; static int mod; /* mod of mod/rm byte */ static int rm; /* rm of mod/rm byte */ static int ttt; /* return reg value (of mod/rm) */ static unsigned char *pMem; /* current position in instruction */ static int mode_32; /* local addressing mode indicator */ static int opsize_32; /* operand size flag */ ULONG EAaddr[2]; // offset of effective address static int EAsize[2]; // size of effective address item static char *pchEAseg[2]; // normal segment for operand int G_mode_32 = 1; /* global address mode indicator */ static BOOLEAN fMovX; // indicates a MOVSX or MOVZX // internal function definitions BOOLEAN disasm(PDEBUGPACKET, PULONG, PUCHAR, BOOLEAN); void DIdoModrm(PDEBUGPACKET dp,char **, int, BOOLEAN); void OutputHexString(char **, char *, int); void OutputHexValue(char **, char *, int, int); void OutputHexCode(char **, char *, int); void OutputString(char **, char *); void OutputSymbol(PDEBUGPACKET, char **, char *, int, int); void GetNextOffset(PDEBUGPACKET, PULONG, BOOLEAN); void OutputHexAddr(PUCHAR *, ULONG); USHORT GetSegRegValue(PDEBUGPACKET,int); /**** disasm - disassemble an 80x86/80x87 instruction * * Input: * pOffset = pointer to offset to start disassembly * fEAout = if set, include EA (effective address) * * Output: * pOffset = pointer to offset of next instruction * pchDst = pointer to result string * ***************************************************************************/ BOOLEAN disasm( PDEBUGPACKET dp, PULONG pOffset, PUCHAR pchDst, BOOLEAN fEAout ) { int opcode; /* current opcode */ int olen = 2; /* operand length */ int alen = 2; /* address length */ int end = FALSE; /* end of instruction flag */ int mrm = FALSE; /* indicator that modrm is generated*/ unsigned char *action; /* action for operand interpretation*/ long tmp; /* temporary storage field */ int indx; /* temporary index */ int action2; /* secondary action */ int instlen; /* instruction length */ int cBytes; // bytes read into instr buffer int segOvr = 0; /* segment override opcode */ char membuf[MAXL]; /* current instruction buffer */ char *pEAlabel = ""; // optional label for operand char *pchResultBuf = pchDst; // working copy of pchDst pointer char RepPrefixBuffer[32]; // rep prefix buffer char *pchRepPrefixBuf = RepPrefixBuffer; // pointer to prefix buffer char OpcodeBuffer[8]; // opcode buffer char *pchOpcodeBuf = OpcodeBuffer; // pointer to opcode buffer char OperandBuffer[80]; // operand buffer char *pchOperandBuf = OperandBuffer; // pointer to operand buffer char ModrmBuffer[80]; // modRM buffer char *pchModrmBuf = ModrmBuffer; // pointer to modRM buffer char EABuffer[42]; // effective address buffer char *pchEABuf = EABuffer; // pointer to EA buffer int obOpcode = OBOFFSET; int obOpcodeMin; int obOpcodeMax; int obOperand = OBOPERAND; int obOperandMin; int obOperandMax; int cbOpcode; int cbOperand; int cbOffset; int cbEAddr; int fTwoLines = FALSE; fMovX = FALSE; EAsize[0] = EAsize[1] = 0; // no effective address pchEAseg[0] = dszDS_; pchEAseg[1] = dszES_; mode_32 = opsize_32 = (G_mode_32 == 1); /* local addressing mode */ olen = alen = (1 + mode_32) << 1; // set operand/address lengths // 2 for 16-bit and 4 for 32-bit OutputHexAddr(&pchResultBuf, *pOffset); *pchResultBuf++ = ' '; DoMemoryRead( dp, (LPVOID)*pOffset, (LPVOID)membuf, MAXL, &cBytes ); /* move full inst to local buffer */ pMem = membuf; /* point to begin of instruction */ opcode = *pMem++; /* get opcode */ OutputString(&pchOpcodeBuf, distbl[opcode].instruct); action = actiontbl + distbl[opcode].opr; /* get operand action */ /***** loop through all operand actions *****/ do { action2 = (*action) & 0xc0; switch((*action++) & 0x3f) { case ALT: /* alter the opcode if 32-bit */ if (opsize_32) { indx = *action++; pchOpcodeBuf = &OpcodeBuffer[indx]; if (indx == 0) OutputString(&pchOpcodeBuf, dszCWDE); else { *pchOpcodeBuf++ = 'd'; if (indx == 1) *pchOpcodeBuf++ = 'q'; } } break; case STROP: // compute size of operands in indx // also if dword operands, change fifth // opcode letter from 'w' to 'd'. if (opcode & 1) { if (opsize_32) { indx = 4; OpcodeBuffer[4] = 'd'; } else indx = 2; } else indx = 1; if (*action & 1) { if (fEAout) { EAaddr[0] = (DWORD)GetRegValue(dp, REGESI); EAsize[0] = indx; } } if (*action++ & 2) { if (fEAout) { EAaddr[1] = (DWORD)GetRegValue(dp, REGEDI); EAsize[1] = indx; } } break; case CHR: /* insert a character */ *pchOperandBuf++ = *action++; break; case CREG: /* set debug, test or control reg */ if ((opcode - 231) & 0x04) // remove bias from opcode *pchOperandBuf++ = 't'; else if ((opcode - 231) & 0x01) *pchOperandBuf++ = 'd'; else *pchOperandBuf++ = 'c'; *pchOperandBuf++ = 'r'; *pchOperandBuf++ = (char)('0' + ttt); break; case SREG2: /* segment register */ ttt = BIT53(opcode); // set value to fall through case SREG3: /* segment register */ *pchOperandBuf++ = sregtab[ttt]; // reg is part of modrm *pchOperandBuf++ = 's'; break; case BRSTR: /* get index to register string */ ttt = *action++; /* from action table */ goto BREGlabel; case BOREG: /* byte register (in opcode) */ ttt = BIT20(opcode); /* register is part of opcode */ goto BREGlabel; case ALSTR: ttt = 0; /* point to AL register */ BREGlabel: case BREG: /* general register */ *pchOperandBuf++ = regtab[ttt * 2]; *pchOperandBuf++ = regtab[ttt * 2 + 1]; break; case WRSTR: /* get index to register string */ ttt = *action++; /* from action table */ goto WREGlabel; case VOREG: /* register is part of opcode */ ttt = BIT20(opcode); goto VREGlabel; case AXSTR: ttt = 0; /* point to eAX register */ VREGlabel: case VREG: /* general register */ if (opsize_32) /* test for 32bit mode */ *pchOperandBuf++ = 'e'; WREGlabel: case WREG: /* register is word size */ *pchOperandBuf++ = regtab[ttt * 2 + 16]; *pchOperandBuf++ = regtab[ttt * 2 + 17]; break; case IST_ST: OutputString(&pchOperandBuf, "st(0),st"); *(pchOperandBuf - 5) += rm; break; case ST_IST: OutputString(&pchOperandBuf, "st,"); case IST: OutputString(&pchOperandBuf, "st(0)"); *(pchOperandBuf - 2) += rm; break; case xBYTE: /* set instruction to byte only */ EAsize[0] = 1; pEAlabel = "byte ptr "; break; case VAR: if (opsize_32) goto DWORDlabel; case xWORD: EAsize[0] = 2; pEAlabel = "word ptr "; break; case EDWORD: opsize_32 = 1; // for control reg move, use eRegs case xDWORD: DWORDlabel: EAsize[0] = 4; pEAlabel = "dword ptr "; break; case QWORD: EAsize[0] = 8; pEAlabel = "qword ptr "; break; case TTBYTE: EAsize[0] = 10; pEAlabel = "tbyte ptr "; break; case FARPTR: if (opsize_32) { EAsize[0] = 6; pEAlabel = "fword ptr "; } else { EAsize[0] = 4; pEAlabel = "dword ptr "; } break; case LMODRM: // output modRM data type if (mod != 3) OutputString(&pchOperandBuf, pEAlabel); else EAsize[0] = 0; case MODRM: /* output modrm string */ if (segOvr) /* in case of segment override */ OutputString(&pchOperandBuf, distbl[segOvr].instruct); *pchModrmBuf = '\0'; OutputString(&pchOperandBuf, ModrmBuffer); break; case ADDRP: /* address pointer */ OutputHexString(&pchOperandBuf, pMem + olen, 2); // segment *pchOperandBuf++ = ':'; OutputSymbol(dp, &pchOperandBuf, pMem, olen, segOvr); pMem += olen + 2; break; case REL8: /* relative address 8-bit */ if (opcode == 0xe3 && mode_32) { pchOpcodeBuf = OpcodeBuffer; OutputString(&pchOpcodeBuf, dszJECXZ); } tmp = (long)*(char *)pMem++; /* get the 8-bit rel offset */ goto DoRelDispl; case REL16: /* relative address 16-/32-bit */ tmp = 0; memmove(&tmp,pMem,sizeof(long)); pMem += alen; /* skip over offset */ DoRelDispl: tmp += *pOffset + (pMem - membuf); /* calculate address */ OutputSymbol(dp, &pchOperandBuf, (char *) &tmp, alen, segOvr); // address break; case UBYTE: // unsigned byte for int/in/out OutputHexString(&pchOperandBuf, pMem, 1); // ubyte pMem++; break; case IB: /* operand is immediate byte */ if ((opcode & ~1) == 0xd4) { // postop for AAD/AAM is 0x0a if (*pMem++ != 0x0a) // test post-opcode byte OutputString(&pchOperandBuf, dszRESERVED); break; } olen = 1; /* set operand length */ goto DoImmed; case IW: /* operand is immediate word */ olen = 2; /* set operand length */ case IV: /* operand is word or dword */ DoImmed: OutputHexValue(&pchOperandBuf, pMem, olen, FALSE); pMem += olen; break; case OFFS: /* operand is offset */ EAsize[0] = (opcode & 1) ? olen : 1; if (segOvr) /* in case of segment override */ OutputString(&pchOperandBuf, distbl[segOvr].instruct); *pchOperandBuf++ = '['; OutputSymbol(dp,&pchOperandBuf, pMem, alen, segOvr); // offset pMem += alen; *pchOperandBuf++ = ']'; break; case GROUP: /* operand is of group 1,2,4,6 or 8 */ /* output opcode symbol */ OutputString(&pchOpcodeBuf, group[*action++][ttt]); break; case GROUPT: /* operand is of group 3,5 or 7 */ indx = *action; /* get indx into group from action */ goto doGroupT; case EGROUPT: /* x87 ESC (D8-DF) group index */ indx = BIT20(opcode) * 2; /* get group index from opcode */ if (mod == 3) { /* some operand variations exists */ /* for x87 and mod == 3 */ ++indx; /* take the next group table entry */ if (indx == 3) { /* for x87 ESC==D9 and mod==3 */ if (ttt > 3) { /* for those D9 instructions */ indx = 12 + ttt; /* offset index to table by 12 */ ttt = rm; /* set secondary index to rm */ } } else if (indx == 7) { /* for x87 ESC==DB and mod==3 */ if (ttt == 4) /* only valid if ttt==4 */ ttt = rm; /* set secondary group table index */ else ttt = 7; /* no an x87 instruction */ } } doGroupT: /* handle group with different types of operands */ OutputString(&pchOpcodeBuf, groupt[indx][ttt].instruct); action = actiontbl + groupt[indx][ttt].opr; /* get new action */ break; case OPC0F: /* secondary opcode table (opcode 0F) */ opcode = *pMem++; /* get real opcode */ fMovX = (BOOLEAN)(opcode == 0xBF || opcode == 0xB7); if (opcode < 7) /* for the first 7 opcodes */ opcode += 256; /* point begin of secondary opcode tab. */ else if (opcode > 0x1f && opcode < 0x27) opcode += 231; /* adjust for non-existing opcodes */ else if (opcode > 0x2f && opcode < 0x33) opcode += 222; /* adjust for non-existing opcodes */ else if (opcode > 0x7e && opcode < 0xd0) opcode += 148; /* adjust for non-existing opcodes */ else opcode = 260; /* all non-existing opcodes */ goto getNxtByte1; case ADR_OVR: /* address override */ mode_32 = !G_mode_32; /* override addressing mode */ alen = (mode_32 + 1) << 1; /* toggle address length */ goto getNxtByte; case OPR_OVR: /* operand size override */ opsize_32 = !G_mode_32; /* override operand size */ olen = (opsize_32 + 1) << 1; /* toggle operand length */ goto getNxtByte; case SEG_OVR: /* handle segment override */ segOvr = opcode; /* save segment override opcode */ pchOpcodeBuf = OpcodeBuffer; // restart the opcode string goto getNxtByte; case REP: /* handle rep/lock prefixes */ *pchOpcodeBuf = '\0'; if (pchRepPrefixBuf != RepPrefixBuffer) *pchRepPrefixBuf++ = ' '; OutputString(&pchRepPrefixBuf, OpcodeBuffer); pchOpcodeBuf = OpcodeBuffer; getNxtByte: opcode = *pMem++; /* next byte is opcode */ getNxtByte1: action = actiontbl + distbl[opcode].opr; OutputString(&pchOpcodeBuf, distbl[opcode].instruct); default: /* opcode has no operand */ break; } switch (action2) { /* secondary action */ case MRM: /* generate modrm for later use */ if (!mrm) { /* ignore if it has been generated */ DIdoModrm(dp, &pchModrmBuf, segOvr, fEAout); /* generate modrm */ mrm = TRUE; /* remember its generation */ } break; case COM: /* insert a comma after operand */ *pchOperandBuf++ = ','; break; case END: /* end of instruction */ end = TRUE; break; } } while (!end); /* loop til end of instruction */ /***** prepare disassembled instruction for output *****/ instlen = pMem - membuf; if (instlen < cBytes) cBytes = instlen; OutputHexCode(&pchResultBuf, membuf, cBytes); if (instlen > cBytes) { *pchResultBuf++ = '?'; *pchResultBuf++ = '?'; (*pOffset)++; // point past unread byte } *pOffset += instlen; /* set instruction length */ if (instlen > cBytes) { do *pchResultBuf++ = ' '; while (pchResultBuf < pchDst + OBOFFSET); OutputString(&pchResultBuf, "???"); *pchResultBuf++ = '\0'; return FALSE; } // if fEAout is set, build each EA with trailing space in EABuf // point back over final trailing space if buffer nonnull if (fEAout) { for (indx = 0; indx < 2; indx++) if (EAsize[indx]) { OutputString(&pchEABuf, segOvr ? distbl[segOvr].instruct : pchEAseg[indx]); OutputHexAddr(&pchEABuf, EAaddr[indx]); *pchEABuf++ = '='; DoMemoryRead( dp, (LPVOID)EAaddr[indx], (LPVOID)membuf, EAsize[indx], &tmp ); if (tmp == EAsize[indx]) OutputHexString(&pchEABuf, (char *)membuf, EAsize[indx]); else while (EAsize[indx]--) { *pchEABuf++ = '?'; *pchEABuf++ = '?'; } *pchEABuf++ = ' '; } if (pchEABuf != EABuffer) pchEABuf--; } // compute lengths of component strings. // if the rep string is nonnull, // add the opcode string length to the operand // make the rep string the opcode string cbOffset = pchResultBuf - pchDst; cbOperand = pchOperandBuf - OperandBuffer; cbOpcode = pchOpcodeBuf - OpcodeBuffer; if (pchRepPrefixBuf != RepPrefixBuffer) { cbOperand += cbOpcode + (cbOperand != 0); cbOpcode = pchRepPrefixBuf - RepPrefixBuffer; } cbEAddr = pchEABuf - EABuffer; // for really long strings, where the opcode and operand // will not fit on a 77-character line, make two lines // with the opcode on offset 0 on the second line with // the operand following after one space if (cbOpcode + cbOperand > OBLINEEND - 1) { fTwoLines = TRUE; obOpcode = 0; obOperand = cbOpcode + 1; } else { // compute the minimum and maximum offset values for // opcode and operand strings. // if strings are nonnull, add extra for separating space obOpcodeMin = cbOffset + 1; obOperandMin = obOpcodeMin + cbOpcode + 1; obOperandMax = OBLINEEND - cbEAddr - (cbEAddr != 0) - cbOperand; obOpcodeMax = obOperandMax - (cbOperand != 0) - cbOpcode; // if minimum offset is more than the maximum, the strings // will not fit on one line. recompute the min/max // values with no offset and EA strings. if (obOpcodeMin > obOpcodeMax) { fTwoLines = TRUE; obOpcodeMin = 0; obOperandMin = cbOpcode + 1; obOperandMax = OBLINEEND - cbOperand; obOpcodeMax = obOperandMax - (cbOperand != 0) - cbOpcode; } // compute the opcode and operand offsets. set offset as // close to the default values as possible. if (obOpcodeMin > OBOFFSET) obOpcode = obOpcodeMin; else if (obOpcodeMax < OBOFFSET) obOpcode = obOpcodeMax; obOperandMin = obOpcode + cbOpcode + 1; if (obOperandMin > OBOPERAND) obOperand = obOperandMin; else if (obOperandMax < OBOPERAND) obOperand = obOperandMax; } // build the resultant string with the offsets computed // if two lines are to be output, // append the EAddr string // output a new line and reset the pointer if (fTwoLines) { if (pchEABuf != EABuffer) { do *pchResultBuf++ = ' '; while (pchResultBuf < pchDst + OBLINEEND - cbEAddr); *pchEABuf = '\0'; OutputString(&pchResultBuf, EABuffer); OutputString(&pchResultBuf, "\n "); } pchDst = pchResultBuf; } // output rep, opcode, and operand strings do *pchResultBuf++ = ' '; while (pchResultBuf < pchDst + obOpcode); if (pchRepPrefixBuf != RepPrefixBuffer) { *pchRepPrefixBuf = '\0'; OutputString(&pchResultBuf, RepPrefixBuffer); do *pchResultBuf++ = ' '; while (pchResultBuf < pchDst + obOperand); } *pchOpcodeBuf = '\0'; OutputString(&pchResultBuf, OpcodeBuffer); if (pchOperandBuf != OperandBuffer) { do *pchResultBuf++ = ' '; while (pchResultBuf < pchDst + obOperand); *pchOperandBuf = '\0'; OutputString(&pchResultBuf, OperandBuffer); } // if one line is to be output, append the EAddr string if (!fTwoLines && pchEABuf != EABuffer) { *pchEABuf = '\0'; do *pchResultBuf++ = ' '; while (pchResultBuf < pchDst + OBLINEEND - cbEAddr); OutputString(&pchResultBuf, EABuffer); } *pchResultBuf = '\0'; return TRUE; } /*...........................internal function..............................*/ /* */ /* generate a mod/rm string */ /* */ void DIdoModrm (PDEBUGPACKET dp, char **ppchBuf, int segOvr, BOOLEAN fEAout) { int mrm; /* modrm byte */ char *src; /* source string */ int sib; int ss; int ind; int oldrm; mrm = *pMem++; /* get the mrm byte from instruction */ mod = BIT76(mrm); /* get mod */ ttt = BIT53(mrm); /* get reg - used outside routine */ rm = BIT20(mrm); /* get rm */ if (mod == 3) { /* register only mode */ src = ®tab[rm * 2]; /* point to 16-bit register */ if (EAsize[0] > 1) { src += 16; /* point to 16-bit register */ if (opsize_32 && !fMovX) *(*ppchBuf)++ = 'e'; /* make it a 32-bit register */ } *(*ppchBuf)++ = *src++; /* copy register name */ *(*ppchBuf)++ = *src; EAsize[0] = 0; // no EA value to output return; } if (mode_32) { /* 32-bit addressing mode */ oldrm = rm; if (rm == 4) { /* rm == 4 implies sib byte */ sib = *pMem++; /* get s_i_b byte */ rm = BIT20(sib); /* return base */ } *(*ppchBuf)++ = '['; if (mod == 0 && rm == 5) { OutputSymbol(dp,ppchBuf, pMem, 4, segOvr); // offset pMem += 4; } else { if (fEAout) { if (segOvr) { EAaddr[0] = (DWORD)GetRegValue(dp, reg32[rm]); pchEAseg[0] = distbl[segOvr].instruct; } else if (reg32[rm] == REGEBP || reg32[rm] == REGESP) { EAaddr[0] = (DWORD)GetRegValue(dp, reg32[rm]); pchEAseg[0] = dszSS_; } else EAaddr[0] = (DWORD)GetRegValue(dp, reg32[rm]); } OutputString(ppchBuf, mrmtb32[rm]); } if (oldrm == 4) { // finish processing sib ind = BIT53(sib); if (ind != 4) { *(*ppchBuf)++ = '+'; OutputString(ppchBuf, mrmtb32[ind]); ss = 1 << BIT76(sib); if (ss != 1) { *(*ppchBuf)++ = '*'; *(*ppchBuf)++ = (char)(ss + '0'); } if (fEAout) EAaddr[0] = (DWORD)GetRegValue(dp, reg32[ind]); } } } else { // 16-bit addressing mode *(*ppchBuf)++ = '['; if (mod == 0 && rm == 6) { OutputSymbol(dp,ppchBuf, pMem, 2, segOvr); // 16-bit offset pMem += 2; } else { if (fEAout) { if (segOvr) { EAaddr[0] = (DWORD)GetRegValue(dp, reg16[rm]); pchEAseg[0] = distbl[segOvr].instruct; } else if (reg16[rm] == REGEBP) { EAaddr[0] = (DWORD)GetRegValue(dp, reg16[rm]); pchEAseg[0] = dszSS_; } else EAaddr[0] = (DWORD)GetRegValue(dp, reg16[rm]); if (rm < 4) EAaddr[0] += (DWORD)GetRegValue(dp, reg16_2[rm]); } OutputString(ppchBuf, mrmtb16[rm]); } } // output any displacement if (mod == 1) { if (fEAout) EAaddr[0] += (ULONG)pMem; OutputHexValue(ppchBuf, pMem, 1, TRUE); pMem++; } else if (mod == 2) { long tmp = 0; if (mode_32) { memmove(&tmp,pMem,sizeof(long)); if (fEAout) EAaddr[0] += (ULONG)tmp; OutputHexValue(ppchBuf, pMem, 4, TRUE); pMem += 4; } else { memmove(&tmp,pMem,sizeof(short)); if (fEAout) EAaddr[0] += tmp; OutputHexValue(ppchBuf, pMem, 2, TRUE); pMem += 2; } } if (!mode_32 && fEAout) { EAaddr[0] &= 0xffff; EAaddr[1] &= 0xffff; } *(*ppchBuf)++ = ']'; } /*** OutputHexValue - output hex value * * Purpose: * Output the value pointed by *ppchBuf of the specified * length. The value is treated as signed and leading * zeroes are not printed. The string is prefaced by a * '+' or '-' sign as appropriate. * * Input: * *ppchBuf - pointer to text buffer to fill * *pchMemBuf - pointer to memory buffer to extract value * length - length in bytes of value (1, 2, and 4 supported) * fDisp - set if displacement to output '+' * * Output: * *ppchBuf - pointer updated to next text character * *************************************************************************/ void OutputHexValue (char **ppchBuf, char *pchMemBuf, int length, int fDisp) { long value; int index; char digit[8]; value = 0; if (length == 1) value = (long)(*(char *)pchMemBuf); else if (length == 2) memmove(&value,pchMemBuf,2); else memmove(&value,pchMemBuf,sizeof(long)); length <<= 1; // shift once to get hex length if (value != 0 || !fDisp) { if (fDisp) if (value < 0 && length == 2) { // use neg value for byte value = -value; // displacement *(*ppchBuf)++ = '-'; } else *(*ppchBuf)++ = '+'; *(*ppchBuf)++ = '0'; *(*ppchBuf)++ = 'x'; for (index = length - 1; index != -1; index--) { digit[index] = (char)(value & 0xf); value >>= 4; } index = 0; while (digit[index] == 0 && index < length - 1) index++; while (index < length) *(*ppchBuf)++ = hexdigit[digit[index++]]; } } /*** OutputHexString - output hex string * * Purpose: * Output the value pointed by *ppchMemBuf of the specified * length. The value is treated as unsigned and leading * zeroes are printed. * * Input: * *ppchBuf - pointer to text buffer to fill * *pchValue - pointer to memory buffer to extract value * length - length in bytes of value * * Output: * *ppchBuf - pointer updated to next text character * *ppchMemBuf - pointer update to next memory byte * *************************************************************************/ void OutputHexString (char **ppchBuf, char *pchValue, int length) { unsigned char chMem; pchValue += length; while (length--) { chMem = *--pchValue; *(*ppchBuf)++ = hexdigit[chMem >> 4]; *(*ppchBuf)++ = hexdigit[chMem & 0x0f]; } } /*** OutputHexCode - output hex code * * Purpose: * Output the code pointed by pchMemBuf of the specified * length. The value is treated as unsigned and leading * zeroes are printed. This differs from OutputHexString * in that bytes are printed from low to high addresses. * * Input: * *ppchBuf - pointer to text buffer to fill * pchMemBuf - pointer to memory buffer to extract value * length - length in bytes of value * * Output: * *ppchBuf - pointer updated to next text character * *************************************************************************/ void OutputHexCode (char **ppchBuf, char *pchMemBuf, int length) { unsigned char chMem; while (length--) { chMem = *pchMemBuf++; *(*ppchBuf)++ = hexdigit[chMem >> 4]; *(*ppchBuf)++ = hexdigit[chMem & 0x0f]; } } /*** OutputString - output string * * Purpose: * Copy the string into the buffer pointed by *ppBuf. * * Input: * *pStr - pointer to string * * Output: * *ppBuf points to next character in buffer. * *************************************************************************/ void OutputString (char **ppBuf, char *pStr) { while (*pStr) *(*ppBuf)++ = *pStr++; } /*** OutputSymbol - output symbolic value * * Purpose: * Output the value in outvalue into the buffer * pointed by *pBuf. Express the value as a * symbol plus displacment, if possible. * * Input: * *ppBuf - pointer to text buffer to fill * *pValue - pointer to memory buffer to extract value * length - length in bytes of value * * Output: * *ppBuf - pointer updated to next text character * *************************************************************************/ void OutputSymbol (PDEBUGPACKET dp, char **ppBuf, char *pValue, int length, int segOvr) { ULONG displacement; ULONG value; char *szSymName; value = 0; if (length == 1) value = (long)(*(char *)pValue); else if (length == 2) memmove(&value,pValue,sizeof(short)); else memmove(&value,pValue,sizeof(long)); EAaddr[0] = value; if (SymGetSymFromAddr( dp->hProcess, value, &displacement, sym )) { szSymName = sym->Name; OutputString(ppBuf, szSymName); OutputHexValue(ppBuf, (char *)&displacement, length, TRUE); *(*ppBuf)++ = ' '; *(*ppBuf)++ = '('; OutputHexString(ppBuf, pValue, length); *(*ppBuf)++ = ')'; } else OutputHexString(ppBuf, pValue, length); } /*** X86GetNextOffset - compute offset for trace or step * * Purpose: * From a limited disassembly of the instruction pointed * by the FIR register, compute the offset of the next * instruction for either a trace or step operation. * * Input: * fStep - TRUE if step offset returned - FALSE for trace offset * * Returns: * step or trace offset if input is TRUE or FALSE, respectively * -1 returned for trace flag to be used * *************************************************************************/ void GetNextOffset (PDEBUGPACKET dp, PULONG pcaddr, BOOLEAN fStep) { int mode_32; int opsize_32; int cBytes; char membuf[MAXL]; // current instruction buffer ULONG addrReturn; USHORT retAddr[3]; // return address buffer char *pMem; UCHAR opcode; int fPrefix = TRUE; int fRepPrefix = FALSE; int ttt; int rm; ULONG instroffset; // read instruction stream bytes into membuf and set mode and // opcode size flags *pcaddr = (DWORD)GetRegValue(dp,REGEIP); instroffset = *pcaddr; G_mode_32 = TRUE; mode_32 = opsize_32 = (G_mode_32 == 1); /* local addressing mode */ DoMemoryRead( dp, (LPVOID)*pcaddr, (LPVOID)membuf, MAXL, &cBytes ); /* move full inst to local buffer */ pMem = membuf; /* point to begin of instruction */ // read and process any prefixes first do { opcode = (UCHAR)*pMem++; /* get opcode */ if (opcode == 0x66) opsize_32 = !G_mode_32; else if (opcode == 0x67) mode_32 = !G_mode_32; else if ((opcode & ~1) == 0xf2) fRepPrefix = TRUE; else if (opcode != 0xf0 && (opcode & ~0x18) != 0x26 && (opcode & ~1) != 0x64) fPrefix = FALSE; } while (fPrefix); // for instructions that alter the TF (trace flag), return the // offset of the next instruction despite the flag of fStep if (((opcode & ~0x3) == 0x9c)) // 9c-9f, pushf, popf, sahf, lahf ; else if (opcode == 0xcf) { // cf - iret - get RA from stack addrReturn = (DWORD)GetRegValue(dp, REGESP); DoMemoryRead( dp, (LPVOID)addrReturn, (LPVOID)retAddr, sizeof(retAddr), NULL ); *pcaddr = retAddr[2]; return; } // repeated string/port instructions if (opcode == 0xe8) // near direct jump pMem += (1 + opsize_32) * 2; else if (opcode == 0x9a) // far direct jump pMem += (2 + opsize_32) * 2; else if (opcode == 0xcd || (opcode >= 0xe0 && opcode <= 0xe2)) // loop / int nn instrs pMem++; else if (opcode == 0xff) { // indirect call - compute length opcode = *pMem++; // get modRM ttt = BIT53(opcode); if ((ttt & ~1) == 2) { mod = BIT76(opcode); if (mod != 3) { // nonregister 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 register 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 } } } else instroffset = (ULONG)-1; // 0xff, but not call } else if (!((fRepPrefix && ((opcode & ~3) == 0x6c || (opcode & ~3) == 0xa4 || (opcode & ~1) == 0xaa || (opcode & ~3) == 0xac)) || opcode == 0xcc || opcode == 0xce)) instroffset = (ULONG)-1; // not repeated string op // or int 3 / into // if not enough bytes were read for instruction parse, // just give up and trace the instruction if (cBytes < pMem - membuf) instroffset = (ULONG)-1; // if not tracing, compute the new instruction offset if (instroffset != (ULONG)-1) instroffset += pMem - membuf; *pcaddr = instroffset; } void OutputHexAddr (PUCHAR *ppBuffer, ULONG offset) { OutputHexString(ppBuffer, (char *)&offset, sizeof(ULONG)); } USHORT GetSegRegValue (PDEBUGPACKET dp, int segOpcode) { ULONG regnum; switch (segOpcode) { case 0x26: regnum = REGES; break; case 0x2e: regnum = REGCS; break; case 0x36: regnum = REGSS; break; case 0x64: regnum = REGFS; break; case 0x65: regnum = REGGS; break; case 0x3e: default: regnum = REGDS; } return (USHORT)GetRegValue(dp,regnum); }