KittyMemOp.cpp
1 #include "KittyMemOp.hpp" 2 #include <cerrno> 3 4 // process_vm_readv & process_vm_writev 5 #if defined(__aarch64__) 6 #define syscall_rpmv_n 270 7 #define syscall_wpmv_n 271 8 #elif defined(__arm__) 9 #define syscall_rpmv_n 376 10 #define syscall_wpmv_n 377 11 #elif defined(__i386__) 12 #define syscall_rpmv_n 347 13 #define syscall_wpmv_n 348 14 #elif defined(__x86_64__) 15 #define syscall_rpmv_n 310 16 #define syscall_wpmv_n 311 17 #else 18 #error "Unsupported ABI" 19 #endif 20 21 static ssize_t call_process_vm_readv(pid_t pid, 22 const iovec *lvec, 23 unsigned long liovcnt, 24 const iovec *rvec, 25 unsigned long riovcnt, 26 unsigned long flags) 27 { 28 return syscall(syscall_rpmv_n, pid, lvec, liovcnt, rvec, riovcnt, flags); 29 } 30 31 static ssize_t call_process_vm_writev(pid_t pid, 32 const iovec *lvec, 33 unsigned long liovcnt, 34 const iovec *rvec, 35 unsigned long riovcnt, 36 unsigned long flags) 37 { 38 return syscall(syscall_wpmv_n, pid, lvec, liovcnt, rvec, riovcnt, flags); 39 } 40 41 /* =================== IKittyMemOp =================== */ 42 43 std::string IKittyMemOp::ReadStr(uintptr_t address, size_t maxLen) 44 { 45 std::vector<char> chars(maxLen); 46 47 if (!Read(address, &chars[0], maxLen)) 48 return ""; 49 50 std::string str = ""; 51 for (size_t i = 0; i < chars.size(); i++) 52 { 53 if (chars[i] == '\0') 54 break; 55 56 str.push_back(chars[i]); 57 } 58 59 if ((int)str[0] == 0 && str.size() == 1) 60 return ""; 61 62 return str; 63 } 64 65 bool IKittyMemOp::WriteStr(uintptr_t address, std::string str) 66 { 67 size_t len = str.length() + 1; // extra for \0; 68 return Write(address, &str[0], len) == len; 69 } 70 71 /* =================== KittyMemSys =================== */ 72 73 bool KittyMemSys::init(pid_t pid) 74 { 75 if (pid < 1) 76 { 77 _lastErrno = ESRCH; 78 KITTY_LOGE("KittyMemSys: Invalid PID."); 79 return false; 80 } 81 82 errno = 0; 83 ssize_t rt = syscall(syscall_rpmv_n, 0, 0, 0, 0, 0, 0); 84 if (rt == -1 && errno == ENOSYS) 85 { 86 _lastErrno = ENOSYS; 87 KITTY_LOGE("KittyMemSys: syscall readv/writev not supported."); 88 return false; 89 } 90 91 _pid = pid; 92 return true; 93 } 94 95 size_t KittyMemSys::Read(uintptr_t address, void *buffer, size_t len) 96 { 97 if (_pid < 1 || !address || !buffer || !len) 98 return 0; 99 100 struct iovec lvec{.iov_base = buffer, .iov_len = 0}; 101 struct iovec rvec{.iov_base = KittyUtils::untagHeepPtr(reinterpret_cast<void *>(address)), .iov_len = 0}; 102 103 ssize_t n = 0; 104 size_t nbytes = 0, remaining = len; 105 bool page_mode = false; 106 do 107 { 108 size_t remaining_or_pglen = remaining; 109 if (page_mode) 110 remaining_or_pglen = std::min(KT_PAGE_LEN(rvec.iov_base), remaining); 111 112 lvec.iov_len = remaining_or_pglen; 113 rvec.iov_len = remaining_or_pglen; 114 115 errno = 0; 116 n = KT_EINTR_RETRY(call_process_vm_readv(_pid, &lvec, 1, &rvec, 1, 0)); 117 if (n > 0) 118 { 119 remaining -= n; 120 nbytes += n; 121 lvec.iov_base = reinterpret_cast<char *>(lvec.iov_base) + n; 122 rvec.iov_base = reinterpret_cast<char *>(rvec.iov_base) + n; 123 } 124 else 125 { 126 if (n == -1) 127 { 128 _lastErrno = errno; 129 if (_lastErrno != EFAULT && _lastErrno != EIO && _lastErrno != EINVAL) 130 break; 131 } 132 if (page_mode) 133 { 134 remaining -= remaining_or_pglen; 135 lvec.iov_base = reinterpret_cast<char *>(lvec.iov_base) + remaining_or_pglen; 136 rvec.iov_base = reinterpret_cast<char *>(rvec.iov_base) + remaining_or_pglen; 137 } 138 } 139 page_mode = n == -1 || size_t(n) != remaining_or_pglen; 140 } while (remaining > 0); 141 return nbytes; 142 } 143 144 size_t KittyMemSys::Write(uintptr_t address, void *buffer, size_t len) 145 { 146 if (_pid < 1 || !address || !buffer || !len) 147 return 0; 148 149 struct iovec lvec{.iov_base = buffer, .iov_len = 0}; 150 struct iovec rvec{.iov_base = KittyUtils::untagHeepPtr(reinterpret_cast<void *>(address)), .iov_len = 0}; 151 152 ssize_t n = 0; 153 size_t nbytes = 0, remaining = len; 154 bool write_one_page = false; 155 do 156 { 157 size_t remaining_or_pglen = remaining; 158 if (write_one_page) 159 remaining_or_pglen = std::min(KT_PAGE_LEN(rvec.iov_base), remaining); 160 161 lvec.iov_len = remaining_or_pglen; 162 rvec.iov_len = remaining_or_pglen; 163 164 errno = 0; 165 n = KT_EINTR_RETRY(call_process_vm_writev(_pid, &lvec, 1, &rvec, 1, 0)); 166 if (n > 0) 167 { 168 remaining -= n; 169 nbytes += n; 170 lvec.iov_base = reinterpret_cast<char *>(lvec.iov_base) + n; 171 rvec.iov_base = reinterpret_cast<char *>(rvec.iov_base) + n; 172 } 173 else 174 { 175 if (n == -1) 176 { 177 _lastErrno = errno; 178 if (_lastErrno != EFAULT && _lastErrno != EIO && _lastErrno != EINVAL) 179 break; 180 } 181 if (write_one_page) 182 { 183 remaining -= remaining_or_pglen; 184 lvec.iov_base = reinterpret_cast<char *>(lvec.iov_base) + remaining_or_pglen; 185 rvec.iov_base = reinterpret_cast<char *>(rvec.iov_base) + remaining_or_pglen; 186 } 187 } 188 write_one_page = n == -1 || size_t(n) != remaining_or_pglen; 189 } while (remaining > 0); 190 return nbytes; 191 } 192 193 /* =================== KittyMemIO =================== */ 194 195 bool KittyMemIO::init(pid_t pid) 196 { 197 if (pid < 1) 198 { 199 _lastErrno = ESRCH; 200 KITTY_LOGE("KittyMemIO: Invalid PID."); 201 return false; 202 } 203 204 _pid = pid; 205 206 char memPath[256] = {0}; 207 snprintf(memPath, sizeof(memPath), "/proc/%d/mem", _pid); 208 _pMem = std::make_unique<KittyIOFile>(memPath, O_RDWR); 209 if (!_pMem->open()) 210 { 211 _lastErrno = _pMem->lastError(); 212 KITTY_LOGE("Couldn't open mem file %s, error=%s", _pMem->path().c_str(), _pMem->lastStrError().c_str()); 213 return false; 214 } 215 216 return _pid > 0 && _pMem.get(); 217 } 218 219 size_t KittyMemIO::Read(uintptr_t address, void *buffer, size_t len) 220 { 221 if (_pid < 1 || !address || !buffer || !len || !_pMem.get()) 222 return 0; 223 224 address = KittyUtils::untagHeepPtr(address); 225 226 ssize_t n = 0; 227 size_t nbytes = 0, remaining = len; 228 bool page_mode = false; 229 do 230 { 231 size_t remaining_or_pglen = remaining; 232 if (page_mode) 233 remaining_or_pglen = std::min(KT_PAGE_LEN(address), remaining); 234 235 errno = 0; 236 n = _pMem->pread(address, buffer, remaining_or_pglen); 237 if (n > 0) 238 { 239 remaining -= n; 240 nbytes += n; 241 address += n; 242 buffer = reinterpret_cast<char *>(buffer) + n; 243 } 244 else 245 { 246 if (n == -1) 247 { 248 _lastErrno = errno; 249 if (_lastErrno != EFAULT && _lastErrno != EIO && _lastErrno != EINVAL) 250 break; 251 } 252 253 if (page_mode) 254 { 255 remaining -= remaining_or_pglen; 256 address += remaining_or_pglen; 257 buffer = reinterpret_cast<char *>(buffer) + remaining_or_pglen; 258 } 259 } 260 page_mode = n == -1 || size_t(n) != remaining_or_pglen; 261 } while (remaining > 0); 262 return nbytes; 263 } 264 265 size_t KittyMemIO::Write(uintptr_t address, void *buffer, size_t len) 266 { 267 if (_pid < 1 || !address || !buffer || !len || !_pMem.get()) 268 return 0; 269 270 address = KittyUtils::untagHeepPtr(address); 271 272 ssize_t n = 0; 273 size_t nbytes = 0, remaining = len; 274 bool page_mode = false; 275 do 276 { 277 size_t remaining_or_pglen = remaining; 278 if (page_mode) 279 remaining_or_pglen = std::min(KT_PAGE_LEN(address), remaining); 280 281 errno = 0; 282 n = _pMem->pwrite(address, buffer, remaining_or_pglen); 283 if (n > 0) 284 { 285 remaining -= n; 286 nbytes += n; 287 address += n; 288 buffer = reinterpret_cast<char *>(buffer) + n; 289 } 290 else 291 { 292 if (n == -1) 293 { 294 _lastErrno = errno; 295 if (_lastErrno != EFAULT && _lastErrno != EIO && _lastErrno != EINVAL) 296 break; 297 } 298 299 if (page_mode) 300 { 301 remaining -= remaining_or_pglen; 302 address += remaining_or_pglen; 303 buffer = reinterpret_cast<char *>(buffer) + remaining_or_pglen; 304 } 305 } 306 page_mode = n == -1 || size_t(n) != remaining_or_pglen; 307 } while (remaining > 0); 308 return nbytes; 309 }