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