Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
fnprint — 바이너리의 함수를 바이트 형태가 아닌 동작 방식으로 매칭합니다. 마이크로실행을 통한 동작 기반 함수 핑거프린팅. | Kitploit
도구/GitHubGitHub/1rhino2/fnprint
Vulnerability AnalysisDynamic Code Analysis (DAST)Reverse EngineeringMalware AnalysisBinary AnalysisFirmware Analysis
GitHub1rhino2/fnprint

fnprint

바이너리의 함수를 바이트 형태가 아닌 동작 방식으로 매칭합니다. 마이크로실행을 통한 동작 기반 함수 핑거프린팅.

저장소 보기
202151일 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

fnprint

fnprint는 바이너리의 함수를 바이트나 제어 흐름 그래프의 모양이 아니라 함수가 하는 일을 기준으로 매칭합니다. 각 함수를 가짜 입력을 넣은 작은 에뮬레이터에서 실행하고, 발생하는 부수 효과를 기록한 다음, 그 동작을 해시하여 지문으로 만듭니다. 동일하게 동작하는 두 함수는 다른 컴파일러나 다른 최적화 수준으로 빌드되었더라도 유사한 지문을 얻습니다.

이렇게 하는 이유는 다음과 같습니다. 바이트 시그니처(FLIRT, FunctionID)는 코드가 재컴파일되는 순간 깨지고, CFG 매처(BinDiff, Diaphora)는 -O0와 -O3 사이에서 불안정해집니다. 동작(behavior)은 두 경우 모두 훨씬 잘 버텨냅니다.

지금은 x86-64 ELF만 지원합니다. 믿고 쓰기 전에 한계를 확인하세요.

사용 예시

스트리핑된 바이너리와 이미 이름을 알고 있는 것들의 코퍼스를 지정해 보세요:

root@kitploit:~
$ strip --strip-all mystery.so
$ nm mystery.so
nm: mystery.so: no symbols

$ fnprint index libz.so -o corpus.db          # a build you have symbols for
$ fnprint query mystery.so --corpus corpus.db
named 9 function(s):
  0x000022f9  100.0%  adler32_z
  0x00002a8d  100.0%  compress2
  0x0000ad5d  100.0%  inflateBackEnd
  0x0000adc4   86.7%  inflate_fast
  0x00002e67   80.5%  crc32_z
  ...

이 실행은 완전히 스트리핑된 -O0 빌드를 -O2 코퍼스로 이름 붙인 것입니다. 최적화 수준이 다르고 심볼이 하나도 남아 있지 않은데도 이름이 올바르게 돌아옵니다. 자신 있는 것만 이름을 붙이고 나머지는 조용히 넘어갑니다.

또 다른 기능은 두 빌드를 diff하여 어떤 함수의 동작이 바뀌었는지 알려주는 것입니다. 벤더가 새 펌웨어를 배포했을 때 실제로 무엇이 변경되었는지 알고 싶을 때 유용합니다:

root@kitploit:~
$ fnprint match old.so new.so
compared 84 functions present in both
  unchanged:  53
  changed:    1
  low-signal: 31 (too small to judge)

changed behavior (lowest similarity first):
   61.7%  deflate_stored

실제 n-day 작업에는 triage가 있습니다. 함수의 알려진 취약 버전에서 코퍼스 하나를, 패치된 버전에서 다른 하나를 만든 다음, 알 수 없는 빌드를 양쪽 모두에 대해 순위를 매깁니다. 취약한 쪽에 가깝고 패치된 쪽과 명확히 분리되는 함수가 사람 앞에 놓여야 할 대상입니다. 해석해야 하는 단일 매치 점수가 아니라요:

root@kitploit:~
$ fnprint index vuln.so    -o vuln.db
$ fnprint index patched.so -o patched.db
$ fnprint triage mystery.so --vuln vuln.db --patched patched.db
43 functions triaged: 1 look vulnerable, 0 patched, 42 inconclusive

review queue (vuln-leaning, strongest first):
   addr         vuln%  patched%  margin  matches
   0x000022f9  100.0     27.3   +72.7  adler32_z vs crc32_z

