|
The Java Specialists' Newsletter
Issue 032 2001-10-11
Category:
Language
Java version: Exceptional Constructors - Resurrecting the deadby Dr. Heinz M. Kabutz
Welcome to the 32nd issue of "The Java(tm) Specialists'
Newsletter", the only Java newsletter for those of us who are
considered Java Specialists. This week I want to look at what
happens when an object is not properly initialised, the results
are quite interesting, I think. The ideas in this newsletter
were spawned by Bruce Eckel, who sent me a piece of code that was
throwing exceptions from the constructor and that made us both
scratch our heads and rethink "what happens".
I must apologize for sending you two copies of the
last two newsletters; a few of you pointed it out in a very nice
manner (well-mannered bunch we are, hey?). When I sent out
Issue030, I was toying with the idea of prefixing the subject
line, so that you can easily write a junk mail filter for these
newsletters. I started by having a prefix of [TJSN] but after I
pressed the "Send" button, decided it would be better to use a
prefix of [JavaSpecialists], so I pressed the IE stop button. I
then changed the prefix and pressed send again, voila! two
copies to everyone :-(. Issue031 was even worse, as my mailing
list provider had changed the user interface, so as a result, the
newsletter could not be read using my Outlook 2000! I changed
what I thought was the problem and resent the newsletter together
with an apology, only to discover that it still did not work. I
do hope these problems are sorted out now.
The latest "skindernuus" (Afrikaans for gossip) is that Microsoft
have now produced J# (J-sharp) which is a language using the Java
syntax that can be compiled onto the .NET platform. This should
make it really easy to port your Java programs onto a Microsoft
system. Have a look at the
DevX site.
Is that bad news for me? Nope, it is fantastic news for me. Why?
Because I have just finished writing a Design Patterns course on
how to write Object-Oriented programs. I can now get customers
who do Java, C++, C#, VB.NET and J# to come on my Design
Patterns course. It's a classroom-based course and I will come
and present it at your company anywhere in the world, if you can
get together 10 programmers who wish to learn about Design
Patterns from someone who's been "in the field" for a long time.
Pop me an email if that interests
you.
Exceptional Constructors
What happens when an Exception is thrown from within the
constructor? Is the object created, or is it not created?
The answer is that the object is indeed constructed, but the
handle to the object is not returned, so no-one will have a
handle to it, so it can be garbage collected almost immediately.
Naturally, when I hear something like "no-one", the wheels in my
head start turning a bit faster.
What happens when you subclass a class that throws an exception
out of its constructor? In the subclass, the first call has to
be to the superclass, so if that throws an exception then the
subclass cannot catch it.
Let's look at an example. We have a LicenseManager class that
does some clever checking in its constructor and then throws an
exception if the license is not valid.
public class LicenseManager {
public LicenseManager() {
if (!cleverLicenseValidation()) {
throw new SecurityException("License invalid");
}
}
private boolean cleverLicenseValidation() {
// here you would typically read the license
// file, do some obfuscated calculations, etc.
// and return true if the license is valid.
return false;
}
}
This class is used together with the SecuritySystem class to
which we pass an instance of LicenseManager:
public class SecuritySystem {
private static LicenseManager licenseManager;
public static void register(LicenseManager lm) {
// only register if it is not initialized
if (licenseManager == null) {
if (lm == null) {
System.out.println("License Manager invalid!");
System.exit(1);
}
licenseManager = lm;
}
}
}
The SecuritySystem would be initialized at some point with the
LicenseManager and used in the Application
public class Application {
public static void main(String[] args) {
LicenseManager lm;
try {
lm = new LicenseManager();
} catch(SecurityException ex) {
lm = null;
}
SecuritySystem.register(lm);
System.out.println("Now let's get things started");
}
}
Yes, the code is rather convoluted, but I want to illustrate my
point to you ;-). Let us assume, for the sake of this newsletter,
that you cannot change any of the classes as they have been
heavily obfuscated. Let's assume we want to attack this system
by subclassing LicenseManager and having our own Application code
load the Application. The problem we face is that if we try to
subclass this LicenseManager with our own class, we cannot catch
the exception from the parent. Let's have a look at the
MyApplication class first:
public class MyApplication {
public static void main(String[] args) {
MyLicenseManager lm;
try {
lm = new MyLicenseManager();
} catch(SecurityException ex) {
lm = null;
}
SecuritySystem.register(lm);
// now we call the other application
Application.main(args);
}
}
Our first attempt at writing our own LicenseManager results in
the following:
public class MyLicenseManager extends LicenseManager {
public MyLicenseManager() {
System.out.println("Created MyLicenseManager");
}
}
When we run this, the result is:
License Manager invalid!
How do we get a handle to the MyLicenseManager class if it the
parent constructor throws an exception?
An interesting use of finalize()
We could abuse finalize() to catch the handle to
the MyLicenseManager class and then resurrect
the object. (hihi). Now James Gosling says that you should never
resurrect objects, but that is only because once resurrected,
finalize will not be called again, which is actually what we want
anyway. We therefore write a CleverLicenseManager that captures
an instance in the finalize method:
public class CleverLicenseManager extends LicenseManager {
private static CleverLicenseManager instance = null;
public static CleverLicenseManager make() {
try {
new CleverLicenseManager();
} catch(Exception ex) {} // ignore
try {
synchronized(CleverLicenseManager.class) {
while (instance == null) {
System.gc();
CleverLicenseManager.class.wait(100);
}
}
} catch(InterruptedException ex) {
return null;
}
return instance;
}
public void finalize() {
System.out.println("In finalize of " + this);
synchronized(CleverLicenseManager.class) {
instance = this;
CleverLicenseManager.class.notify();
}
}
public CleverLicenseManager() {
System.out.println("Created CleverLicenseManager");
}
}
This is then created with the make() method from within our
CleverApplication:
public class CleverApplication {
public static void main(String[] args) {
CleverLicenseManager lm = CleverLicenseManager.make();
SecuritySystem.register(lm);
// now we call the other application
Application.main(args);
}
}
The result is:
In finalize of CleverLicenseManager@77d134
Now let's get things started
Obviously, the CleverLicenseManager could have some data members
that are not properly initialized, even final data
members could be uninitialized.
Say the original LicenseManager class was marked as final.
How do we make it non-final?
Making a class non-final without decompiling
In the good ol' days we were able to decompile any class
with a simple decompiler such as JAD. More recently, Java
programmers have become more nasty and have started using
obfuscators that do terrible things to your class, making it a
major effort to change a class from final to non-final.
Luckily the class format is well-known to us, so with a simple
hex editor, we can make a class non-final without decompiling.
Here's the dummy guide on how to do it:
- Load the class in your favourite hex editor (e.g. frhed
available at
http://www.tu-darmstadt.de/~rkibria)
- Look at the end of the method definitions, you will see
a byte value that has bit 00010000 set, e.g. 0x31. Unset that
bit and your class should now be non-final.
The non-dummy's guide would say that you should use a byte code
library such as BCEL to change the class from final to non-final.
Of course, if you're a freak like me, you'll just load the class
in VI in binary mode and change the value of the correct
character ;-)
Disclaimer! = you shouldn't really hack licenses...
Please note that I do not under any circumstances advocate
hacking license managers to deprive worthy code authors of their
salaries. It is just an example, ok? And besides, no programmer
would ever write a LicenseManager like the one I wrote ;-)
Cheersio
Heinz
Language Articles
Related Java Course
Discuss at The Java Specialist Club
|