This commit is contained in:
Oscar
2025-07-22 22:06:34 +03:00
parent d3c189f949
commit f892794557
780 changed files with 436498 additions and 170 deletions

View File

@@ -0,0 +1,181 @@
/*
config.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef CONFIG_H
#define CONFIG_H
/* diStorm version number. */
#define __DISTORMV__ 0x030502
#include <string.h> /* memset, memcpy - can be easily self implemented for libc independency. */
#include "../include/distorm.h"
/*
* 64 bit offsets support:
* This macro should be defined from compiler command line flags, e.g: -DSUPPORT_64BIT_OFFSET
* Note: make sure that the caller (library user) defines it too!
*/
/* #define SUPPORT_64BIT_OFFSET */
/*
* If you compile diStorm as a dynamic library (.dll or .so) file, make sure you uncomment the next line.
* So the interface functions will be exported, otherwise they are useable only for static library.
* For example, this macro is being set for compiling diStorm as a .dll for Python with CTypes.
*/
/* #define DISTORM_DYNAMIC */
/*
* If DISTORM_LIGHT is defined, everything involved in formatting the instructions
* as text will be excluded from compilation.
* distorm_decode(..) and distorm_format(..) will not be available.
* This will decrease the size of the executable and leave you with decomposition functionality only.
*
* Note: it should be either set in the preprocessor definitions manually or in command line -D switch.
* #define DISTORM_LIGHT
*/
/*
* diStorm now supports little/big endian CPU's.
* It should detect the endianness according to predefined macro's of the compiler.
* If you don't use GCC/MSVC you will have to define it on your own.
*/
/* These macros are used in order to make the code portable. */
#ifdef __GNUC__
#include <stdint.h>
#define _DLLEXPORT_
#define _FASTCALL_
/* Keep inline as static (arrrrg) as it would break linux on some flavors otherwise. */
#define _INLINE_ static
/* GCC ignores this directive... */
/*#define _FASTCALL_ __attribute__((__fastcall__))*/
/* Set endianity (supposed to be LE though): */
#ifdef __BIG_ENDIAN__
#define BE_SYSTEM
#endif
/* End of __GCC__ */
#elif __WATCOMC__
#include <stdint.h>
#define _DLLEXPORT_
#define _FASTCALL_
#define _INLINE_ __inline
/* End of __WATCOMC__ */
#elif __DMC__
#include <stdint.h>
#define _DLLEXPORT_
#define _FASTCALL_
#define _INLINE_ __inline
/* End of __DMC__ */
#elif __TINYC__
#include <stdint.h>
#define _DLLEXPORT_
#define _FASTCALL_
#define _INLINE_ static
/* End of __TINYC__ */
#elif _MSC_VER
/* stdint alternative is defined in distorm.h */
#define _DLLEXPORT_ __declspec(dllexport)
#define _FASTCALL_ __fastcall
#define _INLINE_ __inline
/* Set endianity (supposed to be LE though): */
#if !defined(_M_IX86) && !defined(_M_X64)
#define BE_SYSTEM
#endif
#endif /* #elif _MSC_VER */
/* If the library isn't compiled as a dynamic library don't export any functions. */
#ifndef DISTORM_DYNAMIC
#undef _DLLEXPORT_
#define _DLLEXPORT_
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
/* Define stream read functions for big endian systems. */
#ifdef BE_SYSTEM
/* Avoid defining 'static static' for GCC. */
#ifndef __GNUC__
#define STATIC_INLINE static _INLINE_
#else
#define STATIC_INLINE static
#endif
/*
* Assumption: These functions can read from the stream safely!
* Swap endianity of input to little endian.
*/
STATIC_INLINE int16_t RSHORT(const uint8_t *s)
{
return s[0] | (s[1] << 8);
}
STATIC_INLINE uint16_t RUSHORT(const uint8_t *s)
{
return s[0] | (s[1] << 8);
}
STATIC_INLINE int32_t RLONG(const uint8_t *s)
{
return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
}
STATIC_INLINE uint32_t RULONG(const uint8_t *s)
{
return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
}
STATIC_INLINE int64_t RLLONG(const uint8_t *s)
{
return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24) | ((uint64_t)s[4] << 32) | ((uint64_t)s[5] << 40) | ((uint64_t)s[6] << 48) | ((uint64_t)s[7] << 56);
}
STATIC_INLINE uint64_t RULLONG(const uint8_t *s)
{
return s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24) | ((uint64_t)s[4] << 32) | ((uint64_t)s[5] << 40) | ((uint64_t)s[6] << 48) | ((uint64_t)s[7] << 56);
}
#undef STATIC_INLINE
#else
/* Little endian macro's will just make the cast. */
#define RSHORT(x) *(int16_t *)x
#define RUSHORT(x) *(uint16_t *)x
#define RLONG(x) *(int32_t *)x
#define RULONG(x) *(uint32_t *)x
#define RLLONG(x) *(int64_t *)x
#define RULLONG(x) *(uint64_t *)x
#endif
#endif /* CONFIG_H */

View File

@@ -0,0 +1,574 @@
/*
decoder.c
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#include "decoder.h"
#include "instructions.h"
#include "insts.h"
#include "prefix.h"
#include "x86defs.h"
#include "operands.h"
#include "insts.h"
#include "../include/mnemonics.h"
/* Instruction Prefixes - Opcode - ModR/M - SIB - Displacement - Immediate */
static _DecodeType decode_get_effective_addr_size(_DecodeType dt, _iflags decodedPrefixes)
{
/*
* Map from the current decoding mode to an effective address size:
* Decode16 -> Decode32
* Decode32 -> Decode16
* Decode64 -> Decode32
*/
/* Switch to non default mode if prefix exists, only for ADDRESS SIZE. */
if (decodedPrefixes & INST_PRE_ADDR_SIZE) {
if (dt == Decode32Bits) return Decode16Bits;
return Decode32Bits;
}
return dt;
}
static _DecodeType decode_get_effective_op_size(_DecodeType dt, _iflags decodedPrefixes, unsigned int rex, _iflags instFlags)
{
/*
* Map from the current decoding mode to an effective operand size:
* Decode16 -> Decode32
* Decode32 -> Decode16
* Decode64 -> Decode16
* Not that in 64bits it's a bit more complicated, because of REX and promoted instructions.
*/
if (decodedPrefixes & INST_PRE_OP_SIZE) {
if (dt == Decode16Bits) return Decode32Bits;
return Decode16Bits;
}
if (dt == Decode64Bits) {
/*
* REX Prefix toggles data size to 64 bits.
* Operand size prefix toggles data size to 16.
* Default data size is 32 bits.
* Promoted instructions are 64 bits if they don't require a REX perfix.
* Non promoted instructions are 64 bits if the REX prefix exists.
*/
/* Automatically promoted instructions have only INST_64BITS SET! */
if (((instFlags & (INST_64BITS | INST_PRE_REX)) == INST_64BITS) ||
/* Other instructions in 64 bits can be promoted only with a REX prefix. */
((decodedPrefixes & INST_PRE_REX) && (rex & PREFIX_EX_W))) return Decode64Bits;
return Decode32Bits; /* Default. */
}
return dt;
}
/*
* A helper macro to convert from diStorm's CPU flags to EFLAGS.
* Copy eflags from compact version (8 bits) to eflags compatible (16 bits).
* From D_COMPACT_IF to D_IF, bit index 1 to 9.
* From D_COMPACT_DF to D_DF, bit index 3 to 10.
* From D_COMPACT_OF to D_OF, bit index 5 to 11.
*/
#define CONVERT_FLAGS_TO_EFLAGS(dst, src, field) dst->field = ((src->field & D_COMPACT_SAME_FLAGS) | \
((src->field & D_COMPACT_IF) << (9 - 1)) | \
((src->field & D_COMPACT_DF) << (10 - 3)) | \
((src->field & D_COMPACT_OF) << (11 - 5)));
/* If DECRES_SUCCESS is returned, CI is in sync, otherwise it loses sync. */
/* Important note: CI is keeping track only for code and codeLen, in case of a failure caller has to restart on their own. */
static _DecodeResult decode_inst(_CodeInfo* ci, _PrefixState* ps, const uint8_t* startCode, _DInst* di)
{
/* Holds the info about the current found instruction. */
_InstInfo* ii;
_InstSharedInfo* isi;
/* Calculate (and cache) effective-operand-size and effective-address-size only once. */
_DecodeType effOpSz, effAdrSz;
_iflags instFlags;
/* The ModR/M byte of the current instruction. */
unsigned int modrm = 0;
int isPrefixed = 0;
ii = inst_lookup(ci, ps, &isPrefixed);
if (ii == NULL) goto _Undecodable;
isi = &InstSharedInfoTable[ii->sharedIndex];
instFlags = FlagsTable[isi->flagsIndex];
/* Cache the effective operand-size and address-size. */
if (isPrefixed) {
/*
* If both REX and OpSize are available we will have to disable the OpSize, because REX has precedence.
* However, only if REX.W is set!
* We had to wait with this test, since the operand size may be a mandatory prefix,
* and we know it only after fetching opcode.
*/
if ((ps->decodedPrefixes & INST_PRE_OP_SIZE) &&
(ps->prefixExtType == PET_REX) &&
(ps->vrex & PREFIX_EX_W) &&
(!ps->isOpSizeMandatory)) {
ps->decodedPrefixes &= ~INST_PRE_OP_SIZE;
prefixes_ignore(ps, PFXIDX_OP_SIZE);
}
effAdrSz = decode_get_effective_addr_size(ci->dt, ps->decodedPrefixes);
effOpSz = decode_get_effective_op_size(ci->dt, ps->decodedPrefixes, ps->vrex, instFlags);
}
else
{
effAdrSz = ci->dt; /* Default is current decoding type since there's no prefix. */
effOpSz = decode_get_effective_op_size(ci->dt, 0, 0, instFlags);
}
/*
* In this point we know the instruction we are about to decode and its operands (unless, it's an invalid one!),
* so it makes it the right time for decoding-type suitability testing.
* Which practically means, don't allow 32 bits instructions in 16 bits decoding mode, but do allow
* 16 bits instructions in 32 bits decoding mode, of course...
* NOTE: Make sure the instruction set for 32 bits has explicitly this specific flag set.
* NOTE2: Make sure the instruction set for 64 bits has explicitly this specific flag set.
* If this is the case, drop what we've got and restart all over after DB'ing that byte.
* Though, don't drop an instruction which is also supported in 16 and 32 bits.
*/
/* ! ! ! DISABLED UNTIL FURTHER NOTICE ! ! ! Decode16Bits CAN NOW DECODE 32 BITS INSTRUCTIONS ! ! !*/
/* if (ii && (dt == Decode16Bits) && (instFlags & INST_32BITS) && (~instFlags & INST_16BITS)) ii = NULL; */
memset(di, 0, sizeof(_DInst));
if (instFlags & INST_MODRM_REQUIRED) {
/* If the ModRM byte is not part of the opcode, skip the last byte code, so code points now to ModRM. */
if (!(instFlags & INST_MODRM_INCLUDED)) {
ci->code++;
if (--ci->codeLen < 0) goto _Undecodable;
}
modrm = *ci->code;
}
ci->code++; /* Skip the last byte we just read (either last opcode's byte code or a ModRM). */
di->addr = ci->codeOffset & ci->addrMask;
di->opcode = ii->opcodeId;
di->flags = isi->meta & META_INST_PRIVILEGED;
/*
* Store the address size inside the flags.
* This is necessary for the caller to know the size of rSP when using PUSHA for example.
*/
di->base = R_NONE;
di->segment = R_NONE;
FLAG_SET_ADDRSIZE(di, effAdrSz);
/* Try to extract the next operand only if the latter exists. */
if (isi->d != OT_NONE) {
unsigned int opsNo = 1;
_Operand* op = &di->ops[0];
if (instFlags & (INST_MODRR_REQUIRED | INST_FORCE_REG0)) {
/* Some instructions enforce that mod=11, so validate that. */
if ((modrm < INST_DIVIDED_MODRM) && (instFlags & INST_MODRR_REQUIRED)) goto _Undecodable;
/* Some instructions enforce that reg=000, so validate that. (Specifically EXTRQ). */
if ((instFlags & INST_FORCE_REG0) && (((modrm >> 3) & 7) != 0)) goto _Undecodable;
}
if (!operands_extract(ci, di, ii, instFlags, (_OpType)isi->d, modrm, ps, effOpSz, effAdrSz, op++)) goto _Undecodable;
if (isi->s != OT_NONE) {
if (!operands_extract(ci, di, ii, instFlags, (_OpType)isi->s, modrm, ps, effOpSz, effAdrSz, op++)) goto _Undecodable;
opsNo++;
/* Use third operand, only if the flags says this InstInfo requires it. */
if (instFlags & INST_USE_OP3) {
if (!operands_extract(ci, di, ii, instFlags, (_OpType)((_InstInfoEx*)ii)->op3, modrm, ps, effOpSz, effAdrSz, op++)) goto _Undecodable;
opsNo++;
/* Support for a fourth operand is added for (e.g:) INSERTQ instruction. */
if (instFlags & INST_USE_OP4) {
if (!operands_extract(ci, di, ii, instFlags, (_OpType)((_InstInfoEx*)ii)->op4, modrm, ps, effOpSz, effAdrSz, op++)) goto _Undecodable;
opsNo++;
}
}
}
/* Copy DST_WR flag. */
di->flags |= (instFlags & INST_DST_WR) >> (31 - 6); /* Copy bit from INST_DST_WR (bit 31) to FLAG_DST_WR (bit 6). */
/* operands_extract may touched it for FPU operands, so add on top. */
di->opsNo += (uint8_t)opsNo;
}
if (instFlags & (INST_3DNOW_FETCH |
INST_PSEUDO_OPCODE |
INST_NATIVE |
INST_PRE_REPNZ |
INST_PRE_REP |
INST_PRE_ADDR_SIZE |
INST_INVALID_64BITS |
INST_64BITS_FETCH)) { /* 8 for 1! */
/* If it's a native instruction copy OpSize Prefix. */
if (ps && instFlags & INST_NATIVE) ps->usedPrefixes |= (ps->decodedPrefixes & INST_PRE_OP_SIZE);
if (ci->dt != Decode64Bits) {
/* If it's only a 64 bits instruction drop it in other decoding modes. */
if (instFlags & INST_64BITS_FETCH) goto _Undecodable;
}
else {
/* Drop instructions which are invalid in 64 bits. */
if (instFlags & INST_INVALID_64BITS) goto _Undecodable;
}
/* If it were a 3DNow! instruction, we will have to find the instruction itself now that we got its operands extracted. */
if (instFlags & INST_3DNOW_FETCH) {
ii = inst_lookup_3dnow(ci);
if (ii == NULL) goto _Undecodable;
isi = &InstSharedInfoTable[ii->sharedIndex];
instFlags = FlagsTable[isi->flagsIndex];
di->opcode = ii->opcodeId;
}
/* Check whether pseudo opcode is needed, only for CMP instructions: */
if (instFlags & INST_PSEUDO_OPCODE) {
/* Used only for special CMP instructions which have pseudo opcodes suffix. */
unsigned int cmpType;
if (--ci->codeLen < 0) goto _Undecodable;
cmpType = *ci->code;
ci->code++;
/*
* The opcodeId is the offset to the FIRST pseudo compare mnemonic,
* we will have to fix it so it offsets into the corrected mnemonic.
* Therefore, we use another table to fix the offset.
*/
if (instFlags & INST_PRE_VEX) {
/* AVX Comparison type must be between 0 to 32, otherwise Reserved. */
if (cmpType >= INST_VCMP_MAX_RANGE) goto _Undecodable;
/* Use the AVX pseudo compare mnemonics table. */
di->opcode = ii->opcodeId + VCmpMnemonicOffsets[cmpType];
}
else {
/* SSE Comparison type must be between 0 to 8, otherwise Reserved. */
if (cmpType >= INST_CMP_MAX_RANGE) goto _Undecodable;
di->opcode = ii->opcodeId + CmpMnemonicOffsets[cmpType];
}
goto _SkipOpcoding;
}
/* Start with prefix REP/N/Z. */
if (isPrefixed && (instFlags & (INST_PRE_REPNZ | INST_PRE_REP))) {
if ((instFlags & INST_PRE_REPNZ) && (ps->decodedPrefixes & INST_PRE_REPNZ)) {
ps->usedPrefixes |= INST_PRE_REPNZ;
di->flags |= FLAG_REPNZ;
}
else if ((instFlags & INST_PRE_REP) && (ps->decodedPrefixes & INST_PRE_REP)) {
ps->usedPrefixes |= INST_PRE_REP;
di->flags |= FLAG_REP;
}
}
if (instFlags & INST_PRE_ADDR_SIZE) {
/* If it's JeCXZ the ADDR_SIZE prefix affects them. */
if (instFlags & INST_USE_EXMNEMONIC) {
ps->usedPrefixes |= INST_PRE_ADDR_SIZE;
if (effAdrSz == Decode16Bits) di->opcode = ii->opcodeId;
else if (effAdrSz == Decode32Bits) di->opcode = ((_InstInfoEx*)ii)->opcodeId2;
/* Ignore REX.W in 64bits, JECXZ is promoted. */
else /* Decode64Bits */ di->opcode = ((_InstInfoEx*)ii)->opcodeId3;
}
/* LOOPxx instructions are also native instruction, but they are special case ones, ADDR_SIZE prefix affects them. */
else if (instFlags & INST_NATIVE) {
di->opcode = ii->opcodeId;
/* If LOOPxx gets here from 64bits, it must be Decode32Bits because Address Size prefix is set. */
ps->usedPrefixes |= INST_PRE_ADDR_SIZE;
}
goto _SkipOpcoding;
}
}
/*
* If we reached here the instruction was fully decoded, we located the instruction in the DB and extracted operands.
* Use the correct mnemonic according to the DT.
* If we are in 32 bits decoding mode it doesn't necessarily mean we will choose mnemonic2, alas,
* it means that if there is a mnemonic2, it will be used.
* Note:
* If the instruction is prefixed by operand size we will format it in the non-default decoding mode!
* So there might be a situation that an instruction of 32 bit gets formatted in 16 bits decoding mode.
* Both ways should end up with a correct and expected formatting of the text.
*/
if (effOpSz == Decode32Bits) { /* Decode32Bits */
/* Set operand size. */
FLAG_SET_OPSIZE(di, Decode32Bits);
/* Give a chance for special mnemonic instruction in 32 bits decoding. */
if (instFlags & INST_USE_EXMNEMONIC) {
/* Is it a special instruction which has another mnemonic for mod=11 ? */
if (instFlags & INST_MNEMONIC_MODRM_BASED) {
if (modrm < INST_DIVIDED_MODRM) di->opcode = ((_InstInfoEx*)ii)->opcodeId2;
}
else di->opcode = ((_InstInfoEx*)ii)->opcodeId2;
ps->usedPrefixes |= INST_PRE_OP_SIZE;
}
}
else if (effOpSz == Decode64Bits) { /* Decode64Bits, note that some instructions might be decoded in Decode32Bits above. */
/* Set operand size. */
FLAG_SET_OPSIZE(di, Decode64Bits);
if (instFlags & (INST_USE_EXMNEMONIC | INST_USE_EXMNEMONIC2)) {
/*
* We shouldn't be here for MODRM based mnemonics with a MOD=11,
* because they must not use REX (otherwise it will get to the wrong instruction which share same opcode).
* See XRSTOR and XSAVEOPT.
*/
if ((modrm >= INST_DIVIDED_MODRM) && (instFlags & INST_MNEMONIC_MODRM_BASED)) goto _Undecodable;
/* Use third mnemonic, for 64 bits. */
if ((instFlags & INST_USE_EXMNEMONIC2) && (ps->vrex & PREFIX_EX_W)) {
ps->usedPrefixes |= INST_PRE_REX;
di->opcode = ((_InstInfoEx*)ii)->opcodeId3;
}
else di->opcode = ((_InstInfoEx*)ii)->opcodeId2; /* Use second mnemonic. */
}
}
else { /* Decode16Bits */
/* Set operand size. */
FLAG_SET_OPSIZE(di, Decode16Bits);
/*
* If it's a special instruction which has two mnemonics, then use the 16 bits one + update usedPrefixes.
* Note: use 16 bits mnemonic if that instruction supports 32 bit or 64 bit explicitly.
*/
if ((instFlags & (INST_USE_EXMNEMONIC | INST_32BITS | INST_64BITS)) == INST_USE_EXMNEMONIC) ps->usedPrefixes |= INST_PRE_OP_SIZE;
}
_SkipOpcoding:
/* Check VEX mnemonics: */
if (isPrefixed && (instFlags & INST_PRE_VEX) &&
(((((_InstInfoEx*)ii)->flagsEx & INST_MNEMONIC_VEXW_BASED) && (ps->vrex & PREFIX_EX_W)) ||
((((_InstInfoEx*)ii)->flagsEx & INST_MNEMONIC_VEXL_BASED) && (ps->vrex & PREFIX_EX_L)))) {
di->opcode = ((_InstInfoEx*)ii)->opcodeId2;
}
/* Instruction's size should include prefixes too if exist. */
di->size = (uint8_t)(ci->code - startCode);
/*
* There's a limit of 15 bytes on instruction length. The only way to violate
* this limit is by putting redundant prefixes before an instruction.
* start points to first prefix if any, otherwise it points to instruction first byte.
*/
if (di->size > INST_MAXIMUM_SIZE) goto _Undecodable;
/* Set the unused prefixes mask, if any prefixes (not) used at all. */
if (isPrefixed) di->unusedPrefixesMask = prefixes_set_unused_mask(ps);
/* Copy instruction meta. */
di->meta = isi->meta;
if (ci->features & DF_FILL_EFLAGS) {
/* Copy CPU affected flags. */
if (isi->testedFlagsMask) CONVERT_FLAGS_TO_EFLAGS(di, isi, testedFlagsMask);
if (isi->modifiedFlagsMask) CONVERT_FLAGS_TO_EFLAGS(di, isi, modifiedFlagsMask);
if (isi->undefinedFlagsMask) CONVERT_FLAGS_TO_EFLAGS(di, isi, undefinedFlagsMask);
}
/*
* Instruction can still be invalid if it's total length is over 15 bytes with prefixes.
* Up to the caller to check that.
*/
return DECRES_SUCCESS;
_Undecodable: /* If the instruction couldn't be decoded for some reason, fail. */
/* Special case for WAIT instruction: If it's dropped as a prefix, we have to return a valid instruction! */
if (*startCode == INST_WAIT_INDEX) {
int delta;
memset(di, 0, sizeof(_DInst));
di->addr = ci->codeOffset & ci->addrMask;
di->imm.byte = INST_WAIT_INDEX;
di->segment = R_NONE;
di->base = R_NONE;
di->size = 1;
di->opcode = I_WAIT;
META_SET_ISC(di, ISC_INTEGER);
/* Fix ci because WAIT could be a prefix that failed, and ci->code is now out of sync. */
delta = (int)(ci->code - startCode); /* How many bytes we read so far. */
ci->codeLen += delta - 1;
ci->code = startCode + 1;
/* codeOffset is fixed outside. */
return DECRES_SUCCESS;
}
/* Mark that we didn't manage to decode the instruction well, caller will drop it. */
return DECRES_INPUTERR;
}
/*
* decode_internal
*
* supportOldIntr - Since now we work with new structure instead of the old _DecodedInst, we are still interested in backward compatibility.
* So although, the array is now of type _DInst, we want to read it in jumps of the old array element's size.
* This is in order to save memory allocation for conversion between the new and the old structures.
* It really means we can do the conversion in-place now.
*/
_DecodeResult decode_internal(_CodeInfo* _ci, int supportOldIntr, _DInst result[], unsigned int maxResultCount, unsigned int* usedInstructionsCount)
{
_CodeInfo ci = *_ci; /* A working copy, we don't touch user's _ci except OUT params. */
_PrefixState ps;
/* Bookkeep these from ci below, as it makes things way simpler. */
const uint8_t* code;
int codeLen;
_OffsetType codeOffset;
_DecodeResult ret = DECRES_SUCCESS;
/* Current working decoded instruction in results. */
_DInst* pdi = (_DInst*)&result[0]; /* There's always a room for at least one slot, checked earlier. */
_DInst* maxResultAddr;
unsigned int features = ci.features;
unsigned int diStructSize;
/* Use next entry. */
#ifndef DISTORM_LIGHT
if (supportOldIntr) {
diStructSize = sizeof(_DecodedInst);
maxResultAddr = (_DInst*)((size_t)&result[0] + (maxResultCount * sizeof(_DecodedInst)));
}
else
#endif /* DISTORM_LIGHT */
{
diStructSize = sizeof(_DInst);
maxResultAddr = &result[maxResultCount];
}
ci.addrMask = (_OffsetType)-1;
#ifdef DISTORM_LIGHT
supportOldIntr; /* Unreferenced. */
/*
* Only truncate address if we are using the decompose interface.
* Otherwise, we use the textual interface which needs full addresses for formatting bytes output.
* So distorm_format will truncate later.
*/
if (features & DF_MAXIMUM_ADDR32) ci.addrMask = 0xffffffff;
else if (features & DF_MAXIMUM_ADDR16) ci.addrMask = 0xffff;
#endif
ps.count = 1; /* Force zero'ing ps below. */
/* Decode instructions as long as we have what to decode/enough room in entries. */
while (ci.codeLen > 0) {
code = ci.code;
codeLen = ci.codeLen;
codeOffset = ci.codeOffset;
if (ps.count) memset(&ps, 0, sizeof(ps));
/**** INSTRUCTION DECODING NEXT: ****/
/* Make sure we didn't run out of output entries. */
if (pdi >= maxResultAddr) {
ret = DECRES_MEMORYERR;
break;
}
ret = decode_inst(&ci, &ps, code, pdi);
/* decode_inst keeps track (only if successful!) for code and codeLen but ignores codeOffset, fix it here. */
ci.codeOffset += pdi->size;
if (ret == DECRES_SUCCESS) {
if (features & (DF_SINGLE_BYTE_STEP | DF_RETURN_FC_ONLY | DF_STOP_ON_PRIVILEGED | DF_STOP_ON_FLOW_CONTROL)) {
/* Sync codeinfo, remember that currently it points to beginning of the instruction and prefixes if any. */
if (features & DF_SINGLE_BYTE_STEP) {
ci.code = code + 1;
ci.codeLen = codeLen - 1;
ci.codeOffset = codeOffset + 1;
}
/* See if we need to filter this instruction. */
if ((features & DF_RETURN_FC_ONLY) && (META_GET_FC(pdi->meta) == FC_NONE)) {
continue;
}
/* Check whether we need to stop on any feature. */
if ((features & DF_STOP_ON_PRIVILEGED) && (FLAG_GET_PRIVILEGED(pdi->flags))) {
pdi = (_DInst*)((char*)pdi + diStructSize);
break; /* ret = DECRES_SUCCESS; */
}
if (features & DF_STOP_ON_FLOW_CONTROL) {
unsigned int mfc = META_GET_FC(pdi->meta);
if (mfc && (((features & DF_STOP_ON_CALL) && (mfc == FC_CALL)) ||
((features & DF_STOP_ON_RET) && (mfc == FC_RET)) ||
((features & DF_STOP_ON_SYS) && (mfc == FC_SYS)) ||
((features & DF_STOP_ON_UNC_BRANCH) && (mfc == FC_UNC_BRANCH)) ||
((features & DF_STOP_ON_CND_BRANCH) && (mfc == FC_CND_BRANCH)) ||
((features & DF_STOP_ON_INT) && (mfc == FC_INT)) ||
((features & DF_STOP_ON_CMOV) && (mfc == FC_CMOV)) ||
((features & DF_STOP_ON_HLT) && (mfc == FC_HLT)))) {
pdi = (_DInst*)((char*)pdi + diStructSize);
break; /* ret = DECRES_SUCCESS; */
}
}
}
/* Allocate at least one more entry to use, for the next instruction. */
pdi = (_DInst*)((char*)pdi + diStructSize);
}
else { /* ret == DECRES_INPUTERR */
/* Handle failure of decoding last instruction. */
if ((!(features & DF_RETURN_FC_ONLY))) {
memset(pdi, 0, sizeof(_DInst));
pdi->flags = FLAG_NOT_DECODABLE;
pdi->imm.byte = *code;
pdi->size = 1;
pdi->addr = codeOffset & ci.addrMask;
pdi = (_DInst*)((char*)pdi + diStructSize);
/* If an instruction wasn't decoded then stop on undecodeable if set. */
if (features & DF_STOP_ON_UNDECODEABLE) {
ret = DECRES_SUCCESS;
break;
}
}
/* Skip a single byte in case of a failure and retry instruction. */
ci.code = code + 1;
ci.codeLen = codeLen - 1;
ci.codeOffset = codeOffset + 1;
/* Reset return value. */
ret = DECRES_SUCCESS;
}
}
/* Set OUT params. */
*usedInstructionsCount = (unsigned int)(((size_t)pdi - (size_t)result) / (size_t)diStructSize);
_ci->nextOffset = ci.codeOffset;
return ret;
}

