
مجموعة أدوات متعددة الوظائف لفك تشويش جافا
Threadtear هي أداة متعددة الوظائف لإزالة التعتيم عن كود جافا. قريبًا سيتم دعم تطبيقات أندرويد (أعمل حاليًا على محول من دالفيك إلى جافا). مناسبة لتحليل الكود بسهولة دون القلق كثيرًا بشأن التعتيم. تشمل حتى أغلى أدوات التعتيم مثل ZKM أو Stringer. لتسهيل التصحيح، توجد أدوات أخرى مضمنة. يمكن إدراج أرقام أسطر التصحيح لفهم أفضل لمصدر الاستثناءات، أو إضافة .printStackTrace() إلى الكتل try catch دون إعادة ترجمة الكود. لم يعد التوافق العكسي مشكلة، إذا لم تُستخدم طرق خاصة بالإصدار. حلل تدفق الكود في رسم بياني لفهم الخوارزميات بشكل أفضل.

التنفيذ هو مهمة يتم تشغيلها وتعديل جميع ملفات الكلاس المحملة. هناك أنواع متعددة من التنفيذات، تتراوح من تنظيف البايت كود إلى إزالة تعتيم السلاسل النصية. تأكد من ترتيبها بشكل صحيح. على سبيل المثال، يجب تنفيذ التنفيذات الخاصة بالتنظيف في النهاية، ولكنها يمكن أن تساعد أيضًا التنفيذات الأخرى إذا تم تنفيذها أولاً. عندما تكون مستعدًا، انقر على زر "Run"، وستُنفذ بالترتيب.
استخدم هذه الأداة على مسؤوليتك الخاصة. بعض التنفيذات تستخدم مُحمّلات كلاس (ClassLoaders) منفّذة لتشغيل كود من ملف الجار. قد يقوم مهاجم بتعديل البايت كود بحيث يتم تنفيذ كود خبيث. التنفيذات المتأثرة تستخدم الكلاس me.nov.threadtear.asm.vm.VM. تُستخدم هذه في الغالب لفك تشفير السلاسل النصية أو تعتيم الوصول/الموارد، لأنه من الأسهل بكثير تنفيذ طرق فك التشفير عن بُعد.
يبذل Threadtear قصارى جهده لحمايتك من المكالمات الخبيثة (تنفيذات الكود التعسفية) باستخدام SecurityManager الخاص به، ولكن لا يوجد ضمان. خاصة مع أدوات إزالة التعتيم مثل ZKM أو Stringer، عليك أن تكون حذرًا جدًا، حيث يجب السماح بالانعكاس (reflection)، وإلا فلن تعمل. إذا اكتشفت ثغرة ACE، يرجى فتح مشكلة (issue). سأحاول إصلاحها في أقرب وقت ممكن.
أولاً، قم بتشغيل gradle build، ثم gradle fatJar. في builds/libs سيتم إنشاء ملف جار قابل للتشغيل. إذا كنت لا تريد تنزيل المستودع، يمكنك استخدام أحدث إصدار.
يمكنك بسهولة إنشاء مهمة تنفيذ خاصة بك. فقط قم بتوسيع 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 -> {
//transform the classes here using the tree-API of ASM
});
return false;
}
}
لتحميل ClassNodes في وقت التشغيل، استخدم الكلاس me.nov.threadtear.asm.vm.VM وقم بتطبيق 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);
//transform bytecode to java.lang.Class
Class<?> loadedClass = vm.loadClass(c.name.replace('/', '.'), true);
//do stuff with your class here
loadedClass.getMethods()[0].invoke(...);
return true;
});
}
/**
* Will get invoked by VM, when VM.loadClass is called
*/
@Override
public ClassNode tryClassLoad(String name) {
//try to find the class to be loaded in open jar archive
return classes.containsKey(name) ? classes.get(name).node : null;
}
}
باستخدام ConstantTracker (me.nov.threadtear.analysis.stack.ConstantTracker) يمكنك تحليل الطرق وتتبع قيم المكدس غير المتغيرة. على سبيل المثال، إذا تم دفع iconst_0 إلى المكدس، فإن القيمة نفسها لا تُفقد كما في محلل ASM الأساسي، ويمكنك استخدامها لتوقع الأشياء لاحقًا في الكود.
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 keeps known stack values, e.g. can be useful for jump prediction
Analyzer<ConstantValue> a = new Analyzer<ConstantValue>(new ConstantTracker(this, Access.isStatic(m.access), m.maxLocals, m.desc, new Object[0]));
try {
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);