IDAPython常用函数

官方文档

基础API

  • here() / get_screen_ea():返回光标当前所在地址
  • get_inf_attr(INF_MIN_EA) / get_inf_attr(INF_MAX_EA):获得最小 / 最大地址
  • generate_disasm_line(ea, GENDSM_FORCE_CODE):获得 ea 所在处的一条汇编指令
  • print_insn_mnem(ea):获得 ea 所在处的指令助记符
  • print_operand(ea, n):获取 ea 所在处的第 n 个操作数

  • Segments():返回段的可遍历对象
  • get_segm_name(ea):获得 ea 所在段名
  • get_next_seg(ea):获取下一个段
  • get_segm_start(ea) / get_segm_end(ea):获取段开始 / 结束地址

for seg in Segments():

1
print(f"{get_segm_name(seg)}, {get_segm_start(seg)}, {get_segm_end(seg)}")

函数

Functions() / Funtions(start_addr, end_addr):返回函数的可遍历对象

get_func_name(ea):返回函数名称

idaapi.get_func(ea):获得解析后的函数对象

get_func_attr(ea, FUNCATTR_START) / get_func_attr(ea, FUNCATTR_END):访问函数边界

get_next_func(ea) / get_prev_func(ea):获取上 / 下一个函数

prev_head(ea) / next_head(ea):获得上 / 下一条指令地址

FuncItems(ea):获取 ea 所处函数的所有指令地址

eg:打印一个函数的反汇编

1
2
3
4
5
6
ea = here()
now = get_func_attr(ea, FUNCATTR_START)
end = get_func_attr(ea, FUNCATTR_END)
while now < end:
print(f"0x{now} {generate_disasm_line(now, 0)}")
now = next_head(now)

使用 FuncItems 来完成:

1
2
for ea in FuncItems(here()):
print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")

指令

idaapi.decode_insn(out, ea):解析指令并填充 out (insn_t 结构体)

ida_ua.insn_t():new 一个 insn_t 结构体

例:找到函数中所有的跳转指令

1
2
3
4
for ea in FuncItems(here()):
asm = generate_disasm_line(ea, 0)
if "jmp" in asm:
print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")

另一种实现

1
2
3
4
5
6
JMPS = [idaapi.NN_jmp, idaapi.NN_jmpfi, idaapi.NN_jmpni]
for ea in FuncItems(here()):
ins = ida_ua.insn_t()
idaapi.decode_insn(ins, ea)
if ins.itype in JMPS:
print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")

操作数

可以使用 get_operand_type(ea, n) 得到操作数的类型。get_operand_value(ea, n) 来得到操作数的值。ea 是地址,n 是索引。

这里有 8 中不同类型的操作数类型。

o_void 如果一个指令没有任何操作数它将返回 0

1
2
3
4
Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")
>>> 0xa09166 retn
Python>print(get_operand_type(ea,0))
>>> 0

o_reg 如果一个操作数是一个普遍的寄存器将返回此类型。这个值在内部表示为 1

1
2
3
4
Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")
>>> 0xa09163 pop edi
Python>print(get_operand_type(ea,0))
>>> 1

o_mem 如果一个操作数是直接内存引用它将返回这个类型。这个值在内部表示为 2。这种类型是有用的在 DATA 段查找引用

1
2
3
4
Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")
>>> 0xa05d86 cmp ds:dword_A152B8, 0
Python>print(get_operand_type(ea,0))
>>> 2

o_phrase 这个操作数被返回则这个操作数包含一个基本的寄存器或一个索引寄存器。这个值在内部表示为 3。

1
2
3
4
Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")
>>> 0x1000b8c2 mov [edi+ecx], eax
Python>print(get_operand_type(ea,0))
>>> 3

o_displ 这个操作数被返回则操作数包含寄存器和一个位移值,这个为位移值是一个整数,例如 0x18。

这是常见的当一条指令访问值在一个结构中。在内部,它表示为 4 的值。

1
2
3
4
Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")
>>> 0xa05dc1 mov eax, [edi+18h]
Python>print(get_operand_type(ea,1))
>>> 4

o_imm 操作数是这样一个为整数的 0xc 的值的类型。它在内部表示为 5。

1
2
3
4
Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}")
>>> 0xa05da1 add esp, 0Ch
Python>print(get_operand_type(ea,1))
>>> 5

o_far 是用来寻找操作数的访问立即数远地址的。它在内部表示为 6。

o_near 是用来寻找操作数的访问立即数近地址的。它在内部表示为 7。

数据读写

  • get_bytes(ea, size)
  • ida_bytes.get_byte(ea)
  • ida_bytes.get_word(ea)
  • ida_bytes.get_dword(ea)
  • ida_bytes.get_qword(ea)
  • ida_bytes.patch_bytes(ea, buf)
  • ida_bytes.patch_byte(ea, val)
  • ida_bytes.patch_word(ea, val)
  • ida_bytes.patch_dword(ea, val)
  • ida_bytes.patch_qword(ea, val)

调试

  • add_bpt(ea):添加断点
  • del_bpt(ea):删除断点
  • start_process(path, args, sdir):启动调试
  • step_into():步入
  • step_over():步过
  • step_until_ret():执行到返回
  • get_reg_value(regname):获取寄存器值
  • set_reg_value(value, regname):设置寄存器值
  • wait_for_next_event(wfne, timeout):等待调试事件,第一个参数一般取为 WFNE_SUSP

应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
addr= 0x401198
end = get_func_attr(addr,FUNCATTR_END)

while addr<end:
next_ins=next_head(addr)
if "dword_603054" in generate_disasm_line(addr,0);

while True:
addr = next_dead(addr)
if "jnz" in generate_disasm_line(addr,0):
dest = get _operand_value(addr,0)
offset = dest-(addr+5)
print(offset)

ida_bytes.patch_byte(addr,0xE9)
addr = next_ins
break

else:
addr=next_ins

ELF 运行流程

静态链接

image-20240201223812328

动态链接

image-20240201223823579

基本操作说明

sys_execve

该函数主要用于执行一个新的程序,即执行我们想要执行的程序,会检查相应的 argv 以及 envp 等参

数。

do_execve

该函数打开目标映像文件,并从目标文件的开始处读入指定长度的(目前为128)字节来获取相应目标

文件的基本信息。

search_binary_handler

该函数会搜索支持处理当前类型的二进制文件类型队列,以便于让各种可执行程序的处理程序进行相应

的处理。

load_elf_binary

该函数的主要处理流程如下

  • -检查并获取 elf 文件的头部信息

  • 如果目标文件采用动态链接,则使用 .interp 节来确定 loader 的路径。

  • 将 program header 中记录的相应的段映射到内存中。program header 中有以下重要信息

    • 每一个段需要映射到的地址
    • 每一个段相应的权限
    • 记录哪些节属于哪些段

具体的映射如下

image-20240201223854316

分情况处理

  • 动态链接情况下,将 sys_execve 的返回地址改为 loader(ld.so) 的 entry point

  • 静态链接情况下,将 sys_execve 的返回地址改为程序的入口点

ld.so

该文件有以下功能

  • 主要用于载入 ELF 文件中 DT_NEED 中记录的共享库
  • 初始化工作
  • 初始化 GOT 表
  • 将 symbol table 合并到 global symbol table

_start

  • _start 函数会将以下项目交给 libc_start_main
  • 环境变量起始地址
  • .init
  • 启动 main 函数前的初始化工作
  • .fini
  • 程序结束前的收尾工作