Skip to content

Commit 7a02176

Browse files
committed
bn: add support for BinaryNinja
1 parent 3d8d7a2 commit 7a02176

File tree

5 files changed

+174
-2
lines changed

5 files changed

+174
-2
lines changed

README.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ Before | After
5050
:----------:|:------------:
5151
![i1][i1] | ![i2][i2]
5252

53+
## Binary Ninja
54+
55+
see [Binary Ninja](./binaryninja).
56+
57+
Before | After
58+
:----------:|:------------:
59+
![b2][b2] | ![b4][b4]
60+
5361
## Radare2 Plugin
5462

5563
> WIP, see [Radare2](./r2)
@@ -68,8 +76,8 @@ cd demo_apk
6876
- [x] support both C/C++ JNI functions
6977
- [x] support overloaded JNI functions
7078
- [x] remove Jadx dependence, all in Python
79+
- [x] Add BinaryNinja plugin
7180
- [ ] support [env->RegisterNatives][reg] JNI functions
72-
- [ ] Add BinaryNinja plugin
7381

7482
# LINKS
7583

@@ -85,4 +93,10 @@ cd demo_apk
8593
[i2]: https://img-blog.csdnimg.cn/20201005164352403.png
8694
[g1]: https://img-blog.csdnimg.cn/20201005152933443.png
8795
[g2]: https://img-blog.csdnimg.cn/20201005153107550.png
96+
97+
[b1]: https://i-blog.csdnimg.cn/direct/1a68a50684ef4151a7c6b7442599f295.png
98+
[b2]: https://i-blog.csdnimg.cn/direct/56fb96fdf46a42b8ad5a79367df0b78f.png
99+
[b3]: https://i-blog.csdnimg.cn/direct/6de69105b83c4a9197cbad513ed4fe94.png
100+
[b4]: https://i-blog.csdnimg.cn/direct/58a77ec9f9a54e86871b5325ab5f1333.png
101+
88102
[extract]: https://img-blog.csdnimg.cn/direct/b7dbfbe6b3744b56a6d66079db8ebbb8.png

binary_ninja/README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Load
2+
3+
File -> Run Script
4+
5+
6+
# Logging
7+
8+
```
9+
[+] plugin start, bv=<BinaryView: '/root/jni_helper/assets/lib/arm64-v8a/libdemoc.so', len 0x2a88>
10+
[+] init_header done.
11+
[+] fix 0x3708 JNI_OnLoad -> jint(JavaVM* vm, void* reserved)
12+
[+] fix 0x4980 JNI_OnUnload -> void(JavaVM* vm, void* reserved)
13+
[+] fix 0x5388 Java_com_evilpan_demoapk_FacadeC_testStatic -> jint(JNIEnv* env, jclass clazz, jint a1)
14+
[+] fix 0x5044 Java_com_evilpan_demoapk_FacadeC_stringFromJNI -> jstring(JNIEnv* env, jobject thiz)
15+
[+] fix 0x5916 Java_com_evilpan_demoapk_FacadeC_testArray -> void(JNIEnv* env, jobject thiz, jintArray a1)
16+
[+] fix 0x5468 Java_com_evilpan_demoapk_FacadeC_testClass -> jint(JNIEnv* env, jobject thiz, jobject a1)
17+
[+] fix 0x5144 Java_com_evilpan_demoapk_FacadeC_testOverload__ -> jint(JNIEnv* env, jobject thiz)
18+
[+] fix 0x5220 Java_com_evilpan_demoapk_FacadeC_testOverload__I -> jint(JNIEnv* env, jobject thiz, jint a1)
19+
[+] fix 0x5300 Java_com_evilpan_demoapk_FacadeC_testOverload__JFD -> jint(JNIEnv* env, jobject thiz, jlong a1, jfloat a2, jdouble a3)
20+
```
21+
22+
High Level IL:
23+
24+
Before =>
25+
26+
![b1][b1]
27+
28+
After =>
29+
30+
![b3][b3]
31+
32+
Pseudo C:
33+
34+
Before =>
35+
36+
![b2][b2]
37+
38+
After =>
39+
40+
![b4][b4]
41+
42+
43+
[b1]: https://i-blog.csdnimg.cn/direct/1a68a50684ef4151a7c6b7442599f295.png
44+
[b2]: https://i-blog.csdnimg.cn/direct/56fb96fdf46a42b8ad5a79367df0b78f.png
45+
[b3]: https://i-blog.csdnimg.cn/direct/6de69105b83c4a9197cbad513ed4fe94.png
46+
[b4]: https://i-blog.csdnimg.cn/direct/58a77ec9f9a54e86871b5325ab5f1333.png

