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
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,

View File

@ -457,9 +457,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`
* - `ZYDIS_FORMATTER_HOOK_POST_INSTRUCTION`
@ -482,16 +479,18 @@ 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`
* - `ZYDIS_FORMATTER_HOOK_POST_OPERAND`
@ -560,9 +559,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

View File

@ -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.
};
/* ============================================================================================== */

View File

@ -89,53 +89,80 @@ static ZydisStatus ZydisFormatInstrIntel(const ZydisFormatter* formatter, ZydisS
}
const ZydisUSize strLenPreOperand = string->length;
if (formatter->funcPreOperand)
{
if (!ZYDIS_SUCCESS(formatter->funcPreOperand(formatter, string, instruction,
&instruction->operands[i], userData)))
// 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)
{
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;
}