From c6134bc03894ccf50188e856704e9ec3cc043687 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Tue, 1 Dec 2020 02:21:40 +0100 Subject: [PATCH] Added basic python-based load scripts --- CMakeLists.txt | 8 +- include/helpers/loader_script_handler.hpp | 30 +++++++ include/views/view_hexeditor.hpp | 3 + source/helpers/loader_script_handler.cpp | 99 +++++++++++++++++++++++ source/views/view_bookmarks.cpp | 12 +-- source/views/view_hexeditor.cpp | 59 +++++++++++++- 6 files changed, 201 insertions(+), 10 deletions(-) create mode 100644 include/helpers/loader_script_handler.hpp create mode 100644 source/helpers/loader_script_handler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f4f20aea6..5195b026f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,10 +11,11 @@ pkg_search_module(CAPSTONE REQUIRED capstone) find_package(OpenGL REQUIRED) find_package(LLVM REQUIRED CONFIG) find_package(nlohmann_json REQUIRED) +find_package(Python COMPONENTS Interpreter Development) llvm_map_components_to_libnames(demangler) -include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} libs/ImGui/include libs/glad/include) +include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} libs/ImGui/include libs/glad/include ${Python_INCLUDE_DIRS}) SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD") @@ -34,6 +35,7 @@ add_executable(ImHex source/helpers/patches.cpp source/helpers/math_evaluator.cpp source/helpers/project_file_handler.cpp + source/helpers/loader_script_handler.cpp source/lang/preprocessor.cpp source/lang/lexer.cpp @@ -71,9 +73,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 libcapstone.a libLLVMDemangle.a nlohmann_json::nlohmann_json) + 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 libLLVMDemangle.a ${Python_LIBRARIES} nlohmann_json::nlohmann_json) endif (WIN32) if (UNIX) - target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so libLLVMDemangle.so nlohmann_json::nlohmann_json) + target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so libLLVMDemangle.so ${Python_LIBRARIES} nlohmann_json::nlohmann_json) endif (UNIX) \ No newline at end of file diff --git a/include/helpers/loader_script_handler.hpp b/include/helpers/loader_script_handler.hpp new file mode 100644 index 000000000..b39687087 --- /dev/null +++ b/include/helpers/loader_script_handler.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +struct _object; +typedef struct _object PyObject; + +namespace hex { + + namespace prv { class Provider; } + + class LoaderScript { + public: + LoaderScript() = delete; + + static bool processFile(std::string_view scriptPath); + + static void setFilePath(std::string_view filePath) { LoaderScript::s_filePath = filePath; } + static void setDataProvider(prv::Provider* provider) { LoaderScript::s_dataProvider = provider; } + private: + static inline std::string s_filePath; + static inline prv::Provider* s_dataProvider; + + static PyObject* Py_getFilePath(PyObject *self, PyObject *args); + static PyObject* Py_addPatch(PyObject *self, PyObject *args); + static PyObject* Py_addBookmark(PyObject *self, PyObject *args); + }; + +} \ No newline at end of file diff --git a/include/views/view_hexeditor.hpp b/include/views/view_hexeditor.hpp index a0226bceb..c386b6cfa 100644 --- a/include/views/view_hexeditor.hpp +++ b/include/views/view_hexeditor.hpp @@ -47,6 +47,9 @@ namespace hex { std::vector m_dataToSave; + std::string m_loaderScriptScriptPath; + std::string m_loaderScriptFilePath; + void drawSearchPopup(); void drawGotoPopup(); diff --git a/source/helpers/loader_script_handler.cpp b/source/helpers/loader_script_handler.cpp new file mode 100644 index 000000000..26ec379b5 --- /dev/null +++ b/source/helpers/loader_script_handler.cpp @@ -0,0 +1,99 @@ +#include "helpers/loader_script_handler.hpp" + +#include "views/view.hpp" +#include "helpers/utils.hpp" +#include "providers/provider.hpp" + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +namespace hex { + + PyObject* LoaderScript::Py_getFilePath(PyObject *self, PyObject *args) { + return PyUnicode_FromString(LoaderScript::s_filePath.c_str()); + } + + PyObject* LoaderScript::Py_addPatch(PyObject *self, PyObject *args) { + u64 address; + u8 *patches; + Py_ssize_t count; + + if (!PyArg_ParseTuple(args, "K|y#", &address, &patches, &count)) { + PyErr_BadArgument(); + return nullptr; + } + + if (patches == nullptr || count == 0) { + PyErr_SetString(PyExc_TypeError, "Invalid patch provided"); + return nullptr; + } + + if (address >= LoaderScript::s_dataProvider->getActualSize()) { + PyErr_SetString(PyExc_IndexError, "address out of range"); + return nullptr; + } + + LoaderScript::s_dataProvider->write(address, patches, count); + + Py_RETURN_NONE; + } + + PyObject* LoaderScript::Py_addBookmark(PyObject *self, PyObject *args) { + Bookmark bookmark; + + char *name = nullptr; + char *comment = nullptr; + + if (!PyArg_ParseTuple(args, "K|n|s|s", &bookmark.region.address, &bookmark.region.size, &name, &comment)) { + PyErr_BadArgument(); + return nullptr; + } + + if (name == nullptr || comment == nullptr) { + PyErr_SetString(PyExc_IndexError, "address out of range"); + return nullptr; + } + + std::copy(name, name + std::strlen(name), std::back_inserter(bookmark.name)); + std::copy(comment, comment + std::strlen(comment), std::back_inserter(bookmark.comment)); + + View::postEvent(Events::AddBookmark, &bookmark); + + Py_RETURN_NONE; + } + + bool LoaderScript::processFile(std::string_view scriptPath) { + Py_SetProgramName(Py_DecodeLocale(__argv[0], nullptr)); + + Py_SetPythonHome(Py_DecodeLocale(std::filesystem::path(__argv[0]).parent_path().string().c_str(), nullptr)); + + PyImport_AppendInittab("imhex", []() -> PyObject* { + static PyMethodDef ImHexMethods[] = { + { "get_file_path", &LoaderScript::Py_getFilePath, METH_NOARGS, "Returns the path of the file being loaded." }, + { "patch", &LoaderScript::Py_addPatch, METH_VARARGS, "Patches a region of memory" }, + { "add_bookmark", &LoaderScript::Py_addBookmark, METH_VARARGS, "Adds a bookmark" }, + { nullptr, nullptr, 0, nullptr } + }; + + static PyModuleDef ImHexModule = { + PyModuleDef_HEAD_INIT, "imhex", nullptr, -1, ImHexMethods, nullptr, nullptr, nullptr, nullptr + }; + + return PyModule_Create(&ImHexModule); + }); + + Py_Initialize(); + + FILE *scriptFile = fopen(scriptPath.data(), "r"); + PyRun_SimpleFile(scriptFile, scriptPath.data()); + + fclose(scriptFile); + + Py_Finalize(); + + return true; + } + +} \ No newline at end of file diff --git a/source/views/view_bookmarks.cpp b/source/views/view_bookmarks.cpp index 030c944a3..92f5d9e37 100644 --- a/source/views/view_bookmarks.cpp +++ b/source/views/view_bookmarks.cpp @@ -9,15 +9,17 @@ namespace hex { ViewBookmarks::ViewBookmarks(prv::Provider* &dataProvider) : View("Bookmarks"), m_dataProvider(dataProvider) { View::subscribeEvent(Events::AddBookmark, [this](const void *userData) { - Bookmark bookmark; - bookmark.region = *reinterpret_cast(userData); + Bookmark bookmark = *reinterpret_cast(userData); bookmark.name.resize(64); bookmark.comment.resize(0xF'FFFF); - std::memset(bookmark.name.data(), 0x00, 64); - std::memset(bookmark.comment.data(), 0x00, 0xF'FFFF); + if (bookmark.name.empty()) { + std::memset(bookmark.name.data(), 0x00, 64); + std::strcpy(bookmark.name.data(), ("Bookmark " + std::to_string(this->m_bookmarks.size() + 1)).c_str()); + } - std::strcpy(bookmark.name.data(), ("Bookmark " + std::to_string(this->m_bookmarks.size() + 1)).c_str()); + if (bookmark.comment.empty()) + std::memset(bookmark.comment.data(), 0x00, 0xF'FFFF); this->m_bookmarks.push_back(bookmark); ProjectFile::markDirty(); diff --git a/source/views/view_hexeditor.cpp b/source/views/view_hexeditor.cpp index a30317e51..cc4f7e8f3 100644 --- a/source/views/view_hexeditor.cpp +++ b/source/views/view_hexeditor.cpp @@ -8,6 +8,7 @@ #include "helpers/crypto.hpp" #include "helpers/patches.hpp" #include "helpers/project_file_handler.hpp" +#include "helpers/loader_script_handler.hpp" #undef __STRICT_ANSI__ #include @@ -151,6 +152,53 @@ namespace hex { ImGui::EndPopup(); } + if (ImGui::BeginPopupModal("Load File with Loader Script", nullptr, ImGuiWindowFlags_NoResize)) { + constexpr auto Message = "Load a file using a Python loader script."; + ImGui::NewLine(); + if (ImGui::BeginChild("##scrolling", ImVec2(500, 20))) { + ImGui::SetCursorPosX(10); + ImGui::TextWrapped("%s", Message); + ImGui::EndChild(); + } + + ImGui::NewLine(); + ImGui::InputText("##nolabel", this->m_loaderScriptScriptPath.data(), this->m_loaderScriptScriptPath.length(), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + if (ImGui::Button("Script")) + ImGui::OpenPopup("Loader Script: Open Script"); + ImGui::InputText("##nolabel", this->m_loaderScriptFilePath.data(), this->m_loaderScriptFilePath.length(), ImGuiInputTextFlags_ReadOnly); + ImGui::SameLine(); + if (ImGui::Button("File")) + ImGui::OpenPopup("Loader Script: Open File"); + + + if (this->m_fileBrowser.showFileDialog("Loader Script: Open Script", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".py")) { + this->m_loaderScriptScriptPath = this->m_fileBrowser.selected_path; + } + if (this->m_fileBrowser.showFileDialog("Loader Script: Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) { + this->m_loaderScriptFilePath = this->m_fileBrowser.selected_path; + } + + ImGui::NewLine(); + + ImGui::SetCursorPosX(40); + if (ImGui::Button("Load", ImVec2(100, 20))) { + if (!this->m_loaderScriptScriptPath.empty() && !this->m_loaderScriptFilePath.empty()) { + this->openFile(this->m_loaderScriptFilePath); + LoaderScript::setFilePath(this->m_loaderScriptFilePath); + LoaderScript::setDataProvider(this->m_dataProvider); + LoaderScript::processFile(this->m_loaderScriptScriptPath); + ImGui::CloseCurrentPopup(); + } + } + ImGui::SameLine(); + ImGui::SetCursorPosX(160); + if (ImGui::Button("Cancel", ImVec2(100, 20))) { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + if (this->m_fileBrowser.showFileDialog("Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) { this->openFile(this->m_fileBrowser.selected_path); @@ -279,6 +327,13 @@ namespace hex { View::doLater([]{ ImGui::OpenPopup("Apply IPS32 Patch"); }); } + if (ImGui::MenuItem("File with Loader Script")) { + this->getWindowOpenState() = true; + this->m_loaderScriptFilePath.clear(); + this->m_loaderScriptScriptPath.clear(); + View::doLater([]{ ImGui::OpenPopup("Load File with Loader Script"); }); + } + ImGui::EndMenu(); } @@ -363,9 +418,9 @@ namespace hex { if (ImGui::MenuItem("Create bookmark", nullptr, false, this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) { size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd); size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd); - Region selectionRegion = { start, end - start + 1 }; + Bookmark bookmark = { start, end - start + 1, { }, { } }; - View::postEvent(Events::AddBookmark, &selectionRegion); + View::postEvent(Events::AddBookmark, &bookmark); } ImGui::EndMenu();