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 | ea = here() |
使用 FuncItems 来完成:
1 | for ea in FuncItems(here()): |
指令
idaapi.decode_insn(out, ea):解析指令并填充 out (insn_t 结构体)
ida_ua.insn_t():new 一个 insn_t 结构体
例:找到函数中所有的跳转指令
1 | for ea in FuncItems(here()): |
另一种实现
1 | JMPS = [idaapi.NN_jmp, idaapi.NN_jmpfi, idaapi.NN_jmpni] |
操作数
可以使用 get_operand_type(ea, n) 得到操作数的类型。get_operand_value(ea, n) 来得到操作数的值。ea 是地址,n 是索引。
这里有 8 中不同类型的操作数类型。
o_void 如果一个指令没有任何操作数它将返回 0
1 | Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}") |
o_reg 如果一个操作数是一个普遍的寄存器将返回此类型。这个值在内部表示为 1
1 | Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}") |
o_mem 如果一个操作数是直接内存引用它将返回这个类型。这个值在内部表示为 2。这种类型是有用的在 DATA 段查找引用
1 | Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}") |
o_phrase 这个操作数被返回则这个操作数包含一个基本的寄存器或一个索引寄存器。这个值在内部表示为 3。
1 | Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}") |
o_displ 这个操作数被返回则操作数包含寄存器和一个位移值,这个为位移值是一个整数,例如 0x18。
这是常见的当一条指令访问值在一个结构中。在内部,它表示为 4 的值。
1 | Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}") |
o_imm 操作数是这样一个为整数的 0xc 的值的类型。它在内部表示为 5。
1 | Python>print(f"0x{hex(ea)} {generate_disasm_line(ea, 0)}") |
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 | addr= 0x401198 |
ELF 运行流程
静态链接
动态链接
基本操作说明
sys_execve
该函数主要用于执行一个新的程序,即执行我们想要执行的程序,会检查相应的 argv 以及 envp 等参
数。
do_execve
该函数打开目标映像文件,并从目标文件的开始处读入指定长度的(目前为128)字节来获取相应目标
文件的基本信息。
search_binary_handler
该函数会搜索支持处理当前类型的二进制文件类型队列,以便于让各种可执行程序的处理程序进行相应
的处理。
load_elf_binary
该函数的主要处理流程如下
-检查并获取 elf 文件的头部信息
如果目标文件采用动态链接,则使用 .interp 节来确定 loader 的路径。
将 program header 中记录的相应的段映射到内存中。program header 中有以下重要信息
- 每一个段需要映射到的地址
- 每一个段相应的权限
- 记录哪些节属于哪些段
具体的映射如下
分情况处理
动态链接情况下,将 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
- 程序结束前的收尾工作