#?description=Decompile a file provided to JEB
#?shortcut=
import getopt
import os
import sys
import time
from com.pnfsoftware.jeb.util.io import IO
from com.pnfsoftware.jeb.util.logging import GlobalLog
from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext
from com.pnfsoftware.jeb.core.units import INativeCodeUnit, UnitUtil
from com.pnfsoftware.jeb.core.units.code import ICodeUnit, ICodeItem
from com.pnfsoftware.jeb.core.output.text import ITextDocument
from com.pnfsoftware.jeb.core.util import DecompilerHelper
from com.pnfsoftware.jeb.core.units.code.asm.decompiler import INativeSourceUnit
from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, DexDecompilerExporter
from com.pnfsoftware.jeb.core.output.text import TextDocumentUtil
"""
Sample script for JEB Decompiler.
- This script decompiles some or all code units of the project
- If run on the command line, the provided input file will be analyzed
- It makes use of the 'decompiler exporter' objects to provide fast decompilation.
How to run & options: see usage() below.
For additional details, refer to:
https://www.pnfsoftware.com/jeb/manual/faq/#can-i-execute-a-jeb-python-script-from-the-command-line
"""
class DecompileFile:
# IMPORTANT NOTE (JEB >=5.40): Notice that this script class does not inherit IScript!
#
# to allow running the script with "Java Embedded Python" instead of Jython
# - advantage: you get to use your cpython (modern Python 3)
# - drawback: you cannot create sub-classes of JEB types
#
# if your script requires the creation of sub-types of JEB types, you have two options:
# - have your script extend IScript and revert to using Jython
# - or write a full-blown plugin in Java
#
# The same thing applies to the majority of the sample scripts in this repository.
def usage(self):
print('''DecompileFile.py -- demo how to use JEB to decompile code for automation purposes.
How to run:
$ -c --script=DecompileFile.py -- [options] INPUT_FILE OUTPUT_DIR
Options:
-h : display help and exit
-v : increase verbosity
--mtimeout=T : decompilation timeout for individual methods, in seconds
--decompile=L : comma-separated list of targets to decompile, e.g. 'dalvik,arm64'
Supported targets:
dalvik : decompile x86 32-bit code
x86 : decompile x86 32-bit code
x64 : decompile x86 64-bit code
arm : decompile Arm code
arm64 : decompile Arm 64-bit (aarch64) code
all : decompile all code decompilable by your JEB instance
''')
return
def run(self, ctx):
# for messages issued by JEB, limit ourselves to warn and above
GlobalLog.setCutoffLevel(GlobalLog.LEVEL_WARN);
self.ctx = ctx
self.verbose = False
self.inputFile = None
self.inputDir = None
self.method_timeout = -1
self.decomp_dalvik = False
self.decomp_x86 = False
self.decomp_x64 = False
self.decomp_arm = False
self.decomp_arm64 = False
self.decomp_all = False
args = ctx.getArguments()
iarg = 0
for arg in args:
if not arg.startswith('-'):
break
if arg == '-h':
self.usage()
return
elif arg == '-v':
self.verbose = True
elif arg.startswith('--mtimeout='):
self.method_timeout = int(arg[11:])
elif arg.startswith('--decompile='):
for elt in arg[12:].split(','):
elt = elt.strip().lower()
if elt in ('dalvik', 'dex'):
self.decomp_dalvik = True
elif elt == 'x86':
self.decomp_x86 = True
elif elt in ('x64', 'x86_64', 'x86-64', 'amd64'):
self.decomp_x64 = True
elif elt == 'arm':
self.decomp_arm = True
elif elt in ('arm64', 'aarch64'):
self.decomp_x64 = True
elif elt == 'all':
self.decomp_all = True
else:
print('Unknown processor for decompilation: %s' % elt)
self.usage()
return
else:
print('Unknown option: %s' % arg)
self.usage()
return
iarg += 1
if iarg >= len(args):
print('Provide an input file')
return
self.inputFile = args[iarg]
iarg += 1
if iarg >= len(args):
print('Provide the output folder')
return
self.outputDir = args[iarg]
iarg += 1
if self.method_timeout: print('Method decompilation timeout: %ds' % self.method_timeout)
if self.decomp_all: print('Decompiling all')
if self.decomp_dalvik: print('Decompiling Dalvik')
if self.decomp_x86: print('Decompiling x86')
if self.decomp_x64: print('Decompiling x64')
if self.decomp_arm: print('Decompiling Arm')
if self.decomp_arm64: print('Decompiling Arm64')
print('Processing file: %s...' % self.inputFile)
ctx.open(self.inputFile)
prj = ctx.getMainProject()
assert prj, 'Need a project'
t0 = time.time()
for codeUnit in prj.findUnits(ICodeUnit):
self.decompileCodeUnit(codeUnit)
exectime = time.time() - t0
print('Exectime: %f' % exectime)
def decompileCodeUnit(self, codeUnit):
upath = UnitUtil.buildFullyQualifiedUnitPath(codeUnit)
proceed = False
if self.decomp_all:
proceed = True
elif isinstance(codeUnit, IDexUnit) and self.decomp_dalvik:
proceed = True
elif isinstance(codeUnit, INativeCodeUnit):
procname = codeUnit.getProcessorName()
if procname == 'x86' and self.decomp_x86:
proceed = True
elif procname == 'x86_64' and self.decomp_x64:
proceed = True
elif procname == 'arm' and self.decomp_arm:
proceed = True
elif procname == 'arm64' and self.decomp_arm64:
proceed = True
if not proceed:
print('Skipping code: filtered out: %s' % upath)
return
# make sure the code unit is processed
if not codeUnit.isProcessed():
if not codeUnit.process():
print('Skipping code: cannot be processed: %s' % upath)
return
decomp = DecompilerHelper.getDecompiler(codeUnit)
if not decomp:
print('Skipping code: no decompiler available: %s' % upath)
return
print('Decompiling code: %s' % upath)
outdir = os.path.join(self.outputDir, UnitUtil.buildFullyQualifiedUnitPath(codeUnit, True, '_').replace(' ', '-'))
print('Output folder: %s' % outdir) # created only if necessary, i.e. some contents was exported
# DecompilerExporter object
exp = decomp.getExporter()
exp.setOutputFolder(IO.createFolder(outdir))
if self.method_timeout > 0:
exp.setMethodTimeout(self.method_timeout * 1000)
#if ...
# exp.setTotalTimeout(...)
if self.verbose:
exp.setCallback(DecompilerHelper.createProgressLogger())
# decompile & export
if not exp.export():
cnt = len(exp.getErrors())
i = 1
for sig, err in exp.getErrors().items():
print('%d/%d DECOMPILATION ERROR: METHOD %s: %s' % (i, cnt, sig, err))
i += 1