Ethical Hacking Tools Threadtear – Multi-functional Deobfuscation Framework for Java

Threadtear – Multi-functional Deobfuscation Framework for Java

Threadtear is a multi-functional deobfuscation framework for java. Suitable for easier code analysis with out worrying an excessive amount of about obfuscation. Even the costliest obfuscators like ZKM or Stringer are included. It also contains older deobfuscation tools from github account, nevertheless it will also be helpful for other stuff. Insert debug line numbers to raised perceive where exceptions originate, or add .printStackTrace() to attempt catch blocks with out re-compiling your code. Reverse compatibility can also be not a problem anymore (after all only when no version particular methods are used).

Execution
An “execution” is a activity that’s executed and modifies all loaded class files. There are multiple sorts of executions, varying from bytecode cleanup to string deobfuscation. Make certain to have them in the precise order. Cleanup executions for example needs to be executed at last, but additionally might help other executions if executed first.

Warning
Use this tool at your individual risk. Some executions use implemented ClassLoaders to run code from the jar file, an attacker may tweak the file in order that malicious code can be executed. Affected executions use the category me.nov.threadtear.asm.vm.VM. These are mostly used for decrypting string or resource / access obfuscation.

How to compile
First, run gradle build, then gradle fatJar. In builds/libs a runnable jar file ought to then have been created.

Make your individual execution
You can easily create your individual execution activity. Just extend me.nov.threadtear.execution.Execution:

public class MyExecution extends Execution 
 public MyExecution() 
  super(ExecutionCategory.CLEANING /* category */, "My execution" /* name */,
    "Executes something" /* description, can use html */);
 
 /**
 * This method is invoked when the user clicks on the Run button
 * @return true if success, false if failure
 */
 @Override
 public boolean execute(Map<String, Clazz> classes, boolean verbose) 
  classes.values().stream().map(c -> c.node).forEach(c -> 
   //rework the classes right here utilizing the tree-API of ASM
  );
  return false;
 

To load ClassNodes at runtime, use the me.nov.threadtear.asm.vm.VM class and implement me.nov.threadtear.asm.vm.IVMReferenceHandler:

public class MyExecution extends Execution implements IVMReferenceHandler 
 public MyExecution() 
  super(ExecutionCategory.GENERIC, "My execution", "Loads ClassNodes at runtime");
 
 @Override
 public boolean execute(Map<String, Clazz> classes, boolean verbose) 
  classes.values().stream().map(c -> c.node).forEach(c -> 
   VM vm = VM.constructVM(this);
   //rework bytecode to java.lang.Class
   Class<?> loadedClass = vm.loadClass(c.name.replace('/', '.'), true);
   //do stuff together with your class right here
   loadedClass.getMethods[0].invoke(...);
   return true;
  );
 
 /**
 * Will get invoked by VM, when VM.loadClass is known as
 */
 @Override
 public ClassNode attemptClassLoad(String name) 
  //attempt to find the category to be loaded in open jar archive
  return classes.containsKey(name) ? classes.get(name).node : null;
 

Using the ConstantTracker (me.nov.threadtear.analysis.stack.ConstantTracker) you may analyze methods and keep track of non-variable stack values. If for example iconst_0 is pushed to the stack, the worth itself is not misplaced like within the basic ASM analyzer, and you should utilize it to predict things afterward within the code.

public class MyExecution extends Execution implements IConstantReferenceHandler {
 public MyExecution() 
  super(ExecutionCategory.GENERIC, "My execution", "Performs stack analysis and replaces code.");
 
 @Override
 public boolean execute(Map<String, Clazz> classes, boolean verbose) 
  classes.values().stream().map(c -> c.node).forEach(this::analyzeAndRewrite);
  return true;
 
