-
-
[原创]ida 笔记
-
发表于: 3天前 477
-
前言
记录了我遇到的/遇到过的一些操作和指令, 难免有不全或者错的地方, 我保证会积极改正(狗头保命)
快捷键
功能 | 按键 |
---|---|
(伪代码窗口)重命名 | n |
(伪代码窗口)改变类型 | y |
(伪代码窗口)交叉引用 | x |
(伪代码窗口)移除返回值 | v |
(反汇编窗口)地址跳转 | g |
(反汇编窗口)按数据识别、调整大小 | d |
(反汇编窗口)将地址标记为代码并反汇编 | c |
(反汇编窗口)识别成字符串 | a |
(反汇编窗口)取消 ida 对当前地址的解释 | u |
(反汇编窗口)创建函数 | p |
(反汇编窗口)注释 | ; |
反汇编 | f5/tab |
图形/汇编 | space |
十进制与十六进制互相转换 | h |
常见内置类型
_BYTE、_WORD(16位)、_DWORD(32位)和 _QWORD(64位)
常见函数
- __stack_chk_fail: GCC 栈溢出保护机制(Stack Smashing Protector, SSP)的关键函数, 在检测到栈溢出时触发程序终止。其核心原理是通过 Canary(栈金丝雀) 值验证栈的完整性
技巧(只进行了罗列, 如何操作需自行查询)
- 自定义结构体
- 动态调试
idapython
常用 api
用于静态脚本
idaapi
- func = idaapi.get_func(addr): 查询地址 addr 是否位于某个已识别函数的边界内, 并返回该函数的信息
- func.start_ea: 函数起始地址(包含)
- func.end_ea: 函数结束地址(函数最后一条指令的下一条地址)
- blocks = idaapi.FlowChart(func): 生成函数的控制流图, 将函数分解为多个基本块, 并建立块间的跳转关系, 参数 func 是 idaapi.func_t 对象, 通过 idaapi.get_func(ea) 获取, 返回值 blocks 是函数内所有基本块(Basic Block) 的集合, 每个基本块 block 具有一下属性:
- block.start_ea:块起始地址(包含)
- block.end_ea:块结束地址(块最后一条指令的下一条指令地址)
- block.preds():前驱块列表
- block.succs():后继块列表
- start_ea = idaapi.find_text(ustr=text, x=0, y=0, sflag=idaapi.SEARCH_DOWN, start_ea=start_ea): 此函数在指定地址范围内搜索文本字符串 text, 返回匹配项的起始地址(若未找到则返回 idc.BADADDR)各参数作用如下:
- ustr: 待搜索的文本字符串, 搜索区分大小写, 不支持通配符或正则表达式。
- x 和 y(通常设为 0): y 是行号偏移(0 表示从当前行开始), x 是行内字符偏移(0 表示从行首开始)
- sflag: 搜索方向控制
- start_ea: 指定搜索的起始地址, 函数执行后会被更新为下一个待搜索地址(便于循环迭代)。若搜索失败则更新为 idc.BADADDR。
- idaapi.SEARCH_DOWN: 从当前地址向高地址方向(正向)搜索, 通常与搜索函数(如 idaapi.find_binary()、idaapi.find_text())结合使用。其值为 0x01
- idaapi.SEARCH_UP: 向低地址搜索(逆向)。其值为 0x00
- idaapi.SEARCH_NEXT: 从上一次匹配位置后继续搜索, 用于遍历所有结果。其值为 0x02
- idaapi.SEARCH_NOSHOW: 禁止显示搜索进度(提升脚本性能)。其值为 0x20
idc
- idc.BADADDR: 表示无效的内存地址, 通常作为错误返回值(例如搜索失败、符号不存在)。其值为无符号整数形式的 -1(即 0xFFFFFFFFFFFFFFFF)。
- idc.prev_head(ea: int) -> int: 返回 ea 地址之前的最近一条指令的起始地址, 若 ea 是函数的第一条指令, 则返回 idaapi.BADADDR(通常为 0xFFFFFFFF)
- idc.next_head(ea: int) -> int: 返回 ea 之后下一条有效指令的起始地址, 若 ea 后无有效指令(如函数末尾), 返回 idaapi.BADADDR
- idc.GetDisasm(ea: int) -> str: 用于获取指定地址(ea)的反汇编指令文本
- idc.print_insn_mnem(ea: int) -> str: 获取指定地址处指令助记符(例如 "B", "MOV", "RET" 等)
- idc.get_operand_value(ea: int, index: int) -> int: 用于获取指令操作数值, ea 为指令起始地址, index 是操作数索引
- idc.print_operand(ea: int, index: int) -> str: 用于提取指令操作数字符串, ea 为指令起始地址, index 是操作数索引
- idc.patch_byte(addr: int, value: int): 将 addr 处的 1 个字节修改为 value, 并立即更新 IDA 数据库(但不修改原始二进制文件)
- idc.create_insn(addr: int) -> int: 尝试从 addr 地址开始解析二进制数据,将其识别为有效的汇编指令, 成功时返回指令长度, 如 4 表示 4 字节指令, 失败时返回 0(常见于数据无法解析为合法指令)
- idc.del_items(ea: int, flags: int = 0, count: int = 1) -> bool: 取消 ida 对当前地址的解释, 成功返回 True, 失败返回 False, 参数说明:
- ea:目标起始地址
- flags:控制清除行为的标志位,常用值:
- 0(默认):仅清除 ea 处的单字节属性
- idc.DELIT_EXPAND:清除从 ea 开始的连续 count 字节的属性(最常用)
- idc.DELIT_DELNAMES:同时清除关联的符号名(如标签、变量名)
- count:清除的字节数(需与 idc.DELIT_EXPAND 联用)
idautils
- idautils.Heads(start_ea: int = None, end_ea: int = None) -> generator: 用于高效遍历指令
- start_ea: 遍历起始地址(包含)。若为 None, 默认从程序最小地址(MinEA)开始
- end_ea: 遍历结束地址(不包含)。若为 None, 默认到程序最大地址(MaxEA)结束
- 返回值 generator: 逐个生成指令的起始地址, 例:
for ea in idautils.Heads(block_start_ea, block_end_ea):
ida_hexrays
- cfunc = ida_hexrays.decompile(ea: int) -> Optional[ida_hexrays.cfuncptr_t]: 用于将汇编代码反编译为高级语言伪代码(通常是 C 语言风格),并返回结构化数据供脚本分析, ea 指定待反编译函数的起始地址, 地址必须指向函数的入口点,否则可能返回不完整结果。返回值 cfunc:
- body: cinsn_t 对象,表示函数的根语句(如循环、条件分支)
- get_func(): 返回当前函数的 func_t 对象(包含地址范围、属性等)
- build_c_tree(): 构建 C 语法树(CTree)
- mba: mba_t 对象(Microcode Block Array),存储微码中间表示
- print(): 生成格式化伪代码字符串, 标准输出
- str(cfunc): python 字符串, 完整伪代码字符串
ida_bytes
- ida_bytes.create_strlit(ea: int, length: int = 0, strtype: int = 0) -> bool: 用于将二进制数据标记为字符串, 成功返回 True, 失败返回 False, 参数说明:
- ea:目标字符串的起始地址
- length:字符串长度(以字节为单位)
- strtype: 字符串类型,常用值
- ida_bytes.STRTYPE_C: C 风格 null 终止字符串(默认)
- ida_bytes.STRTYPE_LEN2/ida_bytes.STRTYPE_LEN4:带 2/4 字节长度前缀的字符串
- ida_bytes.STRTYPE_PASCAL:Pascal 风格(长度前缀 + 字符数据)。
ida_funcs
- ida_funcs.add_func(ea: int, end_ea: int, flags=0) -> bool: 用于强制创建函数定义, 成功返回 True, 失败返回 False, 参数说明:
- ea: 函数起始地址
- end_ea: 函数结束地址(可选)。若为 BADADDR(默认值),IDA 自动分析函数边界
- flags: 控制函数创建行为的标志位(通常保留为 0)
用于动态调试
1 2 3 4 5 6 7 8 | # 读取寄存器的值 idc.get_reg_value( "rax" ) # 设置寄存器的值 idaapi.set_reg_val( "rax" , 1234 ) # 读取内存 idc.read_dbg_memory(addr, size) # 修改内存 idc.patch_dbg_byte(addr, val) |
零散
1 2 | ## 获取光标所在地址 here() |
还算通用的函数
文本/指令搜索
1 2 3 4 5 6 7 8 9 10 11 | def search_text(text): '''全局搜索文本字符串''' start_ea = 0 result = [] while True : start_ea = idaapi.find_text(ustr = text, x = 0 , y = 0 , sflag = idaapi.SEARCH_DOWN, start_ea = start_ea) if start_ea = = idc.BADADDR: break result.append(start_ea) start_ea = idc.next_head(start_ea) return result |
控制流中可以用来识别分发器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | def find_dispatchers_by_predecessor_count(addr: int , expect_min_count = 10 ) - > List [idaapi.BasicBlock]: '''根据前驱数确定分发器''' # 获取函数对象 func = idaapi.get_func(addr) # 获取函数的流图对象, 用于分析函数的控制流 blocks = idaapi.FlowChart(func) # 用于存储识别出的分发器块 dispatchers = [] # 遍历函数的每个基本块 for block in blocks: # 获取当前基本块的所有前驱块 preds = block.preds() if len ( list (preds)) > expect_min_count: dispatchers.append(block) |
根据地址获取基本块
1 2 3 4 5 6 7 8 9 10 11 12 13 | def get_block_by_address(addr): '''根据地址获取基本块''' func = idaapi.get_func(addr) if not func: print (f "地址 {hex(addr)} 不在任何函数中" ) return None blocks = idaapi.FlowChart(func) for block in blocks: if block.start_ea < = addr < block.end_ea: return block |
赞赏
他的文章
赞赏
雪币:
留言: