//////////////////////////////////////////////////////////////////////////////// // // // OllyDbg Disassembling Engine v2.01 // // // // Copyright (c) 2007-2013 Oleh Yuschuk, ollydbg@t-online.de // // // // This code is part of the OllyDbg Disassembler v2.01 // // // // Disassembling engine is free software; you can redistribute it and/or // // modify it under the terms of the GNU General Public License as published // // by the Free Software Foundation; either version 3 of the License, or (at // // your option) any later version. // // // // This code is distributed in the hope that it will be useful, but WITHOUT // // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // // more details. // // // // You should have received a copy of the GNU General Public License along // // with this program. If not, see . // // // //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // // This is a fast disassembler that can be used to determine the length of // // the binary 80x86 32-bit command and its attributes, to convert it to the // // human-readable text form, highlight its operands, and create hexadecimal // // dump of the binary command. // // // // It is a stripped down version of the disassembler used by OllyDbg 2.01. // // It can't analyse and comment the contents of the operands, or predict the // // results of the command execution. Analysis-dependent features are not // // included, too. Most other features are kept. // // // // Disassembler supports integer, FPU, MMX, 3DNow, SSE1-SSE4.1 and AVX // // instructions. 64-bit mode, AVX2, FMA and XOP are not (yet) supported. // // // // This code can be compiled either in ASCII or UNICODE mode. It is reentrant // // (thread-safe, feature not available in the original OllyDbg code). // // // // Typical operation speed on 3-GHz Phenom II in MASM mode is: // // // // Command length and info: 130 ns/command (7,700,000 commands/s) // // Disassembly: 290 ns/command (3,400,000 commands/s) // // Disassembly, dump, highlighting: 350 ns/command (2,800,000 commands/s) // // // //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #pragma hdrstop #include "disasm.h" //////////////////////////////////////////////////////////////////////////////// //////////////////////////////// SYMBOLIC NAMES //////////////////////////////// // 8-bit register names, sorted by 'natural' index (as understood by CPU, not // in the alphabetical order as some 'programmers' prefer). const tchar *regname8[NREG] = { T("AL"), T("CL"), T("DL"), T("BL"), T("AH"), T("CH"), T("DH"), T("BH") }; // 16-bit register names. const tchar *regname16[NREG] = { T("AX"), T("CX"), T("DX"), T("BX"), T("SP"), T("BP"), T("SI"), T("DI") }; // 32-bit register names. const tchar *regname32[NREG] = { T("EAX"), T("ECX"), T("EDX"), T("EBX"), T("ESP"), T("EBP"), T("ESI"), T("EDI") }; // Names of segment registers. const tchar *segname[NREG] = { T("ES"), T("CS"), T("SS"), T("DS"), T("FS"), T("GS"), T("SEG6:"), T("SEG7:") }; // Names of FPU registers, classical form. const tchar *fpulong[NREG] = { T("ST(0)"), T("ST(1)"), T("ST(2)"), T("ST(3)"), T("ST(4)"), T("ST(5)"), T("ST(6)"), T("ST(7)") }; // Names of FPU registers, short form. const tchar *fpushort[NREG] = { T("ST0"), T("ST1"), T("ST2"), T("ST3"), T("ST4"), T("ST5"), T("ST6"), T("ST7") }; // Names of MMX/3DNow! registers. const tchar *mmxname[NREG] = { T("MM0"), T("MM1"), T("MM2"), T("MM3"), T("MM4"), T("MM5"), T("MM6"), T("MM7") }; // Names of 128-bit SSE registers. const tchar *sse128[NREG] = { T("XMM0"), T("XMM1"), T("XMM2"), T("XMM3"), T("XMM4"), T("XMM5"), T("XMM6"), T("XMM7") }; // Names of 256-bit SSE registers. const tchar *sse256[NREG] = { T("YMM0"), T("YMM1"), T("YMM2"), T("YMM3"), T("YMM4"), T("YMM5"), T("YMM6"), T("YMM7") }; // Names of control registers. const tchar *crname[NREG] = { T("CR0"), T("CR1"), T("CR2"), T("CR3"), T("CR4"), T("CR5"), T("CR6"), T("CR7") }; // Names of debug registers. const tchar *drname[NREG] = { T("DR0"), T("DR1"), T("DR2"), T("DR3"), T("DR4"), T("DR5"), T("DR6"), T("DR7") }; // Declarations for data types. Depending on ssesizemode, name of 16-byte data // type (DQWORD) may be changed to XMMWORD and that of 32-bit type (QQWORD) to // YMMWORD. const tchar *sizename[33] = { NULL, T("BYTE"), T("WORD"), NULL, T("DWORD"), NULL, T("FWORD"), NULL, T("QWORD"), NULL, T("TBYTE"), NULL, NULL, NULL, NULL, NULL, T("DQWORD"), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, T("QQWORD") }; // Keywords for immediate data. HLA uses sizename[] instead of sizekey[]. const tchar *sizekey[33] = { NULL, T("DB"), T("DW"), NULL, T("DD"), NULL, T("DF"), NULL, T("DQ"), NULL, T("DT"), NULL, NULL, NULL, NULL, NULL, T("DDQ"), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, T("DQQ") }; // Keywords for immediate data in AT&T format. const tchar *sizeatt[33] = { NULL, T(".BYTE"), T(".WORD"), NULL, T(".LONG"), NULL, T(".FWORD"), NULL, T(".QUAD"), NULL, T(".TBYTE"), NULL, NULL, NULL, NULL, NULL, T(".DQUAD"), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, T(".QQUAD") }; // Comparison predicates in SSE [0..7] and VEX commands [0..31]. const tchar *ssepredicate[32] = { T("EQ"), T("LT"), T("LE"), T("UNORD"), T("NEQ"), T("NLT"), T("NLE"), T("ORD"), T("EQ_UQ"), T("NGE"), T("NGT"), T("FALSE"), T("NEQ_OQ"), T("GE"), T("GT"), T("TRUE"), T("EQ_OS"), T("LT_OQ"), T("LE_OQ"), T("UNORD_S"), T("NEQ_US"), T("NLT_UQ"), T("NLE_UQ"), T("ORD_S"), T("EQ_US"), T("NGE_UQ"), T("NGT_UQ"), T("FALSE_OS"), T("NEQ_OS"), T("GE_OQ"), T("GT_OQ"), T("TRUE_US") }; //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// DISASSEMBLER ///////////////////////////////// typedef struct t_imdata { // Intermediate disassembler data t_disasm *da; // Result of disassembly ulong damode; // Disassembling mode, set of DA_xxx t_config *config; // Disassembler configuration int (*decodeaddress)(tchar *s,ulong addr); ulong prefixlist; // List of command's prefixes, PF_xxx int ssesize; // Size of SSE operands (16/32 bytes) ulong immsize1; // Size of first immediate constant ulong immsize2; // Size of second immediate constant ulong mainsize; // Size of command with prefixes ulong modsize; // Size of ModRegRM/SIB bytes ulong dispsize; // Size of address offset int usesdatasize; // May have data size prefix int usesaddrsize; // May have address size prefix int usessegment; // May have segment override prefix } t_imdata; static t_config defconfig = { // Default disassembler configuration DAMODE_MASM, // Main style, one of DAMODE_xxx NUM_STD|NUM_DECIMAL, // Constant part of address, NUM_xxx NUM_STD|NUM_LONG, // Jump/call destination, NUM_xxx NUM_STD|NUM_LONG, // Binary constants, NUM_xxx NUM_STD|NUM_DECIMAL, // Numeric constants, NUM_xxx 0, // Force lowercase display 0, // Tab between mnemonic and arguments 0, // Extra space between arguments 0, // Use RET instead of RETN 1, // Use short form of string commands 0, // Display default segments in listing 1, // Always show memory size 0, // Show NEAR modifiers 1, // How to decode size of SSE operands 0, // How to decode jump hints 0, // How to decode size-sensitive mnemonics 0, // How to decode top of FPU stack 0 // Highlight operands }; static t_config attconfig = { // AT&T disassembler configuration DAMODE_ATT, // Main style, one of DAMODE_xxx NUM_X|NUM_DECIMAL, // Constant part of address, NUM_xxx NUM_X|NUM_LONG, // Jump/call destination, NUM_xxx NUM_X|NUM_LONG, // Binary constants, NUM_xxx NUM_X|NUM_DECIMAL, // Numeric constants, NUM_xxx 1, // Force lowercase display 1, // Tab between mnemonic and arguments 1, // Extra space between arguments 0, // Use RET instead of RETN 1, // Use short form of string commands 0, // Display default segments in listing 0, // Always show memory size 0, // Show NEAR modifiers 1, // How to decode size of SSE operands 0, // How to decode jump hints 0, // How to decode size-sensitive mnemonics 0, // How to decode top of FPU stack 0 // Highlight operands }; //////////////////////////////////////////////////////////////////////////////// ////////////////////////////// SERVICE FUNCTIONS /////////////////////////////// static tchar hexcharu[16] = { // Nibble-to-hexdigit table, uppercase T('0'), T('1'), T('2'), T('3'), T('4'), T('5'), T('6'), T('7'), T('8'), T('9'), T('A'), T('B'), T('C'), T('D'), T('E'), T('F') }; static tchar hexcharl[16] = { // Nibble-to-hexdigit table, lowercase T('0'), T('1'), T('2'), T('3'), T('4'), T('5'), T('6'), T('7'), T('8'), T('9'), T('a'), T('b'), T('c'), T('d'), T('e'), T('f') }; static tchar cvtlower[256]; // Copies at most n-1 wide characters from src to dest and assures that dest is // null-terminated. Slow but reliable. Returns number of copied characters, not // including the terminal null. Attention, does not check that input parameters // are correct! static int Tstrcopy(tchar *dest,int n,const tchar *src) { int i; if (n<=0) return 0; for (i=0; i0) { d=*code++; s[n++]=hexchar[(d>>4) & 0x0F]; s[n++]=hexchar[d & 0x0F]; ncode--; }; s[n]=T('\0'); return n; }; // Converts unsigned 1-, 2- or 4-byte number to hexadecimal text, according to // the specified mode and type of argument. String s must be at least SHORTNAME // characters long. Returns length of resulting text in characters, not // including the terminal zero. static int Hexprint(int size,tchar *s,ulong u,const t_imdata *im,ulong arg) { int i,k,ndigit,lastdigit; ulong nummode,mod; tchar buf[SHORTNAME]; static tchar *hexchar; if (size==1) u&=0x000000FF; // 8-bit number else if (size==2) u&=0x0000FFFF; // 16-bit number else size=4; // Correct possible errors mod=arg & B_MODMASK; if (mod==B_ADDR) nummode=im->config->memmode; else if (mod==B_JMPCALL || mod==B_JMPCALLFAR) nummode=im->config->jmpmode; else if (mod==B_BINARY) nummode=im->config->binconstmode; else nummode=im->config->constmode; hexchar=(im->config->lowercase?hexcharl:hexcharu); buf[SHORTNAME-1]=T('\0'); k=SHORTNAME-1; if ((nummode & NUM_DECIMAL)!=0 && (mod==B_SIGNED || mod==B_UNSIGNED || (u=10) buf[--k]=T('.'); // Period marks decimals in OllyDbg do { buf[--k]=hexchar[u%10]; u/=10; } while (u!=0); } else { // Decode as hexadecimal number. if (nummode & NUM_LONG) // 2, 4 or 8 significant digits ndigit=size*2; else ndigit=1; if ((nummode & NUM_STYLE)==NUM_STD) buf[--k]=T('h'); for (i=0; i>4) & 0x0FFFFFFF; }; if ((nummode & NUM_STYLE)==NUM_X) { buf[--k]=T('x'); buf[--k]=T('0'); } else if (lastdigit>=10 && ((nummode & NUM_STYLE)!=NUM_OLLY || i<(mod==B_BINARY?size*2:8))) buf[--k]=T('0'); ; }; return Tstrcopy(s,SHORTNAME,buf+k); }; //////////////////////////////////////////////////////////////////////////////// ///////////////////////// INTERNAL DISASSEMBLER TABLES ///////////////////////// t_chain *cmdchain; // Commands sorted by first CMDMASK bits t_modrm modrm16[256]; // 16-bit ModRM decodings t_modrm modrm32[256]; // 32-bit ModRM decodings without SIB t_modrm sib0[256]; // ModRM-SIB decodings with Mod=00 t_modrm sib1[256]; // ModRM-SIB decodings with Mod=01 t_modrm sib2[256]; // ModRM-SIB decodings with Mod=10 // Initializes disassembler tables. Call this function once during startup. // Returns 0 on success and -1 if initialization was unsuccessful. In the last // case, continuation is not possible and program must terminate. int Preparedisasm(void) { int n,c,reg,sreg,scale,nchain; ulong u,code,mask; const t_bincmd *pcmd; t_chain *pchain; t_modrm *pmrm,*psib; if (cmdchain!=NULL) return 0; // Already initialized // Sort command descriptors into command chains by first CMDMASK bits. cmdchain=(t_chain *)malloc(NCHAIN*sizeof(t_chain)); if (cmdchain==NULL) // Low memory return -1; memset(cmdchain,0,NCHAIN*sizeof(t_chain)); nchain=CMDMASK+1; // Number of command chains for (pcmd=bincmd; pcmd->length!=0; pcmd++) { if ((pcmd->cmdtype & D_CMDTYPE)==D_PSEUDO) continue; // Pseudocommand, for search models only code=pcmd->code; mask=pcmd->mask & CMDMASK; for (u=0; upcmd!=NULL && pchain->pnext!=NULL) pchain=pchain->pnext; // Walk chain to the end if (pchain->pcmd==NULL) pchain->pcmd=pcmd; else if (nchain>=NCHAIN) return -1; // Too many commands else { pchain->pnext=cmdchain+nchain; // Prolongate chain pchain=pchain->pnext; pchain->pcmd=pcmd; nchain++; }; }; }; // Prepare 16-bit ModRM decodings. memset(modrm16,0,sizeof(modrm16)); for (c=0x00,pmrm=modrm16; c<=0xFF; c++,pmrm++) { reg=c & 0x07; if ((c & 0xC0)==0xC0) { // Register in ModRM. pmrm->size=1; pmrm->features=0; // Register, its type as yet unknown pmrm->reg=reg; pmrm->defseg=SEG_UNDEF; pmrm->basereg=REG_UNDEF; } else if ((c & 0xC7)==0x06) { // Special case of immediate address. pmrm->size=3; pmrm->dispsize=2; pmrm->features=OP_MEMORY|OP_OPCONST|OP_ADDR16; pmrm->reg=REG_UNDEF; pmrm->defseg=SEG_DS; pmrm->basereg=REG_UNDEF; } else { pmrm->features=OP_MEMORY|OP_INDEXED|OP_ADDR16; if ((c & 0xC0)==0x40) { pmrm->dispsize=1; pmrm->features|=OP_OPCONST; } else if ((c & 0xC0)==0x80) { pmrm->dispsize=2; pmrm->features|=OP_OPCONST; }; pmrm->size=pmrm->dispsize+1; pmrm->reg=REG_UNDEF; switch (reg) { case 0: pmrm->scale[REG_EBX]=1; pmrm->scale[REG_ESI]=1; pmrm->defseg=SEG_DS; tstrcpy(pmrm->ardec,T("BX+SI")); tstrcpy(pmrm->aratt,T("%BX,%SI")); pmrm->aregs=(1<basereg=REG_ESI; break; case 1: pmrm->scale[REG_EBX]=1; pmrm->scale[REG_EDI]=1; pmrm->defseg=SEG_DS; tstrcpy(pmrm->ardec,T("BX+DI")); tstrcpy(pmrm->aratt,T("%BX,%DI")); pmrm->aregs=(1<basereg=REG_EDI; break; case 2: pmrm->scale[REG_EBP]=1; pmrm->scale[REG_ESI]=1; pmrm->defseg=SEG_SS; tstrcpy(pmrm->ardec,T("BP+SI")); tstrcpy(pmrm->aratt,T("%BP,%SI")); pmrm->aregs=(1<basereg=REG_ESI; break; case 3: pmrm->scale[REG_EBP]=1; pmrm->scale[REG_EDI]=1; pmrm->defseg=SEG_SS; tstrcpy(pmrm->ardec,T("BP+DI")); tstrcpy(pmrm->aratt,T("%BP,%DI")); pmrm->aregs=(1<basereg=REG_EDI; break; case 4: pmrm->scale[REG_ESI]=1; pmrm->defseg=SEG_DS; tstrcpy(pmrm->ardec,T("SI")); tstrcpy(pmrm->aratt,T("%SI")); pmrm->aregs=(1<basereg=REG_ESI; break; case 5: pmrm->scale[REG_EDI]=1; pmrm->defseg=SEG_DS; tstrcpy(pmrm->ardec,T("DI")); tstrcpy(pmrm->aratt,T("%DI")); pmrm->aregs=(1<basereg=REG_EDI; break; case 6: pmrm->scale[REG_EBP]=1; pmrm->defseg=SEG_SS; tstrcpy(pmrm->ardec,T("BP")); tstrcpy(pmrm->aratt,T("%BP")); pmrm->aregs=(1<basereg=REG_EBP; break; case 7: pmrm->scale[REG_EBX]=1; pmrm->defseg=SEG_DS; tstrcpy(pmrm->ardec,T("BX")); tstrcpy(pmrm->aratt,T("%BX")); pmrm->aregs=(1<basereg=REG_EBX; break; }; }; }; // Prepare 32-bit ModRM decodings without SIB. memset(modrm32,0,sizeof(modrm32)); for (c=0x00,pmrm=modrm32; c<=0xFF; c++,pmrm++) { reg=c & 0x07; if ((c & 0xC0)==0xC0) { // Register in ModRM. pmrm->size=1; pmrm->features=0; // Register, its type as yet unknown pmrm->reg=reg; pmrm->defseg=SEG_UNDEF; pmrm->basereg=REG_UNDEF; } else if ((c & 0xC7)==0x05) { // Special case of 32-bit immediate address. pmrm->size=5; pmrm->dispsize=4; pmrm->features=OP_MEMORY|OP_OPCONST; pmrm->reg=REG_UNDEF; pmrm->defseg=SEG_DS; pmrm->basereg=REG_UNDEF; } else { // Regular memory address. pmrm->features=OP_MEMORY; pmrm->reg=REG_UNDEF; if ((c & 0xC0)==0x40) { pmrm->dispsize=1; // 8-bit sign-extended displacement pmrm->features|=OP_OPCONST; } else if ((c & 0xC0)==0x80) { pmrm->dispsize=4; // 32-bit displacement pmrm->features|=OP_OPCONST; }; if (reg==REG_ESP) { // SIB byte follows, decode with sib32. if ((c & 0xC0)==0x00) pmrm->psib=sib0; else if ((c & 0xC0)==0x40) pmrm->psib=sib1; else pmrm->psib=sib2; pmrm->basereg=REG_UNDEF; } else { pmrm->size=1+pmrm->dispsize; pmrm->features|=OP_INDEXED; pmrm->defseg=(reg==REG_EBP?SEG_SS:SEG_DS); pmrm->scale[reg]=1; tstrcpy(pmrm->ardec,regname32[reg]); pmrm->aratt[0]=T('%'); Tstrcopy(pmrm->aratt+1,SHORTNAME-1,regname32[reg]); pmrm->aregs=(1<basereg=reg; }; }; }; // Prepare 32-bit ModRM decodings with SIB, case Mod=00: usually no disp. memset(sib0,0,sizeof(sib0)); for (c=0x00,psib=sib0; c<=0xFF; c++,psib++) { psib->features=OP_MEMORY; psib->reg=REG_UNDEF; reg=c & 0x07; sreg=(c>>3) & 0x07; if ((c & 0xC0)==0) scale=1; else if ((c & 0xC0)==0x40) scale=2; else if ((c & 0xC0)==0x80) scale=4; else scale=8; if (sreg!=REG_ESP) { psib->scale[sreg]=(uchar)scale; n=Tstrcopy(psib->ardec,SHORTNAME,regname32[sreg]); psib->aregs=(1<features|=OP_INDEXED; if (scale>1) { psib->ardec[n++]=T('*'); psib->ardec[n++]=(tchar)(T('0')+scale); psib->ardec[n]=T('\0'); }; } else n=0; if (reg==REG_EBP) { psib->size=6; psib->dispsize=4; psib->features|=OP_OPCONST; psib->defseg=SEG_DS; psib->basereg=REG_UNDEF; } else { psib->size=2; psib->defseg=((reg==REG_ESP || reg==REG_EBP)?SEG_SS:SEG_DS); psib->scale[reg]++; psib->features|=OP_INDEXED; if (n!=0) psib->ardec[n++]=T('+'); Tstrcopy(psib->ardec+n,SHORTNAME-n,regname32[reg]); psib->aregs|=(1<basereg=reg; }; if (reg!=REG_EBP) { psib->aratt[0]=T('%'); n=1; n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,regname32[reg]); } else n=0; if (sreg!=REG_ESP) { n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,T(",%")); n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,regname32[sreg]); if (scale>1) { psib->aratt[n++]=T(','); psib->aratt[n++]=(tchar)(T('0')+scale); psib->aratt[n]=T('\0'); }; }; }; // Prepare 32-bit ModRM decodings with SIB, case Mod=01: 8-bit displacement. memset(sib1,0,sizeof(sib1)); for (c=0x00,psib=sib1; c<=0xFF; c++,psib++) { psib->features=OP_MEMORY|OP_INDEXED|OP_OPCONST; psib->reg=REG_UNDEF; reg=c & 0x07; sreg=(c>>3) & 0x07; if ((c & 0xC0)==0) scale=1; else if ((c & 0xC0)==0x40) scale=2; else if ((c & 0xC0)==0x80) scale=4; else scale=8; psib->size=3; psib->dispsize=1; psib->defseg=((reg==REG_ESP || reg==REG_EBP)?SEG_SS:SEG_DS); psib->scale[reg]=1; psib->basereg=reg; psib->aregs=(1<scale[sreg]+=(uchar)scale; n=Tstrcopy(psib->ardec,SHORTNAME,regname32[sreg]); psib->aregs|=(1<1) { psib->ardec[n++]=T('*'); psib->ardec[n++]=(tchar)(T('0')+scale); }; } else n=0; if (n!=0) psib->ardec[n++]=T('+'); Tstrcopy(psib->ardec+n,SHORTNAME-n,regname32[reg]); psib->aratt[0]=T('%'); n=1; n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,regname32[reg]); if (sreg!=REG_ESP) { n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,T(",%")); n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,regname32[sreg]); if (scale>1) { psib->aratt[n++]=T(','); psib->aratt[n++]=(tchar)(T('0')+scale); psib->aratt[n]=T('\0'); }; }; }; // Prepare 32-bit ModRM decodings with SIB, case Mod=10: 32-bit displacement. memset(sib2,0,sizeof(sib2)); for (c=0x00,psib=sib2; c<=0xFF; c++,psib++) { psib->features=OP_MEMORY|OP_INDEXED|OP_OPCONST; psib->reg=REG_UNDEF; reg=c & 0x07; sreg=(c>>3) & 0x07; if ((c & 0xC0)==0) scale=1; else if ((c & 0xC0)==0x40) scale=2; else if ((c & 0xC0)==0x80) scale=4; else scale=8; psib->size=6; psib->dispsize=4; psib->defseg=((reg==REG_ESP || reg==REG_EBP)?SEG_SS:SEG_DS); psib->scale[reg]=1; psib->basereg=reg; psib->aregs=(1<scale[sreg]+=(uchar)scale; n=Tstrcopy(psib->ardec,SHORTNAME,regname32[sreg]); psib->aregs|=(1<1) { psib->ardec[n++]=T('*'); psib->ardec[n++]=(tchar)(T('0')+scale); }; } else n=0; if (n!=0) psib->ardec[n++]=T('+'); Tstrcopy(psib->ardec+n,SHORTNAME-n,regname32[reg]); psib->aratt[0]=T('%'); n=1; n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,regname32[reg]); if (sreg!=REG_ESP) { n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,T(",%")); n+=Tstrcopy(psib->aratt+n,SHORTNAME-n,regname32[sreg]); if (scale>1) { psib->aratt[n++]=T(','); psib->aratt[n++]=(tchar)(T('0')+scale); psib->aratt[n]=T('\0'); }; }; }; // Fill lowercase conversion table. This table replaces tolower(). When // compiled with Borland C++ Builder, spares significant time. for (c=0; c<256; c++) cvtlower[c]=(tchar)ttolower(c); // Report success. return 0; }; // Frees resources allocated by Preparedisasm(). Call this function once // during shutdown after disassembling service is no longer necessary. void Finishdisasm(void) { if (cmdchain!=NULL) { free(cmdchain); cmdchain=NULL; }; }; //////////////////////////////////////////////////////////////////////////////// ////////////////////////////// AUXILIARY ROUTINES ////////////////////////////// // Given index of byte register, returns index of 32-bit container. static int Byteregtodwordreg(int bytereg) { if (bytereg<0 || bytereg>=NREG) return REG_UNDEF; if (bytereg>=4) return bytereg-4; return bytereg; }; // Checks prefix override flags and generates warnings if prefix is superfluous. // Returns index of segment register. Note that Disasm() assures that two // segment override bits in im->prefixlist can't be set simultaneously. static int Getsegment(t_imdata *im,int arg,int defseg) { if ((im->prefixlist & PF_SEGMASK)==0) return defseg; // Optimization for most frequent case switch (im->prefixlist & PF_SEGMASK) { case PF_ES: if (defseg==SEG_ES) im->da->warnings|=DAW_DEFSEG; if (arg & B_NOSEG) im->da->warnings|=DAW_SEGPREFIX; return SEG_ES; case PF_CS: if (defseg==SEG_CS) im->da->warnings|=DAW_DEFSEG; if (arg & B_NOSEG) im->da->warnings|=DAW_SEGPREFIX; return SEG_CS; case PF_SS: if (defseg==SEG_SS) im->da->warnings|=DAW_DEFSEG; if (arg & B_NOSEG) im->da->warnings|=DAW_SEGPREFIX; return SEG_SS; case PF_DS: if (defseg==SEG_DS) im->da->warnings|=DAW_DEFSEG; if (arg & B_NOSEG) im->da->warnings|=DAW_SEGPREFIX; return SEG_DS; case PF_FS: if (defseg==SEG_FS) im->da->warnings|=DAW_DEFSEG; if (arg & B_NOSEG) im->da->warnings|=DAW_SEGPREFIX; return SEG_FS; case PF_GS: if (defseg==SEG_GS) im->da->warnings|=DAW_DEFSEG; if (arg & B_NOSEG) im->da->warnings|=DAW_SEGPREFIX; return SEG_GS; default: return defseg; // Most frequent case of default segment }; }; // Decodes generalized memory address to text. static void Memaddrtotext(t_imdata *im,int arg,int datasize,int seg, const tchar *regpart,long constpart,tchar *s) { int n; tchar label[TEXTLEN]; if (im->config->disasmmode==DAMODE_ATT) { // AT&T memory address syntax is so different from Intel that I process it // separately from the rest. n=0; if ((arg & B_MODMASK)==B_JMPCALL) s[n++]=T('*'); // On request, I show only explicit segments. if ((im->config->putdefseg && (arg & B_NOSEG)==0) || (im->prefixlist & PF_SEGMASK)!=0 ) { s[n++]=T('%'); n+=Tcopycase(s+n,TEXTLEN-n,segname[seg],im->config->lowercase); s[n++]=T(':'); }; // Add constant part (offset). if (constpart<0 && constpart>NEGLIMIT) { s[n++]=T('-'); n+=Hexprint((im->prefixlist & PF_ASIZE?2:4),s+n,-constpart,im,B_ADDR); } else if (constpart!=0) { if (seg!=SEG_FS && seg!=SEG_GS && im->decodeaddress!=NULL && im->decodeaddress(label,constpart)!=0) n+=Tstrcopy(s+n,TEXTLEN-n,label); else n+=Hexprint((im->prefixlist & PF_ASIZE?2:4),s+n,constpart,im,B_ADDR); ; }; // Add register part of address, may be absent. if (regpart[0]!=T('\0')) { n+=Tstrcopy(s+n,TEXTLEN-n,T("(")); n+=Tcopycase(s+n,TEXTLEN-n,regpart,im->config->lowercase); n+=Tstrcopy(s+n,TEXTLEN-n,T(")")); }; } else { // Mark far and near jump/call addresses. if ((arg & B_MODMASK)==B_JMPCALLFAR) n=Tcopycase(s,TEXTLEN,T("FAR "),im->config->lowercase); else if (im->config->shownear && (arg & B_MODMASK)==B_JMPCALL) n=Tcopycase(s,TEXTLEN,T("NEAR "),im->config->lowercase); else n=0; if (im->config->disasmmode!=DAMODE_MASM) { s[n++]=T('['); if ((im->prefixlist & PF_ASIZE)!=0 && regpart[0]==T('\0')) n+=Tcopycase(s+n,TEXTLEN-n,T("SMALL "),im->config->lowercase); ; }; // If operand is longer than 32 bytes or of type B_ANYMEM (memory contents // unimportant), its size is not displayed. Otherwise, bit B_SHOWSIZE // indicates that explicit operand's size can't be omitted. if (datasize<=32 && (arg & B_ARGMASK)!=B_ANYMEM && (im->config->showmemsize!=0 || (arg & B_SHOWSIZE)!=0) ) { if (im->config->disasmmode==DAMODE_HLA) n+=Tcopycase(s+n,TEXTLEN-n,T("TYPE "),im->config->lowercase); if ((arg & B_ARGMASK)==B_INTPAIR && im->config->disasmmode==DAMODE_IDEAL){ // If operand is a pair of integers (BOUND), Borland in IDEAL mode // expects size of single integer, whereas MASM requires size of the // whole pair. n+=Tcopycase(s+n,TEXTLEN-n,sizename[datasize/2],im->config->lowercase); s[n++]=T(' '); } else if (datasize==16 && im->config->ssesizemode==1) n+=Tcopycase(s+n,TEXTLEN-n,T("XMMWORD "),im->config->lowercase); else if (datasize==32 && im->config->ssesizemode==1) n+=Tcopycase(s+n,TEXTLEN-n,T("YMMWORD "),im->config->lowercase); else { n+=Tcopycase(s+n,TEXTLEN-n,sizename[datasize],im->config->lowercase); s[n++]=T(' '); }; if (im->config->disasmmode==DAMODE_MASM) n+=Tcopycase(s+n,TEXTLEN-n,T("PTR "),im->config->lowercase); ; }; // On request, I show only explicit segments. if ((im->config->putdefseg && (arg & B_NOSEG)==0) || (im->prefixlist & PF_SEGMASK)!=0 ) { n+=Tcopycase(s+n,TEXTLEN-n,segname[seg],im->config->lowercase); s[n++]=T(':'); }; if (im->config->disasmmode==DAMODE_MASM) { s[n++]=T('['); if ((im->prefixlist & PF_ASIZE)!=0 && regpart[0]==T('\0')) n+=Tcopycase(s+n,TEXTLEN-n,T("SMALL "),im->config->lowercase); ; }; // Add register part of address, may be absent. if (regpart[0]!=T('\0')) n+=Tcopycase(s+n,TEXTLEN-n,regpart,im->config->lowercase); if (regpart[0]!=T('\0') && constpart<0 && constpart>NEGLIMIT) { s[n++]=T('-'); n+=Hexprint((im->prefixlist & PF_ASIZE?2:4),s+n,-constpart,im,B_ADDR); } else if (constpart!=0 || regpart[0]==T('\0')) { if (regpart[0]!=T('\0')) s[n++]=T('+'); if (seg!=SEG_FS && seg!=SEG_GS && im->decodeaddress!=NULL && im->decodeaddress(label,constpart)!=0) n+=Tstrcopy(s+n,TEXTLEN-n,label); else n+=Hexprint((im->prefixlist & PF_ASIZE?2:4),s+n,constpart,im,B_ADDR); ; }; n+=Tstrcopy(s+n,TEXTLEN-n,T("]")); }; s[n]=T('\0'); }; // Service function, returns granularity of MMX, 3DNow! and SSE operands. static int Getgranularity(ulong arg) { int granularity; switch (arg & B_ARGMASK) { case B_MREG8x8: // MMX register as 8 8-bit integers case B_MMX8x8: // MMX reg/memory as 8 8-bit integers case B_MMX8x8DI: // MMX 8 8-bit integers at [DS:(E)DI] case B_XMM0I8x16: // XMM0 as 16 8-bit integers case B_SREGI8x16: // SSE register as 16 8-bit sigints case B_SVEXI8x16: // SSE reg in VEX as 16 8-bit sigints case B_SIMMI8x16: // SSE reg in immediate 8-bit constant case B_SSEI8x16: // SSE reg/memory as 16 8-bit sigints case B_SSEI8x16DI: // SSE 16 8-bit sigints at [DS:(E)DI] case B_SSEI8x8L: // Low 8 8-bit ints in SSE reg/memory case B_SSEI8x4L: // Low 4 8-bit ints in SSE reg/memory case B_SSEI8x2L: // Low 2 8-bit ints in SSE reg/memory granularity=1; break; case B_MREG16x4: // MMX register as 4 16-bit integers case B_MMX16x4: // MMX reg/memory as 4 16-bit integers case B_SREGI16x8: // SSE register as 8 16-bit sigints case B_SVEXI16x8: // SSE reg in VEX as 8 16-bit sigints case B_SSEI16x8: // SSE reg/memory as 8 16-bit sigints case B_SSEI16x4L: // Low 4 16-bit ints in SSE reg/memory case B_SSEI16x2L: // Low 2 16-bit ints in SSE reg/memory granularity=2; break; case B_MREG32x2: // MMX register as 2 32-bit integers case B_MMX32x2: // MMX reg/memory as 2 32-bit integers case B_3DREG: // 3DNow! register as 2 32-bit floats case B_3DNOW: // 3DNow! reg/memory as 2 32-bit floats case B_SREGF32x4: // SSE register as 4 32-bit floats case B_SVEXF32x4: // SSE reg in VEX as 4 32-bit floats case B_SREGF32L: // Low 32-bit float in SSE register case B_SVEXF32L: // Low 32-bit float in SSE in VEX case B_SREGF32x2L: // Low 2 32-bit floats in SSE register case B_SSEF32x4: // SSE reg/memory as 4 32-bit floats case B_SSEF32L: // Low 32-bit float in SSE reg/memory case B_SSEF32x2L: // Low 2 32-bit floats in SSE reg/memory granularity=4; break; case B_XMM0I32x4: // XMM0 as 4 32-bit integers case B_SREGI32x4: // SSE register as 4 32-bit sigints case B_SVEXI32x4: // SSE reg in VEX as 4 32-bit sigints case B_SREGI32L: // Low 32-bit sigint in SSE register case B_SREGI32x2L: // Low 2 32-bit sigints in SSE register case B_SSEI32x4: // SSE reg/memory as 4 32-bit sigints case B_SSEI32x2L: // Low 2 32-bit sigints in SSE reg/memory granularity=4; break; case B_MREG64: // MMX register as 1 64-bit integer case B_MMX64: // MMX reg/memory as 1 64-bit integer case B_XMM0I64x2: // XMM0 as 2 64-bit integers case B_SREGF64x2: // SSE register as 2 64-bit floats case B_SVEXF64x2: // SSE reg in VEX as 2 64-bit floats case B_SREGF64L: // Low 64-bit float in SSE register case B_SVEXF64L: // Low 64-bit float in SSE in VEX case B_SSEF64x2: // SSE reg/memory as 2 64-bit floats case B_SSEF64L: // Low 64-bit float in SSE reg/memory granularity=8; break; case B_SREGI64x2: // SSE register as 2 64-bit sigints case B_SVEXI64x2: // SSE reg in VEX as 2 64-bit sigints case B_SSEI64x2: // SSE reg/memory as 2 64-bit sigints case B_SREGI64L: // Low 64-bit sigint in SSE register granularity=8; break; default: granularity=1; // Treat unknown ops as string of bytes break; }; return granularity; }; //////////////////////////////////////////////////////////////////////////////// ////////////////////////// OPERAND DECODING ROUTINES /////////////////////////// // Decodes 8/16/32-bit integer register operand. ATTENTION, calling routine // must set usesdatasize and usesaddrsize by itself! static void Operandintreg(t_imdata *im,ulong datasize,int index,t_operand *op) { int n,reg32; op->features=OP_REGISTER; op->opsize=op->granularity=datasize; op->reg=index; op->seg=SEG_UNDEF; // Add container register to lists of used and modified registers. if (datasize==1) reg32=Byteregtodwordreg(index); else reg32=index; if ((op->arg & B_CHG)==0) { op->uses=(1<da->uses|=(1<arg & (B_CHG|B_UPD)) { op->modifies=(1<da->modifies|=(1<arg & B_NOESP)!=0 && reg32==REG_ESP) im->da->warnings|=DAW_NOESP; // Decode name of integer register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) { if ((op->arg & B_MODMASK)==B_JMPCALL) op->text[n++]=T('*'); op->text[n++]=T('%'); }; if (datasize==4) // Most frequent case first Tcopycase(op->text+n,TEXTLEN-n,regname32[index],im->config->lowercase); else if (datasize==1) Tcopycase(op->text+n,TEXTLEN-n,regname8[index],im->config->lowercase); else // 16-bit registers are seldom Tcopycase(op->text+n,TEXTLEN-n,regname16[index],im->config->lowercase); ; }; }; // Decodes 16/32-bit memory address in ModRM/SIB bytes. Returns full length of // address (ModRM+SIB+displacement) in bytes, 0 if ModRM indicates register // operand and -1 on error. ATTENTION, calling routine must set usesdatasize, // granularity (preset to datasize) and reg together with OP_MODREG by itself! static int Operandmodrm(t_imdata *im,ulong datasize,uchar *cmd,ulong cmdsize, t_operand *op) { tchar *ardec; t_modrm *pmrm; if (cmdsize==0) { im->da->errors|=DAE_CROSS; // Command crosses end of memory block return -1; }; // Decode ModRM/SIB. Most of the work is already done in Preparedisasm(), we // only need to find corresponding t_modrm. if (im->prefixlist & PF_ASIZE) { pmrm=modrm16+cmd[0]; // 16-bit address im->modsize=1; } else { pmrm=modrm32+cmd[0]; if (pmrm->psib==NULL) im->modsize=1; // No SIB byte else { if (cmdsize<2) { im->da->errors|=DAE_CROSS; // Command crosses end of memory block return -1; }; pmrm=pmrm->psib+cmd[1]; im->modsize=2; // Both ModRM and SIB }; }; // Check whether ModRM indicates register operand and immediately return if // true. As a side effect, modsize is already set. if ((cmd[0] & 0xC0)==0xC0) return 0; // Operand in memory. op->opsize=datasize; op->granularity=datasize; // Default, may be overriden later op->reg=REG_UNDEF; im->usesaddrsize=1; // Address size prefix is meaningful im->usessegment=1; // Segment override prefix is meaningful // Fetch precalculated t_modrm fields. op->features=pmrm->features; memcpy(op->scale,pmrm->scale,8); op->aregs=pmrm->aregs; im->da->uses|=pmrm->aregs; // Mark registers used to form address // Get displacement, if any. im->dispsize=pmrm->dispsize; if (pmrm->dispsize!=0) { if (cmdsizesize) { im->da->errors|=DAE_CROSS; // Command crosses end of memory block return -1; }; if (pmrm->dispsize==1) // 8-bit displacement is sign-extended op->opconst=im->da->memconst=(signed char)cmd[im->modsize]; else if (pmrm->dispsize==4) { // 32-bit full displacement im->da->memfixup= im->mainsize+im->modsize; // Possible 32-bit fixup op->opconst=im->da->memconst=*(ulong *)(cmd+im->modsize); } else // 16-bit displacement, very rare op->opconst=im->da->memconst=*(ushort *)(cmd+im->modsize); ; }; // Get segment. op->seg=Getsegment(im,op->arg,pmrm->defseg); // Warn if memory contents is 16-bit jump/call destination. if (datasize==2 && (op->arg & B_MODMASK)==B_JMPCALL) im->da->warnings|=DAW_JMP16; // Decode memory operand to text, if requested. if (im->damode & DA_TEXT) { ardec=(im->config->disasmmode==DAMODE_ATT?pmrm->aratt:pmrm->ardec); Memaddrtotext(im,op->arg,datasize,op->seg,ardec,op->opconst,op->text); }; return pmrm->size; }; // Decodes 16/32-bit immediate address (used only for 8/16/32-bit memory- // accumulator moves). ATTENTION, calling routine must set usesdatasize by // itself! static void Operandimmaddr(t_imdata *im,ulong datasize,uchar *cmd, ulong cmdsize,t_operand *op) { if (im->prefixlist & PF_ASIZE) im->dispsize=2; else im->dispsize=4; if (cmdsizedispsize) { im->da->errors|=DAE_CROSS; // Command crosses end of memory block return; }; op->features=OP_MEMORY|OP_OPCONST; op->opsize=op->granularity=datasize; op->reg=REG_UNDEF; im->usesaddrsize=1; // Address size prefix is meaningful im->usessegment=1; // Segment override prefix is meaningful if (im->dispsize==4) { // 32-bit immediate address // 32-bit address means possible fixup, calculate offset. im->da->memfixup=im->mainsize; op->opconst=im->da->memconst=*(ulong *)cmd; } else { // 16-bit immediate address, very rare op->opconst=im->da->memconst=*(ushort *)cmd; op->features|=OP_ADDR16; }; // Get segment. op->seg=Getsegment(im,op->arg,SEG_DS); // Decode memory operand to text, if requested. if (im->damode & DA_TEXT) Memaddrtotext(im,op->arg,datasize,op->seg,T(""),op->opconst,op->text); ; }; // Decodes simple register address ([reg16] or [reg32]). Flag changesreg must // be 0 if register remains unchanged and 1 if it changes. If fixseg is set to // SEG_UNDEF, assumes overridable DS:, otherwise assumes fixsegment that cannot // be overriden with segment prefix. If fixaddrsize is 2 or 4, assumes 16- or // 32-bit addressing only, otherwise uses default. ATTENTION, calling routine // must set usesdatasize by itself! static void Operandindirect(t_imdata *im,int index,int changesreg,int fixseg, int fixaddrsize,ulong datasize,t_operand *op) { int n; ulong originallist; tchar ardec[SHORTNAME]; op->features=OP_MEMORY|OP_INDEXED; if (changesreg) { op->features|=OP_MODREG; op->reg=index; im->da->modifies|=(1<reg=REG_UNDEF; if (fixaddrsize==2) op->features|=OP_ADDR16; else if (fixaddrsize==0) { im->usesaddrsize=1; // Address size prefix is meaningful if (im->prefixlist & PF_ASIZE) { op->features|=OP_ADDR16; fixaddrsize=2; }; }; // Get segment. if (fixseg==SEG_UNDEF) { op->seg=Getsegment(im,op->arg,SEG_DS); im->usessegment=1; } // Segment override prefix is meaningful else op->seg=fixseg; op->opsize=datasize; op->granularity=datasize; // Default, may be overriden later op->scale[index]=1; op->aregs=(1<da->uses|=(1<arg & B_MODMASK)==B_JMPCALL) im->da->warnings|=DAW_JMP16; // Decode source operand to text, if requested. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) { ardec[0]=T('%'); n=1; } else n=0; if (fixaddrsize==2) Tstrcopy(ardec+n,SHORTNAME-n,regname16[index]); else Tstrcopy(ardec+n,SHORTNAME-n,regname32[index]); if (fixseg==SEG_UNDEF) Memaddrtotext(im,op->arg,datasize,op->seg,ardec,0,op->text); else { originallist=im->prefixlist; im->prefixlist&=~PF_SEGMASK; Memaddrtotext(im,op->arg,datasize,op->seg,ardec,0,op->text); im->prefixlist=originallist; }; }; }; // Decodes XLAT source address ([(E)BX+AL]). Note that I set scale of EAX to 1, // which is not exactly true. ATTENTION, calling routine must set usesdatasize // by itself! static void Operandxlat(t_imdata *im,t_operand *op) { tchar *ardec; op->features=OP_MEMORY|OP_INDEXED; if (im->prefixlist & PF_ASIZE) op->features|=OP_ADDR16; im->usesaddrsize=1; // Address size prefix is meaningful im->usessegment=1; // Segment override prefix is meaningful op->opsize=1; op->granularity=1; op->reg=REG_UNDEF; // Get segment. op->seg=Getsegment(im,op->arg,SEG_DS); op->scale[REG_EAX]=1; // This is not correct! op->scale[REG_EBX]=1; op->aregs=(1<da->uses|=op->aregs; // Decode address to text, if requested. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) ardec=(im->prefixlist & PF_ASIZE?T("%BX,%AL"):T("%EBX,%AL")); else ardec=(im->prefixlist & PF_ASIZE?T("BX+AL"):T("EBX+AL")); Memaddrtotext(im,op->arg,1,op->seg,ardec,0,op->text); }; }; // Decodes stack pushes of any size, including implicit return address in // CALLs. ATTENTION, calling routine must set usesdatasize by itself! static void Operandpush(t_imdata *im,ulong datasize,t_operand *op) { int n,addrsize; ulong originallist; tchar ardec[SHORTNAME]; op->features=OP_MEMORY|OP_INDEXED|OP_MODREG; op->reg=REG_ESP; op->aregs=(1<da->modifies|=op->aregs; im->usesaddrsize=1; // Address size prefix is meaningful if (im->prefixlist & PF_ASIZE) { op->features|=OP_ADDR16; addrsize=2; } else addrsize=4; // Flat model! op->seg=SEG_SS; if ((op->arg & B_ARGMASK)==B_PUSHA) { im->da->uses=0xFF; // Uses all general registers op->opsize=datasize*8; } else if ((op->arg & B_ARGMASK)==B_PUSHRETF) { im->da->uses|=op->aregs; op->opsize=datasize*2; } else { im->da->uses|=op->aregs; // Warn if memory contents is 16-bit jump/call destination. if (datasize==2 && (op->arg & B_MODMASK)==B_JMPCALL) im->da->warnings|=DAW_JMP16; op->opsize=datasize; }; op->opconst=-(long)op->opsize; // ESP is predecremented op->granularity=datasize; // Default, may be overriden later op->scale[REG_ESP]=1; // Decode source operand to text, if requested. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) { ardec[0]=T('%'); n=1; } else n=0; if (addrsize==2) Tstrcopy(ardec+n,SHORTNAME-n,regname16[REG_ESP]); else Tstrcopy(ardec+n,SHORTNAME-n,regname32[REG_ESP]); originallist=im->prefixlist; im->prefixlist&=~PF_SEGMASK; Memaddrtotext(im,op->arg,datasize,op->seg,ardec,0,op->text); im->prefixlist=originallist; }; }; // Decodes segment register. static void Operandsegreg(t_imdata *im,int index,t_operand *op) { int n; op->features=OP_SEGREG; if (index>=NSEG) { op->features|=OP_INVALID; // Invalid segment register im->da->errors|=DAE_BADSEG; }; op->opsize=op->granularity=2; op->reg=index; op->seg=SEG_UNDEF; // Because this is not a memory address if (op->arg & (B_CHG|B_UPD)) im->da->warnings|=DAW_SEGMOD; // Modifies segment register // Decode name of segment register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('%'); Tcopycase(op->text+n,TEXTLEN-n,segname[index],im->config->lowercase); }; }; // Decodes FPU register operand. static void Operandfpureg(t_imdata *im,int index,t_operand *op) { op->features=OP_FPUREG; op->opsize=op->granularity=10; op->reg=index; op->seg=SEG_UNDEF; // Because this is not a memory address // Decode name of FPU register. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) { if (im->config->simplifiedst && index==0) Tcopycase(op->text,TEXTLEN,T("%ST"),im->config->lowercase); else { op->text[0]=T('%'); Tcopycase(op->text+1,TEXTLEN-1,fpushort[index],im->config->lowercase); }; } else if (im->config->simplifiedst && index==0) Tcopycase(op->text,TEXTLEN,T("ST"),im->config->lowercase); else if (im->config->disasmmode!=DAMODE_HLA) Tcopycase(op->text,TEXTLEN,fpulong[index],im->config->lowercase); else Tcopycase(op->text,TEXTLEN,fpushort[index],im->config->lowercase); ; }; }; // Decodes MMX register operands. ATTENTION, does not set correct granularity! static void Operandmmxreg(t_imdata *im,int index,t_operand *op) { int n; op->features=OP_MMXREG; op->opsize=8; op->granularity=4; // Default, correct it later! op->reg=index; op->seg=SEG_UNDEF; // Decode name of MMX/3DNow! register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('%'); Tcopycase(op->text+n,TEXTLEN-n,mmxname[index],im->config->lowercase); }; }; // Decodes 3DNow! register operands. ATTENTION, does not set correct // granularity! static void Operandnowreg(t_imdata *im,int index,t_operand *op) { int n; op->features=OP_3DNOWREG; op->opsize=8; op->granularity=4; // Default, correct it later! op->reg=index; op->seg=SEG_UNDEF; // Decode name of MMX/3DNow! register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('%'); Tcopycase(op->text+n,TEXTLEN-n,mmxname[index],im->config->lowercase); }; }; // Decodes SSE register operands. ATTENTION, does not set correct granularity! static void Operandssereg(t_imdata *im,int index,t_operand *op) { int n; op->features=OP_SSEREG; if (op->arg & B_NOVEXSIZE) op->opsize=16; else op->opsize=im->ssesize; op->granularity=4; // Default, correct it later! op->reg=index; op->seg=SEG_UNDEF; // Note that some rare SSE commands may use Reg without ModRM. if (im->modsize==0) im->modsize=1; // Decode name of SSE register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('%'); if (op->opsize==32) Tcopycase(op->text+n,TEXTLEN-n,sse256[index],im->config->lowercase); else Tcopycase(op->text+n,TEXTLEN-n,sse128[index],im->config->lowercase); ; }; }; // Decodes flag register EFL. static void Operandefl(t_imdata *im,ulong datasize,t_operand *op) { op->features=OP_OTHERREG; op->opsize=op->granularity=datasize; op->reg=REG_UNDEF; op->seg=SEG_UNDEF; // Decode name of register. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) Tcopycase(op->text,TEXTLEN,T("%EFL"),im->config->lowercase); else Tcopycase(op->text,TEXTLEN,T("EFL"),im->config->lowercase); ; }; }; // Decodes 8/16/32-bit immediate jump/call offset relative to EIP of next // command. static void Operandoffset(t_imdata *im,ulong offsetsize,ulong datasize, uchar *cmd,ulong cmdsize,ulong offsaddr,t_operand *op) { int n; tchar label[TEXTLEN]; if (cmdsizeda->errors|=DAE_CROSS; // Command crosses end of memory block return; }; op->features=OP_CONST; op->opsize=op->granularity=datasize; // NOT offsetsize! im->immsize1=offsetsize; op->reg=REG_UNDEF; op->seg=SEG_UNDEF; offsaddr+=offsetsize; if (offsetsize==1) // Sign-extandable constant op->opconst=*(signed char *)cmd+offsaddr; else if (offsetsize==2) // 16-bit immediate offset, rare op->opconst=*(ushort *)cmd+offsaddr; else // 32-bit immediate offset op->opconst=*(ulong *)cmd+offsaddr; if (datasize==2) { op->opconst&=0x0000FFFF; im->da->warnings|=DAW_JMP16; }; // Practically unused in Win32 code im->usesdatasize=1; // Decode address of destination to text, if requested. if (im->damode & DA_TEXT) { if (offsetsize==1 && im->config->disasmmode!=DAMODE_HLA && im->config->disasmmode!=DAMODE_ATT) n=Tcopycase(op->text,TEXTLEN,T("SHORT "),im->config->lowercase); else n=0; if (datasize==4) { if (im->decodeaddress!=NULL && im->decodeaddress(label,op->opconst)!=0) Tstrcopy(op->text+n,TEXTLEN-n,label); else { if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('$'); Hexprint(4,op->text+n,op->opconst,im,op->arg); }; } else { if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('$'); Hexprint(2,op->text+n,op->opconst,im,op->arg); }; }; }; // Decodes 16:16/16:32-bit immediate absolute far jump/call address. static void Operandimmfaraddr(t_imdata *im,ulong datasize,uchar *cmd, ulong cmdsize,t_operand *op) { int n; if (cmdsizeda->errors|=DAE_CROSS; // Command crosses end of memory block return; }; op->features=OP_CONST|OP_SELECTOR; op->opsize=datasize+2; op->granularity=datasize; // Attention, non-standard case! op->reg=REG_UNDEF; op->seg=SEG_UNDEF; im->immsize1=datasize; im->immsize2=2; if (datasize==2) { op->opconst=*(ushort *)cmd; im->da->warnings|=DAW_JMP16; } // Practically unused in Win32 code else { op->opconst=*(ulong *)cmd; im->da->immfixup=im->mainsize; }; op->selector=*(ushort *)(cmd+datasize); im->usesdatasize=1; // Decode address of destination to text, if requested. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) { op->text[0]=T('$'); n=1; } else n=Tcopycase(op->text,TEXTLEN,T("FAR "),im->config->lowercase); n+=Hexprint(2,op->text+n,op->selector,im,op->arg); op->text[n++]=T(':'); if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('$'); Hexprint(4,op->text+n,op->opconst,im,op->arg); }; }; // Decodes immediate constant 1 used in shift operations. static void Operandone(t_imdata *im,t_operand *op) { op->features=OP_CONST; op->opsize=op->granularity=1; // Just to make it defined op->reg=REG_UNDEF; op->seg=SEG_UNDEF; op->opconst=1; if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) Tstrcopy(op->text,TEXTLEN,T("$1")); else Tstrcopy(op->text,TEXTLEN,T("1")); ; }; }; // Decodes 8/16/32-bit immediate constant (possibly placed after ModRegRM-SIB- // Disp combination). Constant is nbytes long in the command and extends to // constsize bytes. If constant is a count, it deals with data of size datasize. // ATTENTION, calling routine must set usesdatasize by itself! static void Operandimmconst(t_imdata *im,ulong nbytes,ulong constsize, ulong datasize,uchar *cmd,ulong cmdsize,int issecond,t_operand *op) { int n; ulong u,mod; tchar label[TEXTLEN]; if (cmdsizemodsize+im->dispsize+nbytes+(issecond?im->immsize1:0)) { im->da->errors|=DAE_CROSS; // Command crosses end of memory block return; }; op->features=OP_CONST; op->opsize=op->granularity=constsize; cmd+=im->modsize+im->dispsize; if (issecond==0) im->immsize1=nbytes; // First constant else { im->immsize2=nbytes; // Second constant (ENTER only) cmd+=im->immsize1; }; op->reg=REG_UNDEF; op->seg=SEG_UNDEF; if (nbytes==4) { // 32-bit immediate constant op->opconst=*(ulong *)cmd; im->da->immfixup=im->mainsize+im->modsize+im->dispsize+ (issecond?im->immsize1:0); ; } else if (nbytes==1) // 8-byte constant, maybe sign-extendable op->opconst=*(signed char *)cmd; else // 16-bite immediate constant, rare op->opconst=*(ushort *)cmd; if (constsize==1) op->opconst&=0x000000FF; else if (constsize==2) op->opconst&=0x0000FFFF; switch (op->arg & B_MODMASK) { case B_BITCNT: // Constant is a bit count if ((datasize==4 && op->opconst>31) || (datasize==1 && op->opconst>7) || (datasize==2 && op->opconst>15)) im->da->warnings|=DAW_SHIFT; break; case B_SHIFTCNT: // Constant is a shift count if (op->opconst==0 || (datasize==4 && op->opconst>31) || (datasize==1 && op->opconst>7) || (datasize==2 && op->opconst>15)) im->da->warnings|=DAW_SHIFT; break; case B_STACKINC: // Stack increment must be DWORD-aligned if ((op->opconst & 0x3)!=0) im->da->warnings|=DAW_STACK; im->da->stackinc=op->opconst; break; default: break; }; if (im->damode & DA_TEXT) { mod=op->arg & B_MODMASK; n=0; if (constsize==1) { // 8-bit constant if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('$'); Hexprint(1,op->text+n,op->opconst,im,op->arg); } else if (constsize==4) { // 32-bit constant if (im->decodeaddress!=NULL && (mod==B_NONSPEC || mod==B_JMPCALL || mod==B_JMPCALLFAR) && im->decodeaddress(label,op->opconst)!=0) Tstrcopy(op->text+n,TEXTLEN-n,label); else { if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('$'); if (mod!=B_UNSIGNED && mod!=B_BINARY && mod!=B_PORT && (long)op->opconst<0 && (mod==B_SIGNED || (long)op->opconst>NEGLIMIT) ) { op->text[n++]=T('-'); u=-(long)op->opconst; } else u=op->opconst; Hexprint(4,op->text+n,u,im,op->arg); }; } else { // 16-bit constant if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('$'); else if ((op->arg & B_SHOWSIZE)!=0) { n+=Tcopycase(op->text+n,TEXTLEN-n,sizename[constsize], im->config->lowercase); n+=Tstrcopy(op->text+n,TEXTLEN-n,T(" ")); }; Hexprint(2,op->text+n,op->opconst,im,op->arg); }; }; return; }; // Decodes contrtol register operands. static void Operandcreg(t_imdata *im,int index,t_operand *op) { int n; op->features=OP_CREG; op->opsize=op->granularity=4; op->reg=index; op->seg=SEG_UNDEF; // Decode name of control register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('%'); Tcopycase(op->text+n,TEXTLEN-n,crname[index],im->config->lowercase); }; // Some control registers are physically absent. if (index!=0 && index!=2 && index!=3 && index!=4) im->da->errors|=DAE_BADCR; ; }; // Decodes debug register operands. static void Operanddreg(t_imdata *im,int index,t_operand *op) { int n; op->features=OP_DREG; op->opsize=op->granularity=4; op->reg=index; op->seg=SEG_UNDEF; // Decode name of debug register. if (im->damode & DA_TEXT) { n=0; if (im->config->disasmmode==DAMODE_ATT) op->text[n++]=T('%'); Tcopycase(op->text+n,TEXTLEN-n,drname[index],im->config->lowercase); }; }; // Decodes FPU status register FST. static void Operandfst(t_imdata *im,t_operand *op) { op->features=OP_OTHERREG; op->opsize=op->granularity=2; op->reg=REG_UNDEF; op->seg=SEG_UNDEF; // Decode name of register. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) Tcopycase(op->text,TEXTLEN,T("%FST"),im->config->lowercase); else Tcopycase(op->text,TEXTLEN,T("FST"),im->config->lowercase); ; }; }; // Decodes FPU control register FCW. static void Operandfcw(t_imdata *im,t_operand *op) { op->features=OP_OTHERREG; op->opsize=op->granularity=2; op->reg=REG_UNDEF; op->seg=SEG_UNDEF; // Decode name of register. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) Tcopycase(op->text,TEXTLEN,T("%FCW"),im->config->lowercase); else Tcopycase(op->text,TEXTLEN,T("FCW"),im->config->lowercase); ; }; }; // Decodes SSE control register MXCSR. static void Operandmxcsr(t_imdata *im,t_operand *op) { op->features=OP_OTHERREG; op->opsize=op->granularity=4; op->reg=REG_UNDEF; op->seg=SEG_UNDEF; // Decode name of register. if (im->damode & DA_TEXT) { if (im->config->disasmmode==DAMODE_ATT) Tcopycase(op->text,TEXTLEN,T("%MXCSR"),im->config->lowercase); else Tcopycase(op->text,TEXTLEN,T("MXCSR"),im->config->lowercase); ; }; }; //////////////////////////////////////////////////////////////////////////////// ///////////////////////////////// DISASSEMBLER ///////////////////////////////// // Disassembles first command in the binary code of given length at given // address. Assumes that address and data size attributes of all participating // segments are 32 bit (flat model). Returns length of the command or 0 in case // of severe error. ulong Disasm(uchar const *cmd,ulong cmdsize,ulong ip,t_disasm *da, int damode,t_config *config,int (*decodeaddress)(tchar *s,ulong addr)) { int i,j,k,q,noperand,nout,enclose,vexreg,success,cfill,ofill; ulong m,n,u,prefix,prefixmask,code,arg,cmdtype,datasize; ulong type,vex,vexlead; t_imdata im; const t_chain *pchain; const t_bincmd *pcmd; const t_modrm *pmrm; t_operand *op,pseudoop; // Verify input parameters. if (cmd==NULL || cmdsize==0 || da==NULL || cmdchain==NULL) return 0; // Error in parameters or uninitialized // Initialize t_disasm structure that receives results of disassembly. This // structure is very large, memset() or several memset()'s would take much, // much longer. da->ip=ip; da->memfixup=da->immfixup=-1; da->errors=DAE_NOERR; da->warnings=DAW_NOWARN; da->uses=0; da->modifies=0; da->memconst=0; da->stackinc=0; for (i=0,op=da->op; ifeatures=0; op->arg=0; op->opsize=op->granularity=0; op->reg=REG_UNDEF; op->uses=0; op->modifies=0; op->seg=SEG_UNDEF; ((ulong *)op->scale)[0]=0; ((ulong *)op->scale)[1]=0; op->aregs=0; op->opconst=0; op->selector=0; op->text[0]=T('\0'); }; da->dump[0]=T('\0'); da->result[0]=T('\0'); da->masksize=0; // Prepare intermediate data. This data allows to keep Disasm() reentrant // (thread-safe). im.da=da; im.damode=damode; if (config==NULL) im.config=config=&defconfig; // Use default configuration else im.config=config; im.decodeaddress=decodeaddress; im.prefixlist=0; im.ssesize=16; // Default im.immsize1=im.immsize2=0; // Correct 80x86 command may contain up to 4 prefixes belonging to different // prefix groups. If Disasm() detects second prefix from the same group, it // flushes first prefix in the sequence as a pseudocommand. (This is not // quite true; all CPUs that I have tested accept repeating prefixes. Still, // who will place superfluous and possibly nonportable prefixes into the // code?) for (n=0; ; n++) { if (n>=cmdsize) { // Command crosses end of memory block n=0; im.prefixlist=0; // Decode as standalone prefix break; }; // Note that some CPUs treat REPx and LOCK as belonging to the same group. switch (cmd[n]) { case 0x26: prefix=PF_ES; prefixmask=PF_SEGMASK; break; case 0x2E: prefix=PF_CS; prefixmask=PF_SEGMASK; break; case 0x36: prefix=PF_SS; prefixmask=PF_SEGMASK; break; case 0x3E: prefix=PF_DS; prefixmask=PF_SEGMASK; break; case 0x64: prefix=PF_FS; prefixmask=PF_SEGMASK; break; case 0x65: prefix=PF_GS; prefixmask=PF_SEGMASK; break; case 0x66: prefix=prefixmask=PF_DSIZE; break; case 0x67: prefix=prefixmask=PF_ASIZE; break; case 0xF0: prefix=prefixmask=PF_LOCK; break; case 0xF2: prefix=PF_REPNE; prefixmask=PF_REPMASK; break; case 0xF3: prefix=PF_REP; prefixmask=PF_REPMASK; break; default: prefix=0; break; }; if (prefix==0) break; if (im.prefixlist & prefixmask) { da->errors|=DAE_SAMEPREF; // Two prefixes from the same group break; }; im.prefixlist|=prefix; }; // There may be VEX prefix preceding command body. Yes, VEX is supported in // the 32-bit mode! And even in the 16-bit, but who cares? vex=0; vexlead=0; if (cmdsize>=n+3 && (*(ushort *)(cmd+n) & 0xC0FE)==0xC0C4) { // VEX is not compatible with LOCK, 66, F2 and F3 prefixes. VEX is not // compatible with REX, too, but REX prefixes are missing in 32-bit mode. if (im.prefixlist & (PF_LOCK|PF_66|PF_F2|PF_F3)) da->errors|=DAE_SAMEPREF; // Incompatible prefixes else { if (cmd[n]==0xC5) { // 2-byte VEX prefix. im.prefixlist|=PF_VEX2; vex=cmd[n+1]; vexlead=DX_VEX|DX_LEAD0F; n+=2; } else { // 3-byte VEX prefix. im.prefixlist|=PF_VEX3; vex=cmd[n+2]+(cmd[n+1]<<8); // Note the order of the bytes! switch (vex & 0x1F00) { case 0x0100: vexlead=DX_VEX|DX_LEAD0F; n+=3; break; case 0x0200: vexlead=DX_VEX|DX_LEAD38; n+=3; break; case 0x0300: vexlead=DX_VEX|DX_LEAD3A; n+=3; break; default: vex=0; break; // Unsupported VEX, decode as LES }; }; if (vex!=0) { // Get size of operands. if (vex & 0x0004) im.ssesize=32; // 256-bit SSE operands // Get register encoded in VEX prefix. vexreg=(~vex>>3) & 0x07; // Check for SIMD prefix. switch (vex & 0x3) { case 0x0001: im.prefixlist|=PF_66; break; case 0x0002: im.prefixlist|=PF_F3; break; case 0x0003: im.prefixlist|=PF_F2; break; }; }; }; if (n>=cmdsize) { // Command crosses end of memory block n=0; vex=0; im.prefixlist=0; // Decode as LES }; }; // We have gathered all prefixes, including those that are integral part of // the SSE command. if (n>4 || (da->errors & DAE_SAMEPREF)!=0) { if (n>4) da->errors|=DAE_MANYPREF; n=0; im.prefixlist=0; }; // Decode as standalone prefix da->prefixes=im.prefixlist; da->nprefix=n; // Fetch first 4 bytes of the command and find start of command chain in the // command table. if (cmdsize>=n+sizeof(ulong)) code=*(ulong *)(cmd+n); // Optimization for most frequent case else { code=cmd[n]; if (cmdsize>n+1) ((uchar *)&code)[1]=cmd[n+1]; if (cmdsize>n+2) ((uchar *)&code)[2]=cmd[n+2]; if (cmdsize>n+3) ((uchar *)&code)[3]=cmd[n+3]; }; // Walk chain and search for matching command. Command is matched if: // (1) code bits allowed in mask coincide in command and descriptor; // (2) when command type contains D_MEMORY, ModRegRM byte must indicate // memory, and when type contains D_REGISTER, Mod must indicate register; // (3) when bits D_DATAxx or D_ADDRxx are set, size of data and/or code must // match these bits; // (4) field D_MUSTMASK must match gathered prefixes; // (5) presence or absence of VEX prefix must be matched by DX_VEX. If VEX // is present, implied leading bytes must match vexlead and bit L must // match DX_VLMASK; // (6) if short form of string commands is requested, bit D_LONGFORM must be // cleared, or segment override prefix other that DS:, or address size // prefix must be present; // (7) when bit D_POSTBYTE is set, byte after ModRegRM/SIB/offset must match // postbyte. Note that all postbyted commands include memory address in // ModRegRM form and do not include immediate constants; // (8) if alternative forms of conditional commands are requested, command // is conditional, and it is marked as DX_ZEROMASK or DX_CARRYMASK, // check whether these bits match damode. (Conditional branch on flag // Z!=0 can be disassembled either as JZ or JE. First form is preferrable // after SUB or DEC; second form is more natural after CMP); // (9) if command has mnemonics RETN but alternative form RET is expected, // skip it - RET will follow. success=0; for (pchain=cmdchain+(code & CMDMASK); ; pchain=pchain->pnext) { if (pchain==NULL || pchain->pcmd==NULL) break; // End of chain, no match pcmd=pchain->pcmd; if (((code ^ pcmd->code) & pcmd->mask)!=0) continue; // (1) Different code bits cmdtype=pcmd->cmdtype; if ((damode & DA_TEXT)!=0) { if ((pcmd->exttype & DX_RETN)!=0 && config->useretform!=0) continue; // (9) RET form of near return expected if ((cmdtype & D_COND)!=0 && (pcmd->exttype & (DX_ZEROMASK|DX_CARRYMASK))!=0 ) { if ((damode & DA_JZ)!=0 && (pcmd->exttype & DX_ZEROMASK)==DX_JE) continue; // (8) Wait for DX_JZ if ((damode & DA_JC)!=0 && (pcmd->exttype & DX_CARRYMASK)==DX_JB) continue; // (8) Wait for DX_JC ; }; }; if ((pcmd->exttype & (DX_VEX|DX_LEADMASK))!=vexlead) continue; // (5) Unmatched VEX prefix if (pcmd->exttype & DX_VEX) { if (((pcmd->exttype & DX_VLMASK)==DX_LSHORT && (vex & 0x04)!=0) || ((pcmd->exttype & DX_VLMASK)==DX_LLONG && (vex & 0x04)==0)) continue; // (5) Unmatched VEX.L ; }; if ((cmdtype & (D_MEMORY|D_REGISTER|D_LONGFORM|D_SIZEMASK|D_MUSTMASK|D_POSTBYTE))==0 ) { success=1; break; }; // Optimization for most frequent case switch (cmdtype & D_MUSTMASK) { case D_MUST66: // (4) (SSE) Requires 66, no F2 or F3 if ((im.prefixlist & (PF_66|PF_F2|PF_F3))!=PF_66) continue; break; case D_MUSTF2: // (4) (SSE) Requires F2, no 66 or F3 if ((im.prefixlist & (PF_66|PF_F2|PF_F3))!=PF_F2) continue; break; case D_MUSTF3: // (4) (SSE) Requires F3, no 66 or F2 if ((im.prefixlist & (PF_66|PF_F2|PF_F3))!=PF_F3) continue; break; case D_MUSTNONE: // (4) (MMX,SSE) Requires no 66, F2, F3 if ((im.prefixlist & (PF_66|PF_F2|PF_F3))!=0) continue; break; case D_NEEDF2: // (4) (SSE) Requires F2, no F3 if ((im.prefixlist & (PF_F2|PF_F3))!=PF_F2) continue; break; case D_NEEDF3: // (4) (SSE) Requires F3, no F2 if ((im.prefixlist & (PF_F2|PF_F3))!=PF_F3) continue; break; case D_NOREP: // (4) Must not include F2 or F3 if ((im.prefixlist & (PF_REP|PF_REPNE))!=0) continue; break; case D_MUSTREP: // (4) Must include F3 (REP) case D_MUSTREPE: // (4) Must include F3 (REPE) if ((im.prefixlist & PF_REP)==0) continue; break; case D_MUSTREPNE: // (4) Must include F2 (REPNE) if ((im.prefixlist & PF_REPNE)==0) continue; break; default: break; }; if ((cmdtype & D_DATA16)!=0 && (im.prefixlist & PF_DSIZE)==0) continue; // (3) 16-bit data expected if ((cmdtype & D_DATA32)!=0 && (im.prefixlist & PF_DSIZE)!=0) continue; // (3) 32-bit data expected if ((cmdtype & D_ADDR16)!=0 && (im.prefixlist & PF_ASIZE)==0) continue; // (3) 16-bit address expected if ((cmdtype & D_ADDR32)!=0 && (im.prefixlist & PF_ASIZE)!=0) continue; // (3) 32-bit address expected if ((cmdtype & D_LONGFORM)!=0 && config->shortstringcmds!=0 && (im.prefixlist & (PF_ES|PF_CS|PF_SS|PF_FS|PF_GS|PF_ASIZE))==0) continue; // (6) Short form of string cmd expected if (cmdtype & D_MEMORY) { // (2) Command expects operand in memory (Mod in ModRegRM is not 11b). if (n+pcmd->length>=cmdsize) break; // Command longer than available code if ((cmd[n+pcmd->length] & 0xC0)==0xC0) continue; } else if (cmdtype & D_REGISTER) { // (2) Command expects operand in register (Mod in ModRegRM is 11b). if (n+pcmd->length>=cmdsize) break; // Command longer than available code if ((cmd[n+pcmd->length] & 0xC0)!=0xC0) continue; } if (cmdtype & D_POSTBYTE) { // Command expects postbyte after ModRegRM/SIB/offset as part of the // code. If command is longer than available code, immediately report // match - error will be reported elsewhere. m=n+pcmd->length; // Offset to ModRegRM byte if (m>=cmdsize) break; // Command longer than available code if (im.prefixlist & PF_ASIZE) m+=modrm16[cmd[m]].size; // 16-bit address else { pmrm=modrm32+cmd[m]; if (pmrm->psib==NULL) // 32-bit address without SIB m+=pmrm->size; else if (m+1>=cmdsize) // Command longer than available code break; else // 32-bit address with SIB m+=pmrm->psib[cmd[m+1]].size; ; }; if (m>=cmdsize) break; // Command longer than available code // Asterisk in SSE and AVX commands means comparison predicate. Check // for predefined range. if (cmd[m]==(uchar)pcmd->postbyte || ((cmdtype & D_WILDCARD)!=0 && cmd[m]<(pcmd->exttype & DX_VEX?32:8))) im.immsize1=1; // (7) Interprete postbyte as imm const else continue; // (7) ; }; success=1; break; // Perfect match, command found }; // If command is bad but preceded with prefixes, decode first prefix as // standalone. In this case, list of command's prefixes is empty. if (success==0) { pcmd=NULL; if (im.prefixlist!=0) { n=0; da->nprefix=0; da->prefixes=im.prefixlist=0; code=cmd[n] & CMDMASK; for (pchain=cmdchain+code; ; pchain=pchain->pnext) { if (pchain==NULL || pchain->pcmd==NULL) { pcmd=NULL; break; }; // End of chain, no match pcmd=pchain->pcmd; if ((pcmd->cmdtype & D_CMDTYPE)!=D_PREFIX) continue; if (((code ^ pcmd->code) & pcmd->mask)==0) { cmdtype=pcmd->cmdtype; da->errors|=DAE_BADCMD; break; }; }; }; // If matching command is still not found, report error and return one byte // as a command length. if (pcmd==NULL) { if (damode & DA_DUMP) Thexdump(da->dump,cmd,1,config->lowercase); if (damode & DA_TEXT) { if (config->disasmmode==DAMODE_HLA) j=Tcopycase(da->result,TEXTLEN,sizename[1],config->lowercase); else if (config->disasmmode==DAMODE_ATT) j=Tcopycase(da->result,TEXTLEN,sizeatt[1],config->lowercase); else j=Tcopycase(da->result,TEXTLEN,sizekey[1],config->lowercase); j+=Tstrcopy(da->result+j,TEXTLEN-j,T(" ")); Thexdump(da->result+j,cmd,1,config->lowercase); }; da->size=1; da->nprefix=0; da->prefixes=0; da->cmdtype=D_BAD; da->exttype=0; da->errors|=DAE_BADCMD; // Unrecognized command if (damode & DA_HILITE) { da->masksize=tstrlen(da->result); memset(da->mask,DRAW_SUSPECT,da->masksize); }; return 1; }; }; // Exclude prefixes that are integral part of the command from the list of // prefixes. First comparison optimizes for the most frequent case of no // obligatory prefixes. if (cmdtype & (D_SIZEMASK|D_MUSTMASK)) { switch (cmdtype & D_MUSTMASK) { case D_MUST66: // (SSE) Requires 66, no F2 or F3 case D_MUSTF2: // (SSE) Requires F2, no 66 or F3 case D_MUSTF3: // (SSE) Requires F3, no 66 or F2 im.prefixlist&=~(PF_66|PF_F2|PF_F3); break; case D_NEEDF2: // (SSE) Requires F2, no F3 case D_NEEDF3: // (SSE) Requires F3, no F2 im.prefixlist&=~(PF_F2|PF_F3); break; default: break; }; if (cmdtype & D_DATA16) // Must include data size prefix im.prefixlist&=~PF_DSIZE; if (cmdtype & D_ADDR16) // Must include address size prefix im.prefixlist&=~PF_ASIZE; ; }; // Prepare for disassembling. im.modsize=0; // Size of ModRegRM/SIB bytes im.dispsize=0; // Size of address offset im.usesdatasize=0; im.usesaddrsize=0; im.usessegment=0; da->cmdtype=cmdtype; da->exttype=pcmd->exttype; n+=pcmd->length; // Offset of ModRegRM or imm constant if (n>cmdsize) { da->errors|=DAE_CROSS; // Command crosses end of memory block goto error; }; im.mainsize=n; // Size of command with prefixes // Set default data size (note that many commands and operands override it). if ((cmdtype & D_SIZE01)!=0 && (cmd[n-1] & 0x01)==0) { if (im.prefixlist & PF_DSIZE) da->warnings|=DAW_DATASIZE; // Superfluous data size prefix datasize=1; } else if (im.prefixlist & PF_DSIZE) datasize=2; else datasize=4; // Process operands. noperand=0; for (i=0; iarg[i]; if ((arg & B_ARGMASK)==B_NONE) break; // Optimization for most frequent case // If pseudooperands to be skipped, I process them nevertheless. Such // operands may contain important information. if ((arg & B_PSEUDO)!=0 && (damode & DA_PSEUDO)==0) op=&pseudoop; // Request to skip pseudooperands else op=da->op+noperand++; op->arg=arg; switch (arg & B_ARGMASK) { case B_AL: // Register AL Operandintreg(&im,1,REG_AL,op); break; case B_AH: // Register AH Operandintreg(&im,1,REG_AH,op); break; case B_AX: // Register AX Operandintreg(&im,2,REG_EAX,op); break; case B_CL: // Register CL Operandintreg(&im,1,REG_CL,op); break; case B_CX: // Register CX Operandintreg(&im,2,REG_ECX,op); break; case B_DX: // Register DX Operandintreg(&im,2,REG_EDX,op); break; case B_DXPORT: // Register DX as I/O port address Operandintreg(&im,2,REG_EDX,op); op->features|=OP_PORT; break; case B_EAX: // Register EAX Operandintreg(&im,4,REG_EAX,op); break; case B_EBX: // Register EBX Operandintreg(&im,4,REG_EBX,op); break; case B_ECX: // Register ECX Operandintreg(&im,4,REG_ECX,op); break; case B_EDX: // Register EDX Operandintreg(&im,4,REG_EDX,op); break; case B_ACC: // Accumulator (AL/AX/EAX) Operandintreg(&im,datasize,REG_EAX,op); im.usesdatasize=1; break; case B_STRCNT: // Register CX or ECX as REPxx counter Operandintreg(&im,(im.prefixlist & PF_ASIZE?2:4),REG_ECX,op); im.usesaddrsize=1; break; case B_DXEDX: // Register DX or EDX in DIV/MUL Operandintreg(&im,datasize,REG_EDX,op); im.usesdatasize=1; break; case B_BPEBP: // Register BP or EBP in ENTER/LEAVE Operandintreg(&im,datasize,REG_EBP,op); im.usesdatasize=1; break; case B_REG: // 8/16/32-bit register in Reg // Note that all commands that use B_REG have also another operand // that requires ModRM, so we don't need to set modsize here. if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else { Operandintreg(&im,datasize,(cmd[n]>>3) & 0x07,op); im.usesdatasize=1; }; break; case B_REG16: // 16-bit register in Reg if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else Operandintreg(&im,2,(cmd[n]>>3) & 0x07,op); break; case B_REG32: // 32-bit register in Reg if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else Operandintreg(&im,4,(cmd[n]>>3) & 0x07,op); break; case B_REGCMD: // 16/32-bit register in last cmd byte Operandintreg(&im,datasize,cmd[n-1] & 0x07,op); im.usesdatasize=1; break; case B_REGCMD8: // 8-bit register in last cmd byte Operandintreg(&im,1,cmd[n-1] & 0x07,op); break; case B_ANYREG: // Reg field is unused, any allowed break; case B_INT: // 8/16/32-bit register/memory in ModRM case B_INT1632: // 16/32-bit register/memory in ModRM k=Operandmodrm(&im,datasize,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandintreg(&im,datasize,cmd[n] & 0x07,op); im.usesdatasize=1; break; case B_INT8: // 8-bit register/memory in ModRM k=Operandmodrm(&im,1,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandintreg(&im,1,cmd[n] & 0x07,op); break; case B_INT16: // 16-bit register/memory in ModRM k=Operandmodrm(&im,2,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandintreg(&im,2,cmd[n] & 0x07,op); break; case B_INT32: // 32-bit register/memory in ModRM k=Operandmodrm(&im,4,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandintreg(&im,4,cmd[n] & 0x07,op); break; case B_INT64: // 64-bit integer in ModRM, memory only k=Operandmodrm(&im,8,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as 32-bit register and set error. Operandintreg(&im,4,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; break; }; break; case B_INT128: // 128-bit integer in ModRM, memory only k=Operandmodrm(&im,16,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as 32-bit register and set error. Operandintreg(&im,4,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; break; }; break; case B_IMMINT: // 8/16/32-bit int at immediate addr Operandimmaddr(&im,datasize,cmd+n,cmdsize-n,op); im.usesdatasize=1; break; case B_INTPAIR: // Two signed 16/32 in ModRM, memory only k=Operandmodrm(&im,2*datasize,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address op->granularity=datasize; if (k==0) { // Register is not allowed, decode as register and set error. Operandintreg(&im,datasize,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; break; }; im.usesdatasize=1; break; case B_SEGOFFS: // 16:16/16:32 absolute address in memory k=Operandmodrm(&im,datasize+2,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode and set error. Operandintreg(&im,datasize,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; break; }; im.usesdatasize=1; break; case B_STRDEST: // 8/16/32-bit string dest, [ES:(E)DI] Operandindirect(&im,REG_EDI,1,SEG_ES,0,datasize,op); im.usesdatasize=1; break; case B_STRDEST8: // 8-bit string destination, [ES:(E)DI] Operandindirect(&im,REG_EDI,1,SEG_ES,0,1,op); break; case B_STRSRC: // 8/16/32-bit string source, [(E)SI] Operandindirect(&im,REG_ESI,1,SEG_UNDEF,0,datasize,op); im.usesdatasize=1; break; case B_STRSRC8: // 8-bit string source, [(E)SI] Operandindirect(&im,REG_ESI,1,SEG_UNDEF,0,1,op); break; case B_XLATMEM: // 8-bit memory in XLAT, [(E)BX+AL] Operandxlat(&im,op); break; case B_EAXMEM: // Reference to memory addressed by [EAX] Operandindirect(&im,REG_EAX,0,SEG_UNDEF,4,1,op); break; case B_LONGDATA: // Long data in ModRM, mem only k=Operandmodrm(&im,256,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address op->granularity=1; // Just a trick if (k==0) { // Register is not allowed, decode and set error. Operandintreg(&im,4,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; break; }; im.usesdatasize=1; // Caveat user break; case B_ANYMEM: // Reference to memory, data unimportant k=Operandmodrm(&im,1,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode and set error. Operandintreg(&im,4,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; }; break; case B_STKTOP: // 16/32-bit int top of stack Operandindirect(&im,REG_ESP,1,SEG_SS,0,datasize,op); im.usesdatasize=1; break; case B_STKTOPFAR: // Top of stack (16:16/16:32 far addr) Operandindirect(&im,REG_ESP,1,SEG_SS,0,datasize*2,op); op->granularity=datasize; im.usesdatasize=1; break; case B_STKTOPEFL: // 16/32-bit flags on top of stack Operandindirect(&im,REG_ESP,1,SEG_SS,0,datasize,op); im.usesdatasize=1; break; case B_STKTOPA: // 16/32-bit top of stack all registers Operandindirect(&im,REG_ESP,1,SEG_SS,0,datasize*8,op); op->granularity=datasize; op->modifies=da->modifies=0xFF; im.usesdatasize=1; break; case B_PUSH: // 16/32-bit int push to stack case B_PUSHRET: // 16/32-bit push of return address case B_PUSHRETF: // 16:16/16:32-bit push of far retaddr case B_PUSHA: // 16/32-bit push all registers Operandpush(&im,datasize,op); im.usesdatasize=1; break; case B_EBPMEM: // 16/32-bit int at [EBP] Operandindirect(&im,REG_EBP,1,SEG_SS,0,datasize,op); im.usesdatasize=1; break; case B_SEG: // Segment register in Reg if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else Operandsegreg(&im,(cmd[n]>>3) & 0x07,op); break; case B_SEGNOCS: // Segment register in Reg, but not CS if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else { k=(cmd[n]>>3) & 0x07; Operandsegreg(&im,k,op); if (k==SEG_SS) da->exttype|=DX_WONKYTRAP; if (k==SEG_CS) { op->features|=OP_INVALID; da->errors|=DAE_BADSEG; }; }; break; case B_SEGCS: // Segment register CS Operandsegreg(&im,SEG_CS,op); break; case B_SEGDS: // Segment register DS Operandsegreg(&im,SEG_DS,op); break; case B_SEGES: // Segment register ES Operandsegreg(&im,SEG_ES,op); break; case B_SEGFS: // Segment register FS Operandsegreg(&im,SEG_FS,op); break; case B_SEGGS: // Segment register GS Operandsegreg(&im,SEG_GS,op); break; case B_SEGSS: // Segment register SS Operandsegreg(&im,SEG_SS,op); break; case B_ST: // 80-bit FPU register in last cmd byte Operandfpureg(&im,cmd[n-1] & 0x07,op); break; case B_ST0: // 80-bit FPU register ST0 Operandfpureg(&im,0,op); break; case B_ST1: // 80-bit FPU register ST1 Operandfpureg(&im,1,op); break; case B_FLOAT32: // 32-bit float in ModRM, memory only k=Operandmodrm(&im,4,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as FPU register and set error. Operandfpureg(&im,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; }; break; case B_FLOAT64: // 64-bit float in ModRM, memory only k=Operandmodrm(&im,8,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as FPU register and set error. Operandfpureg(&im,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; }; break; case B_FLOAT80: // 80-bit float in ModRM, memory only k=Operandmodrm(&im,10,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as FPU register and set error. Operandfpureg(&im,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; }; break; case B_BCD: // 80-bit BCD in ModRM, memory only k=Operandmodrm(&im,10,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as FPU register and set error. Operandfpureg(&im,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; }; break; case B_MREG8x8: // MMX register as 8 8-bit integers case B_MREG16x4: // MMX register as 4 16-bit integers case B_MREG32x2: // MMX register as 2 32-bit integers case B_MREG64: // MMX register as 1 64-bit integer if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else { Operandmmxreg(&im,(cmd[n]>>3) & 0x07,op); op->granularity=Getgranularity(arg); }; break; case B_MMX8x8: // MMX reg/memory as 8 8-bit integers case B_MMX16x4: // MMX reg/memory as 4 16-bit integers case B_MMX32x2: // MMX reg/memory as 2 32-bit integers case B_MMX64: // MMX reg/memory as 1 64-bit integer k=Operandmodrm(&im,8,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandmmxreg(&im,cmd[n] & 0x07,op); op->granularity=Getgranularity(arg); break; case B_MMX8x8DI: // MMX 8 8-bit integers at [DS:(E)DI] Operandindirect(&im,REG_EDI,0,SEG_UNDEF,0,8,op); op->granularity=Getgranularity(arg); break; case B_3DREG: // 3DNow! register as 2 32-bit floats if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else { Operandnowreg(&im,(cmd[n]>>3) & 0x07,op); op->granularity=4; }; break; case B_3DNOW: // 3DNow! reg/memory as 2 32-bit floats k=Operandmodrm(&im,8,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandnowreg(&im,cmd[n] & 0x07,op); op->granularity=4; break; case B_SREGF32x4: // SSE register as 4 32-bit floats case B_SREGF32L: // Low 32-bit float in SSE register case B_SREGF32x2L: // Low 2 32-bit floats in SSE register case B_SREGF64x2: // SSE register as 2 64-bit floats case B_SREGF64L: // Low 64-bit float in SSE register if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else { Operandssereg(&im,(cmd[n]>>3) & 0x07,op); op->granularity=Getgranularity(arg); }; break; case B_SVEXF32x4: // SSE reg in VEX as 4 32-bit floats case B_SVEXF32L: // Low 32-bit float in SSE in VEX case B_SVEXF64x2: // SSE reg in VEX as 2 64-bit floats case B_SVEXF64L: // Low 64-bit float in SSE in VEX Operandssereg(&im,vexreg,op); op->granularity=Getgranularity(arg); break; case B_SSEF32x4: // SSE reg/memory as 4 32-bit floats case B_SSEF64x2: // SSE reg/memory as 2 64-bit floats k=Operandmodrm(&im, (arg & B_NOVEXSIZE?16:im.ssesize),cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=Getgranularity(arg); break; case B_SSEF32L: // Low 32-bit float in SSE reg/memory k=Operandmodrm(&im,4,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) // Operand in SSE register Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=4; break; case B_SSEF32x2L: // Low 2 32-bit floats in SSE reg/memory k=Operandmodrm(&im, (arg & B_NOVEXSIZE?16:im.ssesize)/2,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) // Operand in SSE register Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=4; break; case B_SSEF64L: // Low 64-bit float in SSE reg/memory k=Operandmodrm(&im,8,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) // Operand in SSE register Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=8; break; case B_XMM0I32x4: // XMM0 as 4 32-bit integers case B_XMM0I64x2: // XMM0 as 2 64-bit integers case B_XMM0I8x16: // XMM0 as 16 8-bit integers Operandssereg(&im,0,op); op->granularity=Getgranularity(arg); break; case B_SREGI8x16: // SSE register as 16 8-bit sigints case B_SREGI16x8: // SSE register as 8 16-bit sigints case B_SREGI32x4: // SSE register as 4 32-bit sigints case B_SREGI64x2: // SSE register as 2 64-bit sigints case B_SREGI32L: // Low 32-bit sigint in SSE register case B_SREGI32x2L: // Low 2 32-bit sigints in SSE register case B_SREGI64L: // Low 64-bit sigint in SSE register if (n>=cmdsize) da->errors|=DAE_CROSS; // Command crosses end of memory block else { Operandssereg(&im,(cmd[n]>>3) & 0x07,op); op->granularity=Getgranularity(arg); }; break; case B_SVEXI8x16: // SSE reg in VEX as 16 8-bit sigints case B_SVEXI16x8: // SSE reg in VEX as 8 16-bit sigints case B_SVEXI32x4: // SSE reg in VEX as 4 32-bit sigints case B_SVEXI64x2: // SSE reg in VEX as 2 64-bit sigints Operandssereg(&im,vexreg,op); op->granularity=Getgranularity(arg); break; case B_SSEI8x16: // SSE reg/memory as 16 8-bit sigints case B_SSEI16x8: // SSE reg/memory as 8 16-bit sigints case B_SSEI32x4: // SSE reg/memory as 4 32-bit sigints case B_SSEI64x2: // SSE reg/memory as 2 64-bit sigints k=Operandmodrm(&im, (arg & B_NOVEXSIZE?16:im.ssesize),cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=Getgranularity(arg); break; case B_SSEI8x8L: // Low 8 8-bit ints in SSE reg/memory case B_SSEI16x4L: // Low 4 16-bit ints in SSE reg/memory case B_SSEI32x2L: // Low 2 32-bit sigints in SSE reg/memory k=Operandmodrm(&im, (arg & B_NOVEXSIZE?16:im.ssesize)/2,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=Getgranularity(arg); break; case B_SSEI8x4L: // Low 4 8-bit ints in SSE reg/memory case B_SSEI16x2L: // Low 2 16-bit ints in SSE reg/memory k=Operandmodrm(&im,4,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=Getgranularity(arg); break; case B_SSEI8x2L: // Low 2 8-bit ints in SSE reg/memory k=Operandmodrm(&im,2,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) Operandssereg(&im,cmd[n] & 0x07,op); op->granularity=Getgranularity(arg); break; case B_SSEI8x16DI: // SSE 16 8-bit sigints at [DS:(E)DI] Operandindirect(&im,REG_EDI,0,SEG_UNDEF,0, (arg & B_NOVEXSIZE?16:im.ssesize),op); op->granularity=1; break; case B_EFL: // Flags register EFL Operandefl(&im,4,op); break; case B_FLAGS8: // Flags (low byte) Operandefl(&im,1,op); break; case B_OFFSET: // 16/32 const offset from next command Operandoffset(&im,datasize,datasize,cmd+n,cmdsize-n,da->ip+n,op); break; case B_BYTEOFFS: // 8-bit sxt const offset from next cmd Operandoffset(&im,1,datasize,cmd+n,cmdsize-n,da->ip+n,op); break; case B_FARCONST: // 16:16/16:32 absolute address constant Operandimmfaraddr(&im,datasize,cmd+n,cmdsize-n,op); break; case B_DESCR: // 16:32 descriptor in ModRM k=Operandmodrm(&im,6,cmd+n,cmdsize-n,op); if (k<0) break; // Error in address if (k==0) { // Register is not allowed, decode as 32-bit register and set error. Operandintreg(&im,4,cmd[n] & 0x07,op); op->features|=OP_INVALID; da->errors|=DAE_MEMORY; }; break; case B_1: // Immediate constant 1 Operandone(&im,op); break; case B_CONST8: // Immediate 8-bit constant Operandimmconst(&im,1,1,datasize,cmd+n,cmdsize-n,0,op); if (arg & B_PORT) op->features|=OP_PORT; break; case B_SIMMI8x16: // SSE reg in immediate 8-bit constant if (cmdsize-nerrors|=DAE_CROSS; // Command crosses end of memory block break; }; im.immsize1=1; Operandssereg(&im,(cmd[n+im.modsize+im.dispsize]>>4) & 0x07,op); op->granularity=Getgranularity(arg); break; case B_CONST8_2: // Immediate 8-bit const, second in cmd Operandimmconst(&im,1,1,datasize,cmd+n,cmdsize-n,1,op); break; case B_CONST16: // Immediate 16-bit constant Operandimmconst(&im,2,2,datasize,cmd+n,cmdsize-n,0,op); break; case B_CONST: // Immediate 8/16/32-bit constant case B_CONSTL: // Immediate 16/32-bit constant Operandimmconst(&im,datasize,datasize,datasize,cmd+n,cmdsize-n,0,op); im.usesdatasize=1; break; case B_SXTCONST: // Immediate 8-bit sign-extended to size Operandimmconst(&im,1,datasize,datasize,cmd+n,cmdsize-n,0,op); im.usesdatasize=1; break; case B_CR: // Control register in Reg Operandcreg(&im,(cmd[n]>>3) & 0x07,op); break; case B_CR0: // Control register CR0 Operandcreg(&im,0,op); break; case B_DR: // Debug register in Reg Operanddreg(&im,(cmd[n]>>3) & 0x07,op); break; case B_FST: // FPU status register Operandfst(&im,op); break; case B_FCW: // FPU control register Operandfcw(&im,op); break; case B_MXCSR: // SSE media control and status register Operandmxcsr(&im,op); break; default: // Internal error da->errors|=DAE_INTERN; break; }; if ((arg & B_32BITONLY)!=0 && op->opsize!=4) da->warnings|=DAW_NONCLASS; if ((arg & B_MODMASK)==B_JMPCALLFAR) da->warnings|=DAW_FARADDR; if (arg & B_PSEUDO) op->features|=OP_PSEUDO; if (arg & (B_CHG|B_UPD)) op->features|=OP_MOD; }; if (im.prefixlist!=0) { // Optimization for most frequent case // If LOCK prefix is present, report error if prefix is not allowed by // command and warning otherwise. Application code usually doesn't need // atomic bus access. if ((im.prefixlist & PF_LOCK)!=0) { if ((cmdtype & D_LOCKABLE)==0) da->errors|=DAE_LOCK; else da->warnings|=DAW_LOCK; }; // Warn if data size prefix is present but not used by command. if ((im.prefixlist & PF_DSIZE)!=0 && im.usesdatasize==0 && (pcmd->exttype & DX_TYPEMASK)!=DX_NOP) da->warnings|=DAW_DATASIZE; // Warn if address size prefix is present but not used by command. if ((im.prefixlist & PF_ASIZE)!=0 && im.usesaddrsize==0) da->warnings|=DAW_ADDRSIZE; // Warn if segment override prefix is present but command doesn't access // memory. Prefixes CS: and DS: are also used as branch hints in // conditional branches. if ((im.prefixlist & PF_SEGMASK)!=0 && im.usessegment==0) { if ((cmdtype & D_BHINT)==0 || (im.prefixlist & PF_HINT)==0) da->warnings|=DAW_SEGPREFIX; }; // Warn if REPxx prefix is present but not used by command. Attention, // Intel frequently uses these prefixes for different means! if (im.prefixlist & PF_REPMASK) { if (((im.prefixlist & PF_REP)!=0 && (cmdtype & D_MUSTMASK)!=D_MUSTREP && (cmdtype & D_MUSTMASK)!=D_MUSTREPE) || ((im.prefixlist & PF_REPNE)!=0 && (cmdtype & D_MUSTMASK)!=D_MUSTREPNE)) da->warnings|=DAW_REPPREFIX; }; }; // Warn on unaligned stack, I/O and privileged commands. switch (cmdtype & D_CMDTYPE) { case D_PUSH: if (datasize==2) da->warnings|=DAW_STACK; break; case D_INT: da->warnings|=DAW_INTERRUPT; break; case D_IO: da->warnings|=DAW_IO; break; case D_PRIVILEGED: da->warnings|=DAW_PRIV; break; }; // Warn on system, privileged and undocumented commands. if ((cmdtype & D_USEMASK)!=0) { if ((cmdtype & D_USEMASK)==D_RARE || (cmdtype & D_USEMASK)==D_SUSPICIOUS) da->warnings|=DAW_RARE; if ((cmdtype & D_USEMASK)==D_UNDOC) da->warnings|=DAW_NONCLASS; }; // If command implicitly changes ESP, it uses and modifies this register. if (cmdtype & D_CHGESP) { da->uses|=(1<modifies|=(1<errors & DAE_CROSS) // Incomplete command Thexdump(da->dump,cmd,cmdsize,config->lowercase); else { j=0; // Dump prefixes. REPxx is treated as prefix and separated from command // with semicolon; prefixes 66, F2 and F3 that are part of SSE command // are glued with command's body - well, at least if there are no other // prefixes inbetween. for (u=0; unprefix; u++) { j+=Thexdump(da->dump+j,cmd+u,1,config->lowercase); if (cmd[u]==0x66 && (cmdtype & D_MUSTMASK)==D_MUST66) continue; if (cmd[u]==0xF2 && ((cmdtype & D_MUSTMASK)==D_MUSTF2 || (cmdtype & D_MUSTMASK)==D_NEEDF2)) continue; if (cmd[u]==0xF3 && ((cmdtype & D_MUSTMASK)==D_MUSTF3 || (cmdtype & D_MUSTMASK)==D_NEEDF3)) continue; if ((im.prefixlist & (PF_VEX2|PF_VEX3))!=0 && u==da->nprefix-2) continue; if ((im.prefixlist & PF_VEX3)!=0 && u==da->nprefix-3) continue; da->dump[j++]=T(':'); }; // Dump body of the command, including ModRegRM and SIB bytes. j+=Thexdump(da->dump+j,cmd+u,im.mainsize+im.modsize-u, config->lowercase); // Dump displacement, if any, separated with space from command's body. if (im.dispsize>0) { da->dump[j++]=T(' '); j+=Thexdump(da->dump+j,cmd+im.mainsize+im.modsize,im.dispsize, config->lowercase); ; }; // Dump immediate constants, if any. if (im.immsize1>0) { da->dump[j++]=T(' '); j+=Thexdump(da->dump+j,cmd+im.mainsize+im.modsize+im.dispsize, im.immsize1,config->lowercase) ; }; if (im.immsize2>0) { da->dump[j++]=T(' '); Thexdump(da->dump+j,cmd+im.mainsize+im.modsize+im.dispsize+im.immsize1, im.immsize2,config->lowercase); ; }; }; }; // Prepare disassembled command. There are many options that control look // and feel of disassembly, so the procedure is a bit, errr, boring. if (damode & DA_TEXT) { if (da->errors & DAE_CROSS) { // Incomplete command q=Tstrcopy(da->result,TEXTLEN,T("???")); if (damode & DA_HILITE) { memset(da->mask,DRAW_SUSPECT,q); da->masksize=q; }; } else { j=0; // If LOCK and/or REPxx prefix is present, prepend it to the command. // Such cases are rare, first comparison makes small optimization. if (im.prefixlist & (PF_LOCK|PF_REPMASK)) { if (im.prefixlist & PF_LOCK) j=Tcopycase(da->result,TEXTLEN,T("LOCK "),config->lowercase); if (im.prefixlist & PF_REPNE) j+=Tcopycase(da->result+j,TEXTLEN-j,T("REPNE "),config->lowercase); else if (im.prefixlist & PF_REP) { if ((cmdtype & D_MUSTMASK)==D_MUSTREPE) j+=Tcopycase(da->result+j,TEXTLEN-j,T("REPE "),config->lowercase); else j+=Tcopycase(da->result+j,TEXTLEN-j,T("REP "),config->lowercase); ; }; }; // If there is a branch hint, prefix jump mnemonics with '+' (taken) or // '-' (not taken), or use pseudoprefixes BHT/BHNT. I don't know how MASM // indicates hints. if (cmdtype & D_BHINT) { if (config->jumphintmode==0) { if (im.prefixlist & PF_TAKEN) da->result[j++]=T('+'); else if (im.prefixlist & PF_NOTTAKEN) da->result[j++]=T('-'); ; } else { if (im.prefixlist & PF_TAKEN) j+=Tcopycase(da->result+j,TEXTLEN-j,T("BHT "),config->lowercase); else if (im.prefixlist & PF_NOTTAKEN) j+=Tcopycase(da->result+j,TEXTLEN-j,T("BHNT "),config->lowercase); ; }; }; // Get command mnemonics. If mnemonics contains asterisk, it must be // replaced by W, D or none according to sizesens. Asterisk in SSE and // AVX commands means comparison predicate. if (cmdtype & D_WILDCARD) { for (i=0; ; i++) { if (pcmd->name[i]==T('\0')) break; else if (pcmd->name[i]!=T('*')) da->result[j++]=pcmd->name[i]; else if (cmdtype & D_POSTBYTE) j+=Tstrcopy(da->result+j,TEXTLEN-j, ssepredicate[cmd[im.mainsize+im.modsize+im.dispsize]]); else if (datasize==4 && (config->sizesens==0 || config->sizesens==1)) da->result[j++]=T('D'); else if (datasize==2 && (config->sizesens==1 || config->sizesens==2)) da->result[j++]=T('W'); ; }; da->result[j]=T('\0'); if (config->lowercase) tstrlwr(da->result); } else { j+=Tcopycase(da->result+j,TEXTLEN-j,pcmd->name,config->lowercase); if (config->disasmmode==DAMODE_ATT && im.usesdatasize!=0) { // AT&T mnemonics are suffixed with the operand's size. if ((cmdtype & D_CMDTYPE)!=D_CMD && (cmdtype & D_CMDTYPE)!=D_MOV && (cmdtype & D_CMDTYPE)!=D_MOVC && (cmdtype & D_CMDTYPE)!=D_TEST && (cmdtype & D_CMDTYPE)!=D_STRING && (cmdtype & D_CMDTYPE)!=D_PUSH && (cmdtype & D_CMDTYPE)!=D_POP) ; else if (datasize==1) j+=Tcopycase(da->result+j,TEXTLEN-j, T("B"),config->lowercase); else if (datasize==2) j+=Tcopycase(da->result+j,TEXTLEN-j, T("W"),config->lowercase); else if (datasize==4) j+=Tcopycase(da->result+j,TEXTLEN-j, T("L"),config->lowercase); else if (datasize==8) j+=Tcopycase(da->result+j,TEXTLEN-j, T("Q"),config->lowercase); ; }; }; if (damode & DA_HILITE) { type=cmdtype & D_CMDTYPE; if (da->errors!=0) cfill=DRAW_SUSPECT; else switch (cmdtype & D_CMDTYPE) { case D_JMP: // Unconditional near jump case D_JMPFAR: // Unconditional far jump cfill=DRAW_JUMP; break; case D_JMC: // Conditional jump on flags case D_JMCX: // Conditional jump on (E)CX (and flags) cfill=DRAW_CJMP; break; case D_PUSH: // PUSH exactly 1 (d)word of data case D_POP: // POP exactly 1 (d)word of data cfill=DRAW_PUSHPOP; break; case D_CALL: // Plain near call case D_CALLFAR: // Far call case D_INT: // Interrupt cfill=DRAW_CALL; break; case D_RET: // Plain near return from call case D_RETFAR: // Far return or IRET cfill=DRAW_RET; break; case D_FPU: // FPU command case D_MMX: // MMX instruction, incl. SSE extensions case D_3DNOW: // 3DNow! instruction case D_SSE: // SSE instruction case D_AVX: // AVX instruction cfill=DRAW_FPU; break; case D_IO: // Accesses I/O ports case D_SYS: // Legal but useful in system code only case D_PRIVILEGED: // Privileged (non-Ring3) command cfill=DRAW_SUSPECT; break; default: cfill=DRAW_PLAIN; break; }; memset(da->mask,cfill,j); da->masksize=j; }; // Add decoded operands. In HLA mode, order of operands is inverted // except for comparison commands (marked with bit D_HLADIR) and // arguments are enclosed in parenthesis (except for immediate jumps). // In AT&T mode, order of operands is always inverted. Operands of type // B_PSEUDO are implicit and don't appear in text. if (config->disasmmode==DAMODE_HLA && (pcmd->arg[0] & B_ARGMASK)!=B_OFFSET && (pcmd->arg[0] & B_ARGMASK)!=B_BYTEOFFS && (pcmd->arg[0] & B_ARGMASK)!=B_FARCONST) enclose=1; // Enclose operand list in parenthesis else enclose=0; if ((damode & DA_HILITE)!=0 && config->hiliteoperands!=0) cfill=DRAW_PLAIN; nout=0; for (i=0; idisasmmode==DAMODE_HLA && (cmdtype & D_HLADIR)==0) || config->disasmmode==DAMODE_ATT) k=noperand-1-i; // Inverted (HLA/AT&T) order of operands else k=i; // Direct (Intel's) order of operands arg=da->op[k].arg; if ((arg & B_ARGMASK)==B_NONE || (arg & B_PSEUDO)!=0) continue; // Empty or implicit operand q=j; if (nout==0) { // Spaces between mnemonic and first operand. da->result[j++]=T(' '); if (config->tabarguments) { for ( ; j<8; j++) da->result[j]=T(' '); }; if (enclose) { da->result[j++]=T('('); if (config->extraspace) da->result[j++]=(' '); }; } else { // Comma and optional space between operands. da->result[j++]=T(','); if (config->extraspace) da->result[j++]=T(' '); }; if (damode & DA_HILITE) { memset(da->mask+q,cfill,j-q); da->masksize=j; }; // Operand itself. q=j; op=da->op+k; j+=Tstrcopy(da->result+j,TEXTLEN-j-10,op->text); if (damode & DA_HILITE) { if (config->hiliteoperands==0) ofill=cfill; else if (op->features & OP_REGISTER) ofill=DRAW_IREG; else if (op->features & (OP_FPUREG|OP_MMXREG|OP_3DNOWREG|OP_SSEREG)) ofill=DRAW_FREG; else if (op->features & (OP_SEGREG|OP_CREG|OP_DREG)) ofill=DRAW_SYSREG; else if (op->features & OP_MEMORY) { if (op->scale[REG_ESP]!=0 || op->scale[REG_EBP]!=0) ofill=DRAW_STKMEM; else ofill=DRAW_MEM; ; } else if (op->features & OP_CONST) ofill=DRAW_CONST; else ofill=cfill; memset(da->mask+q,ofill,j-q); da->masksize=j; }; nout++; }; // All arguments added, close list. if (enclose && nout!=0) { q=j; if (config->extraspace) da->result[j++]=T(' '); da->result[j++]=T(')'); if (damode & DA_HILITE) { memset(da->mask+q,cfill,j-q); da->masksize=j; }; }; da->result[j]=T('\0'); }; }; // Calculate total size of command. if (da->errors & DAE_CROSS) // Incomplete command n=cmdsize; else n+=im.modsize+im.dispsize+im.immsize1+im.immsize2; da->size=n; return n; }; // Given error and warning lists, returns pointer to the string describing // relatively most severe error or warning, or NULL if there are no errors or // warnings. tchar *Geterrwarnmessage(ulong errors,ulong warnings) { tchar *ps; if (errors==0 && warnings==0) ps=NULL; else if (errors & DAE_BADCMD) ps=T("Unknown command"); else if (errors & DAE_CROSS) ps=T("Command crosses end of memory block"); else if (errors & DAE_MEMORY) ps=T("Illegal use of register"); else if (errors & DAE_REGISTER) ps=T("Memory address is not allowed"); else if (errors & DAE_LOCK) ps=T("LOCK prefix is not allowed"); else if (errors & DAE_BADSEG) ps=T("Invalid segment register"); else if (errors & DAE_SAMEPREF) ps=T("Two prefixes from the same group"); else if (errors & DAE_MANYPREF) ps=T("More than 4 prefixes"); else if (errors & DAE_BADCR) ps=T("Invalid CR register"); else if (errors & DAE_INTERN) ps=T("Internal OllyDbg error"); else if (warnings & DAW_DATASIZE) ps=T("Superfluous operand size prefix"); else if (warnings & DAW_ADDRSIZE) ps=T("Superfluous address size prefix"); else if (warnings & DAW_SEGPREFIX) ps=T("Superfluous segment override prefix"); else if (warnings & DAW_REPPREFIX) ps=T("Superfluous REPxx prefix"); else if (warnings & DAW_DEFSEG) ps=T("Explicit default segment register"); else if (warnings & DAW_JMP16) ps=T("16-bit jump, call or return"); else if (warnings & DAW_FARADDR) ps=T("Far jump or call"); else if (warnings & DAW_SEGMOD) ps=T("Modification of segment register"); else if (warnings & DAW_PRIV) ps=T("Privileged instruction"); else if (warnings & DAW_IO) ps=T("I/O command"); else if (warnings & DAW_SHIFT) ps=T("Shift out of range"); else if (warnings & DAW_LOCK) ps=T("Command uses (valid) LOCK prefix"); else if (warnings & DAW_STACK) ps=T("Unaligned stack operation"); else if (warnings & DAW_NOESP) ps=T("Suspicious use of stack pointer"); else if (warnings & DAW_NONCLASS) ps=T("Undocumented instruction or encoding"); else ps=NULL; return ps; };