doronz88/fa
GitHub: doronz88/fa
一款面向IDA的自动化二进制分析工具,通过声明式语法简化符号定位与结构体识别流程。
Stars: 93 | Forks: 13

# 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` 菜单下找到。

快速入门提示:
`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, 二进制分析, 云安全监控, 云安全运维, 云资产清单, 代码探索, 反汇编, 固件分析, 嵌入式安全, 无后门, 符号定位, 结构体识别, 网络调试, 自动化, 逆向工具, 逆向工程, 静态分析