English · Türkçe
一个小型语言模型,它查看剥离二进制文件中的函数,并为其赋予有意义的名称和简短描述。
v4 数据集包含来自 341 个项目的 228,177 个函数,涵盖 5 个优化级别,并发布在 Hugging Face 上,配置为 v4。Ghidra 脚本已准备就绪。目前最好的小型模型,Qwen3-8B + LoRA 在 v5 数据(先描述,后名称)上训练,在固定的 v4 测试样本上达到名称 F1 0.124,在 eval_115 上达到 0.155;带有调用上下文的最佳大型模型在同一测试样本上得分为 0.167。参见结果第 6 节。该模型会写出英文和土耳其文的一句话描述以及名称。适配器位于 Hugging Face 上:krxi123/asmsense-qwen3-8b-lora。
在 Ghidra 或 IDA 中打开一个剥离的程序,你会看到数百个名为 FUN_00401a30 的函数。逆向工程的大部分工作包括逐个理解和命名这些函数。本项目旨在教会模型这第一步:
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.
大型通用模型在一定程度上可以处理这项任务,但它们速度慢、成本高,并且托管在云端。目标是回答这个问题:
一个在专门构建的数据上训练的小型模型,能否在这项任务上匹配甚至超越大型模型——同时还能在笔记本电脑上运行?
在同一个测试集上评估三个层级:
| 层级 | 模型 | 角色 |
|---|---|---|
| 上限 | 大型开放模型(DeepSeek、GLM、MiMo、Gemma、Qwen) | 使用现成模型目前可达到的最佳水平 |
| 基线 | 小型开放模型,微调之前 | 起点 |
| 本项目 | 同一个小型模型 + 在此数据集上的 LoRA | 本项目的贡献 |
数据生成(cikar.py): 开源 C 项目在不同优化级别(-O0、-O2)下为 x86-64 编译。提取每个函数的汇编代码。真实标签——原始函数名——直接来自源代码,因此无需手动标注。projeler.json 记录了每个项目所使用的提交和编译器标志。
模拟 stripped 二进制文件: 输出类似于 Ghidra 在 stripped 二进制文件中显示的函数内容:
sub_01a3,全局数据变为 dat_0042(编号被打乱,-O0 和 -O2 有各自的命名空间),loc_1 和 loc_2,memcpy、__stack_chk_fail)和字符串字面量(; -> "out of memory")保持可见,因为它们在真实二进制文件中也是可见的。如果函数的真实名称出现在其自身的汇编代码中——例如在错误消息中——该行会被标记为 sizinti 并从测试集中排除。
评估(taban.py): 模型只接收汇编代码,并被要求预测一个名称。预测结果按 token 重叠度(F1)评分,因此像 crc32_update 和 update_crc 这样的接近答案会获得部分分数。
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
| 项目 | 函数(-O0 + -O2) | |
|---|---|---|
| 训练 | zlib, libpng, sqlite, lua, mbedtls, zstd, libsodium, expat, brotli, jansson, lz4, libyaml, xxhash, cJSON | 16,804 |
| 测试(抗记忆) | tomlc17, cyaml, mu_json_x, sajs, picomatch | 777 → 评估集 115 |
数据集可在 Hugging Face 上获取:krxi123/asmsense(16,804 个训练样本、777 个测试样本、eval_115 评估集以及许可证文本)。Kaggle 上也有镜像:krxiii/asmsense。
使用 olcekle.py 从 GitHub 选取的宽松许可(MIT、BSD、Apache-2.0、ISC、zlib)C 项目,通过 cikar_bin.py(真实 dylib + strip -x)在 -O0/-O1/-O2/-O3/-Os 下编译。未运行项目构建脚本;文件使用 clang 单独编译,编译失败的文件或项目被跳过。行按规范化汇编哈希去重。汇编与验证/测试数据相同的行从训练中移除,而(文件,名称)对出现在训练中的 vendored 副本则从验证/测试中移除。每一行都有一个 lisans 字段。
| 划分 | 项目 | 行数 | 唯一源函数 |
|---|---|---|---|
| 训练 | 290 | 196,117 | 72,179 |
| 验证 | 23 | 19,770 | 6,667 |
| 测试(鲜为人知,≤200 stars,2024+) | 28 | 12,290 | 4,685 |
在 473 个项目中,352 个生成了行,120 个编译失败(内核、嵌入式或平台相关代码)。仅使用 clang。数据保存在 Git 之外,可通过 python3 aday_bul.py 和 python3 olcekle.py hepsi 重新生成。
训练/测试划分严格按项目级别:任何项目的函数都不能同时出现在两侧。测试项目被刻意选择为鲜为人知(2-190 stars),且大多数始于 2024-2025 年,这使得大型模型在训练期间遇到它们的可能性远低于像 zlib 这样的库。许可证:veri/LISANSLAR.md。
已知例外:vendored 代码。 对已发布 v6 数据的独立审计(rapor/arastirma/V6_VERI_DENETIMI.md,veri_denetim_v6.py)确认没有任何项目、id 或(项目,文件,名称)同时出现在两侧,但测试项目 snkv 以自己的文件名嵌入了 SQLite,而 sqlite 是训练项目。在固定的 2,000 行测试样本中,217 行(10.85%)的名称与某个 SQLite 训练函数相同。每个模型在这些行上的得分都更低,因此它们不会抬高结果;它们反而缩小了小模型与大模型之间的差距(见结果第 6 节)。验证样本 valid_300 包含 16 个类似的行(5.3%)。
平均名称 F1(1.0 = 完全匹配)。在“去除前缀 F1”列中,每个项目的常见名称前缀(cyaml_、mu_、sajs_)从两侧移除;模型无法从汇编中推断出该前缀。
| 模型 | -O0 | -O2 | 去除前缀 F1 | 完全匹配 |
|---|---|---|---|---|
| mimo-v2.6-pro(推理) | 0.20 | 0.17 | 0.21 | 3 / 115 |
| mimo-v2.6-pro | 0.18 | 0.16 | 0.20 | 4 / 115 |
| glm-5.3(推理) | 0.16 | 0.11 | 0.15 | 4 / 115 |
| qwen3.8-flash-next(推理) | 0.16 | 0.08 | 0.13 | 3 / 115 |
| deepseek-v4.1-flash(推理) | 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(推理) | 0.11 | 0.08 | 0.10 | 2 / 115 |
| gemma-4-31b | 0.11 | 0.07 | 0.10 | 0 / 115 |
| qwen2.5-coder-0.5b,未训练 | 0.00 | 0.01 | 0.01 | 0 / 112 |
| qwen2.5-coder-0.5b + LoRA v1 | 0.02 | 0.00 | 0.01 | 0 / 112 |
推理运行使用了 12,288 的 max_tokens 上限。即使在该限制下,deepseek 和 qwen 在超过一半的请求上未能完成推理并给出答案;glm 在 115 个请求中有 50 个也是如此。这些行被计为 0。
相同的数据流水线,相同数量的函数(115),mimo-v2.6-pro:
| 无推理 | 有推理 | |
|---|---|---|
| zlib(非常著名) | 9 个完全匹配,F1 0.18 | 28 个完全匹配,F1 0.29 |
| 鲜为人知的项目 | 4 个完全匹配,F1 0.20 | 3 个完全匹配,F1 0.21 |
推理使 zlib 上的完全匹配数增加了两倍,而在鲜为人知的代码上没有任何贡献。最可能的解释是,在推理过程中,模型并不是在“理解”汇编,而是在回忆它以前见过的源代码。因此,在著名库上评估现成模型会给出对该任务性能的误导性图景。

