5 1 week ago

12M-parameter method-level code encoder for Android smali, ARM64 and x86-64. One shared 256-D space, obfuscation-invariant to class and method renaming and to compiler and optimisation variants. Trained on random Maven AARs and Debian packages.

embedding
ollama pull jordimurgo/methodprint-embed:2026-10-27

Details

1 week ago

7812292a8abb · 47MB

bert
·
11.6M
·
F32
{ "stop": [ "<pad>" ] }
{{ .Prompt }}

Readme

methodprint-embed

A 12M-parameter code encoder that maps methods — not whole binaries — into one shared 256-dimensional space across three architectures: Android smali, ARM64, and x86-64.

Model: jordimurgo/methodprint-embed:2026-10-27

What it is for

Identifying whether a given method comes from a known library, and whether that identification survives obfuscation that renames classes and methods and differences in compiler and optimisation level.

The space is shared but the domains are not interchangeable: an ARM method and a smali method are embedded by the same encoder, yet cross-architecture comparison is explicitly out of scope. Compare smali to smali, ARM64 to ARM64, x86-64 to x86-64.

Model details

Architecture BERT (6 layers, 4 heads, hidden 256, intermediate 1024)
Parameters 11.59M
Output 256-dimensional, one vector per method
Context 512 tokens, all-content-token mean pooling over non-overlapping windows
Quantization F32
Tokenization case-sensitive-code-v3 — word-level, closed vocabulary of 26,256 tokens; no subwords
License MIT

Training

Trained from random initialisation — no third-party weights are involved.

Domain Training windows
Android smali (smali-v3) 225,463
ARM64 (arm-v3) 124,996
x86-64 (x86-v3) 33,842
Total 384,301

Sources are open: Java/Kotlin libraries compiled from Maven artifacts with D8 in release and debug modes, Debian arm64 and amd64 binaries, and obfuscated variants produced with dProtect and Obfuscapk. No APK from any corpus enters training.

Training is two-stage: masked language modelling, then contrastive learning with compiler-variant positives (the same source built at different optimisation levels must land close) and same-library-different-method negatives.

Usage

ollama pull jordimurgo/methodprint-embed:2026-10-27
curl http://localhost:11434/api/embed -d '{
  "model": "jordimurgo/methodprint-embed:2026-10-27",
  "input": "invoke-virtual {v0}, Ljava/lang/String;->length()I"
}'

Returns one 256-dimensional L2-normalised vector.

llama.cpp:

llama-server -m methodprint-embed-2026-10-27-f32.gguf --embeddings \
  --pooling mean --ctx-size 512 --port 8093

Serving caveats — read these before indexing

The GGUF path adds two tokens that the PyTorch path does without. This is a policy difference, not a weight difference: the tensors are byte-identical.

  • The runtime prepends its own CLS id 101 and appends a separator, regardless of the vocabulary keys. Mean pooling therefore includes two tokens the production encoder never sees.
  • Measured effect: same-id parity is cos = 0.9999999; with the runtime’s framing the gap is median cos = 0.993 and top-1 neighbour agreement is 78%.
  • Consequence: do not mix server vectors and PyTorch-pipeline vectors in one index. Pick one runtime per index.
  • Recalibrate any similarity threshold per runtime. On this model the smali whole-artifact clustering threshold moved from 0.68 (PyTorch) to 0.76 (served).
  • Methods longer than 512 tokens are truncated by the server. Window client-side.

Evaluation, honestly

On a frozen smali whole-artifact benchmark, after recalibrating the threshold to the population the clustering step actually uses, this encoder reaches pair precision 1.000, recall 0.929, purity 1.000 over 12 true and 124 cross-identity validation pairs.

That number is real but small. The calibration set holds 12 true clusters, not the ~299 independent samples needed to support a 1% false-accept claim. Treat the result as “the separation exists on this frozen set”, not as a production rate.

On library containment inside larger binaries the encoder does not clear the bar: recall 0.75 with a 0.40 false-containment rate at 0.30 coverage over 12 positives and 81 negatives. Container detection is not a supported claim for this version.

Limitations

  • Methods only. It does not embed whole binaries, packages, or applications.
  • No cross-architecture comparison: smali↔ARM and ARM↔x86 similarity is meaningless.
  • Resistant to renaming-based obfuscation and compiler variants; not resistant to virtualisation (VMProtect, Tigress), and no resistance is claimed against an adaptive attacker who knows the model.
  • Library-presence detection is a similarity signal, never a malware verdict.

License

MIT. The weights were trained from random initialisation on public sources, so there are no upstream model terms to inherit.

If you use it, cite the dataset this work builds on where applicable, and publish the list of samples used so the result can be reproduced.