/ deps / KittyMemoryEx / MemoryPatch.cpp
MemoryPatch.cpp
  1  #include "MemoryPatch.hpp"
  2  
  3  #ifndef kNO_KEYSTONE
  4  #include "Deps/Keystone/includes/keystone.h"
  5  #endif
  6  
  7  MemoryPatch::MemoryPatch()
  8  {
  9      _pMem = nullptr;
 10      _address = 0;
 11      _size = 0;
 12      _orig_code.clear();
 13      _patch_code.clear();
 14  }
 15  
 16  MemoryPatch::~MemoryPatch()
 17  {
 18      // clean up
 19      _orig_code.clear();
 20      _orig_code.shrink_to_fit();
 21  
 22      _patch_code.clear();
 23      _patch_code.shrink_to_fit();
 24  }
 25  
 26  MemoryPatch::MemoryPatch(IKittyMemOp *pMem, uintptr_t absolute_address, const void *patch_code, size_t patch_size)
 27  {
 28      _pMem = nullptr;
 29      _address = 0;
 30      _size = 0;
 31      _orig_code.clear();
 32      _patch_code.clear();
 33  
 34      if (!pMem || !absolute_address || !patch_code || !patch_size)
 35          return;
 36  
 37      _pMem = pMem;
 38      _address = absolute_address;
 39      _size = patch_size;
 40      _orig_code.resize(patch_size);
 41      _patch_code.resize(patch_size);
 42  
 43      // initialize patch
 44      memcpy(&_patch_code[0], patch_code, patch_size);
 45  
 46      // backup current content
 47      _pMem->Read(_address, &_orig_code[0], _size);
 48  }
 49  
 50  bool MemoryPatch::isValid() const
 51  {
 52      return (_pMem && _address && _size && _orig_code.size() == _size && _patch_code.size() == _size);
 53  }
 54  
 55  size_t MemoryPatch::get_PatchSize() const
 56  {
 57      return _size;
 58  }
 59  
 60  uintptr_t MemoryPatch::get_TargetAddress() const
 61  {
 62      return _address;
 63  }
 64  
 65  bool MemoryPatch::Restore()
 66  {
 67      if (!isValid())
 68          return false;
 69  
 70      return _pMem->Write(_address, &_orig_code[0], _size);
 71  }
 72  
 73  bool MemoryPatch::Modify()
 74  {
 75      if (!isValid())
 76          return false;
 77  
 78      return _pMem->Write(_address, &_patch_code[0], _size);
 79  }
 80  
 81  std::string MemoryPatch::get_CurrBytes() const
 82  {
 83      if (!isValid())
 84          return "";
 85  
 86      std::vector<uint8_t> buffer(_size);
 87      _pMem->Read(_address, &buffer[0], _size);
 88  
 89      return KittyUtils::Data::toHex(&buffer[0], _size);
 90  }
 91  
 92  std::string MemoryPatch::get_OrigBytes() const
 93  {
 94      if (!isValid())
 95          return "";
 96  
 97      return KittyUtils::Data::toHex(&_orig_code[0], _orig_code.size());
 98  }
 99  
100  std::string MemoryPatch::get_PatchBytes() const
101  {
102      if (!isValid())
103          return "";
104  
105      return KittyUtils::Data::toHex(&_patch_code[0], _patch_code.size());
106  }
107  
108  /* ============================== MemoryPatchMgr ============================== */
109  
110  MemoryPatch MemoryPatchMgr::createWithBytes(uintptr_t absolute_address, const void *patch_code, size_t patch_size)
111  {
112      return MemoryPatch(_pMem, absolute_address, patch_code, patch_size);
113  }
114  
115  MemoryPatch MemoryPatchMgr::createWithHex(uintptr_t absolute_address, std::string hex)
116  {
117      if (!absolute_address || !KittyUtils::String::validateHex(hex))
118          return MemoryPatch();
119  
120      std::vector<uint8_t> patch_code(hex.length() / 2);
121      KittyUtils::Data::fromHex(hex, &patch_code[0]);
122  
123      return MemoryPatch(_pMem, absolute_address, patch_code.data(), patch_code.size());
124  }
125  
126  #ifndef kNO_KEYSTONE
127  
128  MemoryPatch MemoryPatchMgr::createWithAsm(uintptr_t absolute_address,
129                                            MP_ASM_ARCH asm_arch,
130                                            const std::string &asm_code,
131                                            uintptr_t asm_address)
132  {
133      MemoryPatch patch;
134  
135      if (!absolute_address || asm_code.empty())
136          return patch;
137  
138      ks_engine *ks = nullptr;
139      ks_err err = KS_ERR_ARCH;
140  
141      switch (asm_arch)
142      {
143      case MP_ASM_ARM32:
144          err = ks_open(KS_ARCH_ARM, KS_MODE_LITTLE_ENDIAN, &ks);
145          break;
146      case MP_ASM_ARM64:
147          err = ks_open(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN, &ks);
148          break;
149      case MP_ASM_x86:
150          err = ks_open(KS_ARCH_X86, KS_MODE_32, &ks);
151          break;
152      case MP_ASM_x86_64:
153          err = ks_open(KS_ARCH_X86, KS_MODE_64, &ks);
154          break;
155      default:
156          KITTY_LOGE("Unknown MP_ASM_ARCH '%d'.", asm_arch);
157          return patch;
158      }
159  
160      if (err != KS_ERR_OK)
161      {
162          KITTY_LOGE("ks_open failed with error = '%s'.", ks_strerror(err));
163          return patch;
164      }
165  
166      unsigned char *insn_bytes = nullptr;
167      size_t insn_count = 0, insn_size = 0;
168      int rt = ks_asm(ks, asm_code.c_str(), asm_address, &insn_bytes, &insn_size, &insn_count);
169  
170      if (rt == 0 && insn_bytes != nullptr && insn_size)
171      {
172          patch = MemoryPatch(_pMem, absolute_address, insn_bytes, insn_size);
173      }
174  
175      if (insn_bytes != nullptr)
176      {
177          ks_free(insn_bytes);
178      }
179  
180      ks_close(ks);
181  
182      if (rt)
183      {
184          KITTY_LOGE("ks_asm failed (asm: %s, count = %zu, error = '%s') (code = %u).",
185                     asm_code.c_str(),
186                     insn_count,
187                     ks_strerror(ks_errno(ks)),
188                     ks_errno(ks));
189      }
190  
191      return patch;
192  }
193  
194  #endif // kNO_KEYSTONE