Skip to content

Commit 67ee6ec

Browse files
committed
Add IDA plugin
1 parent 107ebb9 commit 67ee6ec

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

ida/README.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,67 @@
1-
# TODO
1+
# JNI Helper for IDA
2+
3+
Load JNI function signatures from JSON file and apply to IDA-Pro
4+
5+
# Install
6+
7+
macOS:
8+
```sh
9+
cp jni_helper.py $IDA_HOME/ida.app/Contents/MacOS/plugins
10+
```
11+
12+
> e.x. `/Applications/IDA Pro 6.9/idaq.app/Contents/MacOS/plugins/`
13+
14+
Windows:
15+
```
16+
cp jni_helper.py %IDA_HOME%\plugins
17+
```
18+
19+
> e.x. `C:\Program Files (x86)\IDA 6.9\plugins`
20+
21+
# Load
22+
23+
```
24+
Edit -> Plugins -> JNI Helper
25+
```
26+
27+
Logging:
28+
```
29+
[+] plugin init
30+
[+] plugin run
31+
[+] loading signature file: /Users/root/app-debug.json
32+
[+] loaded 14 methods from JSON
33+
[+] apply 0x9f8 JNI_OnLoad
34+
[+] apply 0xa90 JNI_OnUnload
35+
[+] apply 0xabc Java_com_evilpan_demojni_MainActivity_c_1stringFromJNI
36+
[+] apply 0xae4 Java_com_evilpan_demojni_MainActivity_c_1testOverload__
37+
[+] apply 0xb70 Java_com_evilpan_demojni_MainActivity_c_1testOverload__I
38+
[+] apply 0xca8 Java_com_evilpan_demojni_MainActivity_c_1testOverload__JFD
39+
[+] apply 0xdb0 Java_com_evilpan_demojni_MainActivity_c_1testStatic
40+
[+] apply 0xe78 Java_com_evilpan_demojni_MainActivity_c_1testClass
41+
[+] apply 0xf98 Java_com_evilpan_demojni_MainActivity_c_1testArray
42+
```
43+
44+
Before:
45+
46+
![1][1]
47+
48+
After:
49+
50+
![2][2]
51+
52+
# Links
53+
54+
- [hex-rays/idapython_docs][doc]
55+
- [hex-rays/sdkdoc][sdk]
56+
- [hex-rays/ida74_idapython_no_bc695_porting_guide][port]
57+
- [IDAPython cheatsheet][snip]
58+
59+
[doc]: https://www.hex-rays.com/products/ida/support/idapython_docs/
60+
[sdk]: https://www.hex-rays.com/products/ida/support/sdkdoc/index.html
61+
[port]: https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml
62+
[flare]: https://github.com/fireeye/flare-ida
63+
[snip]: https://gist.github.com/icecr4ck/7a7af3277787c794c66965517199fc9c
64+
[pal]: https://unit42.paloaltonetworks.com/using-idapython-to-make-your-life-easier-part-1/
65+
66+
[1]: https://img-blog.csdnimg.cn/20201005164101129.png
67+
[2]: https://img-blog.csdnimg.cn/20201005164352403.png

