Jode java反编译 初识庐山真面目

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()读取字节操作。

本人才疏学浅,学艺不精。对于编译原理一窍不通。字节码操作也没有兴趣去研究了。期待看到大牛们的成果。

 

 

posted @ 2012-12-31 00:30  ~Albert  阅读(993)  评论(0编辑  收藏  举报