More formatter changes

- Added `ZydisFormatterFormatOperand` and `ZydisFormatterFormatOperandEx` (this won't print AVX-512/KNC decorators)
- User defined callbacks should return `ZYDIS_STATUS_SKIP_OPERAND` to omit an operand (returning `ZYDIS_STATUS_SUCCESS` without writing to the buffer is now deprecated)
This commit is contained in:
flobernd 2018-02-06 19:35:54 +01:00
parent 4e189721b2
commit bbb864561d
No known key found for this signature in database
GPG Key ID: 9C3AE0ED4A969F10
4 changed files with 189 additions and 42 deletions

View File

@ -144,8 +144,8 @@ static ZydisStatus ZydisFormatterPrintMnemonic(const ZydisFormatter* formatter,
// Rewrite the instruction-mnemonic for the given instructions // Rewrite the instruction-mnemonic for the given instructions
if (instruction->operands[instruction->operandCount - 1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) if (instruction->operands[instruction->operandCount - 1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE)
{ {
const uint8_t conditionCode = const ZydisU8 conditionCode =
(uint8_t)instruction->operands[instruction->operandCount - 1].imm.value.u; (ZydisU8)instruction->operands[instruction->operandCount - 1].imm.value.u;
switch (instruction->mnemonic) switch (instruction->mnemonic)
{ {
case ZYDIS_MNEMONIC_CMPPS: case ZYDIS_MNEMONIC_CMPPS:
@ -201,9 +201,7 @@ static ZydisStatus ZydisFormatterFormatOperandImm(const ZydisFormatter* formatte
// operand, because it got replaced by the alias-mnemonic // operand, because it got replaced by the alias-mnemonic
if (userData->ommitImmediate) if (userData->ommitImmediate)
{ {
// The formatter will automatically omit the operand, if the buffer remains unchanged return ZYDIS_STATUS_SKIP_OPERAND;
// after the callback returns
return ZYDIS_STATUS_SUCCESS;
} }
// Default immediate formatting // Default immediate formatting
@ -216,7 +214,8 @@ static ZydisStatus ZydisFormatterFormatOperandImm(const ZydisFormatter* formatte
/* Helper functions */ /* Helper functions */
/* ============================================================================================== */ /* ============================================================================================== */
void disassembleBuffer(ZydisDecoder* decoder, uint8_t* data, size_t length, ZydisBool installHooks) void disassembleBuffer(ZydisDecoder* decoder, ZydisU8* data, ZydisUSize length,
ZydisBool installHooks)
{ {
ZydisFormatter formatter; ZydisFormatter formatter;
ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
@ -233,7 +232,7 @@ void disassembleBuffer(ZydisDecoder* decoder, uint8_t* data, size_t length, Zydi
(const void**)&defaultFormatOperandImm); (const void**)&defaultFormatOperandImm);
} }
uint64_t instructionPointer = 0x007FFFFFFF400000; ZydisU64 instructionPointer = 0x007FFFFFFF400000;
ZydisDecodedInstruction instruction; ZydisDecodedInstruction instruction;
ZydisCustomUserData userData; ZydisCustomUserData userData;
@ -263,7 +262,7 @@ int main()
return EXIT_FAILURE; return EXIT_FAILURE;
} }
uint8_t data[] = ZydisU8 data[] =
{ {
// cmpps xmm1, xmm4, 0x03 // cmpps xmm1, xmm4, 0x03
0x0F, 0xC2, 0xCC, 0x03, 0x0F, 0xC2, 0xCC, 0x03,

View File

@ -456,9 +456,6 @@ typedef struct ZydisFormatter_ ZydisFormatter;
* *
* Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the formatting * Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the formatting
* process to fail. * process to fail.
*
* Returning `ZYDIS_STATUS_SUCCESS` in `ZYDIS_FORMATTER_HOOK_PRINT_PREFIXES` without writing to
* the string is valid and signals that the corresponding element should not be printed.
* *
* This function type is used for: * This function type is used for:
* - `ZYDIS_FORMATTER_HOOK_PRE_INSTRUCTION` * - `ZYDIS_FORMATTER_HOOK_PRE_INSTRUCTION`
@ -482,15 +479,17 @@ typedef ZydisStatus (*ZydisFormatterFunc)(const ZydisFormatter* formatter,
* @return A zydis status code. * @return A zydis status code.
* *
* Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the formatting * Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the formatting
* process to fail. * process to fail (see exceptions below).
* *
* Returning `ZYDIS_STATUS_SUCCESS` in one of the `ZYDIS_FORMATTER_HOOK_FORMAT_OPERAND_X` hooks * Returning `ZYDIS_STATUS_SKIP_OPERAND` is valid for `ZYDIS_FORMATTER_HOOK_PRE_OPERAND`,
* without writing to the string is valid and will cause the formatter to omit the current * `ZYDIS_FORMATTER_HOOK_POST_OPERAND` and all of the `ZYDIS_FORMATTER_HOOK_FORMAT_OPERAND_XXX`
* callbacks. This will cause the formatter to omit the current operand.
*
* DEPRECATED:
* Returning `ZYDIS_STATUS_SUCCESS` without writing to the string is valid for
* `ZYDIS_FORMATTER_HOOK_PRE_OPERAND`, `ZYDIS_FORMATTER_HOOK_POST_OPERAND` and all of the
* `ZYDIS_FORMATTER_HOOK_FORMAT_OPERAND_XXX`. This will cause the formatter to omit the current
* operand. * operand.
*
* Returning `ZYDIS_STATUS_SUCCESS` in `ZYDIS_FORMATTER_HOOK_PRINT_MEMSIZE` or
* `ZYDIS_FORMATTER_HOOK_PRINT_DECORATOR` without writing to the string is valid and signals that
* the corresponding element should not be printed for the current operand.
* *
* This function type is used for: * This function type is used for:
* - `ZYDIS_FORMATTER_HOOK_PRE_OPERAND` * - `ZYDIS_FORMATTER_HOOK_PRE_OPERAND`
@ -559,9 +558,6 @@ typedef ZydisStatus (*ZydisFormatterAddressFunc)(const ZydisFormatter* formatter
* *
* @return Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the * @return Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the
* formatting process to fail. * formatting process to fail.
*
* Returning `ZYDIS_STATUS_SUCCESS` without writing to the string is valid and will cause the
* formatter to omit the current decorator.
* *
* This function type is used for: * This function type is used for:
* - `ZYDIS_FORMATTER_HOOK_PRINT_DECORATOR` * - `ZYDIS_FORMATTER_HOOK_PRINT_DECORATOR`
@ -685,6 +681,43 @@ ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatInstruction(const ZydisFormatter* f
ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter, ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter,
const ZydisDecodedInstruction* instruction, char* buffer, ZydisUSize bufferLen, void* userData); const ZydisDecodedInstruction* instruction, char* buffer, ZydisUSize bufferLen, void* userData);
/**
* @brief Formats the given operand and writes it into the output buffer.
*
* @param formatter A pointer to the `ZydisFormatter` instance.
* @param instruction A pointer to the `ZydisDecodedInstruction` struct.
* @param index The index of the operand to format.
* @param buffer A pointer to the output buffer.
* @param bufferLen The length of the output buffer.
*
* @return A zydis status code.
*
* Use `ZydisFormatterFormatInstruction` or `ZydisFormatterFormatInstructionEx` to format a
* complete instruction.
*/
ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatOperand(const ZydisFormatter* formatter,
const ZydisDecodedInstruction* instruction, ZydisU8 index, char* buffer, ZydisUSize bufferLen);
/**
* @brief Formats the given operand and writes it into the output buffer.
*
* @param formatter A pointer to the `ZydisFormatter` instance.
* @param instruction A pointer to the `ZydisDecodedInstruction` struct.
* @param index The index of the operand to format.
* @param buffer A pointer to the output buffer.
* @param bufferLen The length of the output buffer.
* @param userData A pointer to user-defined data which can be used in custom formatter
* callbacks.
*
* @return A zydis status code.
*
* Use `ZydisFormatterFormatInstruction` or `ZydisFormatterFormatInstructionEx` to format a
* complete instruction.
*/
ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatOperandEx(const ZydisFormatter* formatter,
const ZydisDecodedInstruction* instruction, ZydisU8 index, char* buffer, ZydisUSize bufferLen,
void* userData);
/* ============================================================================================== */ /* ============================================================================================== */
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -126,6 +126,16 @@ enum ZydisStatusCodes
*/ */
ZYDIS_STATUS_INVALID_MASK, ZYDIS_STATUS_INVALID_MASK,
/* ------------------------------------------------------------------------------------------ */
/* Formatter */
/* ------------------------------------------------------------------------------------------ */
/**
* @brief Returning this status code in operand-related custom formatter callbacks will cause
* the formatter to omit the operand.
*/
ZYDIS_STATUS_SKIP_OPERAND,
/* ------------------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------------------ */
/* Encoder */ /* Encoder */
/* ------------------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------------------ */
@ -141,8 +151,8 @@ enum ZydisStatusCodes
*/ */
ZYDIS_STATUS_USER = 0x10000000 ZYDIS_STATUS_USER = 0x10000000
// Max value entry intentionally omitted since users might // Max value entry intentionally omitted since users might define custom error codes for
// define custom error codes for formatter hooks. // formatter hooks.
}; };
/* ============================================================================================== */ /* ============================================================================================== */

View File

@ -89,53 +89,80 @@ static ZydisStatus ZydisFormatInstrIntel(const ZydisFormatter* formatter, ZydisS
} }
const ZydisUSize strLenPreOperand = string->length; const ZydisUSize strLenPreOperand = string->length;
// Print embedded-mask registers as decorator instead of a regular operand
if ((i == 1) && (instruction->operands[i].type == ZYDIS_OPERAND_TYPE_REGISTER) &&
(instruction->operands[i].encoding == ZYDIS_OPERAND_ENCODING_MASK))
{
goto SkipOperand;
}
ZydisStatus status;
if (formatter->funcPreOperand) if (formatter->funcPreOperand)
{ {
if (!ZYDIS_SUCCESS(formatter->funcPreOperand(formatter, string, instruction, status = formatter->funcPreOperand(formatter, string, instruction,
&instruction->operands[i], userData))) &instruction->operands[i], userData);
if (status == ZYDIS_STATUS_SKIP_OPERAND)
{ {
goto SkipOperand; goto SkipOperand;
} }
if (status != ZYDIS_STATUS_SUCCESS)
{
return status;
}
} }
switch (instruction->operands[i].type) switch (instruction->operands[i].type)
{ {
case ZYDIS_OPERAND_TYPE_REGISTER: case ZYDIS_OPERAND_TYPE_REGISTER:
ZYDIS_CHECK(formatter->funcFormatOperandReg(formatter, string, instruction, status = formatter->funcFormatOperandReg(formatter, string, instruction,
&instruction->operands[i], userData)); &instruction->operands[i], userData);
break; break;
case ZYDIS_OPERAND_TYPE_MEMORY: case ZYDIS_OPERAND_TYPE_MEMORY:
{ {
ZYDIS_CHECK(formatter->funcPrintMemSize(formatter, string, instruction, ZYDIS_CHECK(formatter->funcPrintMemSize(formatter, string, instruction,
&instruction->operands[i], userData)); &instruction->operands[i], userData));
const ZydisUSize strLenTemp = string->length; const ZydisUSize strLenTemp = string->length;
ZYDIS_CHECK(formatter->funcFormatOperandMem(formatter, string, instruction, status = formatter->funcFormatOperandMem(formatter, string, instruction,
&instruction->operands[i], userData)); &instruction->operands[i], userData);
if (strLenTemp == string->length) if ((status == ZYDIS_STATUS_SUCCESS) && (strLenTemp == string->length))
{ {
string->length = strLenPreOperand; string->length = strLenPreOperand;
} }
break; break;
} }
case ZYDIS_OPERAND_TYPE_POINTER: case ZYDIS_OPERAND_TYPE_POINTER:
ZYDIS_CHECK(formatter->funcFormatOperandPtr(formatter, string, instruction, status = formatter->funcFormatOperandPtr(formatter, string, instruction,
&instruction->operands[i], userData)); &instruction->operands[i], userData);
break; break;
case ZYDIS_OPERAND_TYPE_IMMEDIATE: case ZYDIS_OPERAND_TYPE_IMMEDIATE:
ZYDIS_CHECK(formatter->funcFormatOperandImm(formatter, string, instruction, status = formatter->funcFormatOperandImm(formatter, string, instruction,
&instruction->operands[i], userData)); &instruction->operands[i], userData);
break; break;
default: default:
return ZYDIS_STATUS_INVALID_PARAMETER; return ZYDIS_STATUS_INVALID_PARAMETER;
} }
if (status == ZYDIS_STATUS_SKIP_OPERAND)
{
goto SkipOperand;
}
if (status != ZYDIS_STATUS_SUCCESS)
{
return status;
}
if (formatter->funcPostOperand) if (formatter->funcPostOperand)
{ {
if (!ZYDIS_SUCCESS(formatter->funcPostOperand(formatter, string, instruction, status = formatter->funcPostOperand(formatter, string, instruction,
&instruction->operands[i], userData))) &instruction->operands[i], userData);
if (status == ZYDIS_STATUS_SKIP_OPERAND)
{ {
goto SkipOperand; goto SkipOperand;
} }
if (status != ZYDIS_STATUS_SUCCESS)
{
return status;
}
} }
if (strLenPreOperand == string->length) if (strLenPreOperand == string->length)
@ -203,12 +230,6 @@ static ZydisStatus ZydisFormatOperandRegIntel(const ZydisFormatter* formatter, Z
return ZYDIS_STATUS_INVALID_PARAMETER; return ZYDIS_STATUS_INVALID_PARAMETER;
} }
// We want to print embedded-mask registers as decorator instead of a regular operand
if ((operand->id == 1) && (operand->encoding == ZYDIS_OPERAND_ENCODING_MASK))
{
return ZYDIS_STATUS_SUCCESS;
}
return formatter->funcPrintRegister(formatter, string, instruction, operand, return formatter->funcPrintRegister(formatter, string, instruction, operand,
operand->reg.value, userData); operand->reg.value, userData);
} }
@ -1176,7 +1197,91 @@ ZydisStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter,
const ZydisStatus status = ZydisFormatInstruction(formatter, instruction, &string, userData); const ZydisStatus status = ZydisFormatInstruction(formatter, instruction, &string, userData);
buffer[string.length] = 0; buffer[string.length] = 0;
return status;
}
ZydisStatus ZydisFormatterFormatOperand(const ZydisFormatter* formatter,
const ZydisDecodedInstruction* instruction, ZydisU8 index, char* buffer, ZydisUSize bufferLen)
{
return ZydisFormatterFormatOperandEx(
formatter, instruction, index, buffer, bufferLen, ZYDIS_NULL);
}
ZydisStatus ZydisFormatterFormatOperandEx(const ZydisFormatter* formatter,
const ZydisDecodedInstruction* instruction, ZydisU8 index, char* buffer, ZydisUSize bufferLen,
void* userData)
{
if (!formatter || !instruction || index >= instruction->operandCount || !buffer ||
(bufferLen == 0))
{
return ZYDIS_STATUS_INVALID_PARAMETER;
}
ZydisString string;
string.buffer = buffer;
string.length = 0;
string.capacity = bufferLen - 1;
ZydisStatus status;
const ZydisDecodedOperand* operand = &instruction->operands[index];
if (formatter->funcPreOperand)
{
status = formatter->funcPreOperand(formatter, &string, instruction, operand, userData);
// We ignore `ZYDIS_STATUS_SKIP_OPERAND` as it does not make any sense to skip the only
// operand printed by this function
if ((status != ZYDIS_STATUS_SUCCESS) && (status != ZYDIS_STATUS_SKIP_OPERAND))
{
goto FinalizeString;
}
}
switch (operand->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
status = formatter->funcFormatOperandReg(formatter, &string, instruction, operand,
userData);
break;
case ZYDIS_OPERAND_TYPE_MEMORY:
status = formatter->funcPrintMemSize(formatter, &string, instruction, operand, userData);
if (!ZYDIS_SUCCESS(status))
{
goto FinalizeString;
}
status = formatter->funcFormatOperandMem(formatter, &string, instruction, operand,
userData);
break;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
status = formatter->funcFormatOperandImm(formatter, &string, instruction, operand,
userData);
break;
case ZYDIS_OPERAND_TYPE_POINTER:
status = formatter->funcFormatOperandPtr(formatter, &string, instruction, operand,
userData);
break;
default:
status = ZYDIS_STATUS_INVALID_PARAMETER;
break;
}
if (!ZYDIS_SUCCESS(status))
{
goto FinalizeString;
}
// TODO: Print AVX512/KNC decorator
if (formatter->funcPostOperand)
{
status = formatter->funcPostOperand(formatter, &string, instruction, operand, userData);
// Ignore `ZYDIS_STATUS_SKIP_OPERAND`
if (status == ZYDIS_STATUS_SKIP_OPERAND)
{
status = ZYDIS_STATUS_SUCCESS;
}
}
FinalizeString:
buffer[string.length] = 0;
return status; return status;
} }