Binary Ninja入门

一、安装

参考:

https://od.cloudsploit.top/zh-CN/tools/BinaryNinja/

Binary Ninja 5.1.8005 Personal - 吾爱破解 - 52pojie.cn

修复binaryninjacore.dll前记得备份,之后将修复好的binaryninjacore.dll以及生成的license.dat复制到Binary Ninja目录下。

如果之前安装过Binary Ninja,需要删除缓存文件(%APPDATA%\Binary Ninja\)。

二、使用

文件加载基址设置

通过Analysis -> Rebase将基址设置为0(不要勾选Relative),方便地址定位。

JNIEnv识别

为了让Binary Ninja能够识别JNIEnv类型,需要将 jni.h 文件导入。顶部菜单栏选择Analysis -> Import Header File,选择jni.h文件,点击Preview可以查看它依赖的库文件是否导入了。当然我们需要对jni.h文件进行修改,使其不依赖其他库函数,即手动定义最小 JNIEnv,因为我们只关心函数指针调用。

对于JNIEnv类型参数的重定义,参考ida,通过如下方式定义

1
struct JNINativeInterface const** JNIEnv

这样才将偏移量识别成函数。结果如下

结构体定义

通过如下方式可以使用C语言定义结构体(第二个选项就有点麻烦了)

1
2
3
4
5
6
7
8
struct CmdContext {
int32_t major_cmd; // offset 0
int32_t minor_cmd; // offset 4
int32_t sub_cmd; // offset 8
int32_t padding; // offset 12 (为了对齐指针)
struct JNINativeInterface** env; // offset 16
int64_t* obj; // offset 24
};

定义好后,对目标变量右键,选择Change Type即可修改变量类型。

最终效果如下

patch修复

参考:https://docs.binary.ninja/dev/bnil-modifying.html#support

它是通过遍历函数的IL指令进行修复的,对于没能识别成函数的部分,则无法修改。因此建议直接修改PE文件的字节码,再通过Ninja分析。以下是一个寄存器间接跳转修复的例子:

Binary Ninja能够通过静态分析自动计算出这类跳转中寄存器的值(即跳转地址)。例如

然而,它也会由于计算的问题而产生错误的跳转地址。例如

1
2
3
4
5
6
7
8
9
10
00509bc8 3a000010 adr x26, 0x509bcc 
00509bcc 31010098 ldrsw x17, data_509bf0
00509bd0 31420291 add x17, x17, #0x90
00509bd4 31e20291 add x17, x17, #0xb8
00509bd8 314202d1 sub x17, x17, #0x90
00509bdc 540180b9 ldrsw x20, [x10]
00509be0 310214cb sub x17, x17, x20
00509be4 5a03118b add x26, x26, x17 {0x100509c04}
00509be8 a80380d2 mov x8, #0x1d
00509bec 40031fd6 br x26 {0x100509c04}

根据gemini分析,说是错误的进行了符号扩展。

然后根据官方文档bnil-modifying,让gemini生成的br跳转的patch脚本

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
71
72
73
import binaryninja
from binaryninja.enums import LowLevelILOperation, RegisterValueType, SegmentFlag


def fix_overflow_jumps(bv):
print("[*] 正在等待分析完成...")
bv.update_analysis_and_wait()

print("[*] 开始扫描并修复 BR 寄存器跳转溢出问题...")
bv.begin_undo_actions()
fixed_count = 0

# 遍历所有函数
for func in bv.functions:
# 修复点:检查 llil 是否存在,防止 NoneType 报错
llil_func = func.low_level_il
if llil_func is None:
# 如果 LLIL 还没生成,尝试强制生成一下
continue

# 遍历所有基本块和指令,使用 LLIL 进行分析
for block in llil_func:
for instr in block:
# 寻找所有的间接跳转 (汇编里的 br, blr 等)
if instr.operation == LowLevelILOperation.LLIL_JUMP:

# 获取分析引擎计算出的目标值
# instr.dest 是跳转的目标表达式
# 获取跳转目标的推断值 (虽然指令里是寄存器,但这里拿的是推断出的常量)
value = instr.dest.possible_values

# 只有当引擎算出一个确定的“常量”值 或 常量指针值 时才处理
if value.type in [RegisterValueType.ConstantValue, RegisterValueType.ConstantPointerValue]:
wrong_addr = value.value

# 特征匹配核心逻辑:
# 1. 算出的地址非常大 (大于 4GB,通常意味着溢出)
# 2. 算出的地址在当前内存映射中是无效的 (None)
if wrong_addr >= 0x100000000:

# 尝试修复:减去溢出的 0x1 0000 0000
correct_addr = wrong_addr & 0xFFFFFFFF # 直接强制截断到 32 位

# 验证修复后的地址是否有效
segment = bv.get_segment_at(correct_addr)
if segment and segment.executable:
print(f"[!] 正在替换 IL 表达式 @ {hex(instr.address)}: {hex(wrong_addr)} -> {hex(correct_addr)}")
# --- 官方文档步骤 1: 创建新的常量表达式 ---
# 创建一个代表正确地址的常量表达式
new_expr = llil_func.const_pointer(8, correct_addr)

# --- 官方文档步骤 2 & 3: 找到旧表达式并替换 ---
# instr.dest.expr_index 是原本那个计算出错误值的表达式索引
llil_func.replace_expr(instr.dest.expr_index, new_expr)

# 记录一下跳转目标,确保 CFG 也会跟着连上
func.set_user_indirect_branches(instr.address, [(bv.arch, correct_addr)])

fixed_count += 1

# --- 官方文档步骤 4 & 5: 完成并重构 SSA ---
if fixed_count > 0:
llil_func.finalize()
llil_func.generate_ssa_form()


bv.commit_undo_actions() # 提交更改
bv.update_analysis_and_wait()
print(f"[*] 深度修复完成,共替换 {fixed_count} 处表达式。")


# 运行脚本(传入当前的 BinaryView)
fix_overflow_jumps(bv)

结果还是挺可观的!


Binary Ninja入门
http://example.com/2026/01/11/Android安全/Binary Ninja入门/
作者
gla2xy
发布于
2026年1月11日
许可协议