java的反编译工具比较常用的有两个,jode和jad.
一直都是用Jode插件。jad要先安装.exe文件,觉得挺麻烦。jode的反编译效果也还不错,而且是纯java的,心血来潮想看看反编译是个什么弄法。
网上中文资料几乎没有,英文的看不懂,就自己搞搞吧。
1.下载jode源码
jode源码是用SVN管理的。先下载eclispe的svn插件。
SVN开发的一个标准目录结构:
比如项目是proj,svn地址为svn://proj/,那么标准的svn布局是
svn://proj/ | +-trunk +-branches +-tags
这 是一个标准的布局,trunk为主开发目录,branches为分支开发目录,tags为tag存档目录(不允许修改)。
check出jode的主开发目录:https://jode.svn.sourceforge.net/svnroot/jode/trunk/
2.编译Jode
Jode是用ant编译打包。先要安装ant.
在jode目录下找到build.xml.里面内容很多。去掉一些不需要的。可根据个人需要选择。以下是我改过之后的,只留下build,realease的target.
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE project PUBLIC "-//ANT//DTD project//EN" "project.dtd">
<project name="jode" default="release" basedir=".">
<!-- set global properties for this build -->
<property name="version" value="1.90-CVS"/>
<property name="build" value="${basedir}/build"/>
<property name="props" value="${basedir}/props"/>
<property name="doc" value="${basedir}/doc"/>
<property name="lib" value="${basedir}/lib"/>
<property name="src" value="${basedir}/src"/>
<property name="release" value="${basedir}/release"/>
<property name="distdir" value="${release}/jode-${version}"/>
<property name="scripts" value="${basedir}/scripts"/>
<property name="api.doc" value="${doc}/api"/>
<property name="test" value="${basedir}/test"/>
<property name="test.src" value="${test}/src"/>
<property name="test.build" value="${test}/build"/>
<property name="test.log" value="${test}/log"/>
<property name="jcpp" value="${scripts}/jcpp.pl"/>
<property name="versionfile" value="${src}/jode/GlobalOptions.java"/>
<property file="config.props"/>
<path id="project.classpath">
<pathelement path="${classpath}"/>
<fileset dir="lib" includes="*.jar"/>
</path>
<!-- ********* General targets ******* -->
<!-- compiles jode and creates its javadoc-files -->
<target name="all" depends="build,release"/>
<!-- clean all -->
<target name="clean" depends="clean-build,clean-release"/>
<target name="build" >
<mkdir dir="${build}"/>
<javac srcdir="${src}"
destdir="${build}"
debug="true"
classpathref="project.classpath"
deprecation="on">
<exclude name="net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalOptimizer.java"/>
<exclude name="net/sf/jode/obfuscator/modules/LocalizeFieldTransformer.java"/>
<!--
<exclude name="net/sf/jode/bytecode/*Subroutine*" />
-->
</javac>
</target>
<!-- clean the class files -->
<target name="clean-build">
<delete dir="${build}"/>
</target>
<target name="clean-release">
<delete dir="${release}"/>
</target>
<!-- ********* Create Release files ******* -->
<target name="release" depends="release-src,release-bindist"/>
<target name="release-bindist" depends="build">
<jar jarfile="${distdir}/jode.jar" compress="true" manifest="${basedir}/MANIFEST.MF">
<fileset dir="${build}" includes="**/*.class"/>
<fileset dir="${props}" includes="**/*.properties"/>
</jar>
<copy todir="${distdir}">
<fileset dir="${lib}">
<include name="*getopt*.jar" />
<include name="*collection*.jar" unless="jdk1.2+" />
</fileset>
<fileset dir="${basedir}"
includes="AUTHORS,COPYING,NEWS,README,THANKS,TODO">
</fileset>
</copy>
</target>
<target name="release-bin">
<antcall target="clean"/>
<mkdir dir="${release}"/>
<mkdir dir="${distdir}"/>
<antcall target="release-bindist"/>
<jar jarfile="${release}/jode-${version}.jar"
basedir="${release}" includes="jode-${version}/**"/>
<antcall target="clean"/>
</target>
<target name="release-src" >
<antcall target="clean"/>
<mkdir dir="${release}"/>
<mkdir dir="${distdir}"/>
<copy todir="${distdir}">
<fileset dir="${basedir}"
includes="AUTHORS,COPYING,INSTALL,NEWS,README,THANKS,TODO,ChangeLog">
<include name="build.xml,config.props,project*.dtd"/>
<include name="scripts/**"/>
<include name="src/**"/>
<include name="props/**"/>
<include name="lib/**"/>
<exclude name="src/net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java"/>
<exclude name="src/net/sf/jode/obfuscator/modules/LocalOptimizer.java"/>
<exclude name="src/net/sf/jode/obfuscator/modules/LocalizeFieldTransformer.java"/>
</fileset>
</copy>
<jar jarfile="${release}/jode-${version}-src.jar"
basedir="${release}" includes="jode-${version}/**"/>
</target>
<target name="setversion" if="version">
<echo message="updating version in ${versionfile} ..."/>
<exec executable="perl">
<arg value="-i"/>
<arg value="-pe"/>
<arg value='s/(String\s*version\s*=\s*")[^"]*/$1${version}/' />
<arg value="${versionfile}"/>
</exec>
</target>
<target name="commit" depends="setversion" if="version">
<antcall target="cvsclean"/>
<echo message="---------------------------------------------------"/>
<echo message=' Commiting new Jode version: ${version} !!!'/>
<echo message="==================================================="/>
<echo message="...done!"/>
<echo message="---------------------------------------------------"/>
</target>
</project>
在命令行窗口,切到build.xml目录下,输入ant开始编译。编译成功后在jode目录下会多出一个build和release目录。realease下会有一个jar包和一个同名文件夹,打开那个文件夹. 如下结构。

