Add partial config UI, add more architectures
This commit is contained in:
parent
5b1f5c0dd8
commit
abd21637ce
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include "task_manager.hpp"
|
||||||
|
|
||||||
using ImGuiDataType = int;
|
using ImGuiDataType = int;
|
||||||
using ImGuiInputTextFlags = int;
|
using ImGuiInputTextFlags = int;
|
||||||
struct ImColor;
|
struct ImColor;
|
||||||
@ -1004,7 +1006,14 @@ namespace hex {
|
|||||||
std::string mnemonic;
|
std::string mnemonic;
|
||||||
std::string operands;
|
std::string operands;
|
||||||
|
|
||||||
std::optional<u64> jumpDestination;
|
enum class Type {
|
||||||
|
Jump,
|
||||||
|
Call,
|
||||||
|
Return,
|
||||||
|
Other
|
||||||
|
} type;
|
||||||
|
|
||||||
|
std::optional<u64> extraData;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Architecture {
|
class Architecture {
|
||||||
@ -1017,7 +1026,7 @@ namespace hex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
virtual void drawConfigInterface() = 0;
|
virtual void drawConfigInterface() = 0;
|
||||||
virtual std::vector<Instruction> disassemble(prv::Provider *provider, const Region& region) = 0;
|
virtual std::vector<Instruction> disassemble(prv::Provider *provider, const Region& region, Task &task) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_unlocalizedName;
|
std::string m_unlocalizedName;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <hex/api/content_registry.hpp>
|
#include <hex/api/content_registry.hpp>
|
||||||
|
#include <hex/api/task_manager.hpp>
|
||||||
#include <hex/ui/view.hpp>
|
#include <hex/ui/view.hpp>
|
||||||
|
#include <ui/widgets.hpp>
|
||||||
|
|
||||||
namespace hex::plugin::builtin {
|
namespace hex::plugin::builtin {
|
||||||
|
|
||||||
@ -17,20 +19,33 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct DisassemblyLine {
|
struct DisassemblyLine {
|
||||||
|
enum class Type {
|
||||||
|
Instruction,
|
||||||
|
CallInstruction,
|
||||||
|
Separator,
|
||||||
|
} type;
|
||||||
|
|
||||||
ImHexApi::HexEditor::ProviderRegion region;
|
ImHexApi::HexEditor::ProviderRegion region;
|
||||||
std::string bytes;
|
std::string bytes;
|
||||||
std::string mnemonic;
|
std::string mnemonic;
|
||||||
std::string operands;
|
std::string operands;
|
||||||
|
|
||||||
std::optional<u64> jumpDestination;
|
std::optional<u64> extraData;
|
||||||
ImVec2 linePos;
|
ImVec2 linePosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
void addLine(prv::Provider *provider, const ContentRegistry::Disassembler::Instruction &instruction);
|
void addLine(prv::Provider *provider, const ContentRegistry::Disassembler::Instruction &instruction);
|
||||||
|
|
||||||
|
bool drawInstructionLine(DisassemblyLine &line);
|
||||||
|
bool drawSeparatorLine(DisassemblyLine &line);
|
||||||
private:
|
private:
|
||||||
PerProvider<std::vector<DisassemblyLine>> m_lines;
|
PerProvider<std::vector<DisassemblyLine>> m_lines;
|
||||||
ContentRegistry::Disassembler::Architecture *m_currArchitecture = nullptr;
|
ContentRegistry::Disassembler::Architecture *m_currArchitecture = nullptr;
|
||||||
|
|
||||||
|
ui::RegionType m_regionType = ui::RegionType::EntireData;
|
||||||
|
Region m_disassembleRegion = { 0, 0 };
|
||||||
|
|
||||||
|
TaskHolder m_disassembleTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <hex/ui/imgui_imhex_extensions.h>
|
#include <hex/ui/imgui_imhex_extensions.h>
|
||||||
|
|
||||||
|
#include <hex/api/imhex_api.hpp>
|
||||||
|
#include <hex/api/localization_manager.hpp>
|
||||||
|
|
||||||
namespace pl::ptrn { class Pattern; }
|
namespace pl::ptrn { class Pattern; }
|
||||||
namespace hex::prv { class Provider; }
|
namespace hex::prv { class Provider; }
|
||||||
|
|
||||||
|
@ -3,9 +3,14 @@
|
|||||||
|
|
||||||
#include <capstone/capstone.h>
|
#include <capstone/capstone.h>
|
||||||
#include <hex/providers/provider.hpp>
|
#include <hex/providers/provider.hpp>
|
||||||
|
#include <hex/ui/widgets.hpp>
|
||||||
#include <wolv/utils/guards.hpp>
|
#include <wolv/utils/guards.hpp>
|
||||||
#include <wolv/literals.hpp>
|
#include <wolv/literals.hpp>
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
#include <ui/widgets.hpp>
|
||||||
|
|
||||||
namespace hex::plugin::builtin {
|
namespace hex::plugin::builtin {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -17,15 +22,28 @@ namespace hex::plugin::builtin {
|
|||||||
public:
|
public:
|
||||||
ArchitectureCapstoneBase(const std::string &unlocalizedName, cs_arch arch) : Architecture(unlocalizedName), m_architecture(arch) { }
|
ArchitectureCapstoneBase(const std::string &unlocalizedName, cs_arch arch) : Architecture(unlocalizedName), m_architecture(arch) { }
|
||||||
|
|
||||||
std::vector<Instruction> disassemble(prv::Provider *provider, const Region ®ion) override {
|
virtual Instruction::Type getInstructionType(const cs_insn &instruction) {
|
||||||
|
for (const auto &group : std::span { instruction.detail->groups, instruction.detail->groups_count }) {
|
||||||
|
if (group == CS_GRP_RET || group == CS_GRP_IRET)
|
||||||
|
return Instruction::Type::Return;
|
||||||
|
if (group == CS_GRP_CALL)
|
||||||
|
return Instruction::Type::Call;
|
||||||
|
if (group == CS_GRP_JUMP)
|
||||||
|
return Instruction::Type::Jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instruction::Type::Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Instruction> disassemble(prv::Provider *provider, const Region ®ion, Task &task) override {
|
||||||
std::vector<Instruction> disassembly;
|
std::vector<Instruction> disassembly;
|
||||||
|
|
||||||
csh csHandle = {};
|
csh csHandle = {};
|
||||||
if (cs_open(this->m_architecture, CS_MODE_64, &csHandle) != CS_ERR_OK)
|
if (cs_open(this->m_architecture, cs_mode(u64(this->m_mode) | (this->m_endian == 0 ? CS_MODE_LITTLE_ENDIAN : CS_MODE_BIG_ENDIAN)), &csHandle) != CS_ERR_OK)
|
||||||
return {};
|
return {};
|
||||||
ON_SCOPE_EXIT { cs_close(&csHandle); };
|
ON_SCOPE_EXIT { cs_close(&csHandle); };
|
||||||
|
|
||||||
cs_option(csHandle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL);
|
cs_option(csHandle, CS_OPT_SYNTAX, this->m_syntax);
|
||||||
cs_option(csHandle, CS_OPT_DETAIL, CS_OPT_ON);
|
cs_option(csHandle, CS_OPT_DETAIL, CS_OPT_ON);
|
||||||
cs_option(csHandle, CS_OPT_SKIPDATA, CS_OPT_ON);
|
cs_option(csHandle, CS_OPT_SKIPDATA, CS_OPT_ON);
|
||||||
|
|
||||||
@ -35,6 +53,7 @@ namespace hex::plugin::builtin {
|
|||||||
std::vector<u8> bytes;
|
std::vector<u8> bytes;
|
||||||
u64 prevAddress = std::numeric_limits<u64>::max();
|
u64 prevAddress = std::numeric_limits<u64>::max();
|
||||||
for (u64 address = region.getStartAddress(); address < region.getEndAddress();) {
|
for (u64 address = region.getStartAddress(); address < region.getEndAddress();) {
|
||||||
|
task.update(address - region.getStartAddress());
|
||||||
if (prevAddress == address)
|
if (prevAddress == address)
|
||||||
break;
|
break;
|
||||||
bytes.resize(std::min(2_MiB, (region.getEndAddress() - address) + 1));
|
bytes.resize(std::min(2_MiB, (region.getEndAddress() - address) + 1));
|
||||||
@ -44,11 +63,13 @@ namespace hex::plugin::builtin {
|
|||||||
size_t size = bytes.size();
|
size_t size = bytes.size();
|
||||||
prevAddress = address;
|
prevAddress = address;
|
||||||
while (cs_disasm_iter(csHandle, &code, &size, &address, instruction)) {
|
while (cs_disasm_iter(csHandle, &code, &size, &address, instruction)) {
|
||||||
|
auto type = getInstructionType(*instruction);
|
||||||
auto line = Instruction {
|
auto line = Instruction {
|
||||||
.region = { instruction->address, instruction->size },
|
.region = { instruction->address, instruction->size },
|
||||||
.mnemonic = instruction->mnemonic,
|
.mnemonic = instruction->mnemonic,
|
||||||
.operands = instruction->op_str,
|
.operands = instruction->op_str,
|
||||||
.jumpDestination = getJumpDestination(*instruction)
|
.type = type,
|
||||||
|
.extraData = getExtraData(type, *instruction)
|
||||||
};
|
};
|
||||||
|
|
||||||
disassembly.emplace_back(std::move(line));
|
disassembly.emplace_back(std::move(line));
|
||||||
@ -59,48 +80,268 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void drawConfigInterface() override {
|
void drawConfigInterface() override {
|
||||||
ImGui::TextUnformatted("Config Interface");
|
ImGuiExt::BeginSubWindow("Endianess");
|
||||||
|
{
|
||||||
|
drawRadioButtons(this->m_endian, {
|
||||||
|
{ "Little", CS_MODE_LITTLE_ENDIAN },
|
||||||
|
{ "Big", CS_MODE_BIG_ENDIAN }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ImGuiExt::EndSubWindow();
|
||||||
|
|
||||||
|
ImGuiExt::BeginSubWindow("Syntax");
|
||||||
|
{
|
||||||
|
drawRadioButtons(this->m_syntax, {
|
||||||
|
{ "Intel", CS_OPT_SYNTAX_INTEL },
|
||||||
|
{ "AT&T", CS_OPT_SYNTAX_ATT },
|
||||||
|
{ "MASM", CS_OPT_SYNTAX_MASM },
|
||||||
|
{ "Motorola", CS_OPT_SYNTAX_MOTOROLA }
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ImGuiExt::EndSubWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::optional<u64> getJumpDestination(const cs_insn &instruction) = 0;
|
virtual std::optional<u64> getExtraData(Instruction::Type type, const cs_insn &instruction) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<typename T>
|
||||||
|
static void drawRadioButtons(T &currMode, const std::vector<std::pair<std::string, T>> &modes) {
|
||||||
|
for (const auto &[index, mode] : modes | std::views::enumerate) {
|
||||||
|
const auto &[unlocalizedName, csMode] = mode;
|
||||||
|
|
||||||
|
if (ImGui::RadioButton(Lang(unlocalizedName), csMode == currMode)) {
|
||||||
|
currMode = csMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
}
|
||||||
|
ImGui::NewLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static void drawCheckbox(T &currMode, const std::string &unlocalizedName, T mode) {
|
||||||
|
bool enabled = currMode & mode;
|
||||||
|
if (ImGui::Checkbox(Lang(unlocalizedName), &enabled)) {
|
||||||
|
if (enabled)
|
||||||
|
currMode = T(u64(currMode) | u64(mode));
|
||||||
|
else
|
||||||
|
currMode = T(u64(currMode) & ~u64(mode));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cs_arch m_architecture;
|
cs_arch m_architecture;
|
||||||
|
cs_mode m_endian = cs_mode(0);
|
||||||
|
cs_opt_value m_syntax = CS_OPT_SYNTAX_INTEL;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
cs_mode m_mode = cs_mode(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ArchitectureX86 : public ArchitectureCapstoneBase {
|
class ArchitectureX86 : public ArchitectureCapstoneBase {
|
||||||
public:
|
public:
|
||||||
ArchitectureX86() : ArchitectureCapstoneBase("x86", CS_ARCH_X86) { }
|
ArchitectureX86() : ArchitectureCapstoneBase("x86", CS_ARCH_X86) {
|
||||||
|
this->m_mode = CS_MODE_64;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<u64> getJumpDestination(const cs_insn &instruction) override {
|
std::optional<u64> getExtraData(Instruction::Type type, const cs_insn &instruction) override {
|
||||||
// Get jump destination of jumps on x86
|
switch (type) {
|
||||||
if (instruction.id == X86_INS_JMP) {
|
using enum Instruction::Type;
|
||||||
if (instruction.detail->x86.op_count != 1)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const auto &op = instruction.detail->x86.operands[0];
|
case Jump: {
|
||||||
|
// Get jump destination of jumps on x86
|
||||||
|
if (instruction.id == X86_INS_JMP) {
|
||||||
|
if (instruction.detail->x86.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
if (op.type == X86_OP_IMM)
|
const auto &op = instruction.detail->x86.operands[0];
|
||||||
return op.imm;
|
|
||||||
|
|
||||||
if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP)
|
if (op.type == X86_OP_IMM)
|
||||||
return instruction.address + instruction.size + op.mem.disp;
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get jump destination of conditional jumps on x86
|
||||||
|
if (instruction.id >= X86_INS_JAE && instruction.id <= X86_INS_JLE) {
|
||||||
|
if (instruction.detail->x86.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto &op = instruction.detail->x86.operands[0];
|
||||||
|
|
||||||
|
if (op.type == X86_OP_IMM)
|
||||||
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Call: {
|
||||||
|
if (instruction.id == X86_INS_CALL) {
|
||||||
|
if (instruction.detail->x86.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto &op = instruction.detail->x86.operands[0];
|
||||||
|
|
||||||
|
if (op.type == X86_OP_IMM)
|
||||||
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Return:
|
||||||
|
break;
|
||||||
|
case Other:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get jump destination of conditional jumps on x86
|
|
||||||
if (instruction.id >= X86_INS_JAE && instruction.id <= X86_INS_JLE) {
|
|
||||||
if (instruction.detail->x86.op_count != 1)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
const auto &op = instruction.detail->x86.operands[0];
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
if (op.type == X86_OP_IMM)
|
void drawConfigInterface() override {
|
||||||
return op.imm;
|
ArchitectureCapstoneBase::drawConfigInterface();
|
||||||
|
|
||||||
if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP)
|
ImGuiExt::BeginSubWindow("Address Width");
|
||||||
return instruction.address + instruction.size + op.mem.disp;
|
{
|
||||||
|
drawRadioButtons(this->m_mode, {
|
||||||
|
{ "16 Bit", CS_MODE_16 },
|
||||||
|
{ "32 Bit", CS_MODE_32 },
|
||||||
|
{ "64 Bit", CS_MODE_64 },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
ImGuiExt::EndSubWindow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArchitectureARM32 : public ArchitectureCapstoneBase {
|
||||||
|
public:
|
||||||
|
ArchitectureARM32() : ArchitectureCapstoneBase("ARM", CS_ARCH_ARM) {
|
||||||
|
this->m_mode = CS_MODE_ARM;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u64> getExtraData(Instruction::Type type, const cs_insn &instruction) override {
|
||||||
|
switch (type) {
|
||||||
|
using enum Instruction::Type;
|
||||||
|
|
||||||
|
case Jump: {
|
||||||
|
// Get jump destination of jumps on ARM
|
||||||
|
if (instruction.id == ARM_INS_B) {
|
||||||
|
if (instruction.detail->arm.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto &op = instruction.detail->arm.operands[0];
|
||||||
|
|
||||||
|
if (op.type == ARM_OP_IMM)
|
||||||
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == ARM_OP_MEM && op.mem.base == ARM_REG_PC)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Call: {
|
||||||
|
if (instruction.id == ARM_INS_BL) {
|
||||||
|
if (instruction.detail->arm.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto &op = instruction.detail->arm.operands[0];
|
||||||
|
|
||||||
|
if (op.type == ARM_OP_IMM)
|
||||||
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == ARM_OP_MEM && op.mem.base == ARM_REG_PC)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Return:
|
||||||
|
break;
|
||||||
|
case Other:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawConfigInterface() override {
|
||||||
|
ArchitectureCapstoneBase::drawConfigInterface();
|
||||||
|
|
||||||
|
ImGuiExt::BeginSubWindow("Instruction Set");
|
||||||
|
{
|
||||||
|
drawRadioButtons(this->m_mode, {
|
||||||
|
{ "ARM", CS_MODE_ARM },
|
||||||
|
{ "Thumb & Thumb-2", CS_MODE_THUMB },
|
||||||
|
{ "ARMv8 / AArch32", CS_MODE_THUMB }
|
||||||
|
});
|
||||||
|
|
||||||
|
drawCheckbox(this->m_mode, "Cortex-M", CS_MODE_MCLASS);
|
||||||
|
}
|
||||||
|
ImGuiExt::EndSubWindow();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArchitectureARM64 : public ArchitectureCapstoneBase {
|
||||||
|
public:
|
||||||
|
ArchitectureARM64() : ArchitectureCapstoneBase("ARM64 / AArch64", CS_ARCH_ARM64) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u64> getExtraData(Instruction::Type type, const cs_insn &instruction) override {
|
||||||
|
switch (type) {
|
||||||
|
using enum Instruction::Type;
|
||||||
|
|
||||||
|
case Jump: {
|
||||||
|
// Get jump destination of jumps on ARM64
|
||||||
|
if (instruction.id == ARM64_INS_B) {
|
||||||
|
if (instruction.detail->arm64.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto &op = instruction.detail->arm64.operands[0];
|
||||||
|
|
||||||
|
if (op.type == ARM64_OP_IMM)
|
||||||
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == ARM64_OP_MEM)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Call: {
|
||||||
|
if (instruction.id == ARM64_INS_BL) {
|
||||||
|
if (instruction.detail->arm64.op_count != 1)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto &op = instruction.detail->arm64.operands[0];
|
||||||
|
|
||||||
|
if (op.type == ARM64_OP_IMM)
|
||||||
|
return op.imm;
|
||||||
|
|
||||||
|
if (op.type == ARM64_OP_MEM)
|
||||||
|
return instruction.address + instruction.size + op.mem.disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Return:
|
||||||
|
break;
|
||||||
|
case Other:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@ -110,6 +351,8 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
void registerDisassemblers() {
|
void registerDisassemblers() {
|
||||||
ContentRegistry::Disassembler::add<ArchitectureX86>();
|
ContentRegistry::Disassembler::add<ArchitectureX86>();
|
||||||
|
ContentRegistry::Disassembler::add<ArchitectureARM32>();
|
||||||
|
ContentRegistry::Disassembler::add<ArchitectureARM64>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <hex/helpers/fmt.hpp>
|
#include <hex/helpers/fmt.hpp>
|
||||||
|
|
||||||
#include <hex/providers/buffered_reader.hpp>
|
#include <hex/providers/buffered_reader.hpp>
|
||||||
|
#include <ui/widgets.hpp>
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
@ -13,19 +14,100 @@ namespace hex::plugin::builtin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewDisassembler::addLine(prv::Provider *provider, const ContentRegistry::Disassembler::Instruction &instruction) {
|
void ViewDisassembler::addLine(prv::Provider *provider, const ContentRegistry::Disassembler::Instruction &instruction) {
|
||||||
prv::ProviderReader reader(provider);
|
std::vector<u8> bytes(instruction.region.getSize());
|
||||||
reader.seek(instruction.region.getStartAddress());
|
provider->read(instruction.region.getStartAddress(), bytes.data(), bytes.size());
|
||||||
reader.setEndAddress(instruction.region.getEndAddress());
|
|
||||||
|
|
||||||
std::string bytes;
|
std::string byteString;
|
||||||
for (const auto& byte : reader) {
|
for (const auto& byte : bytes) {
|
||||||
bytes += fmt::format("{:02X} ", byte);
|
byteString += fmt::format("{:02X} ", byte);
|
||||||
}
|
}
|
||||||
bytes.pop_back();
|
byteString.pop_back();
|
||||||
|
|
||||||
this->m_lines.get(provider).emplace_back(ImHexApi::HexEditor::ProviderRegion { instruction.region, provider }, std::move(bytes), instruction.mnemonic, instruction.operands, instruction.jumpDestination, ImVec2());
|
switch (instruction.type) {
|
||||||
|
using enum ContentRegistry::Disassembler::Instruction::Type;
|
||||||
|
case Return:
|
||||||
|
this->m_lines.get(provider).emplace_back(
|
||||||
|
DisassemblyLine::Type::Instruction,
|
||||||
|
ImHexApi::HexEditor::ProviderRegion {
|
||||||
|
instruction.region,
|
||||||
|
provider
|
||||||
|
},
|
||||||
|
std::move(byteString),
|
||||||
|
instruction.mnemonic,
|
||||||
|
instruction.operands,
|
||||||
|
instruction.extraData,
|
||||||
|
ImVec2()
|
||||||
|
);
|
||||||
|
this->m_lines.get(provider).emplace_back(DisassemblyLine::Type::Separator);
|
||||||
|
break;
|
||||||
|
case Call:
|
||||||
|
this->m_lines.get(provider).emplace_back(
|
||||||
|
DisassemblyLine::Type::CallInstruction,
|
||||||
|
ImHexApi::HexEditor::ProviderRegion {
|
||||||
|
instruction.region,
|
||||||
|
provider
|
||||||
|
},
|
||||||
|
std::move(byteString),
|
||||||
|
instruction.mnemonic,
|
||||||
|
instruction.operands,
|
||||||
|
instruction.extraData,
|
||||||
|
ImVec2()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case Jump:
|
||||||
|
case Other:
|
||||||
|
this->m_lines.get(provider).emplace_back(
|
||||||
|
DisassemblyLine::Type::Instruction,
|
||||||
|
ImHexApi::HexEditor::ProviderRegion {
|
||||||
|
instruction.region,
|
||||||
|
provider
|
||||||
|
},
|
||||||
|
std::move(byteString),
|
||||||
|
instruction.mnemonic,
|
||||||
|
instruction.operands,
|
||||||
|
instruction.extraData,
|
||||||
|
ImVec2()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ViewDisassembler::drawInstructionLine(DisassemblyLine& line) {
|
||||||
|
auto height = ImGui::GetTextLineHeight(); //ImGui::CalcTextSize(line.bytes.c_str(), nullptr, false, 80_scaled).y;
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
if (ImGui::Selectable(hex::format("0x{:08X}", line.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, height))) {
|
||||||
|
ImHexApi::HexEditor::setSelection(line.region);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hovered = ImGui::IsItemHovered();
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight), "{}", line.bytes);
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGuiExt::TextFormattedColored(ImGui::GetColorU32(ImGuiCol_HeaderActive), "{} ", line.mnemonic);
|
||||||
|
ImGui::SameLine(0, 0);
|
||||||
|
ImGuiExt::TextFormatted("{}", line.operands);
|
||||||
|
|
||||||
|
return hovered;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ViewDisassembler::drawSeparatorLine(DisassemblyLine&) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetColorU32(ImGuiCol_Text));
|
||||||
|
ImGui::Selectable("##separator", true, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, 2_scaled));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void drawJumpLine(ImVec2 start, ImVec2 end, float columnWidth, u32 slot, bool endVisible, bool hovered) {
|
static void drawJumpLine(ImVec2 start, ImVec2 end, float columnWidth, u32 slot, bool endVisible, bool hovered) {
|
||||||
const u32 slotCount = std::floor(std::max<float>(1.0F, columnWidth / 10_scaled));
|
const u32 slotCount = std::floor(std::max<float>(1.0F, columnWidth / 10_scaled));
|
||||||
|
|
||||||
@ -84,136 +166,145 @@ namespace hex::plugin::builtin {
|
|||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if (this->m_lines->empty()) {
|
if (this->m_disassembleTask.isRunning() || this->m_lines->empty()) {
|
||||||
|
ImGui::BeginDisabled(this->m_disassembleTask.isRunning());
|
||||||
|
|
||||||
auto provider = ImHexApi::Provider::get();
|
auto provider = ImHexApi::Provider::get();
|
||||||
if (ImGuiExt::DimmedButton("Disassemble", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
if (ImGuiExt::DimmedButton("Disassemble", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||||
auto disassembly = this->m_currArchitecture->disassemble(provider, ImHexApi::HexEditor::getSelection().value());
|
this->m_disassembleTask = TaskManager::createTask("Disassembling...", this->m_disassembleRegion.getSize(), [this, provider](auto &task) {
|
||||||
|
const auto disassembly = this->m_currArchitecture->disassemble(provider, this->m_disassembleRegion, task);
|
||||||
|
|
||||||
for (const auto &instruction : disassembly)
|
task.setMaxValue(disassembly.size());
|
||||||
this->addLine(provider, instruction);
|
for (const auto &[index, instruction] : disassembly | std::views::enumerate) {
|
||||||
|
task.update(index);
|
||||||
|
this->addLine(provider, instruction);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiExt::BeginSubWindow("Config");
|
ImGuiExt::BeginSubWindow("Config");
|
||||||
{
|
{
|
||||||
|
ui::regionSelectionPicker(&this->m_disassembleRegion, provider, &this->m_regionType, true, true);
|
||||||
|
|
||||||
|
ImGuiExt::Header("Architecture Settings");
|
||||||
this->m_currArchitecture->drawConfigInterface();
|
this->m_currArchitecture->drawConfigInterface();
|
||||||
}
|
}
|
||||||
ImGuiExt::EndSubWindow();
|
ImGuiExt::EndSubWindow();
|
||||||
|
|
||||||
|
ImGui::EndDisabled();
|
||||||
} else {
|
} else {
|
||||||
if (ImGuiExt::DimmedButton("Reset", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
if (ImGuiExt::DimmedButton("Reset", ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||||
this->m_lines->clear();
|
this->m_lines->clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable, ImGui::GetContentRegionAvail())) {
|
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable, ImGui::GetContentRegionAvail())) {
|
||||||
ImGui::TableSetupScrollFreeze(0, 1);
|
ImGui::TableSetupScrollFreeze(0, 1);
|
||||||
ImGui::TableSetupColumn("##jumps");
|
ImGui::TableSetupColumn("##jumps");
|
||||||
ImGui::TableSetupColumn("##address", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 80_scaled);
|
ImGui::TableSetupColumn("##address", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 80_scaled);
|
||||||
ImGui::TableSetupColumn("##bytes", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 80_scaled);
|
ImGui::TableSetupColumn("##bytes", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, 120_scaled);
|
||||||
ImGui::TableSetupColumn("##instruction", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_NoResize);
|
ImGui::TableSetupColumn("##instruction", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_NoResize);
|
||||||
|
|
||||||
ImGui::TableHeadersRow();
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
ImGuiListClipper clipper;
|
ImGuiListClipper clipper;
|
||||||
clipper.Begin(this->m_lines->size(), ImGui::GetTextLineHeightWithSpacing());
|
clipper.Begin(this->m_lines->size(), ImGui::GetTextLineHeightWithSpacing());
|
||||||
|
|
||||||
int processingStart = 0, processingEnd = 0;
|
int processingStart = 0, processingEnd = 0;
|
||||||
|
|
||||||
float jumpColumnWidth = 0.0F;
|
float jumpColumnWidth = 0.0F;
|
||||||
std::optional<u64> hoveredAddress;
|
std::optional<u64> hoveredAddress;
|
||||||
while (clipper.Step()) {
|
while (clipper.Step()) {
|
||||||
processingStart = clipper.DisplayStart;
|
processingStart = clipper.DisplayStart;
|
||||||
processingEnd = clipper.DisplayEnd;
|
processingEnd = clipper.DisplayEnd;
|
||||||
for (auto i = processingStart; i < processingEnd; i += 1) {
|
for (auto i = processingStart; i < processingEnd; i += 1) {
|
||||||
auto &line = this->m_lines->at(i);
|
auto &line = this->m_lines->at(i);
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
auto height = ImGui::CalcTextSize(line.bytes.c_str(), nullptr, false, 80_scaled).y;
|
{
|
||||||
|
auto height = ImGui::CalcTextSize(line.bytes.c_str(), nullptr, false, 80_scaled).y;
|
||||||
|
// Reserve some space to draw the jump lines later
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
// Remember the position of the line so we can draw the jump lines later
|
||||||
{
|
jumpColumnWidth = ImGui::GetContentRegionAvail().x;
|
||||||
// Reserve some space to draw the jump lines later
|
line.linePosition = ImGui::GetCursorScreenPos() + ImVec2(jumpColumnWidth, height / 2);
|
||||||
|
|
||||||
// Remember the position of the line so we can draw the jump lines later
|
|
||||||
jumpColumnWidth = ImGui::GetContentRegionAvail().x;
|
|
||||||
line.linePos = ImGui::GetCursorScreenPos() + ImVec2(jumpColumnWidth, height / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
if (ImGui::Selectable(hex::format("0x{:08X}", line.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns, ImVec2(0, height))) {
|
|
||||||
ImHexApi::HexEditor::setSelection(line.region);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::IsItemHovered())
|
|
||||||
hoveredAddress = line.region.getStartAddress();
|
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_Highlight));
|
|
||||||
ImGuiExt::TextFormattedWrapped("{}", line.bytes);
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGuiExt::TextFormattedColored(ImGui::GetColorU32(ImGuiCol_HeaderActive), "{} ", line.mnemonic);
|
|
||||||
ImGui::SameLine(0, 0);
|
|
||||||
ImGuiExt::TextFormatted("{}", line.operands);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw jump arrows
|
|
||||||
if (!this->m_lines->empty()) {
|
|
||||||
auto &firstVisibleLine = this->m_lines->at(processingStart);
|
|
||||||
auto &lastVisibleLine = this->m_lines->at(processingEnd - 1);
|
|
||||||
|
|
||||||
const u32 slotCount = std::floor(std::max<float>(1.0F, jumpColumnWidth / 10_scaled));
|
|
||||||
std::map<u64, u64> occupiedSlots;
|
|
||||||
|
|
||||||
auto findFreeSlot = [&](u64 jumpDestination) {
|
|
||||||
for (u32 i = 0; i < slotCount; i += 1) {
|
|
||||||
if (!occupiedSlots.contains(i) || occupiedSlots[i] == jumpDestination) {
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return slotCount;
|
switch (line.type) {
|
||||||
};
|
using enum DisassemblyLine::Type;
|
||||||
|
case CallInstruction:
|
||||||
for (auto sourceLineIndex = processingStart; sourceLineIndex < processingEnd; sourceLineIndex += 1) {
|
case Instruction:
|
||||||
const auto &sourceLine = this->m_lines->at(sourceLineIndex);
|
if (this->drawInstructionLine(line))
|
||||||
|
hoveredAddress = line.region.getStartAddress();
|
||||||
if (auto jumpDestination = sourceLine.jumpDestination; jumpDestination.has_value()) {
|
|
||||||
for (auto destinationLineIndex = processingStart; destinationLineIndex < processingEnd; destinationLineIndex += 1) {
|
|
||||||
const auto &destinationLine = this->m_lines->at(destinationLineIndex);
|
|
||||||
|
|
||||||
auto freeSlot = findFreeSlot(*jumpDestination);
|
|
||||||
|
|
||||||
bool jumpFound = false;
|
|
||||||
if (*jumpDestination == destinationLine.region.getStartAddress()) {
|
|
||||||
drawJumpLine(sourceLine.linePos, destinationLine.linePos, jumpColumnWidth, freeSlot, true, hoveredAddress == sourceLine.region.getStartAddress() || hoveredAddress == destinationLine.region.getStartAddress());
|
|
||||||
jumpFound = true;
|
|
||||||
} else if (*jumpDestination > lastVisibleLine.region.getStartAddress()) {
|
|
||||||
drawJumpLine(sourceLine.linePos, lastVisibleLine.linePos, jumpColumnWidth, freeSlot, false, hoveredAddress == sourceLine.region.getStartAddress() || hoveredAddress == destinationLine.region.getStartAddress());
|
|
||||||
jumpFound = true;
|
|
||||||
} else if (*jumpDestination < firstVisibleLine.region.getStartAddress()) {
|
|
||||||
drawJumpLine(sourceLine.linePos, firstVisibleLine.linePos, jumpColumnWidth, freeSlot, false, hoveredAddress == sourceLine.region.getStartAddress() || hoveredAddress == destinationLine.region.getStartAddress());
|
|
||||||
jumpFound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jumpFound) {
|
|
||||||
if (!occupiedSlots.contains(freeSlot))
|
|
||||||
occupiedSlots[freeSlot] = *jumpDestination;
|
|
||||||
break;
|
break;
|
||||||
|
case Separator:
|
||||||
|
this->drawSeparatorLine(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
|
// Draw jump arrows
|
||||||
|
if (!this->m_lines->empty()) {
|
||||||
|
auto &firstVisibleLine = this->m_lines->at(processingStart);
|
||||||
|
auto &lastVisibleLine = this->m_lines->at(processingEnd - 1);
|
||||||
|
|
||||||
|
const u32 slotCount = std::floor(std::max<float>(1.0F, jumpColumnWidth / 10_scaled));
|
||||||
|
std::map<u64, u64> occupiedSlots;
|
||||||
|
|
||||||
|
auto findFreeSlot = [&](u64 jumpDestination) {
|
||||||
|
for (u32 i = 0; i < slotCount; i += 1) {
|
||||||
|
if (!occupiedSlots.contains(i) || occupiedSlots[i] == jumpDestination) {
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::erase_if(occupiedSlots, [&](const auto &entry) {
|
return slotCount;
|
||||||
auto &[slot, destination] = entry;
|
};
|
||||||
return sourceLine.region.getStartAddress() == destination;
|
|
||||||
});
|
for (auto sourceLineIndex = processingStart; sourceLineIndex < processingEnd; sourceLineIndex += 1) {
|
||||||
|
const auto &sourceLine = this->m_lines->at(sourceLineIndex);
|
||||||
|
|
||||||
|
if (auto jumpDestination = sourceLine.extraData; jumpDestination.has_value()) {
|
||||||
|
for (auto destinationLineIndex = processingStart; destinationLineIndex < processingEnd; destinationLineIndex += 1) {
|
||||||
|
const auto &destinationLine = this->m_lines->at(destinationLineIndex);
|
||||||
|
|
||||||
|
const auto freeSlot = findFreeSlot(*jumpDestination);
|
||||||
|
const bool hovered = hoveredAddress == sourceLine.region.getStartAddress() ||
|
||||||
|
hoveredAddress == destinationLine.region.getStartAddress();
|
||||||
|
|
||||||
|
bool jumpFound = false;
|
||||||
|
if (*jumpDestination == destinationLine.region.getStartAddress()) {
|
||||||
|
drawJumpLine(sourceLine.linePosition, destinationLine.linePosition, jumpColumnWidth, freeSlot, true, hovered);
|
||||||
|
jumpFound = true;
|
||||||
|
} else if (*jumpDestination > lastVisibleLine.region.getStartAddress()) {
|
||||||
|
drawJumpLine(sourceLine.linePosition, lastVisibleLine.linePosition, jumpColumnWidth, freeSlot, false, hovered);
|
||||||
|
jumpFound = true;
|
||||||
|
} else if (*jumpDestination < firstVisibleLine.region.getStartAddress()) {
|
||||||
|
drawJumpLine(sourceLine.linePosition, firstVisibleLine.linePosition, jumpColumnWidth, freeSlot, false, hovered);
|
||||||
|
jumpFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jumpFound) {
|
||||||
|
if (!occupiedSlots.contains(freeSlot))
|
||||||
|
occupiedSlots[freeSlot] = *jumpDestination;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::erase_if(occupiedSlots, [&](const auto &entry) {
|
||||||
|
auto &[slot, destination] = entry;
|
||||||
|
return sourceLine.extraData.value() == destination;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndTable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user