View File

@@ -0,0 +1,21 @@
/*
decoder.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef DECODER_H
#define DECODER_H
#include "config.h"
typedef unsigned int _iflags;
_DecodeResult decode_internal(_CodeInfo* _ci, int supportOldIntr, _DInst result[], unsigned int maxResultCount, unsigned int* usedInstructionsCount);
#endif /* DECODER_H */

View File

@@ -0,0 +1,117 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DISASM_H
#define DISASM_H 1
#ifdef DISASM_DISTORM
#include <distorm.h>
#include <mnemonics.h>
typedef struct funchook_disasm {
funchook_t *funchook;
_CodeInfo ci;
unsigned int idx;
unsigned int cnt;
_DInst dis[MAX_INSN_CHECK_SIZE];
} funchook_disasm_t;
typedef _DInst funchook_insn_t;
#define funchook_insn_size(insn) ((insn)->size)
#define funchook_insn_address(insn) ((size_t)(insn)->addr)
#define funchook_insn_branch_address(insn) ((size_t)INSTRUCTION_GET_TARGET(insn))
#endif
#ifdef DISASM_CAPSTONE
#include <capstone/capstone.h>
typedef struct funchook_disasm {
funchook_t *funchook;
csh handle;
cs_insn *insns;
size_t index;
size_t count;
} funchook_disasm_t;
typedef cs_insn funchook_insn_t;
#define funchook_insn_size(insn) ((insn)->size / sizeof(insn_t))
#define funchook_insn_address(insn) ((size_t)(insn)->address)
#define funchook_insn_branch_address(insn) ((size_t)(insn)->detail->x86.operands[0].imm)
#endif
#ifdef DISASM_ZYDIS
#include <Zydis/Zydis.h>
typedef struct {
ZydisDecodedInstruction insn;
size_t next_address;
} funchook_insn_t;
typedef struct funchook_disasm {
funchook_t *funchook;
ZydisDecoder decoder;
ZydisFormatter formatter;
funchook_insn_t insn;
const uint8_t *code;
const uint8_t *code_end;
} funchook_disasm_t;
#define funchook_insn_size(insn) ((insn)->insn.length)
#define funchook_insn_address(insn) ((insn)->next_address - (insn)->insn.length)
#define funchook_insn_branch_address(insn) ((insn)->next_address + (intptr_t)(insn)->insn.raw.imm[0].value.s)
#endif
#define FUNCHOOK_ERROR_END_OF_INSTRUCTION -2
int funchook_disasm_init(funchook_disasm_t *disasm, funchook_t *funchook, const insn_t *code, size_t code_size, size_t address);
void funchook_disasm_cleanup(funchook_disasm_t *disasm);
int funchook_disasm_next(funchook_disasm_t *disasm, const funchook_insn_t **next_insn);
void funchook_disasm_log_instruction(funchook_disasm_t *disasm, const funchook_insn_t *insn);
#if defined(CPU_ARM64)
funchook_insn_info_t funchook_disasm_arm64_insn_info(funchook_disasm_t *disasm, const funchook_insn_t *insn);
#endif
#if defined(CPU_X86) || defined(CPU_X86_64)
/* RIP-relative address information */
typedef struct {
insn_t *addr; /* absolute address */
intptr_t raddr; /* relative address */
int offset;
int size;
} rip_relative_t;
void funchook_disasm_x86_rip_relative(funchook_disasm_t *disasm, const funchook_insn_t *insn, rip_relative_t *rel_disp, rip_relative_t *rel_imm);
#endif
#endif

View File

@@ -0,0 +1,310 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "funchook_internal.h"
#include "disasm.h"
int funchook_disasm_init(funchook_disasm_t *disasm, funchook_t *funchook, const uint8_t *code, size_t code_size, size_t address)
{
_DecodeResult decres;
disasm->funchook = funchook;
disasm->ci.codeOffset = address;
disasm->ci.code = code;
disasm->ci.codeLen = (int)code_size;
#ifdef CPU_X86_64
disasm->ci.dt = Decode64Bits;
#else
disasm->ci.dt = Decode32Bits;
#endif
disasm->ci.features = DF_STOP_ON_RET;
disasm->idx = 0;
decres = distorm_decompose64(&disasm->ci, disasm->dis, MAX_INSN_CHECK_SIZE, &disasm->cnt);
if (decres != DECRES_SUCCESS) {
funchook_set_error_message(funchook, "Disassemble Error: %d", decres);
return FUNCHOOK_ERROR_DISASSEMBLY;
}
return 0;
}
void funchook_disasm_cleanup(funchook_disasm_t *disasm)
{
/* no need to free resources */
}
int funchook_disasm_next(funchook_disasm_t *disasm, const funchook_insn_t **next_insn)
{
if (disasm->idx < disasm->cnt) {
*next_insn = &disasm->dis[disasm->idx++];
return 0;
} else {
return FUNCHOOK_ERROR_END_OF_INSTRUCTION;
}
}
void funchook_disasm_log_instruction(funchook_disasm_t *disasm, const funchook_insn_t *insn)
{
_DecodedInst dec;
distorm_format64(&disasm->ci, insn, &dec);
funchook_log(disasm->funchook, " "ADDR_FMT" (%02d) %-24s %s%s%s\n",
(size_t)dec.offset, dec.size, (char*)dec.instructionHex.p,
(char*)dec.mnemonic.p, dec.operands.length != 0 ? " " : "", (char*)dec.operands.p);
}
void funchook_disasm_x86_rip_relative(funchook_disasm_t *disasm, const funchook_insn_t *insn, rip_relative_t *rel_disp, rip_relative_t *rel_imm)
{
int opsiz = 0;
int disp_offset = 0;
int imm_offset = 0;
int i;
memset(rel_disp, 0, sizeof(rip_relative_t));
memset(rel_imm, 0, sizeof(rip_relative_t));
/*
* Estimate total operand size and RIP-relative address offsets.
*/
for (i = 0; i < OPERANDS_NO && insn->ops[i].type != O_NONE; i++) {
const _Operand *op = &insn->ops[i];
switch (op->type) {
case O_IMM:
opsiz += op->size / 8;
break;
case O_PC:
rel_imm->addr = (uint8_t*)(size_t)(insn->addr + insn->size + insn->imm.addr);
rel_imm->raddr = (intptr_t)insn->imm.addr;
rel_imm->size = op->size;
imm_offset = opsiz;
opsiz += op->size / 8;
break;
case O_SMEM:
if (insn->dispSize != 0 && op->index == R_RIP) {
rel_disp->addr = (uint8_t*)(size_t)(insn->addr + insn->size + insn->disp);
rel_disp->raddr = (intptr_t)insn->disp;
rel_disp->size = insn->dispSize;
disp_offset = opsiz;
}
opsiz += insn->dispSize / 8;
break;
case O_MEM:
case O_DISP:
opsiz += insn->dispSize / 8;
break;
}
}
switch (insn->opcode) {
/* CMPSD */
case I_CMPEQSD:
case I_CMPLTSD:
case I_CMPLESD:
case I_CMPUNORDSD:
case I_CMPNEQSD:
case I_CMPNLTSD:
case I_CMPNLESD:
case I_CMPORDSD:
case I_VCMPEQSD:
case I_VCMPLTSD:
case I_VCMPLESD:
case I_VCMPUNORDSD:
case I_VCMPNEQSD:
case I_VCMPNLTSD:
case I_VCMPNLESD:
case I_VCMPORDSD:
case I_VCMPEQ_UQSD:
case I_VCMPNGESD:
case I_VCMPNGTSD:
case I_VCMPFALSESD:
case I_VCMPNEQ_OQSD:
case I_VCMPGESD:
case I_VCMPGTSD:
case I_VCMPTRUESD:
case I_VCMPEQ_OSSD:
case I_VCMPLT_OQSD:
case I_VCMPLE_OQSD:
case I_VCMPUNORD_SSD:
case I_VCMPNEQ_USSD:
case I_VCMPNLT_UQSD:
case I_VCMPNLE_UQSD:
case I_VCMPORD_SSD:
case I_VCMPEQ_USSD:
case I_VCMPNGE_UQSD:
case I_VCMPNGT_UQSD:
case I_VCMPFALSE_OSSD:
case I_VCMPNEQ_OSSD:
case I_VCMPGE_OQSD:
case I_VCMPGT_OQSD:
/* CMPSS */
case I_CMPEQSS:
case I_CMPLTSS:
case I_CMPLESS:
case I_CMPUNORDSS:
case I_CMPNEQSS:
case I_CMPNLTSS:
case I_CMPNLESS:
case I_CMPORDSS:
case I_VCMPEQSS:
case I_VCMPLTSS:
case I_VCMPLESS:
case I_VCMPUNORDSS:
case I_VCMPNEQSS:
case I_VCMPNLTSS:
case I_VCMPNLESS:
case I_VCMPORDSS:
case I_VCMPEQ_UQSS:
case I_VCMPNGESS:
case I_VCMPNGTSS:
case I_VCMPFALSESS:
case I_VCMPNEQ_OQSS:
case I_VCMPGESS:
case I_VCMPGTSS:
case I_VCMPTRUESS:
case I_VCMPEQ_OSSS:
case I_VCMPLT_OQSS:
case I_VCMPLE_OQSS:
case I_VCMPUNORD_SSS:
case I_VCMPNEQ_USSS:
case I_VCMPNLT_UQSS:
case I_VCMPNLE_UQSS:
case I_VCMPORD_SSS:
case I_VCMPEQ_USSS:
case I_VCMPNGE_UQSS:
case I_VCMPNGT_UQSS:
case I_VCMPFALSE_OSSS:
case I_VCMPNEQ_OSSS:
case I_VCMPGE_OQSS:
case I_VCMPGT_OQSS:
/* CMPPD */
case I_CMPEQPD:
case I_CMPLTPD:
case I_CMPLEPD:
case I_CMPUNORDPD:
case I_CMPNEQPD:
case I_CMPNLTPD:
case I_CMPNLEPD:
case I_CMPORDPD:
case I_VCMPEQPD:
case I_VCMPLTPD:
case I_VCMPLEPD:
case I_VCMPUNORDPD:
case I_VCMPNEQPD:
case I_VCMPNLTPD:
case I_VCMPNLEPD:
case I_VCMPORDPD:
case I_VCMPEQ_UQPD:
case I_VCMPNGEPD:
case I_VCMPNGTPD:
case I_VCMPFALSEPD:
case I_VCMPNEQ_OQPD:
case I_VCMPGEPD:
case I_VCMPGTPD:
case I_VCMPTRUEPD:
case I_VCMPEQ_OSPD:
case I_VCMPLT_OQPD:
case I_VCMPLE_OQPD:
case I_VCMPUNORD_SPD:
case I_VCMPNEQ_USPD:
case I_VCMPNLT_UQPD:
case I_VCMPNLE_UQPD:
case I_VCMPORD_SPD:
case I_VCMPEQ_USPD:
case I_VCMPNGE_UQPD:
case I_VCMPNGT_UQPD:
case I_VCMPFALSE_OSPD:
case I_VCMPNEQ_OSPD:
case I_VCMPGE_OQPD:
case I_VCMPGT_OQPD:
case I_VCMPTRUE_USPD:
/* CMPPS */
case I_CMPEQPS:
case I_CMPLTPS:
case I_CMPLEPS:
case I_CMPUNORDPS:
case I_CMPNEQPS:
case I_CMPNLTPS:
case I_CMPNLEPS:
case I_CMPORDPS:
case I_VCMPEQPS:
case I_VCMPLTPS:
case I_VCMPLEPS:
case I_VCMPUNORDPS:
case I_VCMPNEQPS:
case I_VCMPNLTPS:
case I_VCMPNLEPS:
case I_VCMPORDPS:
case I_VCMPEQ_UQPS:
case I_VCMPNGEPS:
case I_VCMPNGTPS:
case I_VCMPFALSEPS:
case I_VCMPNEQ_OQPS:
case I_VCMPGEPS:
case I_VCMPGTPS:
case I_VCMPTRUEPS:
case I_VCMPEQ_OSPS:
case I_VCMPLT_OQPS:
case I_VCMPLE_OQPS:
case I_VCMPUNORD_SPS:
case I_VCMPNEQ_USPS:
case I_VCMPNLT_UQPS:
case I_VCMPNLE_UQPS:
case I_VCMPORD_SPS:
case I_VCMPEQ_USPS:
case I_VCMPNGE_UQPS:
case I_VCMPNGT_UQPS:
case I_VCMPFALSE_OSPS:
case I_VCMPNEQ_OSPS:
case I_VCMPGE_OQPS:
case I_VCMPGT_OQPS:
case I_VCMPTRUE_USPS:
/* ohters */
case I_PI2FD:
case I_PI2FW:
case I_PF2IW:
case I_PF2ID:
case I_PSWAPD:
case I_VPBLENDVB:
case I_PFNACC:
opsiz++;
}
if (rel_disp->size > 0) {
rel_disp->offset = insn->size - opsiz + disp_offset;
funchook_log(disasm->funchook, " ip-relative %08x, absolute address= "ADDR_FMT", offset=%d, size=%d\n",
(uint32_t)rel_disp->raddr, (size_t)rel_disp->addr, rel_disp->offset, rel_disp->size);
}
if (rel_imm->size > 0) {
rel_imm->offset = insn->size - opsiz + imm_offset;
funchook_log(disasm->funchook, " ip-relative %08x, absolute address= "ADDR_FMT", offset=%d, size=%d\n",
(uint32_t)rel_imm->raddr, (size_t)rel_imm->addr, rel_imm->offset, rel_imm->size);
}
}

View File

@@ -0,0 +1,419 @@
/*
distorm.c
diStorm3 C Library Interface
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#include "../include/distorm.h"
#include "config.h"
#include "decoder.h"
#include "x86defs.h"
#include "textdefs.h"
#include "wstring.h"
#include "../include/mnemonics.h"
/* C DLL EXPORTS */
#ifdef SUPPORT_64BIT_OFFSET
_DLLEXPORT_ _DecodeResult distorm_decompose64(_CodeInfo* ci, _DInst result[], unsigned int maxInstructions, unsigned int* usedInstructionsCount)
#else
_DLLEXPORT_ _DecodeResult distorm_decompose32(_CodeInfo* ci, _DInst result[], unsigned int maxInstructions, unsigned int* usedInstructionsCount)
#endif
{
if (usedInstructionsCount == NULL) {
return DECRES_SUCCESS;
}
if ((ci == NULL) ||
(ci->codeLen < 0) ||
((unsigned)ci->dt > (unsigned)Decode64Bits) ||
(ci->code == NULL) ||
(result == NULL) ||
(maxInstructions == 0) ||
((ci->features & (DF_MAXIMUM_ADDR16 | DF_MAXIMUM_ADDR32)) == (DF_MAXIMUM_ADDR16 | DF_MAXIMUM_ADDR32)))
{
return DECRES_INPUTERR;
}
return decode_internal(ci, FALSE, result, maxInstructions, usedInstructionsCount);
}
#ifndef DISTORM_LIGHT
/* Helper function to concatenate an explicit size when it's unknown from the operands. */
static void distorm_format_size(unsigned char** str, const _DInst* di, int opNum)
{
int isSizingRequired = 0;
/*
* We only have to output the size explicitly if it's not clear from the operands.
* For example:
* mov al, [0x1234] -> The size is 8, we know it from the AL register operand.
* mov [0x1234], 0x11 -> Now we don't know the size. Pam pam pam
*
* If given operand number is higher than 2, then output the size anyways.
*/
isSizingRequired = ((opNum >= 2) || ((opNum == 0) && (di->ops[0].type != O_REG) && (di->ops[1].type != O_REG)));
/* Still not sure? Try some special instructions. */
if (!isSizingRequired) {
/*
* INS/OUTS are exception, because DX is a port specifier and not a real src/dst register.
* A few exceptions that always requires sizing:
* MOVZX, MOVSX, MOVSXD.
* ROL, ROR, RCL, RCR, SHL, SHR, SAL, SAR.
* SHLD, SHRD.
* CVTSI2SS is also an exception.
*/
switch (di->opcode)
{
case I_INS:
case I_OUTS:
case I_MOVZX:
case I_MOVSX:
case I_MOVSXD:
case I_ROL:
case I_ROR:
case I_RCL:
case I_RCR:
case I_SHL:
case I_SHR:
case I_SAL:
case I_SAR:
case I_SHLD:
case I_SHRD:
case I_CVTSI2SS:
isSizingRequired = 1;
break;
default: /* Instruction doesn't require sizing. */ break;
}
}
if (isSizingRequired)
{
/*case 0: break; OT_MEM's unknown size. */
switch (di->ops[opNum].size / 8)
{
case 1: strcat_WS(*str, "BYTE ", 8, 5); break;
case 2: strcat_WS(*str, "WORD ", 8, 5); break;
case 4: strcat_WS(*str, "DWORD ", 8, 6); break;
case 8: strcat_WS(*str, "QWORD ", 8, 6); break;
case 10: strcat_WS(*str, "TBYTE ", 8, 6); break;
case 16: strcat_WS(*str, "DQWORD ", 8, 7); break;
case 32: strcat_WS(*str, "YWORD ", 8, 6); break;
}
}
}
static void distorm_format_signed_disp(unsigned char** str, const _DInst* di, uint64_t addrMask)
{
int64_t tmpDisp64;
if (di->dispSize) {
if (((int64_t)di->disp < 0)) {
chrcat_WS(*str, MINUS_DISP_CHR);
tmpDisp64 = -(int64_t)di->disp;
tmpDisp64 &= addrMask; /* Verify only for neg numbers. */
}
else {
chrcat_WS(*str, PLUS_DISP_CHR);
tmpDisp64 = di->disp;
}
str_int(str, tmpDisp64);
}
}
static uint8_t prefixTable[6][8] = { "", "LOCK ", "REPNZ ", "REPNZ ", "REP ", "REPZ " };
static unsigned int prefixSizesTable[6] = { 0, 5, 6, 6, 4, 5 };
static uint8_t suffixTable[10] = { 0, 'B', 'W', 0, 'D', 0, 0, 0, 'Q' };
/* WARNING: This function is written carefully to be able to work with same input and output buffer in-place! */
#ifdef SUPPORT_64BIT_OFFSET
_DLLEXPORT_ void distorm_format64(const _CodeInfo* ci, const _DInst* di, _DecodedInst* result)
#else
_DLLEXPORT_ void distorm_format32(const _CodeInfo* ci, const _DInst* di, _DecodedInst* result)
#endif
{
unsigned char* str;
int64_t tmpDisp64;
uint64_t addrMask = (uint64_t)-1;
const _WMnemonic* mnemonic;
int suffixSize = -1;
unsigned int i;
/* Set address mask, when default is for 64bits addresses. */
if (ci->features & DF_USE_ADDR_MASK) addrMask = ci->addrMask;
else {
if (ci->features & DF_MAXIMUM_ADDR32) addrMask = 0xffffffff;
else if (ci->features & DF_MAXIMUM_ADDR16) addrMask = 0xffff;
}
/* Gotta have full address for (di->addr - ci->codeOffset) to work in all modes. */
str_hex(&result->instructionHex, (const uint8_t*)&ci->code[(unsigned int)(di->addr - ci->codeOffset)], di->size);
if ((int)((int16_t)di->flags) == -1) {
/* In-place considerations: DI is RESULT. Deref fields first. */
unsigned int size = di->size;
unsigned int byte = di->imm.byte;
_OffsetType offset = di->addr & addrMask;
result->offset = offset;
result->size = size;
str = (unsigned char*)&result->mnemonic.p;
strcat_WS(str, "DB ", 4, 3);
str_int(&str, byte);
strfinalize_WS(result->mnemonic, str);
*(uint64_t*)&result->operands = 0; /* Clears length and the string at once. */
return; /* Skip to next instruction. */
}
str = (unsigned char*)&result->operands.p;
/* Special treatment for String (movs, cmps, stos, lods, scas) instructions. */
if ((di->opcode >= I_MOVS) && (di->opcode <= I_SCAS)) {
/*
* No operands are needed if the address size is the default one,
* and no segment is overridden, so add the suffix letter,
* to indicate size of operation and continue to next instruction.
*/
if ((SEGMENT_IS_DEFAULT_OR_NONE(di->segment)) && (FLAG_GET_ADDRSIZE(di->flags) == ci->dt)) {
suffixSize = di->ops[0].size / 8;
goto skipOperands;
}
suffixSize = 0; /* Marks it's a string instruction. */
}
for (i = 0; i < di->opsNo; i++) {
unsigned int type = di->ops[i].type;
if (i > 0) strcat_WS(str, ", ", 2, 2);
if (type == O_REG) {
strcat_WSR(&str, &_REGISTERS[di->ops[i].index]);
}
else if (type == O_IMM) {
/* If the instruction is 'push', show explicit size (except byte imm). */
if ((di->opcode == I_PUSH) && (di->ops[i].size != 8)) distorm_format_size(&str, di, i);
/* Special fix for negative sign extended immediates. */
if ((di->flags & FLAG_IMM_SIGNED) && (di->ops[i].size == 8) && (di->imm.sbyte < 0)) {
chrcat_WS(str, MINUS_DISP_CHR);
tmpDisp64 = -di->imm.sbyte;
str_int(&str, tmpDisp64);
}
else {
/* Notice signedness and size of the immediate. */
if (di->ops[i].size == 0x20) str_int(&str, di->imm.dword);
else str_int(&str, di->imm.qword);
}
}
else if (type == O_PC) {
#ifdef SUPPORT_64BIT_OFFSET
str_int(&str, (di->size + di->imm.sqword + di->addr) & addrMask);
#else
tmpDisp64 = ((_OffsetType)di->imm.sdword + di->addr + di->size) & (uint32_t)addrMask;
str_int(&str, tmpDisp64);
#endif
}
else if (type == O_DISP) {
distorm_format_size(&str, di, i);
chrcat_WS(str, OPEN_CHR);
if (!SEGMENT_IS_DEFAULT_OR_NONE(di->segment)) {
strcat_WSR(&str, &_REGISTERS[SEGMENT_GET_UNSAFE(di->segment)]);
chrcat_WS(str, SEG_OFF_CHR);
}
tmpDisp64 = di->disp & addrMask;
str_int(&str, tmpDisp64);
chrcat_WS(str, CLOSE_CHR);
}
else if (type == O_SMEM) {
int isDefault;
int segment;
distorm_format_size(&str, di, i);
chrcat_WS(str, OPEN_CHR);
segment = SEGMENT_GET(di->segment);
isDefault = SEGMENT_IS_DEFAULT(di->segment);
/*
* This is where we need to take special care for String instructions.
* If we got here, it means we need to explicitly show their operands.
* The problem with CMPS and MOVS is that they have two(!) memory operands.
* So we have to complement(!) them ourselves, since the isntruction structure supplies only the segment that can be overridden.
* And make the rest of the String operations explicit.
* We ignore default ES/DS in 64 bits.
* ["MOVS"], [OPT.REGI_EDI, OPT.REGI_ESI] -- DS can be overridden.
* ["CMPS"], [OPT.REGI_ESI, OPT.REGI_EDI] -- DS can be overriden.
*
* suffixSize == 0 was set above for string opcode already.
*/
if (suffixSize == 0) {
if (((di->opcode == I_MOVS) && (i == 0)) || ((di->opcode == I_CMPS) && (i == 1))) {
if (ci->dt != Decode64Bits) {
segment = R_ES;
isDefault = FALSE;
}
else isDefault = TRUE;
}
else if (isDefault && ((di->opcode == I_MOVS) || (di->opcode == I_CMPS))) {
if (ci->dt != Decode64Bits) {
segment = R_DS;
isDefault = FALSE;
}
}
}
if (!isDefault && (segment != R_NONE)) {
strcat_WSR(&str, &_REGISTERS[segment]);
chrcat_WS(str, SEG_OFF_CHR);
}
strcat_WSR(&str, &_REGISTERS[di->ops[i].index]);
distorm_format_signed_disp(&str, di, addrMask);
chrcat_WS(str, CLOSE_CHR);
}
else if (type == O_MEM) {
distorm_format_size(&str, di, i);
chrcat_WS(str, OPEN_CHR);
if (!SEGMENT_IS_DEFAULT_OR_NONE(di->segment)) {
strcat_WSR(&str, &_REGISTERS[SEGMENT_GET_UNSAFE(di->segment)]);
chrcat_WS(str, SEG_OFF_CHR);
}
if (di->base != R_NONE) {
strcat_WSR(&str, &_REGISTERS[di->base]);
chrcat_WS(str, PLUS_DISP_CHR);
}
strcat_WSR(&str, &_REGISTERS[di->ops[i].index]);
if (di->scale != 0) {
switch (di->scale)
{
case 2: strcat_WS(str, "*2", 2, 2); break;
case 4: strcat_WS(str, "*4", 2, 2); break;
case 8: strcat_WS(str, "*8", 2, 2); break;
}
}
distorm_format_signed_disp(&str, di, addrMask);
chrcat_WS(str, CLOSE_CHR);
}
else if (type == O_PTR) {
str_int(&str, di->imm.ptr.seg);
chrcat_WS(str, SEG_OFF_CHR);
str_int(&str, di->imm.ptr.off);
}
else if (type == O_IMM1) {
str_int(&str, di->imm.ex.i1);
}
else if (type == O_IMM2) {
str_int(&str, di->imm.ex.i2);
}
}
skipOperands:
/* Finalize the operands string. */
strfinalize_WS(result->operands, str);
/* Not used anymore.
if (di->flags & FLAG_HINT_TAKEN) strcat_WSN(str, " ;TAKEN");
else if (di->flags & FLAG_HINT_NOT_TAKEN) strcat_WSN(str, " ;NOT TAKEN");
*/
{
/* In-place considerations: DI is RESULT. Deref fields first. */
unsigned int opcode = di->opcode;
unsigned int prefix = FLAG_GET_PREFIX(di->flags);
unsigned int size = di->size;
_OffsetType offset = di->addr & addrMask;
str = (unsigned char*)&result->mnemonic.p;
mnemonic = (const _WMnemonic*)&_MNEMONICS[opcode];
if (prefix) {
/* REP prefix for CMPS and SCAS is really a REPZ. */
prefix += (opcode == I_CMPS);
prefix += (opcode == I_SCAS);
memcpy(str, &prefixTable[prefix][0], 8);
str += prefixSizesTable[prefix];
}
/*
* Always copy 16 bytes from the mnemonic, we have a sentinel padding so we can read past.
* This helps the compiler to remove the call to memcpy and therefore makes this copying much faster.
* The longest instruction is exactly 16 chars long, so we null terminate the string below.
*/
memcpy((int8_t*)str, mnemonic->p, 16);
str += mnemonic->length;
if (suffixSize > 0) {
*str++ = suffixTable[suffixSize];
}
strfinalize_WS(result->mnemonic, str);
result->offset = offset;
result->size = size;
}
}
#ifdef SUPPORT_64BIT_OFFSET
_DLLEXPORT_ _DecodeResult distorm_decode64(_OffsetType codeOffset, const unsigned char* code, int codeLen, _DecodeType dt, _DecodedInst result[], unsigned int maxInstructions, unsigned int* usedInstructionsCount)
#else
_DLLEXPORT_ _DecodeResult distorm_decode32(_OffsetType codeOffset, const unsigned char* code, int codeLen, _DecodeType dt, _DecodedInst result[], unsigned int maxInstructions, unsigned int* usedInstructionsCount)
#endif
{
_DecodeResult res;
_CodeInfo ci;
unsigned int i, instsCount;
*usedInstructionsCount = 0;
/* I use codeLen as a signed variable in order to ease detection of underflow... and besides - */
if (codeLen < 0) {
return DECRES_INPUTERR;
}
if ((unsigned)dt > (unsigned)Decode64Bits) {
return DECRES_INPUTERR;
}
/* Make sure there's at least one instruction in the result buffer. */
if ((code == NULL) || (result == NULL) || (maxInstructions == 0)) {
return DECRES_INPUTERR;
}
/*
* We have to format the result into text. But the interal decoder works with the new structure of _DInst.
* Therefore, we will pass the result array(!) from the caller and the interal decoder will fill it in with _DInst's.
* Then we will copy each result to a temporary structure, and use it to reformat that specific result.
*
* This is all done to save memory allocation and to work on the same result array in-place!!!
* It's a bit ugly, I have to admit, but worth it.
*/
ci.codeOffset = codeOffset;
ci.code = code;
ci.codeLen = codeLen;
ci.dt = dt;
ci.features = DF_USE_ADDR_MASK;
if (dt == Decode16Bits) ci.addrMask = 0xffff;
else if (dt == Decode32Bits) ci.addrMask = 0xffffffff;
else ci.addrMask = (_OffsetType)-1;
res = decode_internal(&ci, TRUE, (_DInst*)result, maxInstructions, usedInstructionsCount);
instsCount = *usedInstructionsCount;
for (i = 0; i < instsCount; i++) {
/* distorm_format is optimized and can work with same input/output buffer in-place. */
#ifdef SUPPORT_64BIT_OFFSET
distorm_format64(&ci, (_DInst*)&result[i], &result[i]);
#else
distorm_format32(&ci, (_DInst*)&result[i], &result[i]);
#endif
}
return res;
}
#endif /* DISTORM_LIGHT */
_DLLEXPORT_ unsigned int distorm_version(void)
{
return __DISTORMV__;
}

