├── .gitignore ├── README.md ├── build.xml ├── client ├── README.md ├── create-eclipse-project-linux.sh ├── create-eclipse-project-windows.cmd ├── scripts │ └── generateEclipseProject.py └── src │ ├── JebClient.java │ └── JebUtil.java ├── plugins ├── README.md ├── scripts │ ├── DOptAdhocDESDecryption.java │ ├── DOptArrayInitCleanup.java │ ├── DOptCleanCondExpr.java │ ├── DOptCleanDoubleSwitchOnSameVar.java │ ├── DOptEmuHooksExample.py │ ├── DOptEmuHooksGlobalExample.java │ ├── DOptEmuSandboxHooksExample.py │ ├── DOptEvalFolder.java │ ├── DOptRemoveNops.java │ ├── DOptSampleJava.java │ ├── DOptSamplePython.py │ ├── DOptStringSimplifier.java │ ├── DOptUnkProtObfuscatedStaticArrays.java │ ├── DOptUnsafeApkSpecificSubst.java │ ├── DOptUnsafeArrayAccessSubst.java │ ├── GarbageCleaner.java │ ├── JOptSampleJava.java │ ├── JOptSamplePython.py │ ├── README.md │ └── RemoveDummyAndroidApiCalls.py └── src │ └── com │ └── pnf │ ├── jebauto │ ├── AutoClient.java │ └── AutoUtil.java │ ├── pdfscan │ └── PDFScanner.java │ ├── plugintest │ └── SampleEnginesPlugin.java │ ├── pommePlugin │ ├── MetaUnit.java │ ├── PommePlugin.java │ ├── PommeTask.java │ └── functionList.json │ └── vtplugin │ └── VirustotalReportPlugin.java └── scripts ├── .gitignore ├── AdbDemo.py ├── AddArtifact.py ├── AddCustomNativeTypes.py ├── AndroidDbgAddAllDex.py ├── AndroidDbgCreate.py ├── AndroidGlobalAnalysis.py ├── AndroidXrefResId.py ├── ApkManifestView.py ├── AutorunTest1.py.DISABLED ├── AutorunTest2.py.DISABLED ├── BookmarkList.py ├── BookmarkSet.py ├── BreakpointsLoad.py ├── BreakpointsSave.py ├── CodeLoad.py ├── CodeSave.py ├── CreateEmptyClass.py ├── CreateExtraDocument.py ├── CreateExtraDocumentTableTree.py ├── CreateNativeStruct.py ├── CustomizeMetadata.py ├── CustomizerExport.py ├── CustomizerImport.py ├── DalvikDataFlow.py ├── DecompileFile.py ├── DecompileSingleMethod.py ├── DexClassRefactor.py ├── DexColorArtificialStrings.py ├── DexColorPackage.py ├── DexJumpToActivity.py ├── DexJumpToFileOffset.py ├── DexJumpToResource.py ├── DexListDecompilationEvents.py ├── DexListMethods.py ├── DexdecDisableEmulation.py ├── DumpBinaryUnits.py ├── EditNativeBytes.py ├── FindMain.py ├── FocusDisassembly.py ├── GUIActionScriptDemo.py ├── GUIActionScriptSkeleton.py ├── GenerateFridaSnippetForDex.py ├── GraphDemo1.py ├── GraphDemo2.py ├── GraphPackagesRelationships.py ├── GraphRtti.py ├── JavaASTCreateMethodRef.py ├── JavaASTDecryptStrings.py ├── JavaASTDemo.py ├── JavaASTTags.py ├── JavaListIdentifiers.py ├── JavaRenameField1.py ├── JavaRenameField2.py ├── JumpTo.py ├── JumpToAndroidComponent.py ├── ListCrossReferences.py ├── ListDexMethodsWithXor.py ├── ListOverrides.py ├── ListRenamedCodeItems.py ├── ListUnits.py ├── ListenToDexChangeEvents.py ├── PdfListStreams.py ├── PrintNativeRoutineIR.py ├── PrintOutDartInfo.py ├── ProcessFile.py ├── README.md ├── ReloadNativeTypelibsAndSiglibs.py ├── RenameDexClassesToDebugNames.py ├── RenameJavaMethodParameters.py ├── ReplaceStringsInDex.py ├── ReplaceStringsInJavaAST.py ├── RequestUserInput.py ├── RequestUserInputWithComplexForm.py ├── RequestUserInputWithForm.py ├── RestoreMissingBookmarksFromComments.py ├── SampleScript.py ├── SampleScriptDebugging.py ├── SearchAll.py ├── TaskInWorkerThread.py ├── TaskWithReturnInWorkerThread.py ├── TranslateString.py ├── UIDemo.py ├── WalkEvmDecomp.py ├── WidgetList.py ├── analysis ├── TriadaStringDecryptor.py ├── TriadaStringDecryptorVer0.py └── WhatsAppStringDecryptor.py ├── cluster ├── DexCluster.py └── cluster.py └── jebio └── jebio.py /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | libs 3 | temp 4 | .classpath 5 | .project -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample code for [JEB Decompiler](https://www.pnfsoftware.com). 2 | 3 | ## Script Development Recommendations 4 | 5 | JEB Client extensions (*scripts*) should be written in Python. (We may add support for scripts written in Java in the future.) 6 | 7 | Feel free to use the built-in editor in JEB (hotkey F2; then, Edit or Create a Script...) 8 | 9 | If you are using Sublime Text 3, we published a ST3 extension to make JEB script writing easier. 10 | Install the `jeb_scriptdev_helper` package using PackageControl or by cloning [that repository](https://github.com/pnfsoftware/jeb_scriptdev_helper) into your Sublime's 'Packages' folder: 11 | 12 | - OS X: ~/Library/Application Support/Sublime Text 3/Packages/ 13 | - Windows: %APPDATA%/Roaming/Sublime Text 3/Packages/ 14 | - Linux: ~/.config/sublime-text-3/Packages/ 15 | 16 | ## Plugin Development Recommendations 17 | 18 | JEB Back-end extensions (*plugins*) should be written in Java. (Some classes of back-end plugins may be written in Python.) 19 | 20 | We recommend using Eclipse IDE, although you may use any code editor. If you are using Eclipse, clone [that repository](https://github.com/pnfsoftware/jeb-template-plugin) and follow the README instructions to create an empty plugin skeleton with tester code as well as the accompanying project (to be imported in Eclipse) with integrated JEB API documentation. 21 | 22 | ## Resources 23 | 24 | - [Developer Portal](https://www.pnfsoftware.com/jeb/devportal) 25 | - [API Reference](https://www.pnfsoftware.com/jeb/apidoc) 26 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Skeleton for a JEB headless client. 2 | 3 | Fork to use in automation and file processing pipelines. 4 | 5 | How to use: 6 | 7 | - Define a `JEB_HOME` environment variable pointing to the JEB installation folder. 8 | - Generate an Eclipse project by running the appropriate `create-eclipse-project-windows` script. 9 | - Load the project in Eclipse. Customize `JebClient` to your needs. 10 | -------------------------------------------------------------------------------- /client/create-eclipse-project-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | python scripts/generateEclipseProject.py $1 -------------------------------------------------------------------------------- /client/create-eclipse-project-windows.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | python scripts\generateEclipseProject.py %1 3 | -------------------------------------------------------------------------------- /client/scripts/generateEclipseProject.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | tplProject = ''' 5 | 6 | %s 7 | 8 | 9 | 10 | 11 | 12 | org.eclipse.jdt.core.javabuilder 13 | 14 | 15 | 16 | 17 | 18 | org.eclipse.jdt.core.javanature 19 | 20 | 21 | ''' 22 | 23 | tplClasspath = ''' 24 | 25 | 26 | 27 | %s 28 | 29 | 30 | ''' 31 | 32 | if __name__ == '__main__': 33 | prjname = os.path.split(os.path.abspath(os.path.dirname(sys.argv[0]) + '/..'))[-1] 34 | print('Project name: %s' % prjname) 35 | 36 | internal = len(sys.argv) > 1 and sys.argv[1] == '-i' 37 | 38 | if 'JEB_HOME' not in os.environ: 39 | print('Set an environment variable JEB_HOME pointing to your JEB folder') 40 | sys.exit(-1) 41 | 42 | jebhome = os.environ['JEB_HOME'] 43 | jebcorepath = os.path.join(jebhome, 'bin/app/jeb.jar') 44 | if not os.path.isfile(jebcorepath): 45 | print('Based on your value of JEB_HOME, jeb.jar was expected at this location, but it was not found: %s' % jebcorepath) 46 | sys.exit(-1) 47 | jebdocpath = os.path.join(jebhome, 'doc/apidoc.zip') 48 | 49 | _Project = tplProject % prjname 50 | with open('.project', 'w') as f: 51 | f.write(_Project) 52 | print('Generated: Eclipse .project file') 53 | 54 | jeblibentry = ''' 55 | 56 | ''' % (jebcorepath, jebdocpath) 57 | if internal: 58 | # FOR INTERNAL USE 59 | jeblibentry = '' 60 | _Classpath = tplClasspath % jeblibentry 61 | with open('.classpath', 'w') as f: 62 | f.write(_Classpath) 63 | print('Generated: Eclipse .classpath file') -------------------------------------------------------------------------------- /client/src/JebUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JEB Copyright PNF Software, Inc. 3 | * 4 | * https://www.pnfsoftware.com 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import org.apache.commons.configuration2.PropertiesConfiguration; 24 | import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; 25 | import org.apache.commons.configuration2.builder.fluent.Parameters; 26 | import org.apache.commons.configuration2.ex.ConfigurationException; 27 | 28 | import com.pnfsoftware.jeb.core.properties.IConfiguration; 29 | import com.pnfsoftware.jeb.core.properties.impl.CommonsConfigurationWrapper; 30 | 31 | /** 32 | * Some utility code used by {@link JebClient}. 33 | * 34 | * @author Nicolas Falliere 35 | * 36 | */ 37 | public class JebUtil { 38 | 39 | /** 40 | * Create a configuration object for a JEB configuration file, such as {@code jeb-engines.cfg}. 41 | * 42 | * @param file 43 | * @return 44 | */ 45 | public static IConfiguration getConfiguration(File file) { 46 | if(!file.isFile()) { 47 | try { 48 | file.createNewFile(); 49 | } 50 | catch(IOException e) { 51 | throw new RuntimeException(e); 52 | } 53 | } 54 | Parameters params = new Parameters(); 55 | FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>( 56 | PropertiesConfiguration.class).configure(params.properties().setFile(file)); 57 | builder.setAutoSave(true); 58 | try { 59 | return new CommonsConfigurationWrapper(builder.getConfiguration()); 60 | } 61 | catch(ConfigurationException e) { 62 | throw new RuntimeException(e); 63 | } 64 | } 65 | 66 | /** 67 | * List files recursively. 68 | * 69 | * @param location root directory 70 | * @return a list of files 71 | */ 72 | public static List retrieveFiles(String location) { 73 | List results = new ArrayList<>(); 74 | retrieveFilesRecurse(new File(location), results); 75 | return results; 76 | } 77 | 78 | private static void retrieveFilesRecurse(File f, List results) { 79 | if(f.isFile()) { 80 | results.add(f); 81 | } 82 | else { 83 | for(String name: f.list()) { 84 | File f1 = new File(f, name); 85 | retrieveFilesRecurse(f1, results); 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | Sample back-end plugins for JEB. 2 | 3 | Back-end plugins go in the `coreplugins/` folder. 4 | -------------------------------------------------------------------------------- /plugins/scripts/DOptAdhocDESDecryption.java: -------------------------------------------------------------------------------- 1 | import java.util.List; 2 | 3 | import javax.crypto.SecretKey; 4 | import javax.crypto.SecretKeyFactory; 5 | import javax.crypto.spec.DESKeySpec; 6 | import javax.crypto.spec.IvParameterSpec; 7 | 8 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 9 | import com.pnfsoftware.jeb.core.units.code.android.ir.DOptimizerType; 10 | import com.pnfsoftware.jeb.core.units.code.android.ir.DexDecEvaluationException; 11 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDImm; 12 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDState; 13 | import com.pnfsoftware.jeb.util.base.Wrapper; 14 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 15 | import com.pnfsoftware.jeb.util.logging.ILogger; 16 | 17 | /** 18 | * Decryption helper for e4596c10ea8168ea00bf2f91398e441eb6f7090eab7e49913b175527726c2513 19 | * 20 | * @author Nicolas Falliere 21 | * 22 | */ 23 | public class DOptAdhocDESDecryption extends AbstractDOptimizer { 24 | 25 | public DOptAdhocDESDecryption() { 26 | super(DOptimizerType.UNSAFE); 27 | setPriority(10); // run this early on 28 | } 29 | 30 | @Override 31 | public int perform() { 32 | String appsig = "Lllll/llll/llll/lllll;"; 33 | if(dex.getClass(appsig) == null) { 34 | // not the app we are targetting 35 | return 0; 36 | } 37 | 38 | // refer to methods in Lllll/llll/llll/lllll; 39 | String key = "KbMMxfM,"; 40 | String fsig_keySpec = "Lllll/llll/llll/lllll;->keySpec:Ljavax/crypto/spec/DESKeySpec;"; 41 | String fsig_secretKey = "Lllll/llll/llll/lllll;->secretKey:Ljavax/crypto/SecretKey;"; 42 | String fsig_iv = "Lllll/llll/llll/lllll;->iv:Ljavax/crypto/spec/IvParameterSpec;"; 43 | 44 | IDState state = g.getEmulator(); 45 | try { 46 | IDImm keySpec0 = state.getStaticField(fsig_keySpec); 47 | if(!keySpec0.isNullRef()) { 48 | // already initialized! 49 | return 0; 50 | } 51 | 52 | DESKeySpec keySpec; 53 | SecretKey secretKey; 54 | IvParameterSpec iv; 55 | try { 56 | keySpec = new DESKeySpec(key.getBytes("UTF-8")); 57 | secretKey = SecretKeyFactory.getInstance("DES").generateSecret(keySpec); 58 | iv = new IvParameterSpec(key.getBytes("UTF-8")); 59 | } 60 | catch(Exception ex) { 61 | return 0; 62 | } 63 | 64 | state.setStaticField(fsig_keySpec, state.registerObject(keySpec)); 65 | state.setStaticField(fsig_secretKey, state.registerObject(secretKey)); 66 | state.setStaticField(fsig_iv, state.registerObject(iv)); 67 | logger.trace("Fields are patched"); 68 | } 69 | catch(DexDecEvaluationException e) { 70 | //logger.catching(e); 71 | } 72 | 73 | // this optimizer is one-off; do not report on success, there is no point in running it more than once 74 | return 0; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /plugins/scripts/DOptArrayInitCleanup.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.DexDecEvaluationException; 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDVisitor; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDArrayElt; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDExpression; 6 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDInstruction; 7 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDNewArrayInfo; 8 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.IVisitResults; 9 | 10 | /** 11 | * (NOTE: Sample IR optimizer plugin for JEB's dexdec. A variant of this plugin is shipping as a dexdec built-in. 12 | * As such, there is no need to enable this sample as it is. Its goal is to showcase parts of dexdec IR API for 13 | * learning and experimental purposes.) 14 | *

15 | * Resolve direct array element access. 16 | *

17 | * Example: 18 | * 19 | *

20 |  * a = new int[]{2, 3, 5, 7, 11}[1]
21 |  * 
22 |  * =>
23 |  * 
24 |  * a = 3
25 |  * 
26 | * 27 | * @author Nicolas Falliere 28 | * 29 | */ 30 | public class DOptArrayInitCleanup extends AbstractDOptimizer { 31 | private int totalcnt; 32 | 33 | @Override 34 | public int perform() { 35 | totalcnt = 0; 36 | 37 | // go through the instructions of the CFG sequentially 38 | for(IDInstruction insn: cfg.instructions()) { 39 | 40 | // pre-order visit of all the IR expressions making up the current instruction 41 | // important note: if the instruction is an ASSIGN, we skip processing the destination (left-side) expression 42 | insn.visitInstruction(new IDVisitor() { 43 | @Override 44 | public void process(IDExpression e, IDExpression parent, IVisitResults results) { 45 | if(e.isArrayElt()) { 46 | IDArrayElt aelt = e.asArrayElt(); 47 | 48 | if(aelt.getIndex().isImm() && aelt.getArray().isNewArrayInfo()) { 49 | 50 | int index; 51 | try { 52 | index = (int)aelt.getIndex().asImm().toLong(); 53 | } 54 | catch(DexDecEvaluationException ex) { 55 | return; 56 | } 57 | 58 | IDNewArrayInfo array = aelt.getArray().asNewArrayInfo(); 59 | if(index >= 0 && index < array.getCountOfInitialValues()) { 60 | 61 | // if any of the array initial values have side-effects, we cannot safely discard them 62 | // example: the expression "new int[] {10, 20, foo(), 40} [1]" 63 | // cannot be replaced by "20" if foo() has or may have have side-effects 64 | for(IDExpression val: array.getInitialValues()) { 65 | if(val.hasSideEffects(ctx, true)) { 66 | return; // bail 67 | } 68 | } 69 | 70 | IDExpression repl = array.getInitialValue(index); 71 | // note: repl should never be null, this is just a sanity check 72 | if(repl != null && parent.replaceSubExpression(e, repl)) { 73 | results.setReplacedNode(repl); // <-- in pre-order, we must report the replaced node 74 | totalcnt++; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | }, true); 81 | } 82 | 83 | if(totalcnt > 0) { 84 | // dexdec IR CFG's share a single DFA object; if modifications of an optimizer changes 85 | // variable (identifier) arrangements in any way, the current DFA must be invalidated 86 | cfg.invalidateDataFlowAnalysis(); 87 | } 88 | return totalcnt; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /plugins/scripts/DOptCleanCondExpr.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDVisitor; 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDExpression; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDInstruction; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDOperation; 6 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.IVisitResults; 7 | 8 | /** 9 | * (NOTE: Sample IR optimizer plugin for JEB's dexdec. A variant of this plugin is shipping as a dexdec built-in. 10 | * As such, there is no need to enable this sample as it is. Its goal is to showcase parts of dexdec IR API for 11 | * learning and experimental purposes.) 12 | *

13 | * Simplify conditional expressions where both alternatives are the same. 14 | *

15 | * Example: 16 | * 17 | *

18 |  * COND ? X: X     // COND has no side-effect
19 |  * 
20 |  * =>
21 |  * 
22 |  * X
23 |  * 
24 | * 25 | * NOTE: gendec: see EOptUselessCondRem 26 | * 27 | * @author Nicolas Falliere 28 | * 29 | */ 30 | public class DOptCleanCondExpr extends AbstractDOptimizer { 31 | private int totalcnt; 32 | 33 | @Override 34 | public int perform() { 35 | totalcnt = 0; 36 | 37 | for(IDInstruction insn: cfg.instructions()) { 38 | insn.visitInstructionPostOrder(new IDVisitor() { 39 | @Override 40 | public void process(IDExpression e, IDExpression parent, IVisitResults results) { 41 | if(e instanceof IDOperation && ((IDOperation)e).isConditional()) { 42 | IDExpression eP = ((IDOperation)e).getCondPredicate(); 43 | IDExpression eT = ((IDOperation)e).getCondTrueExpression(); 44 | IDExpression eR = ((IDOperation)e).getCondFalseExpression(); 45 | if(eT.equalsEx(eR, false) && !eP.hasSideEffects(ctx, true)) { 46 | if(parent.replaceSubExpression(e, eT)) { 47 | totalcnt++; 48 | } 49 | } 50 | } 51 | } 52 | }, true); 53 | } 54 | 55 | if(totalcnt > 0) { 56 | cfg.invalidateDataFlowAnalysis(); 57 | } 58 | return totalcnt; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /plugins/scripts/DOptCleanDoubleSwitchOnSameVar.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.units.code.android.controlflow.BasicBlock; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDExpression; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDInstruction; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDSwitchData; 6 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDTarget; 7 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDTryData; 8 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDVar; 9 | 10 | /** 11 | * (NOTE: Sample IR optimizer plugin for JEB's dexdec. A variant of this plugin is shipping as a dexdec built-in. 12 | * As such, there is no need to enable this sample as it is. Its goal is to showcase parts of dexdec IR API for 13 | * learning and experimental purposes.) 14 | *

15 | * Coalesce two switches, executed back-to-back on the same variable. 16 | *

17 | * Example: 18 | * 19 | *

 20 |  * ...
 21 |  * switch(var) {
 22 |  * case 1: ...
 23 |  * case 2: ...
 24 |  * }
 25 |  * switch(var) {
 26 |  * case 3: ...
 27 |  * case 4: ...
 28 |  * }
 29 |  * //
 30 |  * 
 31 |  * =>
 32 |  * 
 33 |  * ...
 34 |  * switch(var) {
 35 |  * case 1: ...
 36 |  * case 2: ...
 37 |  * case 3: ...
 38 |  * case 4: ...
 39 |  * }
 40 |  * 
41 | * 42 | * @author Nicolas Falliere 43 | * 44 | */ 45 | public class DOptCleanDoubleSwitchOnSameVar extends AbstractDOptimizer { 46 | 47 | @Override 48 | public int perform() { 49 | int totalcnt = 0; 50 | 51 | analyzeChains(); 52 | 53 | for(int i = 1; i < cfg.size(); i++) { 54 | BasicBlock b = cfg.get(i); 55 | 56 | // second switch; block has only that instruction 57 | if(!(b.size() == 1 && b.insize() == 1 && b.get(0).isSwitch())) { 58 | continue; 59 | } 60 | 61 | // first switch; block ends up with the switch but may have more instructions 62 | BasicBlock p = cfg.get(i - 1); 63 | if(!p.getLast().isSwitch()) { 64 | continue; 65 | } 66 | 67 | IDInstruction sw0 = p.getLast(); 68 | IDInstruction sw1 = b.getLast(); 69 | 70 | if(!sw0.getSwitchData().isRegularSwitch() || !sw1.getSwitchData().isRegularSwitch()) { 71 | continue; 72 | } 73 | 74 | IDExpression e0 = sw0.getSwitchExpression(); 75 | if(!(e0 instanceof IDVar) || !e0.equals(sw1.getSwitchExpression())) { 76 | continue; 77 | } 78 | 79 | IDTryData ex = ctx.getExceptionData(); 80 | if(ex != null) { 81 | if(!ex.sameExceptionHandlers((int)p.getAddress(), (int)b.getAddress())) { 82 | continue; 83 | } 84 | } 85 | 86 | // default block of the second switch, i.e. the subsequent block 87 | BasicBlock n = b.getOutputBlock(0); 88 | 89 | // upgrade the second switch (i.e. integrate the first switch info into it) 90 | IDSwitchData data0 = sw0.getSwitchData(); 91 | IDSwitchData data1 = sw1.getSwitchData(); 92 | for(Integer val: data0.getCasesAsInt()) { 93 | IDTarget t0 = data0.getTargetForCase(val); 94 | BasicBlock x0 = cfg.getBlockAt(t0.getOffset()); 95 | if(x0 == n) { 96 | data1.deleteCase(val); 97 | } 98 | else { 99 | data1.addCase(val, t0, true); 100 | } 101 | } 102 | 103 | // edges for the upgraded switch (2nd) 104 | cfg.deleteOutEdges(b); 105 | cfg.addEdge(b, n); 106 | for(IDTarget t: data1.getTargets(true)) { 107 | cfg.addEdge(b, cfg.getBlockAt(t.getOffset())); 108 | } 109 | 110 | // neuter the first switch 111 | cfg.deleteOutEdges(p); 112 | cfg.addEdge(p, b); 113 | // replace it with a nop 114 | IDInstruction repl = ctx.createNop(); 115 | repl.setOffset(sw0.getOffset()); 116 | repl.setSize(sw0.getSize()); 117 | p.set(p.size() - 1, repl); 118 | totalcnt++; 119 | } 120 | 121 | if(totalcnt > 0) { 122 | cleanGraph(); 123 | cfg.invalidateDataFlowAnalysis(); 124 | } 125 | return totalcnt; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /plugins/scripts/DOptEmuHooksExample.py: -------------------------------------------------------------------------------- 1 | from com.pnfsoftware.jeb.core.units.code.android.ir import AbstractDOptimizer, IDEmulatorHooks 2 | ''' 3 | dexdec IR plugin showing how to set up an emulator hooks for internal dex methods. 4 | The plugin below is an IR optimizer repeatedly invoked during the IR optimization phase of decompilation. 5 | API reference: see IDEmulatorHooks 6 | ''' 7 | class DOptEmuHooksExample(AbstractDOptimizer): 8 | def perform(self): 9 | # retrieve or create the current decompiler thread emulator 10 | emu = self.g.getEmulator() 11 | # create and register a hooks object (for internal dex methods) 12 | hooks_id = emu.registerEmulatorHooks(Hooks(emu)) 13 | try: 14 | # do some work using the emulator 15 | # when/if the emulator emulates internal dex methods, the hooks routines will be called 16 | pass 17 | finally: 18 | assert emu.unregisterEmulatorHooks(hooks_id) 19 | 20 | # alternatively, you may want to set up global hooks to customize how other optimizers do their work 21 | # in this case, you want to make sure to not re-register the same hooks every time this optimizer is called 22 | # and also not unregister the hooks object when done 23 | ''' 24 | emu = self.g.getEmulator() 25 | if not emu.getData('hooksSet'): 26 | emu.setData('hooksSet', True) 27 | emu.registerEmulatorHooks(Hooks(emu)) 28 | ''' 29 | return 0 30 | 31 | class Hooks(IDEmulatorHooks): 32 | def __init__(self, emu): 33 | self.emu = emu 34 | 35 | def invokeMethod(self, reqid, msig, args): 36 | print('IR: invokeMethod: id=%d: %s(%s)' % (reqid, msig, args)) 37 | # TODO: add custom code 38 | return None 39 | 40 | # note that this routine is called only if the emulation of the invocation succeeded 41 | def examineMethodResult(self, reqid, result): 42 | print('IR: examineMethodResult: id=%d: ret=%s' % (reqid, result)) 43 | # TODO: add custom code 44 | return None 45 | -------------------------------------------------------------------------------- /plugins/scripts/DOptEmuHooksGlobalExample.java: -------------------------------------------------------------------------------- 1 | import java.util.List; 2 | 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.DOptimizerType; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDEmulatorHooks; 6 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDImm; 7 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDState; 8 | import com.pnfsoftware.jeb.util.base.Wrapper; 9 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 10 | import com.pnfsoftware.jeb.util.logging.ILogger; 11 | 12 | /** 13 | * dexdec IR plugin showing how to set up an emulator hooks for internal dex methods.
14 | * The hooks are called when the emulator is used by other optimizers.
15 | * API reference: see IDEmulatorHooks 16 | */ 17 | public class DOptEmuHooksGlobalExample extends AbstractDOptimizer { 18 | 19 | @Override 20 | public int perform() { 21 | // here, we are setting up a global emulator hooks (not unregistered) that will impact all optimzers 22 | // refer to the sample file DOptEmulatorHooksExample.py for details 23 | 24 | IDState emu = g.getEmulator(); 25 | if(emu.getData("hooksSet") == null) { 26 | emu.setData("hooksSet", true); // do not set this hooks object more that once per emulator 27 | 28 | emu.registerEmulatorHooks(new IDEmulatorHooks() { 29 | @Override 30 | public Wrapper invokeMethod(long reqid, String msig, List args) { 31 | logger.info("IR: invokeMethod: id=%d: %s(%s)", reqid, msig, args); 32 | // TODO: add custom code 33 | return null; 34 | } 35 | 36 | @Override 37 | public Wrapper examineMethodResult(long reqid, IDImm result) { 38 | logger.info("IR: examineMethodResult: id=%d: ret=%s", reqid, result); 39 | // TODO: add custom code 40 | return null; 41 | } 42 | }); 43 | } 44 | return 0; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /plugins/scripts/DOptEmuSandboxHooksExample.py: -------------------------------------------------------------------------------- 1 | from com.pnfsoftware.jeb.core.units.code.android.ir import AbstractDOptimizer, IDSandboxHooks 2 | from com.pnfsoftware.jeb.util.base import Wrapper 3 | ''' 4 | dexdec IR plugin showing how to set up an emulator sandbox hooks for external methods. 5 | The plugin below is an IR optimizer repeatedly invoked during the IR optimization phase of decompilation. 6 | API reference: see IDSandboxHooks 7 | ''' 8 | class DOptEmuSandboxHooksExample(AbstractDOptimizer): 9 | def perform(self): 10 | # retrieve or create the current decompiler thread emulator 11 | emu = self.g.getEmulator() 12 | # create and register a hooks object 13 | # in this sample code, the emulator's sandbox will hooks field accesses to 14 | # android.os.Build.VERSION and provide an alternate value (27) to the caller 15 | hooks_id = emu.registerSandboxHooks(SandboxHooks(emu)) 16 | try: 17 | # do some work using the emulator 18 | # when/if the sandbox executes external dex methods, the hooks routines will be called 19 | pass 20 | finally: 21 | assert emu.unregisterSandboxHooks(hooks_id) 22 | 23 | # alternatively, you may want to set up global hooks to customize how other optimizers do their work 24 | # in this case, you want to make sure to not re-register the same hooks every time this optimizer is called 25 | # and also not unregister the hooks object when done 26 | ''' 27 | emu = self.g.getEmulator() 28 | if not emu.getData('sandboxHooksSet'): 29 | emu.setData('sandboxHooksSet', True) 30 | emu.registerSandboxHooks(SandboxHooks(emu)) 31 | ''' 32 | return 0 33 | 34 | class SandboxHooks(IDSandboxHooks): 35 | def __init__(self, emu): 36 | self.emu = emu 37 | 38 | def getField(reqid, addr, fsig, obj): 39 | if fsig == 'Landroid/os/Build$VERSION;->SDK_INT:I': 40 | # force-return the Android Oreo API level 41 | return Wrapper.wrap(27) 42 | return None 43 | 44 | # additional hook methods (for field setting, post-access checks, method invocation, 45 | # object construction, class loading) can be implemented: refer to IDSandboxHooks 46 | # in the API reference documentation -------------------------------------------------------------------------------- /plugins/scripts/DOptEvalFolder.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.units.code.android.controlflow.BasicBlock; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDExpression; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDImm; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDInstruction; 6 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDOperation; 7 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDPredicate; 8 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDVisitor; 9 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.IVisitResults; 10 | 11 | /** 12 | * (NOTE: Sample IR optimizer plugin for JEB's dexdec. A variant of this plugin is shipping as a dexdec built-in. 13 | * As such, there is no need to enable this sample as it is. Its goal is to showcase parts of dexdec IR API for 14 | * learning and experimental purposes.) 15 | *

16 | * Simple expression evaluator and folder. 17 | *

18 | * Example: 19 | * 20 | *

21 |  * 4 + 5 => 9
22 |  * 1 & 6 => 0
23 |  * ...
24 |  * 
25 | * 26 | *

27 | * Note: this source file is a sample IR optimizer for JEB's dexdec. A variant of this optimizer is 28 | * already shipping with dexdec as a built-in optimizer. 29 | * 30 | * @author Nicolas Falliere 31 | * 32 | */ 33 | public class DOptEvalFolder extends AbstractDOptimizer { 34 | private int totalcnt = 0; 35 | 36 | @Override 37 | public int perform() { 38 | totalcnt = 0; 39 | 40 | for(BasicBlock b: cfg) { 41 | for(IDInstruction insn: b) { 42 | insn.visitInstruction(new IDVisitor() { 43 | @Override 44 | public void process(IDExpression e, IDExpression parent, IVisitResults results) { 45 | if(e instanceof IDOperation) { 46 | IDExpression repl = null; 47 | try { 48 | IDImm cst = e.evaluate(ctx); 49 | if(cst != null && !cst.isRef()) { 50 | // IDPredicate (type inhering IDOperation) need special handling to be replaced 51 | if(e instanceof IDPredicate) { 52 | repl = g.createPredicate(cst); 53 | // we may have created an IR identical to the source, watch for this 54 | if(repl.equalsEx(e, false)) { 55 | repl = null; 56 | } 57 | } 58 | else { 59 | repl = cst; 60 | } 61 | if(repl != null && parent.replaceSubExpression(e, repl)) { 62 | results.setReplacedNode(repl); 63 | totalcnt++; 64 | } 65 | } 66 | } 67 | catch(Exception ex0) { 68 | // neuter eval() exceptions 69 | } 70 | } 71 | } 72 | }, true); 73 | } 74 | } 75 | 76 | if(totalcnt > 0) { 77 | // just in case, since some expressions containing variables may have been reduced, example: 0*x => 0 (-> x is no longer used) 78 | cfg.invalidateDataFlowAnalysis(); 79 | } 80 | return totalcnt; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /plugins/scripts/DOptRemoveNops.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.units.code.android.controlflow.BasicBlock; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.DOpcodeType; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.DUtil; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDInstruction; 6 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDSwitchData; 7 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDTarget; 8 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDTryData; 9 | 10 | /** 11 | * (NOTE: Sample IR optimizer plugin for JEB's dexdec. A variant of this plugin is shipping as a dexdec built-in. 12 | * As such, there is no need to enable this sample as it is. Its goal is to showcase parts of dexdec IR API for 13 | * learning and experimental purposes.) 14 | *

15 | * Remove NOP assignments. Blocks made entirely of nops are removed as well. 16 | *

17 | * Note: this source file is a sample IR optimizer for JEB's dexdec. A variant of this optimizer is 18 | * already shipping with dexdec as a built-in optimizer. 19 | * 20 | * @author Nicolas Falliere 21 | * 22 | */ 23 | public class DOptRemoveNops extends AbstractDOptimizer { 24 | 25 | @Override 26 | public int perform() { 27 | IDTryData exdata = ctx.getExceptionData(); 28 | int cnt = 0; 29 | 30 | // nop removal in multi-instruction blocks, never end-up with empty blocks (no block deletion) 31 | // (i.e., at most, reduce the entire block to a single nop) 32 | if(true) { 33 | for(BasicBlock b: cfg) { 34 | int i = 0; 35 | while(i < b.size()) { 36 | if(b.size() >= 2 && b.get(i).getOpcode() == DOpcodeType.IR_NOP) { 37 | cnt++; 38 | if(DUtil.removeInstruction(b, i)) { 39 | continue; 40 | } 41 | } 42 | i++; 43 | } 44 | } 45 | } 46 | 47 | // nop block removal: since it takes place after the above, those blocks would have been reduced to a single-instruction NOP 48 | // caveat: to keep things simple, we bail if the fall-through block is a handler 49 | int i = 0; 50 | while(i < cfg.size()) { 51 | BasicBlock b = cfg.get(i); 52 | if(b.size() != 1) { 53 | i++; 54 | continue; 55 | } 56 | 57 | // single-instruction NOP block 58 | IDInstruction insn = b.getLast(); 59 | if(insn.getOpcode() != DOpcodeType.IR_NOP) { 60 | i++; 61 | continue; 62 | } 63 | 64 | // we will remove the block if the fall-through block is not a handler 65 | BasicBlock b1 = b.getOutputBlock(0); 66 | if(b1.irrinsize() > 0) { 67 | i++; 68 | continue; 69 | } 70 | 71 | cfg.deleteIrregularOutEdges(b); 72 | if(exdata != null) { 73 | exdata.unprotectBlock((int)b.getAddress()); 74 | } 75 | 76 | cfg.deleteEdge(b, b1); 77 | 78 | IDInstruction newFirstInsn = b1.get(0); 79 | int oldOffset = (int)newFirstInsn.getOffset(); 80 | int newOffset = (int)insn.getOffset(); 81 | for(BasicBlock src: b1.getInputBlocks()) { 82 | updateTargets(src.getLast(), oldOffset, newOffset); 83 | } 84 | 85 | for(BasicBlock src: b.getInputBlocks()) { 86 | cfg.reconnectEdges(src, b, b1); 87 | cfg.removeDuplicateEdges(src, b1); 88 | } 89 | 90 | for(BasicBlock src: b.getIrregularInputBlocks()) { 91 | cfg.reconnectIrregularEdges(src, b, b1); 92 | } 93 | if(exdata != null) { 94 | exdata.moveProtectedBlock((int)b1.getAddress(), (int)b.getAddress()); 95 | } 96 | 97 | newFirstInsn.setOffset(insn.getOffset()); 98 | newFirstInsn.adjustSize(insn.getSize()); 99 | 100 | cfg.removeBlock(b); 101 | 102 | cnt++; 103 | } 104 | 105 | if(cnt > 0) { 106 | cleanGraph(); 107 | cfg.invalidateDataFlowAnalysis(); 108 | } 109 | return cnt; 110 | } 111 | 112 | private void updateTargets(IDInstruction insn, int oldOffset, int newOffset) { 113 | if(insn.isJump() || insn.isJcond()) { 114 | int offset = insn.getBranchTarget(); 115 | if(offset == oldOffset) { 116 | insn.setBranchTarget(newOffset); 117 | } 118 | } 119 | else if(insn.isSwitch()) { 120 | IDSwitchData data = insn.getSwitchData(); 121 | for(IDTarget target: data.getTargets(false)) { 122 | if(target.getOffset() == oldOffset) { 123 | target.setOffset(newOffset); 124 | } 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /plugins/scripts/DOptSampleJava.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.Version; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 3 | 4 | /** 5 | * A sample IR optimizer. 6 | * 7 | * @author Nicolas Falliere 8 | * 9 | */ 10 | public class DOptSampleJava extends AbstractDOptimizer { 11 | 12 | public DOptSampleJava() { 13 | super(); 14 | } 15 | 16 | @Override 17 | public int perform() { 18 | logger.debug("The optimizer is running"); 19 | return 0; 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/scripts/DOptSamplePython.py: -------------------------------------------------------------------------------- 1 | from com.pnfsoftware.jeb.core.units.code.android.ir import AbstractDOptimizer 2 | 3 | ''' 4 | Sample Intermediate Representation (IR) optimizer plugin for dexdec, JEB's DEX/Dalvik Decompiler. 5 | 6 | This Python plugin is executed during the decompilation pipeline of a method. 7 | Needs JEB 4.2 or above. 8 | 9 | NOTE: it is recommended to write non-trivial dexdec IR optimizers in Java 10 | 11 | How to use: 12 | - Drop this file in your JEB's coreplugins/scripts/ sub-directory 13 | - Make sure to have the setting `.LoadPythonPlugins = true` in your JEB's bin/jeb-engines.cfg file 14 | 15 | For additional information regarding dexdec IR optimizer plugins, refer to: 16 | - the Manual (www.pnfsoftware.com/jeb/manual) 17 | - the API documentation: https://www.pnfsoftware.com/jeb/apidoc/reference/com/pnfsoftware/jeb/core/units/code/android/ir/package-summary.html 18 | ''' 19 | class DOptSamplePython(AbstractDOptimizer): # note that we extend AbstractDOptimizer for convenience, instead of implementing IDOptimizer from scratch 20 | def __init__(self): 21 | # default super constructor is called, will set the plugin type to NORMAL (see DOptimizerType constant) 22 | self.logger.debug('DexDecIROptimizerSample: instantiated') 23 | 24 | def perform(self): 25 | # commonly needed objects for IR optimizers are provided as attributes AbstractDOptimizer: 26 | # - ctx: the IDMethodContext 27 | # - cfg: the IR CFG (control flow graph) 28 | # - irb: IR element builder 29 | # - tf: a type factory 30 | # - of: an operator factory 31 | # - g: the global IR context, referring to other important decompiler objects 32 | # - dex: the underlying dex file 33 | # ... 34 | # as well as useful methods, such as: 35 | # - cleanGraph(): to be called in structural modifications of teh CFG were made 36 | # - analyzeChains(): to perform DFA (data flow analysis) if data chains are needed by the optimizer 37 | # ... 38 | # and more: refer to the API documentation 39 | # 40 | # another commonly used class by IR optimizers is DUtil, containing many utitily routines to access and manipulate the IR 41 | 42 | # print the IR CFG 43 | self.logger.debug('DexDecIROptimizerSample: executed: %s', self.cfg.format()) 44 | 45 | # some random work... 46 | for blk in self.cfg: # BasicBlock (of IDInstruction) 47 | for insn in blk: # IDInstruction 48 | if insn.isJcond(): 49 | # we found a JCOND instruction, do something 50 | pass 51 | 52 | # if a value >0 is returned, the decompiler will assume that IR is being transformed and this IR optimizer will be called again 53 | return 0 # no optimization is performed -------------------------------------------------------------------------------- /plugins/scripts/DOptStringSimplifier.java: -------------------------------------------------------------------------------- 1 | import com.pnfsoftware.jeb.core.units.code.android.controlflow.BasicBlock; 2 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDVisitor; 3 | import com.pnfsoftware.jeb.core.units.code.android.ir.AbstractDOptimizer; 4 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDCallInfo; 5 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDExpression; 6 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDImm; 7 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDInstruction; 8 | import com.pnfsoftware.jeb.core.units.code.android.ir.IDOperation; 9 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.IVisitResults; 10 | import com.pnfsoftware.jeb.core.units.code.java.JavaOperatorType; 11 | 12 | /** 13 | * (NOTE: Sample IR optimizer plugin for JEB's dexdec. A variant of this plugin is shipping as a dexdec built-in. 14 | * As such, there is no need to enable this sample as it is. Its goal is to showcase parts of dexdec IR API for 15 | * learning and experimental purposes.) 16 | *

17 | * Simplify useless calls to {@code String.valueOf}. 18 | * 19 | *

20 |  * String.valueOf(some_string)
21 |  * 
22 |  * => (simplified to)
23 |  *  
24 |  * some_string
25 |  * 
26 | * 27 | * @author Nicolas Falliere 28 | * 29 | */ 30 | public class DOptStringSimplifier extends AbstractDOptimizer { 31 | private static final String msig0 = "Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;"; 32 | 33 | private int totalcnt; 34 | 35 | @Override 36 | public int perform() { 37 | totalcnt = 0; 38 | 39 | for(int iblk = 0; iblk < cfg.size(); iblk++) { 40 | BasicBlock b = cfg.get(iblk); 41 | 42 | for(int i = 0; i < b.size(); i++) { 43 | IDInstruction insn = b.get(i); 44 | 45 | insn.visitDepthPost(new IDVisitor() { 46 | @Override 47 | public void process(IDExpression e, IDExpression parent, IVisitResults results) { 48 | if(e instanceof IDCallInfo && msig0.equals(((IDCallInfo)e).getMethodSignature())) { 49 | IDExpression arg0 = ((IDCallInfo)e).getArgument(0); 50 | boolean proceed = false; 51 | if(arg0 instanceof IDImm && ((IDImm)arg0).isString()) { 52 | proceed = true; 53 | } 54 | else if(arg0 instanceof IDOperation 55 | && ((IDOperation)arg0).getOperator().is(JavaOperatorType.CONCAT)) { 56 | proceed = true; 57 | } 58 | if(arg0 instanceof IDCallInfo 59 | && ((IDCallInfo)arg0).getMethodSignature().endsWith(")Ljava/lang/String;")) { 60 | proceed = true; 61 | } 62 | if(proceed) { 63 | if(parent.replaceSubExpression(e, arg0)) { 64 | totalcnt++; 65 | } 66 | } 67 | } 68 | } 69 | }); 70 | } 71 | } 72 | 73 | return totalcnt; 74 | } 75 | } -------------------------------------------------------------------------------- /plugins/scripts/GarbageCleaner.java: -------------------------------------------------------------------------------- 1 | //?type=gendec-ir 2 | import com.pnfsoftware.jeb.core.units.code.asm.cfg.BasicBlock; 3 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEAssign; 4 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEGeneric; 5 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEImm; 6 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEMem; 7 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEOperation; 8 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEStatement; 9 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.IEVar; 10 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.OperationType; 11 | import com.pnfsoftware.jeb.core.units.code.asm.decompiler.ir.opt.AbstractEOptimizer; 12 | import com.pnfsoftware.jeb.core.units.code.asm.items.INativeContinuousItem; 13 | 14 | /** 15 | * This file is an IR optimizer plugin for JEB's gendec. 16 | * Drop this file in your JEB's coreplugins/scripts/ folder. 17 | *

18 | * This optimizer is a garbage array assigment cleaner. 19 | * Any statement writing an immediate value to a global array 20 | * whose name starts with 'garbage' will be discarded.
21 | */ 22 | public class GarbageCleaner extends AbstractEOptimizer { 23 | 24 | @Override 25 | public int perform() { 26 | int cnt = 0; 27 | 28 | for (BasicBlock b : cfg) { 29 | for (int i = 0; i < b.size(); i++) { 30 | IEStatement stm = b.get(i); 31 | if (stm instanceof IEAssign && stm.asAssign().getDstOperand() instanceof IEMem 32 | && stm.asAssign().getSrcOperand() instanceof IEImm) { 33 | IEMem dst = stm.asAssign().getDstOperand().asMem(); 34 | IEGeneric e = dst.getReference(); 35 | // [xxx + offset] = immediate 36 | if (e.isOperation(OperationType.ADD)) { 37 | IEOperation op = e.asOperation(); 38 | if (op.getOperand1().isVar() && op.getOperand2().isImm()) { 39 | IEVar v = op.getOperand1().asVar(); 40 | IEImm off = op.getOperand2().asImm(); 41 | if (v.isGlobalReference()) { 42 | long addr = v.getAddress(); 43 | INativeContinuousItem item = ectx.getNativeContext().getNativeItemAt(addr); 44 | // logger.info("FOUND ITEM %s", item.getName()); 45 | if (item != null && item.getName().startsWith("garbage")) { 46 | long itemsize = item.getMemorySize(); 47 | if (off.canReadAsLong() && off.getValueAsLong() + dst.getBitsize() / 8 < itemsize) { 48 | logger.info("FOUND GARBAGE CODE"); 49 | b.set(i, ectx.createNop(stm)); 50 | cnt++; 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | if (cnt > 0) { 61 | cfg.invalidateDataFlowAnalysis(); 62 | } 63 | return cnt; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /plugins/scripts/JOptSampleJava.java: -------------------------------------------------------------------------------- 1 | //?type=dexdec-ast 2 | import com.pnfsoftware.jeb.core.Version; 3 | import com.pnfsoftware.jeb.core.units.code.java.AbstractJOptimizer; 4 | 5 | /** 6 | * A sample dexdec AST optimizer plugin. Requires JEB 4.23+. 7 | * 8 | * @author Nicolas Falliere 9 | * 10 | */ 11 | public class JOptSampleJava extends AbstractJOptimizer { 12 | 13 | public JOptSampleJava() { 14 | super(); 15 | } 16 | 17 | @Override 18 | public int perform() { 19 | //logger.debug("The Java AST optimizer is running"); 20 | return 0; 21 | } 22 | } -------------------------------------------------------------------------------- /plugins/scripts/JOptSamplePython.py: -------------------------------------------------------------------------------- 1 | #?type=dexdec-ast 2 | from com.pnfsoftware.jeb.core.units.code.java import AbstractJOptimizer 3 | 4 | ''' 5 | Skeleton for an Java Abstract Syntax Tree (AST) optimizer plugin for dexdec, JEB's DEX/Dalvik Decompiler. 6 | This Python plugin is executed during the decompilation pipeline of a method. 7 | 8 | How to use: 9 | - Drop this file in your JEB's coreplugins/scripts/ sub-directory 10 | - Make sure to have the setting `.LoadPythonPlugins = true` in your JEB's bin/jeb-engines.cfg file 11 | 12 | For additional information regarding dexdec AST optimizer plugins, refer to: 13 | - the Manual (www.pnfsoftware.com/jeb/manual) 14 | - the API documentation: https://www.pnfsoftware.com/jeb/apidoc/reference/com/pnfsoftware/jeb/core/units/code/java/package-summary.html 15 | 16 | Requires JEB 4.23+. 17 | ''' 18 | class JOptSamplePython(AbstractJOptimizer): 19 | # note: Python script optimizers are singleton instances! 20 | # the engine will instantiate and provide a single instance for all decompilation threads 21 | # therefore, if you are using object attributes, make sure to provide support for concurrency 22 | # (this restriction does not apply to Java script optimizers, as well as full-blown jar optimizers; 23 | # each decompilation thread has its own unique instance of such optimizer objects) 24 | # for this reason (as well as others), moderately complex AST optimizers should be written in Java 25 | 26 | def __init__(self): 27 | # default super constructor is called, will set the plugin type to NORMAL (see JOptimizerType constant) 28 | self.logger.debug('DexDecASTOptimizerSample: instantiated') 29 | 30 | def perform(self): 31 | # commonly needed objects for AST optimizers are provided as attributes AbstractJOptimizer and sub-classes 32 | # if the optimizer is about to work on a IJavaMethod, the AST method object is in attribute `m` 33 | # if the optimizer is about to work on a IJavaClass, the AST class object is in attribute `c` 34 | 35 | # another commonly used class by IR optimizers is JUtil, containing many utitily routines to access and manipulate the AST 36 | # more: refer to the API documentation 37 | 38 | # UNCOMMENT: message the developer 39 | #self.logger.debug('DexDecASTOptimizerSample: executed') 40 | 41 | # if a value >0 is returned, the decompiler will assume that AST is being transformed and this AST optimizer will be called again 42 | return 0 # no optimization is performed 43 | -------------------------------------------------------------------------------- /plugins/scripts/README.md: -------------------------------------------------------------------------------- 1 | Sample back-end script plugins (java, python) for JEB. 2 | 3 | Back-end script plugins go in the `coreplugins/scripts/` folder. 4 | -------------------------------------------------------------------------------- /plugins/src/com/pnf/jebauto/AutoClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JEB Copyright PNF Software, Inc. 3 | * 4 | * https://www.pnfsoftware.com 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package com.pnf.jebauto; 20 | 21 | import java.io.File; 22 | import java.util.List; 23 | 24 | import org.apache.commons.configuration2.BaseConfiguration; 25 | 26 | import com.pnfsoftware.jeb.core.Artifact; 27 | import com.pnfsoftware.jeb.core.ICoreContext; 28 | import com.pnfsoftware.jeb.core.IEnginesContext; 29 | import com.pnfsoftware.jeb.core.ILiveArtifact; 30 | import com.pnfsoftware.jeb.core.IRuntimeProject; 31 | import com.pnfsoftware.jeb.core.JebCoreService; 32 | import com.pnfsoftware.jeb.core.dao.IDataProvider; 33 | import com.pnfsoftware.jeb.core.dao.IFileDatabase; 34 | import com.pnfsoftware.jeb.core.dao.IFileStore; 35 | import com.pnfsoftware.jeb.core.dao.impl.DataProvider; 36 | import com.pnfsoftware.jeb.core.dao.impl.JEB2FileDatabase; 37 | import com.pnfsoftware.jeb.core.dao.impl.SimpleFSFileStore; 38 | import com.pnfsoftware.jeb.core.input.FileInput; 39 | import com.pnfsoftware.jeb.core.properties.IConfiguration; 40 | import com.pnfsoftware.jeb.core.properties.impl.CommonsConfigurationWrapper; 41 | import com.pnfsoftware.jeb.core.units.IUnit; 42 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 43 | import com.pnfsoftware.jeb.util.logging.ILogger; 44 | 45 | /** 46 | * Skeleton for a JEB2 headless client (eg, for automation/bulk processing) !! IMPORTANT !! The 47 | * areas marked "TODO: customize" must be edited prior to executing this code 48 | * 49 | * @author Nicolas Falliere 50 | * 51 | */ 52 | public class AutoClient { 53 | static final ILogger logger = GlobalLog.getLogger(AutoClient.class); 54 | static { 55 | GlobalLog.addDestinationStream(System.out); 56 | } 57 | 58 | // TODO: customize (should be replaced by the LicenseKey entry in your bin/jeb-client.cfg file) 59 | private static final String licenseKey = "..."; 60 | 61 | // TODO: customize 62 | private static final String baseDir = "..."; 63 | 64 | public static void main(String[] argv) throws Exception { 65 | if(argv.length <= 0) { 66 | return; 67 | } 68 | 69 | long t0 = System.currentTimeMillis(); 70 | String location = argv[0]; 71 | List files = AutoUtil.retrieveFiles(location); 72 | test(files); 73 | logger.info("Done in %ds", (System.currentTimeMillis() - t0) / 1000); 74 | } 75 | 76 | /** 77 | * Initialize a core. Create a context within that core. Then, for each input artifact, a 78 | * project is created and the artifact is loaded within that project. 79 | * 80 | * @param files 81 | * @throws Exception 82 | */ 83 | public static void test(List files) throws Exception { 84 | // create or retrieve a core context (engines container) 85 | ICoreContext core = JebCoreService.getInstance(licenseKey); 86 | 87 | // create an engines context (project container) 88 | IFileDatabase projectdb = new JEB2FileDatabase(baseDir); 89 | IFileStore filestore = new SimpleFSFileStore(baseDir); 90 | BaseConfiguration cfg = new BaseConfiguration(); 91 | 92 | // TODO: customize (alternative is to read your configuration from .cfg file) 93 | cfg.setProperty(".DevPluginClasspath", "..."); 94 | 95 | // TODO: customize 96 | cfg.setProperty(".DevPluginClassnames", "..."); 97 | 98 | IConfiguration config = new CommonsConfigurationWrapper(cfg); 99 | IDataProvider dataProvider = new DataProvider(null, projectdb, filestore, null, null, config); 100 | IEnginesContext engctx = core.createEnginesContext(dataProvider, null); 101 | 102 | int i = 0; 103 | for(File file: files) { 104 | i++; 105 | logger.info("Testing file %d/%d : %s ...", i, files.size(), file.getName()); 106 | 107 | // create or load a project (artifact container) 108 | IRuntimeProject prj = engctx.loadProject("ProjectTest" + i); 109 | 110 | // process the artifact, get units 111 | ILiveArtifact art = prj.processArtifact(new Artifact(file.getName(), new FileInput(file))); 112 | 113 | // proceed with the units 114 | List units = art.getUnits(); 115 | 116 | // TODO: CUSTOMIZE -- this is the important part 117 | // Basic tests go here 118 | // example: 119 | for(IUnit unit: units) { 120 | logger.info("Unit: %s", unit); 121 | //if(unit instanceof Xyz) { 122 | // ... 123 | //} 124 | } 125 | 126 | engctx.unloadProject(prj.getKey()); 127 | } 128 | 129 | // close the engines 130 | JebCoreService.getInstance().closeEnginesContext(engctx); 131 | } 132 | } -------------------------------------------------------------------------------- /plugins/src/com/pnf/jebauto/AutoUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * JEB Copyright PNF Software, Inc. 3 | * 4 | * https://www.pnfsoftware.com 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package com.pnf.jebauto; 20 | 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | 26 | import org.apache.commons.configuration2.PropertiesConfiguration; 27 | import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder; 28 | import org.apache.commons.configuration2.builder.fluent.Parameters; 29 | import org.apache.commons.configuration2.ex.ConfigurationException; 30 | 31 | /** 32 | * Utility methods for headless JEB2 clients. 33 | * 34 | * @author Nicolas Falliere 35 | * 36 | */ 37 | public class AutoUtil { 38 | 39 | /** 40 | * List all files recursively. 41 | * 42 | * @param location root directory 43 | * @return a list of files 44 | */ 45 | public static List retrieveFiles(String location) { 46 | List results = new ArrayList<>(); 47 | retrieveFilesRecurse(new File(location), results); 48 | return results; 49 | } 50 | 51 | private static void retrieveFilesRecurse(File f, List results) { 52 | if(f.isFile()) { 53 | results.add(f); 54 | } 55 | else { 56 | for(String name: f.list()) { 57 | File f1 = new File(f, name); 58 | retrieveFilesRecurse(f1, results); 59 | } 60 | } 61 | } 62 | 63 | /** 64 | * Create an Apache properties object out of a JEB2 configuration file. 65 | * 66 | * @param path path to a JEB2 configuration file, such as jeb-client.cfg or jeb-engines.cfg 67 | * @return 68 | */ 69 | public static PropertiesConfiguration createPropertiesConfiguration(String path) { 70 | File configfile = new File(path); 71 | 72 | if(!configfile.isFile()) { 73 | try { 74 | configfile.createNewFile(); 75 | } 76 | catch(IOException e) { 77 | throw new RuntimeException(e); 78 | } 79 | } 80 | 81 | Parameters params = new Parameters(); 82 | FileBasedConfigurationBuilder builder = new FileBasedConfigurationBuilder<>( 83 | PropertiesConfiguration.class).configure(params.properties().setFileName(path)); 84 | builder.setAutoSave(true); 85 | 86 | try { 87 | return builder.getConfiguration(); 88 | } 89 | catch(ConfigurationException e) { 90 | throw new RuntimeException(e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /plugins/src/com/pnf/plugintest/SampleEnginesPlugin.java: -------------------------------------------------------------------------------- 1 | package com.pnf.plugintest; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import com.pnfsoftware.jeb.core.IEnginesContext; 7 | import com.pnfsoftware.jeb.core.IEnginesPlugin; 8 | import com.pnfsoftware.jeb.core.IOptionDefinition; 9 | import com.pnfsoftware.jeb.core.IPluginInformation; 10 | import com.pnfsoftware.jeb.core.PluginInformation; 11 | import com.pnfsoftware.jeb.core.Version; 12 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 13 | import com.pnfsoftware.jeb.util.logging.ILogger; 14 | 15 | /** 16 | * Sample plugin. 17 | * 18 | * @author Nicolas Falliere 19 | */ 20 | public class SampleEnginesPlugin implements IEnginesPlugin { 21 | private static final ILogger logger = GlobalLog.getLogger(SampleEnginesPlugin.class); 22 | 23 | @Override 24 | public IPluginInformation getPluginInformation() { 25 | return new PluginInformation("Sample Engines Plugin", "A sample JEB back-end plugin", "PNF Software", 26 | Version.create(1, 0, 1)); 27 | } 28 | 29 | @Override 30 | public void load(IEnginesContext context) { 31 | logger.info("Sample plugin is loaded"); 32 | } 33 | 34 | @Override 35 | public List getExecutionOptionDefinitions() { 36 | return null; 37 | } 38 | 39 | @Override 40 | public void execute(IEnginesContext context) { 41 | execute(context, null); 42 | } 43 | 44 | @Override 45 | public void execute(IEnginesContext engctx, Map executionOptions) { 46 | logger.info("Executing sample plugin"); 47 | } 48 | 49 | @Override 50 | public void dispose() { 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /plugins/src/com/pnf/pommePlugin/MetaUnit.java: -------------------------------------------------------------------------------- 1 | package com.pnf.pommePlugin; 2 | 3 | import java.util.List; 4 | 5 | import com.pnfsoftware.jeb.core.output.AbstractUnitRepresentation; 6 | import com.pnfsoftware.jeb.core.output.IGenericDocument; 7 | import com.pnfsoftware.jeb.core.output.IUnitDocumentPresentation; 8 | import com.pnfsoftware.jeb.core.output.IUnitFormatter; 9 | import com.pnfsoftware.jeb.core.output.UnitFormatterAdapter; 10 | import com.pnfsoftware.jeb.core.units.AbstractUnit; 11 | import com.pnfsoftware.jeb.core.units.code.ICodeUnit; 12 | 13 | /* 14 | * 15 | * */ 16 | public class MetaUnit extends AbstractUnit{ 17 | 18 | 19 | private IUnitDocumentPresentation presentation_; 20 | 21 | public MetaUnit(String name, String description, IUnitDocumentPresentation presentation, ICodeUnit codeUnit) { 22 | super(name, description, codeUnit); 23 | presentation_ = presentation; 24 | 25 | } 26 | 27 | @Override 28 | public boolean process() { 29 | setProcessed(true); 30 | return true; 31 | } 32 | 33 | @Override 34 | public IUnitFormatter getFormatter() { 35 | UnitFormatterAdapter adapter = new UnitFormatterAdapter(new AbstractUnitRepresentation("API Use", true) { 36 | public IGenericDocument getDocument() { 37 | return presentation_.getDocument(); 38 | } 39 | }); 40 | return adapter; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /plugins/src/com/pnf/pommePlugin/PommePlugin.java: -------------------------------------------------------------------------------- 1 | package com.pnf.pommePlugin; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import com.pnfsoftware.jeb.core.IEnginesContext; 7 | import com.pnfsoftware.jeb.core.IEnginesPlugin; 8 | import com.pnfsoftware.jeb.core.IOptionDefinition; 9 | import com.pnfsoftware.jeb.core.IPluginInformation; 10 | import com.pnfsoftware.jeb.core.IRuntimeProject; 11 | import com.pnfsoftware.jeb.core.PluginInformation; 12 | import com.pnfsoftware.jeb.core.RuntimeProjectUtil; 13 | import com.pnfsoftware.jeb.core.Version; 14 | import com.pnfsoftware.jeb.core.units.INativeCodeUnit; 15 | import com.pnfsoftware.jeb.core.units.code.ICodeUnit; 16 | import com.pnfsoftware.jeb.core.units.code.IInstruction; 17 | import com.pnfsoftware.jeb.util.logging.GlobalLog; 18 | import com.pnfsoftware.jeb.util.logging.ILogger; 19 | 20 | 21 | public class PommePlugin implements IEnginesPlugin { 22 | 23 | private static final ILogger logger = GlobalLog.getLogger(PommePlugin.class); 24 | 25 | @Override 26 | public IPluginInformation getPluginInformation() { 27 | 28 | return new PluginInformation("Pomme Plugin", "Finds API usage in multiple files.", "PNF Software", Version.create(0, 1)); 29 | } 30 | 31 | @Override 32 | public void dispose() { 33 | logger.info("Dispose"); 34 | } 35 | 36 | @Override 37 | public void execute(IEnginesContext engctx) { 38 | 39 | execute(engctx, null); 40 | 41 | } 42 | 43 | public void execute(IEnginesContext engctx, Map arg1) { 44 | 45 | List projects = engctx.getProjects(); 46 | if (projects == null){ 47 | logger.info("There is no opened project"); 48 | return; 49 | 50 | } 51 | IRuntimeProject prj = projects.get(0); 52 | logger.info("Decompiling code units of %s", prj.toString()); 53 | 54 | List codeUnits = RuntimeProjectUtil.findUnitsByType(prj, ICodeUnit.class, false); 55 | 56 | if (codeUnits == null) { 57 | 58 | logger.warn("No code units found."); 59 | 60 | } 61 | 62 | new PommeTask(codeUnits); 63 | 64 | logger.info("Done."); 65 | 66 | } 67 | 68 | public List getExecutionOptionDefinitions() { 69 | return null; 70 | } 71 | } -------------------------------------------------------------------------------- /plugins/src/com/pnf/pommePlugin/functionList.json: -------------------------------------------------------------------------------- 1 | ["cpy", "gets"] 2 | -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .project 3 | .classpath 4 | .pydevproject 5 | temp -------------------------------------------------------------------------------- /scripts/AdbDemo.py: -------------------------------------------------------------------------------- 1 | #?description=Create and use adb (Android Debug Bridge) wrappers and adb utility objects 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android.adb import AdbWrapperFactory, AdbWrapperFactory, AndroidDeviceUtil 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class AdbDemo(IScript): 9 | def run(self, ctx): 10 | # factory object wraps adb, not tied to a specific device 11 | adbf = AdbWrapperFactory() # see javadoc for a list of methods 12 | adbf.initialize() 13 | print('adb version: %s' %adbf.getVersion()) 14 | 15 | for dev in adbf.listDevices(): 16 | # an AdbWrapper object is tied to a debuggable device 17 | adb = adbf.createWrapper(dev.getSerial()) 18 | break 19 | else: 20 | print 'no android device found' 21 | return 22 | print(adb) # see javadoc for a list of methods 23 | #adb.listPackages() 24 | #adb.listProcesses() 25 | #adb.readProperty("ro.build.version.release") 26 | #adb.... 27 | 28 | # higher-level wrappers: see AndroidDeviceUtil 29 | -------------------------------------------------------------------------------- /scripts/AddArtifact.py: -------------------------------------------------------------------------------- 1 | #?description=Add and analyze an additional artifact into an existing project 2 | #?shortcut= 3 | from java.io import File 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core import Artifact 6 | from com.pnfsoftware.jeb.core.input import FileInput 7 | import os 8 | """ 9 | Sample script for JEB Decompiler. 10 | """ 11 | class AddArtifact(IScript): 12 | 13 | def run(self, ctx): 14 | prj = ctx.getMainProject() 15 | assert prj, 'Need a project' 16 | 17 | path = ctx.displayFileOpenSelector('Select a file to be added to the project') 18 | assert path and os.path.isfile(path), 'Need a valid artifact file path' 19 | 20 | artifactFile = File(path) 21 | a = Artifact(artifactFile.getName(), FileInput(artifactFile)) 22 | print('Adding: %s' % a) 23 | 24 | la = prj.processArtifact(a) 25 | print(la) 26 | -------------------------------------------------------------------------------- /scripts/AddCustomNativeTypes.py: -------------------------------------------------------------------------------- 1 | #?description=Create and add custom native structure types to a project 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 5 | from com.pnfsoftware.jeb.core.units.code.asm.type import TypeUtil 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class AddCustomNativeTypes(IScript): 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | assert prj, 'Need a project' 13 | 14 | unit = prj.findUnit(INativeCodeUnit) 15 | assert unit, 'Need a native code unit' 16 | 17 | print('Will create type for native unit: %s' % unit) 18 | ''' create the following type: 19 | // Size: 10, Padding: 1, Alignment: 1 20 | struct MyStruct1 { 21 | int a; 22 | unsigned char[3][2] b; 23 | }; 24 | ''' 25 | typeman = unit.getTypeManager() 26 | 27 | # method 1: craft the type manually, using the ITypeManager 28 | tInt = typeman.getType('int') 29 | tS1 = typeman.createStructure('MyStruct1') 30 | typeman.addStructureField(tS1, 'a', tInt) 31 | typeman.addStructureField(tS1, 'b', TypeUtil.buildArrayType(typeman, 'unsigned char', 2, 3)) 32 | print('Added type: %s' % tS1) 33 | 34 | # method 2: parse a C-type, using a typeman-provided parser 35 | buf = 'enum SomeEnum {A,B,C};' 36 | tSomeEnum = typeman.getParser().parseTypesRaw(buf) 37 | print('Added type: %s' % tSomeEnum) 38 | -------------------------------------------------------------------------------- /scripts/AndroidDbgAddAllDex.py: -------------------------------------------------------------------------------- 1 | #?description=Search for DEX units in the project and register them to the current Android debugging session 2 | #?shortcut= 3 | #?deprecated 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 6 | from com.pnfsoftware.jeb.core.units import UnitUtil 7 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, IDalvikDebuggerUnit 8 | """ 9 | Sample script for JEB Decompiler. 10 | """ 11 | class AndroidDbgAddAllDex(IScript): 12 | def run(self, ctx): 13 | prj = ctx.getMainProject() 14 | assert prj, 'Need a project' 15 | 16 | dbg = prj.findUnit(IDalvikDebuggerUnit) 17 | assert dbg, 'Need a dalvik debugger' 18 | 19 | dexlist = RuntimeProjectUtil.findUnitsByType(prj, IDexUnit, False) 20 | for dex in dexlist: 21 | if dbg.registerDebuggee(dex): 22 | print('Added to debuggees list: %s', UnitUtil.buildFullyQualifiedUnitPath(dex)) 23 | -------------------------------------------------------------------------------- /scripts/AndroidDbgCreate.py: -------------------------------------------------------------------------------- 1 | #?description=Create a debugger unit for an APK object 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IApkUnit, IDexUnit 5 | from com.pnfsoftware.jeb.core.units.code.debug.impl import DebuggerSetupInformation 6 | from com.pnfsoftware.jeb.core.util import DebuggerHelper 7 | """ 8 | Sample script for JEB Decompiler. 9 | """ 10 | class AndroidDbgCreate(IScript): 11 | def run(self, ctx): 12 | prj = ctx.getMainProject() 13 | assert prj, 'Need a project' 14 | 15 | # let's retrieve the primary apk unit loaded in the project, if there is one 16 | apk = prj.findUnit(IApkUnit) 17 | assert apk, 'Need an APK unit' 18 | 19 | # a debugger unit is the child of another unit 20 | dbg = DebuggerHelper.getDebugger(apk, True) 21 | if not dbg: 22 | print 'Cannot create or retrieve debugger' 23 | return 24 | 25 | # after creation, the debugger unit is not attached to any target 26 | print(dbg) 27 | 28 | if not dbg.isAttached(): 29 | # we will find a matching compatible target on the devices that can be enumerated by the debugger unit 30 | setup = findTarget(dbg, apk) 31 | if not setup: 32 | print 'Target not found' 33 | return 34 | # UNCOMMENT below to request all threads to be stopped before attaching 35 | #setup.setSuspendThreads(True) 36 | # UNCOMMENT below to allow the creation of a child debugger for APK's native code 37 | #setup.setUseChildrenDebuggers(True) 38 | dbg.attach(setup) 39 | 40 | print 'Debugger found and attached: %s' % dbg 41 | 42 | 43 | def findTarget(dbg, apk): 44 | # here, we use the un-attached debugger unit to enumerate compatible targets, 45 | # enumerate processes on those targets, and find one that matches our apk 46 | # this process may seem convoluted, but keep in mind that IDebuggerUnit are 47 | # not specific to android, they are a generic interface for any type of debugger 48 | te = dbg.getTargetEnumerator() 49 | for m in te.listMachines(): 50 | for p in m.getProcesses(): 51 | # match by exact package name 52 | if p.getName() == apk.getPackageName(): 53 | return DebuggerSetupInformation.create(m, p) 54 | return None 55 | -------------------------------------------------------------------------------- /scripts/AndroidGlobalAnalysis.py: -------------------------------------------------------------------------------- 1 | #?description=Perform a global analysis on a dex unit and display decompilation events 2 | #?shortcut= 3 | 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units.code import DecompilationOptions, DecompilationContext 6 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, IDexDecompilerUnit 7 | 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | Retrieve a dex decompiler (dexdec) instance to decompile all methods, 12 | and retrieve and display interesting decompilation events. 13 | """ 14 | class AndroidGlobalAnalysis(IScript): 15 | 16 | def run(self, ctx): 17 | prj = ctx.getMainProject() 18 | dex = prj.findUnit(IDexUnit) 19 | dexdec = dex.getDecompiler() 20 | 21 | dexdec.resetGlobalDecompilationEvents() 22 | 23 | f = (IDexDecompilerUnit.FLAG_BATCH_DECOMPILATION 24 | | IDexDecompilerUnit.FLAG_NO_METHOD_AST_GENERATION 25 | | IDexDecompilerUnit.FLAG_NO_DEFERRED_DECOMPILATION 26 | | IDexDecompilerUnit.FLAG_NO_INNER_DECOMPILATION 27 | | IDexDecompilerUnit.FLAG_TEMP_FORCED_REDECOMPILATIONS) 28 | ctx = DecompilationContext(DecompilationOptions.Builder.newInstance() 29 | .maxTimePerMethod(1 * 60 * 1000) 30 | .maxTimeTotal(5 * 60 * 1000) 31 | .flags(f) 32 | .build()) 33 | 34 | success = dexdec.decompileAllMethods(ctx); 35 | print(success) 36 | 37 | # see DexDecompilerEvent 38 | for e in dexdec.getGlobalDecompilationEvents(): 39 | print(e) -------------------------------------------------------------------------------- /scripts/AndroidXrefResId.py: -------------------------------------------------------------------------------- 1 | #?description=Android APK cross-reference navigation from Resource XML units to DEX Disassembly unit 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core import IPlugin 5 | from com.pnfsoftware.jeb.core.units.code.android import IApkUnit, IDexUnit 6 | from com.pnfsoftware.jeb.core.units.code.android.dex import IDalvikInstruction 7 | from com.pnfsoftware.jeb.util.encoding import Conversion 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | Usage: 12 | - Position the caret on a resource id in XML (eg, ' list_of_usage_addresses 50 | # opti: to avoid rebuilding the map each time the script is called, we cache it in the client transient store 51 | immdb = ctx.getTransientStore().get('intImmDb') 52 | if immdb == None: 53 | immdb = {} 54 | ctx.getTransientStore().put('intImmDb', immdb) 55 | for m in dex.getMethods(): 56 | if m.isInternal(): 57 | ci = m.getData().getCodeItem() 58 | if ci: 59 | for insn in ci.getInstructions(): 60 | for param in insn.getParameters(): 61 | if param.getType() == IDalvikInstruction.TYPE_IMM: 62 | val = param.getValue() 63 | addr = '%s+%Xh' % (m.getSignature(False), insn.getOffset()) 64 | if val not in immdb: 65 | immdb[val] = [] 66 | immdb[val].append(addr) 67 | #print(immdb) 68 | 69 | candidate_addresses = immdb.get(resid) 70 | if not candidate_addresses: 71 | print('No location found') 72 | return 73 | 74 | # pick the best candidate xref; if there are multiple, pop-up a dialog and let the user pick 75 | if len(candidate_addresses) == 1: 76 | addr = candidate_addresses[0] 77 | else: 78 | rows = [] 79 | for candidate_address in candidate_addresses: 80 | rows.append([candidate_address]) 81 | print 'Found %d locations: %s' % (len(candidate_address), candidate_address) 82 | index = ctx.displayList('Select a location (%d candidates)' % len(candidate_addresses), None, ['Addresses'], rows) 83 | if index < 0: 84 | return 85 | addr = candidate_addresses[index] 86 | print('Navigating to: %s' % addr) 87 | 88 | # find, bring up, and focus on the DEX unit Disassembly fragment 89 | for view in ctx.getViews(dex): 90 | for fragment in view.getFragments(): 91 | if view.getFragmentLabel(fragment) == 'Disassembly': 92 | view.setFocus() 93 | view.setActiveFragment(fragment) 94 | fragment.setActiveAddress(addr) 95 | return 96 | print('Assembly fragment was not found?') 97 | -------------------------------------------------------------------------------- /scripts/ApkManifestView.py: -------------------------------------------------------------------------------- 1 | #?description=Retrieve the object representing an Android Manifest 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IApkUnit 5 | from com.pnfsoftware.jeb.core.output.text import TextDocumentUtil 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class ApkManifestView(IScript): 10 | 11 | def run(self, ctx): 12 | # current IRuntimeProject 13 | prj = ctx.getMainProject() 14 | assert prj, 'Need a project' 15 | 16 | # find the first IApkUnit in the project 17 | apk = prj.findUnit(IApkUnit) 18 | assert apk, 'Need an apk unit' 19 | 20 | # retrieve the IXmlUnit representing the Android Manifest 21 | man = apk.getManifest() 22 | assert man, 'The Manifest was not found' 23 | 24 | # 1) print the manifest (first presentation offered by the unit) 25 | doc = man.getFormatter().getPresentation(0).getDocument() 26 | print(TextDocumentUtil.getText(doc)) 27 | 28 | # 2) retrieve the org.w3c.dom.Document 29 | xmldoc = man.getDocument() 30 | # ... 31 | 32 | doc.dispose() # always dispose unit-generated documents 33 | -------------------------------------------------------------------------------- /scripts/AutorunTest1.py.DISABLED: -------------------------------------------------------------------------------- 1 | #?description=This client script will be autorun at start-up (1), project creation (2), and project reloaded (3) 2 | #?autorun=1,2,3 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | """ 5 | *** TO ENABLE THIS SCRIPT, REMOVE THE .DISABLED extension *** 6 | 7 | Sample script for JEB Decompiler.. 8 | 9 | Values for the metadata key autorun (CSL): 10 | 0 (no autorun), 1 (after start-up), 2 (after project creation), 3 (after project reloaded) [default:0] 11 | Values for the metadata key autorun_priority: 12 | any floating value, a high value means a high priority [default:0] 13 | 14 | NOTE the priority of this script: it was left to use the implicit default (0). 15 | At start-up, this script will be run after AutorunTest2, whose priority is a 5. 16 | 17 | Requires JEB 4.22+ 18 | """ 19 | class AutorunTest1(IScript): 20 | def run(self, ctx): 21 | print('AutorunTest1: This script was automatically executed by the client') 22 | -------------------------------------------------------------------------------- /scripts/AutorunTest2.py.DISABLED: -------------------------------------------------------------------------------- 1 | #?description=This client script will be autorun at start-up (1) with priority 5 2 | #?autorun=1 3 | #?autorun_priority=5 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | """ 6 | *** TO ENABLE THIS SCRIPT, REMOVE THE .DISABLED extension *** 7 | 8 | Sample script for JEB Decompiler.. 9 | 10 | Values for the metadata key autorun (CSL): 11 | 0 (no autorun), 1 (after start-up), 2 (after project creation), 3 (after project reloaded) [default:0] 12 | Values for the metadata key autorun_priority: 13 | any floating value, a high value means a high priority [default:0] 14 | 15 | NOTE the priority of this script: it was set to 5. 16 | It will be executed before AutorunTest1, whose priority is a default 0. 17 | 18 | Requires JEB 4.22+ 19 | """ 20 | class AutorunTest2(IScript): 21 | def run(self, ctx): 22 | print('AutorunTest2: This script was automatically executed by the client') 23 | -------------------------------------------------------------------------------- /scripts/BookmarkList.py: -------------------------------------------------------------------------------- 1 | #?description=List custom bookmarks saved by BookmarkSet 2 | #?shortcut= 3 | #?deprecated 4 | import datetime 5 | import json 6 | import time 7 | from com.pnfsoftware.jeb.client.api import IScript, IconType, ButtonGroupType 8 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 9 | from BookmarkSet import BookmarkSet 10 | """ 11 | Sample script for JEB Decompiler. 12 | 13 | For demo purposes only, this script should not be used to list bookmarks. 14 | JEB uses its own bookmarks facility for projects. 15 | """ 16 | class BookmarkList(IScript): 17 | 18 | def run(self, ctx): 19 | prj = ctx.getMainProject() 20 | assert prj, 'Need a project' 21 | 22 | bmstr = prj.getData(BookmarkSet.BMKEY) 23 | if not bmstr: 24 | ctx.displayMessageBox('Bookmarks', 'No recorded boolmarks yet!', IconType.INFORMATION, None) 25 | return 26 | 27 | bm = json.loads(bmstr) 28 | log('Current bookmarks (%d): %s' % (len(bm), bm)) 29 | 30 | headers = ['Timestamp', 'Full Unit Path', 'Name', 'Fragment', 'Address', 'Comment'] 31 | rows = [] 32 | for uid, labelmap in bm.items(): 33 | for label, addrmap in labelmap.items(): 34 | for addr, e in addrmap.items(): 35 | unitpath, unitname, comment, ts = e 36 | # note we're appended uid, but it won't be displayed (per the header's spec above, which specifies 6 columns - not 7) 37 | rows.append([datetime.datetime.fromtimestamp(ts).ctime(), unitpath, unitname, label, addr, comment, uid]) 38 | 39 | index = ctx.displayList('Bookmarks', 'List of currently set bookmarks in the active project', headers, rows) 40 | if index < 0: 41 | return 42 | 43 | sel = rows[index] 44 | uid, label, addr = int(sel[6]), sel[3], sel[4] 45 | log('Selected: uid=%d,fragment=%s,addr=%s' % (uid, label, addr)) 46 | 47 | unit = RuntimeProjectUtil.findUnitByUid(prj, uid) 48 | if not unit: 49 | print('Unit with uid=%d was not found in the project or no longer exists!' % uid) 50 | return 51 | 52 | if not ctx.openView(unit): 53 | print('Could not open view for unit!') 54 | else: 55 | f = ctx.findFragment(unit, label, True) 56 | if not f: 57 | print('Fragment "%s" not found!' % label) 58 | elif addr: 59 | f.setActiveAddress(addr) 60 | 61 | def log(s): 62 | print(s) 63 | -------------------------------------------------------------------------------- /scripts/BookmarkSet.py: -------------------------------------------------------------------------------- 1 | #?description=Set and reset custom bookmarks 2 | #?shortcut= 3 | #?deprecated 4 | import datetime 5 | import json 6 | import time 7 | from com.pnfsoftware.jeb.client.api import IScript 8 | from com.pnfsoftware.jeb.core.units import UnitUtil 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | For demo purposes only, this script should not be used to list bookmarks. 13 | JEB uses its own bookmarks facility for projects. 14 | """ 15 | class BookmarkSet(IScript): 16 | BMKEY = 'BOOKMARKS' 17 | 18 | def run(self, ctx): 19 | f = ctx.getFocusedFragment() 20 | if not f: 21 | print('Set the focus on a UI fragment, and position the caret at the location you would like to bookmark.') 22 | return 23 | 24 | label = ctx.getFocusedView().getFragmentLabel(f) 25 | addr = f.getActiveAddress() 26 | unit = f.getUnit() 27 | uid = unit.getUid() 28 | unitname = unit.getName() 29 | unitpath = UnitUtil.buildFullyQualifiedUnitPath(unit) 30 | 31 | log('Unit: %d (%s)' % (uid, unitpath)) 32 | log('Address: %s' % addr) 33 | log('Fragment: %s' % label) 34 | 35 | prj = ctx.getMainProject() 36 | bmstr = prj.getData(BookmarkSet.BMKEY) 37 | if bmstr != None: 38 | bm = json.loads(bmstr) 39 | else: 40 | bm = {} 41 | 42 | #log('Current bookmarks (%d): %s' % (len(bm), bm)) 43 | log('Current bookmarks: %d' % len(bm)) 44 | 45 | labelmap = bm.get(str(uid)) 46 | if labelmap == None: 47 | labelmap = {} 48 | bm[uid] = labelmap 49 | 50 | addrmap = labelmap.get(label) 51 | if addrmap == None: 52 | addrmap = {} 53 | labelmap[label] = addrmap 54 | 55 | e = addrmap.get(addr) 56 | if e: 57 | log('Found existing entry') 58 | comment = e[2] 59 | savedts = e[3] 60 | title = 'Update a bookmark' 61 | caption = 'Current comment. (Clear to delete the bookmark.)\nSet on ' + datetime.datetime.fromtimestamp(savedts).ctime() 62 | else: 63 | comment = 'bookmarked' 64 | title = 'Add a bookmark' 65 | caption = 'Optional comment.' 66 | 67 | comment = ctx.displayQuestionBox(title, caption, comment) 68 | if comment == None: 69 | return 70 | 71 | ts = time.time() 72 | if comment == '': 73 | log('Removing entry') 74 | if addr in addrmap: 75 | del addrmap[addr] 76 | else: 77 | log('Adding/modifying entry') 78 | addrmap[addr] = [unitpath, unitname, comment, ts] 79 | 80 | prj.setData(BookmarkSet.BMKEY, json.dumps(bm), True) 81 | 82 | def log(s): 83 | pass#print(s) 84 | -------------------------------------------------------------------------------- /scripts/BreakpointsLoad.py: -------------------------------------------------------------------------------- 1 | #?description=Reload previously saved breakpoints onto the current debugging session. (The session must be started first.) 2 | #?shortcut= 3 | import json 4 | import os 5 | import time 6 | from com.pnfsoftware.jeb.client.api import IScript 7 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 8 | from com.pnfsoftware.jeb.core.units.code.debug import IDebuggerUnit 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | Reload previously saved breakpoints onto the current debugging session. The session must be started first. 13 | - Breakpoints file: [JEB]/bin/breakpoints.txt 14 | - See converse script to save breakpoints ot a file: BreakpointsSave.py 15 | """ 16 | class BreakpointsLoad(IScript): 17 | 18 | def run(self, ctx): 19 | prj = ctx.getMainProject() 20 | assert prj, 'Need a project' 21 | 22 | prjname = prj.getName() 23 | 24 | prgdir = ctx.getProgramDirectory() 25 | bpfile = os.path.join(prgdir, 'breakpoints.txt') 26 | with open(bpfile, 'r+') as f: 27 | try: 28 | bpdict = json.load(f) 29 | except: 30 | bpdict = {} 31 | #print('Current breakpoints file:', bpdict) 32 | 33 | d = bpdict.get(prjname, None) 34 | if not d: 35 | print('Nothing to reload') 36 | return 37 | 38 | # get the first code unit available (code units re interactive units) 39 | units = RuntimeProjectUtil.findUnitsByType(prj, IDebuggerUnit, False) 40 | if not units: 41 | print('No unit available') 42 | return 43 | 44 | cnt = 0 45 | for dbg in units: 46 | a = d.get(dbg.getName(), None) 47 | if a: 48 | print(a) 49 | for entry in a: 50 | address = entry['address'] 51 | enabled = entry['enabled'] 52 | #print('- Debugger: %s (for %s): %s (%s)' % (dbg.getName(), dbg.getPotentialDebuggees(), address, 'enabled' if enabled else 'disabled')) 53 | bp = dbg.getBreakpoint(address, None) 54 | if not bp: # do not overwrite an already-set breakpoint 55 | bp = dbg.setBreakpoint(address, None) 56 | if bp: 57 | if not enabled: 58 | bp.setEnabled(False) 59 | cnt += 1 60 | else: 61 | print('Cannot restore breakpoint at address: %s' % address) 62 | 63 | print('Breakpoints reloaded: %d.' % cnt) 64 | -------------------------------------------------------------------------------- /scripts/BreakpointsSave.py: -------------------------------------------------------------------------------- 1 | #?description=Save breakpoints (location and state) of the current debugging session to a file 2 | #?shortcut= 3 | import json 4 | import os 5 | import time 6 | from com.pnfsoftware.jeb.client.api import IScript 7 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 8 | from com.pnfsoftware.jeb.core.units.code.debug import IDebuggerUnit 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | Save (persist) the breakpoints (location and state) of the current debugging session to a file. 13 | - Breakpoints file: [JEB]/bin/breakpoints.txt 14 | - See converse script to reload breakpoints onto a debugging session: BreakpointsLoad.py 15 | 16 | The breakpoints file is JSON formatted and can be edited manually as well. Structure 17 | 18 | ProjectName: 19 | DebuggerName: 20 | BreakpointsList: (= dictionary with "address" and "enabled" keys) 21 | 22 | Example: 23 | 24 | { 25 | "/analysis/appcheck-debug.apk.jdb2": { 26 | "VM": [ 27 | { 28 | "address": "Lcom/xyz/appcheck/AppCheck$1;->(Lcom/xyz/appcheck/AppCheck;)V+4h", 29 | "enabled": true 30 | }, 31 | { 32 | "address": "Lcom/xyz/appcheck/AppCheck$1;->run()V+2h", 33 | "enabled": true 34 | } 35 | ] 36 | } 37 | "/analysis/malware_yil1.apk.jdb2": { 38 | "VM": [ 39 | { 40 | "address": "Lcom/malyy/a/b/c;->f()V+0h", 41 | "enabled": false 42 | } 43 | ] 44 | } 45 | } 46 | """ 47 | class BreakpointsSave(IScript): 48 | 49 | def run(self, ctx): 50 | prj = ctx.getMainProject() 51 | assert prj, 'Need a project' 52 | 53 | prjname = prj.getName() 54 | 55 | prgdir = ctx.getProgramDirectory() 56 | bpfile = os.path.join(prgdir, 'breakpoints.txt') 57 | with open(bpfile, 'r+') as f: 58 | try: 59 | bpdict = json.load(f) 60 | except: 61 | bpdict = {} 62 | #print('Current breakpoints file:', bpdict) 63 | 64 | units = RuntimeProjectUtil.findUnitsByType(prj, IDebuggerUnit, False) 65 | if not units: 66 | print('No unit available') 67 | return 68 | 69 | d = {} 70 | cnt = 0 71 | for dbg in units: 72 | # may be null for a detached debugger 73 | bplist = dbg.getBreakpoints() 74 | if bplist: 75 | a = [] 76 | for bp in bplist: 77 | address = bp.getAddress() 78 | enabled = bp.isEnabled() 79 | #print('- Debugger: %s (for %s): %s (%s)' % (dbg.getName(), dbg.getPotentialDebuggees(), address, 'enabled' if enabled else 'disabled')) 80 | a.append({'address': address, 'enabled': enabled}) 81 | cnt += 1 82 | d[dbg.getName()] = a 83 | bpdict[prjname] = d 84 | 85 | with open(bpfile, 'w') as f: 86 | try: 87 | json.dump(bpdict, f, indent=True) 88 | except Exception as e: 89 | print('ERROR: Cannot save to breakpoints file: %s' % e) 90 | 91 | print('Breakpoints saved: %d.' % cnt) 92 | -------------------------------------------------------------------------------- /scripts/CodeLoad.py: -------------------------------------------------------------------------------- 1 | #?description=Reload and apply basic refactoring data on loaded code units 2 | #?shortcut= 3 | import json 4 | import os 5 | import time 6 | from com.pnfsoftware.jeb.client.api import IScript 7 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 8 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | JEB script to reload and apply basic refactoring data onto loaded code units. 13 | - Data file: [JEB]/bin/codedata.txt 14 | - See converse script to persist such data on disk: CodeSave.py 15 | """ 16 | class CodeLoad(IScript): 17 | 18 | def run(self, ctx): 19 | prj = ctx.getMainProject() 20 | assert prj, 'Need a project' 21 | 22 | prjname = prj.getName() 23 | 24 | prgdir = ctx.getProgramDirectory() 25 | datafile = os.path.join(prgdir, 'codedata.txt') 26 | data = {} 27 | if os.path.exists(datafile): 28 | with open(datafile, 'r') as f: 29 | try: 30 | data = json.load(f) 31 | except: 32 | pass 33 | print('Current data:', data) 34 | 35 | d = data.get(prjname, None) 36 | if not d: 37 | print('Nothing to reload') 38 | return 39 | 40 | units = RuntimeProjectUtil.findUnitsByType(prj, ICodeUnit, False) 41 | if not units: 42 | print('No code unit available') 43 | return 44 | 45 | for unit in units: 46 | if not unit.isProcessed(): 47 | continue 48 | 49 | a = d.get(unit.getName(), None) 50 | if a: 51 | renamed_classes = a['renamed_classes'] 52 | renamed_fields = a['renamed_fields'] 53 | renamed_methods = a['renamed_methods'] 54 | comments = a['comments'] 55 | for sig, name in renamed_classes.items(): 56 | unit.getClass(sig).setName(name) 57 | for sig, name in renamed_fields.items(): 58 | unit.getField(sig).setName(name) 59 | for sig, name in renamed_methods.items(): 60 | unit.getMethod(sig).setName(name) 61 | # note: comments are applied last since `addr` can be a refactored one here 62 | for addr, comment in comments.items(): 63 | unit.setInlineComment(addr, comment) 64 | 65 | print('Basic refactoring data was applied') 66 | -------------------------------------------------------------------------------- /scripts/CodeSave.py: -------------------------------------------------------------------------------- 1 | #?description=Save basic refactoring data of the project's code units into a file 2 | #?shortcut= 3 | import json 4 | import os 5 | import time 6 | from com.pnfsoftware.jeb.client.api import IScript 7 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 8 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | - Data file: [JEB]/bin/codedata.txt 13 | - See converse script to load and apply that data onto a fresh project: CodeLoad.py 14 | """ 15 | class CodeSave(IScript): 16 | 17 | def run(self, ctx): 18 | prj = ctx.getMainProject() 19 | assert prj, 'Need a project' 20 | 21 | prjname = prj.getName() 22 | 23 | prgdir = ctx.getProgramDirectory() 24 | datafile = os.path.join(prgdir, 'codedata.txt') 25 | data = {} 26 | if os.path.exists(datafile): 27 | with open(datafile, 'r') as f: 28 | try: 29 | data = json.load(f) 30 | except: 31 | pass 32 | #print('Current data:', data) 33 | 34 | units = RuntimeProjectUtil.findUnitsByType(prj, ICodeUnit, False) 35 | if not units: 36 | print('No code unit available') 37 | return 38 | 39 | d = {} 40 | for unit in units: 41 | if not unit.isProcessed(): 42 | continue 43 | 44 | a = {} 45 | d[unit.getName()] = a 46 | 47 | a['renamed_classes'] = {} 48 | a['renamed_fields'] = {} 49 | a['renamed_methods'] = {} 50 | a['comments'] = {} 51 | 52 | for c in unit.getClasses(): 53 | name0 = c.getName(False) 54 | name1 = c.getName(True) 55 | if name0 != name1: 56 | a['renamed_classes'][c.getSignature(False)] = name1 57 | 58 | for f in unit.getFields(): 59 | name0 = f.getName(False) 60 | name1 = f.getName(True) 61 | if name0 != name1: 62 | a['renamed_fields'][f.getSignature(False)] = name1 63 | 64 | for m in unit.getMethods(): 65 | name0 = m.getName(False) 66 | name1 = m.getName(True) 67 | if name0 != name1: 68 | a['renamed_methods'][m.getSignature(False)] = name1 69 | 70 | for addr, comment in unit.getInlineComments().items(): 71 | a['comments'][addr] = comment 72 | 73 | data[prjname] = d 74 | 75 | with open(datafile, 'w') as f: 76 | try: 77 | json.dump(data, f, indent=True) 78 | except Exception as e: 79 | print('ERROR: Cannot save to file: %s' % e) 80 | 81 | print('Basic refactoring data recorded to file: %s' % datafile) 82 | -------------------------------------------------------------------------------- /scripts/CreateEmptyClass.py: -------------------------------------------------------------------------------- 1 | #?description=Create a native code class into the first found native code unit 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 5 | from com.pnfsoftware.jeb.core.units.code.asm.type import TypeUtil 6 | """ 7 | Script for JEB Decompiler. 8 | """ 9 | class CreateEmptyClass(IScript): 10 | 11 | def run(self, ctx): 12 | prj = ctx.getMainProject() 13 | assert prj, 'Need a project' 14 | 15 | unit = prj.findUnit(INativeCodeUnit) 16 | assert unit, 'Need a native code unit' 17 | 18 | # class type (abstract) 19 | cname = 'Class001' 20 | typeman = unit.getTypeManager() 21 | #tInt = typeman.getType('int') 22 | ctype = typeman.getType(cname) 23 | if not ctype: 24 | ctype = typeman.createClassType(cname, 1, 0) 25 | typeman.completeClassTypeInitialization(ctype) 26 | print(ctype) 27 | 28 | # class item (concrete) 29 | classman = unit.getClassManager() 30 | c = classman.createClass(ctype) 31 | classman.completeClassInitialization(c) 32 | print(c) 33 | 34 | # make the first routine found in the code a static method of that class 35 | m = unit.getInternalMethods().get(0) 36 | if m: 37 | classman.addStaticMethod(c, m) 38 | 39 | unit.notifyGenericChange() 40 | 41 | #tS1 = typeman.createStructure('MyStruct1') 42 | #typeman.addStructureField(tS1, 'a', tInt) 43 | #typeman.addStructureField(tS1, 'b', TypeUtil.buildArrayType(typeman, 'unsigned char', 2, 3)) 44 | -------------------------------------------------------------------------------- /scripts/CreateExtraDocument.py: -------------------------------------------------------------------------------- 1 | #?description=Generate an extra document for the first found interactive unit 2 | #?shortcut= 3 | from java.util import ArrayList 4 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 5 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 6 | from com.pnfsoftware.jeb.core.output import AbstractUnitRepresentation, UnitRepresentationAdapter 7 | from com.pnfsoftware.jeb.core.output.text.impl import Line, StaticTextDocument 8 | from com.pnfsoftware.jeb.core.units import IInteractiveUnit 9 | """ 10 | Sample script JEB Decompiler. 11 | 12 | The newly-created document is called 'Quotes'. 13 | """ 14 | class CreateExtraDocument(IScript): 15 | def run(self, ctx): 16 | prj = ctx.getMainProject() 17 | assert prj, 'Need a project' 18 | 19 | # pick the first interactive unit 20 | unit = prj.findUnit(IInteractiveUnit) 21 | assert unit, 'Need a unit' 22 | print('Unit: %s' % unit) 23 | 24 | # retrieve the formatter, which is a producer of unit representations 25 | formatter = unit.getFormatter() 26 | 27 | # create an extra document (text document), wrap it in a representtion 28 | lines = ArrayList() 29 | lines.add(Line('There are two hard problems in computer science: cache invalidation, naming things, and off-by-one errors.')) 30 | lines.add(Line(' - Phil Karlton (and others)')) 31 | extraDoc = StaticTextDocument(lines) 32 | extraPres = UnitRepresentationAdapter(100, 'Quotes', False, extraDoc) 33 | 34 | # add the newly created representation to our unit, and notify clients 35 | # the second argument indicates that the presentation should be persisted when saving the project 36 | formatter.addPresentation(extraPres, True) 37 | unit.notifyGenericChange() 38 | 39 | # done - if you are running a UI client, the additional document should be displayed 40 | # in a fragment view (eg, sub-tab in the case of the official RCP client) 41 | -------------------------------------------------------------------------------- /scripts/CreateExtraDocumentTableTree.py: -------------------------------------------------------------------------------- 1 | #?description=Generate extra table and tree documents for the first found interactive unit 2 | #?shortcut= 3 | from java.util import ArrayList, Arrays 4 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 5 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 6 | from com.pnfsoftware.jeb.core.output import AbstractUnitRepresentation, UnitRepresentationAdapter, ItemClassIdentifiers 7 | from com.pnfsoftware.jeb.core.output.table.impl import StaticTableDocument, TableRow, Cell 8 | from com.pnfsoftware.jeb.core.output.tree.impl import StaticTreeDocument, Node, KVNode 9 | from com.pnfsoftware.jeb.core.units import IInteractiveUnit 10 | """ 11 | Sample script for JEB Decompiler. 12 | 13 | Will create 2 documents named 'Demo Tree' and 'Demo Table' 14 | """ 15 | class CreateExtraDocumentTableTree(IScript): 16 | def run(self, ctx): 17 | prj = ctx.getMainProject() 18 | assert prj, 'Need a project' 19 | 20 | unit = prj.findUnit(IInteractiveUnit) 21 | assert unit, 'Need a unit' 22 | print('Unit: %s' % unit) 23 | 24 | # retrieve the formatter, which is a producer of unit representations 25 | formatter = unit.getFormatter() 26 | 27 | # create a table document 28 | columnLabels = Arrays.asList('Key', 'Value', 'Comment') 29 | rows = ArrayList() 30 | rows.add(TableRow(Arrays.asList(Cell('foo'), Cell('bar'), Cell('none')))) 31 | rows.add(TableRow(Arrays.asList(Cell('type'), Cell('integer'), Cell('unset')))) 32 | extraDoc = StaticTableDocument(columnLabels, rows) 33 | extraPres0 = UnitRepresentationAdapter(101, 'Demo Table', False, extraDoc) 34 | 35 | # create a tree document 36 | columnLabels = Arrays.asList('Key', 'Value') 37 | root = KVNode('foo', 'bar') 38 | roots = Arrays.asList(root) 39 | root.addChild(KVNode('quantified', 'self')) 40 | root.addChild(KVNode('galaxy', 'milky way')) 41 | node = KVNode('black hole', '42') 42 | node.setClassId(ItemClassIdentifiers.INFO_DANGEROUS) 43 | root.addChild(node) 44 | extraDoc = StaticTreeDocument(roots, columnLabels, -1) 45 | extraPres1 = UnitRepresentationAdapter(102, 'Demo Tree', False, extraDoc) 46 | 47 | # add the newly created presentations to our unit, and notify clients 48 | # the second argument indicates that the presentation should be persisted when saving the project 49 | formatter.addPresentation(extraPres0, True) 50 | formatter.addPresentation(extraPres1, True) 51 | unit.notifyGenericChange() 52 | 53 | # done - if you are running a UI client, the additional document should be displayed 54 | # in a fragment view (eg, sub-tab in the case of the official RCP client) 55 | -------------------------------------------------------------------------------- /scripts/CreateNativeStruct.py: -------------------------------------------------------------------------------- 1 | #?description=Create a native structure type of the provided size, filled with integer primitives 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 5 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 6 | from com.pnfsoftware.jeb.core.units.code.asm.type import TypeUtil 7 | from com.pnfsoftware.jeb.util.encoding import Conversion 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | This script is a UI helper to quickly create a native structure type of a given size. 12 | The structure will be filled with int32 primitives, and padded with one optional int16 and optional int8, if necessary. 13 | """ 14 | class CreateNativeStruct(IScript): 15 | def run(self, ctx): 16 | if not isinstance(ctx, IGraphicalClientContext): 17 | print('This script must be run within a graphical client') 18 | return 19 | 20 | if not ctx.getFocusedView(): 21 | print('Set the focus on a native disassembly fragment') 22 | return 23 | unit = ctx.getFocusedView().getUnit() 24 | if not isinstance(unit, INativeCodeUnit): 25 | print('Set the focus on a native disassembly fragment') 26 | return 27 | 28 | value = ctx.displayQuestionBox('Structure Name', 'Name:', '') 29 | if not value: 30 | print('Provide a structure name') 31 | return 32 | _name = value 33 | 34 | typeman = unit.getTypeManager() 35 | 36 | tS = typeman.getType(_name) 37 | if tS: 38 | print('A type with the following name already exists: %s' % tS) 39 | return 40 | 41 | value = ctx.displayQuestionBox('Structure Size', 'Size (in bytes):', '') 42 | if not value: 43 | print('Provide a structure size') 44 | return 45 | 46 | _sizebytes = Conversion.toInt(value) 47 | if _sizebytes <= 0: 48 | print('Provide a valid structure size') 49 | return 50 | 51 | # create an empty structure type 52 | tS = typeman.createStructure(_name) 53 | 54 | # determine how many fields are needed to fill it up 55 | cnt4 = _sizebytes / 4 56 | pad2, pad1 = 0, 0 57 | r = _sizebytes % 4 58 | if r != 0: 59 | pad2 = _sizebytes / 2 60 | pad1 = _sizebytes % 2 61 | 62 | # fill up the type 63 | tInt32 = typeman.getType('int') 64 | for i in range(cnt4): 65 | typeman.addStructureField(tS, None, tInt32) 66 | if pad2: 67 | tInt16 = typeman.getType('short') 68 | typeman.addStructureField(tS, None, tInt16) 69 | if pad1: 70 | tInt8 = typeman.getType('char') 71 | typeman.addStructureField(tS, None, tInt8) 72 | 73 | print('Newly created structure type: %s' % tS) 74 | -------------------------------------------------------------------------------- /scripts/CustomizeMetadata.py: -------------------------------------------------------------------------------- 1 | #?description=Customize the metadata of a code unit (and see rendering in the metadata overview bar in GUI) 2 | #?shortcut= 3 | import time 4 | from java.util import ArrayList 5 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 6 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 7 | from com.pnfsoftware.jeb.core.units import IInteractiveUnit, MetadataGroup, MetadataGroupType 8 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit 9 | """ 10 | Sample script JEB Decompiler. 11 | 12 | Customize the metadata of a code unit (and see rendering in the metadata overview bar in GUI). 13 | The beginning of every class will be marked and rendered as a light green in the navigation bar. 14 | """ 15 | class CustomizeMetadata(IScript): 16 | 17 | def run(self, ctx): 18 | prj = ctx.getMainProject() 19 | assert prj, 'Need a project' 20 | 21 | # pick the first code unit offered by the project 22 | unit = prj.findUnit(ICodeUnit) 23 | print('Unit: %s' % unit) 24 | 25 | # the metadata manager is optional (a unit may choose to not provide one) 26 | mm = unit.getMetadataManager() 27 | if not mm: 28 | print('The unit does not have a metadata manager') 29 | return 30 | 31 | # create a new metadata group that will hold RGB color values 32 | g = mm.getGroupByName('custom') 33 | if not g: 34 | print('Creating new metadata group (type: RGB) ...') 35 | g = MetadataGroup('custom', MetadataGroupType.RGB) 36 | mm.addGroup(g) 37 | print('Done') 38 | 39 | # mark the beginning of every class; they will be rendered in the overview bar in the client 40 | for iclass, c in enumerate(unit.getClasses()): 41 | targetAddress = c.getAddress() 42 | 43 | print('Adding a piece of metadata at address "%s" ...' % targetAddress) 44 | g.setData(targetAddress, 0x00FF30) 45 | print('Done') 46 | 47 | print('If the unit has a text document representation with a an Overview bar, do a Refresh to visualize the metadata') 48 | 49 | print('Listing all metadata for this unit (if possible) ...') 50 | for g1 in mm.getGroups(): 51 | print('- Group %s (type: %s)' % (g1.getName(), g1.getType())) 52 | alldata = g1.getAllData() 53 | if alldata == None: 54 | print('(This group manager does not allow metadata enumeration)') 55 | else: 56 | for k in alldata: 57 | print(' - at "%s" -> %s' % (k, alldata[k])) 58 | 59 | unit.notifyGenericChange() 60 | -------------------------------------------------------------------------------- /scripts/DalvikDataFlow.py: -------------------------------------------------------------------------------- 1 | #?description=Examine Dalvik method Control Flow Graph (CFG) and perform data flow analysis on the graph to look at register definition locations and their uses, and register use locations and their definitions 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | 8 | This script is showing how to examine Dalvik method Control Flow Graph (CFG) and perform 9 | data flow analysis (DFA) on the graph to look at register definition locations and their 10 | uses, and register use locations and their definitions. 11 | 12 | What is Data Flow Analysis: a starting point at https://en.wikipedia.org/wiki/Data-flow_analysis 13 | API documentation: start with https://www.pnfsoftware.com/jeb/apidoc/reference/com/pnfsoftware/jeb/core/units/code/IDFA3.html 14 | 15 | How to use this sample script: 16 | - open a DEX file 17 | - position the caret somewhere on a Dalvik instruction in the Disassembly view 18 | - run the script to see the uses and/or definitions of the registers manipulated by the instruction 19 | """ 20 | class DalvikDataFlow(IScript): 21 | 22 | def run(self, ctx): 23 | prj = ctx.getMainProject() 24 | assert prj, 'Need a project' 25 | 26 | dex = prj.findUnit(IDexUnit) 27 | assert prj, 'Need a dex unit' 28 | 29 | addr = ctx.getFocusedView().getActiveFragment().getActiveAddress() 30 | pos = addr.find('+') 31 | if pos >= 0: 32 | mname = addr[0:pos] 33 | off = int(addr[pos + 1:].rstrip('h'), 16) 34 | else: 35 | mname = addr 36 | off = 0 37 | 38 | m = dex.getMethod(mname) 39 | if not m: 40 | print('No method found at address: %s' % addr) 41 | return 42 | 43 | print('Method: %s, Offset: 0x%X' % (mname, off)) 44 | 45 | cfg = m.getData().getCodeItem().getControlFlowGraph() 46 | dfa = cfg.doDataFlowAnalysis() 47 | print('CFG for method was retrieved, DFA was performed') 48 | 49 | insn = cfg.getInstructionAt(off) 50 | print('Instruction on caret: %s' % insn) 51 | 52 | # DU-map: instruction -> map(register, list(instructions using the defined register)) 53 | dumap = dfa.getDefUseChains(insn.getOffset()) 54 | for regid, addrlist in dumap.items(): 55 | print('[du] r%d: used at %s' % (regid, ','.join(['0x%X ' % (addr) for addr in addrlist]))) 56 | 57 | # UD-map: instruction -> map(register, list(instructions using the defined register)) 58 | udmap = dfa.getUseDefChains(insn.getOffset()) 59 | for regid, addrlist in udmap.items(): 60 | print('[ud] r%d: defined at %s' % (regid, ','.join(['0x%X ' % (addr) for addr in addrlist]))) 61 | -------------------------------------------------------------------------------- /scripts/DecompileSingleMethod.py: -------------------------------------------------------------------------------- 1 | #?description=Decompile a single method. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.util import DecompilerHelper 5 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit, IDecompilerUnit, DecompilationContext, DecompilationOptions 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | The class containing the target method is itself not decompiled; inner classes of the method, if any, are not decompiled either. 10 | The decompiled code is displayed in a text box. 11 | """ 12 | class DecompileSingleMethod(IScript): 13 | 14 | def run(self, ctx): 15 | self.ctx = ctx 16 | 17 | f = ctx.getFocusedFragment() 18 | if not f: 19 | print('Set the focus on a UI fragment, and position the caret somewhere in the method you would like to decompile.') 20 | return 21 | 22 | addr = f.getActiveAddress() 23 | unit = f.getUnit() 24 | if not isinstance(unit, ICodeUnit): 25 | print('Not a code unit: %s' % unit) 26 | return 27 | 28 | m = unit.getMethod(addr) 29 | if not m: 30 | print('Not a method at address: %s' % addr) 31 | return 32 | 33 | decomp = DecompilerHelper.getDecompiler(unit) 34 | if not decomp: 35 | print('Cannot acquire decompiler for unit: %s' % decomp) 36 | return 37 | 38 | # *** decompilation Options and Context are optional *** 39 | # here, we're creating an Options object to: 40 | # - override the decompiler settings (if any), and cap method decompilation to 30 seconds 41 | # - prevent the decompilation of inner classes or any deferred decompilations: we decompile the target and only the target 42 | opt = DecompilationOptions.Builder.newInstance().flags(IDecompilerUnit.FLAG_NO_INNER_DECOMPILATION|IDecompilerUnit.FLAG_NO_DEFERRED_DECOMPILATION).maxTimePerMethod(30000).build() 43 | if not decomp.decompileMethod(m.getSignature(), DecompilationContext(opt)): 44 | print('Failed decompiling method') 45 | return 46 | 47 | text = decomp.getDecompiledMethodText(m.getSignature()) 48 | #print(text) 49 | r = ctx.displayText("Decompiled Method: %s" % m.getName(), text, True) 50 | #print(r) 51 | -------------------------------------------------------------------------------- /scripts/DexClassRefactor.py: -------------------------------------------------------------------------------- 1 | #?description=Refactor a dex class, i.e. rename and repackage automatically (if necessary) 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit 5 | from com.pnfsoftware.jeb.core.units.code.android.dex import IDexClass 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class DexClassRefactor(IScript): 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | if not isinstance(ctx, IGraphicalClientContext): 13 | return 14 | 15 | dex = ctx.getFocusedUnit() 16 | assert dex, 'Need focus on dex' 17 | item = ctx.getFocusedItem() 18 | assert item, 'Need caret on item' 19 | o = dex.getObjectById(item.getItemId()) 20 | assert isinstance(o, IDexClass), 'Not a class item' 21 | 22 | r = ctx.displayQuestionBox('Rename', 'Input the new type descriptor:', o.getSignature()) 23 | if not r: 24 | return # user aborted 25 | if not (r.startswith('L') and r.endswith(';')): 26 | return # illegal descriptor 27 | if r == o.getSignature(): 28 | return # nothing to do 29 | 30 | pos = r.rfind('/') 31 | if pos < 0: 32 | psig = '' 33 | cname = r[1:-1] 34 | else: 35 | psig = r[0:pos+1] 36 | cname = r[pos+1:-1] 37 | 38 | print('Renaming class to: %s' % cname) 39 | o.setName(cname) 40 | 41 | pkg = dex.getPackage(psig) 42 | if not pkg: 43 | print('Creating package: %s' % psig) 44 | # note: addPackage() has a glitch, only accepts high-level (dot-separated) forms 45 | pkg = dex.addPackage(psig[1:-1].replace('/', '.')) 46 | if not pkg: 47 | print('Cannot create package: %s' % psig) 48 | return 49 | print('Moving: %s to %s' % (o, pkg)) 50 | 51 | success = dex.moveToPackage(o, pkg) 52 | if success: 53 | dex.notifyGenericChange() 54 | -------------------------------------------------------------------------------- /scripts/DexColorArtificialStrings.py: -------------------------------------------------------------------------------- 1 | #?description=Visualize artificial (e.g. decrypted) strings in the navbar 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 5 | from com.pnfsoftware.jeb.core.output import ItemClassIdentifiers 6 | from com.pnfsoftware.jeb.core.units import MetadataGroup, MetadataGroupType, AddressPrefixMetadataGroup 7 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 8 | from com.pnfsoftware.jeb.core.units.code.android.dex import DexPoolType 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | This script will update the navbar and generate colors where artificial strings are being used. 13 | Artificial strings are strings that were not originally present in the dex pool(s), e.g. auto-decrypted strings. 14 | 15 | Demo: 16 | - open com.parental.control.v4.apk 17 | - run a Global Analysis 18 | - execute the script 19 | """ 20 | class DexColorArtificialStrings(IScript): 21 | def run(self, ctx): 22 | f = ctx.getFocusedFragment() 23 | if not f: 24 | print('Set the focus on a UI fragment, and position the caret on a valid address') 25 | return 26 | 27 | unit = f.getUnit() 28 | if not isinstance(unit, IDexUnit): 29 | print('Focus a fragment representing a dex unit') 30 | return 31 | 32 | mm = unit.getMetadataManager() 33 | if not mm: 34 | print('The unit does not have a metadata manager') 35 | return 36 | 37 | g = mm.getGroupByName('astrings') 38 | if not g: 39 | g = AddressPrefixMetadataGroup(unit, 'astrings') 40 | mm.addGroup(g) 41 | 42 | for s in unit.getStrings(): 43 | if s.isArtificial(): 44 | #print('String: %s' % s) 45 | refs = unit.getReferenceManager().getReferences(DexPoolType.STRING, s.getIndex()) 46 | for ref in refs: 47 | a = ref.getInternalAddress() 48 | #print('Coloring at: %s' % a) 49 | pos = a.find('+') 50 | if pos >= 0: a = a[:pos] 51 | success = g.setData(a, ItemClassIdentifiers.STRING_GENERATED) 52 | 53 | unit.notifyGenericChange() 54 | -------------------------------------------------------------------------------- /scripts/DexColorPackage.py: -------------------------------------------------------------------------------- 1 | #?description= 2 | #?shortcut= 3 | #?nolist 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 6 | from com.pnfsoftware.jeb.core.output import ItemClassIdentifiers 7 | from com.pnfsoftware.jeb.core.units import MetadataGroup, MetadataGroupType, AddressPrefixMetadataGroup 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | Package/Class/Method coloring scrip. 12 | We use the AddressPrefixMetadataGroup object to store metadata information, displayed in a text navbar. 13 | """ 14 | class DexColorPackage(IScript): 15 | def run(self, ctx): 16 | f = ctx.getFocusedFragment() 17 | if not f: 18 | print('Set the focus on a UI fragment, and position the caret on a valid address') 19 | return 20 | 21 | addr = f.getActiveAddress() 22 | unit = f.getUnit() 23 | if not unit or not addr: 24 | print('No unit or no address at current location') 25 | return 26 | 27 | mm = unit.getMetadataManager() 28 | if not mm: 29 | print('The unit does not have a metadata manager') 30 | return 31 | g = mm.getGroupByName(AddressPrefixMetadataGroup.DEFAULT_NAME) 32 | if not g: 33 | g = AddressPrefixMetadataGroup(unit) 34 | mm.addGroup(g) 35 | 36 | addr = unit.getCanonicalAddress(addr) 37 | addrlist = [addr] 38 | # hack: include inner classes if addr is a class address 39 | if addr.find('->') < 0 and addr.endswith(';'): 40 | addrlist.append(addr[:-1] + '$') 41 | 42 | for a in addrlist: 43 | if g.getAllData().get(a) != None: 44 | success = g.setData(a, None) 45 | else: 46 | success = g.setData(a, ItemClassIdentifiers.INFO_NORMAL) 47 | print('%s: %s' % (a, success)) 48 | 49 | unit.notifyGenericChange() 50 | -------------------------------------------------------------------------------- /scripts/DexJumpToActivity.py: -------------------------------------------------------------------------------- 1 | #?description=Jump from an activity name (selected in the Android XML Manifest) to its corresponding bytecode definition in the DEX disassembly fragment 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, IUnitView 4 | from com.pnfsoftware.jeb.core.units import IUnit, IXmlUnit 5 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 6 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 7 | """ 8 | Sample script for JEB Decompiler. 9 | 10 | This JEB UI script allows the user to jump from an activity name (selected in the Android XML 11 | Manifest) to its corresponding bytecode definition in the DEX disassembly fragment. 12 | """ 13 | class DexJumpToActivity(IScript): 14 | 15 | def run(self, ctx): 16 | prj = ctx.getMainProject() 17 | assert prj, 'Need a project' 18 | 19 | if not isinstance(ctx, IGraphicalClientContext): 20 | print('This script must be run within a graphical client') 21 | return 22 | 23 | fragment = ctx.getFocusedView().getActiveFragment() 24 | if type(fragment.getUnit()) is IXmlUnit: 25 | print('Select the Manifest XML view') 26 | return 27 | 28 | # make sure that the fragment has the focus, els the item would not be considered active 29 | aname = fragment.getActiveItemAsText() 30 | if not aname: 31 | print('Select the activity name') 32 | return 33 | 34 | # activity name is relative to the package name 35 | if aname.startswith('.'): 36 | # unit is the Manifest, of type IXmlUnit; we can retrieve the XML document 37 | # note: an alternate way would be to retrieve the top-level IApkUnit, and use getPackageName() 38 | dom = fragment.getUnit().getDocument() 39 | pname = dom.getElementsByTagName("manifest").item(0).getAttribute("package") 40 | #print('Package name: %s' % pname) 41 | aname = pname + aname 42 | 43 | print('Activity name: %s' % aname) 44 | 45 | addr = 'L' + aname.replace('.', '/') + ';' 46 | print('Target address: %s' % addr) 47 | 48 | unit = RuntimeProjectUtil.findUnitsByType(prj, IDexUnit, False).get(0) 49 | if not unit: 50 | print('The DEX unit was not found') 51 | return 52 | 53 | ctx.openView(unit) 54 | # this code assumes that the active fragment is the disassembly (it may not be; strong script should focus the assembly fragment) 55 | ctx.getFocusedView().getActiveFragment().setActiveAddress(addr) 56 | -------------------------------------------------------------------------------- /scripts/DexJumpToFileOffset.py: -------------------------------------------------------------------------------- 1 | #?description= Jump to method code providing a dex file offset 2 | #?shortcut= 3 | 4 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 5 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | Providing a file offset and an optional classesN.dex index, find the corresponding method and the 10 | bytecode location, and jump to that location in the disassembly view. 11 | """ 12 | class DexJumpToFileOffset(IScript): 13 | def run(self, ctx): 14 | assert isinstance(ctx, IGraphicalClientContext), 'Must run in GUI' 15 | 16 | prj = ctx.getMainProject() 17 | assert prj, 'Need an opened project' 18 | 19 | dex = prj.findUnit(IDexUnit) 20 | assert dex, 'Need a processed dex' 21 | 22 | val = ctx.displayQuestionBox('Provide a dex file offset', 23 | 'Offset (hex), followed by optional classes.dex file. Examples:\n' 24 | + '1000 => offset 0x1000 in classes.dex; 2000,2 => offset 0x2000 in classes2.dex', '') 25 | if not val: return 26 | val = val.strip() 27 | if val == '': return 28 | elts = val.split(',') 29 | offset = int(elts[0].strip(), 16) 30 | dexidx = 1 if len(elts) < 2 else int(elts[1].strip(), 16) 31 | print('Searching for method bytecode at: offset 0x%X in classes%s.dex' % (offset, '' if dexidx == 1 else str(dexidx))) 32 | 33 | target_addr = None 34 | for m in dex.getMethods(): 35 | if m.isInternal(): 36 | code = m.getData().getCodeItem() 37 | if code: 38 | if code.getDexFileIndex() + 1 == dexidx: 39 | start = code.getInstructionsOffset() 40 | if offset >= start and offset < start + code.getInstructionsSize(): 41 | target_addr = m.getAddress() 42 | moff = offset - start 43 | for insn in code.getInstructions(): 44 | insn_offset = insn.getOffset() 45 | if moff >= insn_offset and moff < insn_offset + insn.getSize(): 46 | target_addr += '+%Xh' % insn_offset 47 | 48 | if not target_addr: 49 | print('Not found.') 50 | return 51 | 52 | print('Found: %s' % target_addr) 53 | f = ctx.findFragment(dex, 'Disassembly', True) 54 | f.setActiveAddress(target_addr) 55 | -------------------------------------------------------------------------------- /scripts/DexJumpToResource.py: -------------------------------------------------------------------------------- 1 | #?description=Jump to or display an Android target resource (string, XML, etc.) referenced by an active resource ID item (interactive number) 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, IUnitView 4 | from com.pnfsoftware.jeb.core.units import IUnit, IXmlUnit 5 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil, IUnitFilter 6 | import org.w3c.dom.Document 7 | """ 8 | Sample script for JEB Decompiler. 9 | 10 | This JEB script jumps to or displays an Android target resource (string, XML, etc.) 11 | referenced by an active resource ID item (interactive number). 12 | 13 | Open an APK; in a Dalvik or Java view, set the caret on a resource id; execute the script. 14 | """ 15 | class DexJumpToResource(IScript): 16 | 17 | def run(self, ctx): 18 | prj = ctx.getMainProject() 19 | assert prj, 'Need a project' 20 | 21 | self.activeItemValue = self.getActiveItem(ctx) 22 | if self.activeItemValue: 23 | self.name = None 24 | self.typeC = None 25 | 26 | doc = self.getTargetDoc(prj, 'public.xml') 27 | if not doc: 28 | print('Cannot find public.xml') 29 | return 30 | 31 | self.getValue(doc, 'public.xml', ctx) 32 | 33 | if self.name and self.typeC: 34 | if self.typeC == "string" or self.typeC == "id": 35 | self.value = None 36 | doc = self.getTargetDoc(prj, self.typeC + 's.xml') 37 | self.getValue(doc, self.typeC + 's.xml', ctx) 38 | ctx.displayText("Id: " + self.activeItemValue, "Value: " + self.value, False) 39 | else: 40 | self.jumpToTargetFile(prj, ctx) # Open the target file 41 | 42 | # TODO: dispose docs 43 | 44 | 45 | def getActiveItem(self, ctx): 46 | curItem = ctx.getFocusedView().getActiveFragment().getActiveItem() 47 | if curItem == None: 48 | return None 49 | # hack: only work for 32-bit numbers, may be disabled in the future 50 | activeItemVal = str(hex(curItem.getItemId() & 0xFFFFFFFF))[:-1] 51 | return activeItemVal.lower() 52 | 53 | 54 | def getTargetDoc(self, prj, targetXML): 55 | units = RuntimeProjectUtil.findUnitsByType(prj, IXmlUnit, False) 56 | for unit in units: 57 | if not unit.isProcessed(): 58 | unit.process() 59 | if unit.getName() == targetXML: 60 | doc = unit.getDocument() 61 | return doc 62 | return None 63 | 64 | 65 | def getValue(self, doc, targetXML, ctx): 66 | #print(targetXML) 67 | if (targetXML == 'public.xml'): 68 | xmlLists = doc.getElementsByTagName('public'); 69 | for i in range(xmlLists.getLength()): 70 | node = xmlLists.item(i) 71 | if(self.activeItemValue == str(node.getAttribute('id'))): 72 | self.name = node.getAttribute('name') 73 | self.typeC = node.getAttribute('type') 74 | return 75 | if (targetXML == 'ids.xml'): 76 | xmlLists = doc.getElementsByTagName('item'); 77 | for i in range(xmlLists.getLength()): 78 | node = xmlLists.item(i) 79 | if(self.name == str(node.getAttribute('name'))): 80 | self.value = node.getTextContent() 81 | return 82 | if (targetXML == 'strings.xml'): 83 | xmlLists = doc.getElementsByTagName('string'); 84 | for i in range(xmlLists.getLength()): 85 | node = xmlLists.item(i) 86 | if(self.name == str(node.getAttribute('name'))): 87 | self.value = node.getTextContent() 88 | return 89 | print('Cannot find target XML file') 90 | 91 | 92 | def jumpToTargetFile(self, prj, ctx): 93 | unitFilter = UnitFilterByName(self.name + '.xml') 94 | unit = RuntimeProjectUtil.filterUnits(prj, unitFilter).get(0) 95 | if unit: 96 | ctx.openView(unit) 97 | return 98 | 99 | 100 | class UnitFilterByName(IUnitFilter): 101 | def __init__(self, name): 102 | self.name = name 103 | def check(self, unit): 104 | return unit.getName() == self.name 105 | -------------------------------------------------------------------------------- /scripts/DexListDecompilationEvents.py: -------------------------------------------------------------------------------- 1 | #?description=List a dexdec object (IDexDecompilerUnit) decompilation events stored in the global event queue 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, IDexDecompilerUnit 4 | from com.pnfsoftware.jeb.core.util import DecompilerHelper 5 | from com.pnfsoftware.jeb.client.api import IScript, IconType, ButtonGroupType 6 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 7 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit, ICodeItem 8 | from com.pnfsoftware.jeb.core.output.text import ITextDocument 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | Note that dexdec's events can be seen in a Decompiler's unit node (Project Explorer view), "Events" tab 13 | """ 14 | class DexListDecompilationEvents(IScript): 15 | def run(self, ctx): 16 | prj = ctx.getMainProject() 17 | assert prj, 'Need a project' 18 | for dexdec in prj.findUnits(IDexDecompilerUnit): 19 | print('Listing events for %s:' % dexdec) 20 | for e in dexdec.getGlobalDecompilationEvents(): 21 | print(e) 22 | -------------------------------------------------------------------------------- /scripts/DexListMethods.py: -------------------------------------------------------------------------------- 1 | #?description=List methods of all dex units found in the current project 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class DexListMethods(IScript): 9 | 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | assert prj, 'Need a project' 13 | 14 | # replace IDexUnit by ICodeUnit to find all code units (that includes dex units as well) 15 | for codeUnit in prj.findUnits(IDexUnit): 16 | self.processDex(codeUnit) 17 | 18 | def processDex(self, unit): 19 | for m in unit.getMethods(): 20 | print m.getSignature() 21 | -------------------------------------------------------------------------------- /scripts/DexdecDisableEmulation.py: -------------------------------------------------------------------------------- 1 | #?description=Change the propertis of a dexdec instance programmatically 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IDexDecompilerUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | 8 | Reference doc to review: 9 | - IPropertyDefinitionManager 10 | - IPropertyManager 11 | """ 12 | class DexdecDisableEmulation(IScript): 13 | def run(self, ctx): 14 | prj = ctx.getMainProject() 15 | assert prj, 'Need a project' 16 | u = prj.findUnit(IDexDecompilerUnit) 17 | if u: 18 | # the associated PDM (property definition manager) of a PM (property manager) lists the properties, their types, legal values, etc. 19 | # they are also listed here for reference: https://www.pnfsoftware.com/jeb/manual/engines-configuration/ 20 | # other objects can be configured via a PM, including JEB's engines context and its primary GUI client 21 | u.getPropertyManager().setInteger('EmulationSupport', 0) # disable the emulator! 22 | -------------------------------------------------------------------------------- /scripts/DumpBinaryUnits.py: -------------------------------------------------------------------------------- 1 | #?description=Dump all binary units of the opened project to files on disk 2 | #?shortcut= 3 | import os 4 | from java.io import File 5 | from com.pnfsoftware.jeb.client.api import IScript 6 | from com.pnfsoftware.jeb.core.units import IBinaryUnit 7 | from com.pnfsoftware.jeb.util.io import IO 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | Dump all binary units of a JEB opened project to files on disk. 12 | Output folder= [HOME]/JEB_PROJECT_BINARY_EXTRACTED 13 | """ 14 | class DumpBinaryUnits(IScript): 15 | 16 | def run(self, ctx): 17 | prj = ctx.getMainProject() 18 | assert prj, 'Need a project' 19 | 20 | #OUTDIR = IO.expandPath('~/JEB_PROJECT_BINARY_EXTRACTED') 21 | OUTDIR = ctx.displayFolderSelector('Target folder where the dumped files will be written') 22 | print('=> Dumping binary units of project to directory: %s' % OUTDIR) 23 | 24 | for art in prj.getLiveArtifacts(): 25 | for unit in art.getUnits(): 26 | self.checkUnit(unit, OUTDIR+'/'+art.getArtifact().getName()) 27 | 28 | def checkUnit(self, unit, basename, level=0): 29 | basename2 = basename+'/'+unit.getName() 30 | 31 | unitsize = -1 32 | if isinstance(unit, IBinaryUnit): 33 | unitinput = unit.getInput() 34 | print('Creating dir: %s' % basename) 35 | if not os.path.exists(basename): 36 | os.makedirs(basename) 37 | try: 38 | print('Writing file: %s (%db)' % (basename2, unitinput.getCurrentSize())) 39 | f = File(basename2) 40 | data = IO.readInputStream(unitinput.getStream()) 41 | IO.writeFile(f, data, 0, len(data)) 42 | except Exception as e: 43 | print('An error occurred: %s' % e) 44 | basename2 = basename2 + '_sub' 45 | 46 | # recurse over children units 47 | for c in unit.getChildren(): 48 | self.checkUnit(c, basename2, level + 1) 49 | -------------------------------------------------------------------------------- /scripts/EditNativeBytes.py: -------------------------------------------------------------------------------- 1 | #?description=Write memory bytes of a native unit 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.util.encoding import Conversion 5 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 6 | """ 7 | Sample script for JEB Decompiler 8 | """ 9 | class EditNativeBytes(IScript): 10 | 11 | # ctx:IGraphicalClientContext 12 | def run(self, ctx): 13 | f = ctx.getFocusedFragment() 14 | assert f, 'Need a focused fragment' 15 | 16 | text = ctx.displayQuestionBox("Write byte", "Address:", "") 17 | if not text: 18 | return 19 | addr = int(text, 16) 20 | 21 | text = ctx.displayQuestionBox("Write byte", "Value to write at 0x%X:" % addr, "") 22 | if not text: 23 | return 24 | val = int(text, 16) 25 | 26 | u = f.getUnit() 27 | if not isinstance(u, INativeCodeUnit): 28 | return 29 | 30 | u.undefineItem(addr) 31 | u.getMemory().writeByte(addr, val) 32 | u.notifyGenericChange() 33 | -------------------------------------------------------------------------------- /scripts/FindMain.py: -------------------------------------------------------------------------------- 1 | #?description=Attempt to find the high-level entry-point of a binary file 2 | #?shortcut= 3 | 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 6 | 7 | ''' 8 | Sample script for JEB Decompiler. 9 | 10 | Search for the high-level entry-point (the main() or similar) of a compiled binary file. 11 | Currently supported: MSVC WinMain/wWinMain x86 and x64 12 | 13 | Requirement: JEB 5.7+ 14 | ''' 15 | class FindMain(IScript): 16 | def run(self, ctx): 17 | prj = ctx.getMainProject() 18 | u = prj.findUnit(INativeCodeUnit) 19 | if not u: 20 | print('No native code unit') 21 | return 22 | 23 | hlep = u.getHighLevelEntryPointAddress() 24 | if hlep != -1: 25 | print('[NOTE] A high-level entry-point is already set: 0x%X' % hlep) 26 | 27 | # each detection routine returns either None or a tuple (br_to_hlep, hlep, wanted_name) 28 | r = None 29 | if not r: 30 | r = find_hlep_msvc_x86(u) 31 | if not r: 32 | r = find_hlep_msvc_x64(u) 33 | # NOTE: add more detection code here 34 | if not r: 35 | print('Nothing found') 36 | return 37 | 38 | br_to_hlep, hlep, wanted_name = r[0], r[1], r[2] 39 | print('Found high-level entry-point at 0x%X (branched from 0x%X)' % (hlep, br_to_hlep)) 40 | 41 | cm = u.getCommentManager() 42 | cm.setInlineComment(cm.memoryToAddress(br_to_hlep), 'Jump to high-level entry-point') 43 | 44 | m = u.getInternalMethod(hlep) 45 | if m and wanted_name: 46 | print('Renaming entry-point to \'%s\'' % wanted_name) 47 | m.setName(wanted_name) 48 | 49 | #------------------------------------------------------------------------------ 50 | def find_hlep_msvc_x86(u): 51 | if u.getProcessorName() != 'x86': 52 | return None 53 | 54 | # call __security_init_cookie 55 | # jmp next 56 | ep = u.getEntryPointAddress() 57 | if not check_bytes(u, ep, 'e8 ?? ?? ?? ?? e9 ?? ?? ?? ??'): 58 | return None 59 | 60 | # resolve the jmp target 61 | addr = ep + 5 62 | addr += 5 + u.getMemory().readLEInt(addr + 1) 63 | 64 | # the previous instructions may not have been associated to the routine at addr 65 | m = u.getInternalMethod(addr, False) 66 | if not m: 67 | return None 68 | 69 | cfg = m.getData().getCFG() 70 | br_to_hlep, hlep = -1, -1 71 | for b in cfg: 72 | if b.size() >= 2: 73 | i = b.size() - 2 74 | if b.get(i).getMnemonic() == 'push' and b.get(i + 1).getMnemonic() == 'call': 75 | addr, insn = b.getAddressOfInstruction(i), b.get(i) 76 | if check_bytes(u, addr, '68 ?? ?? ?? ??'): 77 | addr = u.getMemory().readLEInt(addr + 1) 78 | if addr == u.getVirtualImageBase(): 79 | addr, insn = b.getAddressOfInstruction(i+1), b.get(i+1) 80 | hlep = insn.getPrimaryBranchAddress(addr) 81 | br_to_hlep = addr 82 | break 83 | if hlep == -1: 84 | return None 85 | return (br_to_hlep, hlep, 'winmain') 86 | 87 | #------------------------------------------------------------------------------ 88 | def find_hlep_msvc_x64(u): 89 | if u.getProcessorName() != 'x86_64': 90 | return None 91 | 92 | # sub rsp, xxx 93 | # call _security_init_cookie 94 | # add rsp, xxx 95 | # jmp next 96 | ep = u.getEntryPointAddress() 97 | if not check_bytes(u, ep, '48 83 ec ?? e8 ?? ?? ?? ?? 48 83 c4 ?? e9 ?? ?? ?? ??'): 98 | return None 99 | 100 | # resolve the jmp target 101 | addr = ep + 13 102 | addr += 5 + u.getMemory().readLEInt(addr + 1) 103 | 104 | # the previous instructions may not have been associated to the routine at addr 105 | m = u.getInternalMethod(addr, False) 106 | if not m: 107 | return None 108 | 109 | # search for basic block ending with: lea rcx, [IMAGE_BASE] / call 110 | cfg = m.getData().getCFG() 111 | br_to_hlep, hlep = -1, -1 112 | for b in cfg: 113 | if b.size() >= 2: 114 | i = b.size() - 2 115 | if b.get(i).getMnemonic() == 'lea' and b.get(i + 1).getMnemonic() == 'call': 116 | addr, insn = b.getAddressOfInstruction(i), b.get(i) 117 | if check_bytes(u, addr, '48 8d 0d ?? ?? ?? ??'): 118 | addr += 7 + u.getMemory().readLEInt(addr + 3) 119 | if addr == u.getVirtualImageBase(): 120 | addr, insn = b.getAddressOfInstruction(i+1), b.get(i+1) 121 | hlep = insn.getRoutineCall(addr).getTargets().get(0).getAddress() 122 | br_to_hlep = addr 123 | break 124 | if hlep == -1: 125 | return None 126 | return (br_to_hlep, hlep, 'winmain') 127 | 128 | #------------------------------------------------------------------------------ 129 | def check_bytes(u, addr, pattern): 130 | pattern = pattern.replace(' ', '') 131 | pattern2 = '' 132 | mask = '' 133 | if len(pattern) % 2 != 0: raise Exception() 134 | for c in pattern: 135 | if c == '?': 136 | mask += '0' 137 | pattern2 += '0' 138 | else: 139 | mask += 'f' 140 | pattern2 += c 141 | import binascii 142 | binpattern = binascii.unhexlify(pattern2) 143 | binmask = binascii.unhexlify(mask) 144 | size = len(binpattern) 145 | data = [-1] * size 146 | for bval, bmask in zip(binpattern, binmask): 147 | x = u.getMemory().readByte(addr) 148 | if (x & ord(bmask)) != ord(bval): 149 | return False 150 | addr += 1 151 | return True 152 | -------------------------------------------------------------------------------- /scripts/FocusDisassembly.py: -------------------------------------------------------------------------------- 1 | #?description=Focus the first view found that contains a fragment named 'Disassembly', and select and focus that fragment. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | """ 5 | Sample script for JEB Decompiler. 6 | """ 7 | class FocusDisassembly(IScript): 8 | def run(self, ctx): 9 | success = focusDisassemblyFragment(ctx) 10 | print('Focused Disassembly fragment: %s' % success) 11 | 12 | def getDisassemblyFragment(view): 13 | for fragment in view.getFragments(): 14 | if view.getFragmentLabel(fragment) == 'Disassembly': 15 | return fragment 16 | 17 | def focusDisassemblyFragment(ctx): 18 | for view in ctx.getViews(): 19 | fragment = getDisassemblyFragment(view) 20 | if fragment: 21 | view.setFocus() 22 | view.setActiveFragment(fragment) 23 | return True 24 | return False 25 | -------------------------------------------------------------------------------- /scripts/GUIActionScriptDemo.py: -------------------------------------------------------------------------------- 1 | #?description=Demo how to chain GUI actions without blocking the UI thread. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from java.lang import Runnable 5 | """ 6 | Sample script for JEB Decompiler. 7 | 8 | Demo how to chain GUI actions without blocking the UI thread. 9 | 10 | Refer to GUIActionScriptSkeleton.py for details about scripting GUI actions. 11 | """ 12 | CTX = None 13 | 14 | class GUIActionScriptDemo(IScript): 15 | def run(self, ctx): 16 | # globals to be used by all GUI action stubs 17 | global CTX 18 | CTX = ctx 19 | 20 | path = ctx.displayFileOpenSelector('Select a file') 21 | assert path, 'Need a valid file path' 22 | 23 | # close any existing project 24 | if ctx.getMainProject(): 25 | ctx.closeMainProject() 26 | 27 | # create a new project, analyze one artifact file 28 | ctx.open(path) 29 | 30 | # give some time for GUI fragments (e.g. disass) to be created and focused 31 | CTX.uiExecuteWithDelay(2000, T1()) 32 | 33 | class T1(Runnable): 34 | def run(self): 35 | print(CTX.getViews()) 36 | -------------------------------------------------------------------------------- /scripts/GUIActionScriptSkeleton.py: -------------------------------------------------------------------------------- 1 | #?description=Demo how to chain GUI actions without blocking the UI thread. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from java.lang import Runnable 5 | """ 6 | Sample script for JEB Decompiler. 7 | 8 | Demo how to chain GUI actions without blocking the UI thread. 9 | 10 | Background: the UI thread of the GUI client for JEB is an event-based executor for stubs of code. 11 | Client scripts are meant to execute relatively short actions on the UI thread, and terminate, or 12 | execute more heavy-weight processing on worker threads (e.g. via ctx.executeAsync) and then 13 | terminate. In either scenario, the UI thread is hogged or blocked for the duration of the action. 14 | 15 | How to chain UI actions to mimic behaviors to what higher-level macro programs would do? 16 | Example: we want to create a project, add an artifact, wait for it to be loaded, wait for the 17 | client to create various views and fragments, then select a fragment and navigate to it. 18 | 19 | Although the JEB scripting API is not truly designed to chain UI actions, it is relatively 20 | straight-forward to obtain the behavior by encapsulating chunks of code, and scheduling them on 21 | the UI thread for execution, when required. 22 | """ 23 | 24 | CTX = None # com.pnfsoftware.jeb.client.api.IGraphicalClientContext 25 | 26 | class GUIActionScriptSkeleton(IScript): 27 | def run(self, ctx): 28 | global CTX 29 | CTX = ctx 30 | # --> ENTRY POINT 31 | print('A0') 32 | CTX.uiExecute(T1()) # execute A1 asap, return before the execution is finished 33 | 34 | class T1(Runnable): 35 | def run(self): 36 | # --> ACTION 1 37 | print('A1') 38 | CTX.uiExecuteWithDelay(3000, T2()) # execute A2 in +3 seconds, return asap 39 | 40 | class T2(Runnable): 41 | def run(self): 42 | # --> ACTION 2 43 | print('A2') 44 | CTX.uiExecuteBlocking(T3()) # CAREFUL, will block until T3 is fully executed! 45 | 46 | class T3(Runnable): 47 | def run(self): 48 | # --> ACTION 3 49 | print('A3') # done 50 | -------------------------------------------------------------------------------- /scripts/GenerateFridaSnippetForDex.py: -------------------------------------------------------------------------------- 1 | #?description=Create a Frida hook for the selected Dex method 2 | #?shortcut= 3 | 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core import Artifact 6 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, IDexDecompilerUnit 7 | 8 | ''' 9 | Python script for JEB Decompiler. 10 | 11 | Frida hook generator for Dex methods. Respect original method names, display effective (renamed) names. 12 | 13 | Forked from https://github.com/cryptax/misc-code/blob/master/jeb/Jeb2Frida.py 14 | ''' 15 | class GenerateFridaSnippetForDex(IScript): 16 | 17 | def run(self, ctx): 18 | addr = ctx.getFocusedAddress() 19 | unit = ctx.getFocusedUnit() 20 | if not addr or not unit: 21 | print("Position the caret on a Dex method inside a text fragment") 22 | return 23 | 24 | if isinstance(unit.getParent(), IDexDecompilerUnit): 25 | unit = unit.getParent().getParent() # the parent of dexdec is a dex 26 | elif not isinstance(unit, IDexUnit): 27 | print("Canot retrieve the Dex unit") 28 | return 29 | 30 | # clear the bytecode position if the caret inside a method (as opposed to being on the method's header) 31 | pos = addr.find('+') 32 | if pos >= 0: 33 | addr = addr[:pos] 34 | # retrieve the method object, we'll need that to collect the original and effective (current) names 35 | m = unit.getMethod(addr) 36 | if not m: 37 | print("The selected address does not resolve to a Dex method") 38 | return 39 | 40 | print("Generating Frida snippet for method: %s" % m.getSignature()) 41 | addr = m.getSignature(False) # original names 42 | 43 | # original type name (used in the binary) 44 | cl = m.getClassType().getSignature(False, False) 45 | 46 | # original method name (used in the binary) 47 | mname = m.getName(False) 48 | if mname == "": 49 | mname = "$init" 50 | 51 | # original parameter types (used in the binary) 52 | partypes = [] 53 | parnames = [] 54 | for i, partype in enumerate(m.getParameterTypes()): 55 | partypes.append(partype.getSignature(False, False)) 56 | parnames.append("arg%d" % i) # TODO: retrieve the decompiler-provided param names 57 | 58 | no_return = m.getReturnType().isVoid() 59 | 60 | # the tag rendered in the console will use the method's actual names (i.e. the renames if any), not the original names 61 | tag = "%s.%s" % (m.getClassType().getSignature(True, False, False), m.getName(True)) 62 | 63 | hook = self.generate(tag, cl, mname, partypes, parnames, no_return) 64 | 65 | snippet = "// Frida hook for %s:\nJava.perform(function() {\n%s});\n" % (m.getSignature(), hook) 66 | print(snippet) 67 | 68 | def generate(self, tag, cl, mname, partypes, parnames, no_return): 69 | hook = "" 70 | hook += " var cl = Java.use('%s');\n" % cl 71 | hook += " var m = cl.%s" % mname 72 | 73 | if partypes: 74 | hook += ".overload(" 75 | for i, partype in enumerate(partypes): 76 | if i > 0: 77 | hook += ", " 78 | hook += "'%s'" % partype 79 | hook += ")" 80 | hook += ";\n" 81 | 82 | hook += " m.implementation = function(%s) {\n" % (', '.join(parnames)) 83 | 84 | hook += " console.log('[%s] called with:" % tag 85 | if parnames: 86 | for i, parname in enumerate(parnames): 87 | if i > 0: 88 | hook += ", " 89 | hook += " arg%d=' + %s + '" % (i, parname) 90 | hook += "'" 91 | hook += ");\n" 92 | 93 | if no_return: 94 | hook += " this.%s(%s);" % (mname, ', '.join(parnames)) 95 | else: 96 | hook += " var ret = this.%s(%s);\n" % (mname, ', '.join(parnames)) 97 | hook += " console.log('[%s] returned: ' + ret);\n" % tag 98 | hook += " return ret;" 99 | 100 | hook += "\n };\n" 101 | return hook 102 | -------------------------------------------------------------------------------- /scripts/GraphDemo1.py: -------------------------------------------------------------------------------- 1 | #?description=Basic use of the displayGraph() API method, which does not make use of the graph extension 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.util.graph import Digraph 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class GraphDemo1(IScript): 9 | def run(self, ctx): 10 | g = Digraph.create().e(0, 1).e(0, 2).e(0, 3).e(1, 0).e(3, 4).e(3, 5).e(4, 6).e(5, 7).done() 11 | ctx.displayGraph('Sample Graph', g) 12 | -------------------------------------------------------------------------------- /scripts/GraphDemo2.py: -------------------------------------------------------------------------------- 1 | #?description=Advanced use of displayGraph() to display the callgraph 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, GraphDialogExtensions 4 | from com.pnfsoftware.jeb.core.units import UnitAddress 5 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit, CodeUtil 6 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, DalvikCallgraphBuilder 7 | from com.pnfsoftware.jeb.util.graph import Digraph 8 | """ 9 | Sample script for JEB Decompiler. 10 | """ 11 | class GraphDemo2(IScript): 12 | 13 | def run(self, ctx): 14 | prj = ctx.getMainProject() 15 | assert prj, 'Need a project' 16 | 17 | code = prj.findUnit(ICodeUnit) 18 | assert code, 'Need a code unit' 19 | 20 | b = CodeUtil.createCallgraphBuilder(code) 21 | g = b.buildModel() 22 | 23 | class Ext(GraphDialogExtensions): 24 | #def getLayoutMode(self): 25 | # return GraphDialogExtensions.LayoutMode.FDC_NO_WEIGHT 26 | #def getVertexShape(self, vertexId): 27 | # return GraphDialogExtensions.VertexShape.SQUARE 28 | #def getVertexColor(self, vertexId): 29 | # return 0x0080A0 30 | def processNewVertexName(self, vertexId, newName): 31 | addr = b.getAddressForVertexId(vertexId) 32 | return code.getMethod(addr).setName(newName) 33 | def getUnitAddress(self, vertexId): 34 | addr = b.getAddressForVertexId(vertexId) 35 | if not addr: 36 | return None 37 | return UnitAddress(code, addr) 38 | def getVertexMark(self, vertexId): 39 | addr = b.getAddressForVertexId(vertexId) 40 | return code.getInlineComment(addr) == 'MARKED' 41 | def processVertexMark(self, vertexId, mark): 42 | addr = b.getAddressForVertexId(vertexId) 43 | code.setInlineComment(addr, 'MARKED' if mark else '') 44 | return True 45 | 46 | ctx.displayGraph('Callgraph', g, Ext()) 47 | -------------------------------------------------------------------------------- /scripts/GraphPackagesRelationships.py: -------------------------------------------------------------------------------- 1 | #?description=Build callgraph between user routines and routines belonging to specific packages 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, GraphDialogExtensions 4 | from com.pnfsoftware.jeb.core.units import UnitAddress, INativeCodeUnit 5 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit, CodeUtil 6 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit, DalvikCallgraphBuilder 7 | from com.pnfsoftware.jeb.util.graph import Digraph 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | Build callgraph between unknown routines (whose name start with 'sub_') 12 | and routines from specific packages (whose names are prefixed by known package names). 13 | 14 | This serves to see relationships between user code and library code. 15 | 16 | Modify PACKAGES_OF_INTEREST below to suit your needs. 17 | """ 18 | PACKAGES_OF_INTEREST = ['OpenSSL', 'cURL'] 19 | 20 | class GraphPackagesRelationships(IScript): 21 | 22 | def getRootModule(self, label): 23 | if '::' in label: 24 | return label.split('::')[0] 25 | return None 26 | 27 | def run(self, ctx): 28 | prj = ctx.getMainProject() 29 | assert prj, 'Need a project' 30 | 31 | code = prj.findUnit(INativeCodeUnit) 32 | assert code, 'Need a native code unit' 33 | 34 | g = Digraph.create() 35 | model = code.getCodeModel() 36 | cg = model.getCallGraphManager().getGlobalCallGraph() 37 | routines = code.getInternalMethods() 38 | 39 | for srcRtn in routines: 40 | srcName = srcRtn.getName(True) 41 | srcModule = self.getRootModule(srcName) 42 | srcIndex = routines.indexOf(srcRtn) 43 | callees = cg.getCallees(srcRtn, False) 44 | for target in callees: 45 | if not target.isInternal(): 46 | continue 47 | targetRtn = code.getInternalMethod(target.getInternalAddress().getAddress(), True) 48 | if not targetRtn: 49 | continue 50 | 51 | targetName = targetRtn.getName(True) 52 | dstModule = self.getRootModule(targetName) 53 | if (srcModule != dstModule) \ 54 | and (srcName.startswith('sub_') or targetName.startswith('sub_')) \ 55 | and (srcModule in PACKAGES_OF_INTEREST or dstModule in PACKAGES_OF_INTEREST): 56 | targetIndex = routines.indexOf(targetRtn) 57 | 58 | if not g.getVertex(srcIndex): 59 | g.v(srcIndex, None, srcName) 60 | if not g.getVertex(targetIndex): 61 | g.v(targetIndex, None, targetName) 62 | if not g.getEdge(srcIndex, targetIndex): 63 | g.e(srcIndex, targetIndex) 64 | 65 | g.done() 66 | 67 | class Ext(GraphDialogExtensions): 68 | def getUnitAddress(self, vertexId): 69 | # implement navigation 70 | rtn = routines[vertexId] 71 | if not rtn: 72 | return None 73 | addr = rtn.getAddress() 74 | if not addr: 75 | return None 76 | return UnitAddress(code, addr) 77 | 78 | ctx.displayGraph('Packages Callgraph', g, Ext()) 79 | -------------------------------------------------------------------------------- /scripts/GraphRtti.py: -------------------------------------------------------------------------------- 1 | #?description=Display Run-Time Type Information as a graph showing inheritance relationships between classes 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, GraphDialogExtensions 4 | from com.pnfsoftware.jeb.client.api.GraphDialogExtensions import EdgeStyle, VertexShape 5 | from com.pnfsoftware.jeb.core.units import IBinaryUnit 6 | from com.pnfsoftware.jeb.util.graph import Digraph 7 | from com.pnfsoftware.jeb.core.input import IInput 8 | from com.pnfsoftware.jeb.util.io import IO 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | Display class inheritance graph built from Run-Time Type Information extracted by JEB. 13 | This script parses JEB's RTTI text output, provided as 'run-time type information' unit. 14 | Each line follows the pattern: 15 | class BaseClassName : public|private [virtual] ParentClassName1, ...; 16 | """ 17 | IGNORE_STD = True # True to ignore C++ std classes (when used as base class) 18 | CLASS_SEPARATOR = ' : ' 19 | PARENT_SEPARATOR = ', ' 20 | 21 | class GraphRtti(IScript): 22 | def run(self, ctx): 23 | 24 | # retrieve RTTI unit 25 | prj = ctx.getMainProject() 26 | if not prj: 27 | print('Need a project') 28 | return 29 | 30 | units = prj.findUnits(IBinaryUnit) 31 | if not units or len(units) == 0: 32 | print('Need RTTI unit') 33 | return 34 | 35 | rttiUnit = None 36 | for unit in units: 37 | if unit.getName() == 'run-time type information': 38 | rttiUnit = unit 39 | if not rttiUnit: 40 | print('Need RTTI unit') 41 | 42 | data = IO.readInputStream(rttiUnit.getInput().getStream()) 43 | rttiText = ''.join(chr(c) for c in data) 44 | 45 | # parsing 46 | g = Digraph.create() 47 | curId = 0 48 | classToId = dict() 49 | virtualEdges = list() # [[childId, parentId]] 50 | lines = rttiText.splitlines()[2:] # skip header 51 | for line in lines: 52 | line = line.strip()[:-1] # beautify 53 | if not line.startswith('class'): 54 | continue 55 | classes = line.split(CLASS_SEPARATOR) 56 | if len(classes) > 2: 57 | print('error: malformed line (%s)' % line) 58 | continue 59 | 60 | # register base class id 61 | baseClassName = classes[0][6:] 62 | if IGNORE_STD and baseClassName.startswith('std::'): 63 | continue 64 | if baseClassName not in classToId.keys(): 65 | g.v(curId, None, baseClassName) 66 | classToId[baseClassName] = curId 67 | curId+=1 68 | print('baseClassName=%s' % baseClassName) 69 | 70 | # add edges for each parent, if any 71 | if len(classes) == 1: 72 | continue 73 | for parent in classes[1].split(PARENT_SEPARATOR): 74 | parentStr = parent.split() 75 | if len(parentStr) < 2: 76 | print('error: malformed line (%s)' % line) 77 | continue 78 | isPublic = parentStr[0] == 'public' 79 | isVirtual = parentStr[1] == 'virtual' 80 | parentStartIndex = 7 if isPublic else 8 81 | parentStartIndex += 8 if isVirtual else 0 82 | parentName = parent[parentStartIndex:] 83 | 84 | print(' parentName=%s, isPublic = %d, isVirtual = %d' % (parentName, isPublic, isVirtual)) 85 | 86 | if parentName not in classToId.keys(): 87 | g.v(curId, None, parentName) 88 | classToId[parentName] = curId 89 | curId+=1 90 | 91 | g.e(classToId[baseClassName], classToId[parentName]) 92 | if isVirtual: 93 | virtualEdges.append([classToId[baseClassName], classToId[parentName]]) 94 | 95 | class Ext(GraphDialogExtensions): 96 | def getEdgeStyle(self, vertexId0, vertexId1): 97 | if [vertexId0, vertexId1] in virtualEdges: 98 | # dashed edges to represent virtual inheritance 99 | return EdgeStyle.DASHED 100 | return EdgeStyle.SOLID 101 | def getVertexShape(self, vertexId): 102 | return VertexShape.SQUARE_FILLED 103 | 104 | g.done() 105 | ctx.displayGraph('RTTI Class Relationship Graph', g, Ext()) 106 | -------------------------------------------------------------------------------- /scripts/JavaASTCreateMethodRef.py: -------------------------------------------------------------------------------- 1 | #?description=Show how the Java AST API can be used to modify and create elements in a decompiled syntax tree 2 | #?shortcut= 3 | import time 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | The script shows how to use the Java factory to create an external method reference (also available for fields), 10 | create a new statement (here, a call to 'new String("...")'), and replace an arbitrary selected statement 11 | (index 0 in the currently selected method) by the new statement. 12 | 13 | - Open an APK/DEX, decompile any class or method 14 | - In the Decompiled view, position the caret somewhere in a method 15 | - Execute the script 16 | """ 17 | class JavaASTCreateMethodRef(IScript): 18 | 19 | def run(self, ctx): 20 | # retrieve the currently active UI fragment (make sure to select a Decompiled Java fragment) 21 | f = ctx.getFocusedFragment() 22 | assert f, 'Need a focused fragment' 23 | 24 | unit = f.getUnit() 25 | assert isinstance(unit, IJavaSourceUnit), 'Need a java unit' 26 | 27 | # a DEX-style address: TYPENAME->METHODNAME(PARAMTYPES)RETTYPE+OFFSET 28 | addr = f.getActiveAddress() 29 | 30 | # IDexDecompilerUnit 31 | dexdec = unit.getParent() 32 | 33 | pos = addr.find('+') 34 | if pos >= 0: addr = addr[:pos] 35 | 36 | # retrieve the already decompiled IJavaMethod on caret 37 | _m = dexdec.getMethod(addr) 38 | 39 | # method body... 40 | _blk = _m.getBody() 41 | # we pick the first statement 42 | _stm0 = _blk.get(0) 43 | print('Will replace statement: "%s"' % _stm0) 44 | 45 | # IJavaFactories 46 | self.jfactory = unit.getFactories() 47 | 48 | # now, replace that first method statement by a call to 'new String("...")' 49 | if self.replaceStatement(_blk, _stm0): 50 | unit.notifyGenericChange() 51 | 52 | 53 | def replaceStatement(self, parent, e): 54 | m = self.jfactory.createMethodReference('Ljava/lang/String;->(Ljava/lang/String;)V', False) 55 | t = self.jfactory.getTypeFactory().createType('Ljava/lang/String;') 56 | args = [self.jfactory.getConstantFactory().createString('FOOBAR__' + str(time.time()))] 57 | stm = self.jfactory.createNew(t, m, args) 58 | return parent.replaceSubElement(e, stm) 59 | -------------------------------------------------------------------------------- /scripts/JavaASTDemo.py: -------------------------------------------------------------------------------- 1 | #?description=Show how to navigate and dump Java AST trees 2 | #?shortcut= 3 | import os 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class JavaASTDemo(IScript): 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | assert prj, 'Need a project' 13 | 14 | for unit in prj.findUnits(IJavaSourceUnit): 15 | c = unit.getClassElement() 16 | for m in c.getMethods(): 17 | self.displayTree(m) 18 | 19 | def displayTree(self, e, level=0): 20 | print('%s%s @ 0x%X' % (level*' ', e.getElementType(), e.getPhysicalOffset())) 21 | if e: 22 | elts = e.getSubElements() 23 | for e in elts: 24 | self.displayTree(e, level+1) 25 | -------------------------------------------------------------------------------- /scripts/JavaASTTags.py: -------------------------------------------------------------------------------- 1 | #?description=Show how to "tag" elements of an AST tree, and later on retrieve those tags from the Java text document output (referred to as "marks"). 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IconType, ButtonGroupType 4 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 5 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit 6 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit, ICodeItem 7 | from com.pnfsoftware.jeb.core.output.text import ITextDocument 8 | from com.pnfsoftware.jeb.core.units.code.java import IJavaConstant 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | This script shows how to "tag" elements of an AST tree, and later on retrieve 13 | those tags from the Java text document output (referred to as "marks"). 14 | 15 | This code looks for Java units, and tags String contants containing the word 16 | 'html'. Output example: 17 | 18 | ... 19 | 20 | ... 21 | => Marks: 22 | 17:59 - htmlTag (Potential HTML code found) 23 | 24 | Tags are persisted in JDB2 database files. 25 | 26 | Note: tags are specific to Java units. However, marks are not (they are 27 | specific to text documents). The Java plugin simply renders tags as text 28 | marks. This example demonstrates usage of tags in that context. 29 | 30 | Marks are not displayed by the desktop GUI client. 31 | It is up to third-party code (clients, plugins, or scripts) to display them. 32 | 33 | Revision note: Script updated on April 12 2022. 34 | """ 35 | class JavaASTTags(IScript): 36 | 37 | def run(self, ctx): 38 | prj = ctx.getMainProject() 39 | assert prj, 'Need a project' 40 | 41 | for unit in prj.findUnits(IJavaSourceUnit): 42 | self.processClassTree(unit.getClassElement()) 43 | doc = unit.getSourceDocument() 44 | javaCode, formattedMarks = self.formatTextDocument(doc) 45 | #print(javaCode) 46 | if(formattedMarks): 47 | print('=> Marks:') 48 | print(formattedMarks) 49 | 50 | def processClassTree(self, e_class): 51 | for e in e_class.getMethods(): 52 | self.processEltTree(e) 53 | 54 | def processEltTree(self, e): 55 | if e: 56 | self.analyzeNode(e) 57 | elts = e.getSubElements() 58 | for e in elts: 59 | self.processEltTree(e) 60 | 61 | # demo 62 | def analyzeNode(self, e): 63 | if isinstance(e, IJavaConstant): 64 | if e.isString(): 65 | if e.getString().find('html') >= 0: 66 | print('TAGGING: %s' % e) 67 | e.addTag('htmlTag', 'Potential HTML code found') 68 | 69 | def formatTextDocument(self, doc): 70 | javaCode, formattedMarks = '', '' 71 | # retrieve the entire document -it's a source file, 72 | # no need to buffer individual parts. 10 MLoC is enough 73 | alldoc = doc.getDocumentPart(0, 10000000) 74 | for lineIndex, line in enumerate(alldoc.getLines()): 75 | javaCode += line.getText().toString() + '\n' 76 | for mark in line.getMarks(): 77 | # the mark name is the tag attribute's key 78 | if mark.getName() == 'htmlTag': 79 | # 0-based line and column indexes 80 | # the mark object is the tag attribute's value 81 | formattedMarks += '%d:%d %s\n' % (lineIndex, mark.getOffset(), mark.getObject()) 82 | return javaCode, formattedMarks 83 | -------------------------------------------------------------------------------- /scripts/JavaListIdentifiers.py: -------------------------------------------------------------------------------- 1 | #?description=Enumerate the IJavaIdentifiers (parameters, local vars) of IJavaMethod objects of a decompiled IJavaClass. Rename some identifiers. List all identifiers. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | 8 | Enumerate the IJavaIdentifiers (parameters, local vars) of IJavaMethod objects of a decompiled IJavaClass. Rename some identifiers. List all identifiers 9 | """ 10 | class JavaListIdentifiers(IScript): 11 | 12 | def run(self, ctx): 13 | prj = ctx.getMainProject() 14 | assert prj, 'Need a project' 15 | 16 | f = ctx.getFocusedFragment() 17 | assert f, 'Need a focused fragment' 18 | 19 | # assume a fragment is focused, assumed it is backed by an IUnit 20 | unit = f.getUnit() 21 | if not isinstance(unit, IJavaSourceUnit): return 22 | 23 | # assume the unit is an IJavaSourceUnit 24 | c = unit.getClassElement() 25 | 26 | # 1) DEMO: rename some method identifiers 27 | # enumerate all methods in the class (NOTE: nested classes are not enumerated, this is just a demo script) 28 | idx = 0 29 | for m in c.getMethods(): 30 | identifiers = m.getIdentifierManager().getIdentifiers() 31 | print('METHOD: %s: identifiers=%s' % (m, identifiers)) 32 | for ident in identifiers: 33 | print(' current=%s original=%s debug=%s' % (unit.getIdentifierName(ident), ident.getName(), ident.getDebugName())) 34 | 35 | # rename all non-this identifiers (including param names) that do not carry a debug name 36 | if ident.getName() != 'this' and ident.getDebugName() == None: 37 | newName = 'RENAMED_%d' % idx 38 | idx += 1 39 | print(' -> renaming to %s' % newName) 40 | unit.setIdentifierName(ident, newName) 41 | 42 | # 2) DEMO: note that all identifiers are uniquely identified by their CodeCoordinates 43 | # they can be enumerated via IDexUnit (the parent of an IDexDecompilerUnit, itself parent of all IJavaSourceUnit) 44 | dexdec = unit.getParent() 45 | dex = dexdec.getParent() 46 | print('All renamed identifiers: %s' % dex.getRenamedIdentifiers()) 47 | -------------------------------------------------------------------------------- /scripts/JavaRenameField1.py: -------------------------------------------------------------------------------- 1 | #?description=Rename a Java field to its source variable's name, in a field assignment statement. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core.units.code.android.dex import IDexField 5 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit 6 | from com.pnfsoftware.jeb.core.units.code.java import IJavaInstanceField, IJavaAssignment, IJavaIdentifier 7 | """ 8 | Sample script for JEB Decompiler. 9 | 10 | Rename a Java field to its source variable's name, in a field assignment statement. 11 | 12 | In a decompiled Java method, place the caret on the target object field. 13 | Example: this.field3 = blah; 14 | ^^^^ caret here 15 | """ 16 | class JavaRenameField1(IScript): 17 | def run(self, ctx): 18 | # must be a GUI client 19 | prj = ctx.getMainProject() 20 | if not isinstance(ctx, IGraphicalClientContext): 21 | return 22 | 23 | # retrieve the focused text fragment 24 | fragment = ctx.getFocusedFragment() 25 | if not fragment: 26 | return 27 | 28 | # focused fragment must be decompiled Java, underlying unit is of type IJavaSourceUnit 29 | ast_unit = fragment.getUnit() 30 | if not isinstance(ast_unit, IJavaSourceUnit): 31 | return 32 | self.dex = ast_unit.getDecompiler().getDex() 33 | 34 | # retrieve the active (highlighted) item, which must be an object field 35 | item = fragment.getActiveItem() 36 | if not item: 37 | return 38 | 39 | # retrieve the underlying IDexField object 40 | o = ast_unit.getItemObject(item.getItemId()) 41 | if not isinstance(o, IDexField): 42 | return 43 | self.target_field = o 44 | 45 | # walk the methods's AST tree, look for assignments to the target field 46 | for m in ast_unit.getClassElement().getMethods(): 47 | self.processASTMethod(None, m) 48 | 49 | def processASTMethod(self, parent, e): 50 | if isinstance(e, IJavaAssignment) and isinstance(e.getRight(), IJavaIdentifier) and isinstance(e.getLeft(), IJavaInstanceField): 51 | dst, src = e.getLeft(), e.getRight() 52 | # perform the renaming 53 | ident_name = src.getName() 54 | fsig = dst.getField().getSignature() 55 | if self.dex.getField(fsig) == self.target_field: 56 | self.target_field.setName(ident_name) 57 | for subelt in e.getSubElements(): 58 | self.processASTMethod(e, subelt) 59 | -------------------------------------------------------------------------------- /scripts/JavaRenameField2.py: -------------------------------------------------------------------------------- 1 | #?description=Rename a Java field to its source variable's name, in a field assignment statement. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit 5 | from com.pnfsoftware.jeb.core.units.code.java import IJavaInstanceField, IJavaAssignment, IJavaIdentifier 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | Rename a Java field to its source variable's name, in a field assignment statement. 10 | 11 | Demo for ITextFragment.getDocumentObjectsAtCaret() 12 | Requires JEB 4.21 or above 13 | """ 14 | class JavaRenameField2(IScript): 15 | def run(self, ctx): 16 | # must be a GUI client 17 | prj = ctx.getMainProject() 18 | if not isinstance(ctx, IGraphicalClientContext): 19 | return 20 | 21 | # retrieve the focused text fragment 22 | fragment = ctx.getFocusedFragment() 23 | if not fragment: 24 | return 25 | 26 | # focused fragment must be decompiled Java, underlying unit is of type IJavaSourceUnit 27 | ast_unit = fragment.getUnit() 28 | if not isinstance(ast_unit, IJavaSourceUnit): 29 | return 30 | dex = ast_unit.getDecompiler().getDex() 31 | 32 | # ITextFragment.getDocumentObjectsAtCaret() is key to this demo script 33 | # this API function retrieves a stack of unit objects relating to the text element where the caret is currently positioned 34 | # these objects are unit-specific; in the case of an IJavaSourceUnit object, they are IJavaElement objects (AST objects) 35 | astobjstk = fragment.getDocumentObjectsAtCaret() 36 | if len(astobjstk) < 2: 37 | return 38 | ast0 = astobjstk[-1] 39 | ast1 = astobjstk[-2] 40 | 41 | # we want: some_object.some_field = some_var 42 | # ^ (the caret is supposed to highlight the field item) 43 | if not isinstance(ast0, IJavaInstanceField): 44 | return 45 | if not isinstance(ast1, IJavaAssignment): 46 | return 47 | if not isinstance(ast1.getRight(), IJavaIdentifier): 48 | return 49 | 50 | # perform the renaming 51 | ident_name = ast1.getRight().getName() 52 | fsig = ast0.getField().getSignature() 53 | dex.getField(fsig).setName(ident_name) 54 | -------------------------------------------------------------------------------- /scripts/JumpTo.py: -------------------------------------------------------------------------------- 1 | #?description= 2 | #?shortcut= 3 | #?nolist 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | - how a script can be invoked after a cmdline-provided file has been processed by the JEB UI client 10 | - currently, this script simply searches for a Dex code unit, attempts to find a disassembly fragment for it, and navigate to the cmdline-provided address 11 | 12 | How to use: 13 | $ jeb_startup_script --script=ScriptPath -- InputFile AddressToJumpTo 14 | 15 | This script can also be used when invoking the JEB UI client via the URI handler 'jeb:' 16 | Example: 17 | - drop this script in your JEB folder scripts/ directory 18 | - open a browser and navigate to the URL: jeb:--script%3DJumpTo.py+--+https%3A%2F%2Fwww.pnfsoftware.com%2Fz.apk+Lcom%2Fpnfsoftware%2Fraasta%2FCoordinatesE6%3B 19 | """ 20 | class JumpTo(IScript): 21 | def run(self, ctx): 22 | if len(ctx.getArguments()) < 2: 23 | return 24 | 25 | prj = ctx.getMainProject() 26 | assert prj, 'Need a project' 27 | 28 | # arg[0] is the InputFile 29 | addr = ctx.getArguments()[1] 30 | print('Will jump to: %s' % addr) 31 | 32 | dexunit = prj.findUnit(IDexUnit) 33 | if dexunit: 34 | f = ctx.findFragment(dexunit, 'Disassembly', True) 35 | if f: 36 | f.setActiveAddress(addr) 37 | -------------------------------------------------------------------------------- /scripts/JumpToAndroidComponent.py: -------------------------------------------------------------------------------- 1 | #?description=Jump from an activity name (selected in the Android XML Manifest) to its corresponding bytecode definition in the DEX disassembly fragment. 2 | #?shortcut= 3 | #?nolist 4 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, IUnitView 5 | from com.pnfsoftware.jeb.core.units import IUnit, IXmlUnit 6 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 7 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | !!! SAME AS DexJumpToActivity.py !!! 12 | 13 | This JEB UI script allows the user to jump from an activity name (selected in the Android XML 14 | Manifest) to its corresponding bytecode definition in the DEX disassembly fragment. 15 | """ 16 | class JumpToAndroidComponent(IScript): 17 | def run(self, ctx): 18 | if not isinstance(ctx, IGraphicalClientContext): 19 | print('This script must be run within a graphical client') 20 | return 21 | 22 | fragment = ctx.getFocusedView().getActiveFragment() 23 | if type(fragment.getUnit()) is IXmlUnit: 24 | print('Select the Manifest XML view') 25 | return 26 | 27 | aname = fragment.getActiveItemAsText() 28 | if not aname: 29 | print('Select the activity name') 30 | return 31 | 32 | # activity name is relative to the package name 33 | if aname.startswith('.'): 34 | # unit is the Manifest, of type IXmlUnit; we can retrieve the XML document 35 | # note: an alternate way would be to retrieve the top-level IApkUnit, and use getPackageName() 36 | pname = fragment.getUnit().getDocument().getElementsByTagName("manifest").item(0).getAttribute("package") 37 | #print('Package name: %s' % pname) 38 | aname = pname + aname 39 | 40 | print('Activity name: %s' % aname) 41 | 42 | addr = 'L' + aname.replace('.', '/') + ';' 43 | print('Target address: %s' % addr) 44 | 45 | dexunit = ctx.getMainProject().findUnit(IDexUnit) 46 | 47 | f = ctx.findFragment(dexunit, 'Disassembly', True) 48 | if f: 49 | f.setActiveAddress(addr) 50 | -------------------------------------------------------------------------------- /scripts/ListCrossReferences.py: -------------------------------------------------------------------------------- 1 | #?description=Lists all cross-references to the currently active item using a generic ActionXrefsData object 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.actions import ActionContext 5 | from com.pnfsoftware.jeb.core.actions import ActionXrefsData 6 | from com.pnfsoftware.jeb.core.actions import Actions 7 | """ 8 | Sample script for JEB Decompiler. 9 | 10 | This script lists xrefs using the most generic way, that is, via Actions.QUERY_XREFS. 11 | Note that some units offer specialized ways to query cross-reference. 12 | Example: IDexUnit's getCrossReferences() and getReferenceManager() 13 | """ 14 | class ListCrossReferences(IScript): 15 | def run(self, ctx): 16 | unit = ctx.getFocusedUnit() 17 | assert unit, 'Need a focused unit fragment' 18 | print(unit.getFormatType()) 19 | 20 | current_addr = ctx.getFocusedAddress() 21 | print(current_addr) 22 | 23 | current_item = ctx.getFocusedItem() 24 | print(current_item) 25 | 26 | data = ActionXrefsData() 27 | if unit.prepareExecution(ActionContext(unit, Actions.QUERY_XREFS, 0 if not current_item else current_item.getItemId(), current_addr), data): 28 | for xref_addr in data.getAddresses(): 29 | print(xref_addr) 30 | -------------------------------------------------------------------------------- /scripts/ListDexMethodsWithXor.py: -------------------------------------------------------------------------------- 1 | #?description=List dex methods making use of xor instructions 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class ListDexMethodsWithXor(IScript): 9 | 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | assert prj, 'Need a project' 13 | 14 | dex = prj.findUnit(IDexUnit) 15 | assert dex, 'Need a dex unit' 16 | 17 | cnt = 0 18 | for m in dex.getMethods(): 19 | if m.isInternal(): 20 | ci = m.getData().getCodeItem() 21 | if ci and self.checkMethod(ci): 22 | print(m.getSignature(True, False)) 23 | cnt += 1 24 | print('Found %d methods' % cnt) 25 | 26 | def checkMethod(self, ci): 27 | for insn in ci.getInstructions(): 28 | if insn.toString().find('xor-') >= 0: 29 | return True 30 | return False 31 | -------------------------------------------------------------------------------- /scripts/ListOverrides.py: -------------------------------------------------------------------------------- 1 | #?description=Lists overrides (in the generic terms) of the currently active method or field using a generic ActionOverridesData object 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.actions import Actions, ActionContext, ActionOverridesData 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class ListOverrides(IScript): 9 | def run(self, ctx): 10 | unit = ctx.getFocusedUnit() 11 | assert unit, 'Need a focused unit fragment' 12 | 13 | current_item = ctx.getFocusedItem() 14 | assert current_item, 'Need a focused item' 15 | 16 | data = ActionOverridesData() 17 | if unit.prepareExecution(ActionContext(unit, Actions.QUERY_OVERRIDES, current_item.getItemId(), None), data): 18 | for a in data.getAddresses(): 19 | print('Found method: %s' % a) 20 | -------------------------------------------------------------------------------- /scripts/ListRenamedCodeItems.py: -------------------------------------------------------------------------------- 1 | #?description=List renamed code items (classes, methods, fields) 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit 5 | """ 6 | Sample script JEB Decompiler. 7 | """ 8 | class ListRenamedCodeItems(IScript): 9 | def run(self, ctx): 10 | prj = ctx.getMainProject() 11 | assert prj, 'Need a project' 12 | units = prj.findUnits(ICodeUnit) 13 | cnt = 0 14 | for unit in units: 15 | for o in unit.getClasses(): 16 | if o.isRenamed(): 17 | print('Class renamed: %s from %s' % (o.getName(), o.getName(False))) 18 | cnt += 1 19 | for o in unit.getMethods(): 20 | if o.isRenamed(): 21 | print('Method renamed: %s from %s' % (o.getName(), o.getName(False))) 22 | cnt += 1 23 | for o in unit.getFields(): 24 | if o.isRenamed(): 25 | print('Field renamed: %s from %s' % (o.getName(), o.getName(False))) 26 | cnt += 1 27 | print('Items renamed: %d' % cnt) 28 | -------------------------------------------------------------------------------- /scripts/ListUnits.py: -------------------------------------------------------------------------------- 1 | #?description=List all units of the currently opened project 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units import IBinaryUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class ListUnits(IScript): 9 | 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | assert prj, 'Need a project' 13 | 14 | print('=> Listing units int project "%s":' % prj.getName()) 15 | for art in prj.getLiveArtifacts(): 16 | for unit in art.getUnits(): 17 | self.checkUnit(unit) 18 | 19 | def checkUnit(self, unit, level=0): 20 | unitsize = -1 21 | if isinstance(unit, IBinaryUnit): 22 | unitinput = unit.getInput() 23 | # use the input 24 | # ... 25 | unitsize = unitinput.getCurrentSize() 26 | s = ' ' * level + unit.getName() 27 | if unitsize >= 0: 28 | s += ' (%d bytes)' % unitsize 29 | print(s) 30 | 31 | # recurse over children units 32 | for c in unit.getChildren(): 33 | self.checkUnit(c, level + 1) 34 | -------------------------------------------------------------------------------- /scripts/ListenToDexChangeEvents.py: -------------------------------------------------------------------------------- 1 | #?description=Listen to IDexUnit-emitted change events, and report comment updates in a pop-up window 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.events import JebEvent, J 5 | from com.pnfsoftware.jeb.core.units import IUnit, UnitChangeEventData 6 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 7 | from com.pnfsoftware.jeb.util.events import IEventListener 8 | """ 9 | Sample script JEB Decompiler. 10 | 11 | This script shows how to listen to unit-emitted UnitChange events. 12 | Specifically, the script sets up a listener for changes emitted by a DEX unit. 13 | Refer to IDexUnit and UnitChangeEventData API documentation for details. 14 | 15 | Run the script a second time to remove all listeners 16 | """ 17 | class ListenToDexChangeEvents(IScript): 18 | 19 | def run(self, ctx): 20 | prj = ctx.getMainProject() 21 | assert prj, 'Need a project' 22 | 23 | dex = prj.findUnit(IDexUnit) 24 | assert dex, 'Need a dex unit' 25 | 26 | # remove stale listeners a previous execution of this script may have added 27 | for listener in dex.getListeners(): 28 | # note: a check isinstance(listener, SampleListener) will not work here 29 | # since class objects are different every time the script is run 30 | if hasattr(listener, 'IN_SCRIPT'): 31 | dex.removeListener(listener) 32 | print('Stopped listening to UnitChange events on: %s' % dex) 33 | return 34 | 35 | # add a fresh listener 36 | dex.addListener(SampleListener(ctx)) 37 | print('Listening to UnitChange events on: %s' % dex) 38 | 39 | 40 | class SampleListener(IEventListener): 41 | def __init__(self, ctx): 42 | self.ctx = ctx 43 | self.IN_SCRIPT = 1 44 | 45 | def onEvent(self, e): 46 | if isinstance(e, JebEvent) and e.type == J.UnitChange and e.data != None: 47 | print('++++ %s' % e.data) 48 | # demo: pick out CommentUpdate changes - refer to UnitChangeEventData for other types 49 | if e.data.type == UnitChangeEventData.CommentUpdate: 50 | msg = 'New comment at %s (unit %s):\nValue:"%s"\nPrevious:"%s"' % (e.data.location, e.data.target, e.data.value, e.data.previousValue) 51 | self.ctx.displayMessageBox('Comment update', msg, None, None) 52 | -------------------------------------------------------------------------------- /scripts/PdfListStreams.py: -------------------------------------------------------------------------------- 1 | #?description=Use the PDF plugin to enumerate and dump decoded stream documents stored in the Stream sub-units of a parsed PDF unit 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units import UnitUtil, WellKnownUnitTypes 5 | from com.pnfsoftware.jeb.core.output.text import TextDocumentUtil 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | This script relies on the PDF plugin to enumerate and dump decoded stream documents stored in the Stream sub-units of a parsed PDF unit. 10 | The PDF plugin does not implement a specific sub-interface of IUnit (API), mostly because it is an open-source plugin, available on GitHub. 11 | The script below uses hardcoded unit types and document types to retrieve the objects to be dumped. 12 | An alternative is to use proper PDF plugin types directly, eg, com.pnf.plugin.pdf.unit.IPdfUnit 13 | """ 14 | class PdfListStreams(IScript): 15 | 16 | def run(self, ctx): 17 | # retrieve the primary unit (first unit of first artifact, assume it exists) 18 | prj = ctx.getMainProject() 19 | assert prj, 'Need a project' 20 | 21 | unit = prj.getLiveArtifact(0).getUnits().get(0) 22 | if unit.getFormatType() != WellKnownUnitTypes.typePdf: 23 | raise Exception('Expected a PDF file') 24 | 25 | # [OPTIONAL] refer to https://github.com/pnfsoftware/jeb-plugin-pdf/tree/master/src/main/java/com/pnf/plugin/pdf 26 | # the unit retrieved is of the IPdfUnit type, and has additional methods, eg getStatistics() provide a PdfSTatistics object 27 | print 'Encrypted:', unit.getStatistics().isEncrypted() 28 | 29 | # process all PDF Stream units 30 | for unit in UnitUtil.findDescendantsByFormatType(unit, 'Stream'): 31 | # the pdf plugin is lazy, make sure to process sub-units before retrieving data 32 | if not unit.isProcessed(): 33 | unit.process() 34 | 35 | # retrieve the 'Decoded Stream' text document 36 | for pres in unit.getFormatter().getDocumentPresentations(): 37 | if pres.getLabel() == 'Decoded Stream': 38 | doc = pres.getDocument() 39 | text = TextDocumentUtil.getText(doc) 40 | print '%s: %s' % (unit.getName(), text[:50] + '...') # TODO: eg, dump text to file(s) 41 | doc.dispose() 42 | -------------------------------------------------------------------------------- /scripts/PrintNativeRoutineIR.py: -------------------------------------------------------------------------------- 1 | #?description=Retrieve and print out the IR (Intermediate Representation) of a routine decompiled by GENDEC 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 5 | from com.pnfsoftware.jeb.core.units.code import IDecompilerUnit, DecompilationContext 6 | from com.pnfsoftware.jeb.core.units.code.asm.decompiler import INativeSourceUnit 7 | from com.pnfsoftware.jeb.core.util import DecompilerHelper 8 | """ 9 | Sample script for JEB Decompiler. 10 | 11 | ===> How to use: 12 | Two modes: 13 | - Using the UI desktop client: 14 | - load a binary file into JEB 15 | - make sure GlobalAnalysis for code plugins is enabled (it is by default) 16 | - then run the script: File, Scripts, Run... 17 | - the script will process your loaded executable 18 | - Using the command line: 19 | - run like: jeb_wincon.bat -c --script= -- 20 | - the script will create a JEB project and process the file provided on the command line 21 | 22 | ===> What is it: 23 | This script demonstrates how to retrieve and print out the Intermediate Representation of a decompiled routine. 24 | 25 | ===> Additional references: 26 | - reference blog post: https://www.pnfsoftware.com/blog/jeb-native-pipeline-intermediate-representation/ 27 | - highly recommended (else you will have much difficulty building upon the code below): follow the tutorials on www.pnfsoftware.com/jeb/devportal 28 | - for Native code and Native Decompilers: the com.pnfsoftware.jeb.core.units.code.asm package and sub-packages 29 | - see API documentation at www.pnfsoftware.com/jeb/apidoc: Native code unit and co, Native decompiler unit and co. 30 | 31 | Comments, questions, needs more details? message on us on Slack or support@pnfsoftware.com -- Nicolas Falliere 32 | """ 33 | class PrintNativeRoutineIR(IScript): 34 | def run(self, ctx): 35 | # retrieve the current project or create one and load the input file 36 | prj = ctx.getMainProject() 37 | if not prj: 38 | argv = ctx.getArguments() 39 | if len(argv) < 1: 40 | print('No project found, please provide an input binary') 41 | return 42 | 43 | self.inputFile = argv[0] 44 | print('Processing ' + self.inputFile + '...') 45 | ctx.open(self.inputFile) 46 | prj.getMainProject() 47 | 48 | # retrieve the primary code unit (must be the result of an EVM contract analysis) 49 | unit = prj.findUnit(INativeCodeUnit) 50 | if not unit: 51 | print('No native code unit found') 52 | return 53 | print('Native code unit: %s' % unit) 54 | 55 | # GlobalAnalysis is assumed to be on (default) 56 | decomp = DecompilerHelper.getDecompiler(unit) 57 | if not decomp: 58 | print('No decompiler unit found') 59 | return 60 | 61 | # retrieve a handle on the method we wish to examine 62 | method = unit.getInternalMethods().get(0) 63 | 64 | ctx = DecompilationContext(IDecompilerUnit.FLAG_KEEP_IR); 65 | decm = decomp.decompile(method, ctx) 66 | if not decm: 67 | print('Routine was not decompiled') 68 | return 69 | 70 | # IDecompiledMethod 71 | print(decm) 72 | 73 | ircfg = decm.getIRContext().getCfg() 74 | # CFG object reference, see package com.pnfsoftware.jeb.core.units.code.asm.cfg 75 | print("+++ IR-CFG for %s +++" % method) 76 | print(ircfg.format()) 77 | -------------------------------------------------------------------------------- /scripts/PrintOutDartInfo.py: -------------------------------------------------------------------------------- 1 | #?description=Basic demo for the API to access processed Dart AOT snapshots unit 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit 5 | from com.pnfsoftware.jeb.core.units.code.dart import IDartAotUnit 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | Find a Dart AOT Snapshots unit (generated by the Dart AOT Snapshots processor plugin), 10 | and use the API to print out basic snapshot information and generate the primary pool. 11 | """ 12 | class PrintOutDartInfo(IScript): 13 | def run(self, ctx): 14 | prj = ctx.getMainProject() 15 | u = prj.findUnit(IDartAotUnit) 16 | if not u: return 17 | print('Found a Dart AOT unit: %s' % u) 18 | 19 | # snapshot is of type IDartAotSnapshotInfo 20 | snap = u.getIsolateSnapshotInfo() 21 | print('Used Dart SDK version %s' % snap.getVersionTag()) 22 | print('Isolate: %s' % snap) 23 | 24 | # pool= list of nulls, longs, and IDartInternalObject 25 | pool = u.generatePrimaryPool() 26 | print('%d entries in the main pool' % len(pool)) 27 | -------------------------------------------------------------------------------- /scripts/ProcessFile.py: -------------------------------------------------------------------------------- 1 | #?description=Open and process a file into a JEB project. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | """ 5 | Sample script for JEB Decompiler. 6 | """ 7 | class ProcessFile(IScript): 8 | def run(self, ctx): 9 | # if run in headless mode, you could instead use ctx.getArguments() to retrieve the input file 10 | # if ctx is IGraphicalClientContext (assumed to be the case here), we display a file selector 11 | path = ctx.displayFileOpenSelector('Select a file for processing') 12 | if not path: 13 | return 14 | # if no project exists, one will be created and the input file processed as the primary artifact 15 | # if a project exists, the input file will be added to the project and processed 16 | ctx.open(path) 17 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | Sample scripts for JEB Decompiler. 2 | 3 | Currently, client scripts must be written with a Python 2.7 syntax. 4 | 5 | Scripts can be run in a GUI client or in headless mode. -------------------------------------------------------------------------------- /scripts/ReloadNativeTypelibsAndSiglibs.py: -------------------------------------------------------------------------------- 1 | #?description=Scan and register newly-added native type libvraries (typelibs) and signature libraries (siglibs) 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | """ 5 | Sample script for JEB Decompiler. 6 | """ 7 | class ReloadNativeTypelibsAndSiglibs(IScript): 8 | def run(self, ctx): 9 | engctx = ctx.getEnginesContext() 10 | engctx.getTypeLibraryService().rescan() 11 | engctx.getNativeSignatureDBManager().rescan() 12 | -------------------------------------------------------------------------------- /scripts/RenameDexClassesToDebugNames.py: -------------------------------------------------------------------------------- 1 | #?description=Rename dex classes to the optionl names provided in the debug metadata 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class RenameDexClassesToDebugNames(IScript): 9 | 10 | def run(self, ctx): 11 | prj = ctx.getMainProject() 12 | assert prj, 'Need a project' 13 | 14 | for unit in prj.findUnits(IDexUnit): 15 | self.process(unit) 16 | 17 | def process(self, dex): 18 | cnt = 0 19 | for c in dex.getClasses(): 20 | name = c.getName() 21 | idx = c.getSourceStringIndex() 22 | if idx >= 0: 23 | debugName = dex.getString(idx).getValue() 24 | #print(debugName) 25 | if debugName: 26 | debugName = debugName.replace('.java', '') 27 | if debugName != name and c.setName(debugName): 28 | print('Renamed class %s to %s' % (name, debugName)) 29 | cnt += 1 30 | if cnt > 0: 31 | dex.notifyGenericChange() 32 | -------------------------------------------------------------------------------- /scripts/RenameJavaMethodParameters.py: -------------------------------------------------------------------------------- 1 | #?description=Rename method parameters of decompiled Java methods based on their types 2 | #?shortcut= 3 | import os 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core import IPlugin 6 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit, JavaElementType 7 | """ 8 | Sample script for JEB Decompiler. 9 | 10 | This script demonstrates how to rename method parameters of decompiled Java methods. 11 | In the example below, generic parameter names ('argX') are renamed based on their type. 12 | """ 13 | class RenameJavaMethodParameters(IScript): 14 | 15 | def run(self, ctx): 16 | prj = ctx.getMainProject() 17 | assert prj, 'Need a project' 18 | 19 | # process all Java decompiled source units 20 | for unit in prj.findUnits(IJavaSourceUnit): 21 | for m in unit.getClassElement().getMethods(): 22 | self.process(unit, m) 23 | 24 | def process(self, unit, e): 25 | if not e.isExternal(): 26 | print 'Method: %s' % e 27 | namemap = {} 28 | params = e.getParameters() 29 | for param in e.getParameters(): 30 | ident = param.getIdentifier() 31 | original_name = ident.getName() 32 | if original_name != 'this': 33 | debug_name = ident.getDebugName() 34 | effective_name = unit.getIdentifierName(ident) 35 | print ' Parameter: %s (debug name: %s) (effective: %s)' % (param, debug_name, effective_name) 36 | if not effective_name and not debug_name and original_name.startswith('arg') and param.getType().isClassOrInterface(): 37 | t = str(param.getType()) 38 | simplename = t[t.rfind('.') + 1:].lower() 39 | v = namemap.get(simplename, 0) 40 | namemap[simplename] = v + 1 41 | effective_name = '%s%d' % (simplename , v) 42 | print ' -> Renaming to: %s' % effective_name 43 | unit.setIdentifierName(ident, effective_name) 44 | -------------------------------------------------------------------------------- /scripts/ReplaceStringsInDex.py: -------------------------------------------------------------------------------- 1 | #?description=Use the specialized IDexUnit interface to replace all dex strings 'text/html' by 'foobar'. 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | from com.pnfsoftware.jeb.core.units.code import ICodeUnit, ICodeItem 5 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit 6 | from com.pnfsoftware.jeb.core.units.code.android.dex import IDexString 7 | """ 8 | Sample script for JEB Decompiler. 9 | """ 10 | class ReplaceStringsInDex(IScript): 11 | 12 | def run(self, ctx): 13 | prj = ctx.getMainProject() 14 | assert prj, 'Need a project' 15 | 16 | for dex in prj.findUnits(IDexUnit): 17 | self.processDex(dex) 18 | 19 | def processDex(self, dex): 20 | # replace DEX strings 21 | cnt = 0 22 | for s in dex.getStrings(): 23 | if s.getValue().startswith('text/html'): 24 | s.setValue('foobar') 25 | cnt += 1 26 | print('String replaced') 27 | if cnt > 0: 28 | dex.notifyGenericChange() 29 | -------------------------------------------------------------------------------- /scripts/ReplaceStringsInJavaAST.py: -------------------------------------------------------------------------------- 1 | #?description=Find the first decompiled Java unit and replace String immediates by random string 2 | #?shortcut= 3 | import time 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units.code.java import IJavaSourceUnit, IJavaConstant 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class ReplaceStringsInJavaAST(IScript): 10 | 11 | def run(self, ctx): 12 | prj = ctx.getMainProject() 13 | assert prj, 'Need a project' 14 | 15 | src = prj.findUnit(IJavaSourceUnit) 16 | assert src, 'Need a java source unit (decompiled)' 17 | 18 | print('Processing %s' % src) 19 | 20 | self.replcnt = 0 21 | self.cstbuilder = src.getFactories().getConstantFactory() 22 | for m in src.getClassElement().getMethods(): 23 | self.checkElement(None, m) 24 | 25 | print('Replaced %d strings' % self.replcnt) 26 | src.notifyGenericChange() 27 | 28 | def checkElement(self, parent, e): 29 | if isinstance(e, IJavaConstant) and e.isString(): 30 | parent.replaceSubElement(e, self.cstbuilder.createString('blah_' + str(time.time()))) 31 | self.replcnt += 1 32 | for subelt in e.getSubElements(): 33 | self.checkElement(e, subelt) 34 | -------------------------------------------------------------------------------- /scripts/RequestUserInput.py: -------------------------------------------------------------------------------- 1 | #?description=Demo how to invoke common dialog boxes 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.client.api import IconType, ButtonGroupType 5 | """ 6 | Sample script for JEB Decompiler. 7 | """ 8 | class RequestUserInput(IScript): 9 | def run(self, ctx): 10 | if not isinstance(ctx, IGraphicalClientContext): 11 | print('This script must be run within a graphical client') 12 | return 13 | 14 | value = ctx.displayQuestionBox('Input', 'Enter a random string', '3') 15 | if value != None: 16 | ctx.displayMessageBox('Information', 'The value was %s' % value, IconType.INFORMATION, ButtonGroupType.OK) 17 | 18 | # other dialogs: see IGraphicalClientContext.displayXxx() methods 19 | -------------------------------------------------------------------------------- /scripts/RequestUserInputWithComplexForm.py: -------------------------------------------------------------------------------- 1 | #?description=Demo how to generate a UI form to request for complex multiple inputs 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.client.api import FormEntry 5 | from java.util.function import Predicate 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class RequestUserInputWithComplexForm(IScript): 10 | def run(self, ctx): 11 | if not isinstance(ctx, IGraphicalClientContext): 12 | print('This script must be run within a graphical client') 13 | return 14 | 15 | # used to validate first name and last name entries 16 | class NameValidator(Predicate): 17 | def __init__(self, minlen): 18 | self.minlen = minlen 19 | def test(self, val): 20 | return len(val) >= self.minlen 21 | 22 | # a more complicated form with multi-line field, inline headers, and a validator 23 | values = ctx.displayForm('Form 2', 'Please provide your full details', 24 | FormEntry.Text('First name', '', FormEntry.INLINE, NameValidator(3), 0, 0), 25 | FormEntry.Text('Last name', '', FormEntry.INLINE, NameValidator(6), 0, 0), 26 | FormEntry.Text('Address', 'default address', 0, None, 40, 5), 27 | ) 28 | if values: 29 | print(values) 30 | 31 | -------------------------------------------------------------------------------- /scripts/RequestUserInputWithForm.py: -------------------------------------------------------------------------------- 1 | #?description=Demo how to generate a simple UI form to request for multiple inputs 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | """ 5 | Sample script for JEB Decompiler. 6 | """ 7 | class RequestUserInputWithForm(IScript): 8 | def run(self, ctx): 9 | if not isinstance(ctx, IGraphicalClientContext): 10 | print('This script must be run within a graphical client') 11 | return 12 | 13 | # a simple form 14 | values = ctx.displaySimpleForm('Form 1', 'Please provide your details', 'First name', '', 'Last name', '') 15 | if values: 16 | print('Info: %s, %s' % (values[1], values[0])) 17 | -------------------------------------------------------------------------------- /scripts/RestoreMissingBookmarksFromComments.py: -------------------------------------------------------------------------------- 1 | #?description=Restore missing bookmarks from special comments 2 | #?shortcut= 3 | 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units import IInteractiveUnit 6 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil 7 | 8 | class RestoreMissingBookmarksFromComments(IScript): 9 | def run(self, ctx): 10 | prj = ctx.getMainProject() 11 | if not prj: 12 | return 13 | favcomments = [] 14 | for unit in RuntimeProjectUtil.getAllUnits(prj): 15 | self.retrieve(unit, favcomments) 16 | print('Retrieved %d bookmarks (stored as special meta-comments)' % len(favcomments)) 17 | bm = prj.getBookmarkManager() 18 | cnt = 0 19 | for unit, addr, desc in favcomments: 20 | if not bm.get(unit, addr): 21 | print('Restoring: \'%s\' (@ %s in %s' % (desc, addr, unit)) 22 | bm.set(unit, addr, desc) 23 | cnt += 1 24 | print('Restored %d bookmarks' % cnt) 25 | 26 | def retrieve(self, unit, li): 27 | if not isinstance(unit, IInteractiveUnit): 28 | return 29 | cm = unit.getCommentManager() 30 | if not cm: 31 | return 32 | for e in cm.getComments().entrySet(): 33 | addr = e.getKey() 34 | com = e.getValue() 35 | for mc in com.getMetaComments(): 36 | s = mc.toString() 37 | if s.endswith(' (FAVORITE)'): 38 | li.append((unit, addr, s[:-11])) 39 | -------------------------------------------------------------------------------- /scripts/SampleScript.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | #?description=A sample script explictly specifying a UTF-8 encoding 4 | #?shortcut= 5 | from com.pnfsoftware.jeb.client.api import IScript 6 | """ 7 | Sample script for JEB Decompiler.. 8 | """ 9 | class SampleScript(IScript): 10 | def run(self, ctx): 11 | # For non-ASCII characters, remember to specify the encoding in the script header (here, UTF-8), 12 | # and do not forget to prefix all Unicode strings with "u", whether they're encoded (using \u or else) or not 13 | print('~~~\n' + u'Hello, 안녕하세요, 你好, こんにちは, Здравствуйте!\n' + 'This line was generated by a JEB Python script\n~~~') 14 | -------------------------------------------------------------------------------- /scripts/SampleScriptDebugging.py: -------------------------------------------------------------------------------- 1 | #?description=How to use pydevd to debug JEB Python scripts inside Eclipse IDE or PyCharm 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript 4 | """ 5 | Sample script for JEB Decompiler.. 6 | """ 7 | class SampleScriptDebugging(IScript): 8 | def run(self, ctx): 9 | 10 | # To debug inside Eclipse IDE: 11 | # 1) Install Eclipse IDE and the PyDev plugin to make it a full-fledged Python IDE 12 | # 2) Git-clone https://github.com/fabioz/PyDev.Debugger into your JEB folder 13 | # To debug inside PyCharm: 14 | # 1) Install JetBrains PyCharm 15 | # 2) Download pydevd-pycharm from https://pypi.org/project/pydevd-pycharm/#files and 16 | # unpack the pydevd-pycharm-xxx directory buried in the tarball to a newly 17 | # created `pydevd-pycharm` folder into your JEB folder 18 | 19 | # Now get ready to debug a script: 20 | # 1) Fire up the IDE, switch to the Debug perspective/layout and start the PyDev Debug Server. 21 | # 2) In the script to be debugged, import the appropriate pydevd module: 22 | 23 | # ... with Eclipse: 24 | import sys 25 | sys.path.append(ctx.getBaseDirectory() + '/PyDev.Debugger') 26 | import pydevd 27 | # ... or with PyCharm: (uncomment below and comment above) 28 | #sys.path.append(ctx.getBaseDirectory() + '/pydevd-pycharm') 29 | #import pydevd_pycharm 30 | 31 | # 3) Wherever the breakpoint is to be placed, call settrace() 32 | # note: additional parameters may be specified, such as the debugger server hostname/port, etc. 33 | # refer to pydevd documentation for details 34 | pydevd.settrace(stdoutToServer=True, stderrToServer=True) 35 | 36 | # 4) Now, run your script (e.g. within JEB GUI, press F2, then select or browse for your script, and execute it) 37 | # With settrace invoked just above, when the script is run, the debugger will be brought up when execution reaches 38 | # the statement that follows it, in the case of this example, that one: 39 | a = 1 40 | 41 | # 5) Switch to your IDE; your script should have been brought up in the editor, with the current on-breakpoint line 42 | # highlighted in green. Debug as expected (tracing, variable views/writes, etc.). Note that rendering of print() statements 43 | # will be delayed in JEB's logger; you can see them if JEB was started from a shell rendering stdout 44 | 45 | # some random code that can be stepped over... 46 | b = 100L 47 | c = 'strings and primitives can be viewed and changed when debugging' 48 | print('Hello, JEB script debugging: %d %d %s' % (a, b, c)) 49 | -------------------------------------------------------------------------------- /scripts/SearchAll.py: -------------------------------------------------------------------------------- 1 | #?description=Search for a string pattern across all text documents produced by all units under the project root 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext, IconType, ButtonGroupType 4 | from com.pnfsoftware.jeb.core.output.text import ITextDocument 5 | import re 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | Note that better search facilities exist in the GUI client, 10 | such as the Omnibox (F3) or Quick-Search (CTR+Q or CMD+Q). 11 | """ 12 | class SearchAll(IScript): 13 | 14 | def run(self, ctx): 15 | if not isinstance(ctx, IGraphicalClientContext): 16 | print('This script must be run within a graphical client') 17 | return 18 | 19 | prj = ctx.getMainProject() 20 | assert prj, 'Need a project' 21 | 22 | searchstring = ctx.displayQuestionBox('Search All Existing Units', 'Regex pattern to be searched across all units: ', '') 23 | self.pattern = re.compile(searchstring, re.I) 24 | if not self.pattern: 25 | print('Please provide a search string') 26 | return 27 | 28 | print('Searching "%s" ...' % searchstring) 29 | for art in prj.getLiveArtifacts(): 30 | for unit in art.getUnits(): 31 | self.checkUnit(unit) 32 | print('Done') 33 | 34 | 35 | def checkUnit(self, unit, level=0): 36 | if not unit.isProcessed(): 37 | unit.process() 38 | for doc in self.getTextDocuments(unit): 39 | searchResults = self.searchTextDocument(doc, self.pattern) 40 | for lineIndex, matchText, fullText in searchResults: 41 | print('Found in unit: %s (%s) on line %d : "%s" (full text: "%s")' % (unit.getName(), unit.getFormatType(), lineIndex, matchText, fullText)) 42 | doc.dispose() 43 | 44 | # recurse over children units 45 | for c in unit.getChildren(): 46 | self.checkUnit(c, level + 1) 47 | 48 | 49 | def getTextDocuments(self, srcUnit): 50 | r = [] 51 | formatter = srcUnit.getFormatter() 52 | if formatter and formatter.getDocumentPresentations(): 53 | for pres in formatter.getPresentations(): 54 | doc = pres.getDocument() 55 | if isinstance(doc, ITextDocument): 56 | r.append(doc) 57 | return r 58 | 59 | 60 | def searchTextDocument(self, doc, pattern): 61 | r = [] 62 | alldoc = doc.getDocumentPart(0, 10000000) 63 | for i, line in enumerate(alldoc.getLines()): 64 | s = line.getText().toString() 65 | matches = pattern.findall(s) 66 | for match in matches: 67 | r.append((i + 1, match, s)) 68 | return r 69 | -------------------------------------------------------------------------------- /scripts/TaskInWorkerThread.py: -------------------------------------------------------------------------------- 1 | #?description=Run an interruptible task in a worker thread 2 | #?shortcut= 3 | import time 4 | from java.lang import Runnable, Thread 5 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 6 | """ 7 | Sample script for JEB Decompiler. 8 | """ 9 | class TaskInWorkerThread(IScript): 10 | def run(self, ctx): 11 | if not isinstance(ctx, IGraphicalClientContext): 12 | print('This script must be run within a graphical client') 13 | return 14 | # will start a worker thread and run the task; a pop-up will be shown to provide a way to interrupt the task 15 | ctx.executeAsync('Counting...', SimpleTask()) 16 | print('Done.') 17 | 18 | class SimpleTask(Runnable): 19 | def run(self): 20 | for i in range(10): 21 | # react to user pressing Cancel 22 | if Thread.interrupted(): 23 | print('The task was interrupted') 24 | break 25 | print('Counter: %d' % i) 26 | time.sleep(1) 27 | -------------------------------------------------------------------------------- /scripts/TaskWithReturnInWorkerThread.py: -------------------------------------------------------------------------------- 1 | #?description=Run an interruptible task in a worker thread 2 | #?shortcut= 3 | import time 4 | from java.lang import Thread 5 | from java.util.concurrent import Callable 6 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 7 | """ 8 | Sample script for JEB Decompiler. 9 | """ 10 | class TaskWithReturnInWorkerThread(IScript): 11 | def run(self, ctx): 12 | if not isinstance(ctx, IGraphicalClientContext): 13 | print('This script must be run within a graphical client') 14 | return 15 | # will start a worker thread and run the task; a pop-up will be shown to provide a way to interrupt the task 16 | r = ctx.executeAsyncWithReturn('Counting... and returning a value', SimpleTask()) 17 | print r 18 | 19 | # note the use of Callable here 20 | class SimpleTask(Callable): 21 | def call(self): 22 | for i in range(5): 23 | # react to user pressing Cancel 24 | if Thread.interrupted(): 25 | print('The task was interrupted') 26 | break 27 | print('Counter: %d' % i) 28 | time.sleep(1) 29 | return 123 30 | -------------------------------------------------------------------------------- /scripts/TranslateString.py: -------------------------------------------------------------------------------- 1 | #?description=Localize the selected string to English. Optionally use GCP to perform the translation (see the script to set up your API key). 2 | #?shortcut= 3 | import json 4 | import os 5 | import traceback 6 | import urllib2 7 | import webbrowser 8 | from com.pnfsoftware.jeb.client.api import IScript 9 | from com.pnfsoftware.jeb.util.net import Net 10 | from com.pnfsoftware.jeb.core.units import IInteractiveUnit 11 | """ 12 | Sample script for JEB Decompiler. 13 | 14 | Localize strings to English. The translated string is also registered as a comment if the unit supports it. 15 | Optionally use Google services to perform the translation: GCP key is expected to be found in the GCP_API_KEY env.var. 16 | """ 17 | class TranslateString(IScript): 18 | def run(self, ctx): 19 | f = ctx.getFocusedFragment() 20 | if not f: 21 | return 22 | 23 | sel = f.getSelectedText() or f.getActiveItemAsText() 24 | if not sel: 25 | return 26 | 27 | print('Text: %s' % sel) 28 | text = sel.strip(' \'\"') 29 | 30 | # if you have set up a Google Cloud Engine API key, use the web Service and add the translated string as a comment 31 | # else, open up a browser and navigate to Google Translate 32 | 33 | key = os.environ.get('GCP_API_KEY') 34 | if not key: 35 | url = 'https://translate.google.com/#view=home&op=translate&sl=auto&tl=en&text=%s' % urllib2.quote(text.encode('utf8')) 36 | print('Query: %s' % url) 37 | webbrowser.open(url) 38 | return 39 | 40 | url = 'https://translation.googleapis.com/language/translate/v2?key=%s' % key 41 | try: 42 | r = Net().query(url, {'target': 'en', 'q': text}) 43 | tt = json.loads(r)['data']['translations'][0]['translatedText'] 44 | except Exception as e: 45 | traceback.print_exc(e) 46 | return 47 | 48 | print('Translation: %s' % tt) 49 | 50 | a = f.getActiveAddress() 51 | if a and isinstance(f.getUnit(), IInteractiveUnit): 52 | comment0 = f.getUnit().getComment(a) 53 | comment = tt + '\n' + comment0 if comment0 else tt 54 | f.getUnit().setComment(a, comment) 55 | -------------------------------------------------------------------------------- /scripts/UIDemo.py: -------------------------------------------------------------------------------- 1 | #?description=Demo on how to use the JEB UI-API to query views and fragments (when run in the GUI client) 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil, IUnitFilter 5 | from com.pnfsoftware.jeb.core.units import IUnit 6 | """ 7 | Sample script for JEB Decompiler. 8 | 9 | This script demonstrates how to use the UI API to query views and fragments generated by JEB GUI. 10 | """ 11 | class UIDemo(IScript): 12 | def run(self, ctx): 13 | if not isinstance(ctx, IGraphicalClientContext): 14 | print('This script must be run within a graphical client') 15 | return 16 | 17 | # show which unit view is currently focused 18 | v = ctx.getFocusedView() 19 | print('Focused view: %s' % v) 20 | 21 | # enumerate all unit views (views representing units) and fragments within those views 22 | print('Views and fragments:') 23 | views = ctx.getViews() 24 | for view in views: 25 | print('- %s' % view.getLabel()) 26 | fragments = view.getFragments() 27 | for fragment in fragments: 28 | print(' - %s' % view.getFragmentLabel(fragment)) 29 | 30 | # focus test 31 | if len(views) >= 2: 32 | print('Focusing the second Unit view (if any)') 33 | views[1].setFocus() 34 | 35 | # opening the first certificate unit we find (in an APK, there should be one) 36 | prj = ctx.getMainProject() 37 | if prj: 38 | unitFilter = UnitFilter('cert') 39 | units = RuntimeProjectUtil.filterUnits(prj, unitFilter) 40 | if units: 41 | ctx.openView(units.get(0)) 42 | 43 | 44 | class UnitFilter(IUnitFilter): 45 | def __init__(self, formatType): 46 | self.formatType = formatType 47 | def check(self, unit): 48 | return unit.getFormatType() == self.formatType 49 | -------------------------------------------------------------------------------- /scripts/WalkEvmDecomp.py: -------------------------------------------------------------------------------- 1 | #?description=Retrieve the decompiled EVM code of an Ethereum contract 2 | #?shortcut= 3 | #?deprecated=refer to PrintNativeRoutineIR.py to see how GENDEC's IR can be navigated 4 | from com.pnfsoftware.jeb.client.api import IScript 5 | from com.pnfsoftware.jeb.core.units import INativeCodeUnit 6 | from com.pnfsoftware.jeb.core.units.code.asm.type import TypeUtil 7 | from com.pnfsoftware.jeb.core.units.code.asm.decompiler import INativeSourceUnit 8 | from com.pnfsoftware.jeb.core.util import DecompilerHelper 9 | """ 10 | Sample script for JEB Decompiler. 11 | 12 | !!! DEPRECATED !!! see note above. 13 | 14 | ===> How to use: 15 | Two modes: 16 | - Using the UI desktop client:\ 17 | - load an Ethereum EVM contract 18 | - make sure the GlobalAnalysis for EVM modules is enabled (it is by default) 19 | - then run the script: File, Scripts, Run... 20 | - the script will process your loaded contract 21 | - Using the command line: 22 | - run like: jeb_wincon.bat -c --script= -- 23 | - the script will create a JEB project and process the contract file provided on the command line 24 | - (make sure the contract file ends with the 'evm-bytecode' extension) 25 | 26 | ===> What is it: 27 | This script demonstrates how to retrieve the decompiled EVM code of an Ethereum contract. 28 | - The decompiled contract's Abstract Syntax Tree (AST) is walked and its node types are printed 29 | (reference: https://www.pnfsoftware.com/jeb/apidoc/reference/com/pnfsoftware/jeb/core/units/code/asm/decompiler/ast/package-summary.html) 30 | - The individual methods of the contract are retrieved: their AST is also displayed 31 | - The most refined Intermediate Representation (IR) code of each individual method is also displayed 32 | (reference: https://www.pnfsoftware.com/jeb/apidoc/reference/com/pnfsoftware/jeb/core/units/code/asm/decompiler/ir/package-summary.html) 33 | 34 | ===> Additional references: 35 | - highly recommended (else you will have much difficulty building upon the code below): follow the tutorials on www.pnfsoftware.com/jeb/devportal 36 | - for Native code and Native Decompilers: the com.pnfsoftware.jeb.core.units.code.asm package and sub-packages 37 | - see apidoc at www.pnfsoftware.com/jeb/apidoc: Native code unit and co, Native decompiler unit and co. 38 | 39 | More to be published on the blog and technical papers. 40 | 41 | Comments, questions, needs more details? message on us on Slack or support@pnfsoftware.com -- Nicolas Falliere 42 | """ 43 | class WalkEvmDecomp(IScript): 44 | 45 | def run(self, ctx): 46 | prj = ctx.getMainProject() 47 | if not prj: 48 | argv = ctx.getArguments() 49 | if len(argv) < 1: 50 | print('Please provide an input contract file') 51 | return 52 | self.inputFile = argv[0] 53 | print('Processing ' + self.inputFile + '...') 54 | if not self.inputFile.endswith('.evm-bytecode'): 55 | print('Warning: it is recommended your contract file has the evm-bytecode extension in order to guarantee processing by the EVM modules') 56 | ctx.open(self.inputFile) 57 | prj = ctx.getMainProject() 58 | 59 | # retrieve the primary code unit (must be the result of an EVM contract analysis) 60 | unit = prj.findUnit(INativeCodeUnit) 61 | print('EVM unit: %s' % unit) 62 | 63 | # GlobalAnalysis is assumed to be on: the contract is already decompiled 64 | # we retrieve a handle on the EVM decompiler ... 65 | decomp = DecompilerHelper.getDecompiler(unit) 66 | if not decomp: 67 | print('No decompiler unit found') 68 | return 69 | 70 | # ... and retrieve a handle on the decompiled contract's INativeSourceUnit 71 | src = decomp.decompile("DecompiledContract") 72 | print(src) 73 | 74 | #targets = src.getDecompilationTargets() 75 | #print(targets) 76 | 77 | # let's get the contract's AST 78 | astDecompClass = src.getRootElement() 79 | # the commented line below will output the entire decompiled source code 80 | #print(astDecompClass) 81 | # here, we walk the AST tree and print the type of each element in the tree 82 | print("*** AST of contract") 83 | self.displayASTTree(astDecompClass) 84 | 85 | # now, let's retrieve the individual methods implemented in the contract 86 | methods = unit.getInternalMethods() 87 | for method in methods: 88 | # retrieve the INativeSourceUnit of the method 89 | r = decomp.decompileMethod(method) 90 | print("*** AST for method: %s" % method.getName(True)) 91 | self.displayASTTree(r.getRootElement()) 92 | 93 | # list of INativeDecompilationTarget for a decompiled method 94 | decompTargets = r.getDecompilationTargets() 95 | if decompTargets: 96 | # get the first (generally, only) target object 97 | decompTarget = decompTargets.get(0) 98 | # an INativeDecompilationTarget object aggregates many useful objects resulting from the decompilation of a method 99 | # here, we retrieve the most refined CFG of the Intermediate Representation for the method 100 | ircfg = decompTarget.getContext().getCfg() 101 | # CFG object reference, see package com.pnfsoftware.jeb.core.units.code.asm.cfg 102 | print("++++ IR-CFG for method: %s" % method.getName(True)) 103 | print(ircfg.formatSimple()) 104 | 105 | # end of demo, we have enough with one method, uncomment to print out the AST and IR of all methods 106 | break 107 | 108 | 109 | def displayASTTree(self, e0, level=0): 110 | print('%s%s' % (level*' ', e0.getElementType())) 111 | if e0: 112 | elts = e0.getSubElements() 113 | for e in elts: 114 | self.displayASTTree(e, level+1) 115 | -------------------------------------------------------------------------------- /scripts/WidgetList.py: -------------------------------------------------------------------------------- 1 | #?description=Display a sample list box 2 | #?shortcut= 3 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext 4 | """ 5 | Sample script for JEB Decompiler. 6 | """ 7 | class WidgetList(IScript): 8 | def run(self, ctx): 9 | if not isinstance(ctx, IGraphicalClientContext): 10 | print('This script must be run within a graphical client') 11 | return 12 | value = ctx.displayList('Input', 'foo', ['Col1', 'Col2'], [['abc', 'def'], ['ffff', '']]) 13 | print(value) 14 | -------------------------------------------------------------------------------- /scripts/cluster/cluster.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | ''' 4 | Simple graph community detection using edge-betweenness method. 5 | Dependency: igraph 6 | 7 | Input file format: 8 | 9 | # vertices (160) 10 | v,449 11 | v,475 12 | ... 13 | # edges (322) 14 | e,376,391,2 15 | e,519,478,3 16 | ... 17 | 18 | Output file format: 19 | 20 | # clusters 21 | 449,711,684,344,497,... 22 | 475,340,633,324,527,... 23 | 24 | Author: Nicolas Falliere 25 | ''' 26 | 27 | import sys 28 | from igraph import * 29 | 30 | 31 | class TypeGraph: 32 | 33 | def __init__(self, filename): 34 | self.nodes = [] 35 | self.edges = [] 36 | self.idToIndex = {} 37 | 38 | with open(filename) as f: 39 | lines = f.readlines() 40 | for line in lines: 41 | line = line.strip() 42 | if not line or line.startswith('#'): 43 | continue 44 | elts = line.split(',') 45 | if elts[0] == 'v': 46 | nodeId = int(elts[1]) 47 | nodeLabel = elts[2] if len(elts) > 2 else None 48 | self.idToIndex[nodeId] = len(self.nodes) 49 | self.nodes.append((nodeId, nodeLabel)) 50 | if elts[0] == 'e': 51 | src = int(elts[1]) 52 | dst = int(elts[2]) 53 | weight = int(elts[3]) if len(elts) > 3 else 1 54 | if weight == 0: 55 | raise Exception('Weight cannot be 0') 56 | self.edges.append((src, dst, weight)) 57 | 58 | self.g = self.__createGraph() 59 | 60 | def __createGraph(self): 61 | g = Graph() 62 | g.add_vertices(len(self.nodes)) 63 | _edges = [] 64 | _weights = [] 65 | for src, dst, weight in self.edges: 66 | _edge = (self.idToIndex[src], self.idToIndex[dst]) 67 | if _edge in _edges or src == dst: 68 | raise Exception('Error in input graph: edge %d->%d' % (src, dst)) 69 | _edges.append(_edge) 70 | _weights.append(weight) 71 | g.add_edges(_edges) 72 | g.es['weight'] = _weights 73 | return g 74 | 75 | def getGraph(self): 76 | return self.g 77 | 78 | def getNodeId(self, index): 79 | return self.nodes[index][0] 80 | 81 | def getNodeLabel(self, index): 82 | return self.nodes[index][1] 83 | 84 | 85 | if __name__ == '__main__': 86 | if len(sys.argv) != 3: 87 | sys.exit(-1) 88 | 89 | typeGraph = TypeGraph(sys.argv[1]) 90 | g = typeGraph.getGraph() 91 | print(g) 92 | 93 | dendro = g.community_edge_betweenness(directed=True, weights='weight') 94 | print(dendro) 95 | 96 | clusters = dendro.as_clustering() 97 | print(clusters) 98 | 99 | output = '# clusters\n' 100 | for cluster in clusters: 101 | _cluster = [] 102 | for i in cluster: 103 | _cluster.append(str(typeGraph.getNodeId(i))) 104 | output += '%s\n' % ','.join(_cluster) 105 | 106 | with open(sys.argv[2], 'w') as f: 107 | f.write(output) 108 | --------------------------------------------------------------------------------