Posted Thursday, 04 March 2010 at 02:03 by Andrew Liu
Tagged: java | web development | xml soap
Read more blogs...
I came across the need to decompile a java package from an existing application, having to recode a small portion of it. This all sounded ok, but I wasn't sure about the recompiling of the package and putting the new package back into the application. It sounded normal, but it was quite a large application and, moreover, it was windows based! So I googled around to see if people had done this before - it sounds doable, but there were no real "this is how you do it" instructions.
So here is a brief run through. ** WARNING ** this is by no means a "this is how you do it" set of instructions, as every scenario is different. You will have to improvise as best you can, and use logic to substitute your values in where possible. I will use an example below instead of a "one instruction fits all" approach, and highlight potential errors on the way.
Firstly, I had to pull apart the JAR file. JAR files are simply ZIP files with a bit more information about the contents (contained in the META-INF). Most linux systems with Java will have a command line available called 'jar', which is similar to the 'tar' command. In my particular scenario, I was extracting the contents of a file called 'sas.portal.jar'.
$ mkdir sas.portal $ cd sas.portal $ jar xvf ../sas.portal.jar $ ls -al total 32 drwxr-xr-x 4 root root 4096 2009-03-24 11:17 . drwx------ 34 aliu aliu 4096 2009-03-24 11:17 .. -rw-r--r-- 1 root root 94 2007-07-19 10:58 buildinfo.txt drwxr-xr-x 3 root root 4096 2007-07-19 10:57 com -rw-r--r-- 1 root root 438 2007-07-19 10:58 disposers.config drwxr-xr-x 3 root root 4096 2009-03-24 11:21 META-INF -rw-r--r-- 1 root root 5161 2007-07-19 10:58 portlet.dtd
If we dig into the 'com' directory, we will find the class files within.
$ ls -al com total 12 drwxr-xr-x 3 root root 4096 2007-07-19 10:57 . drwxr-xr-x 4 root root 4096 2009-03-24 11:17 .. drwxr-xr-x 4 root root 4096 2007-07-19 10:57 sas $ ls -al com/sas total 16 drwxr-xr-x 4 root root 4096 2007-07-19 10:57 . drwxr-xr-x 3 root root 4096 2007-07-19 10:57 .. drwxr-xr-x 3 root root 4096 2007-07-19 10:57 apps drwxr-xr-x 20 root root 4096 2007-07-19 10:58 portal $ ls -al com/sas/portal total 152 drwxr-xr-x 20 root root 4096 2007-07-19 10:58 . drwxr-xr-x 4 root root 4096 2007-07-19 10:57 .. drwxr-xr-x 3 root root 12288 2007-07-19 10:57 actions drwxr-xr-x 3 root root 4096 2007-07-19 10:57 app drwxr-xr-x 6 root root 4096 2007-07-19 10:57 container drwxr-xr-x 13 root root 4096 2007-07-19 10:57 delegates drwxr-xr-x 2 root root 4096 2007-07-19 10:57 dynamicResources drwxr-xr-x 2 root root 4096 2007-07-19 10:57 filters drwxr-xr-x 2 root root 4096 2007-07-19 10:57 httpfilters drwxr-xr-x 3 root root 4096 2007-07-19 10:57 lifecycle -rw-r--r-- 1 root root 12695 2007-07-19 10:57 Logger.class drwxr-xr-x 2 root root 4096 2007-07-19 10:57 metadatadeploy drwxr-xr-x 2 root root 4096 2007-07-19 10:57 mgmt drwxr-xr-x 3 root root 4096 2007-07-19 10:57 model -rw-r--r-- 1 root root 886 2007-07-19 10:57 ObjectNotFoundException.class -rw-r--r-- 1 root root 6629 2007-07-19 10:57 PortalCommonStrings.class -rw-r--r-- 1 root root 5758 2007-07-19 10:57 PortalContext.class -rw-r--r-- 1 root root 1072 2007-07-19 10:57 PortalContextInterface.class -rw-r--r-- 1 root root 862 2007-07-19 10:57 PortalException.class -rw-r--r-- 1 root root 10947 2007-07-19 10:57 PortalFactory.class -rw-r--r-- 1 root root 294 2007-07-19 10:57 PortalGlobals.class -rw-r--r-- 1 root root 2987 2007-07-19 10:57 PortalResources.class drwxr-xr-x 3 root root 4096 2007-07-19 10:57 portlet drwxr-xr-x 4 root root 4096 2007-07-19 10:57 portlets drwxr-xr-x 2 root root 4096 2007-07-19 10:57 res drwxr-xr-x 2 root root 4096 2007-07-19 10:57 security drwxr-xr-x 2 root root 4096 2007-07-19 10:57 state drwxr-xr-x 5 root root 4096 2007-07-19 10:57 taglib drwxr-xr-x 5 root root 4096 2007-07-19 10:57 util
Once you find the class / classes that you need to change, you will need to decompile them.
I used a couple, and all worked pretty well. They are windows tools, so I ended up decompiling in Windows, and building in Linux. There are probably decompilers for Linux too, but I'll describe how I did it in Windows.
The two that I used were Cavaj Java Decompiler, and DJ Java Decompiler. Both worked fine - in fact I think both use JAD as the decompiler behind the scenes (explaining why both sources ended up being the same). DJ Java Decompiler is now shareware, not freeware, but I dug up an old version 3.7 that was freeware, and that worked a treat.
Some files, when decompiled, are not correct. I have noticed some that complain there are missing blocks. Below is an example of one such file I found.
try
{
while((bytesRead = inputStream.read(buffer)) != -1)
outStream.write(buffer, 0, bytesRead);
outStream.flush();
}
catch(SocketException e) { }
break MISSING_BLOCK_LABEL_173;
SocketException e;
e;
break MISSING_BLOCK_LABEL_173;
local;
SocketException e;
try
{
if(inputStream != null)
inputStream.close();
}
// Misplaced declaration of an exception variable
catch(SocketException e) { }
if(outStream != null)
outStream.close();
goto _L1
e;
_L1:
JVM INSTR ret 11;
I would not play around with these files. If you needed to change these files, I'd abort this approach and try something else. You only want to be playing with JAVA files that make sense.
The problem now is to try to rebuild the decompiled java file. Under Linux, I decided to create an Ant project, with minimal files in it. I ended up getting it to work with the following directory structure.
sas.portal/
build.xml [see below]
classes/ [ this is where the extracted jar files go ]
com/
sas/
portal/
...
src/
com/
sas/
portal/
ui/
taglib/
PortalOptions.java [ this is the file that I want to edit. The contents of this was from using a decompiler ]
lib/ [this is where all the associated .jar files go. The package depends on these files. ]
Then I cobbled together a very basic build.xml file for myself, so that I could use Ant to build the java file. The important think was that I needed all the class files in the class path, because in order to build the changed java file, you need to do so against the original class files.
An example build.xml file I created (and one that you can start off with) is:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project basedir="." default="jar">
3 <!--Auto generated ant build file-->
4 <property environment="env"/>
5 <property name="project.base.dir" value="."/>
6 <property name="maven.class.path" value=""/>
7 <property name="name" value="blah"/>
8 <property name="user.email" value="andrew.liu@webtop.com.au"/>
9 <property name="src" value="${project.base.dir}/src"/>
10 <property name="test" value="${project.base.dir}/test"/>
11 <property name="reference" value="${project.base.dir}/lib"/>
12 <property name="build" value="${project.base.dir}/build"/>
13 <property name="classes" value="${build}/classes"/>
14 <property name="lib" value="${build}/lib"/>
15 <path id="reference.class.path">
16 <pathelement path="${java.class.path}" />
17 <fileset dir="${reference}">
18 <include name="*.jar" />
19 </fileset>
20 </path>
21 <target name="init">
22 <mkdir dir="${build}"/>
23 <mkdir dir="${classes}"/>
24 <mkdir dir="${lib}"/>
25 </target>
26 <target depends="init" name="pre.compile.test">
27 </target>
28 <target depends="pre.compile.test" name="compile.src">
29 <javac debug="on" memoryMaximumSize="256m" memoryInitialSize="256m" fork="true" destdir="${classes}" srcdir="${src}">
30 <classpath refid="reference.class.path" />
31 </javac>
32 </target>
33 <target depends="compile.src" name="compile.test">
34 <javac debug="on" memoryMaximumSize="256m" memoryInitialSize="256m" fork="true" destdir="${classes}">
35 <src path="${test}"/>
36 <classpath refid="reference.class.path" />
37 </javac>
38 </target>
39 <target depends="compile.src" name="jar">
40 <jar destfile="${lib}/${name}.jar">
41 <fileset excludes="**/Test.class" dir="${classes}"/>
42 </jar>
43 </target>
44 <target name="clean">
45 <delete dir="${build}"/>
46 </target>
47 </project>
No, I don't use maven here, so you can probably remove or just ignore it (I left it here because I intend on migrating to maven at some stage).
After you edit your java files in the src directory, compile it using Ant. You should have Ant and Java in your path already.
$ ant
Buildfile: build.xml
init:
[mkdir] Created dir: /home/aliu/sas.portal/build
[mkdir] Created dir: /home/aliu/sas.portal/build/classes
[mkdir] Created dir: /home/aliu/sas.portal/build/lib
pre.compile.test:
compile.src:
[javac] Compiling 1 source file to /home/aliu/sas.portal/build/classes
jar:
[jar] Building jar: /home/aliu/sas.portal/build/lib/blah.jar
BUILD SUCCESSFUL
Total time: 1 second
$ ls -al build
total 16
drwxr-xr-x 4 root root 4096 2009-03-24 18:32 .
drwxrwxr-x 9 aliu aliu 4096 2009-03-24 18:32 ..
drwxr-xr-x 3 root root 4096 2009-03-24 18:32 classes
drwxr-xr-x 2 root root 4096 2009-03-24 18:32 lib
$ ls -al build/lib
total 12
drwxr-xr-x 2 root root 4096 2009-03-24 18:32 .
drwxr-xr-x 4 root root 4096 2009-03-24 18:32 ..
-rw-r--r-- 1 root root 3451 2009-03-24 18:32 blah.jar
$ ls -al build/classes/com/sas/portal/taglib/ui/PortalOptionsTag.class
-rw-r--r-- 1 root root 5704 2009-03-24 18:32 build/classes/com/sas/portal/taglib/ui/PortalOptionsTag.class
Here, we can see that there is a 'blah.jar' file, and the result CLASS file created. We don't need the JAR file (hence the name of it) - what we are really after is the CLASS file.
Now that we have the CLASS files of the altered bits of code, we need to reinsert them back into the original JAR file. We can do this using the 'jar' command. Make sure you have backups of the original JAR file, as this will change the target JAR file! Note that you will need to this from the correct location - from the same directory where the build.xml file is located. You can specify the location of the JAR file relative from where you are, but the second argument (the CLASS file you are replacing) ** MUST ** be the same path as that within the original JAR file.
$ pwd /home/aliu/sas.portal $ jar uvf ../sas.portal.jar com/sas/portal/taglib/ui/PortalOptionsTag.class adding: com/sas/portal/taglib/ui/PortalOptionsTag.class(in = 5704) (out= 2422)(deflated 57%)
Now you have a new updated JAR file with your new CLASS file modification!
Note, this does not address updating the META-INF/MANIFEST.MF file. To do this is more complicated, and has not been discussed in here.
Posted Tuesday, 19 October 2010 at 05:58 by Andrew Liu
Posted Sunday, 18 April 2010
Updated Sunday, 24 February 2013 at 06:39 by Andrew Liu
Posted Friday, 05 March 2010 at 23:13 by Andrew Liu
Posted Thursday, 04 March 2010 at 04:34 by Andrew Liu
Posted Wednesday, 03 March 2010 at 20:15 by Andrew Liu