www.pudn.com > PEMonitor_0.10_src.zip > disasm.c
// Free Disassembler and Assembler -- Disassembler // // Copyright (C) 2001 Oleh Yuschuk // // This program 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 2 of the License, or // (at your option) any later version. // // This program 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, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define STRICT #include#include #include #include #include #include #include #pragma hdrstop #include "disasm.h" static int ideal; // Force IDEAL decoding mode static int lowercase; // Force lowercase display static int tabarguments; // Tab between mnemonic and arguments static int extraspace; // Extra space between arguments static int putdefseg; // Display default segments in listing static int showmemsize; // Always show memory size static int shownear; // Show NEAR modifiers static int shortstringcmds; // Use short form of string commands static int sizesens; // How to decode size-sensitive mnemonics static int symbolic; // Show symbolic addresses in disasm static int farcalls; // Accept far calls, returns & addresses static int decodevxd; // Decode VxD calls (Win95/98) static int privileged; // Accept privileged commands static int iocommand; // Accept I/O commands static int badshift; // Accept shift out of range 1..31 static int extraprefix; // Accept superfluous prefixes static int lockedbus; // Accept LOCK prefixes static int stackalign; // Accept unaligned stack operations static int iswindowsnt; // When checking for dangers, assume NT //////////////////////////////////////////////////////////////////////////////// //////////////////////////// DISASSEMBLER FUNCTIONS //////////////////////////// // Work variables of disassembler static ulong datasize; // Size of data (1,2,4 bytes) static ulong addrsize; // Size of address (2 or 4 bytes) static int segprefix; // Segment override prefix or SEG_UNDEF static int hasrm; // Command has ModR/M byte static int hassib; // Command has SIB byte static int dispsize; // Size of displacement (if any) static int immsize; // Size of immediate data (if any) static int softerror; // Noncritical disassembler error static int ndump; // Current length of command dump static int nresult; // Current length of disassembly static int addcomment; // Comment value of operand // Copy of input parameters of function Disasm() static char *cmd; // Pointer to binary data static char *pfixup; // Pointer to possible fixups or NULL static ulong size; // Remaining size of the command buffer static t_disasm *da; // Pointer to disassembly results static int mode; // Disassembly mode (DISASM_xxx) // Disassemble name of 1, 2 or 4-byte general-purpose integer register and, if // requested and available, dump its contents. Parameter type changes decoding // of contents for some operand types. static void DecodeRG(int index,int datasize,int type) { int sizeindex; char name[9]; if (mode error=DAE_INTERN; return; }; if (mode>=DISASM_FILE) { strcpy(name,regname[sizeindex][index]); if (lowercase) strlwr(name); if (type result+nresult,"%s",name); ; }; }; // Disassemble name of 80-bit floating-point register and, if available, dump // its contents. static void DecodeST(int index,int pseudoop) { int i; char s[32]; if (mode result+nresult,s); nresult+=i; }; }; // Disassemble name of 64-bit MMX register. static void DecodeMX(int index) { char *pr; if (mode result+nresult; nresult+=sprintf(pr,"%s%i",(lowercase?"mm":"MM"),index); }; // Disassemble name of 64-bit 3DNow! register and, if available, dump its // contents. static void DecodeNR(int index) { char *pr; if (mode result+nresult; nresult+=sprintf(pr,"%s%i",(lowercase?"mm":"MM"),index); }; // Service function, adds valid memory adress in MASM or Ideal format to // disassembled string. Parameters: defseg - default segment for given // register combination, descr - fully decoded register part of address, // offset - constant part of address, dsize - data size in bytes. If global // flag 'symbolic' is set, function also tries to decode offset as name of // some label. static void Memadr(int defseg,const char *descr,long offset,int dsize) { int i,n,seg; char *pr; char s[TEXTLEN]; if (mode result+nresult; n=0; if (segprefix!=SEG_UNDEF) seg=segprefix; else seg=defseg; if (ideal!=0) pr[n++]='['; // In some cases Disassembler may omit size of memory operand. Namely, flag // showmemsize must be 0, type bit C_EXPL must be 0 (this bit namely means // that explicit operand size is necessary) and type of command must not be // C_MMX or C_NOW (because bit C_EXPL has in these cases different meaning). // Otherwise, exact size must be supplied. if (showmemsize!=0 || (da->cmdtype & C_TYPEMASK)==C_MMX || (da->cmdtype & C_TYPEMASK)==C_NOW || (da->cmdtype & C_EXPL)!=0 ) { if (dsize =DISASM_CODE) i=Decodeaddress(offset,s,TEXTLEN-n-24,NULL); else i=0; if (i>0) { // Offset decoded in symbolic form if (*descr!='\0') pr[n++]='+'; strcpy(pr+n,s); n+=i; } else if (offset<0 && offset>-16384 && *descr!='\0') n+=sprintf(pr+n,"-%lX",-offset); else { if (*descr!='\0') pr[n++]='+'; n+=sprintf(pr+n,"%lX",offset); }; }; pr[n++]=']'; pr[n]='\0'; nresult+=n; }; // Disassemble memory/register from the ModRM/SIB bytes and, if available, dump // address and contents of memory. static void DecodeMR(int type) { int j,memonly,inmemory,seg; int c,sib; ulong dsize,regsize,addr; char s[TEXTLEN]; if (size<2) { da->error=DAE_CROSS; return; }; // ModR/M byte outside the memory block hasrm=1; dsize=regsize=datasize; // Default size of addressed reg/memory memonly=0; // Register in ModM field is allowed // Size and kind of addressed memory or register in ModM has no influence on // the command size, and exact calculations are omitted if only command size // is requested. If register is used, optype will be incorrect and we need // to correct it later. c=cmd[1] & 0xC7; // Leave only Mod and M fields if (mode>=DISASM_DATA) { if ((c & 0xC0)==0xC0) // Register operand inmemory=0; else // Memory operand inmemory=1; switch (type) { case MRG: // Memory/register in ModRM byte if (inmemory) { if (datasize==1) da->memtype=DEC_BYTE; else if (datasize==2) da->memtype=DEC_WORD; else da->memtype=DEC_DWORD; }; break; case MRJ: // Memory/reg in ModRM as JUMP target if (datasize!=2 && inmemory) da->memtype=DEC_DWORD; if (mode>=DISASM_FILE && shownear!=0) nresult+=sprintf(da->result+nresult,"%s ",(lowercase?"near":"NEAR")); break; case MR1: // 1-byte memory/register in ModRM byte dsize=regsize=1; if (inmemory) da->memtype=DEC_BYTE; break; case MR2: // 2-byte memory/register in ModRM byte dsize=regsize=2; if (inmemory) da->memtype=DEC_WORD; break; case MR4: // 4-byte memory/register in ModRM byte case RR4: // 4-byte memory/register (register only) dsize=regsize=4; if (inmemory) da->memtype=DEC_DWORD; break; case MR8: // 8-byte memory/MMX register in ModRM case RR8: // 8-byte MMX register only in ModRM dsize=8; if (inmemory) da->memtype=DEC_QWORD; break; case MRD: // 8-byte memory/3DNow! register in ModRM case RRD: // 8-byte memory/3DNow! (register only) dsize=8; if (inmemory) da->memtype=DEC_3DNOW; break; case MMA: // Memory address in ModRM byte for LEA memonly=1; break; case MML: // Memory in ModRM byte (for LES) dsize=datasize+2; memonly=1; if (datasize==4 && inmemory) da->memtype=DEC_FWORD; da->warnings|=DAW_SEGMENT; break; case MMS: // Memory in ModRM byte (as SEG:OFFS) dsize=datasize+2; memonly=1; if (datasize==4 && inmemory) da->memtype=DEC_FWORD; if (mode>=DISASM_FILE) nresult+=sprintf(da->result+nresult,"%s ",(lowercase?"far":"FAR")); break; case MM6: // Memory in ModRM (6-byte descriptor) dsize=6; memonly=1; if (inmemory) da->memtype=DEC_FWORD; break; case MMB: // Two adjacent memory locations (BOUND) dsize=(ideal?datasize:datasize*2); memonly=1; break; case MD2: // Memory in ModRM byte (16-bit integer) case MB2: // Memory in ModRM byte (16-bit binary) dsize=2; memonly=1; if (inmemory) da->memtype=DEC_WORD; break; case MD4: // Memory in ModRM byte (32-bit integer) dsize=4; memonly=1; if (inmemory) da->memtype=DEC_DWORD; break; case MD8: // Memory in ModRM byte (64-bit integer) dsize=8; memonly=1; if (inmemory) da->memtype=DEC_QWORD; break; case MDA: // Memory in ModRM byte (80-bit BCD) dsize=10; memonly=1; if (inmemory) da->memtype=DEC_TBYTE; break; case MF4: // Memory in ModRM byte (32-bit float) dsize=4; memonly=1; if (inmemory) da->memtype=DEC_FLOAT4; break; case MF8: // Memory in ModRM byte (64-bit float) dsize=8; memonly=1; if (inmemory) da->memtype=DEC_FLOAT8; break; case MFA: // Memory in ModRM byte (80-bit float) dsize=10; memonly=1; if (inmemory) da->memtype=DEC_FLOAT10; break; case MFE: // Memory in ModRM byte (FPU environment) dsize=28; memonly=1; break; case MFS: // Memory in ModRM byte (FPU state) dsize=108; memonly=1; break; case MFX: // Memory in ModRM byte (ext. FPU state) dsize=512; memonly=1; break; default: // Operand is not in ModM! da->error=DAE_INTERN; break; }; }; addr=0; // There are many possibilities to decode ModM/SIB address. The first // possibility is register in ModM - general-purpose, MMX or 3DNow! if ((c & 0xC0)==0xC0) { // Decode register operand if (type==MR8 || type==RR8) DecodeMX(c); // MMX register else if (type==MRD || type==RRD) DecodeNR(c); // 3DNow! register else DecodeRG(c,regsize,type); // General-purpose register if (memonly!=0) softerror=DAE_MEMORY; // Register where only memory allowed return; }; // Next possibility: 16-bit addressing mode, very seldom in 32-bit flat model // but still supported by processor. SIB byte is never used here. if (addrsize==2) { if (c==0x06) { // Special case of immediate address dispsize=2; if (size<4) da->error=DAE_CROSS; // Disp16 outside the memory block else if (mode>=DISASM_DATA) { da->adrconst=addr=*(ushort *)(cmd+2); if (addr==0) da->zeroconst=1; seg=SEG_DS; Memadr(seg,"",addr,dsize); }; } else { da->indexed=1; if ((c & 0xC0)==0x40) { // 8-bit signed displacement if (size<3) da->error=DAE_CROSS; else addr=(signed char)cmd[2] & 0xFFFF; dispsize=1; } else if ((c & 0xC0)==0x80) { // 16-bit unsigned displacement if (size<4) da->error=DAE_CROSS; else addr=*(ushort *)(cmd+2); dispsize=2; }; if (mode>=DISASM_DATA && da->error==DAE_NOERR) { da->adrconst=addr; if (addr==0) da->zeroconst=1; seg=addr16[c & 0x07].defseg; Memadr(seg,addr16[c & 0x07].descr,addr,dsize); }; }; } // Next possibility: immediate 32-bit address. else if (c==0x05) { // Special case of immediate address dispsize=4; if (size<6) da->error=DAE_CROSS; // Disp32 outside the memory block else if (mode>=DISASM_DATA) { da->adrconst=addr=*(ulong *)(cmd+2); if (pfixup==NULL) pfixup=cmd+2; da->fixupsize+=4; if (addr==0) da->zeroconst=1; seg=SEG_DS; Memadr(seg,"",addr,dsize); }; } // Next possibility: 32-bit address with SIB byte. else if ((c & 0x07)==0x04) { // SIB addresation sib=cmd[2]; hassib=1; *s='\0'; if (c==0x04 && (sib & 0x07)==0x05) { dispsize=4; // Immediate address without base if (size<7) da->error=DAE_CROSS; // Disp32 outside the memory block else { da->adrconst=addr=*(ulong *)(cmd+3); if (pfixup==NULL) pfixup=cmd+3; da->fixupsize+=4; if (addr==0) da->zeroconst=1; if ((sib & 0x38)!=0x20) { // Index register present da->indexed=1; if (type==MRJ) da->jmptable=addr; }; seg=SEG_DS; }; } else { // Base and, eventually, displacement if ((c & 0xC0)==0x40) { // 8-bit displacement dispsize=1; if (size<4) da->error=DAE_CROSS; else { da->adrconst=addr=(signed char)cmd[3]; if (addr==0) da->zeroconst=1; }; } else if ((c & 0xC0)==0x80) { // 32-bit displacement dispsize=4; if (size<7) da->error=DAE_CROSS; // Disp32 outside the memory block else { da->adrconst=addr=*(ulong *)(cmd+3); if (pfixup==NULL) pfixup=cmd+3; da->fixupsize+=4; if (addr==0) da->zeroconst=1; // Most compilers use address of type [index*4+displacement] to // address jump table (switch). But, for completeness, I allow all // cases which include index with scale 1 or 4, base or both. if (type==MRJ) da->jmptable=addr; }; }; da->indexed=1; j=sib & 0x07; if (mode>=DISASM_FILE) { strcpy(s,regname[2][j]); seg=addr32[j].defseg; }; }; if ((sib & 0x38)!=0x20) { // Scaled index present if ((sib & 0xC0)==0x40) da->indexed=2; else if ((sib & 0xC0)==0x80) da->indexed=4; else if ((sib & 0xC0)==0xC0) da->indexed=8; else da->indexed=1; }; if (mode>=DISASM_FILE && da->error==DAE_NOERR) { if ((sib & 0x38)!=0x20) { // Scaled index present if (*s!='\0') strcat(s,"+"); strcat(s,addr32[(sib>>3) & 0x07].descr); if ((sib & 0xC0)==0x40) { da->jmptable=0; // Hardly a switch! strcat(s,"*2"); } else if ((sib & 0xC0)==0x80) strcat(s,"*4"); else if ((sib & 0xC0)==0xC0) { da->jmptable=0; // Hardly a switch! strcat(s,"*8"); }; }; Memadr(seg,s,addr,dsize); }; } // Last possibility: 32-bit address without SIB byte. else { // No SIB if ((c & 0xC0)==0x40) { dispsize=1; if (size<3) da->error=DAE_CROSS; // Disp8 outside the memory block else { da->adrconst=addr=(signed char)cmd[2]; if (addr==0) da->zeroconst=1; }; } else if ((c & 0xC0)==0x80) { dispsize=4; if (size<6) da->error=DAE_CROSS; // Disp32 outside the memory block else { da->adrconst=addr=*(ulong *)(cmd+2); if (pfixup==NULL) pfixup=cmd+2; da->fixupsize+=4; if (addr==0) da->zeroconst=1; if (type==MRJ) da->jmptable=addr; }; }; da->indexed=1; if (mode>=DISASM_FILE && da->error==DAE_NOERR) { seg=addr32[c & 0x07].defseg; Memadr(seg,addr32[c & 0x07].descr,addr,dsize); }; }; }; // Disassemble implicit source of string operations and, if available, dump // address and contents. static void DecodeSO(void) { if (mode memtype=DEC_BYTE; else if (datasize==2) da->memtype=DEC_WORD; else if (datasize==4) da->memtype=DEC_DWORD; da->indexed=1; Memadr(SEG_DS,regname[addrsize==2?1:2][REG_ESI],0L,datasize); }; // Disassemble implicit destination of string operations and, if available, // dump address and contents. Destination always uses segment ES, and this // setting cannot be overridden. static void DecodeDE(void) { int seg; if (mode memtype=DEC_BYTE; else if (datasize==2) da->memtype=DEC_WORD; else if (datasize==4) da->memtype=DEC_DWORD; da->indexed=1; seg=segprefix; segprefix=SEG_ES; // Fake Memadr by changing segment prefix Memadr(SEG_DS,regname[addrsize==2?1:2][REG_EDI],0L,datasize); segprefix=seg; // Restore segment prefix }; // Decode XLAT operand and, if available, dump address and contents. static void DecodeXL(void) { if (mode memtype=DEC_BYTE; da->indexed=1; Memadr(SEG_DS,(addrsize==2?"BX+AL":"EBX+AL"),0L,1); }; // Decode immediate operand of size constsize. If sxt is non-zero, byte operand // should be sign-extended to sxt bytes. If type of immediate constant assumes // this, small negative operands may be displayed as signed negative numbers. // Note that in most cases immediate operands are not shown in comment window. static void DecodeIM(int constsize,int sxt,int type) { int i; signed long data; ulong l; char name[TEXTLEN] = { 0 }; char comment[TEXTLEN] = { 0 }; immsize+=constsize; // Allows several immediate operands if (mode error=DAE_CROSS; else if (constsize==1) { if (sxt==0) data=(uchar)cmd[l]; else data=(signed char)cmd[l]; if (type==IMS && ((data & 0xE0)!=0 || data==0)) { da->warnings|=DAW_SHIFT; da->cmdtype|=C_RARE; }; } else if (constsize==2) { if (sxt==0) data=*(ushort *)(cmd+l); else data=*(short *)(cmd+l); } else { data=*(long *)(cmd+l); if (pfixup==NULL) pfixup=cmd+l; da->fixupsize+=4; }; if (sxt==2) data&=0x0000FFFF; if (data==0 && da->error==0) da->zeroconst=1; // Command ENTER, as an exception from Intel's rules, has two immediate // constants. As the second constant is rarely used, I exclude it from // search if the first constant is non-zero (which is usually the case). if (da->immconst==0) da->immconst=data; if (mode>=DISASM_FILE && da->error==DAE_NOERR) { if (mode>=DISASM_CODE && type!=IMU) i=Decodeaddress(data,name,TEXTLEN-nresult-24,comment); else { i=0; comment[0]='\0'; }; if (i!=0 && symbolic!=0) { strcpy(da->result+nresult,name); nresult+=i; } else if (type==IMU || type==IMS || type==IM2 || data>=0 || data result+nresult,"%lX",data); else nresult+=sprintf(da->result+nresult,"-%lX",-data); if (addcomment && comment[0]!='\0') strcpy(da->comment,comment); }; }; // Decode VxD service name (always 4-byte). static void DecodeVX(void) { ulong l,data; immsize+=4; // Allows several immediate operands if (mode error=DAE_CROSS; return; }; data=*(long *)(cmd+l); if (data==0 && da->error==0) da->zeroconst=1; if (da->immconst==0) da->immconst=data; if (mode>=DISASM_FILE && da->error==DAE_NOERR) { if ((data & 0x00008000)!=0 && memicmp("VxDCall",da->result,7)==0) memcpy(da->result,lowercase?"vxdjump":"VxDJump",7); nresult+=sprintf(da->result+nresult,"%lX",data); }; }; // Decode implicit constant 1 (used in shift commands). This operand is so // insignificant that it is never shown in comment window. static void DecodeC1(void) { if (mode immconst=1; if (mode>=DISASM_FILE) nresult+=sprintf(da->result+nresult,"1"); }; // Decode immediate absolute data address. This operand is used in 8080- // compatible commands which allow to move data from memory to accumulator and // back. Note that bytes ModRM and SIB never appear in commands with IA operand. static void DecodeIA(void) { ulong addr; if (size<1+addrsize) { da->error=DAE_CROSS; return; }; dispsize=addrsize; if (mode memtype=DEC_BYTE; else if (datasize==2) da->memtype=DEC_WORD; else if (datasize==4) da->memtype=DEC_DWORD; if (addrsize==2) addr=*(ushort *)(cmd+1); else { addr=*(ulong *)(cmd+1); if (pfixup==NULL) pfixup=cmd+1; da->fixupsize+=4; }; da->adrconst=addr; if (addr==0) da->zeroconst=1; if (mode>=DISASM_FILE) { Memadr(SEG_DS,"",addr,datasize); }; }; // Decodes jump relative to nextip of size offsize. static void DecodeRJ(ulong offsize,ulong nextip) { int i; ulong addr; char s[TEXTLEN]; if (size error=DAE_CROSS; return; }; dispsize=offsize; // Interpret offset as displacement if (mode jmpconst=addr; if (addr==0) da->zeroconst=1; if (mode>=DISASM_FILE) { if (offsize==1) nresult+=sprintf(da->result+nresult, "%s ",(lowercase==0?"SHORT":"short")); if (mode>=DISASM_CODE) i=Decodeaddress(addr,s,TEXTLEN,da->comment); else i=0; if (symbolic==0 || i==0) nresult+=sprintf(da->result+nresult,"%08lX",addr); else nresult+=sprintf(da->result+nresult,"%.*s",TEXTLEN-nresult-25,s); if (symbolic==0 && i!=0 && da->comment[0]=='\0') strcpy(da->comment,s); ; }; }; // Decode immediate absolute far jump address. In flat model, such addresses // are not used (mostly because selector is specified directly in the command), // so I neither decode as symbol nor comment it. To allow search for selector // by value, I interprete it as an immediate constant. static void DecodeJF(void) { ulong addr,seg; if (size<1+addrsize+2) { da->error=DAE_CROSS; return; }; dispsize=addrsize; immsize=2; // Non-trivial but allowed interpretation if (mode jmpconst=addr; da->immconst=seg; if (addr==0 || seg==0) da->zeroconst=1; if (mode>=DISASM_FILE) { nresult+=sprintf(da->result+nresult,"%s %04X:%08X", (lowercase==0?"FAR":"far"),seg,addr); }; }; // Decode segment register. In flat model, operands of this type are seldom. static void DecodeSG(int index) { int i; if (mode =6) softerror=DAE_BADSEG; // Undefined segment register if (mode>=DISASM_FILE) { i=sprintf(da->result+nresult,"%s",segname[index]); if (lowercase) strlwr(da->result+nresult); nresult+=i; }; }; // Decode control register addressed in R part of ModRM byte. Operands of // this type are extremely rare. Contents of control registers are accessible // only from privilege level 0, so I cannot dump them here. static void DecodeCR(int index) { hasrm=1; if (mode>=DISASM_FILE) { index=(index>>3) & 0x07; nresult+=sprintf(da->result+nresult,"%s",crname[index]); if (lowercase) strlwr(da->result+nresult); }; }; // Decode debug register addressed in R part of ModRM byte. Operands of // this type are extremely rare. I can dump only those debug registers // available in CONTEXT structure. static void DecodeDR(int index) { int i; hasrm=1; if (mode>=DISASM_FILE) { index=(index>>3) & 0x07; i=sprintf(da->result+nresult,"%s",drname[index]); if (lowercase) strlwr(da->result+nresult); nresult+=i; }; }; // Skips 3DNow! operands and extracts command suffix. Returns suffix or -1 if // suffix lies outside the memory block. This subroutine assumes that cmd still // points to the beginning of 3DNow! command (i.e. to the sequence of two bytes // 0F, 0F). static int Get3dnowsuffix(void) { int c,sib; ulong offset; if (size<3) return -1; // Suffix outside the memory block offset=3; c=cmd[2] & 0xC7; // Leave only Mod and M fields // Register in ModM - general-purpose, MMX or 3DNow! if ((c & 0xC0)==0xC0) ; // 16-bit addressing mode, SIB byte is never used here. else if (addrsize==2) { if (c==0x06) // Special case of immediate address offset+=2; else if ((c & 0xC0)==0x40) // 8-bit signed displacement offset++; else if ((c & 0xC0)==0x80) // 16-bit unsigned displacement offset+=2; ; } // Immediate 32-bit address. else if (c==0x05) // Special case of immediate address offset+=4; // 32-bit address with SIB byte. else if ((c & 0x07)==0x04) { // SIB addresation if (size<4) return -1; // Suffix outside the memory block sib=cmd[3]; offset++; if (c==0x04 && (sib & 0x07)==0x05) offset+=4; // Immediate address without base else if ((c & 0xC0)==0x40) // 8-bit displacement offset+=1; else if ((c & 0xC0)==0x80) // 32-bit dislacement offset+=4; ; } // 32-bit address without SIB byte else if ((c & 0xC0)==0x40) offset+=1; else if ((c & 0xC0)==0x80) offset+=4; if (offset>=size) return -1; // Suffix outside the memory block return cmd[offset]; }; // Function checks whether 80x86 flags meet condition set in the command. // Returns 1 if condition is met, 0 if not and -1 in case of error (which is // not possible). int Checkcondition(int code,ulong flags) { ulong cond,temp; switch (code & 0x0E) { case 0: // If overflow cond=flags & 0x0800; break; case 2: // If below cond=flags & 0x0001; break; case 4: // If equal cond=flags & 0x0040; break; case 6: // If below or equal cond=flags & 0x0041; break; case 8: // If sign cond=flags & 0x0080; break; case 10: // If parity cond=flags & 0x0004; break; case 12: // If less temp=flags & 0x0880; cond=(temp==0x0800 || temp==0x0080); break; case 14: // If less or equal temp=flags & 0x0880; cond=(temp==0x0800 || temp==0x0080 || (flags & 0x0040)!=0); break; default: return -1; // Internal error, not possible! }; if ((code & 0x01)==0) return (cond!=0); else return (cond==0); // Invert condition }; ulong Disasm(char *src,ulong srcsize,ulong srcip, t_disasm *disasm,int disasmmode) { int i,j,isprefix,is3dnow,repeated,operand,mnemosize,arg; ulong u,code; int lockprefix; // Non-zero if lock prefix present int repprefix; // REPxxx prefix or 0 int cxsize; char name[TEXTLEN],*pname; const t_cmddata *pd,*pdan; // Prepare disassembler variables and initialize structure disasm. datasize=addrsize=4; // 32-bit code and data segments only! segprefix=SEG_UNDEF; hasrm=hassib=0; dispsize=immsize=0; lockprefix=0; repprefix=0; ndump=0; nresult=0; cmd=src; size=srcsize; pfixup=NULL; softerror=0; is3dnow=0; da=disasm; da->ip=srcip; da->comment[0]='\0'; da->cmdtype=C_BAD; da->nprefix=0; da->memtype=DEC_UNKNOWN; da->indexed=0; da->jmpconst=0; da->jmptable=0; da->adrconst=0; da->immconst=0; da->zeroconst=0; da->fixupoffset=0; da->fixupsize=0; da->warnings=0; da->error=DAE_NOERR; mode=disasmmode; // No need to use register contents // Correct 80x86 command may theoretically contain up to 4 prefixes belonging // to different prefix groups. This limits maximal possible size of the // command to MAXCMDSIZE=16 bytes. In order to maintain this limit, if // Disasm() detects second prefix from the same group, it flushes first // prefix in the sequence as a pseudocommand. u=0; repeated=0; while (size>0) { isprefix=1; // Assume that there is some prefix switch (*cmd) { case 0x26: if (segprefix==SEG_UNDEF) segprefix=SEG_ES; else repeated=1; break; case 0x2E: if (segprefix==SEG_UNDEF) segprefix=SEG_CS; else repeated=1; break; case 0x36: if (segprefix==SEG_UNDEF) segprefix=SEG_SS; else repeated=1; break; case 0x3E: if (segprefix==SEG_UNDEF) segprefix=SEG_DS; else repeated=1; break; case 0x64: if (segprefix==SEG_UNDEF) segprefix=SEG_FS; else repeated=1; break; case 0x65: if (segprefix==SEG_UNDEF) segprefix=SEG_GS; else repeated=1; break; case 0x66: if (datasize==4) datasize=2; else repeated=1; break; case 0x67: if (addrsize==4) addrsize=2; else repeated=1; break; case 0xF0: if (lockprefix==0) lockprefix=0xF0; else repeated=1; break; case 0xF2: if (repprefix==0) repprefix=0xF2; else repeated=1; break; case 0xF3: if (repprefix==0) repprefix=0xF3; else repeated=1; break; default: isprefix=0; break; }; if (isprefix==0 || repeated!=0) break; // No more prefixes or duplicated prefix if (mode>=DISASM_FILE) ndump+=sprintf(da->dump+ndump,"%02X:",*cmd); da->nprefix++; cmd++; srcip++; size--; u++; }; // We do have repeated prefix. Flush first prefix from the sequence. if (repeated) { if (mode>=DISASM_FILE) { da->dump[3]='\0'; // Leave only first dumped prefix da->nprefix=1; switch (cmd[-(long)u]) { case 0x26: pname=(char *)(segname[SEG_ES]); break; case 0x2E: pname=(char *)(segname[SEG_CS]); break; case 0x36: pname=(char *)(segname[SEG_SS]); break; case 0x3E: pname=(char *)(segname[SEG_DS]); break; case 0x64: pname=(char *)(segname[SEG_FS]); break; case 0x65: pname=(char *)(segname[SEG_GS]); break; case 0x66: pname="DATASIZE"; break; case 0x67: pname="ADDRSIZE"; break; case 0xF0: pname="LOCK"; break; case 0xF2: pname="REPNE"; break; case 0xF3: pname="REPE"; break; default: pname="?"; break; }; nresult+=sprintf(da->result+nresult,"PREFIX %s:",pname); if (lowercase) strlwr(da->result); if (extraprefix==0) strcpy(da->comment,"Superfluous prefix"); }; da->warnings|=DAW_PREFIX; if (lockprefix) da->warnings|=DAW_LOCK; da->cmdtype=C_RARE; return 1; // Any prefix is 1 byte long }; // If lock prefix available, display it and forget, because it has no // influence on decoding of rest of the command. if (lockprefix!=0) { if (mode>=DISASM_FILE) nresult+=sprintf(da->result+nresult,"LOCK "); da->warnings|=DAW_LOCK; }; // Fetch (if available) first 3 bytes of the command, add repeat prefix and // find command in the command table. code=0; if (size>0) *(((char *)&code)+0)=cmd[0]; if (size>1) *(((char *)&code)+1)=cmd[1]; if (size>2) *(((char *)&code)+2)=cmd[2]; if (repprefix!=0) // RER/REPE/REPNE is considered to be code=(code<<8) | repprefix; // part of command. if (decodevxd && (code & 0xFFFF)==0x20CD) pd=&vxdcmd; // Decode VxD call (Win95/98) else { for (pd=cmddata; pd->mask!=0; pd++) { if (((code^pd->code) & pd->mask)!=0) continue; if (mode>=DISASM_FILE && shortstringcmds && (pd->arg1==MSO || pd->arg1==MDE || pd->arg2==MSO || pd->arg2==MDE)) continue; // Search short form of string command break; }; }; if ((pd->type & C_TYPEMASK)==C_NOW) { // 3DNow! commands require additional search. is3dnow=1; j=Get3dnowsuffix(); if (j<0) da->error=DAE_CROSS; else { for ( ; pd->mask!=0; pd++) { if (((code^pd->code) & pd->mask)!=0) continue; if (((uchar *)&(pd->code))[2]==j) break; }; }; }; if (pd->mask==0) { // Command not found da->cmdtype=C_BAD; if (size<2) da->error=DAE_CROSS; else da->error=DAE_BADCMD; } else { // Command recognized, decode it da->cmdtype=pd->type; cxsize=datasize; // Default size of ECX used as counter if (segprefix==SEG_FS || segprefix==SEG_GS || lockprefix!=0) da->cmdtype|=C_RARE; // These prefixes are rare if (pd->bits==PR) da->warnings|=DAW_PRIV; // Privileged command (ring 0) else if (pd->bits==WP) da->warnings|=DAW_IO; // I/O command // Win32 programs usually try to keep stack dword-aligned, so INC ESP // (44) and DEC ESP (4C) usually don't appear in real code. Also check for // ADD ESP,imm and SUB ESP,imm (81,C4,imm32; 83,C4,imm8; 81,EC,imm32; // 83,EC,imm8). if (cmd[0]==0x44 || cmd[0]==0x4C || (size>=3 && (cmd[0]==0x81 || cmd[0]==0x83) && (cmd[1]==0xC4 || cmd[1]==0xEC) && (cmd[2] & 0x03)!=0) ) { da->warnings|=DAW_STACK; da->cmdtype|=C_RARE; }; // Warn also on MOV SEG,... (8E...). Win32 works in flat mode. if (cmd[0]==0x8E) da->warnings|=DAW_SEGMENT; // If opcode is 2-byte, adjust command. if (pd->len==2) { if (size==0) da->error=DAE_CROSS; else { if (mode>=DISASM_FILE) ndump+=sprintf(da->dump+ndump,"%02X",*cmd); cmd++; srcip++; size--; }; }; if (size==0) da->error=DAE_CROSS; // Some commands either feature non-standard data size or have bit which // allowes to select data size. if ((pd->bits & WW)!=0 && (*cmd & WW)==0) datasize=1; // Bit W in command set to 0 else if ((pd->bits & W3)!=0 && (*cmd & W3)==0) datasize=1; // Another position of bit W else if ((pd->bits & FF)!=0) datasize=2; // Forced word (2-byte) size // Some commands either have mnemonics which depend on data size (8/16 bits // or 32 bits, like CWD/CDQ), or have several different mnemonics (like // JNZ/JNE). First case is marked by either '&' (mnemonic depends on // operand size) or '$' (depends on address size). In the second case, // there is no special marker and disassembler selects main mnemonic. if (mode>=DISASM_FILE) { if (pd->name[0]=='&') mnemosize=datasize; else if (pd->name[0]=='$') mnemosize=addrsize; else mnemosize=0; if (mnemosize!=0) { for (i=0,j=1; pd->name[j]!='\0'; j++) { if (pd->name[j]==':') { // Separator between 16/32 mnemonics if (mnemosize==4) i=0; else break; } else if (pd->name[j]=='*') { // Substitute by 'W', 'D' or none if (mnemosize==4 && sizesens!=2) name[i++]='D'; else if (mnemosize!=4 && sizesens!=0) name[i++]='W'; } else name[i++]=pd->name[j]; }; name[i]='\0'; } else { strcpy(name,pd->name); for (i=0; name[i]!='\0'; i++) { if (name[i]==',') { // Use main mnemonic name[i]='\0'; break; }; }; }; if (repprefix!=0 && tabarguments) { for (i=0; name[i]!='\0' && name[i]!=' '; i++) da->result[nresult++]=name[i]; if (name[i]==' ') { da->result[nresult++]=' '; i++; }; while (nresult<8) da->result[nresult++]=' '; for ( ; name[i]!='\0'; i++) da->result[nresult++]=name[i]; ; } else nresult+=sprintf(da->result+nresult,"%s",name); if (lowercase) strlwr(da->result); }; // Decode operands (explicit - encoded in command, implicit - present in // mmemonic or assumed - used or modified by command). Assumed operands // must stay after all explicit and implicit operands. Up to 3 operands // are allowed. for (operand=0; operand<3; operand++) { if (da->error) break; // Error - no sense to continue // If command contains both source and destination, one usually must not // decode destination to comment because it will be overwritten on the // next step. Global addcomment takes care of this. Decoding routines, // however, may ignore this flag. if (operand==0 && pd->arg2!=NNN && pd->arg2 arg1; else if (operand==1) arg=pd->arg2; else arg=pd->arg3; if (arg==NNN) break; // No more operands // Arguments with arg>=PSEUDOOP are assumed operands and are not // displayed in disassembled result, so they require no delimiter. if ((mode>=DISASM_FILE) && arg result[nresult++]=' '; if (tabarguments) { while (nresult<8) da->result[nresult++]=' '; }; } else { da->result[nresult++]=','; if (extraspace) da->result[nresult++]=' '; }; }; // Decode, analyse and comment next operand of the command. switch (arg) { case REG: // Integer register in Reg field if (size<2) da->error=DAE_CROSS; else DecodeRG(cmd[1]>>3,datasize,REG); hasrm=1; break; case RCM: // Integer register in command byte DecodeRG(cmd[0],datasize,RCM); break; case RG4: // Integer 4-byte register in Reg field if (size<2) da->error=DAE_CROSS; else DecodeRG(cmd[1]>>3,4,RG4); hasrm=1; break; case RAC: // Accumulator (AL/AX/EAX, implicit) DecodeRG(REG_EAX,datasize,RAC); break; case RAX: // AX (2-byte, implicit) DecodeRG(REG_EAX,2,RAX); break; case RDX: // DX (16-bit implicit port address) DecodeRG(REG_EDX,2,RDX); break; case RCL: // Implicit CL register (for shifts) DecodeRG(REG_ECX,1,RCL); break; case RS0: // Top of FPU stack (ST(0)) DecodeST(0,0); break; case RST: // FPU register (ST(i)) in command byte DecodeST(cmd[0],0); break; case RMX: // MMX register MMx if (size<2) da->error=DAE_CROSS; else DecodeMX(cmd[1]>>3); hasrm=1; break; case R3D: // 3DNow! register MMx if (size<2) da->error=DAE_CROSS; else DecodeNR(cmd[1]>>3); hasrm=1; break; case MRG: // Memory/register in ModRM byte case MRJ: // Memory/reg in ModRM as JUMP target case MR1: // 1-byte memory/register in ModRM byte case MR2: // 2-byte memory/register in ModRM byte case MR4: // 4-byte memory/register in ModRM byte case MR8: // 8-byte memory/MMX register in ModRM case MRD: // 8-byte memory/3DNow! register in ModRM case MMA: // Memory address in ModRM byte for LEA case MML: // Memory in ModRM byte (for LES) case MM6: // Memory in ModRm (6-byte descriptor) case MMB: // Two adjacent memory locations (BOUND) case MD2: // Memory in ModRM byte (16-bit integer) case MB2: // Memory in ModRM byte (16-bit binary) case MD4: // Memory in ModRM byte (32-bit integer) case MD8: // Memory in ModRM byte (64-bit integer) case MDA: // Memory in ModRM byte (80-bit BCD) case MF4: // Memory in ModRM byte (32-bit float) case MF8: // Memory in ModRM byte (64-bit float) case MFA: // Memory in ModRM byte (80-bit float) case MFE: // Memory in ModRM byte (FPU environment) case MFS: // Memory in ModRM byte (FPU state) case MFX: // Memory in ModRM byte (ext. FPU state) DecodeMR(arg); break; case MMS: // Memory in ModRM byte (as SEG:OFFS) DecodeMR(arg); da->warnings|=DAW_FARADDR; break; case RR4: // 4-byte memory/register (register only) case RR8: // 8-byte MMX register only in ModRM case RRD: // 8-byte memory/3DNow! (register only) if ((cmd[1] & 0xC0)!=0xC0) softerror=DAE_REGISTER; DecodeMR(arg); break; case MSO: // Source in string op's ([ESI]) DecodeSO(); break; case MDE: // Destination in string op's ([EDI]) DecodeDE(); break; case MXL: // XLAT operand ([EBX+AL]) DecodeXL(); break; case IMM: // Immediate data (8 or 16/32) case IMU: // Immediate unsigned data (8 or 16/32) if ((pd->bits & SS)!=0 && (*cmd & 0x02)!=0) DecodeIM(1,datasize,arg); else DecodeIM(datasize,0,arg); break; case VXD: // VxD service (32-bit only) DecodeVX(); break; case IMX: // Immediate sign-extendable byte DecodeIM(1,datasize,arg); break; case C01: // Implicit constant 1 (for shifts) DecodeC1(); break; case IMS: // Immediate byte (for shifts) case IM1: // Immediate byte DecodeIM(1,0,arg); break; case IM2: // Immediate word (ENTER/RET) DecodeIM(2,0,arg); if ((da->immconst & 0x03)!=0) da->warnings|=DAW_STACK; break; case IMA: // Immediate absolute near data address DecodeIA(); break; case JOB: // Immediate byte offset (for jumps) DecodeRJ(1,srcip+2); break; case JOW: // Immediate full offset (for jumps) DecodeRJ(datasize,srcip+datasize+1); break; case JMF: // Immediate absolute far jump/call addr DecodeJF(); da->warnings|=DAW_FARADDR; break; case SGM: // Segment register in ModRM byte if (size<2) da->error=DAE_CROSS; DecodeSG(cmd[1]>>3); hasrm=1; break; case SCM: // Segment register in command byte DecodeSG(cmd[0]>>3); if ((da->cmdtype & C_TYPEMASK)==C_POP) da->warnings|=DAW_SEGMENT; break; case CRX: // Control register CRx if ((cmd[1] & 0xC0)!=0xC0) da->error=DAE_REGISTER; DecodeCR(cmd[1]); break; case DRX: // Debug register DRx if ((cmd[1] & 0xC0)!=0xC0) da->error=DAE_REGISTER; DecodeDR(cmd[1]); break; case PRN: // Near return address (pseudooperand) break; case PRF: // Far return address (pseudooperand) da->warnings|=DAW_FARADDR; break; case PAC: // Accumulator (AL/AX/EAX, pseudooperand) DecodeRG(REG_EAX,datasize,PAC); break; case PAH: // AH (in LAHF/SAHF, pseudooperand) case PFL: // Lower byte of flags (pseudooperand) break; case PS0: // Top of FPU stack (pseudooperand) DecodeST(0,1); break; case PS1: // ST(1) (pseudooperand) DecodeST(1,1); break; case PCX: // CX/ECX (pseudooperand) DecodeRG(REG_ECX,cxsize,PCX); break; case PDI: // EDI (pseudooperand in MMX extentions) DecodeRG(REG_EDI,4,PDI); break; default: da->error=DAE_INTERN; // Unknown argument type break; }; }; // Check whether command may possibly contain fixups. if (pfixup!=NULL && da->fixupsize>0) da->fixupoffset=pfixup-src; // Segment prefix and address size prefix are superfluous for command which // does not access memory. If this the case, mark command as rare to help // in analysis. if (da->memtype==DEC_UNKNOWN && (segprefix!=SEG_UNDEF || (addrsize!=4 && pd->name[0]!='$')) ) { da->warnings|=DAW_PREFIX; da->cmdtype|=C_RARE; }; // 16-bit addressing is rare in 32-bit programs. If this is the case, // mark command as rare to help in analysis. if (addrsize!=4) da->cmdtype|=C_RARE; }; // Suffix of 3DNow! command is accounted best by assuming it immediate byte // constant. if (is3dnow) { if (immsize!=0) da->error=DAE_BADCMD; else immsize=1; }; // Right or wrong, command decoded. Now dump it. if (da->error!=0) { // Hard error in command detected if (mode>=DISASM_FILE) nresult=sprintf(da->result,"???"); if (da->error==DAE_BADCMD && (*cmd==0x0F || *cmd==0xFF) && size>0 ) { if (mode>=DISASM_FILE) ndump+=sprintf(da->dump+ndump,"%02X",*cmd); cmd++; size--; }; if (size>0) { if (mode>=DISASM_FILE) ndump+=sprintf(da->dump+ndump,"%02X",*cmd); cmd++; size--; }; } else { // No hard error, dump command if (mode>=DISASM_FILE) { ndump+=sprintf(da->dump+ndump,"%02X",*cmd++); if (hasrm) ndump+=sprintf(da->dump+ndump,"%02X",*cmd++); if (hassib) ndump+=sprintf(da->dump+ndump,"%02X",*cmd++); if (dispsize!=0) { da->dump[ndump++]=' '; for (i=0; i dump+ndump,"%02X",*cmd++); }; }; if (immsize!=0) { da->dump[ndump++]=' '; for (i=0; i dump+ndump,"%02X",*cmd++); }; }; } else cmd+=1+hasrm+hassib+dispsize+immsize; size-=1+hasrm+hassib+dispsize+immsize; }; // Check that command is not a dangerous one. if (mode>=DISASM_DATA) { for (pdan=dangerous; pdan->mask!=0; pdan++) { if (((code^pdan->code) & pdan->mask)!=0) continue; if (pdan->type==C_DANGERLOCK && lockprefix==0) break; // Command harmless without LOCK prefix if (iswindowsnt && pdan->type==C_DANGER95) break; // Command harmless under Windows NT // Dangerous command! if (pdan->type==C_DANGER95) da->warnings|=DAW_DANGER95; else da->warnings|=DAW_DANGEROUS; break; }; }; if (da->error==0 && softerror!=0) da->error=softerror; // Error, but still display command if (mode>=DISASM_FILE) { if (da->error!=DAE_NOERR) switch (da->error) { case DAE_CROSS: strcpy(da->comment,"Command crosses end of memory block"); break; case DAE_BADCMD: strcpy(da->comment,"Unknown command"); break; case DAE_BADSEG: strcpy(da->comment,"Undefined segment register"); break; case DAE_MEMORY: strcpy(da->comment,"Illegal use of register"); break; case DAE_REGISTER: strcpy(da->comment,"Memory address not allowed"); break; case DAE_INTERN: strcpy(da->comment,"Internal OLLYDBG error"); break; default: strcpy(da->comment,"Unknown error"); break; } else if ((da->warnings & DAW_PRIV)!=0 && privileged==0) strcpy(da->comment,"Privileged command"); else if ((da->warnings & DAW_IO)!=0 && iocommand==0) strcpy(da->comment,"I/O command"); else if ((da->warnings & DAW_FARADDR)!=0 && farcalls==0) { if ((da->cmdtype & C_TYPEMASK)==C_JMP) strcpy(da->comment,"Far jump"); else if ((da->cmdtype & C_TYPEMASK)==C_CAL) strcpy(da->comment,"Far call"); else if ((da->cmdtype & C_TYPEMASK)==C_RET) strcpy(da->comment,"Far return"); ; } else if ((da->warnings & DAW_SEGMENT)!=0 && farcalls==0) strcpy(da->comment,"Modification of segment register"); else if ((da->warnings & DAW_SHIFT)!=0 && badshift==0) strcpy(da->comment,"Shift constant out of range 1..31"); else if ((da->warnings & DAW_PREFIX)!=0 && extraprefix==0) strcpy(da->comment,"Superfluous prefix"); else if ((da->warnings & DAW_LOCK)!=0 && lockedbus==0) strcpy(da->comment,"LOCK prefix"); else if ((da->warnings & DAW_STACK)!=0 && stackalign==0) strcpy(da->comment,"Unaligned stack operation"); ; }; return (srcsize-size); // Returns number of recognized bytes };