关于内部被调用者的信息被添加到每个函数的汇编中(无推理,去除前缀 F1):
| 模型 | 无上下文 | Summary | Deep | 长函数(无上下文 → deep) |
|---|---|---|---|---|
| mimo-v2.6-pro | 0.20 | 0.22 | 0.24 | 0.12 → 0.25 |
| deepseek-v4.1-flash | 0.12 | 0.17 | 0.17 | 0.10 → 0.18 |
| qwen3.8-flash-next | 0.11 | 0.15 | 0.16 | 0.06 → 0.18 |
| gemma-4-31b | 0.10 | 0.09 | 0.10 | 0.04 → 0.11 |
| glm-5.3 | 0.12 | 0.14 | 0.13 | 0.06 → 0.05 |
上下文对长函数以及调用其他内部函数的函数帮助最大(glm 除外:115 个请求中有 35 个响应被截断,表明长输入对它有害)。Deep 上下文相比 summary 增加很少。即使有 deep 上下文,包装函数仍为 0(测试集中只有 6 个;即使被调用者的完整汇编也不能引导模型得到正确的名称)。
Qwen2.5-Coder-0.5B(4-bit),1,500 步(约 1 小时 40 分钟,4.8 GB 内存)。两次尝试都没有成功:
| 运行 | 数据 | F1 | 完全匹配 | 发生了什么 |
|---|---|---|---|---|
| 未训练 | — | 0.01 | 0 / 112 | 大多数响应毫无意义 |
| LoRA v1 | 14,710 个函数,原始名称 | 0.01 | 0 / 112 | 记住了项目前缀:112 个预测中有 54 个是 mbedtls_… |
| LoRA v2 | 10,360 个函数,移除前缀,每个项目 ≤1,500 | 0.02 | 0 / 112 | 模式崩溃:112 个预测中有 56 个是两个名称之一 |
0.5B 模型和半个 epoch 对于此任务似乎不够。下一步实验:更大的基础模型(1.5B-3B)、更长的训练,以及在输入中加入调用上下文。
相同的固定 v4 测试样本(lora/test_sabit_idler.txt,鲜为人知的测试项目)和 eval_115 集。名称 F1 与真实名称比较;去除前缀 F1 从两侧移除项目前缀。详情:rapor/MOLAB_IKI_F1.md、sonuc/SABIT500_LORA15_V3.md。