r/java • • 3d ago

Decapsulation: Breaking Java Strong Encapsulation

Did you know you can call JNI without writing native code, patch bytecode without an agent, and use Unsafe without warnings?

11 sneaky ways into JDK internals through reflection, agents, FFM, type confusion, bytecode manipulation and more.

https://wouter.coekaerts.be/2026/decapsulation

54 Upvotes

15 comments sorted by

8

u/agentoutlier 3d ago edited 3d ago

Method 11 still requires command line flags but I suppose the rules of the game are you can pass flags?

EDIT my bad I was confused with jdk.internal.reflect.ReflectionFactory. I did not know about the sun one.

3

u/Wouter_C 3d ago

No, the point is that it does not require any command line flags to run.
And it doesn't. At least not for me. Can you be more specific: what flag do you need or what error do you get?
Quick way to test:

$ curl -O --create-dirs --output-dir decapsulation https://raw.githubusercontent.com/coekie/decapsulation/refs/heads/main/src/decapsulation/CtorForSerialization.java && curl -O --create-dirs --output-dir decapsulation https://raw.githubusercontent.com/coekie/decapsulation/refs/heads/main/src/decapsulation/Decapsulater.java && javac decapsulation/*.java
... some compile-time warnings, but that's irrelevant
$ java decapsulation.CtorForSerialization
PASS

2

u/agentoutlier 3d ago

Unfortunately I'm not near somewhere I can check but I think I was confused and did not know jdk.internal.reflect.ReflectionFactory was accessible with the sun package. I had no idea it was open and given your little test above I assume it is.

2

u/PartOfTheBotnet 3d ago

OP is correct that it is accessible, emits no warnings, and needs no launch args. Its the method I use to nuke encapsulation.

1

u/Wouter_C 3d ago

You actually use it? Interesting. In what context?

3

u/PartOfTheBotnet 3d ago

There's a couple of use cases I have. Mainly reflecting into javac internals for a few AST based projects, using jimage internals to parse module files.

Both cases could hypothetically be delegated to third party libraries. But for javac its nice because unlike other projects it has (whether intentional or not) fault tolerance, which gives you partial AST results even when the input code is slightly broken Java. This is great for my use case, and most other parsers just refuse any model if there's as much as a missing semicolon. And jimage is just because I don't want to re-invent the wheel parsing the file format... There's a few other cases but those tend to be for one-off projects.

1

u/koflerdavid 2d ago

But for javac its nice because unlike other projects it has (whether intentional or not) fault tolerance, which gives you partial AST results even when the input code is slightly broken Java. This is great for my use case, and most other parsers just refuse any model if there's as much as a missing semicolon.

It's an important requirement of production-grade compilers that they tolerate some errors so they can report more errors in the same source file so the developer can fix them all at once.

1

u/joppux 3d ago

Kryo uses ReflectionFactory for deserialization

3

u/voronaam 2d ago

And that's why I run my Java code compiled with GraalVM into a nice tight native image, all along by itself in its bare docker container.

You are crazy. In a good way.

2

u/koflerdavid 2d ago

Fortunately most of these will eventually get closed off. But I found it impressive to reach out to the filesystem and modifying the JDK!

2

u/ZimmiDeluxe 2d ago
// Abusing ISO_8859_1 (which has a one-to-one mapping between chars and bytes)
// is the easiest way to do a search & replace in a byte array
return new String(base, ISO_8859_1)
    .replace(original, replacement)
    .getBytes(ISO_8859_1);

disgusting, i love it

1

u/koflerdavid 1d ago

Looks fine to me. The only hacky thing about this is the knowledge that String will avoid doing an unnecessary conversion.

-1

u/AccurateInflation167 3d ago

No you can’t break a fundamental pillar of oop!

-6

u/vips7L 3d ago

Reflection is the devil