diff --git a/CMakeLists.txt b/CMakeLists.txt index c42d8ba87..5c0f2c463 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,11 @@ set(CMAKE_CXX_STANDARD 20) find_package(PkgConfig REQUIRED) pkg_search_module(GLFW REQUIRED glfw3) +pkg_search_module(GLM REQUIRED glm) +pkg_search_module(CAPSTONE REQUIRED capstone) find_package(OpenGL REQUIRED) -include_directories(include ${GLFW_INCLUDE_DIRS} libs/ImGui/include libs/glad/include) +include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} libs/ImGui/include libs/glad/include) SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD") if (WIN32) @@ -37,6 +39,7 @@ add_executable(ImHex source/views/view_tools.cpp source/views/view_strings.cpp source/views/view_data_inspector.cpp + source/views/view_disassembler.cpp libs/glad/source/glad.c @@ -53,9 +56,9 @@ add_executable(ImHex ) if (WIN32) - target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a) + target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a) endif (WIN32) if (UNIX) - target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so) + target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so) endif (UNIX) \ No newline at end of file diff --git a/include/views/view_disassembler.hpp b/include/views/view_disassembler.hpp new file mode 100644 index 000000000..410698d94 --- /dev/null +++ b/include/views/view_disassembler.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "views/view.hpp" + +#include + +#include +#include +#include + +namespace hex { + + namespace prv { class Provider; } + + struct Disassembly { + u64 address; + u64 offset; + std::string bytes; + std::string opcodeString; + }; + + class ViewDisassembler : public View { + public: + explicit ViewDisassembler(prv::Provider* &dataProvider); + ~ViewDisassembler() override; + + void createView() override; + void createMenu() override; + + private: + prv::Provider* &m_dataProvider; + bool m_windowOpen = true; + + bool m_shouldInvalidate = false; + + u64 m_baseAddress = 0; + u64 m_codeOffset = 0; + u64 m_codeSize = 0; + + cs_arch m_architecture = CS_ARCH_ARM; + cs_mode m_modeBasicARM = cs_mode(0), m_modeExtraARM = cs_mode(0), m_modeBasicMIPS = cs_mode(0), m_modeBasicPPC = cs_mode(0), m_modeBasicX86 = cs_mode(0); + bool m_littleEndianMode = true, m_micoMode = false, m_sparcV9Mode = false; + + std::vector m_disassembly; + + + }; + +} \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index cf18ea344..68bb9d4ca 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -10,6 +10,7 @@ #include "views/view_tools.hpp" #include "views/view_strings.hpp" #include "views/view_data_inspector.hpp" +#include "views/view_disassembler.hpp" #include "providers/provider.hpp" @@ -29,8 +30,9 @@ int main() { window.addView(dataProvider); window.addView(dataProvider); window.addView(dataProvider); - window.addView(); + window.addView(dataProvider); window.addView(); + window.addView(); window.loop(); diff --git a/source/views/view_disassembler.cpp b/source/views/view_disassembler.cpp new file mode 100644 index 000000000..fbf021c4c --- /dev/null +++ b/source/views/view_disassembler.cpp @@ -0,0 +1,268 @@ +#include "views/view_disassembler.hpp" + +#include "providers/provider.hpp" +#include "utils.hpp" + +#include + +using namespace std::literals::string_literals; + +namespace hex { + + ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) { + View::subscribeEvent(Events::DataChanged, [this](const void*){ + this->m_shouldInvalidate = true; + }); + } + + ViewDisassembler::~ViewDisassembler() { + View::unsubscribeEvent(Events::DataChanged); + } + + void ViewDisassembler::createView() { + if (!this->m_windowOpen) + return; + + if (this->m_shouldInvalidate) { + this->m_disassembly.clear(); + + csh capstoneHandle; + cs_insn *instructions = nullptr; + + cs_mode mode = cs_mode(this->m_modeBasicARM | this->m_modeExtraARM | this->m_modeBasicMIPS | this->m_modeBasicX86 | this->m_modeBasicPPC); + + if (this->m_littleEndianMode) + mode = cs_mode(mode | CS_MODE_LITTLE_ENDIAN); + else + mode = cs_mode(mode | CS_MODE_BIG_ENDIAN); + + if (this->m_micoMode) + mode = cs_mode(mode | CS_MODE_MICRO); + + if (this->m_sparcV9Mode) + mode = cs_mode(mode | CS_MODE_V9); + + if (cs_open(this->m_architecture, mode, &capstoneHandle) == CS_ERR_OK) { + + std::vector buffer(2048, 0x00); + for (u64 address = 0; address < this->m_codeSize; address += 2048) { + size_t bufferSize = std::min(u64(2048), this->m_codeSize - address); + this->m_dataProvider->read(this->m_codeOffset + address, buffer.data(), bufferSize); + + size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), buffer.size(), this->m_baseAddress + address, 0, &instructions); + + if (instructionCount == 0) + break; + + u64 usedBytes = 0; + for (u32 instr = 0; instr < instructionCount; instr++) { + Disassembly disassembly = { 0 }; + disassembly.address = instructions[instr].address; + disassembly.offset = this->m_codeOffset + address + usedBytes; + disassembly.opcodeString = instructions[instr].mnemonic + " "s + instructions[instr].op_str; + + for (u8 i = 0; i < instructions[instr].size; i++) + disassembly.bytes += hex::format("%02X ", instructions[instr].bytes[i]); + disassembly.bytes.pop_back(); + + this->m_disassembly.push_back(disassembly); + + usedBytes += instructions[instr].size; + } + + if (instructionCount < bufferSize) + address -= (bufferSize - usedBytes); + + cs_free(instructions, instructionCount); + } + + cs_close(&capstoneHandle); + } + + this->m_shouldInvalidate = false; + } + + + if (ImGui::Begin("Disassembler", &this->m_windowOpen)) { + + if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) { + constexpr static const char * const ArchitectureNames[] = { "ARM32", "ARM64", "MIPS", "x86", "PowerPC", "Sparc", "SystemZ", "XCore", "68K", "TMS320C64x", "680X", "Ethereum" }; + + ImGui::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal); + + ImGui::NewLine(); + + ImGui::InputScalar("Code start offset", ImGuiDataType_U64, &this->m_codeOffset, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputScalar("Code size", ImGuiDataType_U64, &this->m_codeSize, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal); + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + + ImGui::Combo("Architecture", reinterpret_cast(&this->m_architecture), ArchitectureNames, 12); + + + if (ImGui::BeginChild("modes", ImVec2(0, 100), true)) { + + if (ImGui::RadioButton("Little Endian", this->m_littleEndianMode)) + this->m_littleEndianMode = true; + ImGui::SameLine(); + if (ImGui::RadioButton("Big Endian", !this->m_littleEndianMode)) + this->m_littleEndianMode = false; + + ImGui::NewLine(); + + switch (this->m_architecture) { + case CS_ARCH_ARM: + this->m_modeBasicMIPS = cs_mode(0); + this->m_modeBasicX86 = cs_mode(0); + this->m_modeBasicPPC = cs_mode(0); + this->m_micoMode = false; + this->m_sparcV9Mode = false; + + if (ImGui::RadioButton("ARM mode", this->m_modeBasicARM == CS_MODE_ARM)) + this->m_modeBasicARM = CS_MODE_ARM; + ImGui::SameLine(); + if (ImGui::RadioButton("Thumb mode", this->m_modeBasicARM == CS_MODE_THUMB)) + this->m_modeBasicARM = CS_MODE_THUMB; + + if (ImGui::RadioButton("Default mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == 0)) + this->m_modeExtraARM = cs_mode(0); + ImGui::SameLine(); + if (ImGui::RadioButton("Cortex-M mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_MCLASS)) + this->m_modeExtraARM = CS_MODE_MCLASS; + ImGui::SameLine(); + if (ImGui::RadioButton("ARMv8 mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_V8)) + this->m_modeExtraARM = CS_MODE_V8; + break; + case CS_ARCH_MIPS: + this->m_modeBasicARM = cs_mode(0); + this->m_modeExtraARM = cs_mode(0); + this->m_modeBasicX86 = cs_mode(0); + this->m_modeBasicPPC = cs_mode(0); + this->m_sparcV9Mode = false; + + if (ImGui::RadioButton("MIPS32 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32)) + this->m_modeBasicMIPS = CS_MODE_MIPS32; + ImGui::SameLine(); + if (ImGui::RadioButton("MIPS64 mode", this->m_modeBasicMIPS == CS_MODE_MIPS64)) + this->m_modeBasicMIPS = CS_MODE_MIPS64; + ImGui::SameLine(); + if (ImGui::RadioButton("MIPS32R6 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32R6)) + this->m_modeBasicMIPS = CS_MODE_MIPS32R6; + + ImGui::Checkbox("Micro Mode", &this->m_micoMode); + break; + case CS_ARCH_X86: + this->m_modeBasicARM = cs_mode(0); + this->m_modeExtraARM = cs_mode(0); + this->m_modeBasicMIPS = cs_mode(0); + this->m_modeBasicPPC = cs_mode(0); + this->m_micoMode = false; + this->m_sparcV9Mode = false; + + if (ImGui::RadioButton("16-bit mode", this->m_modeBasicX86 == CS_MODE_16)) + this->m_modeBasicX86 = CS_MODE_16; + ImGui::SameLine(); + if (ImGui::RadioButton("32-bit mode", this->m_modeBasicX86 == CS_MODE_32)) + this->m_modeBasicX86 = CS_MODE_32; + ImGui::SameLine(); + if (ImGui::RadioButton("64-bit mode", this->m_modeBasicX86 == CS_MODE_64)) + this->m_modeBasicX86 = CS_MODE_64; + break; + case CS_ARCH_PPC: + this->m_modeBasicARM = cs_mode(0); + this->m_modeExtraARM = cs_mode(0); + this->m_modeBasicMIPS = cs_mode(0); + this->m_modeBasicX86 = cs_mode(0); + this->m_micoMode = false; + this->m_sparcV9Mode = false; + + if (ImGui::RadioButton("32-bit mode", this->m_modeBasicPPC == CS_MODE_32)) + this->m_modeBasicPPC = CS_MODE_32; + ImGui::SameLine(); + if (ImGui::RadioButton("64-bit mode", this->m_modeBasicPPC == CS_MODE_64)) + this->m_modeBasicPPC = CS_MODE_64; + break; + case CS_ARCH_SPARC: + this->m_modeBasicARM = cs_mode(0); + this->m_modeExtraARM = cs_mode(0); + this->m_modeBasicMIPS = cs_mode(0); + this->m_modeBasicX86 = cs_mode(0); + this->m_modeBasicPPC = cs_mode(0); + this->m_micoMode = false; + + ImGui::Checkbox("Sparc V9 mode", &this->m_sparcV9Mode); + break; + case CS_ARCH_ARM64: + case CS_ARCH_SYSZ: + case CS_ARCH_XCORE: + case CS_ARCH_M68K: + case CS_ARCH_TMS320C64X: + case CS_ARCH_M680X: + case CS_ARCH_EVM: + this->m_modeBasicARM = cs_mode(0); + this->m_modeExtraARM = cs_mode(0); + this->m_modeBasicMIPS = cs_mode(0); + this->m_modeBasicX86 = cs_mode(0); + this->m_modeBasicPPC = cs_mode(0); + this->m_micoMode = false; + this->m_sparcV9Mode = false; + break; + } + + ImGui::EndChild(); + } + + ImGui::NewLine(); + if (ImGui::Button("Disassemble")) + this->m_shouldInvalidate = true; + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + + if (ImGui::BeginChild("##scrolling")) { + if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) { + ImGui::TableSetupColumn("Address"); + ImGui::TableSetupColumn("Offset"); + ImGui::TableSetupColumn("Bytes"); + ImGui::TableSetupColumn("Disassembly"); + + ImGuiListClipper clipper; + clipper.Begin(this->m_disassembly.size()); + + ImGui::TableHeadersRow(); + while (clipper.Step()) { + for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("0x%llx", this->m_disassembly[i].address); + ImGui::TableNextColumn(); + ImGui::Text("0x%llx", this->m_disassembly[i].offset); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(this->m_disassembly[i].bytes.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(this->m_disassembly[i].opcodeString.c_str()); + } + } + + clipper.End(); + + ImGui::EndTable(); + } + } + ImGui::EndChild(); + + } + } + ImGui::End(); + } + + void ViewDisassembler::createMenu() { + if (ImGui::BeginMenu("View")) { + ImGui::MenuItem("Disassembler View", "", &this->m_windowOpen); + ImGui::EndMenu(); + } + } + +} \ No newline at end of file diff --git a/source/views/view_help.cpp b/source/views/view_help.cpp index 6d2ef4eff..61284b18c 100644 --- a/source/views/view_help.cpp +++ b/source/views/view_help.cpp @@ -32,6 +32,7 @@ namespace hex { ImGui::BulletText("imgui_club by ocornut"); ImGui::BulletText("ImGui-Addons by gallickgunner"); ImGui::BulletText("ImGuiColorTextEdit by BalazsJako"); + ImGui::BulletText("capstone by aquynh"); ImGui::NewLine(); ImGui::BulletText("GNU libmagic"); ImGui::BulletText("OpenSSL libcrypto");