Browse documentation
Documentation / Protection passes / Anti-tamper checksums

Anti-tamper checksums

ImplementedEvery protected class verifies its own bytes at load time, and fails on a mismatch.

What it does

Every protected class's static initialiser calls a runtime check, first, before any of the class's own static initialisation runs. That check reads the class's own current bytes off the classpath, computes a SHA-256 hash, and compares it against the expected value recorded at protect-time, throwing a SecurityException immediately on a mismatch, before any of the class's own code executes.

Where the checksum lives

A checksum covering a class's own final bytes can't be embedded inside those same bytes without excluding its own storage location from what it covers, and carving one constant's bytes out of a serialized class file is the kind of fragile byte-offset patching this project avoids. Instead, every class's checksum is stored in a single separate bundled resource, computed only after every other bytecode mutation, decompiler-resistant wrapping, the anti-tamper call injection itself, has already happened, so the checksum always matches the exact bytes that ship.

Threat model

Detects a class file edited in isolation after protection while its checksum entry is left alone, the common naive-patch pattern, for example editing a single class to defeat an embedded licence check. It does not resist an attacker who edits the checksum resource and the target class together (both are equally editable jar entries), and it does not resist an attacker who modifies a running JVM's in-memory state directly rather than the class file on disk.

Verified. A tampered copy of a protected class, one byte changed inside a string constant, fails to load with a SecurityException, thrown before any of that class's own initialisation or methods can run. Every class in an unmodified Apache Commons Exec build loads and verifies, with its checksum populated.