mirror of https://github.com/x64dbg/zydis
602 lines
18 KiB
C++
602 lines
18 KiB
C++
/***************************************************************************************************
|
|
|
|
Zyan Disassembler Engine
|
|
Version 1.0
|
|
|
|
Remarks : Freeware, Copyright must be included
|
|
|
|
Original Author : Florian Bernd
|
|
Modifications : Joel Höner
|
|
|
|
* 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 "ZydisInstructionFormatter.hpp"
|
|
#include "ZydisUtils.hpp"
|
|
#include <cstdarg>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
|
|
namespace Zydis
|
|
{
|
|
|
|
/* BaseInstructionFormatter ================================================================ */
|
|
|
|
const char *BaseInstructionFormatter::m_registerStrings[] =
|
|
{
|
|
/* 8 bit general purpose registers */
|
|
"al", "cl", "dl", "bl",
|
|
"ah", "ch", "dh", "bh",
|
|
"spl", "bpl", "sil", "dil",
|
|
"r8b", "r9b", "r10b", "r11b",
|
|
"r12b", "r13b", "r14b", "r15b",
|
|
/* 16 bit general purpose registers */
|
|
"ax", "cx", "dx", "bx",
|
|
"sp", "bp", "si", "di",
|
|
"r8w", "r9w", "r10w", "r11w",
|
|
"r12w", "r13w", "r14w", "r15w",
|
|
/* 32 bit general purpose registers */
|
|
"eax", "ecx", "edx", "ebx",
|
|
"esp", "ebp", "esi", "edi",
|
|
"r8d", "r9d", "r10d", "r11d",
|
|
"r12d", "r13d", "r14d", "r15d",
|
|
/* 64 bit general purpose registers */
|
|
"rax", "rcx", "rdx", "rbx",
|
|
"rsp", "rbp", "rsi", "rdi",
|
|
"r8", "r9", "r10", "r11",
|
|
"r12", "r13", "r14", "r15",
|
|
/* segment registers */
|
|
"es", "cs", "ss",
|
|
"ds", "fs", "gs",
|
|
/* control registers */
|
|
"cr0", "cr1", "cr2", "cr3",
|
|
"cr4", "cr5", "cr6", "cr7",
|
|
"cr8", "cr9", "cr10", "cr11",
|
|
"cr12", "cr13", "cr14", "cr15",
|
|
/* debug registers */
|
|
"dr0", "dr1", "dr2", "dr3",
|
|
"dr4", "dr5", "dr6", "dr7",
|
|
"dr8", "dr9", "dr10", "dr11",
|
|
"dr12", "dr13", "dr14", "dr15",
|
|
/* mmx registers */
|
|
"mm0", "mm1", "mm2", "mm3",
|
|
"mm4", "mm5", "mm6", "mm7",
|
|
/* x87 registers */
|
|
"st0", "st1", "st2", "st3",
|
|
"st4", "st5", "st6", "st7",
|
|
/* extended multimedia registers */
|
|
"xmm0", "xmm1", "xmm2", "xmm3",
|
|
"xmm4", "xmm5", "xmm6", "xmm7",
|
|
"xmm8", "xmm9", "xmm10", "xmm11",
|
|
"xmm12", "xmm13", "xmm14", "xmm15",
|
|
/* 256 bit multimedia registers */
|
|
"ymm0", "ymm1", "ymm2", "ymm3",
|
|
"ymm4", "ymm5", "ymm6", "ymm7",
|
|
"ymm8", "ymm9", "ymm10", "ymm11",
|
|
"ymm12", "ymm13", "ymm14", "ymm15",
|
|
/* instruction pointer register */
|
|
"rip"
|
|
};
|
|
|
|
void BaseInstructionFormatter::internalFormatInstruction(const InstructionInfo& /*info*/)
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
BaseInstructionFormatter::BaseInstructionFormatter()
|
|
: m_symbolResolver(nullptr)
|
|
, m_outputStringLen(0)
|
|
, m_outputUppercase(false)
|
|
{
|
|
|
|
}
|
|
|
|
BaseInstructionFormatter::BaseInstructionFormatter(
|
|
BaseSymbolResolver *symbolResolver)
|
|
: m_symbolResolver(symbolResolver)
|
|
, m_outputStringLen(0)
|
|
, m_outputUppercase(false)
|
|
{
|
|
|
|
}
|
|
|
|
const char *BaseInstructionFormatter::formatInstruction(const InstructionInfo& info)
|
|
{
|
|
// Clears the internal string buffer
|
|
outputClear();
|
|
// Calls the virtual format method that actually formats the instruction
|
|
internalFormatInstruction(info);
|
|
if (m_outputBuffer.size() == 0)
|
|
{
|
|
// The basic instruction formatter only returns the instruction menmonic.
|
|
return Internal::GetInstructionMnemonicString(info.mnemonic);
|
|
}
|
|
// Return the formatted instruction string
|
|
return outputString();
|
|
}
|
|
|
|
BaseInstructionFormatter::~BaseInstructionFormatter()
|
|
{
|
|
|
|
}
|
|
|
|
void BaseInstructionFormatter::outputClear()
|
|
{
|
|
m_outputStringLen = 0;
|
|
}
|
|
|
|
char const *BaseInstructionFormatter::outputString()
|
|
{
|
|
return& m_outputBuffer[0];
|
|
}
|
|
|
|
void BaseInstructionFormatter::outputAppend(char const *text)
|
|
{
|
|
// Get the string length including the null-terminator char
|
|
size_t strLen = strlen(text) + 1;
|
|
// Get the buffer size
|
|
size_t bufLen = m_outputBuffer.size();
|
|
// Decrease the offset by one, to exclude already existing null-terminator chars in the
|
|
// output buffer
|
|
size_t offset = (m_outputStringLen) ? m_outputStringLen - 1 : 0;
|
|
// Resize capacity of the output buffer on demand and add some extra space to improve the
|
|
// performance
|
|
if (bufLen <= (m_outputStringLen + strLen))
|
|
{
|
|
m_outputBuffer.resize(bufLen + strLen + 512);
|
|
}
|
|
// Write the text to the output buffer
|
|
memcpy(&m_outputBuffer[offset], text, strLen);
|
|
// Increase the string length
|
|
m_outputStringLen = offset + strLen;
|
|
// Convert to uppercase
|
|
if (m_outputUppercase)
|
|
{
|
|
for (size_t i = offset; i < m_outputStringLen - 1; ++i)
|
|
{
|
|
m_outputBuffer[i] = static_cast<char>(toupper(m_outputBuffer[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
void BaseInstructionFormatter::outputAppendFormatted(char const *format, ...)
|
|
{
|
|
va_list arguments;
|
|
va_start(arguments, format);
|
|
// Get the buffer size
|
|
size_t bufLen = m_outputBuffer.size();
|
|
// Decrease the offset by one, to exclude already existing null-terminator chars in the
|
|
// output buffer
|
|
size_t offset = (m_outputStringLen) ? m_outputStringLen - 1 : 0;
|
|
// Resize the output buffer on demand and add some extra space to improve the performance
|
|
if ((bufLen - m_outputStringLen) < 256)
|
|
{
|
|
bufLen = bufLen + 512;
|
|
m_outputBuffer.resize(bufLen);
|
|
}
|
|
int strLen = 0;
|
|
do
|
|
{
|
|
// If the formatted text did not fit in the output buffer, resize it, and try again
|
|
if (strLen < 0)
|
|
{
|
|
m_outputBuffer.resize(bufLen + 512);
|
|
return outputAppendFormatted(format, arguments);
|
|
}
|
|
// Write the formatted text to the output buffer
|
|
assert((bufLen - offset) > 0);
|
|
strLen =
|
|
vsnprintf_s(&m_outputBuffer[offset], bufLen - offset, _TRUNCATE, format, arguments);
|
|
} while (strLen < 0);
|
|
// Increase the string length
|
|
m_outputStringLen = offset + strLen + 1;
|
|
// Convert to uppercase
|
|
if (m_outputUppercase)
|
|
{
|
|
for (size_t i = offset; i < m_outputStringLen - 1; ++i)
|
|
{
|
|
m_outputBuffer[i] = static_cast<char>(toupper(m_outputBuffer[i]));
|
|
}
|
|
}
|
|
va_end(arguments);
|
|
}
|
|
|
|
void BaseInstructionFormatter::outputAppendAddress(const InstructionInfo& info,
|
|
uint64_t address, bool resolveSymbols)
|
|
{
|
|
uint64_t offset = 0;
|
|
const char *name = nullptr;
|
|
if (resolveSymbols)
|
|
{
|
|
name = resolveSymbol(info, address, offset);
|
|
}
|
|
if (name)
|
|
{
|
|
if (offset)
|
|
{
|
|
outputAppendFormatted("%s+%.2llX", name, offset);
|
|
} else
|
|
{
|
|
outputAppend(name);
|
|
}
|
|
} else
|
|
{
|
|
if (info.flags& IF_DISASSEMBLER_MODE_16)
|
|
{
|
|
outputAppendFormatted("%.4X", address);
|
|
} else if (info.flags& IF_DISASSEMBLER_MODE_32)
|
|
{
|
|
outputAppendFormatted("%.8lX", address);
|
|
} else if (info.flags& IF_DISASSEMBLER_MODE_64)
|
|
{
|
|
outputAppendFormatted("%.16llX", address);
|
|
} else
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BaseInstructionFormatter::outputAppendImmediate(const InstructionInfo& info,
|
|
const OperandInfo& operand, bool resolveSymbols)
|
|
{
|
|
assert(operand.type == OperandType::IMMEDIATE);
|
|
uint64_t value = 0;
|
|
if (operand.signed_lval&& (operand.size != info.operand_mode))
|
|
{
|
|
if (operand.size == 8)
|
|
{
|
|
value = static_cast<int64_t>(operand.lval.sbyte);
|
|
} else
|
|
{
|
|
assert(operand.size == 32);
|
|
value = static_cast<int64_t>(operand.lval.sdword);
|
|
}
|
|
if (info.operand_mode < 64)
|
|
{
|
|
value = value& ((1ull << info.operand_mode) - 1ull);
|
|
}
|
|
} else
|
|
{
|
|
switch (operand.size)
|
|
{
|
|
case 8:
|
|
value = operand.lval.ubyte;
|
|
break;
|
|
case 16:
|
|
value = operand.lval.uword;
|
|
break;
|
|
case 32:
|
|
value = operand.lval.udword;
|
|
break;
|
|
case 64:
|
|
value = operand.lval.uqword;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
uint64_t offset = 0;
|
|
const char *name = nullptr;
|
|
if (resolveSymbols)
|
|
{
|
|
name = resolveSymbol(info, value, offset);
|
|
}
|
|
if (name)
|
|
{
|
|
if (offset)
|
|
{
|
|
outputAppendFormatted("%s+%.2llX", name, offset);
|
|
} else
|
|
{
|
|
outputAppend(name);
|
|
}
|
|
} else
|
|
{
|
|
outputAppendFormatted("%.2llX", value);
|
|
}
|
|
}
|
|
|
|
void BaseInstructionFormatter::outputAppendDisplacement(const OperandInfo& operand)
|
|
{
|
|
assert(operand.offset > 0);
|
|
if ((operand.base == Register::NONE)&& (operand.index == Register::NONE))
|
|
{
|
|
// Assume the displacement value is unsigned
|
|
assert(operand.scale == 0);
|
|
assert(operand.offset != 8);
|
|
uint64_t value = 0;
|
|
switch (operand.offset)
|
|
{
|
|
case 16:
|
|
value = operand.lval.uword;
|
|
break;
|
|
case 32:
|
|
value = operand.lval.udword;
|
|
break;
|
|
case 64:
|
|
value = operand.lval.uqword;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
outputAppendFormatted("%.2llX", value);
|
|
} else
|
|
{
|
|
// The displacement value might be negative
|
|
assert(operand.offset != 64);
|
|
int64_t value = 0;
|
|
switch (operand.offset)
|
|
{
|
|
case 8:
|
|
value = operand.lval.sbyte;
|
|
break;
|
|
case 16:
|
|
value = operand.lval.sword;
|
|
break;
|
|
case 32:
|
|
value = operand.lval.sdword;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
if (value < 0)
|
|
{
|
|
outputAppendFormatted("-%.2lX", -value);
|
|
} else
|
|
{
|
|
outputAppendFormatted("%s%.2lX", (operand.base != Register::NONE ||
|
|
operand.index != Register::NONE) ? "+" : "", value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* IntelInstructionFormatter =============================================================== */
|
|
|
|
void IntelInstructionFormatter::outputAppendOperandCast(const OperandInfo& operand)
|
|
{
|
|
switch(operand.size)
|
|
{
|
|
case 8:
|
|
outputAppend("byte ptr " );
|
|
break;
|
|
case 16:
|
|
outputAppend("word ptr " );
|
|
break;
|
|
case 32:
|
|
outputAppend("dword ptr ");
|
|
break;
|
|
case 64:
|
|
outputAppend("qword ptr ");
|
|
break;
|
|
case 80:
|
|
outputAppend("tword ptr ");
|
|
break;
|
|
case 128:
|
|
outputAppend("oword ptr ");
|
|
break;
|
|
case 256:
|
|
outputAppend("yword ptr ");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void IntelInstructionFormatter::formatOperand(const InstructionInfo& info,
|
|
const OperandInfo& operand)
|
|
{
|
|
switch (operand.type)
|
|
{
|
|
case OperandType::REGISTER:
|
|
outputAppend(registerToString(operand.base));
|
|
break;
|
|
case OperandType::MEMORY:
|
|
if (info.flags& IF_PREFIX_SEGMENT)
|
|
{
|
|
outputAppendFormatted("%s:", registerToString(info.segment));
|
|
}
|
|
outputAppend("[");
|
|
if (operand.base == Register::RIP)
|
|
{
|
|
// TODO: Add option
|
|
outputAppendAddress(info, CalcAbsoluteTarget(info, operand), true);
|
|
} else
|
|
{
|
|
if (operand.base != Register::NONE)
|
|
{
|
|
outputAppend(registerToString(operand.base));
|
|
}
|
|
if (operand.index != Register::NONE)
|
|
{
|
|
outputAppendFormatted("%s%s", operand.base != Register::NONE ? "+" : "",
|
|
registerToString(operand.index));
|
|
if (operand.scale)
|
|
{
|
|
outputAppendFormatted("*%d", operand.scale);
|
|
}
|
|
}
|
|
if (operand.offset)
|
|
{
|
|
outputAppendDisplacement(operand);
|
|
}
|
|
}
|
|
outputAppend("]");
|
|
break;
|
|
case OperandType::POINTER:
|
|
// TODO: resolve symbols
|
|
switch (operand.size)
|
|
{
|
|
case 32:
|
|
outputAppendFormatted("word %.4X:%.4X", operand.lval.ptr.seg,
|
|
operand.lval.ptr.off& 0xFFFF);
|
|
break;
|
|
case 48:
|
|
outputAppendFormatted("dword %.4X:%.8lX", operand.lval.ptr.seg, operand.lval.ptr.off);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
break;
|
|
case OperandType::IMMEDIATE:
|
|
{
|
|
outputAppendImmediate(info, operand, true);
|
|
}
|
|
break;
|
|
case OperandType::REL_IMMEDIATE:
|
|
{
|
|
if (operand.size == 8)
|
|
{
|
|
outputAppend("short ");
|
|
}
|
|
outputAppendAddress(info, CalcAbsoluteTarget(info, operand), true);
|
|
}
|
|
break;
|
|
case OperandType::CONSTANT:
|
|
outputAppendFormatted("%.2X", operand.lval.udword);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void IntelInstructionFormatter::internalFormatInstruction(const InstructionInfo& info)
|
|
{
|
|
// Append string prefixes
|
|
if (info.flags& IF_PREFIX_LOCK)
|
|
{
|
|
outputAppend("lock ");
|
|
}
|
|
if (info.flags& IF_PREFIX_REP)
|
|
{
|
|
outputAppend("rep ");
|
|
} else if (info.flags& IF_PREFIX_REPNE)
|
|
{
|
|
outputAppend("repne ");
|
|
}
|
|
// Append the instruction mnemonic
|
|
outputAppend(Internal::GetInstructionMnemonicString(info.mnemonic));
|
|
// Append the first operand
|
|
if (info.operand[0].type != OperandType::NONE)
|
|
{
|
|
outputAppend(" ");
|
|
bool cast = false;
|
|
if (info.operand[0].type == OperandType::MEMORY)
|
|
{
|
|
if (info.operand[1].type == OperandType::IMMEDIATE ||
|
|
info.operand[1].type == OperandType::CONSTANT ||
|
|
info.operand[1].type == OperandType::NONE ||
|
|
(info.operand[0].size != info.operand[1].size))
|
|
{
|
|
cast = true;
|
|
} else if (info.operand[1].type == OperandType::REGISTER&&
|
|
info.operand[1].base == Register::CL)
|
|
{
|
|
switch (info.mnemonic)
|
|
{
|
|
case InstructionMnemonic::RCL:
|
|
case InstructionMnemonic::ROL:
|
|
case InstructionMnemonic::ROR:
|
|
case InstructionMnemonic::RCR:
|
|
case InstructionMnemonic::SHL:
|
|
case InstructionMnemonic::SHR:
|
|
case InstructionMnemonic::SAR:
|
|
cast = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (cast)
|
|
{
|
|
outputAppendOperandCast(info.operand[0]);
|
|
}
|
|
formatOperand(info, info.operand[0]);
|
|
}
|
|
// Append the second operand
|
|
if (info.operand[1].type != OperandType::NONE)
|
|
{
|
|
outputAppend(", ");
|
|
bool cast = false;
|
|
if (info.operand[1].type == OperandType::MEMORY&&
|
|
info.operand[0].size != info.operand[1].size&&
|
|
((info.operand[0].type != OperandType::REGISTER) ||
|
|
((info.operand[0].base != Register::ES)&&
|
|
(info.operand[0].base != Register::CS)&&
|
|
(info.operand[0].base != Register::SS)&&
|
|
(info.operand[0].base != Register::DS)&&
|
|
(info.operand[0].base != Register::FS)&&
|
|
(info.operand[0].base != Register::GS))))
|
|
{
|
|
cast = true;
|
|
}
|
|
if (cast)
|
|
{
|
|
outputAppendOperandCast(info.operand[1]);
|
|
}
|
|
formatOperand(info, info.operand[1]);
|
|
}
|
|
// Append the third operand
|
|
if (info.operand[2].type != OperandType::NONE)
|
|
{
|
|
outputAppend(", ");
|
|
bool cast = false;
|
|
if (info.operand[2].type == OperandType::MEMORY&&
|
|
(info.operand[2].size != info.operand[1].size))
|
|
{
|
|
cast = true;
|
|
}
|
|
if (cast)
|
|
{
|
|
outputAppendOperandCast(info.operand[2]);
|
|
}
|
|
formatOperand(info, info.operand[2]);
|
|
}
|
|
// Append the fourth operand
|
|
if (info.operand[3].type != OperandType::NONE)
|
|
{
|
|
outputAppend(", ");
|
|
formatOperand(info, info.operand[3]);
|
|
}
|
|
}
|
|
|
|
IntelInstructionFormatter::IntelInstructionFormatter()
|
|
: BaseInstructionFormatter()
|
|
{
|
|
|
|
}
|
|
|
|
IntelInstructionFormatter::IntelInstructionFormatter(
|
|
BaseSymbolResolver *symbolResolver)
|
|
: BaseInstructionFormatter(symbolResolver)
|
|
{
|
|
|
|
}
|
|
|
|
IntelInstructionFormatter::~IntelInstructionFormatter()
|
|
{
|
|
|
|
}
|
|
|
|
/* ============================================================================================== */
|
|
|
|
} |