ida/jni_helper.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#---------------------------------------------------------------------
2+
# jni_helper.py - IDA JNI Helper plugin
3+
#---------------------------------------------------------------------
4+
"""
5+
Plugin for IDA to apply JSON signature to JNI functions.
6+
"""
7+
8+
import ida_typeinf
9+
import idautils
10+
import idaapi
11+
import idc
12+
13+
import os
14+
import sys
15+
import json
16+
17+
def log(fmt, *args):
18+
print "[+]", fmt % args
19+
20+
21+
def load_methods():
22+
sigfile = idc.AskFile(0, "*.json", "Select json signature file")
23+
log("loading signature file: %s", sigfile)
24+
25+
with open(sigfile, 'r') as f:
26+
infos = json.load(f)
27+
28+
log("loaded %d methods from JSON", len(infos))
29+
return infos
30+
31+
32+
def is_jni_header_loaded():
33+
# not work as expected:
34+
# return idaapi.get_struc_id('JNIInvokeInterface_') != idaapi.BADADDR
35+
try:
36+
idc.parse_decl('JNIEnv *env', idc.PT_SILENT)
37+
except Exception as e:
38+
return False
39+
return True
40+
41+
42+
def load_jni_header():
43+
jni_h = idc.AskFile(0, "*.h", "Select JNI header file")
44+
idaapi.idc_parse_types(jni_h, idc.PT_FILE)
45+
46+
47+
def apply_signature(ea, info):
48+
name = idc.GetFunctionName(ea)
49+
if info is None:
50+
log('WARN: no info found for %s', name)
51+
return
52+
log('apply 0x%x %s', ea, name)
53+
decl = '{} {}(JNIEnv* env, '.format(info['returnType'], name)
54+
if info['isStatic']:
55+
decl += 'jclass clazz'
56+
else:
57+
decl += 'jobject thiz'
58+
for idx, atype in enumerate(info['argumentTypes']):
59+
decl += ', {} arg{}'.format(atype, idx + 1)
60+
decl += ')'
61+
# log(decl)
62+
prototype_details = idc.parse_decl(decl, idc.PT_SILENT)
63+
# idc.set_name(ea, name)
64+
idc.apply_type(ea, prototype_details)
65+
66+
67+
def apply_load_unload(ea, load=True):
68+
name = idc.GetFunctionName(ea)
69+
log('apply 0x%x %s', ea, name)
70+
decl = "{} {}(JavaVM *vm, void *reserved)".format(
71+
"jint" if load else "void",
72+
"JNI_OnLoad" if load else "JNI_OnUnload"
73+
)
74+
prototype_details = idc.parse_decl(decl, idc.PT_SILENT)
75+
idc.apply_type(ea, prototype_details)
76+
77+
78+
class JNIHelperPlugin(idaapi.plugin_t):
79+
"""
80+
JNI Helper plugin class
81+
"""
82+
flags = 0
83+
comment = "Import JSON Signature file"
84+
help = "Apply JSON Signature to JNI functions"
85+
wanted_name = "JNI Helper"
86+
wanted_hotkey = "Ctrl-Alt-j"
87+
88+
def init(self):
89+
log("plugin init")
90+
return idaapi.PLUGIN_OK
91+
92+
def run(self, arg):
93+
"""
94+
:param arg: Integer, a non-zero value enables auto-run feature for
95+
IDA batch (no gui) processing mode. Default is 0.
96+
"""
97+
log("plugin run")
98+
if not is_jni_header_loaded():
99+
idaapi.warning('Please load jni.h first')
100+
load_jni_header()
101+
st = idc.set_ida_state(idc.IDA_STATUS_WORK)
102+
infos = load_methods()
103+
failed = []
104+
succ = 0
105+
for ea in idautils.Functions():
106+
fname = idc.GetFunctionName(ea)
107+
if fname.startswith('Java_'):
108+
info = infos.get(fname)
109+
if info is None:
110+
failed.append(name)
111+
else:
112+
succ += 1
113+
apply_signature(ea, info)
114+
if fname == 'JNI_OnLoad':
115+
apply_load_unload(ea, True)
116+
succ += 1
117+
if fname == 'JNI_OnUnload':
118+
apply_load_unload(ea, False)
119+
succ += 1
120+
idaapi.info('JNI functions loaded, {} success. {} failed. \n{}'.format(
121+
succ,
122+
len(failed),
123+
'\n'.join(failed)
124+
))
125+
idc.set_ida_state(st)
126+
127+
def term(self):
128+
log("plugin term")
129+
130+
131+
def PLUGIN_ENTRY():
132+
return JNIHelperPlugin()

0 commit comments

Comments
 (0)