jode.jar可直接双击运行。
src下是java源代码,可以copy到java工程下,lib下是此工程依赖的Jar包;props下是必须的资源文件,需要copy到java工程下对应的包下,否则运行源代码时会报错。 此时就可以在源代码中设置断点,进行调试。发现这个代码似乎很久没更新了,而且反编译对泛型也不会有很好的支持。
3.编译class
我们利用这个jar写段编译class的代码。在classpath中加入上图目录中的Jode.jar;
先自己写一个用于反编译的类,加上泛型。
public class TestJodeClass<T> {
private ArrayList<String> array = null;
private Vector<Object> vector = null;
private ArrayList<String> getArray(){
array = new ArrayList<String>();
return this.array;
}
private T getT(T t){
return t;
}
}
编译类:随意写一写
public static void main(String[] args) throws ClassFormatException, IOException{
/*
* 得到所在工程依赖jar包和bin目录所以.class文件目录
*/
String cp = System.getProperty("java.class.path");
/*
* 得到所有jre下的jar路径
*/
String bootcp = System.getProperty("sun.boot.class.path");
if (bootcp != null)
cp = bootcp + altPathSeparatorChar + cp;
cp = cp.replace(File.pathSeparatorChar, altPathSeparatorChar);
//存入以上路径下所有.class
ClassPath classPath = new ClassPath(cp);
ClassInfo clazz = classPath.getClassInfo("jode1.TestJodeClass");//得到要编译的class的ClassInfo
ImportHandler imports = new ImportHandler(classPath,
ImportHandler.DEFAULT_PACKAGE_LIMIT,
ImportHandler.DEFAULT_CLASS_LIMIT);
ByteArrayOutputStream out = new ByteArrayOutputStream(10240);
TabbedPrintWriter tabbedWriter = new TabbedPrintWriter(out, imports, false);
ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports);
clazzAna.dumpJavaFile(tabbedWriter);
System.out.println(out.toString());
out.close();
}
编译后的结果如下:
/* TestJodeClass - Decompiled by JODE
* Visit http://jode.sourceforge.net/
*/
package jode1;
import java.util.ArrayList;
import java.util.Vector;
public class TestJodeClass
{
private ArrayList array = null;
private Vector vector = null;
private ArrayList getArray() {
array = new ArrayList();
return array;
}
private Object getT(Object t) {
return t;
}
}
确实不支持泛型的。可能这个代码真的很久没有维护了。
粗略的翻了翻源码。大概的了解是:把尽可能涉及到的class路径加入classpath,从classpth中得到被编译的classInfo类,然后交给ClassAnalyzer去分析。
使用DataInputStream读取class文件。readUnsignedShort()读取字节操作。
本人才疏学浅,学艺不精。对于编译原理一窍不通。字节码操作也没有兴趣去研究了。期待看到大牛们的成果。
本文详细介绍如何使用Java反编译工具Jode的编译过程,包括下载源码、配置SVN和Ant、编译源码及创建JAR包。同时,文章还提供了使用Jode反编译泛型类的示例代码。
3430

被折叠的 条评论
为什么被折叠?



