본문으로 건너뛰기

17. YARA 룰로 리버싱 시그니처 잡기

YARA 룰은 악성 여부를 판결하는 마법의 문장이 아니라, 바이너리와 메모리에서 관찰한 가설을 반복해서 확인하는 작은 계측기다. 리버싱에서는 “같은 패밀리인가”와 “이 기능이 존재하는가”를 빠르게 태깅하고, 이후 디스어셈블러와 행위 로그로 검증한다. 샘플은 합법적으로 확보한 CTF·IR 데이터와 격리된 분석 환경에서만 다룬다.

좋은 시그니처의 기준

문자열 하나는 쉽게 재사용되거나 제거된다. 반대로 너무 긴 바이트열은 컴파일러·최적화·버전 변화에 취약하다. 좋은 룰은 의미 있는 짧은 패턴 몇 개와 파일 형식·구조 조건을 조합하고, 정상 샘플을 함께 돌려 오탐률을 측정한다.

import "pe"

rule lab_xor_loader {
meta:
purpose = "training sample classification"
version = "1"
strings:
$marker = "XOR_KEY" ascii wide
$decode = { 48 31 ?? 48 89 ?? ?? ?? 48 83 ?? }
condition:
pe.is_pe and filesize < 2MB and $marker and $decode
}

pe.is_pe를 사용하면 uint16(0)==0x5A4D보다 의도가 분명하다. 바이트 패턴의 ??는 변하는 바이트를 허용하지만, 의미 없는 와일드카드를 늘리면 탐지력과 설명 가능성을 동시에 잃는다. 룰이 실제로 로드되는지 yarac 컴파일과 테스트 샘플 스캔으로 확인한다.

yarac rules.yar rules.yarc
yara -s rules.yar ./samples/known-test.bin

리버싱과 피드백 루프

먼저 문자열·imports·섹션·제어 흐름에서 관찰을 수집하고, 그중 변종에서도 유지될 이유가 있는 특징을 고른다. 룰이 매치한 오프셋을 -s로 확인한 뒤 Ghidra나 디버거에서 같은 기능인지 검증한다. 매치 수가 늘어나는 것이 성공이 아니다. 정상 파일 집합과 변종 집합의 confusion matrix, 샘플 출처, YARA 버전을 함께 기록해야 한다.

조건식을 1 of ($s*)처럼 넓게 쓰는 경우에는 문자열 그룹의 의미가 유지되는지 확인한다. 특정 함수의 고유한 패턴이 필요하면 여러 조건을 모두 요구하되, 패치·컴파일러 변화에 대한 회귀 샘플을 준비한다. 메모리 스캔 룰은 파일 오프셋과 가상 주소를 혼동하지 않도록 대상과 수집 시점을 메타데이터에 남긴다.

운영할 때의 판단

YARA는 차단 결정을 단독으로 내리기보다 triage의 우선순위를 정하는 데 적합하다. 룰 이름과 태그에 관찰 사실을 적고 “악성 확정” 같은 과장을 피한다. 매치된 문자열이 개인정보나 비밀일 수 있으므로 결과 로그에는 필요한 오프셋과 룰 ID만 남기고 원문 수집을 제한한다. 룰을 작게 나누면 오탐의 원인을 고치기 쉽고, 룰 변경이 분석 결론을 바꾼 시점도 추적할 수 있다.