good
This commit is contained in:
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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__;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
7943
examples/raze-internal-cs2-main/dependencies/funchook/src/insts.c
Normal file
7943
examples/raze-internal-cs2-main/dependencies/funchook/src/insts.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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 */
|
||||
1365
examples/raze-internal-cs2-main/dependencies/funchook/src/operands.c
Normal file
1365
examples/raze-internal-cs2-main/dependencies/funchook/src/operands.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user