You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
5.3 KiB

#include "perses.hpp"
using namespace perses;
static Disassembler s_disasm;
Disassembler* Disassembler::instance()
{
return &s_disasm;
}
void Disassembler::create(ZydisMachineMode mode)
{
// - Initialize formatter
ZydisFormatterInit(&s_disasm._formatter, ZYDIS_FORMATTER_STYLE_INTEL);
ZydisFormatterSetProperty(&s_disasm._formatter, ZYDIS_FORMATTER_PROP_FORCE_SIZE, ZYAN_TRUE);
s_disasm._mode = mode;
if (mode == ZYDIS_MACHINE_MODE_LONG_64)
{
ZydisDecoderInit(&s_disasm._decoder, s_disasm._mode, ZYDIS_STACK_WIDTH_64);
return;
}
if (mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32)
{
ZydisDecoderInit(&s_disasm._decoder, s_disasm._mode, ZYDIS_STACK_WIDTH_32);
return;
}
PERSES_THROW("Unexpected machine mode passed into Disassembler::create()!");
}
bool Disassembler::decode(void* buf, instruction_t* instr)
{
ZyanStatus status = ZydisDecoderDecodeFull(&_decoder, buf, 0xFFF, &instr->decoded, instr->operands, ZYDIS_MAX_OPERAND_COUNT_VISIBLE, ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY);
if (ZYAN_SUCCESS(status))
{
instr->raw.resize(instr->decoded.length);
memcpy(&instr->raw[0], buf, instr->decoded.length);
return true;
}
return false;
}
bool Disassembler::decode(void* buf, ZydisDecodedInstruction* instr, ZydisDecodedOperand* op)
{
return ZYAN_SUCCESS(
ZydisDecoderDecodeFull(&_decoder, buf, 0xFFF, instr, op, ZYDIS_MAX_OPERAND_COUNT_VISIBLE, ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY));
}
u64 Disassembler::calcAbsolute(instruction_t* instr)
{
u64 dst = 0ull;
ZydisCalcAbsoluteAddress(&instr->decoded, &instr->operands[0], instr->address, &dst);
return dst;
}
u64 perses::Disassembler::calcAbsolute(ZydisDecodedInstruction* instr, ZydisDecodedOperand* op, u64 address)
{
u64 dst = 0ull;
ZyanStatus status = ZydisCalcAbsoluteAddress(instr, op, address, &dst);
return dst;
}
bool Disassembler::getSegments(instruction_t* intr, ZydisInstructionSegments* segments)
{
return ZYAN_SUCCESS(
ZydisGetInstructionSegments(&intr->decoded, segments));
}
bool Disassembler::isJmp(instruction_t* i)
{
ZydisDecodedInstruction* instr = &i->decoded;
switch (instr->mnemonic)
{
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
case ZYDIS_MNEMONIC_JMP:
case ZYDIS_MNEMONIC_CALL:
return true;
default:
return false;
}
return false;
}
bool Disassembler::isBbTerminatorInstruction(instruction_t* i)
{
// Check if the instruction ends a basic block
ZydisDecodedInstruction* instr = &i->decoded;
switch (instr->mnemonic)
{
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
case ZYDIS_MNEMONIC_JMP:
case ZYDIS_MNEMONIC_RET:
return true;
default:
return false;
}
return false;
}
std::string Disassembler::format(address addr, ZydisDecodedInstruction* instr, ZydisDecodedOperand* op)
{
char buf[0xFF]{};
ZyanStatus status = 0;
status = ZydisFormatterFormatInstruction(&_formatter, instr, op, instr->operand_count_visible, buf, sizeof(buf), addr.uintptr());
if (!ZYAN_SUCCESS(status))
{
return fmt::format("Unexpected error when formatting address: 0x{:X}", addr.uintptr());
}
return buf;
}
void Routine::buildFromCode(address buf)
{
return;
}
void Routine::printAssembly(uint32_t numInstructions)
{
if (empty())
return;
logger()->info("** Printing routine: 0x{:X}", at(0).address);
int count = 0;
for (auto& instr : *this)
{
if (numInstructions != -1)
{
if (count++ >= numInstructions)
break;
}
std::string fmt = Disassembler::instance()->format(instr.address, &instr.decoded, instr.operands);
logger()->debug("** 0x{:X}\t\t|\t{}", instr.address, fmt);
}
}
size_t perses::Routine::codeSize() const
{
size_t sz {};
for (auto& insn : *this)
sz += insn.decoded.length;
return sz;
}
bool instruction_t::isMnemonic(ZydisMnemonic mnem) const
{
return decoded.mnemonic == mnem;
}
bool instruction_t::isOperandType(size_t index, ZydisOperandType type) const
{
if (const ZydisDecodedOperand* op = getOperand(index))
{
return op->type == type;
}
return false;
}
const ZydisDecodedOperand* instruction_t::getOperand(size_t index) const
{
if (index >= decoded.operand_count_visible)
return nullptr;
return &operands[index];
}
size_t instruction_t::getFirstSegmentOffset(ZydisInstructionSegment type)
{
ZydisInstructionSegments segs { };
Disassembler::instance()->getSegments(this, &segs);
for (auto& seg : segs.segments)
{
if (seg.type == type)
return seg.offset;
}
return 0ull;
}