 public void analyzeAndRewrite(ClassNode cn) {
  cn.methods.forEach(m -> {
   // this analyzer retains known stack values, e.g. might be helpful for bounce prediction
   Analyzer<ConstantValue> a = new Analyzer<ConstantValue>(new ConstantTracker(this, Access.isStatic(m.access), m.maxLocals, m.desc, new Object[0]));
   attempt 
    a.analyze(cn.name, m);
    catch (AnalyzerException e) 
    logger.severe("Failed stack analysis in " + cn.name + "." + m.name + ":" + e.getMessage());
    return;
   
      Frame<ConstantValue>[] frames = a.getFrames();
   InsnList rewrittenCode = new InsnList();
   Map<LabelNode, LabelNode> labels = Instructions.cloneLabels(m.instructions);

   // rewrite method instructions
   for (int i = 0; i < m.instructions.size(); i++) 
    SummaryInsnNode ain = m.instructions.get(i);
    Frame<ConstantValue> frame = frames[i];
    // replace / modify instructions, etc...
    if (frame.getStackSize() > 0) 
     ConstantValue top = frame.getStack(frame.getStackSize() - 1);
     if (top.isKnown() && top.isInteger()) 
      int knownTopStackValue = top.getInteger();
      // use the known stack to remove jumps, simplify code, etc...
      // if(...)  rewrittenCode.add(...); 
      continue;
     
    
    rewrittenCode.add(ain.clone(labels));
   
   // update instructions and repair attempt catch blocks, local variables, etc...
   Instructions.upda   teInstructions(m, labels, rewrittenCode);
  });
 }
 /**
  * Use this method to predict stack values if fields are loaded
  */
 @Override
 public Object getFieldValueOrNull(BasicValue v, String owner, String name, String desc) 
  return null;
 
 /**
  * Use this method to predict stack values if methods are invoked on known objects
  */
 @Override
 public Object getMethodReturnOrNull(BasicValue v, String owner, String name, String desc, List<? extends ConstantValue> values) 
  if (name.equals("toCharArray") && owner.equals("java/lang/String")) 
   if (!values.get(0).isKnown()) 
    // invocation target is just not known, we won't compute the return
    return null;
   
   return ((String) values.get(0).getValue()).toCharArray();
  
  return null;
 
}

Don’t forget so as to add your execution to the tree in me.nov.threadtear.swing.part.dialog.ExecutionChoice!

Tips & Tricks
There are some tricks that may assist you identify and deobfuscate jar files successfully. Before running executions, decompile the code to find out what wants for use. You can use the implemented decompiler for that.

Deobfuscation order
The best order for deobfuscation is generic executions > access deobfuscation > string deobfuscation > cleaning executions.

Identification
Obfuscators exhibit patterns which you should utilize to identify obfuscators. The easiest approach to identify an obfuscator is to skim the META-INF/MANIFEST.MF file. It’s potential that there’s an Obfuscated-By: XXX or Protected-By: XXX attribute.

ZKM
Extremely (flow-) obfuscated code, often noticeable by a string decryption method within the static initializer containing switches, or string decryption methods with a really long switch block (about 250 cases). ZKM is one of many best obfuscators for java, and likewise very costly.

Stringer
If your jar file contains some special classes with large decryption algorithms which might be utilized by string obfuscation and access obfuscation, it is most likely Stringer. If your file was obfuscated with multiple obfuscators, and Stringer is considered one of them, it’s best to begin your deobfuscation with Stringer, as Stringer obfuscation can’t be overwritten. Stringer also may be very protecting and one of many costliest obfuscators. Unlike normal obfuscators it doesn’t include name obfuscation. It is very used as “second layer”. Probably 90% of folks that use this obfuscator are utilizing a crack.

Allatori
Class names like IiIlIlIiIl or aUx, cOn, PrX indicate Allatori obfuscation. Allatori may be very common, since it offers a free demo that accessible inside a few clicks. The obfuscation is just not that tough to reverse.

Other obfuscators
For other obfuscators you may attempt generic executions or open a difficulty and I’ll see what i can do.

Libraries needed
commons-io 2.6, darklaf-1.3.3.4, asm-all 8+

Notes:

  • Do not deobfuscate any file that does not belong to you.
  • Please open a difficulty or send me an email if a transformer does not work properly and fasten the log.
  • Note that output files are most likely not runnable. If you continue to need to attempt to run them use -noverify as JVM argument!
  • This tool is meant for Java Eight however it can most likely run on higher versions too.

LEAVE A REPLY

Please enter your comment!
Please enter your name here