You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5373 lines
169 KiB

/*
* Copyright (c) 2020 Bitdefender
* SPDX-License-Identifier: Apache-2.0
*/
#include "include/nd_crt.h"
#include "../inc/bddisasm.h"
// The table definitions.
#include "include/tabledefs.h"
// Handy macros.
#define RET_EQ(x, y, z) if ((x) == (y)) { return (z); }
#define RET_GE(x, y, z) if ((x) >= (y)) { return (z); }
#define RET_GT(x, y, z) if ((x) > (y)) { return (z); }
#ifndef UNREFERENCED_PARAMETER
#define UNREFERENCED_PARAMETER(P) ((void)(P))
#endif
#ifndef BDDISASM_NO_FORMAT
static const char *gReg8Bit[] =
{
"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
};
static const char *gReg8Bit64[] =
{
"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b",
};
static const char *gReg16Bit[] =
{
"ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w",
};
static const char *gReg32Bit[] =
{
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d",
};
static const char *gReg64Bit[] =
{
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
};
static const char *gRegFpu[] =
{
"st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7",
};
static const char *gRegMmx[] =
{
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
};
static const char *gRegControl[] =
{
"cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
"cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15",
};
static const char *gRegDebug[] =
{
"dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7",
"dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15",
};
static const char *gRegTest[] =
{
"tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7",
"tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15",
};
static const char *gRegXmm[] =
{
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
"xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23",
"xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31",
};
static const char *gRegYmm[] =
{
"ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15",
"ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22", "ymm23",
"ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31"
};
static const char *gRegZmm[] =
{
"zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7",
"zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15",
"zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23",
"zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31",
};
static const char *gRegSeg[] =
{
"es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7",
};
static const char *gRegBound[] =
{
"bnd0", "bnd1", "bnd2", "bnd3",
};
static const char *gRegMask[] =
{
"k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7",
};
static const char *gRegTile[] =
{
"tmm0", "tmm1", "tmm2", "tmm3", "tmm4", "tmm5", "tmm6", "tmm7",
};
static const char *gConditionCodes[] =
{
"EQ", "LT", "LE", "UNORD", "NEQ", "NLT", "NLE", "ORD",
"EQ_UQ", "NGE", "NGT", "false", "NEQ_OQ", "GE", "GT", "TRUE",
"EQ_OS", "LT_OQ", "LE_OQ", "UNORD_S", "NEQ_US", "NLT_UQ", "NLE_UQ", "ORD_S",
"EQ_US", "NGE_UQ", "NGT_UQ", "FALSE_OS", "NEQ_OS", "GE_OQ", "GT_OQ", "TRUE_US",
};
static const char *gEmbeddedRounding[] =
{
"rn", "rd", "ru", "rz",
};
#endif // !BDDISASM_NO_FORMAT
static const uint16_t gOperandMap[] =
{
ND_OPE_D, // ND_OPT_A
ND_OPE_V, // ND_OPT_B
ND_OPE_R, // ND_OPT_C
ND_OPE_R, // ND_OPT_D
ND_OPE_M, // ND_OPT_E
ND_OPE_S, // ND_OPT_F
ND_OPE_R, // ND_OPT_G
ND_OPE_V, // ND_OPT_H
ND_OPE_I, // ND_OPT_I
ND_OPE_D, // ND_OPT_J
ND_OPE_S, // ND_OPT_K
ND_OPE_L, // ND_OPT_L
ND_OPE_M, // ND_OPT_M
ND_OPE_M, // ND_OPT_N
ND_OPE_D, // ND_OPT_O
ND_OPE_R, // ND_OPT_P
ND_OPE_M, // ND_OPT_Q
ND_OPE_M, // ND_OPT_R
ND_OPE_R, // ND_OPT_S
ND_OPE_R, // ND_OPT_T
ND_OPE_M, // ND_OPT_U
ND_OPE_R, // ND_OPT_V
ND_OPE_M, // ND_OPT_W
ND_OPE_S, // ND_OPT_X
ND_OPE_S, // ND_OPT_Y
ND_OPE_O, // ND_OPT_Z
ND_OPE_R, // ND_OPT_rB
ND_OPE_M, // ND_OPT_mB
ND_OPE_R, // ND_OPT_rK
ND_OPE_V, // ND_OPT_vK
ND_OPE_M, // ND_OPT_mK
ND_OPE_A, // ND_OPT_aK
ND_OPE_R, // ND_OPT_mR
ND_OPE_M, // ND_OPT_mM
ND_OPE_R, // ND_OPT_rT
ND_OPE_M, // ND_OPT_mT
ND_OPE_V, // ND_OPT_vT
ND_OPE_1, // ND_OPT_1
ND_OPE_S, // ND_OPT_RIP
ND_OPE_S, // ND_OPT_MXCSR
ND_OPE_S, // ND_OPT_PKRU
ND_OPE_S, // ND_OPT_SSP
ND_OPE_S, // ND_OPT_UIF
ND_OPE_S, // ND_OPT_GPR_AH
ND_OPE_S, // ND_OPT_GPR_rAX
ND_OPE_S, // ND_OPT_GPR_rCX
ND_OPE_S, // ND_OPT_GPR_rDX
ND_OPE_S, // ND_OPT_GPR_rBX
ND_OPE_S, // ND_OPT_GPR_rSP
ND_OPE_S, // ND_OPT_GPR_rBP
ND_OPE_S, // ND_OPT_GPR_rSI
ND_OPE_S, // ND_OPT_GPR_rDI
ND_OPE_S, // ND_OPT_GPR_rR8
ND_OPE_S, // ND_OPT_GPR_rR9
ND_OPE_S, // ND_OPT_GPR_rR11
ND_OPE_S, // ND_OPT_SEG_CS
ND_OPE_S, // ND_OPT_SEG_SS
ND_OPE_S, // ND_OPT_SEG_DS
ND_OPE_S, // ND_OPT_SEG_ES
ND_OPE_S, // ND_OPT_SEG_FS
ND_OPE_S, // ND_OPT_SEG_GS
ND_OPE_S, // ND_OPT_FPU_ST0
ND_OPE_M, // ND_OPT_FPU_STX
ND_OPE_S, // ND_OPT_SSE_XMM0
ND_OPE_S, // ND_OPT_SSE_XMM1
ND_OPE_S, // ND_OPT_SSE_XMM2
ND_OPE_S, // ND_OPT_SSE_XMM3
ND_OPE_S, // ND_OPT_SSE_XMM4
ND_OPE_S, // ND_OPT_SSE_XMM5
ND_OPE_S, // ND_OPT_SSE_XMM6
ND_OPE_S, // ND_OPT_SSE_XMM7
ND_OPE_S, // ND_OPT_MEM_rBX_AL (as used by XLAT)
ND_OPE_S, // ND_OPT_MEM_rDI (as used by masked moves)
ND_OPE_S, // ND_OPT_MEM_SHS
ND_OPE_S, // ND_OPT_MEM_SHSP
ND_OPE_S, // ND_OPT_MEM_SHS0
ND_OPE_L, // ND_OPT_Im2z
ND_OPE_S, // ND_OPT_CR_0
ND_OPE_S, // ND_OPT_IDTR
ND_OPE_S, // ND_OPT_GDTR
ND_OPE_S, // ND_OPT_LDTR
ND_OPE_S, // ND_OPT_TR
ND_OPE_S, // ND_OPT_X87_CONTROL
ND_OPE_S, // ND_OPT_X87_TAG
ND_OPE_S, // ND_OPT_X87_STATUS
ND_OPE_E, // ND_OPT_MSR
ND_OPE_E, // ND_OPT_XCR
ND_OPE_S, // ND_OPT_MSR_TSC
ND_OPE_S, // ND_OPT_MSR_TSCAUX
ND_OPE_S, // ND_OPT_MSR_SEIP
ND_OPE_S, // ND_OPT_MSR_SESP
ND_OPE_S, // ND_OPT_MSR_SCS
ND_OPE_S, // ND_OPT_MSR_STAR
ND_OPE_S, // ND_OPT_MSR_LSTAR
ND_OPE_S, // ND_OPT_MSR_FMASK
ND_OPE_S, // ND_OPT_MSR_FSBASE
ND_OPE_S, // ND_OPT_MSR_GSBASE
ND_OPE_S, // ND_OPT_MSR_KGSBASE
ND_OPE_S, // ND_OPT_XCR_0
ND_OPE_S, // ND_OPT_REG_BANK
ND_OPE_S, // Unused.
};
static const uint8_t gDispsizemap16[4][8] =
{
{ 0, 0, 0, 0, 0, 0, 2, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1 },
{ 2, 2, 2, 2, 2, 2, 2, 2 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
static const uint8_t gDispsizemap[4][8] =
{
{ 0, 0, 0, 0, 0, 4, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1 },
{ 4, 4, 4, 4, 4, 4, 4, 4 },
{ 0, 0, 0, 0, 0, 0, 0, 0 },
};
//
// NdGetVersion
//
void
NdGetVersion(
uint32_t *Major,
uint32_t *Minor,
uint32_t *Revision,
char **BuildDate,
char **BuildTime
)
{
if (NULL != Major)
{
*Major = DISASM_VERSION_MAJOR;
}
if (NULL != Minor)
{
*Minor = DISASM_VERSION_MINOR;
}
if (NULL != Revision)
{
*Revision = DISASM_VERSION_REVISION;
}
//
// Do not use __TIME__ and __DATE__ macros when compiling against a kernel tree.
//
#if defined(__KERNEL__)
if (NULL != BuildDate)
{
*BuildDate = NULL;
}
if (NULL != BuildTime)
{
*BuildTime = NULL;
}
#else
if (NULL != BuildDate)
{
*BuildDate = __DATE__;
}
if (NULL != BuildTime)
{
*BuildTime = __TIME__;
}
#endif
}
//
// NdSprintf
//
#ifndef BDDISASM_NO_FORMAT
static NDSTATUS
NdSprintf(
char *Destination,
size_t DestinationSize,
const char *Formatstring,
...
)
//
// Wrapper on vsnprintf.
//
{
int res;
va_list args;
if (NULL == Destination)
{
return ND_STATUS_INVALID_PARAMETER;
}
if (NULL == Formatstring)
{
return ND_STATUS_INVALID_PARAMETER;
}
nd_memzero(Destination, DestinationSize);
va_start(args, Formatstring);
// _vsnprintf is used instead of the more secure _vsnprintf_s because the mini-Petru supports just
// the unsecured version, and we depend on it.
res = nd_vsnprintf_s(Destination, DestinationSize, DestinationSize - 1, Formatstring, args);
va_end(args);
if ((res < 0) || ((size_t)res >= DestinationSize - 1))
{
return ND_STATUS_BUFFER_OVERFLOW;
}
return ND_STATUS_SUCCESS;
}
#endif // !BDDISASM_NO_FORMAT
//
// NdFetchData
//
static uint64_t
NdFetchData(
const uint8_t *Buffer,
uint8_t Size
)
{
return (4 == Size) ? ND_FETCH_32(Buffer) :
(1 == Size) ? ND_FETCH_8(Buffer) :
(8 == Size) ? ND_FETCH_64(Buffer) :
(2 == Size) ? ND_FETCH_16(Buffer) :
0;
}
//
// NdFetchXop
//
static NDSTATUS
NdFetchXop(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// Offset points to the 0x8F XOP prefix.
// One more byte has to follow, the modrm or the second XOP byte.
RET_GT((size_t)Offset + 2, Size, ND_STATUS_BUFFER_TOO_SMALL);
if (((Code[Offset + 1] & 0x1F) >= 8))
{
// XOP found, make sure the third byte is here.
RET_GT((size_t)Offset + 3, Size, ND_STATUS_BUFFER_TOO_SMALL);
// Make sure we don't have any other prefix.
if (Instrux->HasOpSize || Instrux->HasRepnzXacquireBnd || Instrux->HasRepRepzXrelease || Instrux->HasRex)
{
return ND_STATUS_XOP_WITH_PREFIX;
}
// Fill in XOP info.
Instrux->HasXop = true;
Instrux->EncMode = ND_ENCM_XOP;
Instrux->Xop.Xop[0] = Code[Offset];
Instrux->Xop.Xop[1] = Code[Offset + 1];
Instrux->Xop.Xop[2] = Code[Offset + 2];
Instrux->Exs.w = Instrux->Xop.w;
Instrux->Exs.r = ~Instrux->Xop.r;
Instrux->Exs.x = ~Instrux->Xop.x;
Instrux->Exs.b = ~Instrux->Xop.b;
Instrux->Exs.l = Instrux->Xop.l;
Instrux->Exs.v = ~Instrux->Xop.v;
Instrux->Exs.m = Instrux->Xop.m;
Instrux->Exs.p = Instrux->Xop.p;
// if we are in non 64 bit mode, we must make sure that none of the extended registers are being addressed.
if (Instrux->DefCode != ND_CODE_64)
{
// Xop.R and Xop.X must be 1 (inverted).
if ((Instrux->Exs.r | Instrux->Exs.x) == 1)
{
return ND_STATUS_INVALID_ENCODING_IN_MODE;
}
// Xop.V must be less than 8.
if ((Instrux->Exs.v & 0x8) == 0x8)
{
return ND_STATUS_INVALID_ENCODING_IN_MODE;
}
// Xop.B is ignored, so we force it to 0.
Instrux->Exs.b = 0;
}
// Update Instrux length & offset, and make sure we don't exceed 15 bytes.
Instrux->Length += 3;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchVex2
//
static NDSTATUS
NdFetchVex2(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// One more byte has to follow, the modrm or the second VEX byte.
RET_GT((size_t)Offset + 2, Size, ND_STATUS_BUFFER_TOO_SMALL);
// VEX is available only in 32 & 64 bit mode.
if ((ND_CODE_64 == Instrux->DefCode) || ((Code[Offset + 1] & 0xC0) == 0xC0))
{
// Make sure we don't have any other prefix.
if (Instrux->HasOpSize || Instrux->HasRepnzXacquireBnd ||
Instrux->HasRepRepzXrelease || Instrux->HasRex || Instrux->HasLock)
{
return ND_STATUS_VEX_WITH_PREFIX;
}
// Fill in VEX2 info.
Instrux->VexMode = ND_VEXM_2B;
Instrux->HasVex = true;
Instrux->EncMode = ND_ENCM_VEX;
Instrux->Vex2.Vex[0] = Code[Offset];
Instrux->Vex2.Vex[1] = Code[Offset + 1];
Instrux->Exs.m = 1; // For VEX2 instructions, always use the second table.
Instrux->Exs.r = ~Instrux->Vex2.r;
Instrux->Exs.v = ~Instrux->Vex2.v;
Instrux->Exs.l = Instrux->Vex2.l;
Instrux->Exs.p = Instrux->Vex2.p;
// Update Instrux length & offset, and make sure we don't exceed 15 bytes.
Instrux->Length += 2;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchVex3
//
static NDSTATUS
NdFetchVex3(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// One more byte has to follow, the modrm or the second VEX byte.
RET_GT((size_t)Offset + 2, Size, ND_STATUS_BUFFER_TOO_SMALL);
// VEX is available only in 32 & 64 bit mode.
if ((ND_CODE_64 == Instrux->DefCode) || ((Code[Offset + 1] & 0xC0) == 0xC0))
{
// VEX found, make sure the third byte is here.
RET_GT((size_t)Offset + 3, Size, ND_STATUS_BUFFER_TOO_SMALL);
// Make sure we don't have any other prefix.
if (Instrux->HasOpSize || Instrux->HasRepnzXacquireBnd ||
Instrux->HasRepRepzXrelease || Instrux->HasRex || Instrux->HasLock)
{
return ND_STATUS_VEX_WITH_PREFIX;
}
// Fill in XOP info.
Instrux->VexMode = ND_VEXM_3B;
Instrux->HasVex = true;
Instrux->EncMode = ND_ENCM_VEX;
Instrux->Vex3.Vex[0] = Code[Offset];
Instrux->Vex3.Vex[1] = Code[Offset + 1];
Instrux->Vex3.Vex[2] = Code[Offset + 2];
Instrux->Exs.r = ~Instrux->Vex3.r;
Instrux->Exs.x = ~Instrux->Vex3.x;
Instrux->Exs.b = ~Instrux->Vex3.b;
Instrux->Exs.m = Instrux->Vex3.m;
Instrux->Exs.w = Instrux->Vex3.w;
Instrux->Exs.v = ~Instrux->Vex3.v;
Instrux->Exs.l = Instrux->Vex3.l;
Instrux->Exs.p = Instrux->Vex3.p;
// Do validations in case of VEX outside 64 bits.
if (Instrux->DefCode != ND_CODE_64)
{
// Vex.R and Vex.X have been tested by the initial if.
// Vex.vvvv must be less than 8.
Instrux->Exs.v &= 7;
// Vex.B is ignored, so we force it to 0.
Instrux->Exs.b = 0;
}
// Update Instrux length & offset, and make sure we don't exceed 15 bytes.
Instrux->Length += 3;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchEvex
//
static NDSTATUS
NdFetchEvex(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// One more byte has to follow, the modrm or the second VEX byte.
RET_GT((size_t)Offset + 2, Size, ND_STATUS_BUFFER_TOO_SMALL);
if ((ND_CODE_64 != Instrux->DefCode) && ((Code[Offset + 1] & 0xC0) != 0xC0))
{
// BOUND instruction in non-64 bit mode, not EVEX.
return ND_STATUS_SUCCESS;
}
// EVEX found, make sure all the bytes are present. At least 4 bytes in total must be present.
RET_GT((size_t)Offset + 4, Size, ND_STATUS_BUFFER_TOO_SMALL);
// This is EVEX.
Instrux->HasEvex = true;
Instrux->EncMode = ND_ENCM_EVEX;
Instrux->Evex.Evex[0] = Code[Offset + 0];
Instrux->Evex.Evex[1] = Code[Offset + 1];
Instrux->Evex.Evex[2] = Code[Offset + 2];
Instrux->Evex.Evex[3] = Code[Offset + 3];
// Legacy prefixes are not accepted with EVEX.
if (Instrux->HasOpSize || Instrux->HasRepnzXacquireBnd || Instrux->HasRepRepzXrelease || Instrux->HasRex)
{
return ND_STATUS_EVEX_WITH_PREFIX;
}
// Do the opcode independent checks. Opcode dependent checks are done when decoding each instruction.
if (Instrux->Evex.zero != 0 || Instrux->Evex.one != 1 || Instrux->Evex.m == 0)
{
return ND_STATUS_INVALID_ENCODING;
}
// Fill in the generic extension bits
Instrux->Exs.r = ~Instrux->Evex.r;
Instrux->Exs.x = ~Instrux->Evex.x;
Instrux->Exs.b = ~Instrux->Evex.b;
Instrux->Exs.rp = ~Instrux->Evex.rp;
Instrux->Exs.m = Instrux->Evex.m;
Instrux->Exs.w = Instrux->Evex.w;
Instrux->Exs.v = ~Instrux->Evex.v;
Instrux->Exs.vp = ~Instrux->Evex.vp;
Instrux->Exs.p = Instrux->Evex.p;
Instrux->Exs.z = Instrux->Evex.z;
Instrux->Exs.l = Instrux->Evex.l;
Instrux->Exs.bm = Instrux->Evex.bm;
Instrux->Exs.k = Instrux->Evex.a;
// Do EVEX validations outside 64 bits mode.
if (ND_CODE_64 != Instrux->DefCode)
{
// Evex.R and Evex.X must be 1. If they're not, we have BOUND instruction. This is checked in the
// first if. Note that they are inverted inside the Evex prefix.
Instrux->Exs.r = 0;
Instrux->Exs.x = 0;
// Evex.B is ignored, so we force it to 0.
Instrux->Exs.b = 0;
// Evex.R' is ignored, so we force it to 0.
Instrux->Exs.rp = 0;
// High bit inside Evex.VVVV is ignored, so we force it to 0.
Instrux->Exs.v &= 0x7;
// Evex.V' must be 1 (negated to 0) in 32-bit mode.
if (Instrux->Exs.vp == 1)
{
return ND_STATUS_BAD_EVEX_V_PRIME;
}
}
// Update Instrux length & offset, and make sure we don't exceed 15 bytes.
Instrux->Length += 4;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchPrefixes
//
static NDSTATUS
NdFetchPrefixes(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
NDSTATUS status;
bool morePrefixes;
uint8_t prefix;
morePrefixes = true;
while (morePrefixes)
{
morePrefixes = false;
RET_GT((size_t)Offset + 1, Size, ND_STATUS_BUFFER_TOO_SMALL);
prefix = Code[Offset];
// Speedup: if the current byte is not a prefix of any kind, leave now. This will be the case most of the times.
if (ND_PREF_CODE_NONE == gPrefixesMap[prefix])
{
status = ND_STATUS_SUCCESS;
goto done_prefixes;
}
if (ND_PREF_CODE_STANDARD == gPrefixesMap[prefix])
{
switch (prefix)
{
case ND_PREFIX_G0_LOCK:
Instrux->HasLock = true;
morePrefixes = true;
break;
case ND_PREFIX_G1_REPE_REPZ:
Instrux->Rep = ND_PREFIX_G1_REPE_REPZ;
Instrux->HasRepRepzXrelease = true;
morePrefixes = true;
break;
case ND_PREFIX_G1_REPNE_REPNZ:
Instrux->Rep = ND_PREFIX_G1_REPNE_REPNZ;
Instrux->HasRepnzXacquireBnd = true;
morePrefixes = true;
break;
case ND_PREFIX_G2_SEG_CS:
case ND_PREFIX_G2_SEG_SS:
case ND_PREFIX_G2_SEG_DS:
case ND_PREFIX_G2_SEG_ES:
case ND_PREFIX_G2_SEG_FS:
case ND_PREFIX_G2_SEG_GS:
if (ND_CODE_64 == Instrux->DefCode)
{
// Do not overwrite FS/GS with ES/CS/DS/SS in 64 bit mode. In 64 bit mode, only FS/GS overrides
// are considered.
if (prefix == ND_PREFIX_G2_SEG_FS || prefix == ND_PREFIX_G2_SEG_GS)
{
Instrux->Seg = prefix;
Instrux->HasSeg = true;
}
}
else
{
Instrux->Seg = prefix;
Instrux->HasSeg = true;
}
if (prefix == ND_PREFIX_G2_BR_TAKEN || prefix == ND_PREFIX_G2_BR_NOT_TAKEN)
{
Instrux->Bhint = prefix;
Instrux->HasSeg = true;
}
morePrefixes = true;
break;
case ND_PREFIX_G3_OPERAND_SIZE:
Instrux->HasOpSize = true;
morePrefixes = true;
break;
case ND_PREFIX_G4_ADDR_SIZE:
Instrux->HasAddrSize = true;
morePrefixes = true;
break;
default:
break;
}
}
// REX must precede the opcode byte. However, if one or more other prefixes are present, the instruction
// will still decode & execute properly, but REX will be ignored.
if (morePrefixes && Instrux->HasRex)
{
Instrux->HasRex = false;
Instrux->Rex.Rex = 0;
Instrux->Exs.w = 0;
Instrux->Exs.r = 0;
Instrux->Exs.x = 0;
Instrux->Exs.b = 0;
}
// Check for REX.
if ((ND_CODE_64 == Instrux->DefCode) && (ND_PREF_CODE_REX == gPrefixesMap[prefix]))
{
Instrux->HasRex = true;
Instrux->Rex.Rex = prefix;
Instrux->Exs.w = Instrux->Rex.w;
Instrux->Exs.r = Instrux->Rex.r;
Instrux->Exs.x = Instrux->Rex.x;
Instrux->Exs.b = Instrux->Rex.b;
morePrefixes = true;
}
// We have found prefixes, update the instruction length and the current offset.
if (morePrefixes)
{
Instrux->Length++, Offset++;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
}
}
// We must have at least one more free byte after the prefixes, which will be either the opcode, either
// XOP/VEX/EVEX/MVEX prefix.
RET_GT((size_t)Offset + 1, Size, ND_STATUS_BUFFER_TOO_SMALL);
// Try to match a XOP/VEX/EVEX/MVEX prefix.
if (ND_PREF_CODE_EX == gPrefixesMap[Code[Offset]])
{
// Check for XOP
if (Code[Offset] == ND_PREFIX_XOP)
{
status = NdFetchXop(Instrux, Code, Offset, Size);
if (!ND_SUCCESS(status))
{
return status;
}
}
else if (Code[Offset] == ND_PREFIX_VEX_2B)
{
status = NdFetchVex2(Instrux, Code, Offset, Size);
if (!ND_SUCCESS(status))
{
return status;
}
}
else if (Code[Offset] == ND_PREFIX_VEX_3B)
{
status = NdFetchVex3(Instrux, Code, Offset, Size);
if (!ND_SUCCESS(status))
{
return status;
}
}
else if (Code[Offset] == ND_PREFIX_EVEX)
{
status = NdFetchEvex(Instrux, Code, Offset, Size);
if (!ND_SUCCESS(status))
{
return status;
}
}
else
{
return ND_STATUS_INVALID_INSTRUX;
}
}
else
{
Instrux->EncMode = ND_ENCM_LEGACY;
}
done_prefixes:
// The total length of the instruction is the total length of the prefixes right now.
Instrux->PrefLength = Instrux->OpOffset = Instrux->Length;
return ND_STATUS_SUCCESS;
}
//
// NdFetchOpcode
//
static NDSTATUS
NdFetchOpcode(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// At least one byte must be available, for the fetched opcode.
RET_GT((size_t)Offset + 1, Size, ND_STATUS_BUFFER_TOO_SMALL);
Instrux->OpCodeBytes[Instrux->OpLength++] = Code[Offset];
Instrux->Length++;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchModrm
//
static NDSTATUS
NdFetchModrm(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// At least one byte must be available, for the modrm byte.
RET_GT((size_t)Offset + 1, Size, ND_STATUS_BUFFER_TOO_SMALL);
// If we get called, we assume we have ModRM.
Instrux->HasModRm = true;
// Fetch the ModRM byte & update the offset and the instruction length.
Instrux->ModRm.ModRm = Code[Offset];
Instrux->ModRmOffset = Offset;
Instrux->Length++, Offset++;
// Make sure we don't exceed the maximum instruction length.
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchModrmAndSib
//
static NDSTATUS
NdFetchModrmAndSib(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
{
// At least one byte must be available, for the modrm byte.
RET_GT((size_t)Offset + 1, Size, ND_STATUS_BUFFER_TOO_SMALL);
// If we get called, we assume we have ModRM.
Instrux->HasModRm = true;
// Fetch the ModRM byte & update the offset and the instruction length.
Instrux->ModRm.ModRm = Code[Offset];
Instrux->ModRmOffset = Offset;
Instrux->Length++, Offset++;
// Make sure we don't exceed the maximum instruction length.
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
// If needed, fetch the SIB.
if ((Instrux->ModRm.rm == NDR_RSP) && (Instrux->ModRm.mod != 3) && (Instrux->AddrMode != ND_ADDR_16))
{
// At least one more byte must be available, for the sib.
RET_GT((size_t)Offset + 1, Size, ND_STATUS_BUFFER_TOO_SMALL);
// SIB present.
Instrux->HasSib = true;
Instrux->Sib.Sib = Code[Offset];
Instrux->Length++;
// Make sure we don't exceed the maximum instruction length.
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchDisplacement
//
static NDSTATUS
NdFetchDisplacement(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size
)
//
// Will decode the displacement from the instruction. Will fill in extracted information in Instrux,
// and will update the instruction length.
//
{
uint8_t displSize;
displSize = 0;
if (ND_ADDR_16 == Instrux->AddrMode)
{
displSize = gDispsizemap16[Instrux->ModRm.mod][Instrux->ModRm.rm];
}
else
{
displSize = gDispsizemap[Instrux->ModRm.mod][Instrux->HasSib ? Instrux->Sib.base : Instrux->ModRm.rm];
}
if (0 != displSize)
{
static const uint32_t signMask[4] = { 0x80, 0x8000, 0, 0x80000000 };
// Make sure enough buffer space is available.
RET_GT((size_t)Offset + displSize, Size, ND_STATUS_BUFFER_TOO_SMALL);
// If we get here, we have displacement.
Instrux->HasDisp = true;
Instrux->Displacement = (uint32_t)NdFetchData(Code + Offset, displSize);
Instrux->SignDisp = (Instrux->Displacement & signMask[displSize - 1]) ? true : false;
// Fill in displacement info.
Instrux->DispLength = displSize;
Instrux->DispOffset = Offset;
Instrux->Length += displSize;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchAddress
//
static NDSTATUS
NdFetchAddress(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint8_t AddressSize
)
{
//. Make sure the
RET_GT((size_t)Offset + AddressSize, Size, ND_STATUS_BUFFER_TOO_SMALL);
Instrux->HasAddr = true;
Instrux->AddrLength = AddressSize;
Instrux->AddrOffset = Offset;
Instrux->Address.Ip = (uint32_t)NdFetchData(Code + Offset, Instrux->AddrLength - 2);
Instrux->Address.Cs = (uint16_t)NdFetchData(Code + Offset + Instrux->AddrLength - 2, 2);
Instrux->Length += Instrux->AddrLength;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchImmediate
//
static NDSTATUS
NdFetchImmediate(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint8_t ImmediateSize
)
{
uint64_t imm;
RET_GT((size_t)Offset + ImmediateSize, Size, ND_STATUS_BUFFER_TOO_SMALL);
imm = NdFetchData(Code + Offset, ImmediateSize);
if (Instrux->HasImm2)
{
Instrux->HasImm3 = true;
Instrux->Imm3Length = ImmediateSize;
Instrux->Imm3Offset = Offset;
Instrux->Immediate3 = (uint8_t)imm;
}
else if (Instrux->HasImm1)
{
Instrux->HasImm2 = true;
Instrux->Imm2Length = ImmediateSize;
Instrux->Imm2Offset = Offset;
Instrux->Immediate2 = (uint8_t)imm;
}
else
{
Instrux->HasImm1 = true;
Instrux->Imm1Length = ImmediateSize;
Instrux->Imm1Offset = Offset;
Instrux->Immediate1 = imm;
}
Instrux->Length += ImmediateSize;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchRelativeOffset
//
static NDSTATUS
NdFetchRelativeOffset(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint8_t RelOffsetSize
)
{
// Make sure we don't outrun the buffer.
RET_GT((size_t)Offset + RelOffsetSize, Size, ND_STATUS_BUFFER_TOO_SMALL);
Instrux->HasRelOffs = true;
Instrux->RelOffsLength = RelOffsetSize;
Instrux->RelOffsOffset = Offset;
Instrux->RelativeOffset = (uint32_t)NdFetchData(Code + Offset, RelOffsetSize);
Instrux->Length += RelOffsetSize;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchMoffset
//
static NDSTATUS
NdFetchMoffset(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint8_t MoffsetSize
)
{
RET_GT((size_t)Offset + MoffsetSize, Size, ND_STATUS_BUFFER_TOO_SMALL);
Instrux->HasMoffset = true;
Instrux->MoffsetLength = MoffsetSize;
Instrux->MoffsetOffset = Offset;
Instrux->Moffset = NdFetchData(Code + Offset, MoffsetSize);
Instrux->Length += MoffsetSize;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdFetchSseImmediate
//
static NDSTATUS
NdFetchSseImmediate(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint8_t SseImmSize
)
{
RET_GT((size_t)Offset + SseImmSize, Size, ND_STATUS_BUFFER_TOO_SMALL);
Instrux->HasSseImm = true;
Instrux->SseImmOffset = Offset;
Instrux->SseImmediate = *(Code + Offset);
Instrux->Length += SseImmSize;
if (Instrux->Length > ND_MAX_INSTRUCTION_LENGTH)
{
return ND_STATUS_INSTRUCTION_TOO_LONG;
}
return ND_STATUS_SUCCESS;
}
//
// NdGetSegOverride
//
static uint8_t
NdGetSegOverride(
INSTRUX *Instrux,
uint8_t DefaultSeg
)
{
// In 64 bit mode, the segment override is ignored, except for FS and GS.
if ((Instrux->DefCode == ND_CODE_64) &&
(Instrux->Seg != ND_PREFIX_G2_SEG_FS) &&
(Instrux->Seg != ND_PREFIX_G2_SEG_GS))
{
return DefaultSeg;
}
switch (Instrux->Seg)
{
case ND_PREFIX_G2_SEG_CS:
return NDR_CS;
case ND_PREFIX_G2_SEG_DS:
return NDR_DS;
case ND_PREFIX_G2_SEG_ES:
return NDR_ES;
case ND_PREFIX_G2_SEG_SS:
return NDR_SS;
case ND_PREFIX_G2_SEG_FS:
return NDR_FS;
case ND_PREFIX_G2_SEG_GS:
return NDR_GS;
default:
return DefaultSeg;
}
}
//
// NdGetCompDispSize
//
static uint8_t
NdGetCompDispSize(
const INSTRUX *Instrux,
uint32_t MemSize
)
{
static const uint8_t fvszLut[4] = { 16, 32, 64, 0 };
static const uint8_t hvszLut[4] = { 8, 16, 32, 0 };
static const uint8_t qvszLut[4] = { 4, 8, 16, 0 };
static const uint8_t dupszLut[4] = { 8, 32, 64, 0 };
static const uint8_t fvmszLut[4] = { 16, 32, 64, 0 };
static const uint8_t hvmszLut[4] = { 8, 16, 32, 0 };
static const uint8_t qvmszLut[4] = { 4, 8, 16, 0 };
static const uint8_t ovmszLut[4] = { 2, 4, 8, 0 };
if (Instrux->HasBroadcast)
{
// If the instruction uses broadcast, then compressed displacement will use the size of the element as scale:
// - 2 when broadcasting 16 bit
// - 4 when broadcasting 32 bit
// - 8 when broadcasting 64 bit
return (uint8_t)MemSize;
}
switch (Instrux->TupleType)
{
case ND_TUPLE_FV:
return fvszLut[Instrux->Exs.l];
case ND_TUPLE_HV:
return hvszLut[Instrux->Exs.l];
case ND_TUPLE_QV:
return qvszLut[Instrux->Exs.l];
case ND_TUPLE_DUP:
return dupszLut[Instrux->Exs.l];
case ND_TUPLE_FVM:
return fvmszLut[Instrux->Exs.l];
case ND_TUPLE_HVM:
return hvmszLut[Instrux->Exs.l];
case ND_TUPLE_QVM:
return qvmszLut[Instrux->Exs.l];
case ND_TUPLE_OVM:
return ovmszLut[Instrux->Exs.l];
case ND_TUPLE_M128:
return 16;
case ND_TUPLE_T1S8:
return 1;
case ND_TUPLE_T1S16:
return 2;
case ND_TUPLE_T1S:
return !!(Instrux->Attributes & ND_FLAG_WIG) ? 4 : Instrux->Exs.w ? 8 : 4;
case ND_TUPLE_T1F:
return (uint8_t)MemSize;
case ND_TUPLE_T2:
return Instrux->Exs.w ? 16 : 8;
case ND_TUPLE_T4:
return Instrux->Exs.w ? 32 : 16;
case ND_TUPLE_T8:
return 32;
case ND_TUPLE_T1_4X:
return 16;
default:
// Default - we assume byte granularity for memory accesses, therefore, no scaling will be done.
return 1;
}
}
//
// NdParseOperand
//
static NDSTATUS
NdParseOperand(
INSTRUX *Instrux,
const uint8_t *Code,
uint8_t Offset,
size_t Size,
uint32_t Index,
uint64_t Specifier
)
{
NDSTATUS status;
PND_OPERAND operand;
uint8_t opt, ops, opf, opa, opd, opb;
ND_REG_SIZE vsibRegSize;
uint8_t vsibIndexSize, vsibIndexCount;
ND_OPERAND_SIZE size, bcstSize;
bool width;
// pre-init
status = ND_STATUS_SUCCESS;
vsibRegSize = 0;
vsibIndexSize = vsibIndexCount = 0;
size = bcstSize = 0;
// Get actual width.
width = Instrux->Exs.w && !(Instrux->Attributes & ND_FLAG_WIG);
// Get operand components.
opt = ND_OP_TYPE(Specifier);
ops = ND_OP_SIZE(Specifier);
opf = ND_OP_FLAGS(Specifier);
opa = ND_OP_ACCESS(Specifier);
opd = ND_OP_DECORATORS(Specifier);
opb = ND_OP_BLOCK(Specifier);
// Get a pointer to our op.
operand = &Instrux->Operands[Index];
// Fill in the flags.
operand->Flags.Flags = opf;
// Store operand access modes.
operand->Access.Access = opa;
//
// Fill in operand size.
//
switch (ops)
{
case ND_OPS_asz:
// Size given by the address mode.
size = 2 << Instrux->AddrMode;
break;
case ND_OPS_ssz:
// Size given by the stack mode.
size = 2 << Instrux->DefStack;
break;
case ND_OPS_0:
// No memory access. 0 operand size.
size = 0;
break;
case ND_OPS_b:
// Byte, regardless of operand-size attribute.
size = ND_SIZE_8BIT;
break;
case ND_OPS_w:
// Word, regardless of operand-size attribute.
size = ND_SIZE_16BIT;
break;
case ND_OPS_d:
// Dword, regardless of operand-size attribute.
size = ND_SIZE_32BIT;
break;
case ND_OPS_q:
// Qword, regardless of operand-size attribute.
size = ND_SIZE_64BIT;
break;
case ND_OPS_dq:
// Double-Qword, regardless of operand-size attribute.
size = ND_SIZE_128BIT;
break;
case ND_OPS_qq:
// Quad-Quadword (256-bits), regardless of operand-size attribute.
size = ND_SIZE_256BIT;
break;
case ND_OPS_oq:
// Octo-Quadword (512-bits), regardless of operand-size attribute.
size = ND_SIZE_512BIT;
break;
case ND_OPS_fa:
// 80 bits packed BCD.
size = ND_SIZE_80BIT;
break;
case ND_OPS_fw:
// 16 bits real number.
size = ND_SIZE_16BIT;
break;
case ND_OPS_fd:
// 32 bit real number.
size = ND_SIZE_32BIT;
break;
case ND_OPS_fq:
// 64 bit real number.
size = ND_SIZE_64BIT;
break;
case ND_OPS_ft:
// 80 bit real number.
size = ND_SIZE_80BIT;
break;
case ND_OPS_fe:
// 14 bytes or 28 bytes FPU environment.
size = (Instrux->EfOpMode == ND_OPSZ_16) ? ND_SIZE_112BIT : ND_SIZE_224BIT;
break;
case ND_OPS_fs:
// 94 bytes or 108 bytes FPU state.
size = (Instrux->EfOpMode == ND_OPSZ_16) ? ND_SIZE_752BIT : ND_SIZE_864BIT;
break;
case ND_OPS_rx:
// 512 bytes extended state.
size = ND_SIZE_4096BIT;
break;
case ND_OPS_cl:
// The size of one cache line.
size = ND_SIZE_CACHE_LINE;
break;
case ND_OPS_v:
// Word, doubleword or quadword (in 64-bit mode), depending on operand-size attribute.
{
static const uint8_t szLut[3] = { ND_SIZE_16BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
size = szLut[Instrux->EfOpMode];
}
break;
case ND_OPS_y:
// Doubleword or quadword (in 64-bit mode), depending on operand-size attribute.
{
static const uint8_t szLut[3] = { ND_SIZE_32BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
size = szLut[Instrux->EfOpMode];
}
break;
case ND_OPS_yf:
// Always uint64_t in 64 bit mode and uint32_t in 16/32 bit mode.
{
static const uint8_t szLut[3] = { ND_SIZE_32BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
size = szLut[Instrux->DefCode];
}
break;
case ND_OPS_z:
// Word for 16-bit operand-size or double word for 32 or 64-bit operand-size.
{
static const uint8_t szLut[3] = { ND_SIZE_16BIT, ND_SIZE_32BIT, ND_SIZE_32BIT };
size = szLut[Instrux->EfOpMode];
}
break;
case ND_OPS_a:
// Two one-word operands in memory or two double-word operands in memory,
// depending on operand-size attribute (used only by the BOUND instruction).
{
static const uint8_t szLut[3] = { ND_SIZE_16BIT * 2, ND_SIZE_32BIT * 2, 0 };
if (Instrux->DefCode > ND_CODE_32)
{
return ND_STATUS_INVALID_INSTRUX;
}
size = szLut[Instrux->EfOpMode];
}
break;
case ND_OPS_c:
// Byte or word, depending on operand-size attribute.
switch (Instrux->DefCode)
{
case ND_CODE_16:
size = Instrux->HasOpSize ? ND_SIZE_16BIT : ND_SIZE_8BIT;
break;
case ND_CODE_32:
size = Instrux->HasOpSize ? ND_SIZE_16BIT : ND_SIZE_32BIT;
break;
case ND_CODE_64:
size = ND_SIZE_64BIT;
break;
default:
return ND_STATUS_INVALID_INSTRUX;
}
break;
case ND_OPS_p:
// 32-bit, 48-bit, or 80-bit pointer, depending on operand-size attribute.
{
static const uint8_t szLut[3] = { ND_SIZE_32BIT, ND_SIZE_48BIT, ND_SIZE_80BIT };
size = szLut[Instrux->EfOpMode];
}
break;
case ND_OPS_s:
// 6-byte or 10-byte pseudo-descriptor.
{
static const uint8_t szLut[3] = { ND_SIZE_48BIT, ND_SIZE_48BIT, ND_SIZE_80BIT };
size = szLut[Instrux->DefCode];
}
break;
case ND_OPS_l:
// 64 bit in 16 or 32 bit mode, 128 bit in long mode. Used by BNDMOV instruction.
{
static const uint8_t szLut[3] = { ND_SIZE_64BIT, ND_SIZE_64BIT, ND_SIZE_128BIT };
size = szLut[Instrux->DefCode];
}
break;
case ND_OPS_x:
// dq, qq or oq based on the operand-size attribute.
{
static const uint8_t szLut[3] = { ND_SIZE_128BIT, ND_SIZE_256BIT, ND_SIZE_512BIT };
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_n:
// 128, 256 or 512 bit, depending on vector length.
{
static const uint8_t szLut[3] = { ND_SIZE_128BIT, ND_SIZE_256BIT, ND_SIZE_512BIT };
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_u:
// 256 or 512 bit, depending on vector length.
{
static const uint8_t szLut[3] = { 0, ND_SIZE_256BIT, ND_SIZE_512BIT };
if (ND_VECM_128 == Instrux->EfVecMode)
{
return ND_STATUS_INVALID_INSTRUX;
}
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_e:
// eighth = word or dword or qword
{
static const uint8_t szLut[3] = { ND_SIZE_16BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_f:
// fourth = dword or qword or oword
{
static const uint8_t szLut[3] = { ND_SIZE_32BIT, ND_SIZE_64BIT, ND_SIZE_128BIT };
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_h:
// half = qword or oword or yword
{
static const uint8_t szLut[3] = { ND_SIZE_64BIT, ND_SIZE_128BIT, ND_SIZE_256BIT };
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_pd:
case ND_OPS_ps:
case ND_OPS_ph:
// packed double or packed single or packed FP16 values.
{
static const uint8_t szLut[3] = { ND_SIZE_128BIT, ND_SIZE_256BIT, ND_SIZE_512BIT };
size = szLut[Instrux->EfVecMode];
}
break;
case ND_OPS_sd:
// Scalar double.
size = ND_SIZE_64BIT;
break;
case ND_OPS_ss:
// Scalar single.
size = ND_SIZE_32BIT;
break;
case ND_OPS_sh:
// Scalar FP16.
size = ND_SIZE_16BIT;
break;
case ND_OPS_mib:
// MIB addressing, used by MPX instructions.
size = 0;
break;
case ND_OPS_vm32x:
case ND_OPS_vm32y:
case ND_OPS_vm32z:
// 32 bit indexes from XMM, YMM or ZMM register.
vsibIndexSize = ND_SIZE_32BIT;
vsibIndexCount = (Instrux->Exs.l == 0) ? 4 : ((Instrux->Exs.l == 1) ? 8 : 16);
vsibRegSize = (ops == ND_OPS_vm32x) ? ND_SIZE_128BIT :
(ops == ND_OPS_vm32y) ? ND_SIZE_256BIT :
ND_SIZE_512BIT;
size = vsibIndexCount * (width ? ND_SIZE_64BIT : ND_SIZE_32BIT);
break;
case ND_OPS_vm32h:
// 32 bit indexes from XMM or YMM.
vsibIndexSize = ND_SIZE_32BIT;
vsibIndexCount = (Instrux->Exs.l == 0) ? 2 : ((Instrux->Exs.l == 1) ? 4 : 8);
vsibRegSize = (Instrux->Exs.l == 0) ? ND_SIZE_128BIT :
(Instrux->Exs.l == 1) ? ND_SIZE_128BIT :
ND_SIZE_256BIT;
size = vsibIndexCount * (width ? ND_SIZE_64BIT : ND_SIZE_32BIT);
break;
case ND_OPS_vm32n:
// 32 bit indexes from XMM, YMM or ZMM register.
vsibIndexSize = ND_SIZE_32BIT;
vsibIndexCount = (Instrux->Exs.l == 0) ? 4 : ((Instrux->Exs.l == 1) ? 8 : 16);
vsibRegSize = (Instrux->Exs.l == 0) ? ND_SIZE_128BIT :
(Instrux->Exs.l == 1) ? ND_SIZE_256BIT :
ND_SIZE_512BIT;
size = vsibIndexCount * (width ? ND_SIZE_64BIT : ND_SIZE_32BIT);
break;
case ND_OPS_vm64x:
case ND_OPS_vm64y:
case ND_OPS_vm64z:
// 64 bit indexes from XMM, YMM or ZMM register.
vsibIndexSize = ND_SIZE_64BIT;
vsibIndexCount = (Instrux->Exs.l == 0) ? 2 : ((Instrux->Exs.l == 1) ? 4 : 8);
vsibRegSize = (ops == ND_OPS_vm64x) ? ND_SIZE_128BIT :
(ops == ND_OPS_vm64y) ? ND_SIZE_256BIT :
ND_SIZE_512BIT;
size = vsibIndexCount * (width ? ND_SIZE_64BIT : ND_SIZE_32BIT);
break;
case ND_OPS_vm64h:
// 64 bit indexes from XMM or YMM.
vsibIndexSize = ND_SIZE_64BIT;
vsibIndexCount = (Instrux->Exs.l == 0) ? 1 : ((Instrux->Exs.l == 1) ? 2 : 4);
vsibRegSize = (Instrux->Exs.l == 0) ? ND_SIZE_128BIT :
(Instrux->Exs.l == 1) ? ND_SIZE_128BIT :
ND_SIZE_256BIT;
size = vsibIndexCount * (width ? ND_SIZE_64BIT : ND_SIZE_32BIT);
break;
case ND_OPS_vm64n:
// 64 bit indexes from XMM, YMM or ZMM register.
vsibIndexSize = ND_SIZE_64BIT;
vsibIndexCount = (Instrux->Exs.l == 0) ? 2 : ((Instrux->Exs.l == 1) ? 4 : 8);
vsibRegSize = (Instrux->Exs.l == 0) ? ND_SIZE_128BIT :
(Instrux->Exs.l == 1) ? ND_SIZE_256BIT :
ND_SIZE_512BIT;
size = vsibIndexCount * (width ? ND_SIZE_64BIT : ND_SIZE_32BIT);
break;
case ND_OPS_v2:
case ND_OPS_v3:
case ND_OPS_v4:
case ND_OPS_v5:
case ND_OPS_v8:
// Multiple words accessed.
{
static const uint8_t szLut[3] = { ND_SIZE_16BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
uint8_t scale = 1;
scale = (ops == ND_OPS_v2) ? 2 :
(ops == ND_OPS_v3) ? 3 :
(ops == ND_OPS_v4) ? 4 :
(ops == ND_OPS_v5) ? 5 : 8;
size = scale * szLut[Instrux->EfOpMode];
}
break;
case ND_OPS_12:
// SAVPREVSSP instruction reads/writes 4 + 8 bytes from the shadow stack.
size = 12;
break;
case ND_OPS_t:
// Tile register. The actual size depends on how the TILECFG register has been programmed, but it can be
// up to 1K in size.
size = ND_SIZE_1KB;
break;
case ND_OPS_384:
// 384 bit Key Locker handle.
size = ND_SIZE_384BIT;
break;
case ND_OPS_512:
// 512 bit Key Locker handle.
size = ND_SIZE_512BIT;
break;
case ND_OPS_unknown:
size = ND_SIZE_UNKNOWN;
break;
default:
return ND_STATUS_INVALID_INSTRUX;
}
// Store operand info.
operand->Size = operand->RawSize = bcstSize = size;
//
// Fill in the operand type.
//
switch (opt)
{
case ND_OPT_CONST_1:
// operand is an implicit constant (used by shift/rotate instruction).
operand->Type = ND_OP_CONST;
operand->Info.Constant.Const = 1;
break;
case ND_OPT_RIP:
// The operand is the instruction pointer.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_RIP;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = 0;
Instrux->RipAccess |= operand->Access.Access;
break;
case ND_OPT_GPR_rAX:
// Operator is the accumulator.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RAX;
break;
case ND_OPT_GPR_AH:
// Operator is the accumulator.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = ND_SIZE_8BIT;
operand->Info.Register.Reg = NDR_AH;
operand->Info.Register.IsHigh8 = true;
break;
case ND_OPT_GPR_rCX:
// Operator is the counter register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RCX;
break;
case ND_OPT_GPR_rDX:
// Operator is rDX.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RDX;
break;
case ND_OPT_GPR_rBX:
// Operator is BX.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RBX;
break;
case ND_OPT_GPR_rBP:
// Operand is rBP.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RBP;
break;
case ND_OPT_GPR_rSP:
// Operand is rSP.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RSP;
break;
case ND_OPT_GPR_rSI:
// Operand is rSI.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RSI;
break;
case ND_OPT_GPR_rDI:
// Operand is rDI.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_RDI;
break;
case ND_OPT_GPR_rR8:
// Operand is R8.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_R8;
break;
case ND_OPT_GPR_rR9:
// Operand is R9.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_R9;
break;
case ND_OPT_GPR_rR11:
// Operand is R11.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_R11;
break;
case ND_OPT_SEG_CS:
// Operand is the CS register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_CS;
break;
case ND_OPT_SEG_SS:
// Operand is the SS register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_SS;
break;
case ND_OPT_SEG_DS:
// Operand is the DS register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_DS;
break;
case ND_OPT_SEG_ES:
// Operand is the ES register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_ES;
break;
case ND_OPT_SEG_FS:
// Operand is the FS register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_FS;
break;
case ND_OPT_SEG_GS:
// Operand is the GS register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_GS;
break;
case ND_OPT_FPU_ST0:
// Operand is the ST(0) register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_FPU;
operand->Info.Register.Size = ND_SIZE_80BIT;
operand->Info.Register.Reg = 0;
break;
case ND_OPT_FPU_STX:
// Operand is the ST(i) register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_FPU;
operand->Info.Register.Size = ND_SIZE_80BIT;
operand->Info.Register.Reg = Instrux->ModRm.rm;
break;
case ND_OPT_SSE_XMM0:
case ND_OPT_SSE_XMM1:
case ND_OPT_SSE_XMM2:
case ND_OPT_SSE_XMM3:
case ND_OPT_SSE_XMM4:
case ND_OPT_SSE_XMM5:
case ND_OPT_SSE_XMM6:
case ND_OPT_SSE_XMM7:
// Operand is a hard-coded XMM register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SSE;
operand->Info.Register.Size = ND_SIZE_128BIT;
operand->Info.Register.Reg = opt - ND_OPT_SSE_XMM0;
break;
// Special operands. These are always implicit, and can't be encoded inside the instruction.
case ND_OPT_CR_0:
// The operand is implicit and is control register 0.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_CR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_CR0;
break;
case ND_OPT_SYS_GDTR:
// The operand is implicit and is the global descriptor table register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_GDTR;
break;
case ND_OPT_SYS_IDTR:
// The operand is implicit and is the interrupt descriptor table register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_IDTR;
break;
case ND_OPT_SYS_LDTR:
// The operand is implicit and is the local descriptor table register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_LDTR;
break;
case ND_OPT_SYS_TR:
// The operand is implicit and is the task register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = NDR_TR;
break;
case ND_OPT_X87_CONTROL:
// The operand is implicit and is the x87 control word.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = ND_SIZE_16BIT;
operand->Info.Register.Reg = NDR_X87_CONTROL;
break;
case ND_OPT_X87_TAG:
// The operand is implicit and is the x87 tag word.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = ND_SIZE_16BIT;
operand->Info.Register.Reg = NDR_X87_TAG;
break;
case ND_OPT_X87_STATUS:
// The operand is implicit and is the x87 status word.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SYS;
operand->Info.Register.Size = ND_SIZE_16BIT;
operand->Info.Register.Reg = NDR_X87_STATUS;
break;
case ND_OPT_MXCSR:
// The operand is implicit and is the MXCSR.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MXCSR;
operand->Info.Register.Size = ND_SIZE_32BIT;
operand->Info.Register.Reg = 0;
break;
case ND_OPT_PKRU:
// The operand is the PKRU register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_PKRU;
operand->Info.Register.Size = ND_SIZE_32BIT;
operand->Info.Register.Reg = 0;
break;
case ND_OPT_SSP:
// The operand is the SSP register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SSP;
operand->Info.Register.Size = operand->Size;
operand->Info.Register.Reg = 0;
break;
case ND_OPT_UIF:
// The operand is the User Interrupt Flag.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_UIF;
operand->Info.Register.Size = ND_SIZE_8BIT; // 1 bit, in fact, but there is no size defined for one bit.
operand->Info.Register.Reg = 0;
break;
case ND_OPT_MSR:
// The operand is implicit and is a MSR (usually selected by the ECX register).
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = 0xFFFFFFFF;
break;
case ND_OPT_MSR_TSC:
// The operand is implicit and is the IA32_TSC.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_TSC;
break;
case ND_OPT_MSR_TSCAUX:
// The operand is implicit and is the IA32_TSCAUX.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_TSC_AUX;
break;
case ND_OPT_MSR_SCS:
// The operand is implicit and is the IA32_SYSENTER_CS.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_SYSENTER_CS;
break;
case ND_OPT_MSR_SESP:
// The operand is implicit and is the IA32_SYSENTER_ESP.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_SYSENTER_ESP;
break;
case ND_OPT_MSR_SEIP:
// The operand is implicit and is the IA32_SYSENTER_EIP.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_SYSENTER_EIP;
break;
case ND_OPT_MSR_STAR:
// The operand is implicit and is the IA32_STAR.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_STAR;
break;
case ND_OPT_MSR_LSTAR:
// The operand is implicit and is the IA32_STAR.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_LSTAR;
break;
case ND_OPT_MSR_FMASK:
// The operand is implicit and is the IA32_FMASK.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_FMASK;
break;
case ND_OPT_MSR_FSBASE:
// The operand is implicit and is the IA32_FS_BASE MSR.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_FS_BASE;
break;
case ND_OPT_MSR_GSBASE:
// The operand is implicit and is the IA32_GS_BASE MSR.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_GS_BASE;
break;
case ND_OPT_MSR_KGSBASE:
// The operand is implicit and is the IA32_KERNEL_GS_BASE MSR.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_MSR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = NDR_IA32_KERNEL_GS_BASE;
break;
case ND_OPT_XCR:
// The operand is implicit and is an extended control register (usually selected by ECX register).
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_XCR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = 0xFF;
break;
case ND_OPT_XCR_0:
// The operand is implicit and is XCR0.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_XCR;
operand->Info.Register.Size = ND_SIZE_64BIT;
operand->Info.Register.Reg = 0;
break;
case ND_OPT_REG_BANK:
// Multiple registers are accessed.
if ((Instrux->Instruction == ND_INS_PUSHA) || (Instrux->Instruction == ND_INS_POPA))
{
operand->Type = ND_OP_REG;
operand->Size = operand->RawSize = Instrux->WordLength;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = Instrux->WordLength;
operand->Info.Register.Reg = NDR_EAX;
operand->Info.Register.Count = 8;
operand->Info.Register.IsBlock = true;
}
else
{
operand->Type = ND_OP_BANK;
}
break;
case ND_OPT_A:
// Fetch the address. NOTE: The size can't be larger than 8 bytes.
status = NdFetchAddress(Instrux, Code, Offset, Size, (uint8_t)size);
if (!ND_SUCCESS(status))
{
return status;
}
// Fill in operand info.
operand->Type = ND_OP_ADDR;
operand->Info.Address.BaseSeg = Instrux->Address.Cs;
operand->Info.Address.Offset = Instrux->Address.Ip;
break;
case ND_OPT_B:
// General purpose register encoded in VEX.vvvv field.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
// EVEX.V' must be 0, if a GPR is encoded using EVEX encoding.
if (Instrux->Exs.vp != 0)
{
return ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION;
}
operand->Info.Register.Reg = (uint8_t)Instrux->Exs.v;
break;
case ND_OPT_C:
// Control register, encoded in modrm.reg.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_CR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = (uint8_t)((Instrux->Exs.r << 3) | Instrux->ModRm.reg);
// On some AMD processors, the presence of the LOCK prefix before MOV to/from control registers allows accessing
// higher 8 control registers.
if ((ND_CODE_64 != Instrux->DefCode) && (Instrux->HasLock))
{
operand->Info.Register.Reg |= 0x8;
}
// Only CR0, CR2, CR3, CR4 & CR8 valid.
if (operand->Info.Register.Reg != 0 &&
operand->Info.Register.Reg != 2 &&
operand->Info.Register.Reg != 3 &&
operand->Info.Register.Reg != 4 &&
operand->Info.Register.Reg != 8)
{
return ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION;
}
break;
case ND_OPT_D:
// Debug register, encoded in modrm.reg.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_DR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = (uint8_t)((Instrux->Exs.r << 3) | Instrux->ModRm.reg);
// Only DR0-DR7 valid.
if (operand->Info.Register.Reg >= 8)
{
return ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION;
}
break;
case ND_OPT_T:
// Test register, encoded in modrm.reg.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_TR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = (uint8_t)((Instrux->Exs.r << 3) | Instrux->ModRm.reg);
// Only TR0-TR7 valid, only on 486.
if (operand->Info.Register.Reg >= 8)
{
return ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION;
}
break;
case ND_OPT_S:
// Segment register, encoded in modrm.reg.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_SEG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = Instrux->ModRm.reg;
// Only ES, CS, SS, DS, FS, GS valid.
if (operand->Info.Register.Reg >= 6)
{
return ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION;
}
// If CS is loaded - #UD.
if ((operand->Info.Register.Reg == NDR_CS) && operand->Access.Write)
{
return ND_STATUS_CS_LOAD;
}
break;
case ND_OPT_E:
// General purpose register or memory, encoded in modrm.rm.
if (Instrux->ModRm.mod == 3)
{
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = (uint8_t)((Instrux->Exs.b << 3) | Instrux->ModRm.rm);
operand->Info.Register.IsHigh8 = (operand->Info.Register.Size == 1) &&
(operand->Info.Register.Reg >= 4) &&
(ND_ENCM_LEGACY == Instrux->EncMode) &&
!Instrux->HasRex;
}
else
{
goto memory;
}
break;
case ND_OPT_F:
// The flags register.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_FLG;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = 0;
Instrux->FlagsAccess.RegAccess |= operand->Access.Access;
break;
case ND_OPT_K:
// The operand is the stack.
{
static const uint8_t szLut[3] = { ND_SIZE_16BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
Instrux->MemoryAccess |= operand->Access.Access;
operand->Type = ND_OP_MEM;
operand->Info.Memory.IsStack = true;
operand->Info.Memory.HasBase = true;
operand->Info.Memory.Base = NDR_RSP;
operand->Info.Memory.BaseSize = szLut[Instrux->DefStack];
operand->Info.Memory.HasSeg = true;
operand->Info.Memory.Seg = NDR_SS;
Instrux->StackWords = (uint8_t)(operand->Size / Instrux->WordLength);
Instrux->StackAccess |= operand->Access.Access;
}
break;
case ND_OPT_G:
// General purpose register encoded in modrm.reg.
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
// EVEX.R' must be 0 if a GPR is encoded.
if (Instrux->Exs.rp != 0)
{
return ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION;
}
operand->Info.Register.Reg = (uint8_t)((Instrux->Exs.r << 3) | Instrux->ModRm.reg);
operand->Info.Register.IsHigh8 = (operand->Info.Register.Size == 1) &&
(operand->Info.Register.Reg >= 4) &&
(ND_ENCM_LEGACY == Instrux->EncMode) &&
!Instrux->HasRex;
break;
case ND_OPT_R:
// General purpose register encoded in modrm.rm.
if ((Instrux->ModRm.mod == 3) || (0 != (Instrux->Attributes & ND_FLAG_MFR)))
{
operand->Type = ND_OP_REG;
operand->Info.Register.Type = ND_REG_GPR;
operand->Info.Register.Size = (ND_REG_SIZE)size;
operand->Info.Register.Reg = (uint8_t)((Instrux->Exs.b << 3) | Instrux->ModRm.rm);
operand->Info.Register.IsHigh8 = (operand->Info.Register.Size == 1) &&
(operand->Info.Register.Reg >= 4) &&
(ND_ENCM_LEGACY == Instrux->EncMode) &&
!Instrux->HasRex;
}
else
{
return ND_STATUS_INVALID_ENCODING;
}
break;
case ND_OPT_I:
// Immediate, encoded in instructon bytes.
{
uint64_t imm;
// Fetch the immediate. NOTE: The size won't exceed 8 bytes.
status = NdFetchImmediate(Instrux, Code, Offset, Size, (uint8_t)size);
if (!ND_SUCCESS(status))
{
return status;
}
// Get the last immediate.
if (Instrux->HasImm3)
{
imm = Instrux->Immediate3;
}
else if (Instrux->HasImm2)
{
imm = Instrux->Immediate2;
}
else
{
imm = Instrux->Immediate1;
}
operand->Type = ND_OP_IMM;
if (operand->Flags.SignExtendedDws)
{
static const uint8_t wszLut[3] = { ND_SIZE_16BIT, ND_SIZE_32BIT, ND_SIZE_64BIT };
// Get the default word size: the immediate is sign extended to the default word size.
operand->Size = wszLut[Instrux->EfOpMode];
operand->Info.Immediate.Imm = ND_SIGN_EX(size, imm);
}
else if (operand->Flags.SignExtendedOp1)
{
// The immediate is sign extended to the size of the first operand.
operand->Size = Instrux->Operands[0].Size;
operand->Info.Immediate.Imm = ND_SIGN_EX(size, imm);
}
else
{
operand->Info.Immediate.Imm = imm;
}
}
break;
case ND_OPT_Im2z:
{
operand->Type = ND_OP_IMM;
operand->Info.Immediate.Imm = Instrux->SseImmediate & 3;
}
break;
case ND_OPT_J:
// Fetch the relative offset. NOTE: The size of the relative can't exceed 4 bytes.
status = NdFetchRelativeOffset(Instrux, Code, Offset, Size, (uint8_t)size);
if (!ND_SUCCESS(status))
{
return status;
}
// The instruction is RIP relative.
Instrux->IsRipRelative = true;