doronz88/fa

GitHub: doronz88/fa

一款面向IDA的自动化二进制分析工具,通过声明式语法简化符号定位与结构体识别流程。

Stars: 93 | Forks: 13

![Python application](https://github.com/doronz88/fa/workflows/Python%20application/badge.svg) # FA ## 它是什么? FA 代表 Firmware Analysis(固件分析),且是**为人类设计**的。 FA 允许用户轻松执行代码探索、符号搜索及其他功能。 通常,此类任务需要您理解复杂的 API,编写依赖于机器的代码并执行其他繁琐的任务。 FA 旨在以一种更友好、更易于维护的方式,替代那些通常像机器人一样执行的步骤(查找 X 字符串,转到 xref,查找下一个调用函数……)。 当前的代码库非常面向 IDA 插件。将来我会考虑为其他反汇编器添加兼容性,例如:Ghidra, Radare 等... 当然也非常欢迎 Pull Requests :smirk:。 ## 环境要求 支持 IDA 8.0+。 在您的 IDA python 目录中运行: ``` python -m pip install -r requirements.txt ``` 用于测试: ``` python -m pip install -r requirements_testing.txt ``` ## 从何开始? 在使用之前,您应该了解以下术语:Projects(项目)、SIG 文件和 Loaders(加载器)。 所以,喝杯咖啡,听点[nice music](https://www.youtube.com/watch?v=5rrIx7yrxwQ),请花几分钟时间阅读本 README。 ### Projects(项目) “project”是不同签名的命名空间。 例如:linux, linux_x86, linux_arm 等都是不错的项目名称,如果您在这些平台上工作,可以指定它们。 通过将签名划分为此类项目,例如 Windows 符号就不会在 Linux 项目中被搜索,这将带来更好的目录组织布局、更好的性能以及更低的误报率。 签名默认位于 `signatures` 目录中。 如果您希望使用其他位置,可以在 FA 的根目录下创建 `config.ini`,内容如下: ``` [global] signatures_root = /a/b/c ``` ### SIG 格式 SIG 格式是 FA 关于符号搜索的核心功能。每个 SIG 文件都位于项目目录中,当请求生成项目的符号列表时会自动搜索该文件。 该格式基于 Hjson,用于描述您**作为人类**为了执行给定任务(符号搜索或二进制探索)所进行的操作。 SIG 语法(单个): ``` { name: name instructions : [ # Available commands are listed below command1 command2 ] } ``` `instructions` 列表中的每一行行为都类似于 shell 命令行,它将前一个结果作为输入,并将下一个结果输出到下一行。 感到困惑?没关系 :grinning:。[看看下面的例子就知道了](#examples) 用户也可以使用自己的 python 脚本文件来执行搜索。只需在您的项目目录中创建一个新的 `.py` 文件并实现 `run(interpreter)` 方法。 此外,项目的路径会被添加到 python 的 `sys.path` 中,以便您可以在脚本之间相互导入。 要查看可用命令的列表,[请查看下方列表](#available-commands) ### 示例 #### 查找全局结构体 ``` { name: g_awsome_global, instructions: [ # find the byte sequence '11 22 33 44' find-bytes '11 22 33 44' # advance offset by 20 offset 20 # verify the current bytes are 'aa bb cc dd' verify-bytes 'aa bb cc dd' # go back by 20 bytes offset offset -20 # set global name set-name g_awsome_global ] } ``` #### 通过字符串引用查找函数 ``` { name: free instructions: [ # search the string "free" find-str 'free' --null-terminated # goto xref xref # goto function's prolog function-start # reduce to the singletone with most xrefs to max-xrefs # set name and type set-name free set-type 'void free(void *block)' ] } ``` #### 执行代码探索 ``` { name: arm-explorer instructions: [ # search for some potential function prologs arm-find-all 'push {r4, lr}' arm-find-all 'push {r4, r5, lr}' thumb-find-all 'push {r4, lr}' thumb-find-all 'push {r4, r5, lr}' # convert into functions make-function ] } ``` #### 执行字符串探索 ``` { name: arm-string-explorer instructions: [ # goto printf locate printf # iterate every xref xref # and for each, go word-word backwards add-offset-range 0 -40 -4 # if ldr to r0 verify-operand ldr --op0 r0 # go to the global string goto-ref --data # and make it literal make-literal ] } ``` #### 查找枚举和常量 ``` { name: consts-finder instructions: [ # goto printf locate printf # iterate all its function lines function-lines # save this result store printf-lines # look for: li r7, ??? verify-operand li --op0 7 # extract second operand operand 1 # define the constant set-const IMPORTANT_OFFSET # load previous results load printf-lines # look for: li r7, ??? verify-operand li --op0 8 # get second operand operand 1 # set this enum value set-enum important_enum_t some_enum_key ] } ``` #### 添加结构体成员偏移 ``` { name: structs-finder instructions: [ # add hard-coded '0' into resultset add 0 # add a first member at offset 0 set-struct-member struct_t member_at_0 'unsigned int' # advance offset by 4 offset 4 # add a second member set-struct-member struct_t member_at_4 'const char *' # goto function printf locate printf # iterate its function lines function-lines # look for the specific mov opcode (MOV R8, ???) verify-operand mov --op0 8 # extract the offset operand 1 # define this offset into the struct set-struct-member struct_t member_at_r8_offset 'const char *' ] } ``` #### 查找连续的多个函数 ``` { name: cool_functions instructions: [ # find string find-str 'init_stuff' --null-terminated # goto to xref xref # goto function start function-start # verify only one single result unique # iterating every 4-byte opcode add-offset-range 0 80 4 # if mnemonic is bl verify-operand bl # sort results sort # store resultset in 'BLs' store BLs # set first bl to malloc function single 0 goto-ref --code set-name malloc set-type 'void *malloc(unsigned int size)' # go back to the results from 4 commands ago # (the sort results) load BLs # rename next symbol :) single 1 goto-ref --code set-name free set-type 'void free(void *block)' ] } ``` #### 条件分支 ``` { name: set_opcode_const instructions: [ # goto printf function locate printf # goto 'case_opcode_bl' if current opcode is bl if 'verify-operand bl' case_opcode_bl # make: #define is_bl (0) clear add 0 set-const is_bl # finish script by jumping to end b end # mark as 'case_opcode_bl' label label case_opcode_bl # make: #define is_bl (1) clear add 1 set-const is_bl # mark script end label end ] } ``` #### 用于查找符号列表的 Python 脚本 ``` from fa.commands.find_str import find_str from fa.commands.set_name import set_name from fa import context def run(interpreter): # throw an exception if not running within ida context context.verify_ida('script-name') # locate the global string set_name(find_str('hello world', null_terminated=True), 'g_hello_world', interpreter) ``` #### 用于自动化 SIG 文件解释器的 Python 脚本 ``` TEMPLATE = ''' find-str '{unique_string}' xref function-start unique set-name '{function_name}' ''' def run(interpreter): for function_name in ['func1', 'func2', 'func3']: instructions = TEMPLATE.format(unique_string=function_name, function_name=function_name).split('\n') interpreter.find_from_instructions_list(instructions) ``` #### 用于动态添加结构体的 Python 脚本 ``` from fa.commands.set_type import set_type from fa import fa_types TEMPLATE = ''' find-str '{unique_string}' xref ''' def run(interpreter): fa_types.add_const('CONST7', 7) fa_types.add_const('CONST8', 8) foo_e = fa_types.FaEnum('foo_e') foo_e.add_value('val2', 2) foo_e.add_value('val1', 1) foo_e.update_idb() special_struct_t = fa_types.FaStruct('special_struct_t') special_struct_t.add_field('member1', 'const char *') special_struct_t.add_field('member2', 'const char *', offset=0x20) special_struct_t.update_idb() for function_name in ['unique_magic1', 'unique_magic2']: instructions = TEMPLATE.format(unique_string=function_name, function_name=function_name).split('\n') results = interpreter.find_from_instructions_list(instructions) for ea in results: # the set_type can receive either a string, FaStruct # or FaEnum :-) set_type(ea, special_struct_t) ``` ### 别名 可以使用 `fa/commands/alias` 或 `/alias` 中的文件为每个命令设置“别名”。 每行的语法如下:`alias_command = command` 例如: ``` ppc32-verify = keystone-verify-opcodes --bele KS_ARCH_PPC KS_MODE_PPC32 ``` 项目别名具有更高的优先级。 ### Loaders(加载器) 加载器是运行 FA 的入口点。 未来我们可能会添加 Ghidra 和其他工具。 请首先按如下方式安装包: 克隆仓库并在本地安装: ``` # 克隆 git clone git@github.com:doronz88/fa.git cd fa # 安装 python -m pip install -e . ``` #### IDA 在 IDA Python 中运行: ``` from fa import ida_plugin ida_plugin.install() ``` 您应该会在输出窗口中看到一个友好的提示,欢迎您使用 FA。此外,还会打印一份快速使用指南,这样您就不必记住所有内容。 此外,还会添加一个额外的 `FA Toolbar`,其中包含快速功能,这些功能也可以在新创建的 `FA` 菜单下找到。 ![FA Menu](https://github.com/doronz88/fa/raw/master/fa/res/screenshots/menu.png "FA Menu") 快速入门提示: `Ctrl+6` 选择您的项目,然后 `Ctrl+7` 查找其定义的所有符号。 您也可以在脚本模式下运行 IDA,仅使用以下命令提取符号: ``` ida -S"fa/ida_plugin.py --project-name --symbols-file=/tmp/symbols.txt" foo.idb ``` #### ELF 为了在 RAW ELF 文件上使用 FA,只需使用以下命令行: ``` python elf_loader.py ``` ### 可用命令 参见 [commands.md](commands.md)
标签:FA, IDA插件, Python, Wayback Machine, 二进制分析, 云安全监控, 云安全运维, 云资产清单, 代码探索, 反汇编, 固件分析, 嵌入式安全, 无后门, 符号定位, 结构体识别, 网络调试, 自动化, 逆向工具, 逆向工程, 静态分析