{"id":13591139,"url":"https://github.com/GraxCode/threadtear","last_synced_at":"2025-04-08T14:32:36.186Z","repository":{"id":41375398,"uuid":"255287047","full_name":"GraxCode/threadtear","owner":"GraxCode","description":"Multifunctional java deobfuscation tool suite","archived":false,"fork":false,"pushed_at":"2023-04-22T02:42:23.000Z","size":2179,"stargazers_count":897,"open_issues_count":34,"forks_count":123,"subscribers_count":33,"default_branch":"master","last_synced_at":"2024-11-06T11:51:20.492Z","etag":null,"topics":["bytecode","bytecode-manipulation","deobfuscate","jar","java","obfuscation"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GraxCode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-04-13T09:45:37.000Z","updated_at":"2024-10-29T03:00:19.000Z","dependencies_parsed_at":"2024-01-14T04:51:43.367Z","dependency_job_id":null,"html_url":"https://github.com/GraxCode/threadtear","commit_stats":null,"previous_names":[],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GraxCode%2Fthreadtear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GraxCode%2Fthreadtear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GraxCode%2Fthreadtear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GraxCode%2Fthreadtear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GraxCode","download_url":"https://codeload.github.com/GraxCode/threadtear/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247860983,"owners_count":21008403,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bytecode","bytecode-manipulation","deobfuscate","jar","java","obfuscation"],"created_at":"2024-08-01T16:00:54.045Z","updated_at":"2025-04-08T14:32:34.188Z","avatar_url":"https://github.com/GraxCode.png","language":"Java","readme":"# Threadtear [![Build Status](https://travis-ci.com/GraxCode/threadtear.svg?branch=master)](https://travis-ci.com/GraxCode/threadtear) [![Release](https://img.shields.io/github/v/release/GraxCode/threadtear)](https://github.com/GraxCode/threadtear/releases) [![Downloads](https://img.shields.io/github/downloads/GraxCode/threadtear/total)](https://github.com/GraxCode/threadtear/releases)\nThreadtear is a multifunctional deobfuscation tool for java. Android application support is coming soon (Currently working on a dalvik to java converter). \nSuitable for easier code analysis without worrying too much about obfuscation.\nEven the most expensive obfuscators like ZKM or Stringer are included. For easier debugging there are other tools included. \nInsert debug line numbers to better understand where exceptions originate, or add .printStackTrace() to try catch blocks without re-compiling your code. \nReverse compatibility is not a problem anymore, if no version specific methods are used. Analyze code flow in a graph, to better understand algorithms.\n![Screenshot 5](screenshots/4.png)\n![Screenshot 1](screenshots/0.png)\n![Screenshot 2](screenshots/1.png)\n![Screenshot 3](screenshots/2.png)\n![Screenshot 4](screenshots/3.png)\n\n## Executions \nAn \"execution\" is a task that is executed and modifies all loaded class files. \nThere are multiple types of executions, varying from bytecode cleanup to string deobfuscation. \nMake sure to have them in the right order. Cleanup executions for example should be executed at last, but also can help other executions if executed first.\nIf you are ready, click on the \"Run\" button, and they will be executed in order.\n\n## Warning\nUse this tool at your own risk. Some executions use implemented ClassLoaders to run code from the jar file. An attacker could tweak the bytecode so that malicious code could be executed.\nAffected executions use the class `me.nov.threadtear.asm.vm.VM`. \nThese are mostly used for decrypting string or resource / access obfuscation, as it is much easier to execute the decryption methods remotely.\n### Security\nThreadtear tries its best to protect you from malicious calls (arbitrary code executions) using its own `SecurityManager`, but there is no guarantee. \nEspecially with deobfuscators like for ZKM or Stringer you have to be very careful, as reflection has to be allowed, otherwise they would not function.\nIf you discover an ACE, please open an issue. I will try to fix them as soon as possible.\n\n## How to compile \nFirst, run `gradle build`, then `gradle fatJar`. In `builds/libs` a runnable jar file should then have been created. If you don't want to download the repo, you can use the latest release.\n\n## Make your own execution\nYou can easily create your own execution task. Just extend `me.nov.threadtear.execution.Execution`:\n```java\npublic class MyExecution extends Execution {\n\tpublic MyExecution() {\n\t\tsuper(ExecutionCategory.CLEANING /* category */, \"My execution\" /* name */,\n\t\t\t\t\"Executes something\" /* description, can use html */);\n\t}\n\t/**\n\t* This method is invoked when the user clicks on the Run button\n\t* @return true if success, false if failure\n\t*/\n\t@Override\n\tpublic boolean execute(Map\u003cString, Clazz\u003e classes, boolean verbose) {\n\t\tclasses.values().stream().map(c -\u003e c.node).forEach(c -\u003e {\n\t\t\t//transform the classes here using the tree-API of ASM\n\t\t});\n\t\treturn false;\n\t}\n}\n```\nTo load ClassNodes at runtime, use the `me.nov.threadtear.asm.vm.VM` class and implement `me.nov.threadtear.asm.vm.IVMReferenceHandler`:\n```java\npublic class MyExecution extends Execution implements IVMReferenceHandler {\n\tpublic MyExecution() {\n\t\tsuper(ExecutionCategory.GENERIC, \"My execution\", \"Loads ClassNodes at runtime\");\n\t}\n\t@Override\n\tpublic boolean execute(Map\u003cString, Clazz\u003e classes, boolean verbose) {\n\t\tclasses.values().stream().map(c -\u003e c.node).forEach(c -\u003e {\n\t\t\tVM vm = VM.constructVM(this);\n\t\t\t//transform bytecode to java.lang.Class\n\t\t\tClass\u003c?\u003e loadedClass = vm.loadClass(c.name.replace('/', '.'), true);\n\t\t\t//do stuff with your class here\n\t\t\tloadedClass.getMethods()[0].invoke(...);\n\t\t\treturn true;\n\t\t});\n\t}\n\t/**\n\t* Will get invoked by VM, when VM.loadClass is called\n\t*/\n\t@Override\n\tpublic ClassNode tryClassLoad(String name) {\n\t\t//try to find the class to be loaded in open jar archive\n\t\treturn classes.containsKey(name) ? classes.get(name).node : null;\n\t}\n}\n```\nUsing the ConstantTracker (`me.nov.threadtear.analysis.stack.ConstantTracker`) you can analyze methods and keep track of non-variable stack values. \nIf for example `iconst_0` is pushed to the stack, the value itself isn't lost like in the basic ASM analyzer, and you can use it to predict things later on in the code.\n```java\npublic class MyExecution extends Execution implements IConstantReferenceHandler {\n\tpublic MyExecution() {\n\t\tsuper(ExecutionCategory.GENERIC, \"My execution\", \"Performs stack analysis and replaces code.\");\n\t}\n\t@Override\n\tpublic boolean execute(Map\u003cString, Clazz\u003e classes, boolean verbose) {\n\t\tclasses.values().stream().map(c -\u003e c.node).forEach(this::analyzeAndRewrite);\n\t\treturn true;\n\t}\n\tpublic void analyzeAndRewrite(ClassNode cn) {\n\t\tcn.methods.forEach(m -\u003e {\n\t\t\t// this analyzer keeps known stack values, e.g. can be useful for jump prediction\n\t\t\tAnalyzer\u003cConstantValue\u003e a = new Analyzer\u003cConstantValue\u003e(new ConstantTracker(this, Access.isStatic(m.access), m.maxLocals, m.desc, new Object[0]));\n\t\t\ttry {\n\t\t\t\ta.analyze(cn.name, m);\n\t\t\t} catch (AnalyzerException e) {\n\t\t\t\tlogger.severe(\"Failed stack analysis in \" + cn.name + \".\" + m.name + \":\" + e.getMessage());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tFrame\u003cConstantValue\u003e[] frames = a.getFrames();\n\t\t\tInsnList rewrittenCode = new InsnList();\n\t\t\tMap\u003cLabelNode, LabelNode\u003e labels = Instructions.cloneLabels(m.instructions);\n\n\t\t\t// rewrite method instructions\n\t\t\tfor (int i = 0; i \u003c m.instructions.size(); i++) {\n\t\t\t\tAbstractInsnNode ain = m.instructions.get(i);\n\t\t\t\tFrame\u003cConstantValue\u003e frame = frames[i];\n\t\t\t\t// replace / modify instructions, etc...\n\t\t\t\tif (frame.getStackSize() \u003e 0) {\n\t\t\t\t\tConstantValue top = frame.getStack(frame.getStackSize() - 1);\n\t\t\t\t\tif (top.isKnown() \u0026\u0026 top.isInteger()) {\n\t\t\t\t\t\tint knownTopStackValue = top.getInteger();\n\t\t\t\t\t\t// use the known stack to remove jumps, simplify code, etc...\n\t\t\t\t\t\t// if(...) { rewrittenCode.add(...); }\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trewrittenCode.add(ain.clone(labels));\n\t\t\t}\n\t\t\t// update instructions and fix try catch blocks, local variables, etc...\n\t\t\tInstructions.updateInstructions(m, labels, rewrittenCode);\n\t\t});\n\t}\n\t/**\n\t * Use this method to predict stack values if fields are loaded\n\t */\n\t@Override\n\tpublic Object getFieldValueOrNull(BasicValue v, String owner, String name, String desc) {\n\t\treturn null;\n\t}\n\t/**\n\t * Use this method to predict stack values if methods are invoked on known objects\n\t */\n\t@Override\n\tpublic Object getMethodReturnOrNull(BasicValue v, String owner, String name, String desc, List\u003c? extends ConstantValue\u003e values) {\n\t\tif (name.equals(\"toCharArray\") \u0026\u0026 owner.equals(\"java/lang/String\")) {\n\t\t\tif (!values.get(0).isKnown()) {\n\t\t\t\t// invocation target is not known, we can't compute the return\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn ((String) values.get(0).getValue()).toCharArray();\n\t\t}\n\t\treturn null;\n\t}\n}\n```\nDon't forget to add your execution to the tree in `me.nov.threadtear.execution.ExecutionLink`!\n## Tips \u0026 Tricks\nThere are some tricks that can help you identify and deobfuscate jar files successfully. Before running executions, decompile the code to find out what needs to be used. \nYou can use the implemented decompiler for that.\n### Deobfuscation order\nThe best order for a deobfuscation is `generic executions \u003e access deobfuscation \u003e string deobfuscation \u003e cleaning executions`.\n### Identification\nObfuscators exhibit patterns which you can use to identify obfuscators. The easiest way to identify an obfuscator is to skim the `META-INF/MANIFEST.MF` file. \nIt's possible that there is an `Obfuscated-By: XXX` or `Protected-By: XXX` attribute.\n\n##### ZKM\nExtremely (flow-) obfuscated code, often noticeable by a string decryption method in the static initializer containing switches,\n or string decryption methods with a very long switch block (about 250 cases).\nZKM is one of the best (and oldest) obfuscators for java, and very expensive. As ancient as the obfuscator is their website.\n![ZKM](https://i.imgur.com/Psdagyb.png)\n##### Stringer\nIf your jar file contains some special classes with huge decryption algorithms that are used by string obfuscation and access obfuscation, it's probably Stringer.\nThe protection is not bad and Stringer is one of the most expensive obfuscators. Unlike normal obfuscators it does not come with name obfuscation. \nIt is rather used as \"second layer\". Probably 90% of people that use this obfuscator are using a crack, as it costs more than a car.\nIf your file has been obfuscated with multiple obfuscators, and Stringer is one of them, you should begin your deobfuscation with Stringer, as Stringer obfuscation cannot be overwritten. \n(Due to custom JAR signature and usage of method names during string decryption)\n![Stringer](https://i.imgur.com/LmI9SYz.png)\n![Stringer 2](https://i.imgur.com/M72plII.png)\n##### Allatori\nClass names like IiIlIlIiIl or aUx, cOn, PrX indicate Allatori obfuscation.\nAllatori is very common amongst obfuscated jar files, because it offers a free demo that accessible within a few clicks. The obfuscation is not that hard to reverse.\n![Allatori](https://i.imgur.com/eWYKtR4.png)\n##### Paramorphism\nParamorphism is like the little brother of stringer, as it looks similar, but isn't as good as it. It also has some interesting features that aim to crash reverse engineering tools, which can be removed easily. \nThe obfuscation strength is comparable to Allatori.\n![Paramorphism](https://i.imgur.com/netlEgl.png)\n\n##### Other obfuscators\nFor other obfuscators you can try generic executions or open an issue, and I'll see what I can do.\n\n### Description and tags\nBefore selecting an execution, check out the tool-tip texts while hovering. \nThey contain a small description about what they do, but also tags that help you understand how the behavior of your JAR file will be changed.\n\n## License\nThreadtear is licensed under the GNU General Public License 3.0\n\n## Donate\nThis tool was a ton of work. \nIf I saved your time, and you want to buy me a coffee you can do so here: [![Donate with Bitcoin](https://en.cryptobadges.io/badge/micro/37f6MxNoyyksgh3hWtbh9UKkkGDSAoHCtT)](https://en.cryptobadges.io/donate/37f6MxNoyyksgh3hWtbh9UKkkGDSAoHCtT)\n\n## Notice\nUse Threadtear for legal purposes only. Threadtear is not aiming to be a cracking tool, but rather to be a malware analysis toolkit. \nPlease open an issue or email me if a transformer doesn't work properly and attach the log.   \nNote that output files are most likely not runnable. If you still want to try to run them use `-noverify` as JVM argument!   \nThis tool intends to be used with Java 8, but it will probably run on higher versions too. \n","funding_links":[],"categories":["Java","Java (504)"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGraxCode%2Fthreadtear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGraxCode%2Fthreadtear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGraxCode%2Fthreadtear/lists"}