두 버전에서 동일한 42개 함수는 의도적으로 판정 불가(inconclusive)로 돌아옵니다. 어느 쪽에도 확정할 수 없으므로 플래그로 표시하면 안 됩니다. --margin과 --min-sim은 판정을 내리기 전에 양쪽이 얼마나 분리되어야 하는지를 제어합니다.

동작 방식

각 함수에 대해:

  • 바이너리를 매핑하고 인자 레지스터에 쓰레기 값을 넣은 상태에서 함수로 점프합니다.
  • 설정하지 않은 메모리에서 읽기가 발생하면 결정적인 값을 반환하고 페이지가 즉석에서 매핑됩니다. 야생 포인터(wild pointer)가 실행을 중단시키지 않으며, 같은 입력은 항상 같은 트레이스를 만듭니다. 이것은 Godefroid의 microexecution 기법입니다.
  • 다른 함수로의 호출은 스텁 처리됩니다(기록된 후 건너뜀). 그래서 libc 안으로 들어가지 않고 실행이 이 함수에 집중됩니다.
  • 아키텍처 중립적인 부수 효과 스트림을 기록합니다. 어떤 인자 버퍼와 구조체 필드를 읽고 쓰는지, 어떤 값 클래스를 쓰는지(입력의 복사본, 작은 상수, 포인터), 어떤 호출을 하는지, 어떤 분기를 타는지, 무엇을 반환하는지. 절대 주소는 버리고 오프셋과 형태만 유지됩니다.
  • 그 스트림은 shingle로 변환되고 minhash 시그니처가 됩니다. 유사도는 일치하는 minhash 슬롯의 비율로, 두 함수의 동작이 얼마나 겹치는지를 추정합니다. LSH 밴드 인덱스는 쿼리가 모든 것을 일일이 비교하지 않게 해줍니다.

학습 데이터도 모델도 없습니다. 같은 아이디어는 문헌에서 Blanket Execution(Egele et al, USENIX Security 2014)으로 등장합니다. fnprint는 이를 실제로 사용할 수 있는 CLI와 함께 제공되는 실용적이고 유지 관리되는 구현입니다.

설치

rust 툴체인과 unicorn + capstone 라이브러리가 필요합니다.

root@kitploit:~
# debian/ubuntu/kali
sudo apt install libunicorn-dev libcapstone-dev

cargo install --path cli
# or just
cargo build --release   # binary at target/release/fnprint

사용법

root@kitploit:~
fnprint index <binary> [-o out.db]      fingerprint every function, optionally to a db
fnprint match <a> <b>                   diff two binaries (or .db files) by behavior
fnprint query <target> --corpus <db>    name unknown functions from a corpus
fnprint triage <t> --vuln <db> --patched <db>   rank a build against vuln vs patched corpora
fnprint eval <a> <b>                     accuracy metrics using symbol names as truth
fnprint dump <binary> <func>            print the recorded effect trace (debugging)

match와 query는 ELF 또는 index로 만든 .db를 입력으로 받습니다. 따라서 코퍼스를 한 번 지문화해 두고 재사용할 수 있습니다.

정확도

rank-1 정확도란 빌드 A의 함수에 대해 빌드 B의 모든 함수를 유사도 순으로 정렬했을 때 최상위 결과가 올바른 함수인지의 비율입니다. 이것은 정확히 스트리핑된 바이너리의 이름 붙이기 작업입니다. zlib 1.3.1(84개 함수)로 측정했으며, bench/run.sh로 재현할 수 있습니다:

전체 표와 두 번째 라이브러리(lua)는 bench/NUMBERS.md에 있습니다.

eval은 또한 recall@3 / recall@5와 기권률(abstention rate)을 보고합니다. rank-1만으로는 숨겨지는 것이 많기 때문입니다. gcc O0 -> O2의 경우 최상위 결과가 93% 확률로 맞지만 올바른 함수가 상위 5개 안에 들 확률은 96.6%입니다. 따라서 작은 검토 예산만으로도 대부분의 격차를 메울 수 있습니다. 또한 확신이 없는 쌍에 대해서는 추측 대신 기권합니다(자신 있는 "같음" 판정을 거부). 그래서 같은 임계값에서 recall은 낮아도 precision은 높게 유지됩니다.

