首页
社区
课程
招聘
[原创]ida 笔记
发表于: 3天前 477

[原创]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

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回