View File

@@ -0,0 +1,436 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#ifdef WIN32
#include <windows.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#endif
#include "funchook.h"
#include "funchook_internal.h"
#include "disasm.h"
#define FUNCHOOK_MAX_ERROR_MESSAGE_LEN 200
struct funchook {
int installed;
funchook_page_t *page_list;
char error_message[FUNCHOOK_MAX_ERROR_MESSAGE_LEN];
FILE *fp;
};
char funchook_debug_file[PATH_MAX];
const size_t funchook_size = sizeof(funchook_t);
static size_t num_entries_in_page;
static void funchook_logv(funchook_t *funchook, int set_error, const char *fmt, va_list ap);
static void funchook_log_end(funchook_t *funchook, const char *fmt, ...);
static funchook_t *funchook_create_internal(void);
static int funchook_prepare_internal(funchook_t *funchook, void **target_func, void *hook_func);
static void funchook_log_trampoline(funchook_t *funchook, const insn_t *trampoline, size_t trampoline_size);
static int funchook_install_internal(funchook_t *funchook, int flags);
static int funchook_uninstall_internal(funchook_t *funchook, int flags);
static int funchook_destroy_internal(funchook_t *funchook);
static int get_page(funchook_t *funchook, funchook_page_t **page_out, uint8_t *addr, ip_displacement_t *disp);
static void flush_instruction_cache(void *addr, size_t size)
{
#if defined __GNUC__
__builtin___clear_cache((char*)addr, (char*)addr + size);
#elif defined WIN32
FlushInstructionCache(GetCurrentProcess(), addr, size);
#else
#error unsupported OS or compiler
#endif
}
funchook_t *funchook_create(void)
{
funchook_t *funchook = NULL;
funchook_log(funchook, "Enter funchook_create()\n");
funchook = funchook_create_internal();
funchook_log_end(funchook, "Leave funchook_create() => %p\n", funchook);
return funchook;
}
int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func)
{
int rv;
void *orig_func;
funchook_log(funchook, "Enter funchook_prepare(%p, %p, %p)\n", funchook, target_func, hook_func);
orig_func = *target_func;
rv = funchook_prepare_internal(funchook, target_func, hook_func);
funchook_log_end(funchook, "Leave funchook_prepare(..., [%p->%p],...) => %d\n", orig_func, *target_func, rv);
return rv;
}
int funchook_install(funchook_t *funchook, int flags)
{
int rv;
funchook_log(funchook, "Enter funchook_install(%p, 0x%x)\n", funchook, flags);
rv = funchook_install_internal(funchook, flags);
funchook_log_end(funchook, "Leave funchook_install() => %d\n", rv);
return rv;
}
int funchook_uninstall(funchook_t *funchook, int flags)
{
int rv;
funchook_log(funchook, "Enter funchook_uninstall(%p, 0x%x)\n", funchook, flags);
rv = funchook_uninstall_internal(funchook, flags);
funchook_log_end(NULL, "Leave funchook_uninstall() => %d\n", rv);
return rv;
}
int funchook_destroy(funchook_t *funchook)
{
int rv;
funchook_log(funchook, "Enter funchook_destroy(%p)\n", funchook);
rv = funchook_destroy_internal(funchook);
funchook_log_end(rv == 0 ? NULL : funchook, "Leave funchook_destroy() => %d\n", rv);
return rv;
}
const char *funchook_error_message(const funchook_t *funchook)
{
return funchook->error_message;
}
int funchook_set_debug_file(const char *name)
{
if (name != NULL) {
strncpy(funchook_debug_file, name, sizeof(funchook_debug_file) - 1);
funchook_debug_file[sizeof(funchook_debug_file) - 1] = '\0';
} else {
funchook_debug_file[0] = '\0';
}
return 0;
}
void funchook_log(funchook_t *funchook, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
funchook_logv(funchook, 0, fmt, ap);
va_end(ap);
}
void funchook_set_error_message(funchook_t *funchook, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(funchook->error_message, FUNCHOOK_MAX_ERROR_MESSAGE_LEN, fmt, ap);
va_end(ap);
va_start(ap, fmt);
funchook_logv(funchook, 1, fmt, ap);
va_end(ap);
}
static void funchook_logv(funchook_t *funchook, int set_error, const char *fmt, va_list ap)
{
FILE *fp;
if (*funchook_debug_file == '\0') {
return;
}
if (funchook == NULL) {
fp = fopen(funchook_debug_file, "a");
} else if (funchook->fp == NULL) {
funchook->fp = fopen(funchook_debug_file, "a");
fp = funchook->fp;
} else {
fp = funchook->fp;
}
if (fp == NULL) {
return;
}
if (set_error) {
fputs(" ", fp);
}
vfprintf(fp, fmt, ap);
if (set_error) {
fputc('\n', fp);
}
if (funchook == NULL) {
fclose(fp);
} else {
fflush(fp);
}
}
static void funchook_log_end(funchook_t *funchook, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
funchook_logv(funchook, 0, fmt, ap);
va_end(ap);
if (funchook != NULL && funchook->fp != NULL) {
fclose(funchook->fp);
funchook->fp = NULL;
}
}
static funchook_t *funchook_create_internal(void)
{
funchook_t *funchook = funchook_alloc();
if (funchook == NULL) {
return NULL;
}
if (num_entries_in_page == 0) {
#ifdef FUNCHOOK_ENTRY_AT_PAGE_BOUNDARY
num_entries_in_page = 1;
#else
num_entries_in_page = (page_size - offsetof(funchook_page_t, entries)) / sizeof(funchook_entry_t);
#endif
funchook_log(funchook,
#ifdef WIN32
" allocation_unit=%"PRIuPTR"\n"
#endif
" page_size=%"PRIuPTR"\n"
" num_entries_in_page=%"PRIuPTR"\n",
#ifdef WIN32
allocation_unit,
#endif
page_size, num_entries_in_page);
}
return funchook;
}
static int funchook_prepare_internal(funchook_t *funchook, void **target_func, void *hook_func)
{
void *func = *target_func;
insn_t trampoline[TRAMPOLINE_SIZE];
size_t trampoline_size;
ip_displacement_t disp;
funchook_page_t *page = NULL;
funchook_entry_t *entry;
int rv;
if (funchook->installed) {
funchook_set_error_message(funchook, "Could not modify already-installed funchook handle.");
return FUNCHOOK_ERROR_ALREADY_INSTALLED;
}
func = funchook_resolve_func(funchook, func);
rv = funchook_make_trampoline(funchook, &disp, func, trampoline, &trampoline_size);
if (rv != 0) {
funchook_log(funchook, " failed to make trampoline\n");
return rv;
}
rv = get_page(funchook, &page, func, &disp);
if (rv != 0) {
funchook_log(funchook, " failed to get page\n");
return rv;
}
entry = &page->entries[page->used];
/* fill members */
entry->target_func = func;
entry->hook_func = hook_func;
memcpy(entry->trampoline, trampoline, TRAMPOLINE_BYTE_SIZE);
memcpy(entry->old_code, func, JUMP32_BYTE_SIZE);
funchook_fix_code(funchook, entry, &disp);
funchook_log_trampoline(funchook, entry->trampoline, trampoline_size);
#ifdef CPU_ARM64
int i;
for (i = 0; i < LITERAL_POOL_NUM; i++) {
size_t *addr = (size_t*)(entry->trampoline + LITERAL_POOL_OFFSET + i * 2);
if (*addr != 0) {
funchook_log(funchook, " "ADDR_FMT" : 0x%"PRIxPTR"\n", (size_t)addr, *addr);
}
}
#endif
/* Just in case though I think this is unnecessary. */
flush_instruction_cache(entry->trampoline, sizeof(entry->trampoline));
#ifdef CPU_64BIT
flush_instruction_cache(entry->transit, sizeof(entry->transit));
#endif
page->used++;
*target_func = (void*)entry->trampoline;
return 0;
}
static void funchook_log_trampoline(funchook_t *funchook, const insn_t *trampoline, size_t trampoline_size)
{
funchook_disasm_t disasm;
const funchook_insn_t *insn;
if (*funchook_debug_file == '\0') {
return;
}
funchook_log(funchook, " Trampoline Instructions:\n");
if (funchook_disasm_init(&disasm, funchook, trampoline, trampoline_size, (size_t)trampoline) != 0) {
int i;
funchook_log(funchook, " Failed to decode trampoline\n ");
for (i = 0; i < TRAMPOLINE_SIZE; i++) {
funchook_log(funchook, " %02x", trampoline[i]);
}
funchook_log(funchook, "\n");
return;
}
while (funchook_disasm_next(&disasm, &insn) == 0) {
funchook_disasm_log_instruction(&disasm, insn);
}
funchook_disasm_cleanup(&disasm);
}
static int funchook_install_internal(funchook_t *funchook, int flags)
{
funchook_page_t *page;
if (funchook->installed) {
return FUNCHOOK_ERROR_ALREADY_INSTALLED;
}
for (page = funchook->page_list; page != NULL; page = page->next) {
int rv = funchook_page_protect(funchook, page);
int i;
if (rv != 0) {
return rv;
}
for (i = 0; i < page->used; i++) {
funchook_entry_t *entry = &page->entries[i];
mem_state_t mstate;
int rv = funchook_unprotect_begin(funchook, &mstate, entry->target_func, JUMP32_BYTE_SIZE);
if (rv != 0) {
return rv;
}
memcpy(entry->target_func, entry->new_code, JUMP32_BYTE_SIZE);
rv = funchook_unprotect_end(funchook, &mstate);
if (rv != 0) {
return rv;
}
flush_instruction_cache(entry->target_func, JUMP32_BYTE_SIZE);
}
}
funchook->installed = 1;
return 0;
}
static int funchook_uninstall_internal(funchook_t *funchook, int flags)
{
funchook_page_t *page;
if (!funchook->installed) {
return FUNCHOOK_ERROR_NOT_INSTALLED;
}
for (page = funchook->page_list; page != NULL; page = page->next) {
int i;
for (i = 0; i < page->used; i++) {
funchook_entry_t *entry = &page->entries[i];
mem_state_t mstate;
int rv = funchook_unprotect_begin(funchook, &mstate, entry->target_func, JUMP32_BYTE_SIZE);
if (rv != 0) {
return rv;
}
memcpy(entry->target_func, entry->old_code, JUMP32_BYTE_SIZE);
rv = funchook_unprotect_end(funchook, &mstate);
if (rv != 0) {
return rv;
}
flush_instruction_cache(entry->target_func, JUMP32_BYTE_SIZE);
}
funchook_page_unprotect(funchook, page);
}
funchook->installed = 0;
return 0;
}
static int funchook_destroy_internal(funchook_t *funchook)
{
funchook_page_t *page, *page_next;
if (funchook == NULL) {
return -1;
}
if (funchook->installed) {
return FUNCHOOK_ERROR_ALREADY_INSTALLED;
}
for (page = funchook->page_list; page != NULL; page = page_next) {
page_next = page->next;
funchook_page_free(funchook, page);
}
if (funchook->fp != NULL) {
fclose(funchook->fp);
}
funchook_free(funchook);
return 0;
}
static int get_page(funchook_t *funchook, funchook_page_t **page_out, uint8_t *addr, ip_displacement_t *disp)
{
funchook_page_t *page;
int rv;
for (page = funchook->page_list; page != NULL; page = page->next) {
if (page->used < num_entries_in_page && funchook_page_avail(funchook, page, page->used, addr, disp)) {
/* Reuse allocated page. */
*page_out = page;
return 0;
}
}
rv = funchook_page_alloc(funchook, &page, addr, disp);
if (rv != 0) {
return rv;
}
page->used = 0;
page->next = funchook->page_list;
funchook->page_list = page;
*page_out = page;
return 0;
}

View File

@@ -0,0 +1,131 @@
/*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUNCHOOK_H
#define FUNCHOOK_H 1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Only functions with FUNCHOOK_EXPORT are visible from outside of funchook.dll
* or libfunchook.so. Others are invisible.
*/
#ifdef FUNCHOOK_EXPORTS
#if defined(WIN32)
#define FUNCHOOK_EXPORT __declspec(dllexport)
#elif defined(__GNUC__)
#define FUNCHOOK_EXPORT __attribute__((visibility("default")))
#endif
#endif /* FUNCHOOK_EXPORTS */
#ifndef FUNCHOOK_EXPORT
#define FUNCHOOK_EXPORT
#endif
typedef struct funchook funchook_t;
#define FUNCHOOK_ERROR_INTERNAL_ERROR -1
#define FUNCHOOK_ERROR_SUCCESS 0
#define FUNCHOOK_ERROR_OUT_OF_MEMORY 1
#define FUNCHOOK_ERROR_ALREADY_INSTALLED 2
#define FUNCHOOK_ERROR_DISASSEMBLY 3
#define FUNCHOOK_ERROR_IP_RELATIVE_OFFSET 4
#define FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE 5
#define FUNCHOOK_ERROR_FOUND_BACK_JUMP 6
#define FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS 7
#define FUNCHOOK_ERROR_MEMORY_ALLOCATION 8 /* memory allocation error */
#define FUNCHOOK_ERROR_MEMORY_FUNCTION 9 /* other memory function errors */
#define FUNCHOOK_ERROR_NOT_INSTALLED 10
#define FUNCHOOK_ERROR_NO_AVAILABLE_REGISTERS 11
/**
* Create a funchook handle
*
* @return allocated funchook handle. NULL when out-of-memory.
*/
FUNCHOOK_EXPORT funchook_t *funchook_create(void);
/**
* Prepare hooking
*
* @param funchook a funchook handle created by funchook_create()
* @param target_func function pointer to be intercepted. The pointer to trampoline function is set on success.
* @param hook_func function pointer which is called istead of target_func
* @return error code. one of FUNCHOOK_ERROR_*.
*/
FUNCHOOK_EXPORT int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func);
/**
* Install hooks prepared by funchook_prepare().
*
* @param funchook a funchook handle created by funchook_create()
* @param flags reserved. Set zero.
* @return error code. one of FUNCHOOK_ERROR_*.
*/
FUNCHOOK_EXPORT int funchook_install(funchook_t *funchook, int flags);
/**
* Uninstall hooks installed by funchook_install().
*
* @param funchook a funchook handle created by funchook_create()
* @param flags reserved. Set zero.
* @return error code. one of FUNCHOOK_ERROR_*.
*/
FUNCHOOK_EXPORT int funchook_uninstall(funchook_t *funchook, int flags);
/**
* Destroy a funchook handle
*
* @param funchook a funchook handle created by funchook_create()
* @return error code. one of FUNCHOOK_ERROR_*.
*/
FUNCHOOK_EXPORT int funchook_destroy(funchook_t *funchook);
/**
* Get error message
*
* @param funchook a funchook handle created by funchook_create()
* @return pointer to buffer containing error message
*/
FUNCHOOK_EXPORT const char *funchook_error_message(const funchook_t *funchook);
/**
* Set log file name to debug funchook itself.
*
* @param name log file name
* @return error code. one of FUNCHOOK_ERROR_*.
*/
FUNCHOOK_EXPORT int funchook_set_debug_file(const char *name);
#ifdef __cplusplus
} // extern "C"
#endif
#endif

View File

@@ -0,0 +1,155 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUNCHOOK_INTERNAL_H
#define FUNCHOOK_INTERNAL_H 1
#include "funchook.h"
#ifdef WIN32
#include <windows.h>
#endif
#if defined(_MSC_VER) && _MSC_VER < 1700
#ifdef _WIN64
#define PRIxPTR "I64"
#else
#define PRIxPTR ""
#endif
#else
#include <inttypes.h>
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef __GNUC__
#define __attribute__(arg)
#endif
#define ROUND_DOWN(num, unit) ((num) & ~((unit) - 1))
#define ROUND_UP(num, unit) (((num) + (unit) - 1) & ~((unit) - 1))
#if SIZEOF_VOID_P == 8
#define ADDR_FMT "%016" PRIxPTR
#else
#define ADDR_FMT "%08" PRIxPTR
#endif
#if defined _M_ARM64 || defined __aarch64__
#define CPU_ARM64
#define CPU_64BIT
#endif
#if defined _M_AMD64 || defined __x86_64__
#define CPU_X86_64
#define CPU_64BIT
#endif
#if defined _M_IX86 || defined __i686__ || defined __i386__
#define CPU_X86
#endif
#if defined(CPU_ARM64)
#include "funchook_arm64.h"
#endif
#if defined(CPU_X86) || defined(CPU_X86_64)
#include "funchook_x86.h"
#endif
#define JUMP32_BYTE_SIZE (JUMP32_SIZE * sizeof(insn_t))
#define TRAMPOLINE_BYTE_SIZE (TRAMPOLINE_SIZE * sizeof(insn_t))
/* This must be same with sysconf(_SC_PAGE_SIZE) on Unix
* or the dwPageSize member of the SYSTEM_INFO structure on Windows.
*/
#undef PAGE_SIZE
#define PAGE_SIZE 0x1000 /* 4k */
/* This must be same with the dwAllocationGranularity
* member of the SYSTEM_INFO structure on Windows.
*/
#define ALLOCATION_UNIT 0x10000 /* 64k */
typedef struct {
void *addr;
size_t size;
#ifdef WIN32
DWORD protect;
#endif
} mem_state_t;
typedef struct funchook_page {
#ifdef FUNCHOOK_ENTRY_AT_PAGE_BOUNDARY
funchook_entry_t entries[1]; /* This contains at most one. */
#endif
struct funchook_page *next;
uint16_t used;
#ifndef FUNCHOOK_ENTRY_AT_PAGE_BOUNDARY
funchook_entry_t entries[1]; /* This contains zero or more. */
#endif
} funchook_page_t;
/* Functions in funchook.c */
extern const size_t funchook_size;
extern char funchook_debug_file[];
void funchook_log(funchook_t *funchook, const char *fmt, ...) __attribute__((__format__ (__printf__, 2, 3)));
void funchook_set_error_message(funchook_t *funchook, const char *fmt, ...) __attribute__((__format__ (__printf__, 2, 3)));
/* Functions in funchook_linux.c & funchook_windows.c */
extern const size_t page_size;
extern const size_t allocation_unit; /* windows only */
funchook_t *funchook_alloc(void);
int funchook_free(funchook_t *funchook);
int funchook_page_alloc(funchook_t *funchook, funchook_page_t **page_out, uint8_t *func, ip_displacement_t *disp);
int funchook_page_free(funchook_t *funchook, funchook_page_t *page);
int funchook_page_protect(funchook_t *funchook, funchook_page_t *page);
int funchook_page_unprotect(funchook_t *funchook, funchook_page_t *page);
int funchook_unprotect_begin(funchook_t *funchook, mem_state_t *mstate, void *addr, size_t len);
int funchook_unprotect_end(funchook_t *funchook, const mem_state_t *mstate);
void *funchook_resolve_func(funchook_t *funchook, void *func);
/* Functions in funchook_{CPU_NAME}.c */
int funchook_make_trampoline(funchook_t *funchook, ip_displacement_t *disp, const insn_t *func, insn_t *trampoline, size_t *trampoline_size);
int funchook_fix_code(funchook_t *funchook, funchook_entry_t *entry, const ip_displacement_t *disp);
#ifdef CPU_X86_64
int funchook_page_avail(funchook_t *funchook, funchook_page_t *page, int idx, uint8_t *addr, ip_displacement_t *disp);
#else
#define funchook_page_avail(funchook, page, idx, addr, disp) (1)
#endif
#endif

View File

