Skip to content

Commit 50ae9a9

Browse files
authored
JEB Java method hook generation
1 parent a6eaabc commit 50ae9a9

File tree

1 file changed

+161
-0
lines changed

1 file changed

+161
-0
lines changed

README.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,167 @@ ab fridadescribe console.log(Object.getOwnPropertyNames(Java.use('$').__proto__)
113113

114114
</details>
115115

116+
117+
<details>
118+
<summary>JEB</summary>
119+
120+
Place cursor at Java method's signature and press `Ctrl+Shift+F`, it will copy the code to system clipboard using `xclip`.
121+
122+
* Add to `~/$JEB$/scripts`
123+
124+
125+
```python
126+
#?shortcut=Mod1+Shift+F
127+
#!/usr/bin/env python3
128+
# -*- coding: utf-8 -*-
129+
130+
from com.pnfsoftware.jeb.client.api import IScript
131+
from com.pnfsoftware.jeb.core import RuntimeProjectUtil
132+
from com.pnfsoftware.jeb.core.units.code.android import IDexUnit
133+
from subprocess import Popen, PIPE
134+
135+
136+
def arg_format(i):
137+
return 'arg_%d' % i
138+
139+
140+
def generate_body_code(types, retval, method_name, orig_method_name, class_name):
141+
body_code = "\n\tconsole.log('[{}#{}] ' + JSON.strigify({{\n\t".format(
142+
FridaCodeGenerator.to_canonical_name(class_name), method_name)
143+
for i, typ in enumerate(types):
144+
body_code += '\t{}: {}, // {}\n\t'.format('a%d' % i, arg_format(i), typ)
145+
146+
if retval != 'void':
147+
body_code = '\n\tvar retval = this.{}.apply(this, arguments);{}\tretv: retval\n\t}});'.format(
148+
orig_method_name, body_code)
149+
else:
150+
body_code += '}});\n\tthis.{}.apply(this, arguments);'.format(method_name)
151+
152+
return body_code + '\n'
153+
154+
155+
class JavaMethod(object):
156+
def __init__(self):
157+
self.class_name = None
158+
self.class_orig_name = None
159+
self.name = None
160+
self.orig_name = None
161+
self.arg = []
162+
self.retType = None
163+
164+
def get_parameters(self):
165+
return self.arg
166+
167+
def get_return_type(self):
168+
return self.retType
169+
170+
def get_name(self):
171+
return self.name
172+
173+
def get_orig_name(self):
174+
return self.orig_name
175+
176+
def get_class_orig_name(self):
177+
return self.class_orig_name
178+
179+
def get_class_name(self):
180+
return self.class_name
181+
182+
def __str__(self):
183+
return 'JavaMethod[name: %s, orig_name: %s, args: %s, return type: %s]' % (
184+
self.name, self.orig_name, self.arg, self.retType)
185+
186+
187+
class FridaCodeGenerator(IScript):
188+
189+
@staticmethod
190+
def to_canonical_name(mname):
191+
mname = mname.replace('/', '.')
192+
return {
193+
'C': 'char',
194+
'I': 'int',
195+
'B': 'byte',
196+
'Z': 'boolean',
197+
'F': 'float',
198+
'D': 'double',
199+
'S': 'short',
200+
'J': 'long',
201+
'V': 'void',
202+
'L': mname[1:-1],
203+
'[': mname
204+
}[mname[0]]
205+
206+
def run(self, ctx):
207+
project = ctx.getEnginesContext().getProjects()[0] # Get current project(IRuntimeProject)
208+
self.dexunit = RuntimeProjectUtil.findUnitsByType(project, IDexUnit, False)[0] # Get dex context, needs >=V2.2.1
209+
try:
210+
self.current_unit = ctx.getFocusedView().getActiveFragment().getUnit() # Get current Source Tab in Focus
211+
java_class = self.current_unit.getClassElement().getName()
212+
current_addr = ctx.getFocusedView().getActiveFragment().getActiveAddress()
213+
m = FridaCodeGenerator.get_decompiled_method(self.dexunit, current_addr, java_class)
214+
method_name = m.get_name()
215+
class_name = FridaCodeGenerator.to_canonical_name(m.get_class_orig_name())
216+
return_type = FridaCodeGenerator.to_canonical_name(str(m.get_return_type()))
217+
if method_name == '<clinit>':
218+
raise Exception('Class initializer')
219+
args_code = ', '.join([arg_format(i) for i in range(len(m.get_parameters()))])
220+
221+
if method_name == '<init>': method_name = '$init'
222+
223+
types = [FridaCodeGenerator.to_canonical_name(param) for param in m.get_parameters()]
224+
# TODO get original type class names
225+
type_code = ', '.join(["'{0}'".format(t) for t in types])
226+
body_code = generate_body_code(types, return_type, method_name, m.get_orig_name(), m.get_class_name())
227+
hook = "Java.use('{class_name}').{method}.overload({sig}).implementation = function({args}) {{{body}}}".format(
228+
class_name=class_name,
229+
method=m.get_orig_name() if method_name != '$init' else method_name,
230+
sig=type_code,
231+
args=args_code,
232+
body=body_code
233+
)
234+
print(hook)
235+
# copy to system's clipboard
236+
Popen(['xclip', '-sel', 'c', '-i'], stdin=PIPE).communicate(input=(hook.encode()))
237+
except Exception as e:
238+
print(e)
239+
ctx.displayMessageBox(None, 'Place the cursor in the function you want to generate the Frida code', None, None)
240+
241+
@staticmethod
242+
def get_decompiled_method(dex, addr, class_orig_name):
243+
method_info = JavaMethod()
244+
method_info.orig_name = dex.getMethod(addr).getName(False)
245+
msig = addr.split('+')[0]
246+
infos = str(msig).split('->')
247+
if len(infos) == 2:
248+
method_info.class_name = infos[0]
249+
method_info.class_orig_name = class_orig_name
250+
if len(infos[1].split('(')) == 2:
251+
method_info.name = infos[1].split('(')[0]
252+
if len(infos[1].split(')')) == 2:
253+
method_info.retType = infos[1].split(')')[1]
254+
if len(infos[1].split('(')) == 2 and len(infos[1].split(')')) == 2:
255+
args = infos[1].split('(')[-1].split(')')[0]
256+
while args:
257+
if args[0] in ['C', 'I', 'B', 'Z', 'F', 'D', 'S', 'J', 'V']:
258+
method_info.arg.append(str(args[0]))
259+
args = args[1:]
260+
elif args[0] == '[':
261+
if args[1] == 'L':
262+
offset = args.find(';')
263+
method_info.arg.append(str(args[0:offset + 1]))
264+
args = args[offset + 1:]
265+
else:
266+
method_info.arg.append(str(args[0:2]))
267+
args = args[2:]
268+
elif args[0] == 'L':
269+
offset = args.find(";")
270+
method_info.arg.append(str(args[0:offset + 1]))
271+
args = args[offset + 1:]
272+
print(method_info)
273+
return method_info
274+
```
275+
276+
</details>
116277
<hr />
117278

118279
#### Load CPP module

0 commit comments

Comments
 (0)