Last active
August 26, 2025 15:27
-
-
Save jimmy947788/7e91f1ddb0f853be17012e8769e5883d to your computer and use it in GitHub Desktop.
该脚本用于在 IDA 中查找 ARM64 架构的 SVC 指令,并添加注释显示系统调用号对应的系统调用名称。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # IDA 9.1 / Python 3.x | |
| # 更快的 ARM64 SVC 標註器:單次遍歷 + 狀態機追蹤 W8/X8 的 syscall 立即數 | |
| # Author: Jimmy(優化版 by ChatGPT) | |
| # Note: 依賴你提供的 syscall_mapping(已內嵌),支援 MOV/MOVZ/ORR(wzr/xzr)三種常見立即數寫入型式。 | |
| import idaapi | |
| import idautils | |
| import idc | |
| import ida_funcs | |
| import ida_segment | |
| import ida_kernwin | |
| VERBOSE = False # 需要詳細日誌時設 True | |
| ONLY_SVC_ZERO = True # 只對 SVC #0 標註(常見 Linux aarch64 約定) | |
| # === 系統呼叫映射(來源:你的原始腳本) =========================== | |
| syscall_mapping = { | |
| 0: "__NR_io_setup", 1: "__NR_io_destroy", 2: "__NR_io_submit", 3: "__NR_io_cancel", | |
| 4: "__NR_io_getevents", 5: "__NR_setxattr", 6: "__NR_lsetxattr", 7: "__NR_fsetxattr", | |
| 8: "__NR_getxattr", 9: "__NR_lgetxattr", 10: "__NR_fgetxattr", 11: "__NR_listxattr", | |
| 12: "__NR_llistxattr", 13: "__NR_flistxattr", 14: "__NR_removexattr", 15: "__NR_lremovexattr", | |
| 16: "__NR_fremovexattr", 17: "__NR_getcwd", 18: "__NR_lookup_dcookie", 19: "__NR_eventfd2", | |
| 20: "__NR_epoll_create1", 21: "__NR_epoll_ctl", 22: "__NR_epoll_pwait", 23: "__NR_dup", | |
| 24: "__NR_dup3", 25: "__NR_fcntl", 26: "__NR_inotify_init1", 27: "__NR_inotify_add_watch", | |
| 28: "__NR_inotify_rm_watch", 29: "__NR_ioctl", 30: "__NR_ioprio_set", 31: "__NR_ioprio_get", | |
| 32: "__NR_flock", 33: "__NR_mknodat", 34: "__NR_mkdirat", 35: "__NR_unlinkat", | |
| 36: "__NR_symlinkat", 37: "__NR_linkat", 38: "__NR_renameat", 39: "__NR_umount2", | |
| 40: "__NR_mount", 41: "__NR_pivot_root", 42: "__NR_nfsservctl", 43: "__NR_statfs", | |
| 44: "__NR_fstatfs", 45: "__NR_truncate", 46: "__NR_ftruncate", 47: "__NR_fallocate", | |
| 48: "__NR_faccessat", 49: "__NR_chdir", 50: "__NR_fchdir", 51: "__NR_chroot", | |
| 52: "__NR_fchmod", 53: "__NR_fchmodat", 54: "__NR_fchownat", 55: "__NR_fchown", | |
| 56: "__NR_openat", 57: "__NR_close", 58: "__NR_vhangup", 59: "__NR_pipe2", | |
| 60: "__NR_quotactl", 61: "__NR_getdents64", 62: "__NR_lseek", 63: "__NR_read", | |
| 64: "__NR_write", 65: "__NR_readv", 66: "__NR_writev", 67: "__NR_pread64", | |
| 68: "__NR_pwrite64", 69: "__NR_preadv", 70: "__NR_pwritev", 71: "__NR_sendfile", | |
| 72: "__NR_pselect6", 73: "__NR_ppoll", 74: "__NR_signalfd4", 75: "__NR_vmsplice", | |
| 76: "__NR_splice", 77: "__NR_tee", 78: "__NR_readlinkat", 79: "__NR_fstatat", | |
| 80: "__NR_fstat", 81: "__NR_sync", 82: "__NR_fsync", 83: "__NR_fdatasync", | |
| 84: "__NR_sync_file_range", 85: "__NR_timerfd_create", 86: "__NR_timerfd_settime", | |
| 87: "__NR_timerfd_gettime", 88: "__NR_utimensat", 89: "__NR_acct", 90: "__NR_capget", | |
| 91: "__NR_capset", 92: "__NR_personality", 93: "__NR_exit", 94: "__NR_exit_group", | |
| 95: "__NR_waitid", 96: "__NR_set_tid_address", 97: "__NR_unshare", 98: "__NR_futex", | |
| 99: "__NR_set_robust_list", 100: "__NR_get_robust_list", 101: "__NR_nanosleep", | |
| 102: "__NR_getitimer", 103: "__NR_setitimer", 104: "__NR_kexec_load", | |
| 105: "__NR_init_module", 106: "__NR_delete_module", 107: "__NR_timer_create", | |
| 108: "__NR_timer_gettime", 109: "__NR_timer_getoverrun", 110: "__NR_timer_settime", | |
| 111: "__NR_timer_delete", 112: "__NR_clock_settime", 113: "__NR_clock_gettime", | |
| 114: "__NR_clock_getres", 115: "__NR_clock_nanosleep", 116: "__NR_syslog", | |
| 117: "__NR_ptrace", 118: "__NR_sched_setparam", 119: "__NR_sched_setscheduler", | |
| 120: "__NR_sched_getscheduler", 121: "__NR_sched_getparam", 122: "__NR_sched_setaffinity", | |
| 123: "__NR_sched_getaffinity", 124: "__NR_sched_yield", | |
| 125: "__NR_sched_get_priority_max", 126: "__NR_sched_get_priority_min", | |
| 127: "__NR_sched_rr_get_interval", 128: "__NR_restart_syscall", 129: "__NR_kill", | |
| 130: "__NR_tkill", 131: "__NR_tgkill", 132: "__NR_sigaltstack", 133: "__NR_rt_sigsuspend", | |
| 134: "__NR_rt_sigaction", 135: "__NR_rt_sigprocmask", 136: "__NR_rt_sigpending", | |
| 137: "__NR_rt_sigtimedwait", 138: "__NR_rt_sigqueueinfo", 139: "__NR_rt_sigreturn", | |
| 140: "__NR_setpriority", 141: "__NR_getpriority", 142: "__NR_reboot", | |
| 143: "__NR_setregid", 144: "__NR_setgid", 145: "__NR_setreuid", 146: "__NR_setuid", | |
| 147: "__NR_setresuid", 148: "__NR_getresuid", 149: "__NR_setresgid", 150: "__NR_getresgid", | |
| 151: "__NR_setfsuid", 152: "__NR_setfsgid", 153: "__NR_times", 154: "__NR_setpgid", | |
| 155: "__NR_getpgid", 156: "__NR_getsid", 157: "__NR_setsid", 158: "__NR_getgroups", | |
| 159: "__NR_setgroups", 160: "__NR_uname", 161: "__NR_sethostname", | |
| 162: "__NR_setdomainname", 163: "__NR_getrlimit", 164: "__NR_setrlimit", | |
| 165: "__NR_getrusage", 166: "__NR_umask", 167: "__NR_prctl", 168: "__NR_getcpu", | |
| 169: "__NR_gettimeofday", 170: "__NR_settimeofday", 171: "__NR_adjtimex", | |
| 172: "__NR_getpid", 173: "__NR_getppid", 174: "__NR_getuid", 175: "__NR_geteuid", | |
| 176: "__NR_getgid", 177: "__NR_getegid", 178: "__NR_gettid", 179: "__NR_sysinfo", | |
| 180: "__NR_mq_open", 181: "__NR_mq_unlink", 182: "__NR_mq_timedsend", 183: "__NR_mq_timedreceive", | |
| 184: "__NR_mq_notify", 185: "__NR_mq_getsetattr", 186: "__NR_msgget", 187: "__NR_msgctl", | |
| 188: "__NR_msgrcv", 189: "__NR_msgsnd", 190: "__NR_semget", 191: "__NR_semctl", | |
| 192: "__NR_semtimedop", 193: "__NR_semop", 194: "__NR_shmget", 195: "__NR_shmctl", | |
| 196: "__NR_shmat", 197: "__NR_shmdt", 198: "__NR_socket", 199: "__NR_socketpair", | |
| 200: "__NR_bind", 201: "__NR_listen", 202: "__NR_accept", 203: "__NR_connect", | |
| 204: "__NR_getsockname", 205: "__NR_getpeername", 206: "__NR_sendto", 207: "__NR_recvfrom", | |
| 208: "__NR_setsockopt", 209: "__NR_getsockopt", 210: "__NR_shutdown", 211: "__NR_sendmsg", | |
| 212: "__NR_recvmsg", 213: "__NR_readahead", 214: "__NR_brk", 215: "__NR_munmap", | |
| 216: "__NR_mremap", 217: "__NR_add_key", 218: "__NR_rest_key", 219: "__NR_keyctl", | |
| 220: "__NR_clone", 221: "__NR_execve", 222: "__NR_mmap", 223: "__NR_fadvise64", | |
| 224: "__NR_swapon", 225: "__NR_swapoff", 226: "__NR_mprotect", 227: "__NR_msync", | |
| 228: "__NR_mlock", 229: "__NR_munlock", 230: "__NR_mlockall", 231: "__NR_munlockall", | |
| 232: "__NR_mincore", 233: "__NR_madvise", 234: "__NR_remap_file_pages", 235: "__NR_mbind", | |
| 236: "__NR_get_mempolicy", 237: "__NR_set_mempolicy", 238: "__NR_migrate_pages", | |
| 239: "__NR_move_pages", 240: "__NR_rt_tgsigqueueinfo", 241: "__NR_perf_event_open", | |
| 242: "__NR_accept4", 243: "__NR_recvmmsg", 244: "__NR_or1k_atomic", 260: "__NR_wait4", | |
| 261: "__NR_prlimit64", 262: "__NR_fanotify_init", 263: "__NR_fanotify_mark", | |
| 264: "__NR_name_to_handle_at", 265: "__NR_open_by_handle_at", 266: "__NR_clock_adjtime", | |
| 267: "__NR_syncfs", 268: "__NR_setns", 269: "__NR_sendmmsg", 270: "__NR_process_vm_readv", | |
| 271: "__NR_process_vm_writev", 272: "__NR_kcmp", 273: "__NR_finit_module", | |
| 274: "__NR_sched_setattr", 275: "__NR_sched_getattr", 276: "__NR_renameat2", | |
| 277: "__NR_seccomp", 278: "__NR_getrandom", 279: "__NR_memfd_create", 280: "__NR_bpf", | |
| 281: "__NR_execveat", 282: "__NR_userfaultfd", 283: "__NR_membarrier", 284: "__NR_mlock2", | |
| 285: "__NR_copy_file_range", 286: "__NR_preadv2", 287: "__NR_pwritev2" | |
| } | |
| # =============================================================== | |
| def is_code_seg(seg): | |
| return seg and (seg.perm & ida_segment.SEGPERM_EXEC) != 0 | |
| def write_to_x8_with_imm(ea): | |
| """ | |
| 偵測是否為將立即數寫入 W8/X8 的常見型式: | |
| 1) MOV {W8|X8}, #imm | |
| 2) MOVZ {W8|X8}, #imm (忽略帶 lsl 的搬運) | |
| 3) ORR {W8|X8}, {WZR|XZR}, #imm | |
| 回傳 (True, imm) 或 (False, None) | |
| 若偵測到對 W8/X8 的寫入但非立即數,回傳 ('clobber', None) 表示應重置狀態。 | |
| """ | |
| mnem = idc.print_insn_mnem(ea) | |
| dst = idc.print_operand(ea, 0).upper() | |
| if dst not in ("W8", "X8"): | |
| return (False, None) | |
| # MOV dst, #imm | |
| if mnem == "MOV": | |
| if idc.get_operand_type(ea, 1) == idc.o_imm: | |
| return (True, idc.get_operand_value(ea, 1)) | |
| else: | |
| return ("clobber", None) | |
| # MOVZ dst, #imm (若帶 lsl,通常是拼 32/64 位,這裡保守略過) | |
| if mnem == "MOVZ": | |
| if idc.get_operand_type(ea, 1) == idc.o_imm: | |
| # 粗略判斷是否有 lsl(避免錯讀 MOVZ+MOVK 序列中的高段) | |
| asm = idc.generate_disasm_line(ea, 0) or "" | |
| if "lsl" in asm.lower(): | |
| return ("clobber", None) # 可能是高 16 位,保守視為非立即數可用 | |
| return (True, idc.get_operand_value(ea, 1)) | |
| else: | |
| return ("clobber", None) | |
| # ORR dst, {WZR|XZR}, #imm => 等效 mov dst, #imm | |
| if mnem == "ORR": | |
| op1 = idc.print_operand(ea, 1).upper() | |
| if op1 in ("WZR", "XZR") and idc.get_operand_type(ea, 2) == idc.o_imm: | |
| return (True, idc.get_operand_value(ea, 2)) | |
| else: | |
| return ("clobber", None) | |
| # 其它對 W8/X8 的寫入(ADD, MOVK, MOVN, LDR, BL, etc.)視為污損 | |
| if dst in ("W8", "X8"): | |
| return ("clobber", None) | |
| return (False, None) | |
| def is_svc(ea): | |
| if idc.print_insn_mnem(ea) != "SVC": | |
| return False | |
| if ONLY_SVC_ZERO: | |
| try: | |
| return idc.get_operand_type(ea, 0) == idc.o_imm and idc.get_operand_value(ea, 0) == 0 | |
| except Exception: | |
| return False | |
| return True | |
| def annotate(ea, sysno): | |
| name = syscall_mapping.get(sysno, f"Unknown syscall {sysno}") | |
| comment = f"System call: {name}" | |
| # 1=repeatable comment,方便在多處顯示 | |
| idc.set_cmt(ea, comment, 1) | |
| if VERBOSE: | |
| print(f"[+] 注釋 {hex(ea)} -> {comment}") | |
| def main(): | |
| ida_kernwin.show_wait_box("auto:ARM64 SVC 標註中…") | |
| total_funcs = 0 | |
| total_hits = 0 | |
| for f_ea in idautils.Functions(): | |
| total_funcs += 1 | |
| # 只處理屬於可執行段的函式 | |
| seg = ida_segment.getseg(f_ea) | |
| if not is_code_seg(seg): | |
| continue | |
| f_end = idc.get_func_attr(f_ea, idc.FUNCATTR_END) | |
| current_sysno = None | |
| if VERBOSE: | |
| print(f"== 處理函式: {idc.get_func_name(f_ea)} ({hex(f_ea)}~{hex(f_end)})") | |
| for ea in idautils.FuncItems(f_ea): | |
| # 快速過濾:非代碼指令直接跳過 | |
| if not idc.is_code(idc.get_full_flags(ea)): | |
| continue | |
| # 嘗試匹配對 W8/X8 的立即數寫入 | |
| flag, imm = write_to_x8_with_imm(ea) | |
| if flag is True: | |
| current_sysno = imm | |
| if VERBOSE: | |
| print(f" 捕獲 syscall #{imm} at {hex(ea)}") | |
| continue | |
| elif flag == "clobber": | |
| # 任一非立即數寫入視為污染,重置 | |
| current_sysno = None | |
| continue | |
| # 匹配 SVC 並標註 | |
| if is_svc(ea) and current_sysno is not None: | |
| annotate(ea, current_sysno) | |
| total_hits += 1 | |
| # 標註完可選擇重置(避免跨基本塊污染);這裡保守重置 | |
| current_sysno = None | |
| ida_kernwin.hide_wait_box() | |
| ida_kernwin.info(f"完成:掃描 {total_funcs} 個函式,標註 {total_hits} 處 SVC。") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment