2

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.

  • Just curious. Which country is this? – tbodt Jan 13 '15 at 23:35
  • Note that all sorts of things are possible via the debugger interface. – Hot Licks Jan 13 '15 at 23:36
  • 1
    Actually, this is how the normal classloader would also behave on a case-insensitive filesystem (causes problems sometimes on Macs). Maybe this "encrypted archive" is a case-insensitive one? – Thilo Jan 13 '15 at 23:37
  • No, the case sensitiveness of the file system should not matter. I created an example and tested on Windows. It throws NoClassDefFoundError if it looks for P.x but loads P.X. The class is finally created by a call to defineClass(name,...), if this name is not the binary name that is inside the class file you get the error. The file system does not matter. – stackunderflow Jan 13 '15 at 23:56
  • Again about FS: you have the problem on Mac if it on Windows wants to load P.x and loads it from a file P.X, but the class inside it must still be P.x, otherwise you get the error. That is not the case I have. – stackunderflow Jan 14 '15 at 00:07
  • What about a [custom ClassLoader](http://stackoverflow.com/questions/13498741/define-custom-system-classloader) defined among the [JVM extensions](http://docs.oracle.com/javase/tutorial/ext/basics/load.html)? – Martín Schonaker Jan 14 '15 at 00:28
  • Just a rogue idea. Is it possible that this class P.x is created and compiled at runtime? So in fact it isn't on that encripted file. I'm asking just because I've seen all sorts of black magic sh** hole done with beanshell http://www.beanshell.org/ – Jorge Campos Jan 14 '15 at 00:35
  • "I hoped it maybe is some secret VM arg", so you already ruled out -noverify? – the8472 Jan 15 '15 at 10:34
  • VM args: yes, I have a simple example and tried a lot of them. That check is not influenced by them. Also in the meantime I know exactly what they set, nothing.I can now attach a debugger (hacked their exe to start debug listener of JVM). But no luck so far. – stackunderflow Jan 16 '15 at 10:31
  • Note Edit2 I added. Thanks all. I override P.x with P.X if both exist, I'm rather sure it is that. – stackunderflow Jan 16 '15 at 12:49

2 Answers2

1

Maybe they bypass the normal Classloader#defineClass implementation and use sun.misc.Unsafe#defineClass instead?

the8472
  • 40,999
  • 5
  • 70
  • 122
  • They directly extend java.lang.ClassLoader and call its defineClass, so I don't think so. But thanks for the hint that that one exists. – stackunderflow Jan 15 '15 at 09:50
0

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.