From 4402120ffc7f23899697fcf5fe2128f63faa0f1d Mon Sep 17 00:00:00 2001
From: WerWolv <werwolv98@gmail.com>
Date: Sun, 22 Nov 2020 23:07:50 +0100
Subject: [PATCH] Added the capstone disassembler and a disassembler window

---
 CMakeLists.txt                      |   9 +-
 include/views/view_disassembler.hpp |  49 +++++
 source/main.cpp                     |   4 +-
 source/views/view_disassembler.cpp  | 268 ++++++++++++++++++++++++++++
 source/views/view_help.cpp          |   1 +
 5 files changed, 327 insertions(+), 4 deletions(-)
 create mode 100644 include/views/view_disassembler.hpp
 create mode 100644 source/views/view_disassembler.cpp

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 <capstone/capstone.h>
+
+#include <cstdio>
+#include <string>
+#include <vector>
+
+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<Disassembly> 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<hex::ViewHashes>(dataProvider);
     window.addView<hex::ViewInformation>(dataProvider);
     window.addView<hex::ViewStrings>(dataProvider);
-    window.addView<hex::ViewHelp>();
+    window.addView<hex::ViewDisassembler>(dataProvider);
     window.addView<hex::ViewTools>();
+    window.addView<hex::ViewHelp>();
 
     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 <cstring>
+
+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<u8> 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<int*>(&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");