
Small language model that names and describes functions in stripped x86-64 binaries, with an open dataset, baselines, and LoRA fine-tuning experiments.
English · Türkçe
A small language model that looks at a function in a stripped binary and gives it a meaningful name and a short description.
The v4 dataset contains 228,177 functions from 341 projects across 5 optimization levels and is published on Hugging Face under the v4 config. The Ghidra script is ready. The best small model so far, Qwen3-8B + LoRA trained on the v5 data (description first, then name), reaches name F1 0.124 on the fixed v4 test sample and 0.155 on eval_115; the best large model with call context scores 0.167 on the same test sample. See Results, section 6. The model writes an English and a Turkish one-sentence description along with the name. The adapter is on Hugging Face: krxi123/asmsense-qwen3-8b-lora.
Open a stripped program in Ghidra or IDA and you will see hundreds of functions named FUN_00401a30. Much of reverse engineering consists of understanding and naming these functions one by one. This project aims to teach a model that first step:
input (stripped x86-64) target output
───────────────────────────── ──────────────────────────────────────────────
movzx eax, byte ptr [rsi] name: adler32_update
lea rdx, [rdi + rax] description: Computes the Adler-32 checksum
cmp rdx, 0xfff1 ← 65521 over the bytes of a buffer.
... # TR: Tampondaki baytlar üzerinden Adler-32
# sağlama toplamı hesaplar.
Large general-purpose models can handle this task to some extent, but they are slow, expensive, and cloud-hosted. The goal is to answer this question:
Can a small model trained on purpose-built data match or even outperform large models on this task—while running on a laptop?
Three tiers are evaluated on the same test set:
| Tier | Model | Role |
|---|---|---|
| Ceiling | Large open models (DeepSeek, GLM, MiMo, Gemma, Qwen) | The best currently achievable with off-the-shelf models |
| Baseline | Small open model, before fine-tuning | Starting point |
| This project | The same small model + LoRA on this dataset | The project's contribution |
Data generation (cikar.py): Open-source C projects are compiled for x86-64 at different optimization levels (-O0, -O2). The assembly for each function is extracted. The ground truth—the original function name—comes directly from the source, so no manual labeling is needed. projeler.json records the commit and compiler flags used for every project.
Simulating a stripped binary: The output resembles what Ghidra shows for a function in a stripped binary:
sub_01a3, and global data becomes dat_0042 (the numbers are shuffled, with separate namespaces for -O0 and -O2),loc_1 and loc_2,memcpy, __stack_chk_fail) and string literals (; -> "out of memory") remain visible because they are also visible in a real binary.If the function's real name appears in its own assembly—for example, in an error message—the row is marked sizinti and excluded from the test set.
Evaluation (taban.py): The model receives only the assembly and is asked to predict a name. Predictions are scored by token overlap (F1), so close answers such as crc32_update and update_crc receive partial credit.
loc_6:
movzx r9d, byte ptr [rsi + rdx]
add rdi, r9 ← adler32_z (-O2), as the model sees it
add rcx, rdi
inc rdx
cmp rax, rdx
jne loc_6
| Projects | Functions (-O0 + -O2) | |
|---|---|---|
| Training | zlib, libpng, sqlite, lua, mbedtls, zstd, libsodium, expat, brotli, jansson, lz4, libyaml, xxhash, cJSON | 16,804 |
| Test (memorization-resistant) | tomlc17, cyaml, mu_json_x, sajs, picomatch | 777 → evaluation set 115 |
The dataset is available on Hugging Face: krxi123/asmsense (16,804 training examples, 777 test examples, the eval_115 evaluation set, and license texts). It is also mirrored on Kaggle: krxiii/asmsense.
Permissively licensed (MIT, BSD, Apache-2.0, ISC, zlib) C projects selected from GitHub with olcekle.py were compiled at -O0/-O1/-O2/-O3/-Os using cikar_bin.py (real dylib + strip -x). Project build scripts were not run; files were compiled individually with clang, and files or projects that failed to compile were skipped. Rows were deduplicated by normalized assembly hash. Rows with assembly identical to validation/test data were removed from training, while vendored copies whose (file, name) pairs appeared in training were removed from validation/test. Every row has a lisans field.
| Split | Projects | Rows | Unique source functions |
|---|---|---|---|
| Training | 290 | 196,117 | 72,179 |
| Validation | 23 | 19,770 | 6,667 |
| Test (little-known, ≤200 stars, 2024+) | 28 | 12,290 | 4,685 |
Of 473 projects, 352 produced rows and 120 failed to compile (kernel, embedded, or platform-dependent code). Only clang was used. The data is kept outside Git and can be regenerated with python3 aday_bul.py and python3 olcekle.py hepsi.
The training/test split is strictly project-level: no function from a project can appear on both sides. Test projects were deliberately chosen to be little-known (2-190 stars), and most were started in 2024-2025, making it far less likely that large models encountered them during training than a library such as zlib. Licenses: veri/LISANSLAR.md.
Known exception: vendored code. An independent audit of the released v6 data (rapor/arastirma/V6_VERI_DENETIMI.md, veri_denetim_v6.py) confirmed that no project, id or (project, file, name) appears on both sides, but test project snkv embeds SQLite under its own file names while sqlite is a training project. In the fixed 2,000-row test sample, 217 rows (10.85%) have a name identical to a SQLite training function. Every model scores lower on those rows, so they do not inflate the results; they narrow the small-vs-large gap instead (see Results, section 6). The validation sample valid_300 contains 16 similar rows (5.3%).
Mean name F1 (1.0 = exact match). In the “Prefix-stripped F1” column, each project's common name prefix (cyaml_, mu_, sajs_) is removed from both sides; the model cannot infer this prefix from assembly.
| Model | -O0 | -O2 | Prefix-stripped F1 | Exact matches |
|---|---|---|---|---|
| mimo-v2.6-pro (reasoning) | 0.20 | 0.17 | 0.21 | 3 / 115 |
| mimo-v2.6-pro | 0.18 | 0.16 | 0.20 | 4 / 115 |
| glm-5.3 (reasoning) | 0.16 | 0.11 | 0.15 | 4 / 115 |
| qwen3.8-flash-next (reasoning) | 0.16 | 0.08 | 0.13 | 3 / 115 |
| deepseek-v4.1-flash (reasoning) | 0.12 | 0.08 | 0.12 | 3 / 115 |
| deepseek-v4.1-flash | 0.10 | 0.11 | 0.12 | 2 / 115 |
| glm-5.3 | 0.12 | 0.08 | 0.12 | 2 / 115 |
| qwen3.8-flash-next | 0.13 | 0.07 | 0.11 | 1 / 115 |
| gemma-4-31b (reasoning) | 0.11 | 0.08 | 0.10 | 2 / 115 |
| gemma-4-31b | 0.11 | 0.07 | 0.10 | 0 / 115 |
| qwen2.5-coder-0.5b, untrained | 0.00 | 0.01 | 0.01 | 0 / 112 |
| qwen2.5-coder-0.5b + LoRA v1 | 0.02 | 0.00 | 0.01 | 0 / 112 |