From bbb864561d38130066528dc3a4db4a85c712c197 Mon Sep 17 00:00:00 2001 From: flobernd Date: Tue, 6 Feb 2018 19:35:54 +0100 Subject: [PATCH] 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) --- examples/FormatterHooks.c | 15 ++-- include/Zydis/Formatter.h | 59 ++++++++++++---- include/Zydis/Status.h | 14 +++- src/Formatter.c | 143 +++++++++++++++++++++++++++++++++----- 4 files changed, 189 insertions(+), 42 deletions(-) diff --git a/examples/FormatterHooks.c b/examples/FormatterHooks.c index 92313e9..d6edd0f 100644 --- a/examples/FormatterHooks.c +++ b/examples/FormatterHooks.c @@ -144,8 +144,8 @@ static ZydisStatus ZydisFormatterPrintMnemonic(const ZydisFormatter* formatter, // Rewrite the instruction-mnemonic for the given instructions if (instruction->operands[instruction->operandCount - 1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) { - const uint8_t conditionCode = - (uint8_t)instruction->operands[instruction->operandCount - 1].imm.value.u; + const ZydisU8 conditionCode = + (ZydisU8)instruction->operands[instruction->operandCount - 1].imm.value.u; switch (instruction->mnemonic) { case ZYDIS_MNEMONIC_CMPPS: @@ -201,9 +201,7 @@ static ZydisStatus ZydisFormatterFormatOperandImm(const ZydisFormatter* formatte // operand, because it got replaced by the alias-mnemonic if (userData->ommitImmediate) { - // The formatter will automatically omit the operand, if the buffer remains unchanged - // after the callback returns - return ZYDIS_STATUS_SUCCESS; + return ZYDIS_STATUS_SKIP_OPERAND; } // Default immediate formatting @@ -216,7 +214,8 @@ static ZydisStatus ZydisFormatterFormatOperandImm(const ZydisFormatter* formatte /* 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; ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); @@ -233,7 +232,7 @@ void disassembleBuffer(ZydisDecoder* decoder, uint8_t* data, size_t length, Zydi (const void**)&defaultFormatOperandImm); } - uint64_t instructionPointer = 0x007FFFFFFF400000; + ZydisU64 instructionPointer = 0x007FFFFFFF400000; ZydisDecodedInstruction instruction; ZydisCustomUserData userData; @@ -263,7 +262,7 @@ int main() return EXIT_FAILURE; } - uint8_t data[] = + ZydisU8 data[] = { // cmpps xmm1, xmm4, 0x03 0x0F, 0xC2, 0xCC, 0x03, diff --git a/include/Zydis/Formatter.h b/include/Zydis/Formatter.h index 4f6d0b4..d4fc980 100644 --- a/include/Zydis/Formatter.h +++ b/include/Zydis/Formatter.h @@ -456,9 +456,6 @@ typedef struct ZydisFormatter_ ZydisFormatter; * * Returning a status code other than `ZYDIS_STATUS_SUCCESS` will immediately cause the formatting * 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: * - `ZYDIS_FORMATTER_HOOK_PRE_INSTRUCTION` @@ -482,15 +479,17 @@ typedef ZydisStatus (*ZydisFormatterFunc)(const ZydisFormatter* formatter, * @return A zydis status code. * * 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 - * without writing to the string is valid and will cause the formatter to omit the current + * Returning `ZYDIS_STATUS_SKIP_OPERAND` is valid for `ZYDIS_FORMATTER_HOOK_PRE_OPERAND`, + * `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. - * - * 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: * - `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 * 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: * - `ZYDIS_FORMATTER_HOOK_PRINT_DECORATOR` @@ -685,6 +681,43 @@ ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatInstruction(const ZydisFormatter* f ZYDIS_EXPORT ZydisStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter, 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 diff --git a/include/Zydis/Status.h b/include/Zydis/Status.h index fa5abb3..dc2ee93 100644 --- a/include/Zydis/Status.h +++ b/include/Zydis/Status.h @@ -126,6 +126,16 @@ enum ZydisStatusCodes */ 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 */ /* ------------------------------------------------------------------------------------------ */ @@ -141,8 +151,8 @@ enum ZydisStatusCodes */ ZYDIS_STATUS_USER = 0x10000000 - // Max value entry intentionally omitted since users might - // define custom error codes for formatter hooks. + // Max value entry intentionally omitted since users might define custom error codes for + // formatter hooks. }; /* ============================================================================================== */ diff --git a/src/Formatter.c b/src/Formatter.c index 9cdb076..ec40974 100644 --- a/src/Formatter.c +++ b/src/Formatter.c @@ -89,53 +89,80 @@ static ZydisStatus ZydisFormatInstrIntel(const ZydisFormatter* formatter, ZydisS } 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 (!ZYDIS_SUCCESS(formatter->funcPreOperand(formatter, string, instruction, - &instruction->operands[i], userData))) + status = formatter->funcPreOperand(formatter, string, instruction, + &instruction->operands[i], userData); + if (status == ZYDIS_STATUS_SKIP_OPERAND) { goto SkipOperand; } + if (status != ZYDIS_STATUS_SUCCESS) + { + return status; + } } switch (instruction->operands[i].type) { case ZYDIS_OPERAND_TYPE_REGISTER: - ZYDIS_CHECK(formatter->funcFormatOperandReg(formatter, string, instruction, - &instruction->operands[i], userData)); + status = formatter->funcFormatOperandReg(formatter, string, instruction, + &instruction->operands[i], userData); break; case ZYDIS_OPERAND_TYPE_MEMORY: { ZYDIS_CHECK(formatter->funcPrintMemSize(formatter, string, instruction, &instruction->operands[i], userData)); const ZydisUSize strLenTemp = string->length; - ZYDIS_CHECK(formatter->funcFormatOperandMem(formatter, string, instruction, - &instruction->operands[i], userData)); - if (strLenTemp == string->length) + status = formatter->funcFormatOperandMem(formatter, string, instruction, + &instruction->operands[i], userData); + if ((status == ZYDIS_STATUS_SUCCESS) && (strLenTemp == string->length)) { string->length = strLenPreOperand; } break; } case ZYDIS_OPERAND_TYPE_POINTER: - ZYDIS_CHECK(formatter->funcFormatOperandPtr(formatter, string, instruction, - &instruction->operands[i], userData)); + status = formatter->funcFormatOperandPtr(formatter, string, instruction, + &instruction->operands[i], userData); break; case ZYDIS_OPERAND_TYPE_IMMEDIATE: - ZYDIS_CHECK(formatter->funcFormatOperandImm(formatter, string, instruction, - &instruction->operands[i], userData)); + status = formatter->funcFormatOperandImm(formatter, string, instruction, + &instruction->operands[i], userData); break; default: return ZYDIS_STATUS_INVALID_PARAMETER; } + if (status == ZYDIS_STATUS_SKIP_OPERAND) + { + goto SkipOperand; + } + if (status != ZYDIS_STATUS_SUCCESS) + { + return status; + } if (formatter->funcPostOperand) { - if (!ZYDIS_SUCCESS(formatter->funcPostOperand(formatter, string, instruction, - &instruction->operands[i], userData))) + status = formatter->funcPostOperand(formatter, string, instruction, + &instruction->operands[i], userData); + if (status == ZYDIS_STATUS_SKIP_OPERAND) { goto SkipOperand; } + if (status != ZYDIS_STATUS_SUCCESS) + { + return status; + } } if (strLenPreOperand == string->length) @@ -203,12 +230,6 @@ static ZydisStatus ZydisFormatOperandRegIntel(const ZydisFormatter* formatter, Z 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, operand->reg.value, userData); } @@ -1176,7 +1197,91 @@ ZydisStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter, const ZydisStatus status = ZydisFormatInstruction(formatter, instruction, &string, userData); 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; }