Extracts the ONNX sub-models and vocabularies out of Windows 11's oneocr.onemodel, in pure Java.
Windows only. Run it once. It is the bootstrap step for oneocr/onnx, which then runs OCR anywhere with no Microsoft binaries at all.
From your own licensed Windows installation, into ~/oneocr/models/:
detector/text_detector.onnx 11.5 MB FPN text detector
classifier/script_classifier.onnx 3.5 MB script, orientation, print-vs-handwriting
recognizers/recognizer_<script>.onnx 9 files CTC recognizers, 1.7-13.4 MB each
vocab/vocab_<script>.txt 9 files index to character, recovered per script
aux/ 3 files see "the three we cannot use"
Nine scripts: CJK, Cyrillic, Latin, Arabic, Devanagari, Greek, Thai, Hebrew, Tamil.
oneocr.dll links against Microsoft's own build of ONNX Runtime and reaches it through one public entry point, OrtGetApiBase(), which returns a table of roughly 200 function pointers. Slot 8 is CreateSessionFromArray, and the DLL has to hand that function fully decrypted model bytes in order to load anything at all.
So: load onnxruntime.dll first, make the table page writable with VirtualProtect, overwrite slots 8, 9 and 151 with Java FFM upcall stubs, then ask oneocr.dll to build its pipeline with model delay-load turned off. Each stub forwards to the original so the DLL keeps working, and captures the plaintext ONNX on the way past.
Vocabularies need a second trick, because the recognizers emit indices rather than characters. Slot 9 is Run, so the classifier's script_id_score output can be forced to select one chosen script and the recognizer's logsoftmax output forced to spell out a known run of consecutive vocabulary indices — then you read back the text the DLL's own decoder produces and record which character each index gave. Repeat until the vocabulary is covered, for each script.
This is userland API interception inside our own process, using documented Windows calls. No disassembler, no debugger, no driver, no patched files on disk.
java --enable-native-access=ALL-UNNAMED -jar oneocr-modelex.jar
java --enable-native-access=ALL-UNNAMED -jar oneocr-modelex.jar --natives ~/oneocr --out ~/oneocr/models --no-vocab
Needs oneocr.dll, oneocr.onemodel and onnxruntime.dll in the natives folder (~/oneocr by default). JDK 22 or later. Takes about two minutes.
The vocabulary sweep is self-checking. The obvious implementation forces a fixed block of 200 indices per pass and assumes the decoded text lines up one-for-one. It doesn't always: an index that decodes to nothing shifts everything after it, silently corrupting the rest of the vocabulary. So when the decoded length doesn't match the number of timesteps forced, this halves the range and retries, isolating the blank index instead of shifting past it. That is why the vocabularies here need no post-hoc "cleaning" pass — index i in the output file really is model index i.
The three we cannot use. Extraction also yields three small models that will not load in stock ONNX Runtime: they need com.microsoft.oneocr:OneOCRFeatureExtract, a custom operator that exists only inside oneocr.dll. They are parked in aux/ and are not part of the OCR path. What they unlock is an open question — most plausibly per-line typography, since OneOCR exports a GetOcrLineStyle function nobody has used. Tracked as PRP 20.
The script naming in circulation is wrong on three entries, and this was established by reading the characters that actually came out rather than by trusting the vocabulary size:
- V=179 is Tamil, not Greek — ஂ ஃ அ ஆ இ ஈ உ ஊ
- V=244 is Greek, not Hebrew — the U+0370 block
- V=201 is Hebrew, not Bengali — the U+0591 block
So this build of OneOCR ships no Bengali recognizer, and it does ship a Tamil one that prior work did not know existed.
The classifier index to script mapping was also measured directly rather than inferred: forcing script_id_score to index N and observing which recognizer the DLL then selects gives 0 → nothing, then CJK, Cyrillic, Latin, Arabic, Devanagari, Greek, Thai, Hebrew, Tamil.
Both are properties of one OneOCR build. A future Windows build could reorder them, and the failure would be silent — OCR would still run, in the wrong alphabet. Naming scripts from the recovered characters instead of from a size table is tracked as PRP 24.
The models are Microsoft intellectual property. This tool decrypts them on the machine that owns the licence, for use on that machine. Nothing proprietary is contained in or distributed by this repository, and the extracted models must not be committed or redistributed — treat them as a build artefact with a provenance note.
The technique itself is not novel here and was published before this existed; see the credits.
- b1tg/win11-oneocr — first to reverse engineer the engine publicly.
- bropines/oneocr-onnx-python — worked out the in-memory ONNX Runtime API hook that this tool ports to Java FFM, and documented it properly. The method here is theirs; the Java implementation, the self-checking vocabulary sweep and the naming corrections are ours.
- JanikRitz/win11-oneocr, AuroraWright/oneocr, Cecilia-pj/win11_oneocr_py, wangfu91/oneocr-rs — the implementations that mapped the DLL's surface.
- MattyMroz — an early decryption effort referenced in the issue threads.