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 ®s, 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 ®s, 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(®s, 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(®s)) 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(®s)) 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 ®s, 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(®s)) 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 }