@@ -0,0 +1,424 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#define PSAPI_VERSION 1
#include <stdint.h>
#include <stdio.h>
#include <windows.h>
#include <psapi.h>
#include "funchook_internal.h"
typedef struct page_info {
struct page_info *next;
struct page_info *prev;
int num_used;
char used[1];
} page_list_t;
const size_t page_size = PAGE_SIZE; /* 4K */
const size_t allocation_unit = ALLOCATION_UNIT; /* 64K */
static size_t max_num_pages = ALLOCATION_UNIT / PAGE_SIZE - 1; /* 15 */
static page_list_t page_list = {
&page_list,
&page_list,
};
static const char *to_errmsg(DWORD err, char *buf, size_t bufsiz)
{
size_t len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
buf, (DWORD)bufsiz, NULL);
if (len == 0) {
return "Unknown Error";
}
if (len >= bufsiz) {
len = bufsiz - 1;
}
while (len > 0 && (buf[len - 1] == '\r' || buf[len - 1] == '\n')) {
len--;
}
buf[len] = '\0';
return buf;
}
funchook_t *funchook_alloc(void)
{
size_t size = ROUND_UP(funchook_size, page_size);
return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
}
int funchook_free(funchook_t *funchook)
{
VirtualFree(funchook, 0, MEM_RELEASE);
return 0;
}
/* Reserve 64K bytes (allocation_unit) and use the first
* 4K bytes (1 page) as the control page.
*/
static int alloc_page_info(funchook_t *funchook, page_list_t **pl_out, void *hint)
{
void *addr;
page_list_t *pl;
#ifdef CPU_64BIT
void *old_hint = hint;
while (1) {
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(hint, &mbi, sizeof(mbi)) == 0) {
DWORD err = GetLastError();
char errbuf[128];
funchook_set_error_message(funchook, "Failed to execute VirtualQuery (addr=%p, error=%lu(%s))",
hint,
err, to_errmsg(err, errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
funchook_log(funchook, " process map: %016I64x-%016I64x %s\n",
(size_t)mbi.BaseAddress, (size_t)mbi.BaseAddress + mbi.RegionSize,
(mbi.State == MEM_FREE) ? "free" : "used");
if (mbi.State == MEM_FREE) {
size_t addr = ROUND_UP((size_t)mbi.BaseAddress, allocation_unit);
intptr_t diff = addr - (size_t)mbi.BaseAddress;
if (diff >= 0) {
if (mbi.RegionSize - diff >= allocation_unit) {
hint = (void*)addr;
funchook_log(funchook, " change hint address from %p to %p\n",
old_hint, hint);
break;
}
}
}
hint = (void*)((size_t)mbi.BaseAddress + mbi.RegionSize);
}
#else
hint = NULL;
#endif
pl = VirtualAlloc(hint, allocation_unit, MEM_RESERVE, PAGE_NOACCESS);
if (pl == NULL) {
DWORD err = GetLastError();
char errbuf[128];
funchook_set_error_message(funchook, "Failed to reserve memory %p (hint=%p, size=%"PRIuPTR", errro=%lu(%s))",
pl, hint, allocation_unit,
err, to_errmsg(err, errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_ALLOCATION;
}
funchook_log(funchook, " reserve memory %p (hint=%p, size=%"PRIuPTR")\n", pl, hint, allocation_unit);
addr = VirtualAlloc(pl, page_size, MEM_COMMIT, PAGE_READWRITE);
if (addr == NULL) {
DWORD err = GetLastError();
char errbuf[128];
funchook_set_error_message(funchook, "Failed to commit memory %p for read-write (hint=%p, size=%"PRIuPTR", error=%lu(%s))",
addr, pl, page_size,
err, to_errmsg(err, errbuf, sizeof(errbuf)));
VirtualFree(pl, 0, MEM_RELEASE);
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
funchook_log(funchook, " commit memory %p for read-write (hint=%p, size=%"PRIuPTR")\n", addr, pl, page_size);
pl->next = page_list.next;
pl->prev = &page_list;
page_list.next->prev = pl;
page_list.next = pl;
*pl_out = pl;
return 0;
}
/*
* Get one page from page_list, commit it and return it.
*/
int funchook_page_alloc(funchook_t *funchook, funchook_page_t **page_out, uint8_t *func, ip_displacement_t *disp)
{
page_list_t *pl;
funchook_page_t *page = NULL;
size_t i;
for (pl = page_list.next; pl != &page_list; pl = pl->next) {
for (i = 0; i < max_num_pages; i++) {
if (!pl->used[i]) {
funchook_page_t *p = (funchook_page_t *)((size_t)pl + (i + 1) * page_size);
if (funchook_page_avail(funchook, p, 0, func, disp)) {
page = p;
goto exit_loop;
}
}
}
}
exit_loop:
if (page == NULL) {
/* no page_list is available. */
int rv = alloc_page_info(funchook, &pl, func);
if (rv != 0) {
return rv;
}
i = 0;
page = (funchook_page_t *)((size_t)pl + page_size);
}
if (VirtualAlloc(page, page_size, MEM_COMMIT, PAGE_READWRITE) == NULL) {
DWORD err = GetLastError();
char errbuf[128];
funchook_set_error_message(funchook, "Failed to commit page %p (base=%p(used=%d), idx=%"PRIuPTR", size=%"PRIuPTR", error=%lu(%s))",
page, pl, pl->num_used, i, page_size,
err, to_errmsg(err, errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
pl->used[i] = 1;
pl->num_used++;
funchook_log(funchook, " commit page %p (base=%p(used=%d), idx=%"PRIuPTR", size=%"PRIuPTR")\n",
page, pl, pl->num_used, i, page_size);
*page_out = page;
return 0;
}
/*
* Back to one page to page_list.
*/
int funchook_page_free(funchook_t *funchook, funchook_page_t *page)
{
page_list_t *pl = (page_list_t *)((size_t)page & ~(allocation_unit - 1));
size_t idx = ((size_t)page - (size_t)pl) / page_size - 1;
BOOL ok;
ok = VirtualFree(page, page_size, MEM_DECOMMIT);
if (!ok) {
DWORD err = GetLastError();
char errbuf[128];
funchook_set_error_message(funchook, "Failed to decommit page %p (base=%p(used=%d), idx=%"PRIuPTR", size=%"PRIuPTR", error=%lu(%s))",
page, pl, pl->num_used, idx, page_size,
err, to_errmsg(err, errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
funchook_log(funchook, " decommit page %p (base=%p(used=%d), idx=%"PRIuPTR", size=%"PRIuPTR")\n",
page, pl, pl->num_used, idx, page_size);
pl->num_used--;
pl->used[idx] = 0;
if (pl->num_used != 0) {
return 0;
}
/* all pages in this allocation unit are decommitted. delete this page_list */
pl->next->prev = pl->prev;
pl->prev->next = pl->next;
ok = VirtualFree(pl, 0, MEM_RELEASE);
if (!ok) {
DWORD err = GetLastError();
char errbuf[128];
funchook_set_error_message(funchook, "Failed to release memory %p (size=%"PRIuPTR", error=%lu(%s))",
pl, allocation_unit,
err, to_errmsg(err, errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
funchook_log(funchook, " release memory %p (size=%"PRIuPTR")\n",
pl, allocation_unit);
return 0;
}
int funchook_page_protect(funchook_t *funchook, funchook_page_t *page)
{
char errbuf[128];
DWORD oldprot;
BOOL ok = VirtualProtect(page, page_size, PAGE_EXECUTE_READ, &oldprot);
if (ok) {
funchook_log(funchook, " protect page %p (size=%"PRIuPTR", prot=read,exec)\n",
page, page_size);
return 0;
}
funchook_set_error_message(funchook, "Failed to protect page %p (size=%"PRIuPTR", prot=read,exec, error=%lu(%s))",
page, page_size,
GetLastError(), to_errmsg(GetLastError(), errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
int funchook_page_unprotect(funchook_t *funchook, funchook_page_t *page)
{
char errbuf[128];
DWORD oldprot;
BOOL ok = VirtualProtect(page, page_size, PAGE_READWRITE, &oldprot);
if (ok) {
funchook_log(funchook, " unprotect page %p (size=%"PRIuPTR", prot=read,write)\n",
page, page_size);
return 0;
}
funchook_set_error_message(funchook, "Failed to unprotect page %p (size=%"PRIuPTR", prot=read,write, error=%lu(%s))",
page, page_size,
GetLastError(), to_errmsg(GetLastError(), errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
int funchook_unprotect_begin(funchook_t *funchook, mem_state_t *mstate, void *start, size_t len)
{
char errbuf[128];
size_t saddr = ROUND_DOWN((size_t)start, page_size);
BOOL ok;
mstate->addr = (void*)saddr;
mstate->size = len + (size_t)start - saddr;
mstate->size = ROUND_UP(mstate->size, page_size);
ok = VirtualProtect(mstate->addr, mstate->size, PAGE_EXECUTE_READWRITE, &mstate->protect);
if (ok) {
funchook_log(funchook, " unprotect memory %p (size=%"PRIuPTR") <- %p (size=%"PRIuPTR")\n",
mstate->addr, mstate->size, start, len);
return 0;
}
funchook_set_error_message(funchook, "Failed to unprotect memory %p (size=%"PRIuPTR") <- %p (size=%"PRIuPTR", error=%lu(%s))",
mstate->addr, mstate->size, start, len,
GetLastError(), to_errmsg(GetLastError(), errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
int funchook_unprotect_end(funchook_t *funchook, const mem_state_t *mstate)
{
char errbuf[128];
DWORD oldprot;
BOOL ok = VirtualProtect(mstate->addr, mstate->size, mstate->protect, &oldprot);
if (ok) {
funchook_log(funchook, " protect memory %p (size=%"PRIuPTR")\n",
mstate->addr, mstate->size);
return 0;
}
funchook_set_error_message(funchook, "Failed to protect memory %p (size=%"PRIuPTR", error=%lu(%s))",
mstate->addr, mstate->size,
GetLastError(), to_errmsg(GetLastError(), errbuf, sizeof(errbuf)));
return FUNCHOOK_ERROR_MEMORY_FUNCTION;
}
static IMAGE_IMPORT_DESCRIPTOR *get_image_import_descriptor(HMODULE hMod, DWORD *cnt)
{
IMAGE_DOS_HEADER *doshdr;
IMAGE_NT_HEADERS *nthdr;
IMAGE_DATA_DIRECTORY *dir;
if (memcmp(hMod, "MZ", 2) != 0) {
return NULL;
}
doshdr = (IMAGE_DOS_HEADER*)hMod;
nthdr = (PIMAGE_NT_HEADERS)((size_t)hMod + doshdr->e_lfanew);
dir = &nthdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (dir->VirtualAddress == 0) {
return NULL;
}
*cnt = dir->Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
return (IMAGE_IMPORT_DESCRIPTOR*)((size_t)hMod + dir->VirtualAddress);
}
void *funchook_resolve_func(funchook_t *funchook, void *func)
{
char path[MAX_PATH];
HMODULE hMod;
BOOL ok;
IMAGE_IMPORT_DESCRIPTOR *desc_head, *desc;
insn_t *fn = (insn_t*)func;
size_t pos = 0;
DWORD cnt;
if (*funchook_debug_file != '\0') {
DWORD len = GetMappedFileNameA(GetCurrentProcess(), func, path, sizeof(path));
if (len > 0) {
funchook_log(funchook, " func %p is in %.*s\n", func, (int)len, path);
}
}
#if defined CPU_X86 || defined CPU_X86_64
if (fn[0] == 0xe9) {
fn = (fn + 5) + *(int*)(fn + 1);
funchook_log(funchook, " relative jump to %p\n", fn);
}
if (fn[0] == 0xff && fn[1] == 0x25) {
#ifdef CPU_X86_64
pos = (size_t)(fn + 6) + *(int*)(fn + 2);
#else
pos = *(size_t*)(fn + 2);
#endif
funchook_log(funchook, " indirect jump to addresss at %p\n", (void*)pos);
}
#endif
#if defined CPU_ARM64
#define ADRP_XIP0 0x90000010
#define ADRP_XIP0_MASK 0x9F00001F
#define ADRP_XIP0_IMMLO 0x60000000
#define ADRP_XIP0_IMMHI 0x00FFFFE0
#define LDR_XIP0 0xF9400210
#define LDR_XIP0_MASK 0xFFC003FF
#define LDR_XIP0_IMM12 0x003FFC00
#define BR_XIP0 0xD61F0200
if ((fn[0] & ADRP_XIP0_MASK) == ADRP_XIP0 &&
(fn[1] & LDR_XIP0_MASK) == LDR_XIP0 &&
fn[2] == BR_XIP0) {
// fn[0]: addrp xip0, immhi&immlo
// fn[1]: ldr xip0, [xip0,imm12]
// fn[2]: br xip0
size_t addr = (size_t)fn & ~((1 << 12) - 1);
size_t immhi = ((size_t)(fn[0] & ADRP_XIP0_IMMHI) >> 5) << (12 + 2);
size_t immlo = ((size_t)(fn[0] & ADRP_XIP0_IMMLO) >> 29) << 12;
size_t imm12 = ((size_t)(fn[1] & LDR_XIP0_IMM12) >> 10) << 3;
pos = addr + immhi + immlo + imm12;
// fprintf(stderr, "%016I64x: %08x %08x %08x : %I64x %I64x %I64x %I64x\n", (size_t)fn, fn[0], fn[1], fn[2], addr, immhi, immlo, imm12);
funchook_log(funchook, " indirect jump to addresss at %p\n", (void*)pos);
}
#endif
if (pos == 0) {
return func;
}
ok = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, func, &hMod);
if (!ok) {
return func;
}
desc_head = get_image_import_descriptor(hMod, &cnt);
if (desc_head == NULL) {
return func;
}
for (desc = desc_head; desc->Name != 0; desc++) {
IMAGE_THUNK_DATA *addr_thunk = (IMAGE_THUNK_DATA*)((char*)hMod + desc->FirstThunk);
while (addr_thunk->u1.Function != 0) {
if (pos == (size_t)&addr_thunk->u1.Function) {
func = (void*)addr_thunk->u1.Function;
if (*funchook_debug_file != '\0') {
DWORD len = GetMappedFileNameA(GetCurrentProcess(), func, path, sizeof(path));
if (len > 0) {
funchook_log(funchook, " -> func %p in %.*s\n", func, (int)len, path);
} else {
funchook_log(funchook, " -> func %p\n", func);
}
}
return func;
}
addr_thunk++;
}
}
return func;
}

View File

@@ -0,0 +1,385 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "funchook_internal.h"
#include "disasm.h"
typedef struct {
funchook_t *funchook;
ip_displacement_t *rip_disp;
const uint8_t *src;
const uint8_t *dst_base;
uint8_t *dst;
} make_trampoline_context_t;
#define NOP_INSTRUCTION 0x90
#if defined(__i386)
static int handle_x86_get_pc_thunk(make_trampoline_context_t *ctx, const funchook_insn_t *di);
static int handle_x86_get_pc_by_call_and_pop(make_trampoline_context_t *ctx, const funchook_insn_t *di);
#else
#define handle_x86_get_pc_thunk(ctx, di) (0)
#define handle_x86_get_pc_by_call_and_pop(ctx, di) (0)
#endif
static int handle_rip_relative(make_trampoline_context_t *ctx, const rip_relative_t *rel, size_t insn_size);
static int funchook_write_jump32(funchook_t *funchook, const uint8_t *src, const uint8_t *dst, uint8_t *out)
{
out[0] = 0xe9;
*(int*)(out + 1) = (int)(dst - (src + 5));
funchook_log(funchook, " Write jump32 0x"ADDR_FMT" -> 0x"ADDR_FMT"\n",
(size_t)src, (size_t)dst);
return 0;
}
#ifdef CPU_X86_64
static int funchook_write_jump64(funchook_t *funchook, uint8_t *src, const uint8_t *dst)
{
src[0] = 0xFF;
src[1] = 0x25;
src[2] = 0x00;
src[3] = 0x00;
src[4] = 0x00;
src[5] = 0x00;
*(const uint8_t**)(src + 6) = dst;
funchook_log(funchook, " Write jump64 0x"ADDR_FMT" -> 0x"ADDR_FMT"\n",
(size_t)src, (size_t)dst);
return 0;
}
static int funchook_within_32bit_relative(const uint8_t *src, const uint8_t *dst)
{
int64_t diff = (int64_t)(dst - src);
return (INT32_MIN <= diff && diff <= INT32_MAX);
}
static int funchook_jump32_avail(const uint8_t *src, const uint8_t *dst)
{
return funchook_within_32bit_relative(src + 5, dst);
}
#endif
int funchook_make_trampoline(funchook_t *funchook, ip_displacement_t *disp, const uint8_t *func, uint8_t *trampoline, size_t *trampoline_size)
{
make_trampoline_context_t ctx;
funchook_disasm_t disasm;
int rv;
const funchook_insn_t *insn;
memset(disp, 0, sizeof(*disp));
memset(trampoline, NOP_INSTRUCTION, TRAMPOLINE_SIZE);
*trampoline_size = 0;
ctx.funchook = funchook;
ctx.rip_disp = disp;
ctx.src = func;
ctx.dst_base = ctx.dst = trampoline;
rv = funchook_disasm_init(&disasm, funchook, func, MAX_INSN_CHECK_SIZE, (size_t)func);
if (rv != 0) {
return rv;
}
funchook_log(funchook, " Original Instructions:\n");
while ((rv = funchook_disasm_next(&disasm, &insn)) == 0) {
rip_relative_t rel_disp;
rip_relative_t rel_imm;
funchook_disasm_log_instruction(&disasm, insn);
if (handle_x86_get_pc_thunk(&ctx, insn)) {
;
} else if (handle_x86_get_pc_by_call_and_pop(&ctx, insn)) {
if ((rv = funchook_disasm_next(&disasm, &insn)) == 0) {
funchook_disasm_log_instruction(&disasm, insn);
}
} else {
size_t insn_size = funchook_insn_size(insn);
memcpy(ctx.dst, ctx.src, insn_size);
funchook_disasm_x86_rip_relative(&disasm, insn, &rel_disp, &rel_imm);
rv = handle_rip_relative(&ctx, &rel_disp, insn_size);
if (rv != 0) {
goto cleanup;
}
rv = handle_rip_relative(&ctx, &rel_imm, insn_size);
if (rv != 0) {
goto cleanup;
}
ctx.src += insn_size;
ctx.dst += insn_size;
}
if (ctx.src - func >= JUMP32_SIZE) {
ctx.dst[0] = 0xe9; /* unconditional jump */
disp->disp[0].dst_addr = ctx.src;
disp->disp[0].src_addr_offset = (ctx.dst - ctx.dst_base) + 5;
disp->disp[0].pos_offset = (ctx.dst - ctx.dst_base) + 1;
*trampoline_size = (ctx.dst - ctx.dst_base) + 5;
while ((rv = funchook_disasm_next(&disasm, &insn)) == 0) {
funchook_disasm_log_instruction(&disasm, insn);
funchook_disasm_x86_rip_relative(&disasm, insn, &rel_disp, &rel_imm);
if (func < rel_imm.addr && rel_imm.addr < func + JUMP32_SIZE) {
/* jump to the hot-patched region. */
funchook_set_error_message(funchook, "instruction jumping back to the hot-patched region was found");
rv = FUNCHOOK_ERROR_FOUND_BACK_JUMP;
goto cleanup;
}
}
break;
}
}
if (rv != FUNCHOOK_ERROR_END_OF_INSTRUCTION) {
goto cleanup;
}
rv = 0;
/* too short function. Check whether NOP instructions continue. */
while (ctx.src - func < JUMP32_SIZE) {
if (*ctx.src != NOP_INSTRUCTION) {
funchook_set_error_message(funchook, "Too short instructions");
rv = FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS;
goto cleanup;
}
ctx.src++;
}
cleanup:
funchook_disasm_cleanup(&disasm);
return rv;
}
#ifndef handle_x86_get_pc_thunk
/* special cases to handle "call __x86.get_pc_thunk.??"
* If the target instructions are "movl (%esp), %???; ret",
* use "movl addr + 5, %???" instead.
*/
static int handle_x86_get_pc_thunk(make_trampoline_context_t *ctx, const funchook_insn_t *insn)
{
uint32_t eip = 0;
const char *reg_name = NULL;
if (*ctx->src == 0xe8) {
uint32_t first_4_bytes = *(uint32_t*)funchook_insn_branch_address(insn);
eip = funchook_insn_address(insn) + 5;
switch (first_4_bytes) {
case 0xc324048b: /* 8b 04 24 c3: movl (%esp), %eax; ret */
reg_name = "ax";
*ctx->dst = 0xb8; /* movl addr + 5, %eax */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0xc3241c8b: /* 8b 1c 24 c3: movl (%esp), %ebx; ret */
reg_name = "bx";
*ctx->dst = 0xbb; /* movl addr + 5, %ebx */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0xc3240c8b: /* 8b 0c 24 c3: movl (%esp), %ecx; ret */
reg_name = "cx";
*ctx->dst = 0xb9; /* movl addr + 5, %ecx */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0xc324148b: /* 8b 14 24 c3: movl (%esp), %edx; ret */
reg_name = "dx";
*ctx->dst = 0xba; /* movl addr + 5, %edx */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0xc324348b: /* 8b 34 24 c3: movl (%esp), %esi; ret */
reg_name = "si";
*ctx->dst = 0xbe; /* movl addr + 5, %esi */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0xc3243c8b: /* 8b 3c 24 c3: movl (%esp), %edi; ret */
reg_name = "di";
*ctx->dst = 0xbf; /* movl addr + 5, %edi */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0xc3242c8b: /* 8b 2c 24 c3: movl (%esp), %ebp; ret */
reg_name = "bp";
*ctx->dst = 0xbd; /* movl addr + 5, %ebp */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
}
}
return 0;
fixed:
funchook_log(ctx->funchook, " use 'MOV E%c%c, 0x%x' instead of 'CALL __x86.get_pc_thunk.%s'\n",
reg_name[0] + 'A' - 'a',
reg_name[1] + 'A' - 'a',
eip, reg_name);
ctx->dst += 5;
ctx->src += 5;
return 1;
}
#endif
#ifndef handle_x86_get_pc_by_call_and_pop
static int handle_x86_get_pc_by_call_and_pop(make_trampoline_context_t *ctx, const funchook_insn_t *insn)
{
uint32_t eip = 0;
const char *reg_name = NULL;
if (*ctx->src == 0xe8 && *(uint32_t*)(ctx->src + 1) == 0) {
eip = funchook_insn_address(insn) + 5;
switch (*(ctx->src + 5)) {
case 0x58: /* pop %eax */
reg_name = "EAX";
*ctx->dst = 0xb8; /* movl addr + 5, %eax */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0x5b: /* pop %ebx */
reg_name = "EBX";
*ctx->dst = 0xbb; /* movl addr + 5, %ebx */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0x59: /* pop %ecx */
reg_name = "ECX";
*ctx->dst = 0xb9; /* movl addr + 5, %ecx */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0x5a: /* pop %edx */
reg_name = "EDX";
*ctx->dst = 0xba; /* movl addr + 5, %edx */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0x5e: /* pop %esi */
reg_name = "ESI";
*ctx->dst = 0xbe; /* movl addr + 5, %esi */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0x5f: /* pop %edi */
reg_name = "EDI";
*ctx->dst = 0xbf; /* movl addr + 5, %edi */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
case 0x5d: /* pop %ebp */
reg_name = "EBP";
*ctx->dst = 0xbd; /* movl addr + 5, %ebp */
*(uint32_t*)(ctx->dst + 1) = eip;
goto fixed;
}
}
return 0;
fixed:
funchook_log(ctx->funchook, " use 'MOV %s, 0x%x' instead of 'CALL 0x%x; POP %s'\n",
reg_name, eip, eip, reg_name);
ctx->dst += 5;
ctx->src += 6;
return 1;
}
#endif
/*
* Fix RIP-relative address in an instruction
*/
static int handle_rip_relative(make_trampoline_context_t *ctx, const rip_relative_t *rel, size_t insn_size)
{
if (rel->size == 32) {
if (*(int32_t*)(ctx->dst + rel->offset) != (uint32_t)rel->raddr) {
/* sanity check.
* reach here if opsiz and/or disp_offset are incorrectly
* estimated.
*/
funchook_set_error_message(ctx->funchook, "Invalid ip-relative offset %d. The value at the offset should be %08x but %08x",
rel->offset, (uint32_t)rel->raddr, *(int32_t*)(ctx->dst + rel->offset));
return FUNCHOOK_ERROR_IP_RELATIVE_OFFSET;
}
ctx->rip_disp->disp[1].dst_addr = rel->addr;
ctx->rip_disp->disp[1].src_addr_offset = (ctx->dst - ctx->dst_base) + insn_size;
ctx->rip_disp->disp[1].pos_offset = (ctx->dst - ctx->dst_base) + rel->offset;
} else if (rel->size != 0) {
funchook_set_error_message(ctx->funchook, "Could not fix ip-relative address. The size is not 32.");
return FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE;
}
return 0;
}
int funchook_fix_code(funchook_t *funchook, funchook_entry_t *entry, const ip_displacement_t *disp)
{
insn_t *src_addr;
uint32_t *offset_addr;
#ifdef CPU_X86_64
if (funchook_jump32_avail(entry->target_func, entry->hook_func)) {
funchook_write_jump32(funchook, entry->target_func, entry->hook_func, entry->new_code);
entry->transit[0] = 0;
} else {
funchook_write_jump32(funchook, entry->target_func, entry->transit, entry->new_code);
funchook_write_jump64(funchook, entry->transit, entry->hook_func);
}
#else
funchook_write_jump32(funchook, entry->target_func, entry->hook_func, entry->new_code);
#endif
/* fix rip-relative offsets */
src_addr = entry->trampoline + disp->disp[0].src_addr_offset;
offset_addr = (uint32_t*)(entry->trampoline + disp->disp[0].pos_offset);
*offset_addr = (uint32_t)(disp->disp[0].dst_addr - src_addr);
if (disp->disp[1].dst_addr != 0) {
src_addr = entry->trampoline + disp->disp[1].src_addr_offset;
offset_addr = (uint32_t*)(entry->trampoline + disp->disp[1].pos_offset);
*offset_addr = (uint32_t)(disp->disp[1].dst_addr - src_addr);
}
return 0;
}
#ifdef CPU_X86_64
int funchook_page_avail(funchook_t *funchook, funchook_page_t *page, int idx, uint8_t *addr, ip_displacement_t *disp)
{
funchook_entry_t *entry = &page->entries[idx];
const uint8_t *src;
const uint8_t *dst;
if (!funchook_jump32_avail(addr, entry->trampoline)) {
funchook_log(funchook, " could not jump function %p to trampoline %p\n", addr, entry->trampoline);
return 0;
}
src = entry->trampoline + disp->disp[0].src_addr_offset;
dst = disp->disp[0].dst_addr;
if (!funchook_within_32bit_relative(src, dst)) {
funchook_log(funchook, " could not jump trampoline %p to function %p\n",
src, dst);
return 0;
}
src = entry->trampoline + disp->disp[1].src_addr_offset;
dst = disp->disp[1].dst_addr;
if (dst != 0 && !funchook_within_32bit_relative(src, dst)) {
funchook_log(funchook, " could not make 32-bit relative address from %p to %p\n",
src, dst);
return 0;
}
return 1;
}
#endif

View File

@@ -0,0 +1,67 @@
/* -*- indent-tabs-mode: nil -*-
*
* This file is part of Funchook.
* https://github.com/kubo/funchook
*
* Funchook is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under
* terms of your choice, provided that you also meet, for each linked
* independent module, the terms and conditions of the license of that
* module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this
* exception to your version of the library, but you are not obliged to
* do so. If you do not wish to do so, delete this exception statement
* from your version.
*
* Funchook 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 Funchook. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FUNCHOOK_X86_H
#define FUNCHOOK_X86_H 1
#define MAX_INSN_LEN 16
#define MAX_INSN_CHECK_SIZE 256
#define JUMP32_SIZE 5
#ifdef CPU_X86_64
#define JUMP64_SIZE 14
#endif
#define TRAMPOLINE_SIZE (JUMP32_SIZE + (MAX_INSN_LEN - 1) + JUMP32_SIZE)
typedef uint8_t insn_t;
typedef struct funchook_entry {
void *target_func;
void *hook_func;
uint8_t trampoline[TRAMPOLINE_SIZE];
uint8_t old_code[JUMP32_SIZE];
uint8_t new_code[JUMP32_SIZE];
#ifdef CPU_X86_64
uint8_t transit[JUMP64_SIZE];
#endif
} funchook_entry_t;
typedef struct {
const insn_t *dst_addr;
intptr_t src_addr_offset;
intptr_t pos_offset;
} ip_displacement_entry_t;
typedef struct {
ip_displacement_entry_t disp[2];
} ip_displacement_t;
#endif

View File

@@ -0,0 +1,611 @@
/*
instructions.c
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#include "instructions.h"
#include "insts.h"
#include "prefix.h"
#include "x86defs.h"
#include "../include/mnemonics.h"
/* Helper macros to extract the type or index from an inst-node value. */
#define INST_NODE_INDEX(n) ((n) & 0x1fff)
#define INST_NODE_TYPE(n) ((n) >> 13)
/* Helper macro to read the actual flags that are associated with an inst-info. */
#define INST_INFO_FLAGS(ii) (FlagsTable[InstSharedInfoTable[(ii)->sharedIndex].flagsIndex])
/*
I use the trie data structure as I found it most fitting to a disassembler mechanism.
When you read a byte and have to decide if it's enough or you should read more bytes, 'till you get to the instruction information.
It's really fast because you POP the instruction info in top 3 iterates on the DB, because an instruction can be formed from two bytes + 3 bits reg from the ModR/M byte.
For a simple explanation, check this out:
http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Tree/Trie/
Further reading: http://en.wikipedia.org/wiki/Trie
The first GATE (array you read off a trie data structure), as I call them, is statically allocated by the compiler.
The second and third gates if used are being allocated dynamically by the instructions-insertion functionality.
How would such a thing look in memory, say we support 4 instructions with 3 bytes top (means 2 dynamically allocated gates).
->
|-------| 0,
|0| -------------------------------> |-------|
|1|RET | 1, |0|AND |
|2| -----> |-------| |1|XOR |
|3|INT3 | |0|PUSH | |2|OR | 0,3,
|-------| |1|POP | |3| --------->|-------|
|2|PUSHF| |-------| |0|ROR |
|3|POPF | |1|ROL |
|-------| |2|SHR |
|3|SHL |
|-------|
Of course, this is NOT how Intel instructions set looks!!!
but I just wanted to give a small demonstration.
Now the instructions you get from such a trie DB goes like this:
0, 0 - AND
0, 1 - XOR
0, 2 - OR
0, 3, 0, ROR
0, 3, 1, ROL
0, 3, 2, SHR
0, 3, 3, SHL
1 - RET
2, 0 - PUSH
2, 1 - POP
2, 2 - PUSHF
2, 3 - POPF
3 - INT3
I guess it's clear by now.
So now, if you read 0, you know that you have to enter the second gate(list) with the second byte specifying the index.
But if you read 1, you know that you go to an instruction (in this case, a RET).
That's why there's an Instruction-Node structure, it tells you whether you got to an instruction or another list
so you should keep on reading byte).
In Intel, you could go through 4 gates at top, because there are instructions which are built from 2 bytes and another smaller list
for the REG part, or newest SSE4 instructions which use 4 bytes for opcode.
Therefore, Intel's first gate is 256 long, and other gates are 256 (/72) or 8 long, yes, it costs pretty much a lot of memory
for non-used defined instructions, but I think that it still rocks.
*/
/*
* A helper function to look up the correct inst-info structure.
* It does one fetch from the index-table, and then another to get the inst-info.
* Note that it takes care about basic inst-info or inst-info-ex.
* The caller should worry about boundary checks and whether it accesses a last-level table.
*/
static _InstInfo* inst_get_info(_InstNode in, int index)
{
int instIndex = 0;
in = InstructionsTree[INST_NODE_INDEX(in) + index];
if (in == INT_NOTEXISTS) return NULL;
instIndex = INST_NODE_INDEX(in);
return INST_NODE_TYPE(in) == INT_INFO ? &InstInfos[instIndex] : (_InstInfo*)&InstInfosEx[instIndex];
}
/*
* This function is responsible to return the instruction information of the first found in code.
* It returns the _InstInfo of the found instruction, otherwise NULL.
* code should point to the ModR/M byte upon exit (if used), or after the instruction binary code itself.
* This function is NOT decoding-type dependant, it is up to the caller to see whether the instruction is valid.
* Get the instruction info, using a Trie data structure.
*
* Sometimes normal prefixes become mandatory prefixes, which means they are now part of the instruction opcode bytes.
* This is a bit tricky now,
* if the first byte is a REP (F3) prefix, we will have to give a chance to an SSE instruction.
* If an instruction doesn't exist, we will make it as a prefix and re-locateinst.
* A case such that a REP prefix is being changed into an instruction byte and also an SSE instruction will not be found can't happen,
* simply because there are no collisions between string instruction and SSE instructions (they are escaped).
* As for S/SSE2/3, check for F2 and 66 as well.
* In 64 bits, we have to make sure that we will skip the REX prefix, if it exists.
* There's a specific case, where a 66 is mandatory but it was dropped because REG.W was used,
* but it doesn't behave as an operand size prefix but as a mandatory, so we will have to take it into account.
* For example (64 bits decoding mode):
* 66 98 CBW
* 48 98 CDQE
* 66 48 98: db 0x66; CDQE
* Shows that operand size is dropped.
* Now, it's a mandatory prefix and NOT an operand size one.
* 66480f2dc0 db 0x48; CVTPD2PI XMM0, XMM0
* Although this instruction doesn't require a REX.W, it just shows, that even if it did - it doesn't matter.
* REX.W is dropped because it's not required, but the decode function disabled the operand size even so.
*/
static _InstInfo* inst_lookup_prefixed(_InstNode in, _PrefixState* ps)
{
int checkOpSize = FALSE;
int index = 0;
_InstInfo* ii = NULL;
/* Check prefixes of current decoded instruction (None, 0x66, 0xf3, 0xf2). */
switch (ps->decodedPrefixes & (INST_PRE_OP_SIZE | INST_PRE_REPS))
{
case 0:
/* Non-prefixed, index = 0. */
index = 0;
break;
case INST_PRE_OP_SIZE:
/* 0x66, index = 1. */
index = 1;
/* Mark that we used it as a mandatory prefix. */
ps->isOpSizeMandatory = TRUE;
ps->decodedPrefixes &= ~INST_PRE_OP_SIZE;
break;
case INST_PRE_REP:
/* 0xf3, index = 2. */
index = 2;
ps->decodedPrefixes &= ~INST_PRE_REP;
break;
case INST_PRE_REPNZ:
/* 0xf2, index = 3. */
index = 3;
ps->decodedPrefixes &= ~INST_PRE_REPNZ;
break;
default:
/*
* Now we got a problem, since there are a few mandatory prefixes at once.
* There is only one case when it's ok, when the operand size prefix is for real (not mandatory).
* Otherwise we will have to return NULL, since the instruction is illegal.
* Therefore we will start with REPNZ and REP prefixes,
* try to get the instruction and only then check for the operand size prefix.
*/
/* If both REPNZ and REP are together, it's illegal for sure. */
if ((ps->decodedPrefixes & INST_PRE_REPS) == INST_PRE_REPS) return NULL;
/* Now we know it's either REPNZ+OPSIZE or REP+OPSIZE, so examine the instruction. */
if (ps->decodedPrefixes & INST_PRE_REPNZ) {
index = 3;
ps->decodedPrefixes &= ~INST_PRE_REPNZ;
}
else if (ps->decodedPrefixes & INST_PRE_REP) {
index = 2;
ps->decodedPrefixes &= ~INST_PRE_REP;
}
/* Mark to verify the operand-size prefix of the fetched instruction below. */
checkOpSize = TRUE;
break;
}
/* Fetch the inst-info from the index. */
ii = inst_get_info(in, index);
if (checkOpSize) {
/* If the instruction doesn't support operand size prefix, then it's illegal. */
if ((ii == NULL) || (~INST_INFO_FLAGS(ii) & INST_PRE_OP_SIZE)) return NULL;
}
/* If there was a prefix, but the instruction wasn't found. Try to fall back to use the normal instruction. */
if (ii == NULL) ii = inst_get_info(in, 0);
return ii;
}
/* A helper function to look up special VEX instructions.
* See if it's a MOD based instruction and fix index if required.
* Only after a first lookup (that was done by caller), we can tell if we need to fix the index.
* Because these are coupled instructions
* (which means that the base instruction hints about the other instruction).
* Note that caller should check if it's a MOD dependent instruction before getting in here.
*/
static _InstInfo* inst_vex_mod_lookup(_CodeInfo* ci, _InstNode in, _InstInfo* ii, unsigned int index)
{
/* Advance to read the MOD from ModRM byte. */
ci->code += 1;
ci->codeLen -= 1;
if (ci->codeLen < 0) return NULL;
if (*ci->code < INST_DIVIDED_MODRM) {
/* MOD is not 11, therefore change the index to 8 - 12 range in the prefixed table. */
index += 4;
/* Make a second lookup for this special instruction. */
return inst_get_info(in, index);
}
/* Return the original one, in case we didn't find a suited instruction. */
return ii;
}
static _InstInfo* inst_vex_lookup(_CodeInfo* ci, _PrefixState* ps)
{
_InstNode in = 0;
unsigned int pp = 0, start = 0;
unsigned int index = 4; /* VEX instructions start at index 4 in the Prefixed table. */
uint8_t vex = *ps->vexPos, vex2 = 0, v = 0;
int instType = 0, instIndex = 0;
/* The VEX instruction will #ud if any of 66, f0, f2, f3, REX prefixes precede. */
_iflags illegal = (INST_PRE_OP_SIZE | INST_PRE_LOCK | INST_PRE_REP | INST_PRE_REPNZ | INST_PRE_REX);
if ((ps->decodedPrefixes & illegal) != 0) return NULL;
/* Read the some fields from the VEX prefix we need to extract the instruction. */
if (ps->prefixExtType == PET_VEX2BYTES) {
ps->vexV = v = (~vex >> 3) & 0xf;
pp = vex & 3;
/* Implied leading 0x0f byte by default for 2 bytes VEX prefix. */
start = 1;
}
else { /* PET_VEX3BYTES */
start = vex & 0x1f;
vex2 = *(ps->vexPos + 1);
ps->vexV = v = (~vex2 >> 3) & 0xf;
pp = vex2 & 3;
}
/* start can be either 1 (0x0f), 2 (0x0f, 0x038) or 3 (0x0f, 0x3a), otherwise it's illegal. */
switch (start)
{
case 1: in = Table_0F; break;
case 2: in = Table_0F_38; break;
case 3: in = Table_0F_3A; break;
default: return NULL;
}
/* pp is actually the implied mandatory prefix, apply it to the index. */
index += pp; /* (None, 0x66, 0xf3, 0xf2) */
/* Read a byte from the stream. */
ci->codeLen -= 1;
if (ci->codeLen < 0) return NULL;
in = InstructionsTree[INST_NODE_INDEX(in) + *ci->code];
if (in == INT_NOTEXISTS) return NULL;
instType = INST_NODE_TYPE(in);
instIndex = INST_NODE_INDEX(in);
/*
* If we started with 0f38 or 0f3a so it's a prefixed table,
* therefore it's surely a VEXed instruction (because of a high index).
* However, starting with 0f, could also lead immediately to a prefixed table for some bytes.
* it might return NULL, if the index is invalid.
*/
if (instType == INT_LIST_PREFIXED) {
_InstInfo* ii = inst_get_info(in, index);
/* See if the instruction is dependent on MOD. */
if ((ii != NULL) && (((_InstInfoEx*)ii)->flagsEx & INST_MODRR_BASED)) {
ii = inst_vex_mod_lookup(ci, in, ii, index);
}
return ii;
}
/*
* If we reached here, obviously we started with 0f. VEXed instructions must be nodes of a prefixed table.
* But since we found an instruction (or divided one), just return NULL.
* They cannot lead to a VEXed instruction.
*/
if ((instType == INT_INFO) || (instType == INT_INFOEX) || (instType == INT_LIST_DIVIDED)) return NULL;
/* Now we are left with handling either GROUP or FULL tables, therefore we will read another byte from the stream. */
ci->code += 1;
ci->codeLen -= 1;
if (ci->codeLen < 0) return NULL;
if (instType == INT_LIST_GROUP) {
in = InstructionsTree[instIndex + ((*ci->code >> 3) & 7)];
/* Continue below to check prefixed table. */
}
else if (instType == INT_LIST_FULL) {
in = InstructionsTree[instIndex + *ci->code];
/* Continue below to check prefixed table. */
}
/* Now that we got to the last table in the trie, check for a prefixed table. */
if (INST_NODE_TYPE(in) == INT_LIST_PREFIXED) {
_InstInfo* ii = inst_get_info(in, index);
/* See if the instruction is dependent on MOD. */
if ((ii != NULL) && (((_InstInfoEx*)ii)->flagsEx & INST_MODRR_BASED)) {
ii = inst_vex_mod_lookup(ci, in, ii, index);
}
return ii;
}
/* No VEXed instruction was found. */
return NULL;
}
_InstInfo* inst_lookup(_CodeInfo* ci, _PrefixState* ps, int* isPrefixed)
{
unsigned int tmpIndex0, tmpIndex1, tmpIndex2;
int instType;
_InstNode in;
_InstInfo* ii = NULL;
int isWaitIncluded = FALSE;
/* Always safe to read first byte codeLen > 0. */
tmpIndex0 = *ci->code;
if (prefixes_is_valid((unsigned char)tmpIndex0, ci->dt)) {
*isPrefixed = TRUE;
prefixes_decode(ci, ps);
if (ci->codeLen < 1) return NULL; /* No more bytes for opcode, halt. */
tmpIndex0 = *ci->code; /* Reload. */
/* If there are too many prefixes, it will be checked later in decode_inst. */
/* See whether we have to handle a VEX prefixed instruction. */
if (ps->decodedPrefixes & INST_PRE_VEX) {
ii = inst_vex_lookup(ci, ps);
if (ii != NULL) {
/* Make sure that VEX.L exists when forced. */
if ((((_InstInfoEx*)ii)->flagsEx & INST_FORCE_VEXL) && (~ps->vrex & PREFIX_EX_L)) return NULL;
/* If the instruction doesn't use VEX.vvvv it must be zero. */
if ((((_InstInfoEx*)ii)->flagsEx & INST_VEX_V_UNUSED) && ps->vexV) return NULL;
}
return ii;
}
}
/* Account first byte, we know it's safe to read. */
ci->codeLen -= 1;
/* Check for special 0x9b, WAIT instruction, which can be part of some instructions(x87). */
if (tmpIndex0 == INST_WAIT_INDEX) {
/* Only OCST_1dBYTES get a chance to include this byte as part of the opcode. */
isWaitIncluded = TRUE;
/* Ignore all prefixes, since they are useless and operate on the WAIT instruction itself. */
prefixes_ignore_all(ps);
/* Move to next code byte as a new whole instruction. */
ci->code += 1;
ci->codeLen -= 1;
if (ci->codeLen < 0) return NULL; /* Faster to return NULL, it will be detected as WAIT later anyway. */
/* Since we got a WAIT prefix, we re-read the first byte. */
tmpIndex0 = *ci->code;
}
/* Walk first byte in InstructionsTree root. */
in = InstructionsTree[tmpIndex0];
if ((uint32_t)in == INT_NOTEXISTS) return NULL;
instType = INST_NODE_TYPE(in);
/* Single byte instruction (OCST_1BYTE). */
if ((instType < INT_INFOS) && (!isWaitIncluded)) {
/* Some single byte instructions need extra treatment. */
if (instType == INT_INFO_TREAT) {
if (tmpIndex0 == INST_NOP_INDEX) { /* Nopnopnop */
/* Check for Pause, since it's prefixed with 0xf3, which is not a real mandatory prefix. */
if (ps->decodedPrefixes & INST_PRE_REP) {
/* Flag this prefix as used. */
ps->usedPrefixes |= INST_PRE_REP;
return &II_PAUSE;
}
/*
* Treat NOP/XCHG specially.
* If we're not in 64bits restore XCHG to NOP, since in the DB it's XCHG.
* Else if we're in 64bits examine REX, if exists, and decide which instruction should go to output.
* 48 90 XCHG RAX, RAX is a true NOP (eat REX in this case because it's valid).
* 90 XCHG EAX, EAX is a true NOP (and not high dword of RAX = 0 although it should be a 32 bits operation).
* Note that if the REX.B is used, then the register is not RAX anymore but R8, which means it's not a NOP.
*/
if (ps->vrex & PREFIX_EX_W) ps->usedPrefixes |= INST_PRE_REX;
if ((ci->dt != Decode64Bits) || (~ps->vrex & PREFIX_EX_B)) return &II_NOP;
}
else if (tmpIndex0 == INST_LEA_INDEX) {
/* Ignore segment override prefixes for LEA instruction. */
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
/* Update unused mask for ignoring segment prefix. */
prefixes_ignore(ps, PFXIDX_SEG);
}
else if (tmpIndex0 == INST_ARPL_INDEX) {
/*
* ARPL/MOVSXD share the same opcode, and both have different operands and mnemonics, of course.
* Practically, I couldn't come up with a comfortable way to merge the operands' types of ARPL/MOVSXD.
* And since the DB can't be patched dynamically, because the DB has to be multi-threaded compliant,
* I have no choice but to check for ARPL/MOVSXD right here - "right about now, the funk soul brother, check it out now, the funk soul brother...", fatboy slim
*/
if (ci->dt == Decode64Bits) {
return &II_MOVSXD;
} /* else ARPL will be returned because its defined in the DB already. */
}
}
/*
* Return the 1 byte instruction we found.
* We can have three node types here: infoex, info_treat and info.
* The latter two are really the same basic structure.
*/
return instType == INT_INFOEX ? (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in)] : &InstInfos[INST_NODE_INDEX(in)];
}
/* Read second byte, still doesn't mean all of its bits are used (I.E: ModRM). */
ci->code += 1;
ci->codeLen -= 1;
if (ci->codeLen < 0) return NULL;
tmpIndex1 = *ci->code;
/* Try single byte instruction + reg bits (OCST_13BYTES). */
if ((instType == INT_LIST_GROUP) && (!isWaitIncluded)) return inst_get_info(in, (tmpIndex1 >> 3) & 7);
/* Try single byte instruction + reg byte OR one whole byte (OCST_1dBYTES). */
if (instType == INT_LIST_DIVIDED) {
/* Checking for inst by REG bits is higher priority if it's found not to be divided instruction. */
{
_InstNode in2 = InstructionsTree[INST_NODE_INDEX(in) + ((tmpIndex1 >> 3) & 7)];
/*
* Do NOT check for NULL here, since we do a bit of a guess work,
* hence we don't override 'in', cause we might still need it.
*/
instType = INST_NODE_TYPE(in2);
if (instType == INT_INFO) ii = &InstInfos[INST_NODE_INDEX(in2)];
else if (instType == INT_INFOEX) ii = (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in2)];
if ((ii != NULL) && (INST_INFO_FLAGS(ii) & INST_NOT_DIVIDED)) return ii;
/* ii is reset below. */
}
/* Continue normally because of wait prefix. */
if (tmpIndex1 < INST_DIVIDED_MODRM) {
/* An instruction which requires a ModR/M byte. Thus it's 1.3 bytes long instruction. */
tmpIndex1 = (tmpIndex1 >> 3) & 7; /* Isolate the 3 REG/OPCODE bits. */
}
else { /* Normal 2 bytes instruction. */
/*
* Divided instructions can't be in the range of 0x8-0xc0.
* That's because 0-8 are used for 3 bits group.
* And 0xc0-0xff are used for not-divided instruction.
* So the in between range is omitted, thus saving some more place in the tables.
*/
tmpIndex1 -= INST_DIVIDED_MODRM - 8;
}
in = InstructionsTree[INST_NODE_INDEX(in) + tmpIndex1];
if (in == INT_NOTEXISTS) return NULL;
instType = INST_NODE_TYPE(in);
if (instType < INT_INFOS) {
/* If the instruction doesn't support the wait (marked as opsize) as part of the opcode, it's illegal. */
ii = instType == INT_INFO ? &InstInfos[INST_NODE_INDEX(in)] : (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in)];
if ((~INST_INFO_FLAGS(ii) & INST_PRE_OP_SIZE) && (isWaitIncluded)) return NULL;
return ii;
}
/*
* If we got here the instruction can support the wait prefix, so see if it was part of the stream.
* Examine prefixed table, specially used for 0x9b, since it's optional.
* No Wait: index = 0.
* Wait Exists, index = 1.
*/
return inst_get_info(in, isWaitIncluded);
}
/* Don't allow to continue if WAIT is part of the opcode, because there are no instructions that include it. */
if (isWaitIncluded) return NULL;
/* Try 2 bytes long instruction (doesn't include ModRM byte). */
if (instType == INT_LIST_FULL) {
in = InstructionsTree[INST_NODE_INDEX(in) + tmpIndex1];
if (in == INT_NOTEXISTS) return NULL;
instType = INST_NODE_TYPE(in);
/* This is where we check if we just read two escape bytes in a row, which means it is a 3DNow! instruction. */
if ((tmpIndex0 == _3DNOW_ESCAPE_BYTE) && (tmpIndex1 == _3DNOW_ESCAPE_BYTE)) return &II_3DNOW;
/* 2 bytes instruction (OCST_2BYTES). */
if (instType < INT_INFOS)
return instType == INT_INFO ? &InstInfos[INST_NODE_INDEX(in)] : (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in)];
/*
* 2 bytes + mandatory prefix.
* Mandatory prefixes can be anywhere in the prefixes.
* There cannot be more than one mandatory prefix, unless it's a normal operand size prefix.
*/
if (instType == INT_LIST_PREFIXED) return inst_lookup_prefixed(in, ps);
}
/* Read third byte, still doesn't mean all of its bits are used (I.E: ModRM). */
ci->code += 1;
ci->codeLen -= 1;
if (ci->codeLen < 0) return NULL;
tmpIndex2 = *ci->code;
/* Try 2 bytes + reg instruction (OCST_23BYTES). */
if (instType == INT_LIST_GROUP) {
in = InstructionsTree[INST_NODE_INDEX(in) + ((tmpIndex2 >> 3) & 7)];
if (in == INT_NOTEXISTS) return NULL;
instType = INST_NODE_TYPE(in);
if (instType < INT_INFOS)
return instType == INT_INFO ? &InstInfos[INST_NODE_INDEX(in)] : (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in)];
/* It has to be a prefixed table then. */
ii = inst_lookup_prefixed(in, ps);
/* RDRAND and VMPTRLD share same 2.3 bytes opcode, and alternate on the MOD bits. See insts.h for more info. */
if ((ii != NULL) && (ii->opcodeId == I_VMPTRLD) && (tmpIndex1 >= INST_DIVIDED_MODRM)) return &II_RDRAND;
return ii;
}
/* Try 2 bytes + divided range (OCST_2dBYTES). */
if (instType == INT_LIST_DIVIDED) {
_InstNode in2 = InstructionsTree[INST_NODE_INDEX(in) + ((tmpIndex2 >> 3) & 7)];
/*
* Do NOT check for NULL here, since we do a bit of a guess work,
* hence we don't override 'in', cause we might still need it.
*/
instType = INST_NODE_TYPE(in2);
if (instType == INT_INFO) ii = &InstInfos[INST_NODE_INDEX(in2)];
else if (instType == INT_INFOEX) ii = (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in2)];
/*
* OCST_2dBYTES is complex, because there are a few instructions which are not divided in some special cases.
* If the instruction wasn't divided (but still it must be a 2.3 because we are in divided category)
* or it was an official 2.3 (because its index was less than 0xc0) -
* Then it means the instruction should be using the REG bits, otherwise give a chance to range 0xc0-0xff.
*/
/* If we found an instruction only by its REG bits, AND it is not divided, then return it. */
if ((ii != NULL) && (INST_INFO_FLAGS(ii) & INST_NOT_DIVIDED)) return ii;
/* Otherwise, if the range is above 0xc0, try the special divided range (range 0x8-0xc0 is omitted). */
if (tmpIndex2 >= INST_DIVIDED_MODRM) return inst_get_info(in, tmpIndex2 - INST_DIVIDED_MODRM + 8);
/* It might be that we got here without touching ii in the above if statements, then it becomes an invalid instruction prolly. */
return ii;
}
/* Try 3 full bytes (OCST_3BYTES - no ModRM byte). */
if (instType == INT_LIST_FULL) {
/* OCST_3BYTES. */
in = InstructionsTree[INST_NODE_INDEX(in) + tmpIndex2];
if (in == INT_NOTEXISTS) return NULL;
instType = INST_NODE_TYPE(in);
if (instType < INT_INFOS)
return instType == INT_INFO ? &InstInfos[INST_NODE_INDEX(in)] : (_InstInfo*)&InstInfosEx[INST_NODE_INDEX(in)];
if (instType == INT_LIST_PREFIXED) return inst_lookup_prefixed(in, ps);
}
/* Kahtchinggg, damn. */
return NULL;
}
/*
* 3DNow! instruction handling:
* This is used when we encounter a 3DNow! instruction.
* We can't really locate a 3DNow! instruction before we see two escaped bytes,
* 0x0f, 0x0f. Then we have to extract operands which are, dest=mmx register, src=mmx register or quadword indirection.
* When we are finished with the extraction of operands we can resume to locate the instruction by reading another byte
* which tells us which 3DNow instruction we really tracked down...
* So in order to tell the extract operands function which operands the 3DNow! instruction require, we need to set up some
* generic instruction info for 3DNow! instructions.
* In the inst_lookup itself, when we read an OCST_3BYTES which the two first bytes are 0x0f and 0x0f.
* we will return this special generic II for the specific operands we are interested in (MM, MM64).
* Then after extracting the operand, we'll call a completion routine for locating the instruction
* which will be called only for 3DNow! instructions, distinguished by a flag, and it will read the last byte of the 3 bytes.
*
* The id of this opcode should not be used, the following function should change it anyway.
*/
_InstInfo* inst_lookup_3dnow(_CodeInfo* ci)
{
/* Start off from the two escape bytes gates... which is 3DNow! table.*/
_InstNode in = Table_0F_0F;
int index;
/* Make sure we can read a byte off the stream. */
if (ci->codeLen < 1) return NULL;
index = *ci->code;
ci->codeLen -= 1;
ci->code += 1;
return inst_get_info(in, index);
}

View File

@@ -0,0 +1,479 @@
/*
instructions.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef INSTRUCTIONS_H
#define INSTRUCTIONS_H
#include "config.h"
#include "prefix.h"
/*
* Operand type possibilities:
* Note "_FULL" suffix indicates to decode the operand as 16 bits or 32 bits depends on DecodeType -
* actually, it depends on the decoding mode, unless there's an operand/address size prefix.
* For example, the code: 33 c0 could be decoded/executed as XOR AX, AX or XOR EAX, EAX.
*/
typedef enum OpType {
/* No operand is set */
OT_NONE = 0,
/* Read a byte(8 bits) immediate */
OT_IMM8,
/* Force a read of a word(16 bits) immediate, used by ret only */
OT_IMM16,
/* Read a word/dword immediate */
OT_IMM_FULL,
/* Read a double-word(32 bits) immediate */
OT_IMM32,
/* Read a signed extended byte(8 bits) immediate */
OT_SEIMM8,
/* Use a 8bit register */
OT_REG8,
/* Use a 16bit register */
OT_REG16,
/* Use a 16/32/64bit register */
OT_REG_FULL,
/* Use a 32bit register */
OT_REG32,
/*
* If used with REX the reg operand size becomes 64 bits, otherwise 32 bits.
* VMX instructions are promoted automatically without a REX prefix.
*/
OT_REG32_64,
/* Use AL */
OT_ACC8,
/* Use AX (FSTSW) */
OT_ACC16,
/* Use AX/EAX/RAX */
OT_ACC_FULL,
/* Use AX/EAX, no REX is possible for RAX, used only with IN/OUT which don't support 64 bit registers */
OT_ACC_FULL_NOT64,
/* Read a byte(8 bits) immediate and calculate it relatively to the current offset of the instruction being decoded */
OT_RELCB,
/* Read a word/dword immediate and calculate it relatively to the current offset of the instruction being decoded */
OT_RELC_FULL,
/*
* Instruction-Block for one byte long instructions, used by INC/DEC/PUSH/POP/XCHG,
* REG is extracted from the value of opcode
* Use a 8bit register
*/
OT_IB_RB,
/* Use a 16/32/64bit register */
OT_IB_R_FULL,
/* Read an immediate as an absolute address, size is known by instruction, used by MOV (memory offset) only */
OT_MOFFS8,
OT_MOFFS_FULL,
/* Use [(r)SI] as INDIRECTION, for repeatable instructions */
OT_REGI_ESI,
/* Use [(r)DI] as INDIRECTION, for repeatable instructions */
OT_REGI_EDI,
/* Use [(r)BX + AL] as INDIRECTIOM, used by XLAT only */
OT_REGI_EBXAL,
/* Use [(r)AX] as INDIRECTION, used by AMD's SVM instructions */
OT_REGI_EAX,
/* Use DX, as for OUTS DX, BYTE [SI] */
OT_REGDX,
/* Use ECX in INVLPGA instruction */
OT_REGECX,
/* FPU registers: */
OT_FPU_SI, /* ST(i) */
OT_FPU_SSI, /* ST(0), ST(i) */
OT_FPU_SIS, /* ST(i), ST(0) */
/* SSE registers: */
OT_XMM,
/* Extract the SSE register from the RM bits this time (used when the REG bits are used for opcode extension) */
OT_XMM_RM,
/* Implied XMM0 register as operand, used in SSE4. */
OT_REGXMM0,
/* Reg32/Reg 64 depends on prefix width only. */
OT_WREG32_64,
/* XMM is encoded in VEX.VVVV. */
OT_VXMM,
/* XMM is encoded in the high nibble of an immediate byte. */
OT_XMM_IMM,
/* YMM/XMM is dependent on VEX.L. */
OT_YXMM,
/* YMM/XMM (depends on prefix length) is encoded in the high nibble of an immediate byte. */
OT_YXMM_IMM,
/* YMM is encoded in reg. */
OT_YMM,
/* YMM is encoded in VEX.VVVV. */
OT_VYMM,
/* YMM/XMM is dependent on VEX.L, and encoded in VEX.VVVV. */
OT_VYXMM,
/* Use an immediate of 1, as for SHR R/M, 1 */
OT_CONST1,
/* Use CL, as for SHR R/M, CL */
OT_REGCL,
/* Use a control register */
OT_CREG,
/* Use a debug register */
OT_DREG,
/* Use a segment register */
OT_SREG,
/*
* SEG is encoded in the flags of the opcode itself!
* This is used for specific "push SS" where SS is a segment where
* each "push SS" has an absolutely different opcode byte.
* We need this to detect whether an operand size prefix is used.
*/
OT_SEG,
/*
* Special immediates for instructions which have more than one immediate,
* which is an exception from standard instruction format.
* As to version v1.0: ENTER, INSERTQ, EXTRQ are the only problematic ones.
*/
/* 16 bits immediate using the first imm-slot */
OT_IMM16_1,
/* 8 bits immediate using the first imm-slot */
OT_IMM8_1,
/* 8 bits immediate using the second imm-slot */
OT_IMM8_2,
/* Read one word (seg) and a word/dword/qword (depends on operand size), usually SEG:OFF, JMP 1234:1234 */
OT_PTR16_FULL,
/* Used only by MOV CR/DR(n). Promoted with REX onlly. */
OT_FREG32_64_RM,
/* MMX registers: */
OT_MM,
/* Extract the MMX register from the RM bits this time (used when the REG bits are used for opcode extension) */
OT_MM_RM,
/**** MEMORY only operands: ****/
/* Use general memory indirection, with varying sizes: */
OT_MEM,
OT_MEM32,
/* Memory dereference for MOVNTI, either 32 or 64 bits (with REX). */
OT_MEM32_64,
OT_MEM64,
/* Used for cmpxchg8b/16b. */
OT_MEM64_128,
OT_MEM128,
/*
* Read one word (seg), and a word/dword/qword (depends on operand size) from memory.
* JMP FAR [EBX] means EBX point to 16:32 ptr.
*/
OT_MEM16_FULL,
/* Read one word (limit) and a dword/qword (limit) (depends on operand size), used by SGDT, SIDT, LGDT, LIDT. */
OT_MEM16_3264,
/* Used when a memory indirection is required, but if the mod field is 11, this operand will be ignored. */
OT_MEM_OPT,
/* Same as OT_RMXX but POINTS to 16 bits [cannot use GENERAL-PURPOSE REG!] */
OT_FPUM16,
/* Same as OT_RMXX but POINTS to 32 bits (single precision) [cannot use GENERAL-PURPOSE REG!] */
OT_FPUM32,
/* Same as OT_RMXX but POINTS to 64 bits (double precision) [cannot use GENERAL-PURPOSE REG!] */
OT_FPUM64,
/* Same as OT_RMXX but POINTS to 80 bits (extended precision) [cannot use GENERAL-PURPOSE REG!] */
OT_FPUM80,
/* Mem128/Mem256 is dependent on VEX.L. */
OT_LMEM128_256,
/**** MEMORY & REGISTER only operands: ****/
/* Use or read (indirection) a 8bit register or immediate byte */
OT_RM8,
/* Some instructions force 16 bits (mov sreg, rm16) */
OT_RM16,
/* ModR/M for 32 bits. */
OT_RM32,
/*
* Special operand type for MOV reg16/32/64/mem16, segReg 8C /r. and SMSW.
* It supports all decoding modes, but if used as a memory indirection it's a 16 bit ModR/M indirection.
*/
OT_RFULL_M16,
/* Use or read a 16/32/64bit register or immediate word/dword/qword */
OT_RM_FULL,
/* RM32/RM64 depends on prefix width only. */
OT_WRM32_64,
/*
* Special type for SSE4, ModR/M might be a 32 bits or 64 bits (with REX) register or
* a 8 bits memory indirection operand.
*/
OT_R32_64_M8,
/*
* Special type for SSE4, ModR/M might be a 32 bits or 64 bits (with REX) register or
* a 16 bits memory indirection operand.
*/
OT_R32_64_M16,
/*
* 32 or 64 bits (with REX) operand size indirection memory operand.
* Some instructions are promoted automatically without a REX prefix.
*/
OT_RM32_64,
/* 16 or 32 bits RM. This is used only with MOVZXD instruction in 64bits. */
OT_RM16_32,
/*
* Special operand type for SSE4 where the ModR/M might
* be a 32 bits register or 8 bits memory indirection operand.
*/
OT_R32_M8,
/*
* Special ModR/M for PINSRW, which need a 16 bits memory operand or 32 bits register.
* In 16 bits decoding mode R32 becomes R16, operand size cannot affect this.
*/
OT_R32_M16,
/* Reg32/Reg64 (prefix width) or Mem8. */
OT_REG32_64_M8,
/* Reg32/Reg64 (prefix width) or Mem16. */
OT_REG32_64_M16,
/* ModR/M points to 32 bits MMX variable */
OT_MM32,
/* ModR/M points to 32 bits MMX variable */
OT_MM64,
/* ModR/M points to 16 bits SSE variable */
OT_XMM16,
/* ModR/M points to 32 bits SSE variable */
OT_XMM32,
/* ModR/M points to 64 bits SSE variable */
OT_XMM64,
/* ModR/M points to 128 bits SSE variable */
OT_XMM128,
/* AVX operands: */
/* XMM or Mem32/Mem64 depends on perfix width only. */
OT_WXMM32_64,
/* YMM or Mem256. */
OT_YMM256,
/* YMM/XMM or Mem64/Mem256 is dependent on VEX.L. */
OT_YXMM64_256,
/* YMM/XMM or Mem128/Mem256 is dependent on VEX.L. */
OT_YXMM128_256,
/* XMM or Mem64/Mem256 is dependent on VEX.L. */
OT_LXMM64_128
} _OpType;
/* Flags for instruction: */
/* Empty flags indicator: */
#define INST_FLAGS_NONE (0)
/* The instruction we are going to decode requires ModR/M encoding. */
#define INST_MODRM_REQUIRED (1)
/* Special treatment for instructions which are in the divided-category but still needs the whole byte for ModR/M... */
#define INST_NOT_DIVIDED (1 << 1)
/*
* Used explicitly in repeatable instructions,
* which needs a suffix letter in their mnemonic to specify operation-size (depend on operands).
*/
#define INST_16BITS (1 << 2)
/* If the opcode is supported by 80286 and upper models (16/32 bits). */
#define INST_32BITS (1 << 3)
/*
* Prefix flags (6 types: lock/rep, seg override, addr-size, oper-size, REX, VEX)
* There are several specific instructions that can follow LOCK prefix,
* note that they must be using a memory operand form, otherwise they generate an exception.
*/
#define INST_PRE_LOCK (1 << 4)
/* REPNZ prefix for string instructions only - means an instruction can follow it. */
#define INST_PRE_REPNZ (1 << 5)
/* REP prefix for string instructions only - means an instruction can follow it. */
#define INST_PRE_REP (1 << 6)
/* CS override prefix. */
#define INST_PRE_CS (1 << 7)
/* SS override prefix. */
#define INST_PRE_SS (1 << 8)
/* DS override prefix. */
#define INST_PRE_DS (1 << 9)
/* ES override prefix. */
#define INST_PRE_ES (1 << 10)
/* FS override prefix. Funky Segment :) */
#define INST_PRE_FS (1 << 11)
/* GS override prefix. Groovy Segment, of course not, duh ! */
#define INST_PRE_GS (1 << 12)
/* Switch operand size from 32 to 16 and vice versa. */
#define INST_PRE_OP_SIZE (1 << 13)
/* Switch address size from 32 to 16 and vice versa. */
#define INST_PRE_ADDR_SIZE (1 << 14)
/* Native instructions which needs suffix letter to indicate their operation-size (and don't depend on operands). */
#define INST_NATIVE (1 << 15)
/* Use extended mnemonic, means it's an _InstInfoEx structure, which contains another mnemonic for 32 bits specifically. */
#define INST_USE_EXMNEMONIC (1 << 16)
/* Use third operand, means it's an _InstInfoEx structure, which contains another operand for special instructions. */
#define INST_USE_OP3 (1 << 17)
/* Use fourth operand, means it's an _InstInfoEx structure, which contains another operand for special instructions. */
#define INST_USE_OP4 (1 << 18)
/* The instruction's mnemonic depends on the mod value of the ModR/M byte (mod=11, mod!=11). */
#define INST_MNEMONIC_MODRM_BASED (1 << 19)
/* The instruction uses a ModR/M byte which the MOD must be 11 (for registers operands only). */
#define INST_MODRR_REQUIRED (1 << 20)
/* The way of 3DNow! instructions are built, we have to handle their locating specially. Suffix imm8 tells which instruction it is. */
#define INST_3DNOW_FETCH (1 << 21)
/* The instruction needs two suffixes, one for the comparison type (imm8) and the second for its operation size indication (second mnemonic). */
#define INST_PSEUDO_OPCODE (1 << 22)
/* Invalid instruction at 64 bits decoding mode. */
#define INST_INVALID_64BITS (1 << 23)
/* Specific instruction can be promoted to 64 bits (without REX, it is promoted automatically). */
#define INST_64BITS (1 << 24)
/* Indicates the instruction must be REX prefixed in order to use 64 bits operands. */
#define INST_PRE_REX (1 << 25)
/* Third mnemonic is set. */
#define INST_USE_EXMNEMONIC2 (1 << 26)
/* Instruction is only valid in 64 bits decoding mode. */
#define INST_64BITS_FETCH (1 << 27)
/* Forces that the ModRM-REG/Opcode field will be 0. (For EXTRQ). */
#define INST_FORCE_REG0 (1 << 28)
/* Indicates that instruction is encoded with a VEX prefix. */
#define INST_PRE_VEX (1 << 29)
/* Indicates that the instruction is encoded with a ModRM byte (REG field specifically). */
#define INST_MODRM_INCLUDED (1 << 30)
/* Indicates that the first (/destination) operand of the instruction is writable. */
#define INST_DST_WR (1 << 31)
#define INST_PRE_REPS (INST_PRE_REPNZ | INST_PRE_REP)
#define INST_PRE_LOKREP_MASK (INST_PRE_LOCK | INST_PRE_REPNZ | INST_PRE_REP)
#define INST_PRE_SEGOVRD_MASK32 (INST_PRE_CS | INST_PRE_SS | INST_PRE_DS | INST_PRE_ES)
#define INST_PRE_SEGOVRD_MASK64 (INST_PRE_FS | INST_PRE_GS)
#define INST_PRE_SEGOVRD_MASK (INST_PRE_SEGOVRD_MASK32 | INST_PRE_SEGOVRD_MASK64)
/* Extended flags for VEX: */
/* Indicates that the instruction might have VEX.L encoded. */
#define INST_VEX_L (1)
/* Indicates that the instruction might have VEX.W encoded. */
#define INST_VEX_W (1 << 1)
/* Indicates that the mnemonic of the instruction is based on the VEX.W bit. */
#define INST_MNEMONIC_VEXW_BASED (1 << 2)
/* Indicates that the mnemonic of the instruction is based on the VEX.L bit. */
#define INST_MNEMONIC_VEXL_BASED (1 << 3)
/* Forces the instruction to be encoded with VEX.L, otherwise it's undefined. */
#define INST_FORCE_VEXL (1 << 4)
/*
* Indicates that the instruction is based on the MOD field of the ModRM byte.
* (MOD==11: got the right instruction, else skip +4 in prefixed table for the correct instruction).
*/
#define INST_MODRR_BASED (1 << 5)
/* Indicates that the instruction doesn't use the VVVV field of the VEX prefix, if it does then it's undecodable. */
#define INST_VEX_V_UNUSED (1 << 6)
/* Indication that the instruction is privileged (Ring 0), this should be checked on the opcodeId field. */
#define META_INST_PRIVILEGED ((uint16_t)0x8000)
/*
* Indicates which operand is being decoded.
* Destination (1st), Source (2nd), op3 (3rd), op4 (4th).
* Used to set the operands' fields in the _DInst structure!
*/
typedef enum {ONT_NONE = -1, ONT_1 = 0, ONT_2 = 1, ONT_3 = 2, ONT_4 = 3} _OperandNumberType;
/* CPU Flags that instructions modify, test or undefine, in compacted form (CF,PF,AF,ZF,SF are 1:1 map to EFLAGS). */
#define D_COMPACT_CF 1 /* Carry */
#define D_COMPACT_PF 4 /* Parity */
#define D_COMPACT_AF 0x10 /* Auxiliary */
#define D_COMPACT_ZF 0x40 /* Zero */
#define D_COMPACT_SF 0x80 /* Sign */
/* The following flags have to be translated to EFLAGS. */
#define D_COMPACT_IF 2 /* Interrupt */
#define D_COMPACT_DF 8 /* Direction */
#define D_COMPACT_OF 0x20 /* Overflow */
/* The mask of flags that are already compatible with EFLAGS. */
#define D_COMPACT_SAME_FLAGS (D_COMPACT_CF | D_COMPACT_PF | D_COMPACT_AF | D_COMPACT_ZF | D_COMPACT_SF)
/*
* In order to save more space for storing the DB statically,
* I came up with another level of shared info.
* Because I saw that most of the information that instructions use repeats itself.
*
* Info about the instruction, source/dest types, meta and flags.
* _InstInfo points to a table of _InstSharedInfo.
*/
typedef struct {
uint8_t flagsIndex; /* An index into FlagsTables */
uint8_t s, d; /* OpType. */
/*
* The following are CPU flag masks that the instruction changes.
* The flags are compacted so 8 bits representation is enough.
* They will be expanded in runtime to be compatible to EFLAGS.
*/
uint8_t modifiedFlagsMask;
uint8_t testedFlagsMask;
uint8_t undefinedFlagsMask;
uint16_t meta; /* High byte = Instruction set class | Low byte = flow control flags. */
} _InstSharedInfo;
/*
* This structure is used for the instructions DB and NOT for the disassembled result code!
* This is the BASE structure, there are extensions to this structure below.
*/
typedef struct {
uint16_t sharedIndex; /* An index into the SharedInfoTable. */
uint16_t opcodeId; /* The opcodeId is really a byte-offset into the mnemonics table. MSB is a privileged indication. */
} _InstInfo;
/*
* There are merely few instructions which need a second mnemonic for 32 bits.
* Or a third for 64 bits. Therefore sometimes the second mnemonic is empty but not the third.
* In all decoding modes the first mnemonic is the default.
* A flag will indicate it uses another mnemonic.
*
* There are a couple of (SSE4) instructions in the whole DB which need both op3 and 3rd mnemonic for 64bits,
* therefore, I decided to make the extended structure contain all extra info in the same structure.
* There are a few instructions (SHLD/SHRD/IMUL and SSE too) which use third operand (or a fourth).
* A flag will indicate it uses a third/fourth operand.
*/
typedef struct {
/* Base structure (doesn't get accessed directly from code). */
_InstInfo BASE;
/* Extended starts here. */
uint8_t flagsEx; /* 8 bits are enough, in the future we might make it a bigger integer. */
uint8_t op3, op4; /* OpType. */
uint16_t opcodeId2, opcodeId3;
} _InstInfoEx;
/* Trie data structure node type: */
typedef enum {
INT_NOTEXISTS = 0, /* Not exists. */
INT_INFO = 1, /* It's an instruction info. */
INT_INFOEX,
INT_INFO_TREAT, /* Extra intervention is required by inst_lookup. */
INT_LIST_GROUP,
INT_LIST_FULL,
INT_LIST_DIVIDED,
INT_LIST_PREFIXED
} _InstNodeType;
/* Used to check instType < INT_INFOS, means we got an inst-info. Cause it has to be only one of them. */
#define INT_INFOS (INT_LIST_GROUP)
/* Instruction node is treated as { int index:13; int type:3; } */
typedef uint16_t _InstNode;
_InstInfo* inst_lookup(_CodeInfo* ci, _PrefixState* ps, int* isPrefixed);
_InstInfo* inst_lookup_3dnow(_CodeInfo* ci);
#endif /* INSTRUCTIONS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
/*
insts.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef INSTS_H
#define INSTS_H
#include "instructions.h"
/* Flags Table */
extern _iflags FlagsTable[];
/* Root Trie DB */
extern _InstSharedInfo InstSharedInfoTable[];
extern _InstInfo InstInfos[];
extern _InstInfoEx InstInfosEx[];
extern _InstNode InstructionsTree[];
/* 3DNow! Trie DB */
extern _InstNode Table_0F_0F;
/* AVX related: */
extern _InstNode Table_0F, Table_0F_38, Table_0F_3A;
/*
* The inst_lookup will return on of these two instructions according to the specified decoding mode.
* ARPL or MOVSXD on 64 bits is one byte instruction at index 0x63.
*/
extern _InstInfo II_MOVSXD;
/*
* The NOP instruction can be prefixed by REX in 64bits, therefore we have to decide in runtime whether it's an XCHG or NOP instruction.
* If 0x90 is prefixed by a usable REX it will become XCHG, otherwise it will become a NOP.
* Also note that if it's prefixed by 0xf3, it becomes a Pause.
*/
extern _InstInfo II_NOP;
extern _InstInfo II_PAUSE;
/*
* RDRAND and VMPTRLD share same 2.3 bytes opcode, and then alternates on the MOD bits,
* RDRAND is OT_FULL_REG while VMPTRLD is OT_MEM, and there's no such mixed type.
* So a hack into the inst_lookup was added for this decision, the DB isn't flexible enough. :(
*/
extern _InstInfo II_RDRAND;
/*
* Used for letting the extract operand know the type of operands without knowing the
* instruction itself yet, because of the way those instructions work.
* See function instructions.c!inst_lookup_3dnow.
*/
extern _InstInfo II_3DNOW;
/* Helper tables for pseudo compare mnemonics. */
extern uint16_t CmpMnemonicOffsets[8]; /* SSE */
extern uint16_t VCmpMnemonicOffsets[32]; /* AVX */
#endif /* INSTS_H */

View File

@@ -0,0 +1,314 @@
/*
mnemonics.c
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#include "../include/mnemonics.h"
#ifndef DISTORM_LIGHT
const unsigned char _MNEMONICS[] =
"\x09" "UNDEFINED\0" "\x03" "ADD\0" "\x04" "PUSH\0" "\x03" "POP\0" \
"\x02" "OR\0" "\x03" "ADC\0" "\x03" "SBB\0" "\x03" "AND\0" "\x03" "DAA\0" \
"\x03" "SUB\0" "\x03" "DAS\0" "\x03" "XOR\0" "\x03" "AAA\0" "\x03" "CMP\0" \
"\x03" "AAS\0" "\x03" "INC\0" "\x03" "DEC\0" "\x05" "PUSHA\0" "\x04" "POPA\0" \
"\x05" "BOUND\0" "\x04" "ARPL\0" "\x04" "IMUL\0" "\x03" "INS\0" "\x04" "OUTS\0" \
"\x02" "JO\0" "\x03" "JNO\0" "\x02" "JB\0" "\x03" "JAE\0" "\x02" "JZ\0" \
"\x03" "JNZ\0" "\x03" "JBE\0" "\x02" "JA\0" "\x02" "JS\0" "\x03" "JNS\0" \
"\x02" "JP\0" "\x03" "JNP\0" "\x02" "JL\0" "\x03" "JGE\0" "\x03" "JLE\0" \
"\x02" "JG\0" "\x04" "TEST\0" "\x04" "XCHG\0" "\x03" "MOV\0" "\x03" "LEA\0" \
"\x03" "CBW\0" "\x04" "CWDE\0" "\x04" "CDQE\0" "\x03" "CWD\0" "\x03" "CDQ\0" \
"\x03" "CQO\0" "\x08" "CALL FAR\0" "\x05" "PUSHF\0" "\x04" "POPF\0" \
"\x04" "SAHF\0" "\x04" "LAHF\0" "\x04" "MOVS\0" "\x04" "CMPS\0" "\x04" "STOS\0" \
"\x04" "LODS\0" "\x04" "SCAS\0" "\x03" "RET\0" "\x03" "LES\0" "\x03" "LDS\0" \
"\x05" "ENTER\0" "\x05" "LEAVE\0" "\x04" "RETF\0" "\x05" "INT 3\0" \
"\x03" "INT\0" "\x04" "INTO\0" "\x04" "IRET\0" "\x03" "AAM\0" "\x03" "AAD\0" \
"\x04" "SALC\0" "\x04" "XLAT\0" "\x06" "LOOPNZ\0" "\x05" "LOOPZ\0" \
"\x04" "LOOP\0" "\x04" "JCXZ\0" "\x05" "JECXZ\0" "\x05" "JRCXZ\0" "\x02" "IN\0" \
"\x03" "OUT\0" "\x04" "CALL\0" "\x03" "JMP\0" "\x07" "JMP FAR\0" "\x04" "INT1\0" \
"\x03" "HLT\0" "\x03" "CMC\0" "\x03" "CLC\0" "\x03" "STC\0" "\x03" "CLI\0" \
"\x03" "STI\0" "\x03" "CLD\0" "\x03" "STD\0" "\x03" "LAR\0" "\x03" "LSL\0" \
"\x07" "SYSCALL\0" "\x04" "CLTS\0" "\x06" "SYSRET\0" "\x04" "INVD\0" \
"\x06" "WBINVD\0" "\x03" "UD2\0" "\x05" "FEMMS\0" "\x03" "NOP\0" "\x05" "WRMSR\0" \
"\x05" "RDTSC\0" "\x05" "RDMSR\0" "\x05" "RDPMC\0" "\x08" "SYSENTER\0" \
"\x07" "SYSEXIT\0" "\x06" "GETSEC\0" "\x05" "CMOVO\0" "\x06" "CMOVNO\0" \
"\x05" "CMOVB\0" "\x06" "CMOVAE\0" "\x05" "CMOVZ\0" "\x06" "CMOVNZ\0" \
"\x06" "CMOVBE\0" "\x05" "CMOVA\0" "\x05" "CMOVS\0" "\x06" "CMOVNS\0" \
"\x05" "CMOVP\0" "\x06" "CMOVNP\0" "\x05" "CMOVL\0" "\x06" "CMOVGE\0" \
"\x06" "CMOVLE\0" "\x05" "CMOVG\0" "\x04" "SETO\0" "\x05" "SETNO\0" \
"\x04" "SETB\0" "\x05" "SETAE\0" "\x04" "SETZ\0" "\x05" "SETNZ\0" "\x05" "SETBE\0" \
"\x04" "SETA\0" "\x04" "SETS\0" "\x05" "SETNS\0" "\x04" "SETP\0" "\x05" "SETNP\0" \
"\x04" "SETL\0" "\x05" "SETGE\0" "\x05" "SETLE\0" "\x04" "SETG\0" "\x05" "CPUID\0" \
"\x02" "BT\0" "\x04" "SHLD\0" "\x03" "RSM\0" "\x03" "BTS\0" "\x04" "SHRD\0" \
"\x07" "CMPXCHG\0" "\x03" "LSS\0" "\x03" "BTR\0" "\x03" "LFS\0" "\x03" "LGS\0" \
"\x05" "MOVZX\0" "\x03" "BTC\0" "\x05" "MOVSX\0" "\x04" "XADD\0" "\x06" "MOVNTI\0" \
"\x05" "BSWAP\0" "\x03" "ROL\0" "\x03" "ROR\0" "\x03" "RCL\0" "\x03" "RCR\0" \
"\x03" "SHL\0" "\x03" "SHR\0" "\x03" "SAL\0" "\x03" "SAR\0" "\x06" "XABORT\0" \
"\x06" "XBEGIN\0" "\x04" "FADD\0" "\x04" "FMUL\0" "\x04" "FCOM\0" "\x05" "FCOMP\0" \
"\x04" "FSUB\0" "\x05" "FSUBR\0" "\x04" "FDIV\0" "\x05" "FDIVR\0" "\x03" "FLD\0" \
"\x03" "FST\0" "\x04" "FSTP\0" "\x06" "FLDENV\0" "\x05" "FLDCW\0" "\x04" "FXCH\0" \
"\x04" "FNOP\0" "\x04" "FCHS\0" "\x04" "FABS\0" "\x04" "FTST\0" "\x04" "FXAM\0" \
"\x04" "FLD1\0" "\x06" "FLDL2T\0" "\x06" "FLDL2E\0" "\x05" "FLDPI\0" \
"\x06" "FLDLG2\0" "\x06" "FLDLN2\0" "\x04" "FLDZ\0" "\x05" "F2XM1\0" \
"\x05" "FYL2X\0" "\x05" "FPTAN\0" "\x06" "FPATAN\0" "\x07" "FXTRACT\0" \
"\x06" "FPREM1\0" "\x07" "FDECSTP\0" "\x07" "FINCSTP\0" "\x05" "FPREM\0" \
"\x07" "FYL2XP1\0" "\x05" "FSQRT\0" "\x07" "FSINCOS\0" "\x07" "FRNDINT\0" \
"\x06" "FSCALE\0" "\x04" "FSIN\0" "\x04" "FCOS\0" "\x05" "FIADD\0" \
"\x05" "FIMUL\0" "\x05" "FICOM\0" "\x06" "FICOMP\0" "\x05" "FISUB\0" \
"\x06" "FISUBR\0" "\x05" "FIDIV\0" "\x06" "FIDIVR\0" "\x06" "FCMOVB\0" \
"\x06" "FCMOVE\0" "\x07" "FCMOVBE\0" "\x06" "FCMOVU\0" "\x07" "FUCOMPP\0" \
"\x04" "FILD\0" "\x06" "FISTTP\0" "\x04" "FIST\0" "\x05" "FISTP\0" \
"\x07" "FCMOVNB\0" "\x07" "FCMOVNE\0" "\x08" "FCMOVNBE\0" "\x07" "FCMOVNU\0" \
"\x04" "FENI\0" "\x06" "FEDISI\0" "\x06" "FSETPM\0" "\x06" "FUCOMI\0" \
"\x05" "FCOMI\0" "\x06" "FRSTOR\0" "\x05" "FFREE\0" "\x05" "FUCOM\0" \
"\x06" "FUCOMP\0" "\x05" "FADDP\0" "\x05" "FMULP\0" "\x06" "FCOMPP\0" \
"\x06" "FSUBRP\0" "\x05" "FSUBP\0" "\x06" "FDIVRP\0" "\x05" "FDIVP\0" \
"\x04" "FBLD\0" "\x05" "FBSTP\0" "\x07" "FUCOMIP\0" "\x06" "FCOMIP\0" \
"\x03" "NOT\0" "\x03" "NEG\0" "\x03" "MUL\0" "\x03" "DIV\0" "\x04" "IDIV\0" \
"\x04" "SLDT\0" "\x03" "STR\0" "\x04" "LLDT\0" "\x03" "LTR\0" "\x04" "VERR\0" \
"\x04" "VERW\0" "\x04" "SGDT\0" "\x04" "SIDT\0" "\x04" "LGDT\0" "\x04" "LIDT\0" \
"\x04" "SMSW\0" "\x04" "LMSW\0" "\x06" "INVLPG\0" "\x06" "VMCALL\0" \
"\x08" "VMLAUNCH\0" "\x08" "VMRESUME\0" "\x06" "VMXOFF\0" "\x07" "MONITOR\0" \
"\x05" "MWAIT\0" "\x06" "XGETBV\0" "\x06" "XSETBV\0" "\x06" "VMFUNC\0" \
"\x04" "XEND\0" "\x05" "VMRUN\0" "\x07" "VMMCALL\0" "\x06" "VMLOAD\0" \
"\x06" "VMSAVE\0" "\x04" "STGI\0" "\x04" "CLGI\0" "\x06" "SKINIT\0" \
"\x07" "INVLPGA\0" "\x06" "SWAPGS\0" "\x06" "RDTSCP\0" "\x08" "PREFETCH\0" \
"\x09" "PREFETCHW\0" "\x05" "PI2FW\0" "\x05" "PI2FD\0" "\x05" "PF2IW\0" \
"\x05" "PF2ID\0" "\x06" "PFNACC\0" "\x07" "PFPNACC\0" "\x07" "PFCMPGE\0" \
"\x05" "PFMIN\0" "\x05" "PFRCP\0" "\x07" "PFRSQRT\0" "\x05" "PFSUB\0" \
"\x05" "PFADD\0" "\x07" "PFCMPGT\0" "\x05" "PFMAX\0" "\x08" "PFRCPIT1\0" \
"\x08" "PFRSQIT1\0" "\x06" "PFSUBR\0" "\x05" "PFACC\0" "\x07" "PFCMPEQ\0" \
"\x05" "PFMUL\0" "\x08" "PFRCPIT2\0" "\x07" "PMULHRW\0" "\x06" "PSWAPD\0" \
"\x07" "PAVGUSB\0" "\x06" "MOVUPS\0" "\x06" "MOVUPD\0" "\x05" "MOVSS\0" \
"\x05" "MOVSD\0" "\x07" "VMOVUPS\0" "\x07" "VMOVUPD\0" "\x06" "VMOVSS\0" \
"\x06" "VMOVSD\0" "\x07" "MOVHLPS\0" "\x06" "MOVLPS\0" "\x06" "MOVLPD\0" \
"\x08" "MOVSLDUP\0" "\x07" "MOVDDUP\0" "\x08" "VMOVHLPS\0" "\x07" "VMOVLPS\0" \
"\x07" "VMOVLPD\0" "\x09" "VMOVSLDUP\0" "\x08" "VMOVDDUP\0" "\x08" "UNPCKLPS\0" \
"\x08" "UNPCKLPD\0" "\x09" "VUNPCKLPS\0" "\x09" "VUNPCKLPD\0" "\x08" "UNPCKHPS\0" \
"\x08" "UNPCKHPD\0" "\x09" "VUNPCKHPS\0" "\x09" "VUNPCKHPD\0" "\x07" "MOVLHPS\0" \
"\x06" "MOVHPS\0" "\x06" "MOVHPD\0" "\x08" "MOVSHDUP\0" "\x08" "VMOVLHPS\0" \
"\x07" "VMOVHPS\0" "\x07" "VMOVHPD\0" "\x09" "VMOVSHDUP\0" "\x0b" "PREFETCHNTA\0" \
"\x0a" "PREFETCHT0\0" "\x0a" "PREFETCHT1\0" "\x0a" "PREFETCHT2\0" "\x06" "MOVAPS\0" \
"\x06" "MOVAPD\0" "\x07" "VMOVAPS\0" "\x07" "VMOVAPD\0" "\x08" "CVTPI2PS\0" \
"\x08" "CVTPI2PD\0" "\x08" "CVTSI2SS\0" "\x08" "CVTSI2SD\0" "\x09" "VCVTSI2SS\0" \
"\x09" "VCVTSI2SD\0" "\x07" "MOVNTPS\0" "\x07" "MOVNTPD\0" "\x07" "MOVNTSS\0" \
"\x07" "MOVNTSD\0" "\x08" "VMOVNTPS\0" "\x08" "VMOVNTPD\0" "\x09" "CVTTPS2PI\0" \
"\x09" "CVTTPD2PI\0" "\x09" "CVTTSS2SI\0" "\x09" "CVTTSD2SI\0" "\x0a" "VCVTTSS2SI\0" \
"\x0a" "VCVTTSD2SI\0" "\x08" "CVTPS2PI\0" "\x08" "CVTPD2PI\0" "\x08" "CVTSS2SI\0" \
"\x08" "CVTSD2SI\0" "\x09" "VCVTSS2SI\0" "\x09" "VCVTSD2SI\0" "\x07" "UCOMISS\0" \
"\x07" "UCOMISD\0" "\x08" "VUCOMISS\0" "\x08" "VUCOMISD\0" "\x06" "COMISS\0" \
"\x06" "COMISD\0" "\x07" "VCOMISS\0" "\x07" "VCOMISD\0" "\x08" "MOVMSKPS\0" \
"\x08" "MOVMSKPD\0" "\x09" "VMOVMSKPS\0" "\x09" "VMOVMSKPD\0" "\x06" "SQRTPS\0" \
"\x06" "SQRTPD\0" "\x06" "SQRTSS\0" "\x06" "SQRTSD\0" "\x07" "VSQRTPS\0" \
"\x07" "VSQRTPD\0" "\x07" "VSQRTSS\0" "\x07" "VSQRTSD\0" "\x07" "RSQRTPS\0" \
"\x07" "RSQRTSS\0" "\x08" "VRSQRTPS\0" "\x08" "VRSQRTSS\0" "\x05" "RCPPS\0" \
"\x05" "RCPSS\0" "\x06" "VRCPPS\0" "\x06" "VRCPSS\0" "\x05" "ANDPS\0" \
"\x05" "ANDPD\0" "\x06" "VANDPS\0" "\x06" "VANDPD\0" "\x06" "ANDNPS\0" \
"\x06" "ANDNPD\0" "\x07" "VANDNPS\0" "\x07" "VANDNPD\0" "\x04" "ORPS\0" \
"\x04" "ORPD\0" "\x05" "VORPS\0" "\x05" "VORPD\0" "\x05" "XORPS\0" \
"\x05" "XORPD\0" "\x06" "VXORPS\0" "\x06" "VXORPD\0" "\x05" "ADDPS\0" \
"\x05" "ADDPD\0" "\x05" "ADDSS\0" "\x05" "ADDSD\0" "\x06" "VADDPS\0" \
"\x06" "VADDPD\0" "\x06" "VADDSS\0" "\x06" "VADDSD\0" "\x05" "MULPS\0" \
"\x05" "MULPD\0" "\x05" "MULSS\0" "\x05" "MULSD\0" "\x06" "VMULPS\0" \
"\x06" "VMULPD\0" "\x06" "VMULSS\0" "\x06" "VMULSD\0" "\x08" "CVTPS2PD\0" \
"\x08" "CVTPD2PS\0" "\x08" "CVTSS2SD\0" "\x08" "CVTSD2SS\0" "\x09" "VCVTPS2PD\0" \
"\x09" "VCVTPD2PS\0" "\x09" "VCVTSS2SD\0" "\x09" "VCVTSD2SS\0" "\x08" "CVTDQ2PS\0" \
"\x08" "CVTPS2DQ\0" "\x09" "CVTTPS2DQ\0" "\x09" "VCVTDQ2PS\0" "\x09" "VCVTPS2DQ\0" \
"\x0a" "VCVTTPS2DQ\0" "\x05" "SUBPS\0" "\x05" "SUBPD\0" "\x05" "SUBSS\0" \
"\x05" "SUBSD\0" "\x06" "VSUBPS\0" "\x06" "VSUBPD\0" "\x06" "VSUBSS\0" \
"\x06" "VSUBSD\0" "\x05" "MINPS\0" "\x05" "MINPD\0" "\x05" "MINSS\0" \
"\x05" "MINSD\0" "\x06" "VMINPS\0" "\x06" "VMINPD\0" "\x06" "VMINSS\0" \
"\x06" "VMINSD\0" "\x05" "DIVPS\0" "\x05" "DIVPD\0" "\x05" "DIVSS\0" \
"\x05" "DIVSD\0" "\x06" "VDIVPS\0" "\x06" "VDIVPD\0" "\x06" "VDIVSS\0" \
"\x06" "VDIVSD\0" "\x05" "MAXPS\0" "\x05" "MAXPD\0" "\x05" "MAXSS\0" \
"\x05" "MAXSD\0" "\x06" "VMAXPS\0" "\x06" "VMAXPD\0" "\x06" "VMAXSS\0" \
"\x06" "VMAXSD\0" "\x09" "PUNPCKLBW\0" "\x0a" "VPUNPCKLBW\0" "\x09" "PUNPCKLWD\0" \
"\x0a" "VPUNPCKLWD\0" "\x09" "PUNPCKLDQ\0" "\x0a" "VPUNPCKLDQ\0" "\x08" "PACKSSWB\0" \
"\x09" "VPACKSSWB\0" "\x07" "PCMPGTB\0" "\x08" "VPCMPGTB\0" "\x07" "PCMPGTW\0" \
"\x08" "VPCMPGTW\0" "\x07" "PCMPGTD\0" "\x08" "VPCMPGTD\0" "\x08" "PACKUSWB\0" \
"\x09" "VPACKUSWB\0" "\x09" "PUNPCKHBW\0" "\x0a" "VPUNPCKHBW\0" "\x09" "PUNPCKHWD\0" \
"\x0a" "VPUNPCKHWD\0" "\x09" "PUNPCKHDQ\0" "\x0a" "VPUNPCKHDQ\0" "\x08" "PACKSSDW\0" \
"\x09" "VPACKSSDW\0" "\x0a" "PUNPCKLQDQ\0" "\x0b" "VPUNPCKLQDQ\0" "\x0a" "PUNPCKHQDQ\0" \
"\x0b" "VPUNPCKHQDQ\0" "\x04" "MOVD\0" "\x04" "MOVQ\0" "\x05" "VMOVD\0" \
"\x05" "VMOVQ\0" "\x06" "MOVDQA\0" "\x06" "MOVDQU\0" "\x07" "VMOVDQA\0" \
"\x07" "VMOVDQU\0" "\x06" "PSHUFW\0" "\x06" "PSHUFD\0" "\x07" "PSHUFHW\0" \
"\x07" "PSHUFLW\0" "\x07" "VPSHUFD\0" "\x08" "VPSHUFHW\0" "\x08" "VPSHUFLW\0" \
"\x07" "PCMPEQB\0" "\x08" "VPCMPEQB\0" "\x07" "PCMPEQW\0" "\x08" "VPCMPEQW\0" \
"\x07" "PCMPEQD\0" "\x08" "VPCMPEQD\0" "\x04" "EMMS\0" "\x0a" "VZEROUPPER\0" \
"\x08" "VZEROALL\0" "\x06" "VMREAD\0" "\x05" "EXTRQ\0" "\x07" "INSERTQ\0" \
"\x07" "VMWRITE\0" "\x08" "CVTPH2PS\0" "\x08" "CVTPS2PH\0" "\x06" "HADDPD\0" \
"\x06" "HADDPS\0" "\x07" "VHADDPD\0" "\x07" "VHADDPS\0" "\x06" "HSUBPD\0" \
"\x06" "HSUBPS\0" "\x07" "VHSUBPD\0" "\x07" "VHSUBPS\0" "\x05" "XSAVE\0" \
"\x07" "XSAVE64\0" "\x06" "LFENCE\0" "\x06" "XRSTOR\0" "\x08" "XRSTOR64\0" \
"\x06" "MFENCE\0" "\x08" "XSAVEOPT\0" "\x0a" "XSAVEOPT64\0" "\x06" "SFENCE\0" \
"\x07" "CLFLUSH\0" "\x06" "POPCNT\0" "\x03" "BSF\0" "\x05" "TZCNT\0" \
"\x03" "BSR\0" "\x05" "LZCNT\0" "\x07" "CMPEQPS\0" "\x07" "CMPLTPS\0" \
"\x07" "CMPLEPS\0" "\x0a" "CMPUNORDPS\0" "\x08" "CMPNEQPS\0" "\x08" "CMPNLTPS\0" \
"\x08" "CMPNLEPS\0" "\x08" "CMPORDPS\0" "\x07" "CMPEQPD\0" "\x07" "CMPLTPD\0" \
"\x07" "CMPLEPD\0" "\x0a" "CMPUNORDPD\0" "\x08" "CMPNEQPD\0" "\x08" "CMPNLTPD\0" \
"\x08" "CMPNLEPD\0" "\x08" "CMPORDPD\0" "\x07" "CMPEQSS\0" "\x07" "CMPLTSS\0" \
"\x07" "CMPLESS\0" "\x0a" "CMPUNORDSS\0" "\x08" "CMPNEQSS\0" "\x08" "CMPNLTSS\0" \
"\x08" "CMPNLESS\0" "\x08" "CMPORDSS\0" "\x07" "CMPEQSD\0" "\x07" "CMPLTSD\0" \
"\x07" "CMPLESD\0" "\x0a" "CMPUNORDSD\0" "\x08" "CMPNEQSD\0" "\x08" "CMPNLTSD\0" \
"\x08" "CMPNLESD\0" "\x08" "CMPORDSD\0" "\x08" "VCMPEQPS\0" "\x08" "VCMPLTPS\0" \
"\x08" "VCMPLEPS\0" "\x0b" "VCMPUNORDPS\0" "\x09" "VCMPNEQPS\0" "\x09" "VCMPNLTPS\0" \
"\x09" "VCMPNLEPS\0" "\x09" "VCMPORDPS\0" "\x0b" "VCMPEQ_UQPS\0" "\x09" "VCMPNGEPS\0" \
"\x09" "VCMPNGTPS\0" "\x0b" "VCMPFALSEPS\0" "\x0c" "VCMPNEQ_OQPS\0" "\x08" "VCMPGEPS\0" \
"\x08" "VCMPGTPS\0" "\x0a" "VCMPTRUEPS\0" "\x0b" "VCMPEQ_OSPS\0" "\x0b" "VCMPLT_OQPS\0" \
"\x0b" "VCMPLE_OQPS\0" "\x0d" "VCMPUNORD_SPS\0" "\x0c" "VCMPNEQ_USPS\0" \
"\x0c" "VCMPNLT_UQPS\0" "\x0c" "VCMPNLE_UQPS\0" "\x0b" "VCMPORD_SPS\0" \
"\x0b" "VCMPEQ_USPS\0" "\x0c" "VCMPNGE_UQPS\0" "\x0c" "VCMPNGT_UQPS\0" \
"\x0e" "VCMPFALSE_OSPS\0" "\x0c" "VCMPNEQ_OSPS\0" "\x0b" "VCMPGE_OQPS\0" \
"\x0b" "VCMPGT_OQPS\0" "\x0d" "VCMPTRUE_USPS\0" "\x08" "VCMPEQPD\0" "\x08" "VCMPLTPD\0" \
"\x08" "VCMPLEPD\0" "\x0b" "VCMPUNORDPD\0" "\x09" "VCMPNEQPD\0" "\x09" "VCMPNLTPD\0" \
"\x09" "VCMPNLEPD\0" "\x09" "VCMPORDPD\0" "\x0b" "VCMPEQ_UQPD\0" "\x09" "VCMPNGEPD\0" \
"\x09" "VCMPNGTPD\0" "\x0b" "VCMPFALSEPD\0" "\x0c" "VCMPNEQ_OQPD\0" "\x08" "VCMPGEPD\0" \
"\x08" "VCMPGTPD\0" "\x0a" "VCMPTRUEPD\0" "\x0b" "VCMPEQ_OSPD\0" "\x0b" "VCMPLT_OQPD\0" \
"\x0b" "VCMPLE_OQPD\0" "\x0d" "VCMPUNORD_SPD\0" "\x0c" "VCMPNEQ_USPD\0" \
"\x0c" "VCMPNLT_UQPD\0" "\x0c" "VCMPNLE_UQPD\0" "\x0b" "VCMPORD_SPD\0" \
"\x0b" "VCMPEQ_USPD\0" "\x0c" "VCMPNGE_UQPD\0" "\x0c" "VCMPNGT_UQPD\0" \
"\x0e" "VCMPFALSE_OSPD\0" "\x0c" "VCMPNEQ_OSPD\0" "\x0b" "VCMPGE_OQPD\0" \
"\x0b" "VCMPGT_OQPD\0" "\x0d" "VCMPTRUE_USPD\0" "\x08" "VCMPEQSS\0" "\x08" "VCMPLTSS\0" \
"\x08" "VCMPLESS\0" "\x0b" "VCMPUNORDSS\0" "\x09" "VCMPNEQSS\0" "\x09" "VCMPNLTSS\0" \
"\x09" "VCMPNLESS\0" "\x09" "VCMPORDSS\0" "\x0b" "VCMPEQ_UQSS\0" "\x09" "VCMPNGESS\0" \
"\x09" "VCMPNGTSS\0" "\x0b" "VCMPFALSESS\0" "\x0c" "VCMPNEQ_OQSS\0" "\x08" "VCMPGESS\0" \
"\x08" "VCMPGTSS\0" "\x0a" "VCMPTRUESS\0" "\x0b" "VCMPEQ_OSSS\0" "\x0b" "VCMPLT_OQSS\0" \
"\x0b" "VCMPLE_OQSS\0" "\x0d" "VCMPUNORD_SSS\0" "\x0c" "VCMPNEQ_USSS\0" \
"\x0c" "VCMPNLT_UQSS\0" "\x0c" "VCMPNLE_UQSS\0" "\x0b" "VCMPORD_SSS\0" \
"\x0b" "VCMPEQ_USSS\0" "\x0c" "VCMPNGE_UQSS\0" "\x0c" "VCMPNGT_UQSS\0" \
"\x0e" "VCMPFALSE_OSSS\0" "\x0c" "VCMPNEQ_OSSS\0" "\x0b" "VCMPGE_OQSS\0" \
"\x0b" "VCMPGT_OQSS\0" "\x0d" "VCMPTRUE_USSS\0" "\x08" "VCMPEQSD\0" "\x08" "VCMPLTSD\0" \
"\x08" "VCMPLESD\0" "\x0b" "VCMPUNORDSD\0" "\x09" "VCMPNEQSD\0" "\x09" "VCMPNLTSD\0" \
"\x09" "VCMPNLESD\0" "\x09" "VCMPORDSD\0" "\x0b" "VCMPEQ_UQSD\0" "\x09" "VCMPNGESD\0" \
"\x09" "VCMPNGTSD\0" "\x0b" "VCMPFALSESD\0" "\x0c" "VCMPNEQ_OQSD\0" "\x08" "VCMPGESD\0" \
"\x08" "VCMPGTSD\0" "\x0a" "VCMPTRUESD\0" "\x0b" "VCMPEQ_OSSD\0" "\x0b" "VCMPLT_OQSD\0" \
"\x0b" "VCMPLE_OQSD\0" "\x0d" "VCMPUNORD_SSD\0" "\x0c" "VCMPNEQ_USSD\0" \
"\x0c" "VCMPNLT_UQSD\0" "\x0c" "VCMPNLE_UQSD\0" "\x0b" "VCMPORD_SSD\0" \
"\x0b" "VCMPEQ_USSD\0" "\x0c" "VCMPNGE_UQSD\0" "\x0c" "VCMPNGT_UQSD\0" \
"\x0e" "VCMPFALSE_OSSD\0" "\x0c" "VCMPNEQ_OSSD\0" "\x0b" "VCMPGE_OQSD\0" \
"\x0b" "VCMPGT_OQSD\0" "\x0d" "VCMPTRUE_USSD\0" "\x06" "PINSRW\0" "\x07" "VPINSRW\0" \
"\x06" "PEXTRW\0" "\x07" "VPEXTRW\0" "\x06" "SHUFPS\0" "\x06" "SHUFPD\0" \
"\x07" "VSHUFPS\0" "\x07" "VSHUFPD\0" "\x09" "CMPXCHG8B\0" "\x0a" "CMPXCHG16B\0" \
"\x07" "VMPTRST\0" "\x08" "ADDSUBPD\0" "\x08" "ADDSUBPS\0" "\x09" "VADDSUBPD\0" \
"\x09" "VADDSUBPS\0" "\x05" "PSRLW\0" "\x06" "VPSRLW\0" "\x05" "PSRLD\0" \
"\x06" "VPSRLD\0" "\x05" "PSRLQ\0" "\x06" "VPSRLQ\0" "\x05" "PADDQ\0" \
"\x06" "VPADDQ\0" "\x06" "PMULLW\0" "\x07" "VPMULLW\0" "\x07" "MOVQ2DQ\0" \
"\x07" "MOVDQ2Q\0" "\x08" "PMOVMSKB\0" "\x09" "VPMOVMSKB\0" "\x07" "PSUBUSB\0" \
"\x08" "VPSUBUSB\0" "\x07" "PSUBUSW\0" "\x08" "VPSUBUSW\0" "\x06" "PMINUB\0" \
"\x07" "VPMINUB\0" "\x04" "PAND\0" "\x05" "VPAND\0" "\x07" "PADDUSB\0" \
"\x08" "VPADDUSW\0" "\x07" "PADDUSW\0" "\x06" "PMAXUB\0" "\x07" "VPMAXUB\0" \
"\x05" "PANDN\0" "\x06" "VPANDN\0" "\x05" "PAVGB\0" "\x06" "VPAVGB\0" \
"\x05" "PSRAW\0" "\x06" "VPSRAW\0" "\x05" "PSRAD\0" "\x06" "VPSRAD\0" \
"\x05" "PAVGW\0" "\x06" "VPAVGW\0" "\x07" "PMULHUW\0" "\x08" "VPMULHUW\0" \
"\x06" "PMULHW\0" "\x07" "VPMULHW\0" "\x09" "CVTTPD2DQ\0" "\x08" "CVTDQ2PD\0" \
"\x08" "CVTPD2DQ\0" "\x0a" "VCVTTPD2DQ\0" "\x09" "VCVTDQ2PD\0" "\x09" "VCVTPD2DQ\0" \
"\x06" "MOVNTQ\0" "\x07" "MOVNTDQ\0" "\x08" "VMOVNTDQ\0" "\x06" "PSUBSB\0" \
"\x07" "VPSUBSB\0" "\x06" "PSUBSW\0" "\x07" "VPSUBSW\0" "\x06" "PMINSW\0" \
"\x07" "VPMINSW\0" "\x03" "POR\0" "\x04" "VPOR\0" "\x06" "PADDSB\0" \
"\x07" "VPADDSB\0" "\x06" "PADDSW\0" "\x07" "VPADDSW\0" "\x06" "PMAXSW\0" \
"\x07" "VPMAXSW\0" "\x04" "PXOR\0" "\x05" "VPXOR\0" "\x05" "LDDQU\0" \
"\x06" "VLDDQU\0" "\x05" "PSLLW\0" "\x06" "VPSLLW\0" "\x05" "PSLLD\0" \
"\x06" "VPSLLD\0" "\x05" "PSLLQ\0" "\x06" "VPSLLQ\0" "\x07" "PMULUDQ\0" \
"\x08" "VPMULUDQ\0" "\x07" "PMADDWD\0" "\x08" "VPMADDWD\0" "\x06" "PSADBW\0" \
"\x07" "VPSADBW\0" "\x08" "MASKMOVQ\0" "\x0a" "MASKMOVDQU\0" "\x0b" "VMASKMOVDQU\0" \
"\x05" "PSUBB\0" "\x06" "VPSUBB\0" "\x05" "PSUBW\0" "\x06" "VPSUBW\0" \
"\x05" "PSUBD\0" "\x06" "VPSUBD\0" "\x05" "PSUBQ\0" "\x06" "VPSUBQ\0" \
"\x05" "PADDB\0" "\x06" "VPADDB\0" "\x05" "PADDW\0" "\x06" "VPADDW\0" \
"\x05" "PADDD\0" "\x06" "VPADDD\0" "\x07" "FNSTENV\0" "\x06" "FSTENV\0" \
"\x06" "FNSTCW\0" "\x05" "FSTCW\0" "\x06" "FNCLEX\0" "\x05" "FCLEX\0" \
"\x06" "FNINIT\0" "\x05" "FINIT\0" "\x06" "FNSAVE\0" "\x05" "FSAVE\0" \
"\x06" "FNSTSW\0" "\x05" "FSTSW\0" "\x06" "PSHUFB\0" "\x07" "VPSHUFB\0" \
"\x06" "PHADDW\0" "\x07" "VPHADDW\0" "\x06" "PHADDD\0" "\x07" "VPHADDD\0" \
"\x07" "PHADDSW\0" "\x08" "VPHADDSW\0" "\x09" "PMADDUBSW\0" "\x0a" "VPMADDUBSW\0" \
"\x06" "PHSUBW\0" "\x07" "VPHSUBW\0" "\x06" "PHSUBD\0" "\x07" "VPHSUBD\0" \
"\x07" "PHSUBSW\0" "\x08" "VPHSUBSW\0" "\x06" "PSIGNB\0" "\x07" "VPSIGNB\0" \
"\x06" "PSIGNW\0" "\x07" "VPSIGNW\0" "\x06" "PSIGND\0" "\x07" "VPSIGND\0" \
"\x08" "PMULHRSW\0" "\x09" "VPMULHRSW\0" "\x09" "VPERMILPS\0" "\x09" "VPERMILPD\0" \
"\x07" "VTESTPS\0" "\x07" "VTESTPD\0" "\x08" "PBLENDVB\0" "\x08" "BLENDVPS\0" \
"\x08" "BLENDVPD\0" "\x05" "PTEST\0" "\x06" "VPTEST\0" "\x0c" "VBROADCASTSS\0" \
"\x0c" "VBROADCASTSD\0" "\x0e" "VBROADCASTF128\0" "\x05" "PABSB\0" "\x06" "VPABSB\0" \
"\x05" "PABSW\0" "\x06" "VPABSW\0" "\x05" "PABSD\0" "\x06" "VPABSD\0" \
"\x08" "PMOVSXBW\0" "\x09" "VPMOVSXBW\0" "\x08" "PMOVSXBD\0" "\x09" "VPMOVSXBD\0" \
"\x08" "PMOVSXBQ\0" "\x09" "VPMOVSXBQ\0" "\x08" "PMOVSXWD\0" "\x09" "VPMOVSXWD\0" \
"\x08" "PMOVSXWQ\0" "\x09" "VPMOVSXWQ\0" "\x08" "PMOVSXDQ\0" "\x09" "VPMOVSXDQ\0" \
"\x06" "PMULDQ\0" "\x07" "VPMULDQ\0" "\x07" "PCMPEQQ\0" "\x08" "VPCMPEQQ\0" \
"\x08" "MOVNTDQA\0" "\x09" "VMOVNTDQA\0" "\x08" "PACKUSDW\0" "\x09" "VPACKUSDW\0" \
"\x0a" "VMASKMOVPS\0" "\x0a" "VMASKMOVPD\0" "\x08" "PMOVZXBW\0" "\x09" "VPMOVZXBW\0" \
"\x08" "PMOVZXBD\0" "\x09" "VPMOVZXBD\0" "\x08" "PMOVZXBQ\0" "\x09" "VPMOVZXBQ\0" \
"\x08" "PMOVZXWD\0" "\x09" "VPMOVZXWD\0" "\x08" "PMOVZXWQ\0" "\x09" "VPMOVZXWQ\0" \
"\x08" "PMOVZXDQ\0" "\x09" "VPMOVZXDQ\0" "\x07" "PCMPGTQ\0" "\x08" "VPCMPGTQ\0" \
"\x06" "PMINSB\0" "\x07" "VPMINSB\0" "\x06" "PMINSD\0" "\x07" "VPMINSD\0" \
"\x06" "PMINUW\0" "\x07" "VPMINUW\0" "\x06" "PMINUD\0" "\x07" "VPMINUD\0" \
"\x06" "PMAXSB\0" "\x07" "VPMAXSB\0" "\x06" "PMAXSD\0" "\x07" "VPMAXSD\0" \
"\x06" "PMAXUW\0" "\x07" "VPMAXUW\0" "\x06" "PMAXUD\0" "\x07" "VPMAXUD\0" \
"\x06" "PMULLD\0" "\x07" "VPMULLD\0" "\x0a" "PHMINPOSUW\0" "\x0b" "VPHMINPOSUW\0" \
"\x06" "INVEPT\0" "\x07" "INVVPID\0" "\x07" "INVPCID\0" "\x0e" "VFMADDSUB132PS\0" \
"\x0e" "VFMADDSUB132PD\0" "\x0e" "VFMSUBADD132PS\0" "\x0e" "VFMSUBADD132PD\0" \
"\x0b" "VFMADD132PS\0" "\x0b" "VFMADD132PD\0" "\x0b" "VFMADD132SS\0" \
"\x0b" "VFMADD132SD\0" "\x0b" "VFMSUB132PS\0" "\x0b" "VFMSUB132PD\0" \
"\x0b" "VFMSUB132SS\0" "\x0b" "VFMSUB132SD\0" "\x0c" "VFNMADD132PS\0" \
"\x0c" "VFNMADD132PD\0" "\x0c" "VFNMADD132SS\0" "\x0c" "VFNMADD132SD\0" \
"\x0c" "VFNMSUB132PS\0" "\x0c" "VFNMSUB132PD\0" "\x0c" "VFNMSUB132SS\0" \
"\x0c" "VFNMSUB132SD\0" "\x0e" "VFMADDSUB213PS\0" "\x0e" "VFMADDSUB213PD\0" \
"\x0e" "VFMSUBADD213PS\0" "\x0e" "VFMSUBADD213PD\0" "\x0b" "VFMADD213PS\0" \
"\x0b" "VFMADD213PD\0" "\x0b" "VFMADD213SS\0" "\x0b" "VFMADD213SD\0" \
"\x0b" "VFMSUB213PS\0" "\x0b" "VFMSUB213PD\0" "\x0b" "VFMSUB213SS\0" \
"\x0b" "VFMSUB213SD\0" "\x0c" "VFNMADD213PS\0" "\x0c" "VFNMADD213PD\0" \
"\x0c" "VFNMADD213SS\0" "\x0c" "VFNMADD213SD\0" "\x0c" "VFNMSUB213PS\0" \
"\x0c" "VFNMSUB213PD\0" "\x0c" "VFNMSUB213SS\0" "\x0c" "VFNMSUB213SD\0" \
"\x0e" "VFMADDSUB231PS\0" "\x0e" "VFMADDSUB231PD\0" "\x0e" "VFMSUBADD231PS\0" \
"\x0e" "VFMSUBADD231PD\0" "\x0b" "VFMADD231PS\0" "\x0b" "VFMADD231PD\0" \
"\x0b" "VFMADD231SS\0" "\x0b" "VFMADD231SD\0" "\x0b" "VFMSUB231PS\0" \
"\x0b" "VFMSUB231PD\0" "\x0b" "VFMSUB231SS\0" "\x0b" "VFMSUB231SD\0" \
"\x0c" "VFNMADD231PS\0" "\x0c" "VFNMADD231PD\0" "\x0c" "VFNMADD231SS\0" \
"\x0c" "VFNMADD231SD\0" "\x0c" "VFNMSUB231PS\0" "\x0c" "VFNMSUB231PD\0" \
"\x0c" "VFNMSUB231SS\0" "\x0c" "VFNMSUB231SD\0" "\x06" "AESIMC\0" "\x07" "VAESIMC\0" \
"\x06" "AESENC\0" "\x07" "VAESENC\0" "\x0a" "AESENCLAST\0" "\x0b" "VAESENCLAST\0" \
"\x06" "AESDEC\0" "\x07" "VAESDEC\0" "\x0a" "AESDECLAST\0" "\x0b" "VAESDECLAST\0" \
"\x05" "MOVBE\0" "\x05" "CRC32\0" "\x0a" "VPERM2F128\0" "\x07" "ROUNDPS\0" \
"\x08" "VROUNDPS\0" "\x07" "ROUNDPD\0" "\x08" "VROUNDPD\0" "\x07" "ROUNDSS\0" \
"\x08" "VROUNDSS\0" "\x07" "ROUNDSD\0" "\x08" "VROUNDSD\0" "\x07" "BLENDPS\0" \
"\x08" "VBLENDPS\0" "\x07" "BLENDPD\0" "\x08" "VBLENDPD\0" "\x07" "PBLENDW\0" \
"\x08" "VPBLENDW\0" "\x07" "PALIGNR\0" "\x08" "VPALIGNR\0" "\x06" "PEXTRB\0" \
"\x07" "VPEXTRB\0" "\x06" "PEXTRD\0" "\x06" "PEXTRQ\0" "\x07" "VPEXTRD\0" \
"\x07" "VPEXTRQ\0" "\x09" "EXTRACTPS\0" "\x0a" "VEXTRACTPS\0" "\x0b" "VINSERTF128\0" \
"\x0c" "VEXTRACTF128\0" "\x06" "PINSRB\0" "\x07" "VPINSRB\0" "\x08" "INSERTPS\0" \
"\x09" "VINSERTPS\0" "\x06" "PINSRD\0" "\x06" "PINSRQ\0" "\x07" "VPINSRD\0" \
"\x07" "VPINSRQ\0" "\x04" "DPPS\0" "\x05" "VDPPS\0" "\x04" "DPPD\0" \
"\x05" "VDPPD\0" "\x07" "MPSADBW\0" "\x08" "VMPSADBW\0" "\x09" "PCLMULQDQ\0" \
"\x0a" "VPCLMULQDQ\0" "\x09" "VBLENDVPS\0" "\x09" "VBLENDVPD\0" "\x09" "VPBLENDVB\0" \
"\x09" "PCMPESTRM\0" "\x0a" "VPCMPESTRM\0" "\x09" "PCMPESTRI\0" "\x0a" "VPCMPESTRI\0" \
"\x09" "PCMPISTRM\0" "\x0a" "VPCMPISTRM\0" "\x09" "PCMPISTRI\0" "\x0a" "VPCMPISTRI\0" \
"\x0f" "AESKEYGENASSIST\0" "\x10" "VAESKEYGENASSIST\0" "\x06" "PSRLDQ\0" \
"\x07" "VPSRLDQ\0" "\x06" "PSLLDQ\0" "\x07" "VPSLLDQ\0" "\x06" "FXSAVE\0" \
"\x08" "FXSAVE64\0" "\x08" "RDFSBASE\0" "\x07" "FXRSTOR\0" "\x09" "FXRSTOR64\0" \
"\x08" "RDGSBASE\0" "\x07" "LDMXCSR\0" "\x08" "WRFSBASE\0" "\x08" "VLDMXCSR\0" \
"\x07" "STMXCSR\0" "\x08" "WRGSBASE\0" "\x08" "VSTMXCSR\0" "\x07" "VMPTRLD\0" \
"\x07" "VMCLEAR\0" "\x05" "VMXON\0" "\x06" "MOVSXD\0" "\x05" "PAUSE\0" \
"\x04" "WAIT\0" "\x06" "RDRAND\0" "\x06" "_3DNOW\0" \
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; /* Sentinel mnemonic. */
const _WRegister _REGISTERS[] = {
{3, "RAX"}, {3, "RCX"}, {3, "RDX"}, {3, "RBX"}, {3, "RSP"}, {3, "RBP"}, {3, "RSI"}, {3, "RDI"}, {2, "R8"}, {2, "R9"}, {3, "R10"}, {3, "R11"}, {3, "R12"}, {3, "R13"}, {3, "R14"}, {3, "R15"},
{3, "EAX"}, {3, "ECX"}, {3, "EDX"}, {3, "EBX"}, {3, "ESP"}, {3, "EBP"}, {3, "ESI"}, {3, "EDI"}, {3, "R8D"}, {3, "R9D"}, {4, "R10D"}, {4, "R11D"}, {4, "R12D"}, {4, "R13D"}, {4, "R14D"}, {4, "R15D"},
{2, "AX"}, {2, "CX"}, {2, "DX"}, {2, "BX"}, {2, "SP"}, {2, "BP"}, {2, "SI"}, {2, "DI"}, {3, "R8W"}, {3, "R9W"}, {4, "R10W"}, {4, "R11W"}, {4, "R12W"}, {4, "R13W"}, {4, "R14W"}, {4, "R15W"},
{2, "AL"}, {2, "CL"}, {2, "DL"}, {2, "BL"}, {2, "AH"}, {2, "CH"}, {2, "DH"}, {2, "BH"}, {3, "R8B"}, {3, "R9B"}, {4, "R10B"}, {4, "R11B"}, {4, "R12B"}, {4, "R13B"}, {4, "R14B"}, {4, "R15B"},
{3, "SPL"}, {3, "BPL"}, {3, "SIL"}, {3, "DIL"},
{2, "ES"}, {2, "CS"}, {2, "SS"}, {2, "DS"}, {2, "FS"}, {2, "GS"},
{3, "RIP"},
{3, "ST0"}, {3, "ST1"}, {3, "ST2"}, {3, "ST3"}, {3, "ST4"}, {3, "ST5"}, {3, "ST6"}, {3, "ST7"},
{3, "MM0"}, {3, "MM1"}, {3, "MM2"}, {3, "MM3"}, {3, "MM4"}, {3, "MM5"}, {3, "MM6"}, {3, "MM7"},
{4, "XMM0"}, {4, "XMM1"}, {4, "XMM2"}, {4, "XMM3"}, {4, "XMM4"}, {4, "XMM5"}, {4, "XMM6"}, {4, "XMM7"}, {4, "XMM8"}, {4, "XMM9"}, {5, "XMM10"}, {5, "XMM11"}, {5, "XMM12"}, {5, "XMM13"}, {5, "XMM14"}, {5, "XMM15"},
{4, "YMM0"}, {4, "YMM1"}, {4, "YMM2"}, {4, "YMM3"}, {4, "YMM4"}, {4, "YMM5"}, {4, "YMM6"}, {4, "YMM7"}, {4, "YMM8"}, {4, "YMM9"}, {5, "YMM10"}, {5, "YMM11"}, {5, "YMM12"}, {5, "YMM13"}, {5, "YMM14"}, {5, "YMM15"},
{3, "CR0"}, {0, ""}, {3, "CR2"}, {3, "CR3"}, {3, "CR4"}, {0, ""}, {0, ""}, {0, ""}, {3, "CR8"},
{3, "DR0"}, {3, "DR1"}, {3, "DR2"}, {3, "DR3"}, {0, ""}, {0, ""}, {3, "DR6"}, {3, "DR7"},
{0, ""} /* There must be an empty last reg, see strcat_WSR. */
};
#endif /* DISTORM_LIGHT */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
/*
operands.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef OPERANDS_H
#define OPERANDS_H
#include "config.h"
#include "decoder.h"
#include "prefix.h"
#include "instructions.h"
int operands_extract(_CodeInfo* ci, _DInst* di, _InstInfo* ii,
_iflags instFlags, _OpType type,
unsigned int modrm, _PrefixState* ps, _DecodeType effOpSz,
_DecodeType effAdrSz, _Operand* op);
#endif /* OPERANDS_H */

View File

@@ -0,0 +1,381 @@
/*
prefix.c
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#include "prefix.h"
#include "x86defs.h"
#include "instructions.h"
#include "../include/mnemonics.h"
/*
* The main purpose of this module is to keep track of all kind of prefixes a single instruction may have.
* The problem is that a single instruction may have up to six different prefix-types.
* That's why I have to detect such cases and drop those excess prefixes.
*/
int PrefixTables[256 * 2] = {
/* Decode 16/32 Bits */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* ES (0x26) CS (0x2e) */
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* DS (0x3e) SS (0x36) */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* FS(0x64) GS(0x65) OP_SIZE(0x66) ADDR_SIZE(0x67) */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* VEX2b (0xc5) VEX3b (0xc4) */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* LOCK (0xf0) REPNZ (0xf2) REP (0xf3) */
/* Decode64Bits */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* REX: 0x40 - 0x4f */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* Ignore all prefix. */
void prefixes_ignore_all(_PrefixState* ps)
{
int i;
for (i = 0; i < PFXIDX_MAX; i++)
prefixes_ignore(ps, i);
}
/* Calculates which prefixes weren't used and accordingly sets the bits in the unusedPrefixesMask. */
uint16_t prefixes_set_unused_mask(_PrefixState* ps)
{
/*
* The decodedPrefixes represents the prefixes that were *read* from the binary stream for the instruction.
* The usedPrefixes represents the prefixes that were actually used by the instruction in the *decode* phase.
* Xoring between the two will result in a 'diff' which returns the prefixes that were read
* from the stream *and* that were never used in the actual decoding.
*
* Only one prefix per type can be set in decodedPrefixes from the stream.
* Therefore it's enough to check each type once and set the flag accordingly.
* That's why we had to book-keep each prefix type and its position.
* So now we know which bits we need to set exactly in the mask.
*/
_iflags unusedPrefixesDiff = ps->decodedPrefixes ^ ps->usedPrefixes;
uint16_t unusedPrefixesMask = ps->unusedPrefixesMask;
/* Examine unused prefixes by type: */
/*
* About REX: it might be set in the diff although it was never in the stream itself.
* This is because the vrex is shared between VEX and REX and some places flag it as REX usage, while
* we were really decoding an AVX instruction.
* It's not a big problem, because the prefixes_ignore func will ignore it anyway,
* since it wasn't seen earlier. But it's important to know this.
*/
if (unusedPrefixesDiff) {
if (unusedPrefixesDiff & INST_PRE_REX) unusedPrefixesMask |= ps->pfxIndexer[PFXIDX_REX];
if (unusedPrefixesDiff & INST_PRE_SEGOVRD_MASK) unusedPrefixesMask |= ps->pfxIndexer[PFXIDX_SEG];
if (unusedPrefixesDiff & INST_PRE_LOKREP_MASK) unusedPrefixesMask |= ps->pfxIndexer[PFXIDX_LOREP];
if (unusedPrefixesDiff & INST_PRE_OP_SIZE) unusedPrefixesMask |= ps->pfxIndexer[PFXIDX_OP_SIZE];
if (unusedPrefixesDiff & INST_PRE_ADDR_SIZE) unusedPrefixesMask |= ps->pfxIndexer[PFXIDX_ADRS];
/* If a VEX instruction was found, its prefix is considered as used, therefore no point for checking for it. */
}
return unusedPrefixesMask;
}
/*
* Mark a prefix as unused, and bookkeep where we last saw this same type,
* because in the future we might want to disable it too.
*/
_INLINE_ void prefixes_track_unused(_PrefixState* ps, int index, _PrefixIndexer pi)
{
/* Mark the previously used prefix (if exists) in the unused mask. */
prefixes_ignore(ps, pi);
/* Book-keep the current index for this type. */
ps->pfxIndexer[pi] = 1 << index;
}
/*
* Read as many prefixes as possible, up to 15 bytes, and halt when we encounter non-prefix byte.
* This algorithm tries to imitate a real processor, where the same prefix can appear a few times, etc.
* The tiny complexity is that we want to know when a prefix was superfluous and mark any copy of it as unused.
* Note that the last prefix of its type will be considered as used, and all the others (of same type) before it as unused.
*/
void prefixes_decode(_CodeInfo* ci, _PrefixState* ps)
{
const uint8_t* rexPos = NULL;
const uint8_t* start = ci->code;
uint8_t byte, vex;
unsigned int index;
/*
* First thing to do, scan for prefixes, there are six types of prefixes.
* There may be up to six prefixes before a single instruction, not the same type, no special order,
* except REX/VEX must precede immediately the first opcode byte.
* BTW - This is the reason why I didn't make the REP prefixes part of the instructions (STOS/SCAS/etc).
*
* Another thing, the instruction maximum size is 15 bytes, thus if we read more than 15 bytes, we will halt.
*
* We attach all prefixes to the next instruction, there might be two or more occurrences from the same prefix.
* Also, since VEX can be allowed only once we will test it separately.
*/
for (index = 0;
(ci->codeLen > 0) && (index < INST_MAXIMUM_SIZE);
ci->code++, ci->codeLen--, index++) {
/*
NOTE: AMD treat lock/rep as two different groups... But I am based on Intel.
- Lock and Repeat:
- 0xF0 — LOCK
- 0xF2 — REPNE/REPNZ
- 0xF3 - REP/REPE/REPZ
- Segment Override:
- 0x2E - CS
- 0x36 - SS
- 0x3E - DS
- 0x26 - ES
- 0x64 - FS
- 0x65 - GS
- Operand-Size Override: 0x66, switching default size.
- Address-Size Override: 0x67, switching default size.
64 Bits:
- REX: 0x40 - 0x4f, extends register access.
- 2 Bytes VEX: 0xc4
- 3 Bytes VEX: 0xc5
32 Bits:
- 2 Bytes VEX: 0xc4 11xx-xxxx
- 3 Bytes VEX: 0xc5 11xx-xxxx
*/
/* Examine what type of prefix we got. */
byte = *ci->code;
switch (byte)
{
case PREFIX_OP_SIZE: {/* Op Size type: */
ps->decodedPrefixes |= INST_PRE_OP_SIZE;
prefixes_track_unused(ps, index, PFXIDX_OP_SIZE);
} break;
/* Look for both common arch prefixes. */
case PREFIX_LOCK: {
/* LOCK and REPx type: */
ps->decodedPrefixes |= INST_PRE_LOCK;
prefixes_track_unused(ps, index, PFXIDX_LOREP);
} break;
case PREFIX_REPNZ: {
ps->decodedPrefixes |= INST_PRE_REPNZ;
prefixes_track_unused(ps, index, PFXIDX_LOREP);
} break;
case PREFIX_REP: {
ps->decodedPrefixes |= INST_PRE_REP;
prefixes_track_unused(ps, index, PFXIDX_LOREP);
} break;
case PREFIX_CS: {
/* Seg Overide type: */
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
ps->decodedPrefixes |= INST_PRE_CS;
prefixes_track_unused(ps, index, PFXIDX_SEG);
} break;
case PREFIX_SS: {
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
ps->decodedPrefixes |= INST_PRE_SS;
prefixes_track_unused(ps, index, PFXIDX_SEG);
} break;
case PREFIX_DS: {
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
ps->decodedPrefixes |= INST_PRE_DS;
prefixes_track_unused(ps, index, PFXIDX_SEG);
} break;
case PREFIX_ES: {
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
ps->decodedPrefixes |= INST_PRE_ES;
prefixes_track_unused(ps, index, PFXIDX_SEG);
} break;
case PREFIX_FS: {
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
ps->decodedPrefixes |= INST_PRE_FS;
prefixes_track_unused(ps, index, PFXIDX_SEG);
} break;
case PREFIX_GS: {
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK;
ps->decodedPrefixes |= INST_PRE_GS;
prefixes_track_unused(ps, index, PFXIDX_SEG);
} break;
case PREFIX_ADDR_SIZE: {
/* Addr Size type: */
ps->decodedPrefixes |= INST_PRE_ADDR_SIZE;
prefixes_track_unused(ps, index, PFXIDX_ADRS);
} break;
default:
if (ci->dt == Decode64Bits) {
/* REX type, 64 bits decoding mode only: */
if ((byte & 0xf0) == 0x40) {
ps->decodedPrefixes |= INST_PRE_REX;
rexPos = ci->code;
ps->vrex = byte & 0xf; /* Keep only BXRW. */
ps->prefixExtType = PET_REX;
prefixes_track_unused(ps, index, PFXIDX_REX);
continue;
}
}
goto _Break2;
}
}
_Break2:
/* 2 Bytes VEX: */
if ((ci->codeLen >= 2) &&
(*ci->code == PREFIX_VEX2b) &&
((ci->code - start) <= INST_MAXIMUM_SIZE - 2)) {
/*
* In 32 bits the second byte has to be in the special range of Mod=11.
* Otherwise it might be a normal LDS instruction.
*/
if ((ci->dt == Decode64Bits) || (*(ci->code + 1) >= INST_DIVIDED_MODRM)) {
ps->vexPos = ci->code + 1;
ps->decodedPrefixes |= INST_PRE_VEX;
ps->prefixExtType = PET_VEX2BYTES;
/*
* VEX 1 byte bits:
* |7-6--3-2-10|
* |R|vvvv|L|pp|
* |-----------|
*/
/* -- Convert from VEX prefix to VREX flags -- */
vex = *ps->vexPos;
if (!(vex & 0x80) && (ci->dt == Decode64Bits)) ps->vrex |= PREFIX_EX_R; /* Convert VEX.R. */
if (vex & 4) ps->vrex |= PREFIX_EX_L; /* Convert VEX.L. */
ci->code += 2;
ci->codeLen -= 2;
}
}
/* 3 Bytes VEX: */
if ((ci->codeLen >= 3) &&
(*ci->code == PREFIX_VEX3b) &&
((ci->code - start) <= INST_MAXIMUM_SIZE - 3) &&
(!(ps->decodedPrefixes & INST_PRE_VEX))) {
/*
* In 32 bits the second byte has to be in the special range of Mod=11.
* Otherwise it might be a normal LES instruction.
* And we don't care now about the 3rd byte.
*/
if ((ci->dt == Decode64Bits) || (*(ci->code + 1) >= INST_DIVIDED_MODRM)) {
ps->vexPos = ci->code + 1;
ps->decodedPrefixes |= INST_PRE_VEX;
ps->prefixExtType = PET_VEX3BYTES;
/*
* VEX first and second bytes:
* |7-6-5-4----0| |7-6--3-2-10|
* |R|X|B|m-mmmm| |W|vvvv|L|pp|
* |------------| |-----------|
*/
/* -- Convert from VEX prefix to VREX flags -- */
vex = *ps->vexPos;
ps->vrex |= ((~vex >> 5) & 0x7); /* Shift and invert VEX.R/X/B to their place */
vex = *(ps->vexPos + 1);
if (vex & 4) ps->vrex |= PREFIX_EX_L; /* Convert VEX.L. */
if (vex & 0x80) ps->vrex |= PREFIX_EX_W; /* Convert VEX.W. */
/* Clear some flags if the mode isn't 64 bits. */
if (ci->dt != Decode64Bits) ps->vrex &= ~(PREFIX_EX_B | PREFIX_EX_X | PREFIX_EX_R | PREFIX_EX_W);
ci->code += 3;
ci->codeLen -= 3;
}
}
if (ci->dt == Decode64Bits) {
if (ps->decodedPrefixes & INST_PRE_REX) {
/* REX prefix must precede first byte of instruction. */
if (rexPos != (ci->code - 1)) {
ps->decodedPrefixes &= ~INST_PRE_REX;
if (ps->prefixExtType == PET_REX) ps->prefixExtType = PET_NONE; /* It might be a VEX by now, keep it that way. */
prefixes_ignore(ps, PFXIDX_REX);
}
/*
* We will disable operand size prefix,
* if it exists only after decoding the instruction, since it might be a mandatory prefix.
* This will be done after calling inst_lookup in decode_inst.
*/
}
/* In 64 bits, segment overrides of CS, DS, ES and SS are ignored. So don't take'em into account. */
if (ps->decodedPrefixes & INST_PRE_SEGOVRD_MASK32) {
ps->decodedPrefixes &= ~INST_PRE_SEGOVRD_MASK32;
prefixes_ignore(ps, PFXIDX_SEG);
}
}
/* Store number of prefixes scanned. */
ps->count = (uint8_t)(ci->code - start);
}
/*
* For every memory-indirection operand we want to set a used segment.
* If the segment is being overrided with a prefix, we will need to check if it's a default.
* Defaults don't use their prefix, e.g "mov [rsp]" can ignore a given SS: prefix,
* but still set the used segment as SS.
* This function is called only with SS and DS as defaults.
* If there's a segment prefix used, it will override the default one.
* And If the prefix is a default seg in 64 bits, it will be ignored.
*/
void prefixes_use_segment(_iflags defaultSeg, _PrefixState* ps, _DecodeType dt, _DInst* di)
{
/* Extract given segment prefix from the decoded prefixes. */
_iflags flags;
if (dt == Decode64Bits) {
if (ps->decodedPrefixes & INST_PRE_SEGOVRD_MASK64) { /* Either GS or FS. */
di->segment = ps->decodedPrefixes & INST_PRE_GS ? R_GS : R_FS;
}
return;
}
flags = ps->decodedPrefixes & INST_PRE_SEGOVRD_MASK;
/* Use the given prefix only if it's not the default. */
if (flags && (flags != defaultSeg)) {
ps->usedPrefixes |= flags;
switch (flags >> 7) /* INST_PRE_CS is 1 << 7. And the rest of the prefixes follow as bit fields. */
{
case 1: di->segment = R_CS; break;
case 2: di->segment = R_SS; break;
case 4: di->segment = R_DS; break;
case 8: di->segment = R_ES; break;
case 0x10: di->segment = R_FS; break;
case 0x20: di->segment = R_GS; break;
}
}
else {
if (defaultSeg == INST_PRE_SS) di->segment = SEGMENT_DEFAULT | R_SS;
else di->segment = SEGMENT_DEFAULT | R_DS;
}
}

View File

@@ -0,0 +1,79 @@
/*
prefix.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef PREFIX_H
#define PREFIX_H
#include "config.h"
#include "decoder.h"
/* Specifies the type of the extension prefix, such as: REX, 2 bytes VEX, 3 bytes VEX. */
typedef enum {PET_NONE = 0, PET_REX, PET_VEX2BYTES, PET_VEX3BYTES} _PrefixExtType;
/* Specifies an index into a table of prefixes by their type. */
typedef enum {PFXIDX_NONE = -1, PFXIDX_REX, PFXIDX_LOREP, PFXIDX_SEG, PFXIDX_OP_SIZE, PFXIDX_ADRS, PFXIDX_MAX} _PrefixIndexer;
/*
* This holds the prefixes state for the current instruction we decode.
* decodedPrefixes includes all specific prefixes that the instruction got.
* start is a pointer to the first prefix to take into account.
* last is a pointer to the last byte we scanned.
* Other pointers are used to keep track of prefixes positions and help us know if they appeared already and where.
*/
typedef struct {
_iflags decodedPrefixes, usedPrefixes;
/* Number of prefixes scanned for current instruction, including VEX! */
unsigned int count;
uint16_t unusedPrefixesMask;
/* Holds the offset to the prefix byte by its type. */
uint16_t pfxIndexer[PFXIDX_MAX];
_PrefixExtType prefixExtType;
/* Indicates whether the operand size prefix (0x66) was used as a mandatory prefix. */
int isOpSizeMandatory;
/* If VEX prefix is used, store the VEX.vvvv field. */
unsigned int vexV;
/* The fields B/X/R/W/L of REX and VEX are stored together in this byte. */
unsigned int vrex;
const uint8_t* vexPos;
} _PrefixState;
/*
* Intel supports 6 types of prefixes, whereas AMD supports 5 types (lock is seperated from rep/nz).
* REX is the fifth prefix type, this time I'm based on AMD64.
* VEX is the 6th, though it can't be repeated.
*/
#define MAX_PREFIXES (5)
extern int PrefixTables[256 * 2];
_INLINE_ int prefixes_is_valid(unsigned char ch, _DecodeType dt)
{
/* The predicate selects (branchlessly) second half table for 64 bits otherwise selects first half. */
return PrefixTables[ch + ((dt >> 1) << 8)];
}
/* Ignore a specific prefix type. */
_INLINE_ void prefixes_ignore(_PrefixState* ps, _PrefixIndexer pi)
{
/*
* If that type of prefix appeared already, set the bit of that *former* prefix.
* Anyway, set the new index of that prefix type to the current index, so next time we know its position.
*/
ps->unusedPrefixesMask |= ps->pfxIndexer[pi];
}
void prefixes_ignore_all(_PrefixState* ps);
uint16_t prefixes_set_unused_mask(_PrefixState* ps);
void prefixes_decode(_CodeInfo* ci, _PrefixState* ps);
void prefixes_use_segment(_iflags defaultSeg, _PrefixState* ps, _DecodeType dt, _DInst* di);
#endif /* PREFIX_H */

View File

@@ -0,0 +1,116 @@
/*
textdefs.c
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#include "textdefs.h"
#ifndef DISTORM_LIGHT
static uint8_t Nibble2ChrTable[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
#define NIBBLE_TO_CHR Nibble2ChrTable[t]
void str_hex(_WString* s, const uint8_t* buf, unsigned int len)
{
/* 256 * 2 : 2 chars per byte value. */
static const char* TextBTable =
"000102030405060708090a0b0c0d0e0f" \
"101112131415161718191a1b1c1d1e1f" \
"202122232425262728292a2b2c2d2e2f" \
"303132333435363738393a3b3c3d3e3f" \
"404142434445464748494a4b4c4d4e4f" \
"505152535455565758595a5b5c5d5e5f" \
"606162636465666768696a6b6c6d6e6f" \
"707172737475767778797a7b7c7d7e7f" \
"808182838485868788898a8b8c8d8e8f" \
"909192939495969798999a9b9c9d9e9f" \
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" \
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" \
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" \
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" \
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef" \
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
unsigned int i = 0;
/* Length is at least 1, enter loop. */
s->length = len * 2;
s->p[len * 2] = 0;
do {
RSHORT(&s->p[i]) = RSHORT(&TextBTable[(*buf) * 2]);
buf++;
i += 2;
} while (i < len * 2);
}
#ifdef SUPPORT_64BIT_OFFSET
void str_int_impl(unsigned char** s, uint64_t x)
{
int8_t* buf;
int shift = 0;
OFFSET_INTEGER t = x;
buf = (int8_t*)*s;
*buf++ = '0';
*buf++ = 'x';
if (x == 0) {
*buf = '0';
*s += 3;
return;
}
do {
t >>= 4;
shift += 4;
} while (t);
do {
shift -= 4;
t = (x >> shift) & 0xf;
*buf++ = NIBBLE_TO_CHR;
} while (shift > 0);
*s = (unsigned char*)buf;
}
#else
void str_int_impl(unsigned char** s, uint8_t src[8])
{
int8_t* buf;
int i = 0, shift = 0;
uint32_t x = RULONG(&src[sizeof(int32_t)]);
int t;
buf = (int8_t*)*s;
buf[0] = '0';
buf[1] = 'x';
buf += 2;
for (shift = 28; shift != -4; shift -= 4) {
t = (x >> shift) & 0xf;
if (i | t) buf[i++] = NIBBLE_TO_CHR;
}
x = RULONG(src);
for (shift = 28; shift != 0; shift -= 4) {
t = (x >> shift) & 0xf;
if (i | t) buf[i++] = NIBBLE_TO_CHR;
}
t = x & 0xf;
buf[i++] = NIBBLE_TO_CHR;
*s += (size_t)(i + 2);
}
#endif /* SUPPORT_64BIT_OFFSET */
#endif /* DISTORM_LIGHT */

View File

@@ -0,0 +1,58 @@
/*
textdefs.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef TEXTDEFS_H
#define TEXTDEFS_H
#include "config.h"
#include "wstring.h"
#ifndef DISTORM_LIGHT
#define PLUS_DISP_CHR '+'
#define MINUS_DISP_CHR '-'
#define OPEN_CHR '['
#define CLOSE_CHR ']'
#define SP_CHR ' '
#define SEG_OFF_CHR ':'
/*
Naming Convention:
* get - returns a pointer to a string.
* str - concatenates to string.
* hex - means the function is used for hex dump (number is padded to required size) - Little Endian output.
* code - means the function is used for disassembled instruction - Big Endian output.
* off - means the function is used for 64bit offset - Big Endian output.
* h - '0x' in front of the string.
* b - byte
* dw - double word (can be used for word also)
* qw - quad word
* all numbers are in HEX.
*/
void str_hex(_WString* s, const uint8_t* buf, unsigned int len);
#ifdef SUPPORT_64BIT_OFFSET
#define str_int(s, x) str_int_impl((s), (x))
void str_int_impl(unsigned char** s, uint64_t x);
#else
#define str_int(s, x) str_int_impl((s), (uint8_t*)&(x))
void str_int_impl(unsigned char** s, uint8_t src[8]);
#endif
#endif /* DISTORM_LIGHT */
#endif /* TEXTDEFS_H */

View File

@@ -0,0 +1,38 @@
/*
wstring.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef WSTRING_H
#define WSTRING_H
#include "config.h"
#include "../include/mnemonics.h"
#ifndef DISTORM_LIGHT
_INLINE_ void strcat_WSR(unsigned char** str, const _WRegister* reg)
{
/*
* Longest register name is YMM15 - 5 characters,
* Copy 8 so compiler can do a QWORD move.
* We copy nul termination and fix the length, so it's okay to copy more to the output buffer.
* There's a sentinel register to make sure we don't read past the end of the registers table.
*/
memcpy((int8_t*)*str, (const int8_t*)reg->p, 8);
*str += reg->length;
}
#define strfinalize_WS(s, end) do { *end = 0; s.length = (unsigned int)((size_t)end - (size_t)s.p); } while (0)
#define chrcat_WS(s, ch) do { *s = ch; s += 1; } while (0)
#define strcat_WS(s, buf, copylen, advancelen) do { memcpy((int8_t*)s, buf, copylen); s += advancelen; } while(0)
#endif /* DISTORM_LIGHT */
#endif /* WSTRING_H */

View File

@@ -0,0 +1,82 @@
/*
x86defs.h
diStorm3 - Powerful disassembler for X86/AMD64
http://ragestorm.net/distorm/
distorm at gmail dot com
Copyright (C) 2003-2021 Gil Dabah
This library is licensed under the BSD license. See the file COPYING.
*/
#ifndef X86DEFS_H
#define X86DEFS_H
#define SEG_REGS_MAX (6)
#define CREGS_MAX (9)
#define DREGS_MAX (8)
/* Maximum instruction size, including prefixes */
#define INST_MAXIMUM_SIZE (15)
/* Maximum range of imm8 (comparison type) of special SSE CMP instructions. */
#define INST_CMP_MAX_RANGE (8)
/* Maximum range of imm8 (comparison type) of special AVX VCMP instructions. */
#define INST_VCMP_MAX_RANGE (32)
/* Wait instruction byte code. */
#define INST_WAIT_INDEX (0x9b)
/* Lea instruction byte code. */
#define INST_LEA_INDEX (0x8d)
/* NOP/XCHG instruction byte code. */
#define INST_NOP_INDEX (0x90)
/* ARPL/MOVSXD instruction byte code. */
#define INST_ARPL_INDEX (0x63)
/*
* Minimal MODR/M value of divided instructions.
* It's 0xc0, two MSBs set, which indicates a general purpose register is used too.
*/
#define INST_DIVIDED_MODRM (0xc0)
/* This is the escape byte value used for 3DNow! instructions. */
#define _3DNOW_ESCAPE_BYTE (0x0f)
#define PREFIX_LOCK (0xf0)
#define PREFIX_REPNZ (0xf2)
#define PREFIX_REP (0xf3)
#define PREFIX_CS (0x2e)
#define PREFIX_SS (0x36)
#define PREFIX_DS (0x3e)
#define PREFIX_ES (0x26)
#define PREFIX_FS (0x64)
#define PREFIX_GS (0x65)
#define PREFIX_OP_SIZE (0x66)
#define PREFIX_ADDR_SIZE (0x67)
#define PREFIX_VEX2b (0xc5)
#define PREFIX_VEX3b (0xc4)
/* REX prefix value range, 64 bits mode decoding only. */
#define PREFIX_REX_LOW (0x40)
#define PREFIX_REX_HI (0x4f)
/* In order to use the extended GPR's we have to add 8 to the Modr/M info values. */
#define EX_GPR_BASE (8)
/* Mask for REX and VEX features: */
/* Base */
#define PREFIX_EX_B (1)
/* Index */
#define PREFIX_EX_X (2)
/* Register */
#define PREFIX_EX_R (4)
/* Operand Width */
#define PREFIX_EX_W (8)
/* Vector Lengh */
#define PREFIX_EX_L (0x10)
#endif /* X86DEFS_H */