java的反编译工具有那么几款,使用最广泛的当属jd-gui,速度快,功能强大。网上介绍反编译工具的文章也不少,但几乎没有介绍如何与程序对接的。
jd-gui的设计相当精妙,核心就是jd-core,但核心几乎没有人提到,开源的jd-gui也没有找到core的踪影。
我是这么获的jd-core的,在官网下载jd-gui-1.4.0.jar,删除其他只保留jd文件夹,里面就是core了。
反编译jd-core之后发现api使用也相当简单:
jd.core.Decompiler----反编译接口
jd.core.process.DecompilerImpl--反编译接口实现
只有一个方法:
public void <strong>decompile</strong>(jd.core.preferences.Preferences preferences, jd.core.loader.Loader loader, jd.core.printer.Printer printer, java.lang.String internalClassPath) throws jd.core.loader.LoaderException;
Preferences 为配置,构造方法只有两个参数:
<h5><div style="padding-top: 2px; margin-left: 20px; position: relative; -ms-word-wrap: break-word;"><a target=_blank href="eclipse-open:%E2%98%82=boss/jd-core-1.4.0.jar%3Cjd.core.preferences(Preferences.class%E2%98%83Preferences~Preferences~Z~Z"></a><a target=_blank class="header" href="eclipse-javadoc:%E2%98%82=boss/jd-core-1.4.0.jar%3Cjd">jd</a>.<a target=_blank class="header" href="eclipse-javadoc:%E2%98%82=boss/jd-core-1.4.0.jar%3Cjd.core">core</a>.<a target=_blank class="header" href="eclipse-javadoc:%E2%98%82=boss/jd-core-1.4.0.jar%3Cjd.core.preferences">preferences</a>.<a target=_blank class="header" href="eclipse-javadoc:%E2%98%82=boss/jd-core-1.4.0.jar%3Cjd.core.preferences(Preferences.class%E2%98%83Preferences">Preferences</a>.Preferences(<span style="font-weight: normal;"></span>boolean showDefaultConstructor, <span style="font-weight: normal;"></span>boolean realignmentLineNumber) 参数含义不言自明</div></h5>
loader为加载文件(.class或.jar)的接口,自己实现也很简单
internalClassPath 参数是内部路径,这个会作为参数传给load接口
稍微复杂点的就是Printer了 (再次赞叹jd灵活的设计)
我这里实现一个简单的动态代理,仅为了减少一些方法实现而已,因为printer接口的方法比较多。
final StringBuilder javaFileBuilder = new StringBuilder();// 将反编译后源码输出到StringBuilder
// jd.core.printer.Printer接口代理的简单实现
InvocationHandler h = new java.lang.reflect.InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
System.out.println("Printer:: " + name + "(" + Arrays.toString(args) + ")");
if (name.equals("print")) {
javaFileBuilder.append(args[0]);
} else if (name.equals("endOfLine")) {
javaFileBuilder.append("\n");
} else if (name.equals("printKeyword")) {
javaFileBuilder.append(args[0]);
} else if (name.equals("printTypeImport")) {
javaFileBuilder.append(args[1]);
} else if (name.equals("printMethodDeclaration")) {
javaFileBuilder.append(args[1]);
} else if (name.equals("printStaticMethodDeclaration")) {
javaFileBuilder.append(args[1]);
} else if (name.equals("printType")) {
javaFileBuilder.append(args[0].toString().replace('/', '.'));
} else if (name.equals("printString")) {
javaFileBuilder.append(args[0]);
} else if (name.equals("printStaticMethod")) {
javaFileBuilder.append(args[1]);
} else if (name.equals("printMethod")) {
javaFileBuilder.append(args[1]);
} else if (name.equals("printField")) {
javaFileBuilder.append(args[1]);
} else if (name.equals("printConstructor")) {
javaFileBuilder.append(args[0].toString().replace('/', '.'));
} else if (name.equals("printNumeric")) {
javaFileBuilder.append(args[0]);
} else if (name.equals("printTypeDeclaration")) {
javaFileBuilder.append(args[1]);
}
return null;
}
};
Printer printer = (Printer) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Printer.class },
h);
loader接口的实现比较简单,就不贴了,读者自己解决吧,下面是最终的反编译的调用:
DecompilerImpl de = new DecompilerImpl();
de.decompile(preferences, loader, printer, internalClassPath);