官网angr (不够详细,想要用来做一些事情还是要看angr的代码,至少得看python实现的接口)
开始 Project angr从加载二进制文件到Project
中开始。从此以后的object基本上都围绕它展开。
1 proj = angr.Project('/binary_file' )
项目基本属性:
1 2 3 proj.arch proj.entry proj.filename
arch 是一个archinfo.Arch
对象的实例。它包含大量关于它所运行的CPU的文书数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 __all__ = [ "RegisterOffset" , "TmpVar" , "RegisterName" , "Endness" , "Register" , "Arch" , "register_arch" , "ArchNotFound" , "arch_from_id" , "reverse_ends" , "get_host_arch" , "all_arches" , "defines" , "ArchAMD64" , "ArchX86" , "ArchARM" , "ArchARMEL" , "ArchARMHF" , "ArchARMCortexM" , "ArchAArch64" , "ArchAVR8" , "ArchPPC32" , "ArchPPC64" , "ArchMIPS32" , "ArchMIPS64" , "ArchSoot" , "ArchError" , "ArchS390X" , "ArchPcode" , ] bits = 32 vex_arch = "VexArchARM" name = "ARMEL" qemu_name = "arm" ida_processor = "armb" linux_name = "arm" triplet = "arm-linux-gnueabihf" max_inst_bytes = 4 ret_offset = 8 fp_ret_offset = 8 vex_conditional_helpers = True syscall_num_offset = 36 call_pushes_ret = False stack_change = -4 memory_endness = Endness.LE register_endness = Endness.LE sizeof = {"short" : 16 , "int" : 32 , "long" : 32 , "long long" : 64 } ''' AMD64 X86 ARM ARMEL ARMHF ARMCortexM AArch64 AVR8 PPC32 PPC64 MIPS32 MIPS64 Soot Error S390X Pcode '''
CLE模块:用于加载到虚拟空间。 1 2 3 4 5 6 7 8 9 10 11 proj.loader proj.loader.shared_objects proj.loader.min_addr proj.loader.max_addr proj.loader.main_object proj.loader.main_object.execstack proj.loader.main_object.pic
The factory
这个函数接收一个地址,用于提取一个基础块,返回一个Block对象。angr以基本块为单位分析代码。 如何使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> >>> block.pp()0x401670 : xor ebp, ebp0x401672 : mov r9, rdx0x401675 : pop rsi0x401676 : mov rdx, rsp0x401679 : and rsp, 0xfffffffffffffff0 0x40167d : push rax0x40167e : push rsp0x40167f : lea r8, [rip + 0x2e2a ]0x401686 : lea rcx, [rip + 0x2db3 ]0x40168d : lea rdi, [rip - 0xd4 ]0x401694 : call qword ptr [rip + 0x205866 ]block.instructions block.instruction_addrs
还可以获取block的其他形式。比如capstone
、VEX IRSB
。
States Project 对象只表示程序的初始化图像
。当你使用angr执行时,你正在处理一个表示模拟程序状态的特定对象—— SimState:
1 state = proj.factory.entry_state()
使用 state.regs 和 state.mem 来访问该状态的寄存器和内存
1 2 3 state.regs.rip state.mem[proj.entry].int .resolved
注意!这里返回的都是向量,可以用.length
返回位宽度。 如何从Python int转换为位向量:
1 2 3 4 bv = state.solver.BVV(0x1234 , 32 ) state.solver.eval (bv)
mem接口功能强大,可以用.resolved
获取bv值,用.concrete
获取Python int类型值。
Function Manager CFG结果生成一个名为Function Manager
的对象,可通过cfg.kb.functions
访问。这个对象最常见的用例是像字典一样访问它。它将地址映射到1个对象,可以告诉您关于函数的属性。
1 entry_func = cfg.kb.functions[p.entry]
函数类有几个重要的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 entry_func.block_addrs entry_func.blocks entry_func.string_references() entry_func.returning entry_func.transition_graph entry_func.get_call_sites()
操作数 1 2 3 4 5 6 7 8 9 10 11 12 block_cap = block.capstone for i in block_cap.insns: operands = i.operands ins.append(str (i)) op_types.append([]) for op in operands: capstone.x86.X86OpValue op_types[-1 ].append(op.type )
capstone
是个很牛b的反汇编引擎,angr可以通过方法快速获取其对象,例如第一行。 然后可以通过capstone.insns
获取所有指令的对象。然后每个指令对象可以通过insn.operands
方法获取这条指令的操作数对象列表
。 类似IDAPython
的GetOpType
函数: 是IDAPython中的一个函数,它的作用是获取指令操作数的类型。它接受两个参数:ea和n。ea是指令的地址,n是操作数的索引(0表示第一个操作数,1表示第二个操作数)。它返回一个整数值,表示操作数的类型。不同的类型有不同的含义,参考:IDA Help: get_operand_type — IDA帮助:get_operand_type
1 2 3 4 5 6 7 8 9 Get type of instruction operand ea - linear address of instruction n - number of operand: 0 - the first operand 1 - the second operand returns: -1 bad operand number passed long get_operand_type (long ea, long n) ;
但是angr这个不一样,通过操作数列表里的元素,也就是操作数对象
的.type
方法,可以获取这个操作数的类型,是一个整数,有以下含义: 1: 寄存器 2: 已知地址 3: 指向地址的指针 4: 常量值(有待考证)
段信息 使用angr的Project类的loader属性,它是一个cle.Loader对象,可以加载二进制文件和库。您可以通过loader对象的main_object属性或shared_objects属性来访问不同的内存段,它们是cle.Backend对象,包含了内存段的信息。您可以通过Backend对象的segments属性或sections属性来获取内存段的地址和空间。 使用angr的State类的memory属性,它是一个SimMemory对象,表示一个符号执行状态下的内存模型。您可以通过memory对象的load方法或store方法来读写内存地址和空间。您也可以通过memory对象的map_region方法或unmap_region方法来映射或取消映射内存区域。 .text:(代码段),可读、可执行 .data:(数据段),存放全局变量、全局常量等 .idata:(数据段),导入函数的代码段,存放外部函数地址。(当然还有 edata ,导出函数代码段,但不常用) .rdata:(数据段),资源数据段,程序用到什么资源数据都在这里(包括自己打包的,还有开发工具打包的)
段对象的类型 cle.backends.pe.regions.PESection elf也类似。
段的属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [ 'addr_to_offset' , 'characteristics' , 'contains_addr' , 'contains_offset' , 'filesize' , 'is_executable' , 'is_readable' , 'is_writable' , 'max_addr' , 'max_offset' , 'memsize' , 'min_addr' , 'min_offset' , 'name' , 'offset' , 'offset_to_addr' , 'only_contains_uninitialized_data' , 'vaddr' ] [ '.rdata\x00\x00' , 1076887616 , <bound method Region.contains_addr of <.rdata | offset 0x3400 , vaddr 0x405000 , size 0x304 >>, <bound method Region.contains_offset of <.rdata | offset 0x3400 , vaddr 0x405000 , size 0x304 >>, 1024 , False , True , False , 4215555 , 14335 , 772 , 4214784 , <bound method Region.min_offset of <.rdata | offset 0x3400 , vaddr 0x405000 , size 0x304 >>, 13312 , <bound method Region.offset_to_addr of <.rdata | offset 0x3400 , vaddr 0x405000 , size 0x304 >>, 4214784 ]
多余节点去除 思路:默认引擎会在call后切割(ida和radare2都不会在call后面切割),那么会产生这样的节点: 或者这种: 可以想象: 因为节点的定义是块开始地址和大小,同时节点的末尾会指向其他节点 那么:如果两个节点可以合并,下面那个节点一定只有一个别人指向它的边,同时上面那个节点一定只有一个指向下面节点的边, 这样它就可以将头部拼接到指向它的那个节点的末尾,从而合并成一个节点。
阅读angr-utils源代码 出现dot找不到问题的bug解决:(65条消息) 使用angrutils生成控制流图出错的解决过程_芳芳超人爱学习的博客-CSDN博客