
Threadtear は Java 向けの多機能難読化解除ツールです。Android アプリケーションのサポートは近日中に追加予定です(現在 Dalvik から Java へのコンバーターを開発中)。難読化をあまり気にせず、より簡単にコード解析を行うのに適しています。ZKM や Stringer といった最も高価な難読化ツールにも対応しています。デバッグを容易にするための他のツールも含まれています。例外の発生源をより良く理解するためにデバッグ行番号を挿入したり、コードを再コンパイルせずに try-catch ブロックに .printStackTrace() を追加したりできます。バージョン固有のメソッドが使用されていない限り、逆互換性はもはや問題になりません。グラフ内のコードフローを分析して、アルゴリズムをより良く理解できます。

「実行」とは、実行され、読み込まれたすべてのクラスファイルを変更するタスクです。実行には複数の種類があり、バイトコードのクリーンアップから文字列の難読化解除まで様々です。適切な順序に並べることが重要です。たとえば、クリーンアップ実行は最後に実行するべきですが、最初に実行することで他の実行を助けることもあります。準備ができたら「Run」ボタンをクリックすると、順番に実行されます。
このツールは自己責任で使用してください。一部の実行では、JAR ファイルからコードを実行するために実装された ClassLoader を使用します。攻撃者がバイトコードを改ざんして悪意のあるコードを実行させる可能性があります。影響を受ける実行では、me.nov.threadtear.asm.vm.VM クラスを使用します。これらは主に文字列やリソース/アクセスの難読化解除に使用され、リモートで復号メソッドを実行する方がはるかに簡単だからです。
Threadtear は独自の SecurityManager を使用して悪意のある呼び出し(任意のコード実行)から保護しようと試みますが、保証はありません。特に ZKM や Stringer のような難読化解除ツールでは、リフレクションを許可する必要があるため、細心の注意が必要です。ACE を発見した場合は、問題を報告してください。できるだけ早く修正します。
まず gradle build を実行し、次に gradle fatJar を実行します。builds/libs に実行可能な JAR ファイルが作成されます。リポジトリをダウンロードしたくない場合は、最新のリリースを使用できます。
独自の実行タスクを簡単に作成できます。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;
}
}
実行時に ClassNode をロードするには、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);
// rewrite method instructions
for (int i = 0; i < m.instructions.size(); i++) {
AbstractInsnNode 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 fix try catch blocks, local variables, etc...
Instructions.updateInstructions(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 not known, we can't compute the return
return null;
}
return ((String) values.get(0).getValue()).toCharArray();
}
return null;
}
}
me.nov.threadtear.execution.ExecutionLink のツリーに実行を追加するのを忘れないでください!
JAR ファイルを正常に識別し難読化解除するのに役立ついくつかのテクニックがあります。実行を実行する前に、コードを逆コンパイルして何を使用すべきかを調べてください。実装されたデコンパイラを使用できます。
難読化解除に最適な順序は「汎用実行 > アクセス難読化解除 > 文字列難読化解除 > クリーンアップ実行」です。
難読化ツールは特定のパターンを示し、それを使用して識別できます。最も簡単な識別方法は、META-INF/MANIFEST.MF ファイルをざっと見ることです。Obfuscated-By: XXX または Protected-By: XXX 属性が存在する可能性があります。
非常に(フローが)難読化されたコードで、静的初期化子内にスイッチを含む文字列復号メソッド、または非常に長いスイッチブロック(約 250 ケース)を持つ文字列復号メソッドがしばしば見られます。ZKM は Java 向けの最高(かつ最古)の難読化ツールの一つで、非常に高価です。難読化ツールと同じくらい古いウェブサイトを持っています。

JAR ファイルに、文字列難読化とアクセス難読化で使用される巨大な復号アルゴリズムを持つ特別なクラスが含まれている場合、それはおそらく Stringer です。保護は悪くなく、Stringer は最も高価な難読化ツールの一つです。通常の難読化ツールとは異なり、名前難読化は付属しません。むしろ「第2層」として使用されます。この難読化ツールを使用する人の約 90% はクラックを使用していると思われます。自動車よりも高価だからです。ファイルが複数の難読化ツールで難読化されており、その中に Stringer が含まれている場合、Stringer の難読化は上書きできないため、Stringer から難読化解除を開始する必要があります。(カスタム JAR 署名と文字列復号中のメソッド名の使用のため)

IiIlIlIiIl や aUx、cOn、PrX のようなクラス名は Allatori 難読化を示します。Allatori は難読化された JAR ファイルの間で非常に一般的です。なぜなら、数回のクリックでアクセスできる無料デモを提供しているからです。難読化の解除はそれほど難しくありません。

Paramorphism は Stringer の弟分のようなもので、見た目は似ていますが、それほど優れていません。また、リバースエンジニアリングツールをクラッシュさせることを目的とした興味深い機能もいくつかありますが、簡単に削除できます。難読化の強度は Allatori に匹敵します。

その他の難読化ツールについては、汎用実行を試すか、問題を報告してください。対応を検討します。
実行を選択する前に、ホバー時のツールチップテキストを確認してください。それらには、何を行うかの簡単な説明と、JAR ファイルの動作がどのように変更されるかを理解するのに役立つタグが含まれています。
Threadtear は GNU General Public License 3.0 の下でライセンスされています。
このツールは非常に多くの作業を要しました。もしあなたの時間を節約でき、私にコーヒーをご馳走したい場合は、こちらからどうぞ: Donate with Bitcoin
Threadtear は法的な目的でのみ使用してください。Threadtear はクラッキングツールではなく、マルウェア分析ツールキットを目指しています。トランスフォーマーが正常に動作しない場合は、問題を報告するか、ログを添付してメールを送信してください。出力ファイルはおそらく実行可能ではないことに注意してください。それでも実行を試みる場合は、JVM 引数に -noverify を指定してください! このツールは Java 8 での使用を想定していますが、それ以降のバージョンでも動作する可能性があります。