/ deps / KittyMemoryEx / KittyTrace.cpp
KittyTrace.cpp
   1  #include "KittyTrace.hpp"
   2  
   3  bool KittyTraceMgr::attach(int options)
   4  {
   5      if (_pid <= 0)
   6          return false;
   7  
   8      if (isAttached())
   9      {
  10          _attached = true;
  11          return true;
  12      }
  13  
  14      errno = 0;
  15      if (ptrace(PTRACE_ATTACH, _pid, nullptr, options) == -1L)
  16      {
  17          KITTY_LOGE("PTRACE_ATTACH failed for pid %d. \"%s\".", _pid, strerror(errno));
  18          return false;
  19      }
  20  
  21      _seized = false;
  22  
  23      int status;
  24      if (waitpid(_pid, &status, 0) != _pid || !WIFSTOPPED(status))
  25      {
  26          KITTY_LOGE("Error occurred while waiting for pid %d to stop. \"%s\".", _pid, strerror(errno));
  27          ptrace(PTRACE_DETACH, _pid, nullptr, nullptr);
  28          return false;
  29      }
  30  
  31      _attached = true;
  32  
  33      if (options != 0)
  34          setOptions(options);
  35  
  36      return true;
  37  }
  38  
  39  bool KittyTraceMgr::seize(int options)
  40  {
  41      if (_pid <= 0)
  42          return false;
  43  
  44      if (isAttached())
  45      {
  46          _attached = true;
  47          return true;
  48      }
  49  
  50      errno = 0;
  51      if (ptrace(PTRACE_SEIZE, _pid, nullptr, options) == -1L)
  52      {
  53          KITTY_LOGE("PTRACE_SEIZE failed for pid %d. \"%s\".", _pid, strerror(errno));
  54          return false;
  55      }
  56  
  57      _seized = true;
  58      _attached = true;
  59  
  60      return true;
  61  }
  62  
  63  bool KittyTraceMgr::setOptions(int options)
  64  {
  65      errno = 0;
  66      if (ptrace(PTRACE_SETOPTIONS, _pid, nullptr, options) == -1L)
  67      {
  68          KITTY_LOGE("PTRACE_SETOPTIONS failed for pid %d. \"%s\".", _pid, strerror(errno));
  69          return false;
  70      }
  71  
  72      return true;
  73  }
  74  
  75  bool KittyTraceMgr::detach()
  76  {
  77      _attached = false;
  78  
  79      if (!isAttached())
  80          return true;
  81  
  82      while (true)
  83      {
  84          int status = 0;
  85          if (waitpid(_pid, &status, __WALL | WNOHANG) <= 0)
  86              break;
  87      }
  88  
  89      errno = 0;
  90      if (ptrace(PTRACE_DETACH, _pid, nullptr, nullptr) == -1L)
  91      {
  92          KITTY_LOGE("PTRACE_DETACH failed for pid %d. \"%s\".", _pid, strerror(errno));
  93          return false;
  94      }
  95  
  96      while (true)
  97      {
  98          int status = 0;
  99          if (waitpid(_pid, &status, __WALL | WNOHANG) <= 0)
 100              break;
 101      }
 102  
 103      return true;
 104  }
 105  
 106  bool KittyTraceMgr::stop()
 107  {
 108      if (!_attached || _pid <= 0)
 109          return false;
 110  
 111      errno = 0;
 112  
 113      if (_seized)
 114      {
 115          if (ptrace(PTRACE_INTERRUPT, _pid, nullptr, nullptr) == -1L)
 116          {
 117              KITTY_LOGE("PTRACE_INTERRUPT failed for pid %d. \"%s\".", _pid, strerror(errno));
 118              return false;
 119          }
 120      }
 121      else
 122      {
 123          if (tgkill(_pid, _pid, SIGSTOP) == -1)
 124          {
 125              KITTY_LOGE("tgkill failed for pid %d. \"%s\".", _pid, strerror(errno));
 126              return false;
 127          }
 128      }
 129  
 130      int status;
 131      if (waitpid(_pid, &status, 0) != _pid || !WIFSTOPPED(status))
 132      {
 133          KITTY_LOGE("Error occurred while waiting for pid %d to stop. \"%s\".", _pid, strerror(errno));
 134          return false;
 135      }
 136  
 137      return true;
 138  }
 139  
 140  bool KittyTraceMgr::cont(int sig)
 141  {
 142      if (!_attached || _pid <= 0)
 143          return false;
 144  
 145      errno = 0;
 146      if (ptrace(PTRACE_CONT, _pid, nullptr, sig) == -1L)
 147      {
 148          KITTY_LOGE("PTRACE_CONT failed for pid %d. \"%s\".", _pid, strerror(errno));
 149          return false;
 150      }
 151  
 152      return true;
 153  }
 154  
 155  pid_t KittyTraceMgr::wait(int *status, int options, int timeout_ms) const
 156  {
 157      if (!_attached)
 158          return -1;
 159  
 160      if (timeout_ms <= 0)
 161          return waitpid(_pid, status, options);
 162  
 163      int elapsed = 0;
 164      pid_t res;
 165      if (!(options & WNOHANG))
 166          options |= WNOHANG;
 167  
 168      while (elapsed < timeout_ms)
 169      {
 170          res = waitpid(_pid, status, options);
 171          if (res != 0)
 172              return res;
 173  
 174          usleep(25000);
 175          elapsed += 25;
 176      }
 177  
 178      return res;
 179  }
 180  
 181  bool KittyTraceMgr::waitSyscall() const
 182  {
 183      if (!_attached || _pid <= 0)
 184          return false;
 185  
 186      errno = 0;
 187      if (ptrace(PTRACE_SYSCALL, _pid, nullptr, nullptr) == -1L)
 188      {
 189          KITTY_LOGE("PTRACE_SYSCALL failed for pid %d. \"%s\".", _pid, strerror(errno));
 190          return false;
 191      }
 192  
 193      int status = 0;
 194      waitpid(_pid, &status, 0);
 195      if (!WIFSTOPPED(status))
 196          return false;
 197  
 198      return true;
 199  }
 200  
 201  bool KittyTraceMgr::step(int steps) const
 202  {
 203      if (!_attached || _pid <= 0)
 204          return false;
 205  
 206      int status = 0;
 207      for (int i = 0; i < steps; ++i)
 208      {
 209          errno = 0;
 210          if (ptrace(PTRACE_SINGLESTEP, _pid, nullptr, nullptr) == -1L)
 211          {
 212              KITTY_LOGE("PTRACE_SINGLESTEP failed for pid %d. \"%s\".", _pid, strerror(errno));
 213              return false;
 214          }
 215  
 216          if ((i + 1) < steps)
 217          {
 218              waitpid(_pid, &status, 0);
 219              if (!WIFSTOPPED(status))
 220                  return false;
 221          }
 222      }
 223  
 224      return true;
 225  }
 226  
 227  bool KittyTraceMgr::waitStep(int steps) const
 228  {
 229      if (!_attached || _pid <= 0)
 230          return false;
 231  
 232      int status = 0;
 233      for (int i = 0; i < steps; ++i)
 234      {
 235          errno = 0;
 236          if (ptrace(PTRACE_SINGLESTEP, _pid, nullptr, nullptr) == -1L)
 237          {
 238              KITTY_LOGE("PTRACE_SINGLESTEP failed for pid %d. \"%s\".", _pid, strerror(errno));
 239              return false;
 240          }
 241  
 242          waitpid(_pid, &status, 0);
 243          if (!WIFSTOPPED(status))
 244              return false;
 245      }
 246  
 247      return true;
 248  }
 249  
 250  bool KittyTraceMgr::getRegs(user_regs_struct *regs) const
 251  {
 252      if (!_attached || _pid <= 0 || !regs)
 253          return false;
 254  
 255      errno = 0;
 256  
 257  #if defined(__LP64__)
 258      iovec ioVec;
 259      ioVec.iov_base = regs;
 260      ioVec.iov_len = sizeof(*regs);
 261      long ret = ptrace(KT_PTRACE_GETREG_REQ, _pid, NT_PRSTATUS, &ioVec);
 262  #else
 263      long ret = ptrace(KT_PTRACE_GETREG_REQ, _pid, nullptr, regs);
 264  #endif
 265      if (ret == -1L)
 266      {
 267          KITTY_LOGE("PTRACE_GETREGS failed for pid %d. \"%s\".", _pid, strerror(errno));
 268          return false;
 269      }
 270  
 271      return true;
 272  }
 273  
 274  bool KittyTraceMgr::setRegs(user_regs_struct *regs) const
 275  {
 276      if (!_attached || _pid <= 0 || !regs)
 277          return false;
 278  
 279      errno = 0;
 280  
 281  #if defined(__LP64__)
 282      iovec ioVec;
 283      ioVec.iov_base = regs;
 284      ioVec.iov_len = sizeof(*regs);
 285      long ret = ptrace(KT_PTRACE_SETREG_REQ, _pid, NT_PRSTATUS, &ioVec);
 286  #else
 287      long ret = ptrace(KT_PTRACE_SETREG_REQ, _pid, nullptr, regs);
 288  #endif
 289      if (ret == -1L)
 290      {
 291          KITTY_LOGE("PTRACE_SETREGS failed for pid %d. \"%s\".", _pid, strerror(errno));
 292          return false;
 293      }
 294  
 295      return true;
 296  }
 297  
 298  size_t KittyTraceMgr::peekMem(uintptr_t addr, void *buf, size_t size) const
 299  {
 300      static constexpr size_t WORD_SIZE = sizeof(long);
 301  
 302      if (!_attached || _pid <= 0)
 303          return false;
 304  
 305      uint8_t *out = static_cast<uint8_t *>(buf);
 306      size_t total = 0;
 307  
 308      uintptr_t aligned_start = addr & ~(WORD_SIZE - 1);
 309      uintptr_t aligned_end = (addr + size + WORD_SIZE - 1) & ~(WORD_SIZE - 1);
 310  
 311      for (uintptr_t cur = aligned_start; cur < aligned_end; cur += WORD_SIZE)
 312      {
 313          errno = 0;
 314          long data = ptrace(PTRACE_PEEKDATA, _pid, (void *)cur, nullptr);
 315          if (data == -1 && errno)
 316              return total;
 317  
 318          size_t copy_start = (cur < addr) ? addr - cur : 0;
 319          size_t copy_end = ((cur + WORD_SIZE) > (addr + size)) ? (addr + size) - cur : WORD_SIZE;
 320          size_t copy_len = copy_end - copy_start;
 321  
 322          memcpy(out + total, ((uint8_t *)&data) + copy_start, copy_len);
 323          total += copy_len;
 324      }
 325  
 326      return total;
 327  }
 328  
 329  size_t KittyTraceMgr::pokeMem(uintptr_t addr, const void *buf, size_t size) const
 330  {
 331      static constexpr size_t WORD_SIZE = sizeof(long);
 332  
 333      if (!_attached || _pid <= 0)
 334          return false;
 335  
 336      const uint8_t *in = static_cast<const uint8_t *>(buf);
 337      size_t total = 0;
 338  
 339      uintptr_t aligned_start = addr & ~(WORD_SIZE - 1);
 340      uintptr_t aligned_end = (addr + size + WORD_SIZE - 1) & ~(WORD_SIZE - 1);
 341  
 342      for (uintptr_t cur = aligned_start; cur < aligned_end; cur += WORD_SIZE)
 343      {
 344          size_t write_start = (cur < addr) ? addr - cur : 0;
 345          size_t write_end = ((cur + WORD_SIZE) > (addr + size)) ? (addr + size) - cur : WORD_SIZE;
 346          size_t write_len = write_end - write_start;
 347  
 348          long data = 0;
 349  
 350          if (write_len != WORD_SIZE)
 351          {
 352              errno = 0;
 353              data = ptrace(PTRACE_PEEKDATA, _pid, (void *)cur, nullptr);
 354              if (data == -1 && errno)
 355                  return total;
 356          }
 357  
 358          memcpy(((uint8_t *)&data) + write_start, in + total, write_len);
 359  
 360          if (ptrace(PTRACE_POKEDATA, _pid, (void *)cur, data) == -1)
 361              return total;
 362  
 363          total += write_len;
 364      }
 365  
 366      return total;
 367  }
 368  
 369  // refs
 370  // https://github.com/evilsocket/arminject
 371  // https://github.com/Chainfire/injectvm-binderjack
 372  // https://github.com/shunix/TinyInjector
 373  // https://github.com/topjohnwu/Magisk/blob/master/native/src/zygisk/ptrace.cpp
 374  
 375  kitty_rp_call_t KittyTraceMgr::_callFunctionFrom(uintptr_t callerAddress, uintptr_t functionAddress, int nargs, ...)
 376  {
 377      if (!_attached || _pid <= 0 || functionAddress == 0)
 378          return {KT_RP_CALL_FAILED, {0}};
 379  
 380      user_regs_struct backup_regs, return_regs, tmp_regs;
 381      memset(&backup_regs, 0, sizeof(backup_regs));
 382      memset(&return_regs, 0, sizeof(return_regs));
 383      memset(&tmp_regs, 0, sizeof(tmp_regs));
 384  
 385      // backup current regs
 386      if (!getRegs(&backup_regs))
 387      {
 388          KITTY_LOGE("callFunction(%p): Failed, couldn't get regs.", (void *)functionAddress);
 389          return {KT_RP_CALL_REGS_FAILED, {0}};
 390      }
 391  
 392      memcpy(&tmp_regs, &backup_regs, sizeof(backup_regs));
 393  
 394      KT_REGS_ALIGN_STACK(backup_regs);
 395      KT_REGS_ALIGN_STACK(tmp_regs);
 396      KT_REGS_ALIGN_STACK(return_regs);
 397  
 398      KITTY_LOGD("callFunction(%p): Calling with %d args.", (void *)functionAddress, nargs);
 399  
 400      std::vector<uintptr_t> vargs(nargs, 0);
 401      if (nargs > 0)
 402      {
 403          va_list vl;
 404          va_start(vl, nargs);
 405          for (int i = 0; i < nargs; i++)
 406          {
 407              vargs[i] = va_arg(vl, uintptr_t);
 408          }
 409          va_end(vl);
 410      }
 411  
 412      // cleanup failure return
 413      auto failure_return = [&](KT_RP_CALL_STATUS s = KT_RP_CALL_FAILED) -> kitty_rp_call_t {
 414          KITTY_LOGE("callFunction(%p): Failed.", (void *)functionAddress);
 415          if (_autoRestoreRegs)
 416              setRegs(&backup_regs);
 417          return {s, {0}};
 418      };
 419  
 420      auto validate_ret = [this](const user_regs_struct &regs, uintptr_t return_addr) -> bool {
 421          uintptr_t pc = regs.KT_REG_PC;
 422          if (pc != return_addr)
 423          {
 424  #if defined(__arm__)
 425              if (uintptr_t((intptr_t(pc) & ~1)) != uintptr_t((intptr_t(return_addr) & ~1)))
 426  #elif defined(__i386__) || defined(__x86_64__)
 427              if (pc < return_addr || pc > (return_addr + 7))
 428  #endif
 429              {
 430                  siginfo_t si = {};
 431                  getSignalInfo(&si);
 432                  return uintptr_t(si.si_addr) == return_addr;
 433              }
 434          }
 435          return true;
 436      };
 437  
 438  #if defined(__arm__) || defined(__aarch64__)
 439  
 440      // Fill R0-Rx with the first 4 (32-bit) or 8 (64-bit) parameters
 441      for (int i = 0; (i < nargs) && (i < KT_REG_ARGS_NUM); i++)
 442      {
 443  #if defined(__arm__)
 444          tmp_regs.uregs[i] = vargs[i];
 445  #else
 446          tmp_regs.regs[i] = vargs[i];
 447  #endif
 448      }
 449  
 450      // push remaining parameters onto stack
 451      if (nargs > KT_REG_ARGS_NUM)
 452      {
 453          KT_REGS_ALIGN_STACK_N(tmp_regs, sizeof(uintptr_t) * (nargs - KT_REG_ARGS_NUM));
 454          if (!pokeMem(tmp_regs.KT_REG_SP, &vargs[KT_REG_ARGS_NUM], sizeof(uintptr_t) * (nargs - KT_REG_ARGS_NUM)))
 455              return failure_return(KT_RP_CALL_MEM_FAILED);
 456      }
 457  
 458      // Set return address
 459      tmp_regs.KT_REG_LR = callerAddress;
 460  
 461      // Set function address
 462      tmp_regs.KT_REG_PC = functionAddress;
 463  
 464      // Setup the current processor status register
 465  #if defined(__arm__)
 466      if (tmp_regs.KT_REG_PC & 1)
 467      {
 468          // thumb
 469          tmp_regs.KT_REG_PC &= (~1u);
 470          tmp_regs.KT_REG_CPSR |= KT_CPSR_T_MASK;
 471      }
 472      else
 473      {
 474          // arm
 475          tmp_regs.KT_REG_CPSR &= ~KT_CPSR_T_MASK;
 476      }
 477  #endif
 478  
 479  #elif defined(__i386__)
 480  
 481      // push all parameters onto stack
 482      if (nargs > 0)
 483      {
 484          KT_REGS_ALIGN_STACK_N(tmp_regs, sizeof(uintptr_t) * nargs);
 485          if (!pokeMem(tmp_regs.KT_REG_SP, &vargs[0], nargs * sizeof(uintptr_t)))
 486              return failure_return(KT_RP_CALL_MEM_FAILED);
 487      }
 488  
 489      // Push return address onto stack
 490      tmp_regs.KT_REG_SP -= sizeof(uintptr_t);
 491      if (!pokeMem(tmp_regs.KT_REG_SP, &callerAddress, sizeof(uintptr_t)))
 492          return failure_return(KT_RP_CALL_MEM_FAILED);
 493  
 494      // Set function address to call
 495      tmp_regs.KT_REG_IP = functionAddress;
 496  
 497  #elif defined(__x86_64__)
 498  
 499      // Fill [RDI, RSI, RDX, RCX, R8, R9] with the first 6 parameters
 500      for (int i = 0; (i < nargs) && (i < KT_REG_ARGS_NUM); ++i)
 501      {
 502          switch (i)
 503          {
 504          case 0:
 505              tmp_regs.rdi = vargs[i];
 506              break;
 507          case 1:
 508              tmp_regs.rsi = vargs[i];
 509              break;
 510          case 2:
 511              tmp_regs.rdx = vargs[i];
 512              break;
 513          case 3:
 514              tmp_regs.rcx = vargs[i];
 515              break;
 516          case 4:
 517              tmp_regs.r8 = vargs[i];
 518              break;
 519          case 5:
 520              tmp_regs.r9 = vargs[i];
 521              break;
 522          }
 523      }
 524  
 525      // Push remaining parameters onto stack
 526      if (nargs > KT_REG_ARGS_NUM)
 527      {
 528          KT_REGS_ALIGN_STACK_N(tmp_regs, sizeof(uintptr_t) * (nargs - KT_REG_ARGS_NUM));
 529          if (!pokeMem(tmp_regs.KT_REG_SP, &vargs[KT_REG_ARGS_NUM], sizeof(uintptr_t) * (nargs - KT_REG_ARGS_NUM)))
 530              return failure_return(KT_RP_CALL_MEM_FAILED);
 531      }
 532  
 533      // Push return address onto stack
 534      tmp_regs.KT_REG_SP -= sizeof(uintptr_t);
 535      if (!pokeMem(tmp_regs.KT_REG_SP, &callerAddress, sizeof(uintptr_t)))
 536          return failure_return(KT_RP_CALL_MEM_FAILED);
 537  
 538      // Set function address to call
 539      tmp_regs.KT_REG_IP = functionAddress;
 540  
 541      // may be needed
 542      tmp_regs.rax = 0;
 543      tmp_regs.orig_rax = 0;
 544  
 545  #else
 546  #error "Unsupported ABI"
 547  #endif
 548  
 549      // Set new registers
 550      if (!setRegs(&tmp_regs))
 551          return failure_return(KT_RP_CALL_REGS_FAILED);
 552  
 553      // Resume execution
 554      if (!cont())
 555          return failure_return(KT_RP_CALL_CONT_FAILED);
 556  
 557      // Catch SIGSEGV caused by our code
 558      do
 559      {
 560          int status = 0;
 561          errno = 0;
 562          pid_t wp = wait(&status, WUNTRACED, _remoteCallTimeout);
 563          if (wp != _pid)
 564          {
 565              if (wp == 0)
 566              {
 567                  stop();
 568                  KITTY_LOGE("callFunction(%p): timedout!", (void *)functionAddress);
 569                  return failure_return(KT_RP_CALL_TIMEOUT);
 570                  ;
 571              }
 572  
 573              KITTY_LOGE("callFunction(%p): waitpid return %d. \"%s\".", (void *)functionAddress, wp, strerror(errno));
 574              return failure_return(KT_RP_CALL_WAIT_FAILED);
 575          }
 576  
 577          if (WIFEXITED(status))
 578          {
 579              _attached = false;
 580              KITTY_LOGE("callFunction(%p): Target process exited (%d).", (void *)functionAddress, WEXITSTATUS(status));
 581              return {KT_RP_CALL_EXITED, {0}};
 582          }
 583  
 584          if (WIFSIGNALED(status))
 585          {
 586              _attached = false;
 587              KITTY_LOGE("callFunction(%p): Target process terminated (%d).", (void *)functionAddress, WTERMSIG(status));
 588              return {KT_RP_CALL_EXITED, {0}};
 589          }
 590  
 591          if (!WIFSTOPPED(status))
 592              continue;
 593  
 594          if (WSTOPSIG(status) == SIGCHLD || WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTSTP)
 595          {
 596              if (!cont())
 597                  return failure_return(KT_RP_CALL_CONT_FAILED);
 598  
 599              continue;
 600          }
 601  
 602          if (!getRegs(&return_regs))
 603              return failure_return(KT_RP_CALL_REGS_FAILED);
 604  
 605          KITTY_LOGD("callFunction(%p): Ok.", (void *)functionAddress);
 606  
 607          if (validate_ret(return_regs, callerAddress))
 608              break;
 609  
 610          KITTY_LOGE("callFunction(%p): Process didn't jump to specified return address (%p)",
 611                     (void *)functionAddress,
 612                     (void *)callerAddress);
 613  
 614          KITTY_LOGE("callFunction(%p): PC(%p) | RET(%p).",
 615                     (void *)functionAddress,
 616                     (void *)(return_regs.KT_REG_PC),
 617                     (void *)(return_regs.KT_REG_RET));
 618  
 619          siginfo_t si = {};
 620          getSignalInfo(&si);
 621  
 622          KITTY_LOGE("callFunction(%p): SIG(%s) | CODE(%d) | ADDR(%p).",
 623                     (void *)functionAddress,
 624                     strsignal(si.si_signo),
 625                     si.si_code,
 626                     (void *)(si.si_addr));
 627  
 628          auto map = KittyMemoryEx::getAddressMap(_pid, uintptr_t(si.si_addr));
 629          if (map.isValid())
 630          {
 631              KITTY_LOGE("callFunction(%p): Fault Map(<base>+%p) %s",
 632                         (void *)functionAddress,
 633                         (void *)((map.offset + uintptr_t(si.si_addr)) - map.startAddress),
 634                         map.toString().c_str());
 635          }
 636  
 637          map = KittyMemoryEx::getAddressMap(_pid, return_regs.KT_REG_PC);
 638          if (map.isValid())
 639          {
 640              KITTY_LOGE("callFunction(%p): PC Map(<base>+%p) %s",
 641                         (void *)functionAddress,
 642                         (void *)((map.offset + return_regs.KT_REG_PC) - map.startAddress),
 643                         map.toString().c_str());
 644          }
 645  
 646          if (!cont(WSTOPSIG(status)))
 647              return failure_return(KT_RP_CALL_CONT_FAILED);
 648  
 649          return failure_return(KT_RP_CALL_MISMATCH_STOP);
 650      } while (true);
 651  
 652      kitty_rp_call_t result = {KT_RP_CALL_SUCCESS, {static_cast<intptr_t>(return_regs.KT_REG_RET)}};
 653  
 654      // Restore regs
 655      if (_autoRestoreRegs)
 656          setRegs(&backup_regs);
 657  
 658      KITTY_LOGD("callFunction: Calling function %p returned %p.", (void *)functionAddress, (void *)result.result.ptr);
 659  
 660      return result;
 661  }
 662  
 663  kitty_rp_call_t KittyTraceMgr::_callSyscall(long sysnr, int nargs, ...)
 664  {
 665      if (!_attached || _pid <= 0)
 666          return {KT_RP_CALL_FAILED, {0}};
 667  
 668      user_regs_struct backup_regs, return_regs, tmp_regs;
 669      memset(&backup_regs, 0, sizeof(backup_regs));
 670      memset(&return_regs, 0, sizeof(return_regs));
 671      memset(&tmp_regs, 0, sizeof(tmp_regs));
 672  
 673      // backup current regs
 674      if (!getRegs(&backup_regs))
 675      {
 676          KITTY_LOGE("callSyscall(%d): Failed, couldn't get regs.", int(sysnr));
 677          return {KT_RP_CALL_REGS_FAILED, {0}};
 678      }
 679  
 680      memcpy(&tmp_regs, &backup_regs, sizeof(backup_regs));
 681  
 682      KT_REGS_ALIGN_STACK(backup_regs);
 683      KT_REGS_ALIGN_STACK(tmp_regs);
 684      KT_REGS_ALIGN_STACK(return_regs);
 685  
 686      std::vector<uintptr_t> vargs(6, 0);
 687      if (nargs > 0)
 688      {
 689          va_list vl;
 690          va_start(vl, nargs);
 691          for (int i = 0; i < nargs; i++)
 692          {
 693              vargs[i] = va_arg(vl, uintptr_t);
 694          }
 695          va_end(vl);
 696      }
 697  
 698      KITTY_LOGD("callSyscall(%d, 0x%zx, 0x%zx, 0x%zx, 0x%zx, 0x%zx, 0x%zx)",
 699                 int(sysnr),
 700                 vargs[0],
 701                 vargs[1],
 702                 vargs[2],
 703                 vargs[3],
 704                 vargs[4],
 705                 vargs[5]);
 706  
 707      uintptr_t target_pc_mem = tmp_regs.KT_REG_PC;
 708      std::vector<uint8_t> syscall_code;
 709  
 710  #if defined(__arm__)
 711      bool thumb = (target_pc_mem & 1) != 0 || (tmp_regs.KT_REG_CPSR & KT_CPSR_T_MASK) != 0;
 712      target_pc_mem &= ~1;
 713      if (thumb)
 714          syscall_code.assign(std::begin(KittyTraceInsns::THUMB_SYSCALL), std::end(KittyTraceInsns::THUMB_SYSCALL));
 715      else
 716          syscall_code.assign(std::begin(KittyTraceInsns::SYSCALL), std::end(KittyTraceInsns::SYSCALL));
 717  #else
 718      syscall_code.assign(std::begin(KittyTraceInsns::SYSCALL), std::end(KittyTraceInsns::SYSCALL));
 719  #endif
 720  
 721      std::vector<uint8_t> backup_code(syscall_code.size(), 0);
 722      if (!peekMem(target_pc_mem, backup_code.data(), backup_code.size()))
 723      {
 724          KITTY_LOGE("callSyscall(%d): Failed to backup PC(%p) memory code.", int(sysnr), (void *)target_pc_mem);
 725          return {KT_RP_CALL_MEM_FAILED, {0}};
 726      }
 727  
 728      // cleanup failure return
 729      auto failure_return = [&](KT_RP_CALL_STATUS s = KT_RP_CALL_FAILED) -> kitty_rp_call_t {
 730          KITTY_LOGE("callSyscall(%d): Failed.", int(sysnr));
 731  
 732          if (_autoRestoreRegs)
 733              setRegs(&backup_regs);
 734  
 735          pokeMem(target_pc_mem, backup_code.data(), backup_code.size());
 736          return {s, {0}};
 737      };
 738  
 739  #if defined(__arm__) || defined(__aarch64__)
 740      for (int i = 0; i < 6; i++)
 741      {
 742  #if defined(__arm__)
 743          tmp_regs.uregs[i] = vargs[i];
 744  #else
 745          tmp_regs.regs[i] = vargs[i];
 746  #endif
 747      }
 748  
 749  #elif defined(__i386__)
 750      tmp_regs.ebx = vargs[0];
 751      tmp_regs.ecx = vargs[1];
 752      tmp_regs.edx = vargs[2];
 753      tmp_regs.esi = vargs[3];
 754      tmp_regs.edi = vargs[4];
 755      tmp_regs.ebp = vargs[5];
 756      tmp_regs.orig_eax = 0;
 757  
 758  #elif defined(__x86_64__)
 759      tmp_regs.rdi = vargs[0];
 760      tmp_regs.rsi = vargs[1];
 761      tmp_regs.rdx = vargs[2];
 762      tmp_regs.r10 = vargs[3];
 763      tmp_regs.r8 = vargs[4];
 764      tmp_regs.r9 = vargs[5];
 765      tmp_regs.orig_rax = 0;
 766  
 767  #endif
 768  
 769      tmp_regs.KT_REG_SYSNR = sysnr;
 770  
 771      if (!pokeMem(target_pc_mem, syscall_code.data(), syscall_code.size()))
 772      {
 773          KITTY_LOGE("callSyscall(%d): Failed to write syscall code into PC(%p) memory.",
 774                     int(sysnr),
 775                     (void *)target_pc_mem);
 776          return {KT_RP_CALL_MEM_FAILED, {0}};
 777      }
 778  
 779      // Set new registers
 780      if (!setRegs(&tmp_regs))
 781          return failure_return(KT_RP_CALL_REGS_FAILED);
 782  
 783      // Single step to execute syscall
 784      if (!step())
 785          return failure_return(KT_RP_CALL_STEP_FAILED);
 786  
 787      // Wait for step
 788      do
 789      {
 790          errno = 0;
 791          int status = 0;
 792          pid_t wp = wait(&status, WUNTRACED);
 793          if (wp != _pid)
 794          {
 795              KITTY_LOGE("callSyscall(%d): waitpid returned %d. \"%s\".", int(sysnr), wp, strerror(errno));
 796              return failure_return(KT_RP_CALL_WAIT_FAILED);
 797          }
 798  
 799          if (WIFEXITED(status))
 800          {
 801              _attached = false;
 802              KITTY_LOGE("callSyscall(%d): Target process exited (%d).", int(sysnr), WEXITSTATUS(status));
 803              return {KT_RP_CALL_EXITED, {0}};
 804          }
 805  
 806          if (WIFSIGNALED(status))
 807          {
 808              _attached = false;
 809              KITTY_LOGE("callSyscall(%d): Target process terminated (%d).", int(sysnr), WTERMSIG(status));
 810              return {KT_RP_CALL_EXITED, {0}};
 811          }
 812  
 813          if (!WIFSTOPPED(status))
 814              continue;
 815  
 816          if (WSTOPSIG(status) == SIGCHLD || WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTSTP)
 817          {
 818              if (!cont())
 819                  return failure_return(KT_RP_CALL_CONT_FAILED);
 820  
 821              continue;
 822          }
 823  
 824          if (!getRegs(&return_regs))
 825              return failure_return(KT_RP_CALL_REGS_FAILED);
 826  
 827          if (return_regs.KT_REG_PC > tmp_regs.KT_REG_PC && return_regs.KT_REG_PC <= tmp_regs.KT_REG_PC + 16)
 828              break;
 829  
 830          KITTY_LOGE("callSyscall(%d): Process didn't stop after syscall!", int(sysnr));
 831  
 832          KITTY_LOGE("callSyscall(%d): PC(%p) | RET(%p).",
 833                     int(sysnr),
 834                     (void *)(return_regs.KT_REG_PC),
 835                     (void *)(return_regs.KT_REG_RET));
 836  
 837          siginfo_t si = {};
 838          getSignalInfo(&si);
 839  
 840          KITTY_LOGE("callSyscall(%d): SIG(%s) | CODE(%d) | ADDR(%p).",
 841                     int(sysnr),
 842                     strsignal(si.si_signo),
 843                     si.si_code,
 844                     (void *)(si.si_addr));
 845  
 846          auto map = KittyMemoryEx::getAddressMap(_pid, uintptr_t(si.si_addr));
 847          if (map.isValid())
 848          {
 849              KITTY_LOGE("callSyscall(%d): MAP(<base>+%p) %s",
 850                         int(sysnr),
 851                         (void *)((map.offset + uintptr_t(si.si_addr)) - map.startAddress),
 852                         map.toString().c_str());
 853          }
 854  
 855          if (!cont(WSTOPSIG(status)))
 856              return failure_return(KT_RP_CALL_CONT_FAILED);
 857  
 858          return failure_return(KT_RP_CALL_MISMATCH_STOP);
 859  
 860      } while (true);
 861  
 862      kitty_rp_call_t result = {KT_RP_CALL_SUCCESS, {static_cast<intptr_t>(return_regs.KT_REG_RET)}};
 863  
 864      if (!pokeMem(target_pc_mem, backup_code.data(), backup_code.size()))
 865      {
 866          KITTY_LOGW("callSyscall(%d): Failed to restore PC(%p) memory code!", int(sysnr), (void *)target_pc_mem);
 867      }
 868  
 869      // Restore regs
 870      if (_autoRestoreRegs)
 871          setRegs(&backup_regs);
 872  
 873      KITTY_LOGD("callSyscall(%d): returned %p.", int(sysnr), (void *)result.result.ptr);
 874      return result;
 875  }
 876  
 877  KT_BP_RESULT KittyTraceMgr::setSoftBreakpointAndWait(uintptr_t address,
 878                                                       const std::function<bool(user_regs_struct regs)> &cb,
 879                                                       int timeout_ms)
 880  {
 881      if (!_attached || _pid <= 0 || address == 0)
 882          return KT_BP_FAILED;
 883  
 884  #if defined(__arm__)
 885      bool thumb = address & 1;
 886      if (thumb)
 887          address &= ~1;
 888      else
 889          address &= ~3UL;
 890  #elif defined(__aarch64__)
 891      address &= ~3UL;
 892  #endif
 893  
 894      pid_t tid = _pid;
 895  
 896      std::vector<uint8_t> brk_code(sizeof(uintptr_t), 0);
 897      std::vector<uint8_t> bak_code(sizeof(uintptr_t), 0);
 898      int status = 0;
 899      pid_t wp = 0;
 900      user_regs_struct regs = {};
 901  
 902      // cleanup failure return
 903      auto failure_return = [&](KT_BP_RESULT res = KT_BP_FAILED) -> KT_BP_RESULT {
 904          KITTY_LOGE("setSoftBreakpointAndWait(%p): Failed.", (void *)address);
 905          pokeMem(address, bak_code.data(), bak_code.size());
 906          return res;
 907      };
 908  
 909      auto validate_trap = [this](const user_regs_struct &regs, uintptr_t trap_addr) -> bool {
 910          uintptr_t pc = regs.KT_REG_PC;
 911          uintptr_t max_range = KT_ALIGN_UP(trap_addr + sizeof(KittyTraceInsns::BRKP), sizeof(uintptr_t));
 912          if (!(pc >= trap_addr && pc <= max_range))
 913          {
 914              siginfo_t si = {};
 915              getSignalInfo(&si);
 916              return uintptr_t(si.si_addr) >= trap_addr && uintptr_t(si.si_addr) <= max_range;
 917          }
 918          return true;
 919      };
 920  
 921  again:
 922      wp = 0;
 923      status = 0;
 924      memset(&regs, 0, sizeof(regs));
 925  
 926      if (!peekMem(address, bak_code.data(), bak_code.size()))
 927      {
 928          KITTY_LOGE("setSoftBreakpointAndWait(%p): Failed to backup memory code.", (void *)address);
 929          return KT_BP_MEM_FAILED;
 930      }
 931  
 932  #if defined(__arm__)
 933      if (thumb)
 934          memcpy(brk_code.data(), KittyTraceInsns::THUMB_BRKP, sizeof(KittyTraceInsns::THUMB_BRKP));
 935      else
 936          memcpy(brk_code.data(), KittyTraceInsns::BRKP, sizeof(KittyTraceInsns::BRKP));
 937  #else
 938      memcpy(brk_code.data(), KittyTraceInsns::BRKP, sizeof(KittyTraceInsns::BRKP));
 939  #endif
 940  
 941      if (!pokeMem(address, brk_code.data(), brk_code.size()))
 942      {
 943          KITTY_LOGE("setSoftBreakpointAndWait(%p): Failed to write brk code into memory.", (void *)address);
 944          return KT_BP_MEM_FAILED;
 945      }
 946  
 947      if (!cont())
 948          return failure_return(KT_BP_CONT_FAILED);
 949  
 950      do
 951      {
 952          errno = 0;
 953          status = 0;
 954          wp = wait(&status, WUNTRACED, timeout_ms);
 955          if (wp != tid)
 956          {
 957              if (wp == 0)
 958              {
 959                  stop();
 960                  KITTY_LOGE("setSoftBreakpointAndWait(%p): timedout!", (void *)address);
 961                  pokeMem(address, bak_code.data(), bak_code.size());
 962                  return KT_BP_TIMEOUT;
 963              }
 964  
 965              KITTY_LOGE("setSoftBreakpointAndWait(%p): waitpid returned %d. \"%s\".",
 966                         (void *)address,
 967                         wp,
 968                         strerror(errno));
 969  
 970              return failure_return(KT_BP_WAIT_FAILED);
 971          }
 972  
 973          if (WIFEXITED(status))
 974          {
 975              _attached = false;
 976              KITTY_LOGE("setSoftBreakpointAndWait(%p): Target process exited (%d).",
 977                         (void *)address,
 978                         WEXITSTATUS(status));
 979              return KT_BP_EXITED;
 980          }
 981  
 982          if (WIFSIGNALED(status))
 983          {
 984              _attached = false;
 985              KITTY_LOGE("setSoftBreakpointAndWait(%p): Target process terminated (%d).",
 986                         (void *)address,
 987                         WTERMSIG(status));
 988              return KT_BP_EXITED;
 989          }
 990  
 991          if (!WIFSTOPPED(status))
 992              continue;
 993  
 994          if (WSTOPSIG(status) == SIGCHLD || WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTSTP)
 995          {
 996              if (!cont())
 997                  return failure_return(KT_BP_CONT_FAILED);
 998  
 999              continue;
1000          }
1001  
1002          if (!getRegs(&regs))
1003              return failure_return(KT_BP_REGS_FAILED);
1004  
1005          if (WSTOPSIG(status) == SIGTRAP)
1006          {
1007              if (validate_trap(regs, address))
1008                  break;
1009  
1010              KITTY_LOGE("setSoftBreakpointAndWait(%p): Process didn't stop at specified Hardware Breakpoint",
1011                         (void *)address);
1012          }
1013          else
1014          {
1015              KITTY_LOGE("setSoftBreakpointAndWait(%p): Target process didn't stop with SIGTRAP", (void *)address);
1016          }
1017  
1018          KITTY_LOGE("setSoftBreakpointAndWait(%p): PC(%p) | RET(%p).",
1019                     (void *)address,
1020                     (void *)(regs.KT_REG_PC),
1021                     (void *)(regs.KT_REG_RET));
1022  
1023          siginfo_t si = {};
1024          getSignalInfo(&si);
1025  
1026          KITTY_LOGE("setSoftBreakpointAndWait(%p): SIG(%s) | CODE(%d) | ADDR(%p).",
1027                     (void *)address,
1028                     strsignal(si.si_signo),
1029                     si.si_code,
1030                     (void *)(si.si_addr));
1031  
1032          auto map = KittyMemoryEx::getAddressMap(_pid, uintptr_t(si.si_addr));
1033          if (map.isValid())
1034          {
1035              KITTY_LOGE("setSoftBreakpointAndWait(%p): MAP(<base>+%p) %s",
1036                         (void *)address,
1037                         (void *)((map.offset + uintptr_t(si.si_addr)) - map.startAddress),
1038                         map.toString().c_str());
1039          }
1040  
1041          if (!cont(WSTOPSIG(status)))
1042              return failure_return(KT_BP_CONT_FAILED);
1043  
1044          return failure_return(KT_BP_MISMATCH_STOP);
1045  
1046      } while (true);
1047  
1048      if (!pokeMem(address, bak_code.data(), bak_code.size()))
1049      {
1050          KITTY_LOGE("setSoftBreakpointAndWait(%p): Failed to restore memory code!", (void *)address);
1051          return KT_BP_MEM_FAILED;
1052      }
1053  
1054  #if defined(__i386__) || defined(__x86_64__)
1055      regs.KT_REG_PC -= sizeof(KittyTraceInsns::BRKP);
1056      if (!setRegs(&regs))
1057      {
1058          KITTY_LOGE("setSoftBreakpointAndWait(%p): Failed to rewind PC!", (void *)address);
1059          return KT_BP_REGS_FAILED;
1060      }
1061  #endif
1062  
1063      KITTY_LOGD("setSoftBreakpointAndWait(%p): Success PC(%p).", (void *)address, (void *)regs.KT_REG_PC);
1064  
1065      if (cb && !cb(regs))
1066      {
1067          if (!waitStep())
1068          {
1069              KITTY_LOGE("setSoftBreakpointAndWait(%p): Failed to step past breakpoint!", (void *)address);
1070              return KT_BP_STEP_FAILED;
1071          }
1072  
1073          goto again;
1074      }
1075  
1076      return KT_BP_SUCCESS;
1077  }
1078  
1079  KT_BP_RESULT KittyTraceMgr::setHardBreakpointAndWait(uintptr_t address,
1080                                                       KT_HW_BP_TYPE type,
1081                                                       KT_HW_BP_SIZE size,
1082                                                       int slot,
1083                                                       const std::function<bool(user_regs_struct regs)> &cb,
1084                                                       int timeout_ms)
1085  {
1086      if (!_attached || _pid <= 0 || address == 0)
1087          return KT_BP_FAILED;
1088  
1089      pid_t tid = _pid;
1090      int status = 0;
1091      pid_t wp = 0;
1092      user_regs_struct regs = {};
1093  
1094      auto failure_return = [&](KT_BP_RESULT res = KT_BP_FAILED) -> KT_BP_RESULT {
1095          KITTY_LOGE("setHardBreakpointAndWait(%p): Failed.", (void *)address);
1096          clearHwBreakpoint(type, slot);
1097          return res;
1098      };
1099  
1100      auto validate_trap = [this, size](const user_regs_struct &regs, uintptr_t trap_addr) -> bool {
1101          trap_addr &= ~1;
1102          trap_addr &= ~(sizeof(uintptr_t) - 1);
1103          uintptr_t pc = regs.KT_REG_PC;
1104          uintptr_t max_range = KT_ALIGN_UP(trap_addr + std::max(int(size), int(sizeof(KittyTraceInsns::BRKP))),
1105                                            int(sizeof(uintptr_t)));
1106          if (!(pc >= trap_addr && pc <= max_range))
1107          {
1108              siginfo_t si = {};
1109              getSignalInfo(&si);
1110              return uintptr_t(si.si_addr) >= trap_addr && uintptr_t(si.si_addr) <= max_range;
1111          }
1112          return true;
1113      };
1114  
1115  again:
1116  
1117      if (type == KT_HW_BP_EXECUTE)
1118      {
1119  #if defined(__arm__)
1120          if (!setHwBreakpoint(address, type, (address & 1) != 0 ? KT_HW_BP_SIZE_2 : KT_HW_BP_SIZE_4, slot))
1121  #elif defined(__aarch64__)
1122          if (!setHwBreakpoint(address, type, KT_HW_BP_SIZE_4, slot))
1123  #else
1124          if (!setHwBreakpoint(address, type, KT_HW_BP_SIZE_1, slot))
1125  #endif
1126          {
1127              KITTY_LOGE("setHardBreakpointAndWait(%p): Failed to set breakpoint. \"%s\"",
1128                         (void *)address,
1129                         strerror(errno));
1130              return KT_BP_FAILED;
1131          }
1132      }
1133      else
1134      {
1135          if (!setHwBreakpoint(address, type, size, slot))
1136          {
1137              KITTY_LOGE("setHardBreakpointAndWait(%p): Failed to set watchpoint. \"%s\"",
1138                         (void *)address,
1139                         strerror(errno));
1140              return KT_BP_FAILED;
1141          }
1142      }
1143  
1144      if (!cont())
1145          return failure_return(KT_BP_CONT_FAILED);
1146  
1147      do
1148      {
1149          errno = 0;
1150          status = 0;
1151          wp = wait(&status, WUNTRACED, timeout_ms);
1152          if (wp != tid)
1153          {
1154              if (wp == 0)
1155              {
1156                  stop();
1157                  KITTY_LOGE("setHardBreakpointAndWait(%p): timedout!", (void *)address);
1158                  clearHwBreakpoint(type, slot);
1159                  return KT_BP_TIMEOUT;
1160              }
1161  
1162              KITTY_LOGE("setHardBreakpointAndWait(%p): waitpid returned %d. \"%s\".",
1163                         (void *)address,
1164                         wp,
1165                         strerror(errno));
1166  
1167              return failure_return(KT_BP_WAIT_FAILED);
1168          }
1169  
1170          if (WIFEXITED(status))
1171          {
1172              _attached = false;
1173              KITTY_LOGE("setHardBreakpointAndWait(%p): Target process exited (%d).",
1174                         (void *)address,
1175                         WEXITSTATUS(status));
1176              return KT_BP_EXITED;
1177          }
1178  
1179          if (WIFSIGNALED(status))
1180          {
1181              _attached = false;
1182              KITTY_LOGE("setHardBreakpointAndWait(%p): Target process terminated (%d).",
1183                         (void *)address,
1184                         WTERMSIG(status));
1185              return KT_BP_EXITED;
1186          }
1187  
1188          if (!WIFSTOPPED(status))
1189              continue;
1190  
1191          if (WSTOPSIG(status) == SIGCHLD || WSTOPSIG(status) == SIGSTOP || WSTOPSIG(status) == SIGTSTP)
1192          {
1193              if (!cont())
1194                  return failure_return(KT_BP_CONT_FAILED);
1195  
1196              continue;
1197          }
1198  
1199          if (!getRegs(&regs))
1200              return failure_return(KT_BP_REGS_FAILED);
1201  
1202          if (WSTOPSIG(status) == SIGTRAP)
1203          {
1204              /*siginfo_t si{};
1205              getSignalInfo(&si);
1206              if (si.si_code == 4)
1207                  break;*/
1208  
1209              if (validate_trap(regs, address))
1210                  break;
1211  
1212              KITTY_LOGE("setHardBreakpointAndWait(%p): Process didn't stop at specified Hardware Breakpoint",
1213                         (void *)address);
1214          }
1215          else
1216          {
1217              KITTY_LOGE("setHardBreakpointAndWait(%p): Target process didn't stop with SIGTRAP", (void *)address);
1218          }
1219  
1220          KITTY_LOGE("setHardBreakpointAndWait(%p): PC(%p) | RET(%p).",
1221                     (void *)address,
1222                     (void *)(regs.KT_REG_PC),
1223                     (void *)(regs.KT_REG_RET));
1224  
1225          siginfo_t si = {};
1226          getSignalInfo(&si);
1227  
1228          KITTY_LOGE("setHardBreakpointAndWait(%p): SIG(%s) | CODE(%d) | ADDR(%p).",
1229                     (void *)address,
1230                     strsignal(si.si_signo),
1231                     si.si_code,
1232                     (void *)(si.si_addr));
1233  
1234          auto map = KittyMemoryEx::getAddressMap(_pid, uintptr_t(si.si_addr));
1235          if (map.isValid())
1236          {
1237              KITTY_LOGE("setHardBreakpointAndWait(%p): MAP(<base>+%p) %s",
1238                         (void *)address,
1239                         (void *)((map.offset + uintptr_t(si.si_addr)) - map.startAddress),
1240                         map.toString().c_str());
1241          }
1242  
1243          if (!cont(WSTOPSIG(status)))
1244              return failure_return(KT_BP_CONT_FAILED);
1245  
1246          // return failure_return(KT_BP_MISMATCH_STOP);
1247  
1248      } while (true);
1249  
1250      KITTY_LOGD("setHardBreakpointAndWait(%p): Success PC(%p).", (void *)address, (void *)regs.KT_REG_PC);
1251  
1252      clearHwBreakpoint(type, slot);
1253  
1254      if (cb && !cb(regs))
1255      {
1256          if (!waitStep())
1257          {
1258              KITTY_LOGE("setHardBreakpointAndWait(%p): Failed to step past breakpoint!", (void *)address);
1259              return KT_BP_STEP_FAILED;
1260          }
1261  
1262          goto again;
1263      }
1264  
1265      return KT_BP_SUCCESS;
1266  }
1267  
1268  #if defined(__arm__) && !defined(PTRACE_GETHBPREGS)
1269  #define PTRACE_GETHBPREGS 29
1270  #define PTRACE_SETHBPREGS 30
1271  #endif
1272  
1273  bool KittyTraceMgr::setHwBreakpoint(uintptr_t address, KT_HW_BP_TYPE type, KT_HW_BP_SIZE size, int slot)
1274  {
1275      errno = 0;
1276      pid_t tid = _pid;
1277  
1278  #if defined(__arm__) || defined(__aarch64__)
1279      address &= ~1;
1280      size_t alignment_mask = type == KT_HW_BP_EXECUTE ? (sizeof(uint32_t) - 1) : (sizeof(uintptr_t) - 1);
1281  
1282      uintptr_t offset = address & alignment_mask;
1283      address &= ~alignment_mask;
1284  
1285      // Build the Byte Address Select (BAS) mask based on size and offset
1286      uint32_t bas = ((1U << size) - 1) << offset;
1287  
1288      uint32_t type_bits = 0;
1289      switch (type)
1290      {
1291      case KT_HW_BP_EXECUTE:
1292          type_bits = 0;
1293          break;
1294      case KT_HW_BP_READ:
1295          type_bits = 1;
1296          break;
1297      case KT_HW_BP_WRITE:
1298          type_bits = 2;
1299          break;
1300      case KT_HW_BP_ACCESS:
1301          type_bits = 3;
1302          break;
1303      }
1304  
1305      uint32_t privilege = (1 << 1); // User mode only
1306      uint32_t enabled = 1;          // Bit 0: Enable
1307      uint32_t ctrl = enabled | privilege | (type_bits << 3) | (bas << 5);
1308  
1309  #if defined(__arm__)
1310      long vr_idx = type == KT_HW_BP_EXECUTE ? (((slot * 2) + 1)) : (-(slot * 2) + 1);
1311      long cr_idx = type == KT_HW_BP_EXECUTE ? (((slot * 2) + 2)) : (-(slot * 2) + 2);
1312      return ptrace(PTRACE_SETHBPREGS, tid, vr_idx, &address) != -1L &&
1313             ptrace(PTRACE_SETHBPREGS, tid, cr_idx, &ctrl) != -1L;
1314  
1315  #elif defined(__aarch64__)
1316      struct user_hwdebug_state state{};
1317  
1318      struct iovec iov;
1319      iov.iov_base = &state;
1320      iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + (sizeof(state.dbg_regs[0]) * (slot + 1));
1321  
1322      int regset = (type == KT_HW_BP_EXECUTE) ? NT_ARM_HW_BREAK : NT_ARM_HW_WATCH;
1323  
1324      // Read current state
1325      if (ptrace(PTRACE_GETREGSET, tid, regset, &iov) == -1)
1326          return false;
1327  
1328      // Overwrite slot
1329      state.dbg_regs[slot].addr = address;
1330      state.dbg_regs[slot].ctrl = ctrl;
1331  
1332      // Update state
1333      return ptrace(PTRACE_SETREGSET, tid, regset, &iov) != -1L;
1334  #endif
1335  
1336  #elif defined(__x86_64__) || defined(__i386__)
1337      // Set Address in DR0-DR3
1338      if (ptrace(PTRACE_POKEUSER, tid, offsetof(struct user, u_debugreg) + slot * sizeof(((struct user*)0)->u_debugreg[0]), address) == -1L)
1339          return false;
1340  
1341      // Retrieve current DR7 to avoid overwriting other slots
1342      errno = 0;
1343      unsigned long dr7 = ptrace(PTRACE_PEEKUSER, tid, offsetof(struct user, u_debugreg[7]), 0);
1344      if (dr7 == ((unsigned long)-1) && errno != 0)
1345          return false;
1346  
1347      // Configure type bits
1348      unsigned long type_bits = 0;
1349      switch (type)
1350      {
1351      case KT_HW_BP_EXECUTE:
1352          type_bits = 0;
1353          break;
1354      case KT_HW_BP_WRITE:
1355          type_bits = 1;
1356          break;
1357      case KT_HW_BP_READ:
1358      case KT_HW_BP_ACCESS:
1359          type_bits = 3;
1360          break; // x86 doesn't support 'Read-Only'
1361      }
1362  
1363      // Configure length
1364      unsigned long len_bits = 0;
1365      if (type != KT_HW_BP_EXECUTE)
1366      {
1367          if (address % size != 0)
1368              return false;
1369  
1370          switch (size)
1371          {
1372          case 1:
1373              len_bits = 0;
1374              break;
1375          case 2:
1376              len_bits = 1;
1377              break;
1378          case 4:
1379              len_bits = 3;
1380              break;
1381          case 8:
1382              len_bits = 2;
1383              break; // x64 only
1384          default:
1385              return false;
1386          }
1387      }
1388  
1389      int l_bit = (slot * 2);        // Local Enable bits are 0, 2, 4, 6
1390      int rw_bit = 16 + (slot * 4);  // RW bits are 16, 20, 24, 28
1391      int len_bit = 18 + (slot * 4); // LEN bits are 18, 22, 26, 30
1392  
1393      dr7 &= ~((3UL << rw_bit) | (3UL << len_bit) | (1UL << l_bit));         // Clear slot
1394      dr7 |= (type_bits << rw_bit) | (len_bits << len_bit) | (1UL << l_bit); // Set slot
1395  
1396      // Update DR7
1397      return ptrace(PTRACE_POKEUSER, tid, offsetof(struct user, u_debugreg[7]), dr7) != -1L;
1398  #endif
1399  }
1400  
1401  bool KittyTraceMgr::clearHwBreakpoint(KT_HW_BP_TYPE type, int slot)
1402  {
1403      pid_t tid = _pid;
1404      errno = 0;
1405      ((void)type);
1406  
1407  #if defined(__arm__)
1408      uintptr_t address = 0;
1409      uint32_t ctrl = 0;
1410      long vr_idx = type == KT_HW_BP_EXECUTE ? (((slot * 2) + 1)) : (-(slot * 2) + 1);
1411      long cr_idx = type == KT_HW_BP_EXECUTE ? (((slot * 2) + 2)) : (-(slot * 2) + 2);
1412      return ptrace(PTRACE_SETHBPREGS, tid, vr_idx, &address) != -1L &&
1413             ptrace(PTRACE_SETHBPREGS, tid, cr_idx, &ctrl) != -1L;
1414  
1415  #elif defined(__aarch64__)
1416      struct user_hwdebug_state state{};
1417  
1418      struct iovec iov;
1419      iov.iov_base = &state;
1420      iov.iov_len = offsetof(struct user_hwdebug_state, dbg_regs) + (sizeof(state.dbg_regs[0]) * (slot + 1));
1421  
1422      int regset = (type == KT_HW_BP_EXECUTE) ? NT_ARM_HW_BREAK : NT_ARM_HW_WATCH;
1423  
1424      // Read current state
1425      if (ptrace(PTRACE_GETREGSET, tid, regset, &iov) == -1)
1426          return false;
1427  
1428      // Overwrite slot
1429      state.dbg_regs[slot].addr = 0;
1430      state.dbg_regs[slot].ctrl = 0;
1431  
1432      // Update state
1433      return ptrace(PTRACE_SETREGSET, tid, regset, &iov) != -1L;
1434  
1435  #elif defined(__x86_64__) || defined(__i386__)
1436      errno = 0;
1437      unsigned long dr7 = ptrace(PTRACE_PEEKUSER, tid, offsetof(struct user, u_debugreg[7]), 0);
1438      if (dr7 == ((unsigned long)-1) && errno != 0)
1439          return false;
1440  
1441      // Clear L/G enable bits (bits 0-7)
1442      dr7 &= ~(3UL << (slot * 2));
1443  
1444      // Clear RW/LEN bits (bits 16-31)
1445      // Each slot has 4 bits of config starting at bit 16
1446      dr7 &= ~(0xFUL << (16 + (slot * 4)));
1447  
1448      if (ptrace(PTRACE_POKEUSER, tid, offsetof(struct user, u_debugreg[7]), dr7) == -1L)
1449          return false;
1450  
1451      if (ptrace(PTRACE_POKEUSER, tid, offsetof(struct user, u_debugreg) + slot * sizeof(((struct user*)0)->u_debugreg[0]), 0) == -1L)
1452          return false;
1453  
1454      ptrace(PTRACE_POKEUSER, tid, offsetof(struct user, u_debugreg[6]), 0);
1455  
1456      return true;
1457  #endif
1458  }