/ deps / KittyMemoryEx / KittyMemOp.cpp
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  }