Search code examples
javajvmclassloader

Java Classloader: How to load class with binary name different from looked up name?


Edit: seems to be something different, read Edit2 first.

How to load a class with binary name different from looked up name?

I know the Java Spec does not allow that, but I have an application in front of me that does it somehow.

I have a proprietary Server (Windows exe that runs a JVM), the company that created it does no longer exist. Since I finally hit a bug I started extracting the classes to fix it and move it to Linux at the same time. The classes are obfuscated but that is not what bothers me.

I manipulated their top-level Classloader to dump each class file after they call defineClass (it loads the classes from an encrypted archive). There I see it looks for and defines a class P.x but the class file dumped contains a class with binary name P.X (case mismatch) (there are also inner classes named P.X$Y, so they match the binary name). The case mismatch should throw a NoClassDefFoundError in defineClass(), but since I dump the class directly after defineClass() it clearly does not.

I'm a little afraid to continue before I understand that. Because maybe they included some traps, like classes that must not load and checking for that later. So maybe the server will return wrong results if I just continue without understanding in detail how this works.

From how they packed the JVM I assume they did not manipulate the JVM. From looking at OpenJDK code such stuff is in the C++ part, so they cannot just replace some class file to achieve that.

I can only think of 3 ways without hacking the JVM:

  1. I hoped it maybe is some secret VM arg, but I found nothing so far: https://web.archive.org/web/20100130070337/http://www.md.pp.ru/~eu/jdk6options.html
  2. Replace java.lang.ClassLoader and set the passed in name to null so that only the binary name matters. But would this even work?
  3. Using ClassFileTransformer somehow. But would this even work?

I did not find clues that they do 2) or 3). And I don't see how those would work because the code looks for P.x so even if P.X would be loaded, so not sure those would help.

Does anybody have an idea how this could be done? They use 32bit JRE Hotspot 1.6.0_01-b06, mixed mode on Windows.

Legal note: In my country I'm clearly allowed to repair a software for which I paid a lot of money and which is no longer supported.

Edit1:
I now injected my little sample that does the same thing into their code. It fails with NoClassDefFoundError, so they do not manipulate the JVM or java.lang.ClassLoader such that this test is not done globally.

Edit2:
It is probably the file system, but the other way around as suggested in the comments (I overwrite files).
Since I can now attach a debugger I saw something strange: the byte[] that goes into defineClass() contains the correct binary name.
If P/x and P/X exist I would override one because Windows FS is case insensitive. Doh. Will be back if I'm sure it is that.


Solution

  • The problem was that the Windows File System is case insensitive (but not because of loading classes like suggested in the comments, but for me writing the file dumps).

    I wrote out the classes with their names, but because P.x and P.X exist, I did overwrite P.x with P.X.

    So it only looked for me as if it is loading P.X from file P.x, because in my dumped file P.x was class P.X because of that error.

    Thanks all and sorry.