#include "content/views/view_disassembler.hpp" #include #include #include #include using namespace std::literals::string_literals; namespace hex::plugin::builtin { ViewDisassembler::ViewDisassembler() : View("hex.builtin.view.disassembler.name") { EventManager::subscribe(this, [this]() { this->disassemble(); }); EventManager::subscribe(this, [this](Region region) { if (this->m_shouldMatchSelection) { if (region.address == size_t(-1)) { this->m_codeRegion[0] = this->m_codeRegion[1] = 0; } else { this->m_codeRegion[0] = region.address; this->m_codeRegion[1] = region.address + region.size - 1; } } }); EventManager::subscribe(this, [this] { this->m_disassembly.clear(); }); } ViewDisassembler::~ViewDisassembler() { EventManager::unsubscribe(this); EventManager::unsubscribe(this); EventManager::unsubscribe(this); } void ViewDisassembler::disassemble() { this->m_disassembly.clear(); this->m_disassembling = true; std::thread([this] { csh capstoneHandle; cs_insn *instructions = nullptr; cs_mode mode = this->m_mode; if (cs_open(Disassembler::toCapstoneArchitecture(this->m_architecture), mode, &capstoneHandle) == CS_ERR_OK) { cs_option(capstoneHandle, CS_OPT_SKIPDATA, CS_OPT_ON); auto provider = ImHexApi::Provider::get(); std::vector buffer(2048, 0x00); size_t size = (this->m_codeRegion[1] - this->m_codeRegion[0] + 1); auto task = ImHexApi::Tasks::createTask("hex.builtin.view.disassembler.disassembling", size); for (u64 address = 0; address < size; address += 2048) { task.update(address); size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address); provider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize); size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions); if (instructionCount == 0) break; this->m_disassembly.reserve(this->m_disassembly.size() + instructionCount); u64 usedBytes = 0; for (u32 i = 0; i < instructionCount; i++) { const auto &instr = instructions[i]; Disassembly disassembly = { }; disassembly.address = instr.address; disassembly.offset = this->m_codeRegion[0] + address + usedBytes; disassembly.size = instr.size; disassembly.mnemonic = instr.mnemonic; disassembly.operators = instr.op_str; for (u16 j = 0; j < instr.size; j++) disassembly.bytes += hex::format("{0:02X} ", instr.bytes[j]); disassembly.bytes.pop_back(); this->m_disassembly.push_back(disassembly); usedBytes += instr.size; } if (instructionCount < bufferSize) address -= (bufferSize - usedBytes); cs_free(instructions, instructionCount); } cs_close(&capstoneHandle); } this->m_disassembling = false; }).detach(); } void ViewDisassembler::drawContent() { if (ImGui::Begin(View::toWindowName("hex.builtin.view.disassembler.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { auto provider = ImHexApi::Provider::get(); if (ImHexApi::Provider::isValid() && provider->isReadable()) { ImGui::TextUnformatted("hex.builtin.view.disassembler.position"_lang); ImGui::Separator(); ImGui::InputScalar("hex.builtin.view.disassembler.base"_lang, ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal); ImGui::InputScalarN("hex.builtin.view.disassembler.region"_lang, ImGuiDataType_U64, this->m_codeRegion, 2, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal); ImGui::Checkbox("hex.builtin.common.match_selection"_lang, &this->m_shouldMatchSelection); if (ImGui::IsItemEdited()) { // Force execution of Region Selection Event ImHexApi::HexEditor::setSelection(0, 0); } ImGui::NewLine(); ImGui::TextUnformatted("hex.builtin.view.disassembler.settings.header"_lang); ImGui::Separator(); if (ImGui::Combo("hex.builtin.view.disassembler.arch"_lang, reinterpret_cast(&this->m_architecture), Disassembler::ArchitectureNames, Disassembler::getArchitectureSupportedCount())) this->m_mode = cs_mode(0); if (ImGui::BeginChild("modes", ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 6), true, ImGuiWindowFlags_AlwaysAutoResize)) { static int littleEndian = true; ImGui::RadioButton("hex.builtin.common.little_endian"_lang, &littleEndian, true); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.common.big_endian"_lang, &littleEndian, false); ImGui::NewLine(); switch (this->m_architecture) { case Architecture::ARM: { static int mode = CS_MODE_ARM; ImGui::RadioButton("hex.builtin.view.disassembler.arm.arm"_lang, &mode, CS_MODE_ARM); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.arm.thumb"_lang, &mode, CS_MODE_THUMB); static int extraMode = 0; ImGui::RadioButton("hex.builtin.view.disassembler.arm.default"_lang, &extraMode, 0); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.arm.cortex_m"_lang, &extraMode, CS_MODE_MCLASS); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.arm.armv8"_lang, &extraMode, CS_MODE_V8); this->m_mode = cs_mode(mode | extraMode); } break; case Architecture::MIPS: { static int mode = CS_MODE_MIPS32; ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32"_lang, &mode, CS_MODE_MIPS32); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips64"_lang, &mode, CS_MODE_MIPS64); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32R6"_lang, &mode, CS_MODE_MIPS32R6); ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips2"_lang, &mode, CS_MODE_MIPS2); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips3"_lang, &mode, CS_MODE_MIPS3); static bool microMode; ImGui::Checkbox("hex.builtin.view.disassembler.mips.micro"_lang, µMode); this->m_mode = cs_mode(mode | (microMode ? CS_MODE_MICRO : cs_mode(0))); } break; case Architecture::X86: { static int mode = CS_MODE_32; ImGui::RadioButton("hex.builtin.view.disassembler.16bit"_lang, &mode, CS_MODE_16); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); this->m_mode = cs_mode(mode); } break; case Architecture::PPC: { static int mode = CS_MODE_32; ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64); static bool qpx = false; ImGui::Checkbox("hex.builtin.view.disassembler.ppc.qpx"_lang, &qpx); static bool spe = false; ImGui::Checkbox("hex.builtin.view.disassembler.ppc.spe"_lang, &spe); static bool booke = false; ImGui::Checkbox("hex.builtin.view.disassembler.ppc.booke"_lang, &booke); this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)) | (spe ? CS_MODE_SPE : cs_mode(0)) | (booke ? CS_MODE_BOOKE : cs_mode(0))); } break; case Architecture::SPARC: { static bool v9Mode = false; ImGui::Checkbox("hex.builtin.view.disassembler.sparc.v9"_lang, &v9Mode); this->m_mode = cs_mode(v9Mode ? CS_MODE_V9 : cs_mode(0)); } break; case Architecture::RISCV: { static int mode = CS_MODE_RISCV32; ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_RISCV32); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_RISCV64); static bool compressed = false; ImGui::Checkbox("hex.builtin.view.disassembler.riscv.compressed"_lang, &compressed); this->m_mode = cs_mode(mode | (compressed ? CS_MODE_RISCVC : cs_mode(0))); } break; case Architecture::M68K: { static int selectedMode = 0; std::pair modes[] = { {"hex.builtin.view.disassembler.m68k.000"_lang, CS_MODE_M68K_000}, { "hex.builtin.view.disassembler.m68k.010"_lang, CS_MODE_M68K_010}, { "hex.builtin.view.disassembler.m68k.020"_lang, CS_MODE_M68K_020}, { "hex.builtin.view.disassembler.m68k.030"_lang, CS_MODE_M68K_030}, { "hex.builtin.view.disassembler.m68k.040"_lang, CS_MODE_M68K_040}, { "hex.builtin.view.disassembler.m68k.060"_lang, CS_MODE_M68K_060}, }; if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { if (ImGui::Selectable(modes[i].first)) selectedMode = i; } ImGui::EndCombo(); } this->m_mode = cs_mode(modes[selectedMode].second); } break; case Architecture::M680X: { static int selectedMode = 0; std::pair modes[] = { {"hex.builtin.view.disassembler.m680x.6301"_lang, CS_MODE_M680X_6301 }, { "hex.builtin.view.disassembler.m680x.6309"_lang, CS_MODE_M680X_6309 }, { "hex.builtin.view.disassembler.m680x.6800"_lang, CS_MODE_M680X_6800 }, { "hex.builtin.view.disassembler.m680x.6801"_lang, CS_MODE_M680X_6801 }, { "hex.builtin.view.disassembler.m680x.6805"_lang, CS_MODE_M680X_6805 }, { "hex.builtin.view.disassembler.m680x.6808"_lang, CS_MODE_M680X_6808 }, { "hex.builtin.view.disassembler.m680x.6809"_lang, CS_MODE_M680X_6809 }, { "hex.builtin.view.disassembler.m680x.6811"_lang, CS_MODE_M680X_6811 }, { "hex.builtin.view.disassembler.m680x.cpu12"_lang, CS_MODE_M680X_CPU12}, { "hex.builtin.view.disassembler.m680x.hcs08"_lang, CS_MODE_M680X_HCS08}, }; if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { if (ImGui::Selectable(modes[i].first)) selectedMode = i; } ImGui::EndCombo(); } this->m_mode = cs_mode(modes[selectedMode].second); } break; case Architecture::MOS65XX: { static int selectedMode = 0; std::pair modes[] = { {"hex.builtin.view.disassembler.mos65xx.6502"_lang, CS_MODE_MOS65XX_6502 }, { "hex.builtin.view.disassembler.mos65xx.65c02"_lang, CS_MODE_MOS65XX_65C02 }, { "hex.builtin.view.disassembler.mos65xx.w65c02"_lang, CS_MODE_MOS65XX_W65C02 }, { "hex.builtin.view.disassembler.mos65xx.65816"_lang, CS_MODE_MOS65XX_65816 }, { "hex.builtin.view.disassembler.mos65xx.65816_long_m"_lang, CS_MODE_MOS65XX_65816_LONG_M }, { "hex.builtin.view.disassembler.mos65xx.65816_long_x"_lang, CS_MODE_MOS65XX_65816_LONG_X }, { "hex.builtin.view.disassembler.mos65xx.65816_long_mx"_lang, CS_MODE_MOS65XX_65816_LONG_MX}, }; if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) { for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) { if (ImGui::Selectable(modes[i].first)) selectedMode = i; } ImGui::EndCombo(); } this->m_mode = cs_mode(modes[selectedMode].second); } break; case Architecture::BPF: { static int mode = CS_MODE_BPF_CLASSIC; ImGui::RadioButton("hex.builtin.view.disassembler.bpf.classic"_lang, &mode, CS_MODE_BPF_CLASSIC); ImGui::SameLine(); ImGui::RadioButton("hex.builtin.view.disassembler.bpf.extended"_lang, &mode, CS_MODE_BPF_EXTENDED); this->m_mode = cs_mode(mode); } break; case Architecture::EVM: case Architecture::TMS320C64X: case Architecture::ARM64: case Architecture::SYSZ: case Architecture::XCORE: case Architecture::WASM: case Architecture::MAX: this->m_mode = cs_mode(0); break; } } ImGui::EndChild(); ImGui::BeginDisabled(this->m_disassembling); { if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang)) this->disassemble(); } ImGui::EndDisabled(); if (this->m_disassembling) { ImGui::SameLine(); ImGui::TextSpinner("hex.builtin.view.disassembler.disassembling"_lang); } ImGui::NewLine(); ImGui::TextUnformatted("hex.builtin.view.disassembler.disassembly.title"_lang); ImGui::Separator(); if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) { ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.address"_lang); ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.offset"_lang); ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.bytes"_lang); ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.title"_lang); if (!this->m_disassembling) { ImGuiListClipper clipper; clipper.Begin(this->m_disassembly.size()); ImGui::TableHeadersRow(); while (clipper.Step()) { for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { const auto &instruction = this->m_disassembly[i]; ImGui::TableNextRow(); ImGui::TableNextColumn(); if (ImGui::Selectable(("##DisassemblyLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) { ImHexApi::HexEditor::setSelection(instruction.offset, instruction.size); } ImGui::SameLine(); ImGui::TextFormatted("0x{0:X}", instruction.address); ImGui::TableNextColumn(); ImGui::TextFormatted("0x{0:X}", instruction.offset); ImGui::TableNextColumn(); ImGui::TextUnformatted(instruction.bytes.c_str()); ImGui::TableNextColumn(); ImGui::TextFormattedColored(ImColor(0xFFD69C56), "{}", instruction.mnemonic); ImGui::SameLine(); ImGui::TextUnformatted(instruction.operators.c_str()); } } clipper.End(); } ImGui::EndTable(); } } } ImGui::End(); } }