i386_emulator.hpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #pragma once
  2. #include <winsock2.h>
  3. #include <windows.h>
  4. #include <unicorn/unicorn.h>
  5. #include <any>
  6. #include <memory>
  7. #include <string>
  8. #include <unordered_map>
  9. #include <functional>
  10. #include "resource_wrapper.hpp"
  11. #include "resource_traits/unicorn/unicorn_handle.hpp"
  12. #include "exception.hpp"
  13. #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\i386_emulator.hpp"
  14. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  15. namespace nkg {
  16. class i386_emulator {
  17. public:
  18. class backend_error : public ::nkg::exception {
  19. public:
  20. using error_code_t = uc_err;
  21. private:
  22. error_code_t m_error_code;
  23. std::string m_error_string;
  24. public:
  25. backend_error(std::string_view file, int line, error_code_t unicorn_err, std::string_view message) noexcept :
  26. ::nkg::exception(file, line, message), m_error_code(unicorn_err), m_error_string(uc_strerror(unicorn_err)) {}
  27. [[nodiscard]]
  28. virtual bool error_code_exists() const noexcept override {
  29. return true;
  30. }
  31. [[nodiscard]]
  32. virtual intptr_t error_code() const noexcept override {
  33. return m_error_code;
  34. }
  35. [[nodiscard]]
  36. virtual const std::string& error_string() const noexcept override {
  37. return m_error_string;
  38. }
  39. };
  40. using hookcode_cb_t = void(uint32_t address, size_t size);
  41. using hookmem_cb_t = void(uc_mem_type type, uint32_t address, size_t size, int32_t value);
  42. using eventmem_cb_t = bool(uc_mem_type type, uint32_t address, size_t size, int32_t value);
  43. private:
  44. struct hook_stub_context_t {
  45. i386_emulator* self;
  46. uc_hook unicorn_hook_handle;
  47. };
  48. resource_wrapper<resource_traits::unicorn::unicorn_handle> m_unicorn_engine;
  49. std::unordered_map<std::string, std::any> m_unicorn_user_ctx;
  50. std::unordered_map<uc_hook, std::unique_ptr<hook_stub_context_t>> m_unicorn_hook_stub_ctxs;
  51. std::unordered_map<uc_hook, std::any> m_unicorn_hook_callbacks;
  52. static void _unicorn_hookcode_cb_stub(uc_engine* uc, uint64_t address, uint32_t size, void* user_data);
  53. static void _unicorn_hookmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data);
  54. static bool _unicorn_eventmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data);
  55. public:
  56. i386_emulator();
  57. void reg_read(int regid, void* value);
  58. void reg_write(int regid, const void* value);
  59. void mem_map(uint32_t address, size_t size, uint32_t perms);
  60. void mem_unmap(uint32_t address, size_t size);
  61. void mem_read(uint32_t address, void* buf, size_t size);
  62. std::vector<uint8_t> mem_read(uint32_t address, size_t size);
  63. void mem_write(uint32_t address, const void* buf, size_t size);
  64. void mem_write(uint32_t address, const std::vector<uint8_t>& buf);
  65. template<int hook_type, typename callable_t>
  66. uc_hook hook_add(callable_t&& hook_callback, uint32_t begin_address = 1, uint32_t end_address = 0) {
  67. uc_err err;
  68. auto hook_stub_ctx = std::make_unique<hook_stub_context_t>();
  69. hook_stub_ctx->self = this;
  70. hook_stub_ctx->unicorn_hook_handle = 0;
  71. if constexpr (hook_type == UC_HOOK_CODE) {
  72. err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, _unicorn_hookcode_cb_stub, hook_stub_ctx.get(), begin_address, end_address);
  73. if (err != UC_ERR_OK) {
  74. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed.");
  75. }
  76. m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function<hookcode_cb_t>{ std::forward<callable_t>(hook_callback) }));
  77. } else if constexpr ((hook_type & ~UC_HOOK_MEM_VALID) == 0) {
  78. err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, _unicorn_hookmem_cb_stub, hook_stub_ctx.get(), begin_address, end_address);
  79. if (err != UC_ERR_OK) {
  80. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed.");
  81. }
  82. m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function<hookmem_cb_t>{ std::forward<callable_t>(hook_callback) }));
  83. } else if constexpr ((hook_type & ~UC_HOOK_MEM_UNMAPPED) == 0) {
  84. err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, _unicorn_eventmem_cb_stub, hook_stub_ctx.get(), begin_address, end_address);
  85. if (err != UC_ERR_OK) {
  86. throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed.");
  87. }
  88. m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function<eventmem_cb_t>{ std::forward<callable_t>(hook_callback) }));
  89. } else {
  90. static_assert(
  91. hook_type == UC_HOOK_CODE ||
  92. (hook_type & ~UC_HOOK_MEM_VALID) == 0 ||
  93. (hook_type & ~UC_HOOK_MEM_UNMAPPED) == 0, "Unsupported hook type.");
  94. }
  95. return m_unicorn_hook_stub_ctxs.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::move(hook_stub_ctx))).first->first;
  96. }
  97. void hook_del(uc_hook hook_handle);
  98. void create_gdt_entry(uint32_t gdt_entry_address, uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flags);
  99. void emu_start(uint32_t begin_address, uint32_t end_address = 0, uint64_t timeout = 0, size_t count = 0);
  100. void emu_stop();
  101. template<typename val_t>
  102. void context_set(const std::string& name, val_t&& value) {
  103. m_unicorn_user_ctx[name] = std::forward<val_t>(value);
  104. }
  105. template<typename val_t>
  106. val_t context_get(const std::string& name) {
  107. return std::any_cast<val_t>(m_unicorn_user_ctx[name]);
  108. }
  109. };
  110. }
  111. #undef NKG_CURRENT_SOURCE_LINE
  112. #undef NKG_CURRENT_SOURCE_FILE