
스트립된 x86-64 바이너리에서 함수의 이름을 지정하고 설명하는 소형 언어 모델로, 공개 데이터셋, 베이스라인, LoRA 파인튜닝 실험을 포함합니다.
영어 · 터키어
스트립된 바이너리에서 함수를 보고 의미 있는 이름과 짧은 설명을 부여하는 소형 언어 모델.
v4 데이터셋은 5개의 최적화 수준에 걸쳐 341개 프로젝트의 228,177개 함수를 포함하며, v4 구성으로 Hugging Face에 공개되어 있습니다. 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은 모든 프로젝트에 사용된 커밋과 컴파일러 플래그를 기록합니다.
스트립된 바이너리 시뮬레이션: 출력은 스트립된 바이너리의 함수에 대해 Ghidra가 보여주는 것과 유사합니다:
sub_01a3이 되고, 전역 데이터는 dat_0042가 됩니다 (숫자는 섞여 있으며, -O0과 -O2에 대해 별도의 네임스페이스가 있습니다),loc_1 및 loc_2와 같은 함수 로컬 레이블을 사용합니다,memcpy, __stack_chk_fail)과 문자열 리터럴(; -> "out of memory")은 실제 바이너리에서도 보이기 때문에 그대로 표시됩니다.함수의 실제 이름이 자체 어셈블리에 나타나는 경우—예를 들어 오류 메시지에서—해당 행은 sizinti로 표시되고 테스트 세트에서 제외됩니다.
평가 (taban.py): 모델은 어셈블리만 받고 이름을 예측하도록 요청받습니다. 예측은 토큰 중첩(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으로 개별 컴파일되었으며, 컴파일에 실패한 파일이나 프로젝트는 건너뛰었습니다. 행은 정규화된 어셈블리 해시로 중복 제거되었습니다. 검증/테스트 데이터와 어셈블리가 동일한 행은 학습에서 제거되었고, (파일, 이름) 쌍이 학습에 나타난 벤더링된 복사본은 검증/테스트에서 제거되었습니다. 모든 행에는 lisans 필드가 있습니다.
| 분할 | 프로젝트 | 행 | 고유 소스 함수 |
|---|---|---|---|
| 학습 | 290 | 196,117 | 72,179 |
| 검증 | 23 | 19,770 | 6,667 |
| 테스트 (잘 알려지지 않음, ≤200 스타, 2024+) | 28 | 12,290 | 4,685 |
473개 프로젝트 중 352개가 행을 생성했고 120개는 컴파일에 실패했습니다(커널, 임베디드 또는 플랫폼 의존적 코드). clang만 사용되었습니다. 데이터는 Git 외부에 보관되며 python3 aday_bul.py와 python3 olcekle.py hepsi로 재생성할 수 있습니다.
학습/테스트 분할은 엄격하게 프로젝트 수준입니다: 어떤 프로젝트의 함수도 양쪽에 나타날 수 없습니다. 테스트 프로젝트는 의도적으로 잘 알려지지 않은 것들(2-190 스타)로 선택되었고, 대부분 2024-2025년에 시작되어 zlib와 같은 라이브러리보다 대형 모델이 학습 중에 접했을 가능성이 훨씬 낮습니다. 라이선스: veri/LISANSLAR.md.
알려진 예외: 벤더링된 코드. 릴리스된 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 |
추론 실행은 max_tokens 상한 12,288을 사용했습니다. 그 한계에서도 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개에서 응답이 잘렸으며, 이는 긴 입력이 glm에 해롭다는 것을 나타냅니다). Deep 컨텍스트는 summary보다 거의 추가 이점이 없습니다. 래퍼는 deep 컨텍스트에서도 0으로 남습니다(테스트 세트에 6개만 있음; 피호출자의 완전한 어셈블리조차 모델을 올바른 이름으로 이끌지 못합니다).
Qwen2.5-Coder-0.5B (4-bit), 1,500 스텝 (~1시간 40분, 4.8 GB 메모리). 두 시도 모두 성공하지 못했습니다: