More encoder progress

This commit is contained in:
Joel Höner 2017-01-17 20:53:34 +01:00
parent 689708fbd3
commit 14848083ae
5 changed files with 345 additions and 12 deletions

View File

@ -36,6 +36,31 @@
extern "C" {
#endif
/* ============================================================================================== */
/* Constants */
/* ============================================================================================== */
/**
* @brief Defines a mask of attributes users may excplicitly ask for.
*/
#define ZYDIS_USER_ENCODABLE_ATTRIB_MASK ( \
ZYDIS_ATTRIB_HAS_LOCK | \
ZYDIS_ATTRIB_HAS_REP | \
ZYDIS_ATTRIB_HAS_REPE | \
ZYDIS_ATTRIB_HAS_REPNE | \
ZYDIS_ATTRIB_HAS_BOUND | \
ZYDIS_ATTRIB_HAS_XACQUIRE | \
ZYDIS_ATTRIB_HAS_XRELEASE | \
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN | \
ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN | \
ZYDIS_ATTRIB_HAS_SEGMENT_CS | \
ZYDIS_ATTRIB_HAS_SEGMENT_DS | \
ZYDIS_ATTRIB_HAS_SEGMENT_SS | \
ZYDIS_ATTRIB_HAS_SEGMENT_ES | \
ZYDIS_ATTRIB_HAS_SEGMENT_FS | \
ZYDIS_ATTRIB_HAS_SEGMENT_GS \
)
/* ============================================================================================== */
/* Exported functions */
/* ============================================================================================== */
@ -49,7 +74,7 @@ extern "C" {
*
* @return A zydis status code.
*/
ZYDIS_EXPORT ZydisStatus ZydisEncoderEncodeInstruction(const void* buffer, size_t bufferLen,
ZYDIS_EXPORT ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t bufferLen,
ZydisInstructionInfo* info);
/* ============================================================================================== */

View File

@ -111,6 +111,13 @@ enum ZydisStatusCode
ZYDIS_STATUS_INVALID_MASK,
ZYDIS_STATUS_INVALID_VSIB,
/* ------------------------------------------------------------------------------------------ */
/* Encoder */
/* ------------------------------------------------------------------------------------------ */
// TODO:
ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION,
/* ------------------------------------------------------------------------------------------ */
/* Formatter */
/* ------------------------------------------------------------------------------------------ */

View File

@ -35,6 +35,7 @@
#include <Zydis/InstructionInfo.h>
#include <Zydis/Decoder.h>
#include <Zydis/Formatter.h>
#include <Zydis/Encoder.h>
#ifdef __cplusplus
extern "C" {

View File

@ -26,10 +26,51 @@
#include <Zydis/Encoder.h>
#include <string.h>
#include <stdint.h>
/* ============================================================================================== */
/* Internal context */
/* ============================================================================================== */
typedef struct ZydisEncoderTableOperand_
{
ZydisOperandEncoding encoding;
} ZydisEncoderTableOperand;
typedef uint8_t ZydisModRMMod;
enum ZydisModRMMods
{
ZYDIS_MODRM_MOD_NONE,
ZYDIS_MODRM_MOD_REGISTER,
ZYDIS_MODRM_MOD_MEMORY,
};
typedef uint8_t ZydisModeConstraint;
enum ZydisModeConstraints
{
ZYDIS_MODE_CONSTR_NONE,
ZYDIS_MODE_CONSTR_EXCLUDE64,
ZYDIS_MODE_CONSTR_REQUIRE64,
};
typedef struct ZydisEncoderTableEntry_
{
uint16_t mnemonic;
uint8_t opcode;
ZydisInstructionEncoding encoding;
uint8_t operandCount;
ZydisEncoderTableOperand operands[5];
ZydisOpcodeMap map;
ZydisInstructionAttributes mandatoryAttribs;
ZydisModRMMod modRmMod;
ZydisModeConstraint modeConstraint;
} ZydisEncoderTableEntry;
#include <Zydis/Internal/EncoderTable.inc>
/**
* @brief The encoder context struct.
*/
@ -39,6 +80,7 @@ typedef struct ZydisInstructionEncoder_
size_t bufferLen;
size_t writeOffs;
ZydisInstructionInfo* info;
const ZydisEncoderTableEntry* matchingEntry;
} ZydisInstructionEncoder;
/* ============================================================================================== */
@ -56,10 +98,31 @@ static ZydisStatus ZydisEmitByte(ZydisInstructionEncoder* encoder, uint8_t byte)
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitImm(ZydisInstructionEncoder* encoder, uint64_t imm, int bits)
{
ZYDIS_ASSERT(bits == 8 || bits == 16 || bits == 32 || bits == 64);
if (encoder->writeOffs + bits / 8 >= encoder->bufferLen)
{
return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE;
}
// TODO: bswap on big-endian
switch (bits)
{
case 8: *(uint8_t* )&encoder->buffer[encoder->writeOffs++] = (uint8_t )imm; break;
case 16: *(uint16_t*)&encoder->buffer[encoder->writeOffs++] = (uint16_t)imm; break;
case 32: *(uint32_t*)&encoder->buffer[encoder->writeOffs++] = (uint32_t)imm; break;
case 64: *(uint64_t*)&encoder->buffer[encoder->writeOffs++] = (uint64_t)imm; break;
default: ZYDIS_UNREACHABLE;
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEncodeLegacyPrefixes(ZydisInstructionEncoder* encoder)
{
ZYDIS_ASSERT(encoder);
uint64_t attribs = encoder->info->attributes;
ZydisInstructionAttributes attribs = encoder->info->attributes;
if (attribs & ZYDIS_ATTRIB_HAS_LOCK)
{
@ -173,11 +236,11 @@ static ZydisStatus ZydisEncodeEVEX(ZydisInstructionEncoder *encoder)
ZYDIS_CHECK(ZydisEmitByte(encoder, 0x62));
ZYDIS_CHECK(ZydisEmitByte(
encoder,
(encoder->info->details.evex.R & 0x01) << 7 |
(encoder->info->details.evex.X & 0x01) << 6 |
(encoder->info->details.evex.B & 0x01) << 5 |
(encoder->info->details.evex.R2 & 0x01) << 4 |
(encoder->info->details.evex.mm & 0x03) << 0
(encoder->info->details.evex.R & 0x01) << 7 |
(encoder->info->details.evex.X & 0x01) << 6 |
(encoder->info->details.evex.B & 0x01) << 5 |
(encoder->info->details.evex.R2 & 0x01) << 4 |
(encoder->info->details.evex.mm & 0x03) << 0
));
ZYDIS_CHECK(ZydisEmitByte(
encoder,
@ -198,7 +261,7 @@ static ZydisStatus ZydisEncodeEVEX(ZydisInstructionEncoder *encoder)
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder *encoder)
static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder* encoder)
{
ZYDIS_ASSERT(encoder);
@ -221,6 +284,204 @@ static ZydisStatus ZydisEncodeXOP(ZydisInstructionEncoder *encoder)
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEncodeModRM(ZydisInstructionEncoder* encoder)
{
ZYDIS_ASSERT(encoder);
ZYDIS_CHECK(ZydisEmitByte(
encoder,
(encoder->info->details.modrm.mod & 0x03) << 6 |
(encoder->info->details.modrm.reg & 0x07) << 3 |
(encoder->info->details.modrm.rm & 0x07) << 0
));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEncodeSIB(ZydisInstructionEncoder* encoder)
{
ZYDIS_ASSERT(encoder);
ZYDIS_CHECK(ZydisEmitByte(
encoder,
(encoder->info->details.sib.scale & 0x03) << 6 |
(encoder->info->details.sib.index & 0x07) << 3 |
(encoder->info->details.sib.base & 0x07) << 0
));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEncodeOpcode(ZydisInstructionEncoder* encoder)
{
ZYDIS_ASSERT(encoder);
ZYDIS_ASSERT(encoder->matchingEntry);
// Put opcode extension prefix(es), if required.
switch (encoder->matchingEntry->map)
{
case ZYDIS_OPCODE_MAP_0F:
ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F));
break;
case ZYDIS_OPCODE_MAP_0F38:
ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F));
ZYDIS_CHECK(ZydisEmitByte(encoder, 0x38));
break;
case ZYDIS_OPCODE_MAP_0F3A:
ZYDIS_CHECK(ZydisEmitByte(encoder, 0x0F));
ZYDIS_CHECK(ZydisEmitByte(encoder, 0x3A));
break;
case ZYDIS_OPCODE_MAP_XOP8:
case ZYDIS_OPCODE_MAP_XOP9:
case ZYDIS_OPCODE_MAP_XOPA:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
break;
case ZYDIS_OPCODE_MAP_DEFAULT:
break; // Nothing to do.
default:
ZYDIS_UNREACHABLE;
}
// Emit actual opcode.
ZYDIS_CHECK(ZydisEmitByte(encoder, encoder->matchingEntry->opcode));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisPrepareOperand(ZydisInstructionEncoder* encoder,
ZydisOperandInfo* operand)
{
switch (operand->encoding)
{
case ZYDIS_OPERAND_ENCODING_NONE:
break; // Nothing to do.
case ZYDIS_OPERAND_ENCODING_REG:
{
int16_t reg = ZydisRegisterGetId(operand->reg);
if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER;
encoder->info->details.modrm.reg = reg & 0x07;
encoder->info->details.rex.R = (reg & 0x08) >> 3;
encoder->info->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
if (encoder->info->details.rex.R)
{
encoder->info->attributes |= ZYDIS_ATTRIB_HAS_REX;
}
} break;
case ZYDIS_OPERAND_ENCODING_RM:
case ZYDIS_OPERAND_ENCODING_RM_CD2:
case ZYDIS_OPERAND_ENCODING_RM_CD4:
case ZYDIS_OPERAND_ENCODING_RM_CD8:
case ZYDIS_OPERAND_ENCODING_RM_CD16:
case ZYDIS_OPERAND_ENCODING_RM_CD32:
case ZYDIS_OPERAND_ENCODING_RM_CD64:
{
// Memory operand?
if (operand->reg == ZYDIS_REGISTER_NONE)
{
int32_t divisor = 1;
switch (operand->encoding)
{
case ZYDIS_OPERAND_ENCODING_RM: divisor = 1; break;
case ZYDIS_OPERAND_ENCODING_RM_CD2: divisor = 2; break;
case ZYDIS_OPERAND_ENCODING_RM_CD4: divisor = 4; break;
case ZYDIS_OPERAND_ENCODING_RM_CD8: divisor = 8; break;
case ZYDIS_OPERAND_ENCODING_RM_CD16: divisor = 16; break;
case ZYDIS_OPERAND_ENCODING_RM_CD32: divisor = 32; break;
case ZYDIS_OPERAND_ENCODING_RM_CD64: divisor = 64; break;
default: ZYDIS_UNREACHABLE;
}
// Has compressed disp encoding and is compression possible?
int32_t* sdword = &operand->mem.disp.value.sdword;
encoder->info->details.modrm.mod = 0x02 /* 32 bit disp */;
if (divisor != 1 &&
*sdword % divisor == 0 &&
*sdword / divisor <= INT8_MAX &&
*sdword / divisor >= INT8_MIN)
{
encoder->info->details.modrm.mod = 0x01 /* 8 bit disp */;
*sdword /= divisor;
}
// Nope, regular encoding. Does it fit a byte anyway?
else if (*sdword <= INT8_MAX && *sdword >= INT8_MIN)
{
encoder->info->details.modrm.mod = 0x01 /* 8 bit disp */;
}
}
// Nope, register.
else
{
int16_t reg = ZydisRegisterGetId(operand->reg);
if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER;
encoder->info->details.modrm.reg = reg & 0x07;
encoder->info->details.rex.B = (reg & 0x08) >> 3;
encoder->info->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
if (encoder->info->details.rex.B)
{
encoder->info->attributes |= ZYDIS_ATTRIB_HAS_REX;
}
encoder->info->details.modrm.mod = 0x03;
}
}
case ZYDIS_OPERAND_ENCODING_OPCODE:
break; // TODO
case ZYDIS_OPERAND_ENCODING_VVVV:
break; // TODO
case ZYDIS_OPERAND_ENCODING_AAA:
break; // TODO
case ZYDIS_OPERAND_ENCODING_IMM8_LO:
case ZYDIS_OPERAND_ENCODING_IMM8_HI:
case ZYDIS_OPERAND_ENCODING_IMM8:
case ZYDIS_OPERAND_ENCODING_IMM16:
case ZYDIS_OPERAND_ENCODING_IMM32:
case ZYDIS_OPERAND_ENCODING_IMM64:
// Nothing to do here, we put those in a later stage.
// TODO: moffs
break;
default:
ZYDIS_UNREACHABLE;
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisFindMatchingDef(const ZydisInstructionInfo* info,
const ZydisEncoderTableEntry** matchingEntry)
{
ZYDIS_ASSERT(info);
ZYDIS_ASSERT(matchingEntry);
// Locate entries with matching mnemonic.
// TODO: do binary search / hash based lookup instead
for (size_t i = 0; i < sizeof(kEncoderTable) / sizeof(kEncoderTable[0]); ++i)
{
const ZydisEncoderTableEntry* curEntry = &kEncoderTable[i];
if (curEntry->mnemonic != info->mnemonic ||
curEntry->operandCount != info->operandCount ||
curEntry->encoding != info->encoding ||
(info->mode == ZYDIS_DISASSEMBLER_MODE_64BIT &&
curEntry->modeConstraint == ZYDIS_MODE_CONSTR_EXCLUDE64) ||
(info->mode != ZYDIS_DISASSEMBLER_MODE_64BIT &&
curEntry->modeConstraint == ZYDIS_MODE_CONSTR_REQUIRE64))
{
continue;
}
// Check operands.
for (size_t k = 0; k < curEntry->operandCount; ++k)
{
const ZydisEncoderTableOperand* curEncoderOp = &curEntry->operands[k];
const ZydisOperandInfo* curReqOp = &info->operands[k];
if (curEncoderOp->encoding != curReqOp->encoding) goto continueTopLevel;
}
// Still here? We found our entry!
*matchingEntry = curEntry;
return ZYDIS_STATUS_SUCCESS;
continueTopLevel:
;
}
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
}
/* ============================================================================================== */
/* Implementation of public functions */
/* ============================================================================================== */
@ -232,12 +493,37 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t bufferLen,
if (!buffer || !bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE;
ZydisInstructionEncoder encoder;
memset(&encoder, 0, sizeof(encoder));
memset(&info->details, 0, sizeof(info->details));
encoder.buffer = (uint8_t*)buffer;
encoder.bufferLen = bufferLen;
encoder.writeOffs = 0;
encoder.info = info;
// Mask out attributes that can't be set explicitly by user.
info->attributes &= ZYDIS_USER_ENCODABLE_ATTRIB_MASK;
// Search matching instruction, collect information about what needs to be
// encoded, what prefixes are required etc..
ZYDIS_CHECK(ZydisFindMatchingDef(info, &encoder.matchingEntry));
info->opcode = encoder.matchingEntry->opcode;
info->attributes |= encoder.matchingEntry->mandatoryAttribs;
// Analyze and prepare operands.
for (size_t i = 0; i < encoder.matchingEntry->operandCount; ++i)
{
ZYDIS_CHECK(ZydisPrepareOperand(&encoder, &info->operands[i]));
}
// Do actual encoding work.
ZYDIS_CHECK(ZydisEncodeLegacyPrefixes(&encoder));
if (info->attributes & ZYDIS_ATTRIB_HAS_REX ) ZYDIS_CHECK(ZydisEncodeREX (&encoder));
if (info->attributes & ZYDIS_ATTRIB_HAS_VEX ) ZYDIS_CHECK(ZydisEncodeVEX (&encoder));
if (info->attributes & ZYDIS_ATTRIB_HAS_EVEX ) ZYDIS_CHECK(ZydisEncodeEVEX (&encoder));
if (info->attributes & ZYDIS_ATTRIB_HAS_XOP ) ZYDIS_CHECK(ZydisEncodeXOP (&encoder));
ZYDIS_CHECK(ZydisEncodeOpcode(&encoder));
if (info->attributes & ZYDIS_ATTRIB_HAS_MODRM) ZYDIS_CHECK(ZydisEncodeModRM(&encoder));
if (info->attributes & ZYDIS_ATTRIB_HAS_SIB ) ZYDIS_CHECK(ZydisEncodeSIB (&encoder));
return ZYDIS_STATUS_SUCCESS;
}

View File

@ -1,4 +1,4 @@
/***************************************************************************************************
/***************************************************************************************************
Zyan Disassembler Engine (Zydis)
@ -88,10 +88,24 @@ int main(int argc, char** argv)
continue;
}
char printBuffer[256];
ZydisFormatterFormatInstruction(&formatter, &info, printBuffer, sizeof(printBuffer));
puts(printBuffer);
//char printBuffer[256];
//ZydisFormatterFormatInstruction(
// &formatter, &info, printBuffer, sizeof(printBuffer)
//);
//puts(printBuffer);
readOffs += info.length;
// TODO: Remove
// DEBUG CODE START
//uint8_t encBuffer[15];
//ZydisStatus encStatus = ZydisEncoderEncodeInstruction(
// encBuffer, sizeof(encBuffer), &info
//);
//if (!ZYDIS_SUCCESS(encStatus)) {
// //__asm int 3;
// *(volatile int*)0 = 0;
//}
// DEBUG CODE END
}
if (readOffs < sizeof(readBuf))