This is a complex tool for developing java applications and is itself written in java. One neat feature is that it can read in an existing classes and draw a nice hierarchy tree.
The demo version (called Whiteboard) has many restrictions, one is that it is not possible to print the hierarchy tree L
Jad 1.5.7g (www.geocities.com/SiliconValley/Bridge/8617/jad.html)
ZelixKlassMaster
2.3.2 (www.zelix.com/klassmaster)
JBuilder 3.5
UltraEdit 7.0a
At least 150MB RAM
In together/bin is a text file called license.tg which looks like this:
3;fc98f78d;
license.id = 001070720009
license.version.major = 4
license.lastUpdate = forever
license.expiration = forever
license.edition = whiteboard
If we change whiteboard to something else, for instance “enterprise”, together 4.1 says “license corrupted” and starts as whiteboard edition. So, this is the license file which controls if together starts as whiteboard or enterprise and if we can find out what it should look like, we have won. Together itself is written in Java, which is good news. The basic idea is to decompile “together”, look where and how it checks licence.tg and write a license file which qualifies as enterprise edition.
There are three ways how together can be started. With together.exe which somehow starts the java part, with togethercon.exe which also starts the java part, but which also opens a console window (DOS box) and with together.bat which starts the java virtual machine with:
SET JDK=d:\entwicklung\jdk13
SET
JDK_RT_JAR=d:\entwicklung\jdk13\jre\lib\rt.jar
SET JDK_TOOLS_JAR=d:\entwicklung\jdk13\lib\tools.jar
"%JDK%\bin\java" -Xms64m -Xmx512m -cp "%TGH%\lib\together.jar;%TGH%\lib\openapi.jar;%TGH%\modules;%TGH%\lib\gifs.zip;%TGH%\out;%TGH%\lib\i18n;%TGH%\lib\jgl.zip;%TGH%\lib\jacl.jar;%TGH%\lib\coroutine;%TGH%\lib\xml4j.jar;%TGH%\lib\tcljava.jar;%TGH%\lib\jcvs\application\jcvs.jar;%TGH%\lib\misclib.zip;%TGH%\lib\javax.jar;%JDK_TOOLS_JAR%;%TGH%\lib\jhall.jar;%TGH%\help\together-help.jar;%TG_CLASSPATH%;%JDK_RT_JAR%" com.togethersoft.together.Main %1 %2 %3 %4 %5 %6 %7
So, our main routine where it all starts is in the class com.togethersoft.together.Main and this has to be in one of the jar or zip files which were added to the classpath. We can open those archives with WinZIP and see that this and ca. 6000 other classes are in together.jar. Now we unpack the jar and decompile it with jad 1.5.7g using the command jad –r –f –ff –nonlb –sjava –dtogsrc togclass/**/*.class. Using Ultraedit to search for “corrupted” we find it in zc1v.java and zv.java but the code looks rather strange.
The class & method names tell us that an obfuscator has been used to make decompilation difficult. Therefore we use ZelixKlassMaster which itself is an obfuscator, but also a great unobfuscator ! We select together.jar as source and now Zelix tries to read in all classes which takes a lot of memory. But even if you have enough memory you have to tell Zelix explicitly to use it. That means we start Zelix with java –mx100m –jar ZKM.jar. After all classes are loaded (taking up 90Mb) Zelix informs us that many classes have an invalid inner class attribute set (which Zelix removes). This is one of the reasons why the initial jad result looked to strange.
After some experimenting it turns our that there are four
different routes how to continue.
1) We unobfuscate
together.jar (which means we get new class, field & method names) and hope
that we can completely recompile it (using JBuilder for instance). Then we
could use a debugger to step through the program and find the location where
the license file is checked.
2) We only remove
the invalid inner class attribute and decompile together.jar with the existing
names. Then we modify (inserting print statements) and recompile only selected
classes (maybe those with the word “corrupted” in it), and re-introduce the
modified class files into the original together.jar archive. If we now start
togethercon.exe or together.bat our modified class prints information which we
can use to construct a valid license file.
3) The obfuscated
names make it very difficult to understand the program flow and to find out
from where a specific method has been called (tracing the flow backwards).
Therefore a combination of approach 1 and 2 is possible. We unobfuscate the
target and modify selected classes (print statements). However, after these
classes have been compiled the unobfuscation process has to be exactly
reversed. That means our nice names have to be replaced by the original
obfuscated names. After that the class can be re-introduced in together.jar and
the rest is as in 2.
4) It is also
possible to unobfuscate & decompile together.jar and use this set of files
to understand the program flow, but use a second set of files with the original
names for making changes (introducing print statements) and recompilation.
After trying all three approaches it turned out that the last one is the most promising. 1 doesn´t work because in the >6000 classes there are ca. 70 where jad reports that it couldn´t decompile it properly and many more where it didn´t complain, but where JBuilder had problems re-compiling them. So we want to decompile/recompile as few classes as necessary. Approach 2 had the problem it was not good enough to remove the wrong inner class attribute, but there were other hidden problems with the names which caused problems during the recompilation. Those problems only disappeared after complete unobfuscation. And because there were many (!) methods with identical names (in different classes) it is not practical to find out from where a given method has been called. This is different after unobfuscation, because then all methods have different names. But idea 4 has two big advantages compared to approach three. After each recompilation it saves us the un-unobfuscation step which easily takes 10 min. for a large project as together. And secondly it turns out that ZelixKlassMaster removes all local variable information during the unobfuscation step which means that the modified class which we finally produced in approach 3, is pretty useless for debugging because we don’t have access to local variables. So here is the description of approach 4:
It turned out that for the actual unobfuscation process Zelix needs even more RAM. We start it with java –mx150m –jar ZKM.jar and read in together.jar. During the unobfuscation process Zelix needs to find the archives which would be accessed by the classes within together.jar. In advance it is difficult to know which archives that are exactly but if the right archive is not on the classpath Zelix complains that class xyz cannot be found. Then one has to look in all the archives which are used in together.bat and add the appropriate archive to zelix classpath. This can be done from Zelix´s classpath menu option. To spare you effort I tell you that before unobfuscation you should add the archives: jdk\lib\tools.jar, together\lib\openapi.jar, together\lib\misclib.zip, together\lib\jgl.zip, together\lib\tcljava.jar and together\lib\jhall.jar. Now we click on unobfuscate and set the following options: At the basic exclusion dialog box we tick everything which has to do with packages (because the packages within together.jar are not obfuscated and have nice meaningful names) and we also exclude com.togethersoft.together.Main. In the advanced section we add exclusion entries for class, field and methods (“containing package” and then com.*.). In the last dialog we tell zelix to write a change log file (very important) and to derive subclass names from superclass names. After the unobfuscation has finished we “save all” classes in a new directory of our choice.
As described above we use two sets of files. An unobfuscated and one with the original names. But as mentioned we cannot use the original classes as they are, because there are some (unknown) hidden features which cause Jbuilder to complain. So to produce a “clean” set of files with original names we exactly reverse the renaming which happened during the unobfuscation process. This seems to be a tricky problem, but fortunately Zelix helps us quite a bit. In the last unobfuscation dialog box there is an option called “use input change log file”. Here we can specify a file which explicitly tells Zelix how to change the names. But how do we write this file ? Here our “change log file” comes handy which was generated during the unobfuscation step. It is huge and looks like this:
Class: public za07 => Class9
Source:
[DashoPro-V1.2-120198]
FieldsOf:
za07
public a => aJPanel_Sub39_Sub4_1
MethodsOf: za07
public a() => method212
public a(boolean) => method213
public b() => method214
public c() => method215
public d() => method216
What we need is a program that (basically) changes the names on the right and left side of each =>. Then we have a new change file which tells Zelix to exactly reverse the name changes it did during unobfuscation. Such a program is not too difficult to write and a perl script which does the job is at the end of this essay.
Btw., do you see the “Source:” line ? It seems that normally the name of the source file is stored somewhere in the class file, but this information is not vital for execution by the virtual machine and can be changed without doing harm. If we look for DashoPro on the internet we see that this is actually a packaging system for java programs which also obfuscates the code. And probably DashoPro puts its name in the source field of a class file J
Anyway, after un-unobfuscation we save our clean class files with the original names in a new folder.
Now we decompile the unobfuscated and the cleaned classes. As described earlier we use jad with jad –r –f –ff –nonlb –sjava –dtogUnobSrc togUnobclass/**/*.class and jad –r –f –ff –nonlb –sjava –dtogCleanSrc togCleanclass/**/*.class. The unobfuscated classes can now be deleted since we only use this set of files for the program analysis (and not recompilation). Now we can use Ultraedit and look in the unobfuscated source files for “corrupted” and find it in Class2206_Sub3.java and in method8923 of Class2206_Sub4.java.
To test our idea we now want to introduce a print statement in method8923 of Class2206_Sub4. But don’t forget we want make this modification to the file with the original names ! The change log file which was produced by Zelix tells us that methot8923 of Class2206_Sub4 was originally method “a” of class zv. Now we have to set up a JBuilder project where we only compile this single java file. zv imports other classes which are originally located in together.jar and openapi.jar. We don´t want to recompile these classes as well, but want to use our cleaned classes and the existing classes in openapi.jar. We therefore make a copy of openapi.jar in our project folder and archive all cleaned classes (including zv) in one large zip file. In the JBuilder project we add zv.java to the project and under project properties/required libraries we add openapi.jar and the zip file with the cleaned classes. We see that in method8923 the contents of class209.anInt3 decides if the license if accepted or classified as corrupted. Therefore we introduce System.out.println("method8923: anInt3="+zab.c) (zab.c is the original name of class209.anInt3 as we are told by the change log file) at the beginning of method “a” of zv.java. Now we try to compile the java file, but there is a compilation error in a(zv,zab,Boolean) (Many methods are called “a” which only differ in the type of the arguments). If we look at the single line of this method:
public static void a(zv zv1, zab zab1, boolean flag) {
zv1.zv.a(zab1, flag);
}
we see that it does indeed look strange. Shouldn´t it be like this:
public static void a(zv zv1, zab zab1, boolean flag) {
zv1.a(zab1, flag);
}
Indeed, if we change it class zv.java compiles without problems !! We learn from this that unfortunately jad sometimes produces slightly wrong java code, even if it is not complaining L. But nothing is perfect and we have to live with it (or try a different decompiler).
Now we see why we have to make the changes in the set of files with the original names. We simply copy zv.class into together/lib und update together.jar (of course we keep a copy of the original) with jar –uf together.jar zv.class. We run togethercon.exe and indeed we see in the console: method9088: anint3 = 2. If we use the original license file (with whiteboard) we get method9088: anint3 = 0.
But this is not all. We can also make a copy of together.bat (in together/bin) and change the line where the java interpreter is called so that the debugger is called. It’s also necessary to copy zv.java into together/bin so that the debugger finds the source code.
"%JDK%\bin\jdb" -Xms64m -Xmx512m -classpath "%TGH%\lib\together.jar;%TGH%\lib\openapi.jar;%TGH%\modules;%TGH%\lib\gifs.zip;%TGH%\out;%TGH%\lib\i18n;%TGH%\lib\jgl.zip;%TGH%\lib\jacl.jar;%TGH%\lib\coroutine;%TGH%\lib\xml4j.jar;%TGH%\lib\tcljava.jar;%TGH%\lib\jcvs\application\jcvs.jar;%TGH%\lib\misclib.zip;%TGH%\lib\javax.jar;%JDK_TOOLS_JAR%;%TGH%\lib\jhall.jar;%TGH%\help\together-help.jar;%TG_CLASSPATH%;%JDK_RT_JAR%" com.togethersoft.together.Main %1 %2 %3 %4 %5 %6 %7
Notice that jdb doesn’t understand –cp, but wants –classpath. Now we set a breakpoint stop in zv.a(zab,Boolean) and start with run. After together hits the breakpoint we can step through the routine and inspect all variables at leisure J
Now all technical problems are solved and we can actually start the real work. The task is to find out when Class209.anInt3 gets filled with 2, when it actually should be 0. Therefore we have to work our way back through the program flow. That means we want to know where method8923 is called. We do this by just searching for “method8923” with UltraEdit in all unobfuscated java files. It’s really that easy, because the unobfuscation process created unique method names. There is no other class which has a method8923.
There are only two calls and both are in Class2206_Sub4. One is in method8914, the other in method8907. It can’t be method8914 because anInt3 is set to 1 before the call and we know it is 2 when it arrives at method8923. Method8907 is a one liner
public static void method8907(Class2206_Sub4 class2206_sub4, Class209 class209, boolean flag) {
class2206_sub4.Class2206_Sub4.method8923(class209, flag);
}
and so we have to go one step further back. We find that method8907 is called from method53 of Class298
public Class2206 method53(Class209 class209, Class2206 class2206) {
Class2206_Sub4.method8907(aClass2206_Sub4_1, class209, false);
return class209.anInt3 != 0 ? aClass2206_2 : class2206;
}
That means another step back. We see that method53 is called at several locations, but all lie within method8922 of Class2206_Sub4, from where we started initially. At the beginning of this method anInt3 = 0, at the end anInt3 = 2. So somewhere here the license check is done. To find out where in method8922 the anInt3 is set to 2 we step through the routine with the debugger. We find out that the call to method2456 is where class209.anInt3 is modified.
Class2206 class2206 = aClass201Array17[class209.anInt1].method2456(class209);
In method2456 is all the important stuff:
public Class2206 method2456(Class209 class209) {
int i = class209.aString2.indexOf(";");
if(i <= 0) {
class209.anInt3 = 2;
return null;
}
long l = 0L;
java.lang.String s;
try {
l = java.lang.Long.parseLong(class209.aString2.substring(0, i), 16);
s = class209.aString2.substring(i + 1);
}
catch(java.lang.NumberFormatException numberformatexception) {
com.togethersoft.component.diagnostic.Diagnostic.trace("license", numberformatexception);
class209.anInt3 = 2;
return null;
}
s = method2457(s);
long l1 = Class288.method2793();
for(int j = 0; j < s.length();
j++)
l1 = Class288.method2794((byte)s.charAt(j), l1);
if(l != l1) class209.anInt3 = 2;
Class2206_Sub1 class2206_sub1 = new Class2206_Sub1();
class2206_sub1.method8885(s);
return class2206_sub1;
}
Again we use the debugger (after recompiling the corresponding clean file zz.java, and introducing it into together.jar) to find our what’s going on. We see that in class209.aString2 there is the complete license file license.tg. That looks promising ! The hexstring in the first line (FC98F78D) is converted to decimal and stored in L. Then a checksum is calculated from the rest of the license file (stored in L1) and if this checksum is not equal to the one which is in the first line of the license file then anInt3 = 2, which means that the license is corrupted.
With the debugger we see that L1 = 1279346110 = &H4C4145BE. So, in the license file license.tg we only have to replace the old checksum FC98F78D with 4C4145BE. That’s it. If we now start TogetherJ it starts as enterprise edition without complaining J
Written By MrSmith
December 2000
Finito