1
0
mirror of https://github.com/DxWnd/DxWnd.reloaded synced 2024-12-30 09:25:35 +01:00

2901 lines
118 KiB
C
Raw Normal View History

////////////////////////////////////////////////////////////////////////////////
// //
// 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 <http://www.gnu.org/licenses/>. //
// //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// //
// 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 <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#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; i<n-1; i++) {
if (*src==T('\0')) break;
*dest++=*src++; };
*dest=T('\0');
return i;
};
// Copies at most n-1 wide characters from src to dest and assures that dest is
// null-terminated. If lowercase is 1, simultaneously converts it to lower
// case. Slow but reliable. Returns number of copied characters, not including
// the terminal null. Attention, does not check that input parameters are
// correct!
static int Tcopycase(tchar *dest,int n,const tchar *src,int lowercase) {
int i;
if (n<=0)
return 0;
for (i=0; i<n-1; i++) {
if (*src==T('\0')) break;
if (lowercase)
*dest++=cvtlower[*src++]; // Much faster than call to tolower()
else
*dest++=*src++;
;
};
*dest=T('\0');
return i;
};
// Dumps ncode bytes of code to the string s. Returns length of resulting text,
// characters, not including terminal zero. Attention, does not check that
// input parameters are correct or that s has sufficient length!
static int Thexdump(tchar *s,uchar *code,int ncode,int lowercase) {
int d,n;
static tchar *hexchar;
hexchar=(lowercase?hexcharl:hexcharu);
n=0;
while (ncode>0) {
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<DECLIMIT && mod!=B_BINARY && mod!=B_JMPCALL && mod!=B_JMPCALLFAR))
) {
// Decode as decimal unsigned number.
if ((nummode & NUM_STYLE)==NUM_OLLY && 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<ndigit || u!=0; i++) {
lastdigit=u & 0x0F;
buf[--k]=hexchar[lastdigit];
u=(u>>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; u<CMDMASK+1; u++) {
if (((u ^ code) & mask)!=0)
continue; // Command has different first bytes
pchain=cmdchain+u;
while (pchain->pcmd!=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<<REG_EBX)|(1<<REG_ESI);
pmrm->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<<REG_EBX)|(1<<REG_EDI);
pmrm->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<<REG_EBP)|(1<<REG_ESI);
pmrm->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<<REG_EBP)|(1<<REG_EDI);
pmrm->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<<REG_ESI);
pmrm->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<<REG_EDI);
pmrm->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<<REG_EBP);
pmrm->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<<REG_EBX);
pmrm->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<<reg);
pmrm->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<<sreg);
psib->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<<reg);
psib->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<<reg);
if (sreg!=REG_ESP) {
psib->scale[sreg]+=(uchar)scale;
n=Tstrcopy(psib->ardec,SHORTNAME,regname32[sreg]);
psib->aregs|=(1<<sreg);
if (scale>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<<reg);
if (sreg!=REG_ESP) {
psib->scale[sreg]+=(uchar)scale;
n=Tstrcopy(psib->ardec,SHORTNAME,regname32[sreg]);
psib->aregs|=(1<<sreg);
if (scale>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<<reg32);
im->da->uses|=(1<<reg32); };
if (op->arg & (B_CHG|B_UPD)) {
op->modifies=(1<<reg32);
im->da->modifies|=(1<<reg32); };
// Warn if ESP is misused.
if ((op->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 (cmdsize<pmrm->size) {
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 (cmdsize<im->dispsize) {
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<<index); }
else
op->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<<index);
im->da->uses|=(1<<index);
// 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 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<<REG_EAX)|(1<<REG_EBX);
im->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<<REG_ESP);
im->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 (cmdsize<offsetsize) {
im->da->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 (cmdsize<datasize+2) {
im->da->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 (cmdsize<im->modsize+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; i<NOPERAND; i++,op++) {
op->features=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; i<NOPERAND; i++) {
arg=pcmd->arg[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-n<im.modsize+im.dispsize+1) {
da->errors|=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<<REG_ESP);
da->modifies|=(1<<REG_ESP);
};
error:
// Prepare hex dump, if requested. As maximal size of command is limited to
// MAXCMDSIZE=16 bytes, string can't overflow.
if (damode & DA_DUMP) {
if (da->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; u<da->nprefix; 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; i<noperand; i++) {
if ((config->disasmmode==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;
};