binary_ninja/jni_helper.py

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import json
2+
import os
3+
import binaryninja
4+
from binaryninja.interaction import (
5+
OpenFileNameField,
6+
get_form_input,
7+
show_message_box,
8+
)
9+
10+
def log(msg, *args, **kwargs):
11+
print("[+]", msg, *args, **kwargs)
12+
13+
class JNIHelper:
14+
15+
def __init__(self):
16+
self.jni_header = ""
17+
18+
def start(self):
19+
if not self.init_header():
20+
return
21+
self.apply_signatures()
22+
23+
def init_header(self):
24+
options = ["-fdeclspec"]
25+
jni_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "headers", "jni.h")
26+
if not os.path.exists(jni_file):
27+
jni_file = self.choose_file("jni.h not found, choose one")
28+
if jni_file is None:
29+
return
30+
with open(jni_file, 'r') as f:
31+
self.jni_header = f.read()
32+
pr = self.parse_source(self.jni_header, "jni.h")
33+
if not pr:
34+
return False
35+
log("init_header done.")
36+
return True
37+
38+
def parse_source(self, source, name="<source>"):
39+
options = ["-fdeclspec"]
40+
result, errors = TypeParser.default.parse_types_from_source(
41+
source, name, bv.platform,
42+
options=options
43+
)
44+
if result is None:
45+
log("parse error:")
46+
for err in errors:
47+
log(err, end='')
48+
return None
49+
return result
50+
51+
def choose_file(self, desc, title="File"):
52+
fd = OpenFileNameField(desc)
53+
if get_form_input([fd], title):
54+
return fd.result
55+
return None
56+
57+
def apply_signatures(self):
58+
file = self.choose_file("signature.json from extract_jni.py")
59+
if not file:
60+
return
61+
with open(file, 'r') as f:
62+
meta = json.load(f)
63+
jni_ext = self.jni_header + "\n"
64+
func_map = {}
65+
for cls, methods in meta["dexInfo"].items():
66+
for method in methods:
67+
mangle = method["mangle"]
68+
found = bv.get_functions_by_name(mangle)
69+
if not found:
70+
continue
71+
func = found[0]
72+
func_map[mangle] = func
73+
# skip those already defined
74+
ret = method["ret"]
75+
args = ",".join(method["args"])
76+
line = f"{ret} {mangle}({args})"
77+
if cls == "__COMMON__":
78+
continue
79+
jni_ext += line + ";\n"
80+
pr = self.parse_source(jni_ext, "jni_ext.h")
81+
if pr is None:
82+
return
83+
for pt in pr.types:
84+
bv.define_user_type(pt.name, pt.type)
85+
for pf in pr.functions:
86+
if pf.name not in func_map:
87+
continue
88+
func = func_map[pf.name]
89+
log(f"fix 0x{func.start} {pf.name} -> {pf.type}")
90+
func.type = pf.type
91+
func.reanalyze()
92+
93+
94+
log(f"plugin start, bv={bv}")
95+
jh = JNIHelper()
96+
jh.start()

extract_jni.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ def native_args(self):
132132
if self.static:
133133
native_args_list.append(("jclass", "clazz"))
134134
else:
135-
native_args_list.append(("jobject", "this"))
135+
native_args_list.append(("jobject", "thiz"))
136136
return native_args_list + [
137137
(get_type(arg), f"a{i + 1}") for i, arg in enumerate(self.args)
138138
]

headers/jni.h

+16
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@
2929
#define JNIIMPORT __declspec(dllimport)
3030
#define JNICALL __stdcall
3131

32+
// tricks for binary ninja
33+
// -fdeclspec -x c -std=c99
34+
#ifndef __int64
35+
typedef int64_t __int64;
36+
#endif
37+
#ifndef va_list
38+
typedef char* va_list;
39+
#endif
40+
#ifndef va_start
41+
#define va_start(ap, last) (ap = (va_list)&last + sizeof(last))
42+
#endif
43+
#ifndef va_end
44+
#define va_end(ap) (ap = (va_list)0)
45+
#endif
46+
47+
3248
typedef long jint;
3349
typedef __int64 jlong;
3450
typedef signed char jbyte;

0 commit comments

Comments
 (0)