/*************************************************************************************************** Zyan Disassembler Library (Zydis) Original Author : Florian Bernd, Joel Hoener * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ***************************************************************************************************/ #include "zydis/Zycore/LibC.h" #include "zydis/Zydis/Formatter.h" #include "zydis/Zydis/Internal/FormatterATT.h" #include "zydis/Zydis/Internal/FormatterIntel.h" #include "zydis/Zydis/Internal/String.h" /* ============================================================================================== */ /* Constants */ /* ============================================================================================== */ /* ---------------------------------------------------------------------------------------------- */ /* Formatter presets */ /* ---------------------------------------------------------------------------------------------- */ static const ZydisFormatter* const FORMATTER_PRESETS[ZYDIS_FORMATTER_STYLE_MAX_VALUE + 1] = { &FORMATTER_ATT, &FORMATTER_INTEL, &FORMATTER_INTEL_MASM }; /* ---------------------------------------------------------------------------------------------- */ /* ============================================================================================== */ /* Internal functions */ /* ============================================================================================== */ /* ---------------------------------------------------------------------------------------------- */ /* Helper functions */ /* ---------------------------------------------------------------------------------------------- */ void ZydisFormatterBufferInit(ZydisFormatterBuffer* buffer, char* user_buffer, ZyanUSize length) { ZYAN_ASSERT(buffer); ZYAN_ASSERT(user_buffer); ZYAN_ASSERT(length); buffer->is_token_list = ZYAN_FALSE; buffer->string.flags = ZYAN_STRING_HAS_FIXED_CAPACITY; buffer->string.vector.allocator = ZYAN_NULL; buffer->string.vector.element_size = sizeof(char); buffer->string.vector.size = 1; buffer->string.vector.capacity = length; buffer->string.vector.data = user_buffer; *user_buffer = '\0'; } void ZydisFormatterBufferInitTokenized(ZydisFormatterBuffer* buffer, ZydisFormatterToken** first_token, void* user_buffer, ZyanUSize length) { ZYAN_ASSERT(buffer); ZYAN_ASSERT(first_token); ZYAN_ASSERT(user_buffer); ZYAN_ASSERT(length); *first_token = user_buffer; (*first_token)->type = ZYDIS_TOKEN_INVALID; (*first_token)->next = 0; user_buffer = (ZyanU8*)user_buffer + sizeof(ZydisFormatterToken); length -= sizeof(ZydisFormatterToken); buffer->is_token_list = ZYAN_TRUE; buffer->capacity = length; buffer->string.flags = ZYAN_STRING_HAS_FIXED_CAPACITY; buffer->string.vector.allocator = ZYAN_NULL; buffer->string.vector.element_size = sizeof(char); buffer->string.vector.size = 1; buffer->string.vector.capacity = length; buffer->string.vector.data = user_buffer; *(char*)user_buffer = '\0'; } /* ---------------------------------------------------------------------------------------------- */ /* ============================================================================================== */ /* Exported functions */ /* ============================================================================================== */ /* ---------------------------------------------------------------------------------------------- */ /* Initialization */ /* ---------------------------------------------------------------------------------------------- */ ZyanStatus ZydisFormatterInit(ZydisFormatter* formatter, ZydisFormatterStyle style) { if (!formatter || (style > ZYDIS_FORMATTER_STYLE_MAX_VALUE)) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZYAN_MEMCPY(formatter, FORMATTER_PRESETS[style], sizeof(*formatter)); return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /* Setter */ /* ---------------------------------------------------------------------------------------------- */ ZyanStatus ZydisFormatterSetProperty(ZydisFormatter* formatter, ZydisFormatterProperty property, ZyanUPointer value) { if (!formatter) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZydisNumericBase base = (ZydisNumericBase)(-1); ZyanU8 index = 0xFF; switch (property) { case ZYDIS_FORMATTER_PROP_FORCE_SIZE: { formatter->force_memory_size = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_FORCE_SEGMENT: { formatter->force_memory_segment = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_FORCE_RELATIVE_BRANCHES: { formatter->force_relative_branches = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_FORCE_RELATIVE_RIPREL: { formatter->force_relative_riprel = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_PRINT_BRANCH_SIZE: { formatter->print_branch_size = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_DETAILED_PREFIXES: { formatter->detailed_prefixes = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_ADDR_BASE: { if ((ZydisNumericBase)value > ZYDIS_NUMERIC_BASE_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->addr_base = (ZydisNumericBase)value; break; } case ZYDIS_FORMATTER_PROP_ADDR_SIGNEDNESS: { if ((ZydisSignedness)value > ZYDIS_SIGNEDNESS_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->addr_signedness = (ZydisSignedness)value; break; } case ZYDIS_FORMATTER_PROP_ADDR_PADDING_ABSOLUTE: { formatter->addr_padding_absolute = (ZydisPadding)value; break; } case ZYDIS_FORMATTER_PROP_ADDR_PADDING_RELATIVE: { formatter->addr_padding_relative = (ZydisPadding)value; break; } case ZYDIS_FORMATTER_PROP_DISP_BASE: { if ((ZydisNumericBase)value > ZYDIS_NUMERIC_BASE_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->disp_base = (ZydisNumericBase)value; break; } case ZYDIS_FORMATTER_PROP_DISP_SIGNEDNESS: { if ((ZydisSignedness)value > ZYDIS_SIGNEDNESS_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->disp_signedness = (ZydisSignedness)value; break; } case ZYDIS_FORMATTER_PROP_DISP_PADDING: { if ((ZydisPadding)value == ZYDIS_PADDING_AUTO) { if (formatter->style > ZYDIS_FORMATTER_STYLE_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->disp_padding = FORMATTER_PRESETS[formatter->style]->disp_padding; } formatter->disp_padding = (ZydisPadding)value; break; } case ZYDIS_FORMATTER_PROP_IMM_BASE: { if ((ZydisNumericBase)value > ZYDIS_NUMERIC_BASE_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->imm_base = (ZydisNumericBase)value; break; } case ZYDIS_FORMATTER_PROP_IMM_SIGNEDNESS: { if ((ZydisSignedness)value > ZYDIS_SIGNEDNESS_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->imm_signedness = (ZydisSignedness)value; break; } case ZYDIS_FORMATTER_PROP_IMM_PADDING: { if ((ZydisPadding)value == ZYDIS_PADDING_AUTO) { if (formatter->style > ZYDIS_FORMATTER_STYLE_MAX_VALUE) { return ZYAN_STATUS_INVALID_ARGUMENT; } formatter->imm_padding = FORMATTER_PRESETS[formatter->style]->imm_padding; } formatter->imm_padding = (ZydisPadding)value; break; } case ZYDIS_FORMATTER_PROP_UPPERCASE_PREFIXES: { formatter->case_prefixes = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT; break; } case ZYDIS_FORMATTER_PROP_UPPERCASE_MNEMONIC: { formatter->case_mnemonic = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT; break; } case ZYDIS_FORMATTER_PROP_UPPERCASE_REGISTERS: { formatter->case_registers = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT; break; } case ZYDIS_FORMATTER_PROP_UPPERCASE_TYPECASTS: { formatter->case_typecasts = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT; break; } case ZYDIS_FORMATTER_PROP_UPPERCASE_DECORATORS: { formatter->case_decorators = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT; break; } case ZYDIS_FORMATTER_PROP_DEC_PREFIX: { base = ZYDIS_NUMERIC_BASE_DEC; index = 0; break; } case ZYDIS_FORMATTER_PROP_DEC_SUFFIX: { base = ZYDIS_NUMERIC_BASE_DEC; index = 1; break; } case ZYDIS_FORMATTER_PROP_HEX_UPPERCASE: { formatter->hex_uppercase = (value) ? ZYAN_TRUE : ZYAN_FALSE; break; } case ZYDIS_FORMATTER_PROP_HEX_PREFIX: { base = ZYDIS_NUMERIC_BASE_HEX; index = 0; break; } case ZYDIS_FORMATTER_PROP_HEX_SUFFIX: { base = ZYDIS_NUMERIC_BASE_HEX; index = 1; break; } default: return ZYAN_STATUS_INVALID_ARGUMENT; } // Set prefix or suffix if (base != (ZydisNumericBase)(-1)) { if (value) { const ZyanUSize len = ZYAN_STRLEN((char*)value); if (len > 10) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZYAN_MEMCPY(formatter->number_format[base][index].buffer, (void*)value, len); formatter->number_format[base][index].buffer[len] = '\0'; formatter->number_format[base][index].string_data.string.vector.data = formatter->number_format[base][index].buffer; formatter->number_format[base][index].string_data.string.vector.size = len + 1; formatter->number_format[base][index].string = &formatter->number_format[base][index].string_data; } else { formatter->number_format[base][index].string = ZYAN_NULL; } } return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisFormatterSetHook(ZydisFormatter* formatter, ZydisFormatterFunction type, const void** callback) { if (!formatter || !callback || (type > ZYDIS_FORMATTER_FUNC_MAX_VALUE)) { return ZYAN_STATUS_INVALID_ARGUMENT; } const void* const temp = *callback; // The following code relies on the order of the enum values and the function fields inside // the `ZydisFormatter` struct #ifdef ZYAN_DEBUG const ZyanUPointer* test = (ZyanUPointer*)(&formatter->func_pre_instruction + type); switch (type) { case ZYDIS_FORMATTER_FUNC_PRE_INSTRUCTION: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_pre_instruction ); break; case ZYDIS_FORMATTER_FUNC_POST_INSTRUCTION: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_post_instruction ); break; case ZYDIS_FORMATTER_FUNC_FORMAT_INSTRUCTION: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_instruction); break; case ZYDIS_FORMATTER_FUNC_PRE_OPERAND: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_pre_operand ); break; case ZYDIS_FORMATTER_FUNC_POST_OPERAND: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_post_operand ); break; case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_REG: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_reg); break; case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_MEM: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_mem); break; case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_PTR: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_ptr); break; case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_IMM: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_imm); break; case ZYDIS_FORMATTER_FUNC_PRINT_MNEMONIC: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_mnemonic ); break; case ZYDIS_FORMATTER_FUNC_PRINT_REGISTER: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_register ); break; case ZYDIS_FORMATTER_FUNC_PRINT_ADDRESS_ABS: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_address_abs ); break; case ZYDIS_FORMATTER_FUNC_PRINT_ADDRESS_REL: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_address_rel ); break; case ZYDIS_FORMATTER_FUNC_PRINT_DISP: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_disp ); break; case ZYDIS_FORMATTER_FUNC_PRINT_IMM: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_imm ); break; case ZYDIS_FORMATTER_FUNC_PRINT_TYPECAST: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_typecast ); break; case ZYDIS_FORMATTER_FUNC_PRINT_SEGMENT: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_segment ); break; case ZYDIS_FORMATTER_FUNC_PRINT_PREFIXES: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_prefixes ); break; case ZYDIS_FORMATTER_FUNC_PRINT_DECORATOR: ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_decorator ); break; default: ZYAN_UNREACHABLE; } #endif *callback = *(const void**)(&formatter->func_pre_instruction + type); if (!temp) { return ZYAN_STATUS_SUCCESS; } ZYAN_MEMCPY(&formatter->func_pre_instruction + type, &temp, sizeof(ZyanUPointer)); return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /* Formatting */ /* ---------------------------------------------------------------------------------------------- */ ZyanStatus ZydisFormatterFormatInstruction(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, char* buffer, ZyanUSize length, ZyanU64 runtime_address) { return ZydisFormatterFormatInstructionEx(formatter, instruction, buffer, length, runtime_address, ZYAN_NULL); } ZyanStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, char* buffer, ZyanUSize length, ZyanU64 runtime_address, void* user_data) { if (!formatter || !instruction || !buffer || (length == 0)) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZydisFormatterBuffer formatter_buffer; ZydisFormatterBufferInit(&formatter_buffer, buffer, length); ZydisFormatterContext context; context.instruction = instruction; context.runtime_address = runtime_address; context.operand = ZYAN_NULL; context.user_data = user_data; if (formatter->func_pre_instruction) { ZYAN_CHECK(formatter->func_pre_instruction(formatter, &formatter_buffer, &context)); } ZYAN_CHECK(formatter->func_format_instruction(formatter, &formatter_buffer, &context)); if (formatter->func_post_instruction) { ZYAN_CHECK(formatter->func_post_instruction(formatter, &formatter_buffer, &context)); } return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisFormatterFormatOperand(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, ZyanU8 index, char* buffer, ZyanUSize length, ZyanU64 runtime_address) { return ZydisFormatterFormatOperandEx(formatter, instruction, index, buffer, length, runtime_address, ZYAN_NULL); } ZyanStatus ZydisFormatterFormatOperandEx(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, ZyanU8 index, char* buffer, ZyanUSize length, ZyanU64 runtime_address, void* user_data) { if (!formatter || !instruction || index >= instruction->operand_count || !buffer || (length == 0)) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZydisFormatterBuffer formatter_buffer; ZydisFormatterBufferInit(&formatter_buffer, buffer, length); ZydisFormatterContext context; context.instruction = instruction; context.runtime_address = runtime_address; context.operand = &instruction->operands[index]; context.user_data = user_data; // We ignore `ZYDIS_STATUS_SKIP_TOKEN` for all operand-functions as it does not make any sense // to skip the only operand printed by this function if (formatter->func_pre_operand) { ZYAN_CHECK(formatter->func_pre_operand(formatter, &formatter_buffer, &context)); } switch (context.operand->type) { case ZYDIS_OPERAND_TYPE_REGISTER: ZYAN_CHECK(formatter->func_format_operand_reg(formatter, &formatter_buffer, &context)); break; case ZYDIS_OPERAND_TYPE_MEMORY: ZYAN_CHECK(formatter->func_format_operand_mem(formatter, &formatter_buffer, &context)); break; case ZYDIS_OPERAND_TYPE_IMMEDIATE: ZYAN_CHECK(formatter->func_format_operand_imm(formatter, &formatter_buffer, &context)); break; case ZYDIS_OPERAND_TYPE_POINTER: ZYAN_CHECK(formatter->func_format_operand_ptr(formatter, &formatter_buffer, &context)); break; default: return ZYAN_STATUS_INVALID_ARGUMENT; } if (formatter->func_post_operand) { ZYAN_CHECK(formatter->func_post_operand(formatter, &formatter_buffer, &context)); } return ZYAN_STATUS_SUCCESS; } /* ---------------------------------------------------------------------------------------------- */ /* Tokenizing */ /* ---------------------------------------------------------------------------------------------- */ ZyanStatus ZydisFormatterTokenizeInstruction(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, void* buffer, ZyanUSize length, ZyanU64 runtime_address, ZydisFormatterTokenConst** token) { return ZydisFormatterTokenizeInstructionEx(formatter, instruction, buffer, length, runtime_address, token, ZYAN_NULL); } ZyanStatus ZydisFormatterTokenizeInstructionEx(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, void* buffer, ZyanUSize length, ZyanU64 runtime_address, ZydisFormatterTokenConst** token, void* user_data) { if (!formatter || !instruction || !buffer || (length <= sizeof(ZydisFormatterToken)) || !token) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZydisFormatterBuffer formatter_buffer; ZydisFormatterToken* first_token; ZydisFormatterBufferInitTokenized(&formatter_buffer, &first_token, buffer, length); ZydisFormatterContext context; context.instruction = instruction; context.runtime_address = runtime_address; context.operand = ZYAN_NULL; context.user_data = user_data; if (formatter->func_pre_instruction) { ZYAN_CHECK(formatter->func_pre_instruction(formatter, &formatter_buffer, &context)); } ZYAN_CHECK(formatter->func_format_instruction(formatter, &formatter_buffer, &context)); if (formatter->func_post_instruction) { ZYAN_CHECK(formatter->func_post_instruction(formatter, &formatter_buffer, &context)); } if (first_token->next) { *token = (ZydisFormatterTokenConst*)((ZyanU8*)first_token + sizeof(ZydisFormatterToken) + first_token->next); return ZYAN_STATUS_SUCCESS; } *token = first_token; return ZYAN_STATUS_SUCCESS; } ZyanStatus ZydisFormatterTokenizeOperand(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, ZyanU8 index, void* buffer, ZyanUSize length, ZyanU64 runtime_address, ZydisFormatterTokenConst** token) { return ZydisFormatterTokenizeOperandEx(formatter, instruction, index, buffer, length, runtime_address, token, ZYAN_NULL); } ZyanStatus ZydisFormatterTokenizeOperandEx(const ZydisFormatter* formatter, const ZydisDecodedInstruction* instruction, ZyanU8 index, void* buffer, ZyanUSize length, ZyanU64 runtime_address, ZydisFormatterTokenConst** token, void* user_data) { if (!formatter || !instruction || (index >= instruction->operand_count) || !buffer || (length <= sizeof(ZydisFormatterToken)) || !token) { return ZYAN_STATUS_INVALID_ARGUMENT; } ZydisFormatterToken* first_token; ZydisFormatterBuffer formatter_buffer; ZydisFormatterBufferInitTokenized(&formatter_buffer, &first_token, buffer, length); ZydisFormatterContext context; context.instruction = instruction; context.runtime_address = runtime_address; context.operand = &instruction->operands[index]; context.user_data = user_data; // We ignore `ZYDIS_STATUS_SKIP_TOKEN` for all operand-functions as it does not make any sense // to skip the only operand printed by this function if (formatter->func_pre_operand) { ZYAN_CHECK(formatter->func_pre_operand(formatter, &formatter_buffer, &context)); } switch (context.operand->type) { case ZYDIS_OPERAND_TYPE_REGISTER: ZYAN_CHECK(formatter->func_format_operand_reg(formatter, &formatter_buffer, &context)); break; case ZYDIS_OPERAND_TYPE_MEMORY: ZYAN_CHECK(formatter->func_format_operand_mem(formatter, &formatter_buffer, &context)); break; case ZYDIS_OPERAND_TYPE_IMMEDIATE: ZYAN_CHECK(formatter->func_format_operand_imm(formatter, &formatter_buffer, &context)); break; case ZYDIS_OPERAND_TYPE_POINTER: ZYAN_CHECK(formatter->func_format_operand_ptr(formatter, &formatter_buffer, &context)); break; default: return ZYAN_STATUS_INVALID_ARGUMENT; } if (formatter->func_post_operand) { ZYAN_CHECK(formatter->func_post_operand(formatter, &formatter_buffer, &context)); } if (first_token->next) { *token = (ZydisFormatterTokenConst*)((ZyanU8*)first_token + sizeof(ZydisFormatterToken) + first_token->next); return ZYAN_STATUS_SUCCESS; } *token = first_token; return ZYAN_STATUS_SUCCESS; } /* ============================================================================================== */ /* ============================================================================================== */