amd64_emulator.hpp 6.6 KB

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