솔직한 평가는 이렇습니다. 적어도 한쪽에 행동적 풍부함이 있으면(-O0/-O1 빌드 또는 -O0의 크로스 컴파일러 쌍) 80-98% 범위에 들어갑니다. 양쪽 모두 과도하게 최적화된 경우 관찰할 수 있는 동작이 얇아져 동전 던지기 수준으로 떨어집니다. 이것이 단일 패스, 학습 없는 매처에게는 어려운 경계이며, 이 도구도 그렇지 않은 척 하지 않습니다.

단점

  • 아주 작은 함수. thunk와 한 줄짜리 접근자(accessor)는 지문화할 만큼 충분히 동작하지 않으므로 이를 보류합니다(그것이 "low-signal" 및 "with enough signal" 개수입니다).
  • 순수 연산. 둘 다 버퍼를 읽고 숫자를 반환하는 두 체크섬은 서로 비슷해 보입니다. 외부에서 보면 거의 동일하기 때문입니다.
  • 실제 사전 조건 뒤에 숨은 깊은 로직. 쓰레기 입력을 사용한 microexecution은 함수의 진입 동작만 실행합니다. 쓰레기 입력으로는 도달하지 못하는 상태에 묻혀 있는 변경은 match에 나타나지 않습니다. 구조적 변경과 초기 경로 변경은 잡아내지만 모든 깊은 변경까지 잡지는 못합니다.
  • 양쪽 모두 과도한 최적화가 적용된 경우. 위의 수치가 보여주듯이.
  • 과도한 난독화(특히 VM 기반)는 도구를 망가뜨립니다.

기존 연구 및 이 도구의 위치

  • FLIRT / FunctionID / Lumina: 바이트 시그니처. 정확하고 빠르지만 재컴파일되면 깨집니다.
  • BinDiff / Diaphora: 그래프 구조. 좋지만 최적화 수준과 아키텍처가 다르면 취약합니다.
  • Ghidra BSim: 디컴파일러 피처 벡터. 정신적으로는 가장 가깝고 단일 아키텍처에 가깝습니다.
  • Asm2Vec / SAFE / jTrans: 학습된 임베딩. 강력하지만 학습이 필요하고, 학습에 포함되지 않은 아키텍처에는 일반화되지 않습니다.
  • microexecution(Godefroid, 2014)과 Blanket Execution(Egele et al, 2014): 이 접근 방식의 학문적 뿌리. 유지 관리되는 도구로 제공된 적은 없습니다.

fnprint는 학습이 필요 없고 동작 우선(behavior-first) 방식입니다. 효과 모델은 이미 아키텍처 중립적이며, 이는 CPU 간 매칭을 위한 기초 작업입니다.

로드맵

  • arm64와 mips 지원. x86에서 함수를 지문화한 다음 스트리핑된 라우터 펌웨어에서 그 함수를 찾을 수 있게 해줍니다. 효과 모델은 이미 아키텍처 중립적이며, 대부분은 아키텍처별 에뮬레이터 배관 작업입니다.
  • 더 똑똑한 경로 커버리지. 단순한 분기 뒤집기가 코드에 구현되어 있지만(explore_depth, 기본값은 꺼짐) 도달 불가능한 경로에서 빌드별 노이즈를 추가하고 테스트에서 크로스 빌드 정확도를 떨어뜨렸습니다. 따라서 쓸모를 인정받으려면 경로 일관성 필터가 필요합니다.
  • pe 및 mach-o 로더.
  • CLI를 호출해 매칭된 함수의 이름을 제자리에서 바꿔주는 ghidra / ida 플러그인.

라이선스

MIT. LICENSE를 참조하세요.

도구 다운로드
쌍rank-1정밀도
gcc O0 -> O197.1%93.8%
gcc O0 -> O293.1%83.3%
gcc O0 -> O391.3%100.0%
gcc/clang O097.7%97.1%
gcc O2 -> O356.5%66.7%
gcc/clang O259.1%50.0%