Skip to content

Commit

Permalink
bn: add support for BinaryNinja
Browse files Browse the repository at this point in the history
  • Loading branch information
evilpan committed Oct 21, 2024
1 parent 3d8d7a2 commit 7a02176
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 2 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ Before | After
:----------:|:------------:
![i1][i1] | ![i2][i2]

## Binary Ninja

see [Binary Ninja](./binaryninja).

Before | After
:----------:|:------------:
![b2][b2] | ![b4][b4]

## Radare2 Plugin

> WIP, see [Radare2](./r2)
Expand All @@ -68,8 +76,8 @@ cd demo_apk
- [x] support both C/C++ JNI functions
- [x] support overloaded JNI functions
- [x] remove Jadx dependence, all in Python
- [x] Add BinaryNinja plugin
- [ ] support [env->RegisterNatives][reg] JNI functions
- [ ] Add BinaryNinja plugin

# LINKS

Expand All @@ -85,4 +93,10 @@ cd demo_apk
[i2]: https://img-blog.csdnimg.cn/20201005164352403.png
[g1]: https://img-blog.csdnimg.cn/20201005152933443.png
[g2]: https://img-blog.csdnimg.cn/20201005153107550.png

[b1]: https://i-blog.csdnimg.cn/direct/1a68a50684ef4151a7c6b7442599f295.png
[b2]: https://i-blog.csdnimg.cn/direct/56fb96fdf46a42b8ad5a79367df0b78f.png
[b3]: https://i-blog.csdnimg.cn/direct/6de69105b83c4a9197cbad513ed4fe94.png
[b4]: https://i-blog.csdnimg.cn/direct/58a77ec9f9a54e86871b5325ab5f1333.png

[extract]: https://img-blog.csdnimg.cn/direct/b7dbfbe6b3744b56a6d66079db8ebbb8.png
46 changes: 46 additions & 0 deletions binary_ninja/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Load

File -> Run Script


# Logging

```
[+] plugin start, bv=<BinaryView: '/root/jni_helper/assets/lib/arm64-v8a/libdemoc.so', len 0x2a88>
[+] init_header done.
[+] fix 0x3708 JNI_OnLoad -> jint(JavaVM* vm, void* reserved)
[+] fix 0x4980 JNI_OnUnload -> void(JavaVM* vm, void* reserved)
[+] fix 0x5388 Java_com_evilpan_demoapk_FacadeC_testStatic -> jint(JNIEnv* env, jclass clazz, jint a1)
[+] fix 0x5044 Java_com_evilpan_demoapk_FacadeC_stringFromJNI -> jstring(JNIEnv* env, jobject thiz)
[+] fix 0x5916 Java_com_evilpan_demoapk_FacadeC_testArray -> void(JNIEnv* env, jobject thiz, jintArray a1)
[+] fix 0x5468 Java_com_evilpan_demoapk_FacadeC_testClass -> jint(JNIEnv* env, jobject thiz, jobject a1)
[+] fix 0x5144 Java_com_evilpan_demoapk_FacadeC_testOverload__ -> jint(JNIEnv* env, jobject thiz)
[+] fix 0x5220 Java_com_evilpan_demoapk_FacadeC_testOverload__I -> jint(JNIEnv* env, jobject thiz, jint a1)
[+] fix 0x5300 Java_com_evilpan_demoapk_FacadeC_testOverload__JFD -> jint(JNIEnv* env, jobject thiz, jlong a1, jfloat a2, jdouble a3)
```

High Level IL:

Before =>

![b1][b1]

After =>

![b3][b3]

Pseudo C:

Before =>

![b2][b2]

After =>

![b4][b4]


[b1]: https://i-blog.csdnimg.cn/direct/1a68a50684ef4151a7c6b7442599f295.png
[b2]: https://i-blog.csdnimg.cn/direct/56fb96fdf46a42b8ad5a79367df0b78f.png
[b3]: https://i-blog.csdnimg.cn/direct/6de69105b83c4a9197cbad513ed4fe94.png
[b4]: https://i-blog.csdnimg.cn/direct/58a77ec9f9a54e86871b5325ab5f1333.png
96 changes: 96 additions & 0 deletions binary_ninja/jni_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import json
import os
import binaryninja
from binaryninja.interaction import (
OpenFileNameField,
get_form_input,
show_message_box,
)

def log(msg, *args, **kwargs):
print("[+]", msg, *args, **kwargs)

class JNIHelper:

def __init__(self):
self.jni_header = ""

def start(self):
if not self.init_header():
return
self.apply_signatures()

def init_header(self):
options = ["-fdeclspec"]
jni_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "headers", "jni.h")
if not os.path.exists(jni_file):
jni_file = self.choose_file("jni.h not found, choose one")
if jni_file is None:
return
with open(jni_file, 'r') as f:
self.jni_header = f.read()
pr = self.parse_source(self.jni_header, "jni.h")
if not pr:
return False
log("init_header done.")
return True

def parse_source(self, source, name="<source>"):
options = ["-fdeclspec"]
result, errors = TypeParser.default.parse_types_from_source(
source, name, bv.platform,
options=options
)
if result is None:
log("parse error:")
for err in errors:
log(err, end='')
return None
return result

def choose_file(self, desc, title="File"):
fd = OpenFileNameField(desc)
if get_form_input([fd], title):
return fd.result
return None

def apply_signatures(self):
file = self.choose_file("signature.json from extract_jni.py")
if not file:
return
with open(file, 'r') as f:
meta = json.load(f)
jni_ext = self.jni_header + "\n"
func_map = {}
for cls, methods in meta["dexInfo"].items():
for method in methods:
mangle = method["mangle"]
found = bv.get_functions_by_name(mangle)
if not found:
continue
func = found[0]
func_map[mangle] = func
# skip those already defined
ret = method["ret"]
args = ",".join(method["args"])
line = f"{ret} {mangle}({args})"
if cls == "__COMMON__":
continue
jni_ext += line + ";\n"
pr = self.parse_source(jni_ext, "jni_ext.h")
if pr is None:
return
for pt in pr.types:
bv.define_user_type(pt.name, pt.type)
for pf in pr.functions:
if pf.name not in func_map:
continue
func = func_map[pf.name]
log(f"fix 0x{func.start} {pf.name} -> {pf.type}")
func.type = pf.type
func.reanalyze()


log(f"plugin start, bv={bv}")
jh = JNIHelper()
jh.start()
2 changes: 1 addition & 1 deletion extract_jni.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def native_args(self):
if self.static:
native_args_list.append(("jclass", "clazz"))
else:
native_args_list.append(("jobject", "this"))
native_args_list.append(("jobject", "thiz"))
return native_args_list + [
(get_type(arg), f"a{i + 1}") for i, arg in enumerate(self.args)
]
Expand Down
16 changes: 16 additions & 0 deletions headers/jni.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall

// tricks for binary ninja
// -fdeclspec -x c -std=c99
#ifndef __int64
typedef int64_t __int64;
#endif
#ifndef va_list
typedef char* va_list;
#endif
#ifndef va_start
#define va_start(ap, last) (ap = (va_list)&last + sizeof(last))
#endif
#ifndef va_end
#define va_end(ap) (ap = (va_list)0)
#endif


typedef long jint;
typedef __int64 jlong;
typedef signed char jbyte;
Expand Down

0 comments on commit 7a02176

Please sign in to comment.