앞으로 분석을 하게 될 것 같아서 분석 기법(중 정적 위주로)에 대해서 알아보았다.
정적 분석(Static Analysis)
코드 실행 없이 소스 코드나 바이너리를 검사하여 버퍼 오버플로우, 인젝션 취약점 등을 탐지
대표적인 방식으로 패턴 매칭과 데이터 흐름 분석이 존재
패턴매칭(Pattern Matching)
복잡도와 코드이탈 메트릭 기반의 취약점 예측 모델(ML활용)
- 순환 복잡도와 코드 이탈 메트릭을 결합해 제어 프름 그래프와 변경 이력을 분석하는 방식
- 코드 복잡도 : McCabe의 공식 M=E−N+2P (E: 간선 수, N: 노드 수, P: 연결 컴포넌트 수)로 계산
- 코드 이탈 메트릭 : 표준이나 규칙을 벗어나는 정도를 의미
- 아래는 cyclomatic에 따른 위험도/ 실제로는 패스 복잡도, 패스 수 등 8개를 기준으로 매트릭을 구성했다고 함
- https://support.suresofttech.com/ko/support/solutions/articles/5000478085-%EB%B3%B5%EC%9E%A1%EB%8F%84-cyclomatic-complexity-%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B3%84%EC%82%B0%EB%90%98%EB%82%98%EC%9A%94-
| Cyclomatic | Complexity Complexity | 위험도 |
| 1-10 | 단순함 | Low risk |
| 11-20 | 약간 복잡함 | Moderate risk |
| 21-50 | 복잡함 | High risk |
| 50 이상 | 매우 복잡함 | Very high risk |
- 취약점에 대한 예측을 수행하기 위해 Random-Forest를 1차 분류 알고리즘으로 선택하거나, wrapper subset을 사용
- random-forest : 부트스트래핑(bootstrap aggregating, bagging)을 통해 훈련 데이터를 무작위로 샘플링하고, 각 트리가 피처 서브셋만 사용해 학습
- wrapper subset: 최적 서브셋 찾는 방식
일반적인 패턴 매칭
취약점 코드(패턴) → 코드 파싱 → AST 생성 → 비교
의 과정을 거친다.
이 방식은 변형된 형태이거나, 제로데이 추정에는 약점을 보이는데, 아래의 방식은 이와 같은 약점을 최소화하기 위한 방법이다.
Vulnerability Extrapolation

알려진 취약점 패턴(AST)을 벡터 공간에 매핑해 새로운 취약점 후보를 코사인 거리를 이용하는 유사도 기반으로 추정·발견하는 방식
데이터 / 정보흐름 분석(Data Flow Analysis)
Static Taint analysis
데이터(일반적으로 사용자의 입력)에 taint를 삽입 후 이를 추적하는 방식으로, 크게 아래의 과정을 따른다.
- 데이터에 taint 마킹
- 대입(a=b), 산술 연산, 함수 호출 통해 taint 전파 추적(propagaion 과정)
- 오염된 데이터(taint)가 위험한 함수나 지점(sink)에 도달하는지 확인
특히, 오염된 값이 exec(), system()등에 도달하는지 여부를 체크한다.
전체 코드에 대해서 이런 과정을 하기에 경로가 폭발하하거나 허위양성이 나올 가능성이 있다.
동적분석(Dynamic Analysis)
Dynamic Taint analysis
taint analaysis를 런타임에 하는 것으로 정확한 런타임 경로를 알수 있다는 장점이 있으나, 실행되지 않은 경로는 탐지하지 못하는 단점이 있다.
퍼징 (Fuzzing)
다양하고 무작위의 데이터를 입력하여 소프트웨어 내부의 충돌, 검증 실패 및 메모리 누수 등의 예외를 발생시키는 것
| 유형 | 정보 활용 | 도구 예시 | 특징 |
| 블랙박스 | 없음 | zzuf | 단순 무작위 |
| 그레이박스 | 코드 커버리지 피드백 | AFL++ | 최고 효율 |
| 화이트박스 | 소스코드 | KLEE | 심볼릭 결합 |
그레이박스
- 커버리지 피드백: 새로운 경로(transition) 발견 시 해당 입력 우선 큐에 저장
- 즉, 새로운 브랜치에 초점을 맞춰 입력을 선택하는 방식
- 버그 발견은 블랙박스보다 빠르지만, 깊은 비교 조건에 약함
화이트박스
- 모든 가능한 실행 경로를 커버하는 입력을 만들어 내는 기법
- CFG 구축이 선행되어야하고, 각 조건 분기의 path constraint를 수식으로 모델링함
- path constraint : 특정 실행 경로를 통과하기 위해 입력값이 만족해야 하는 수학적 조건
기호 실행(Symbolic Execution)
프로그램 입력을 기호(σ_x 같은 미지수)로 치환해 실행하면서 각 분기 조건을 path condition으로 누적하고, SMT 솔버가 이를 풀어 각 경로에 맞는 구체적인 입력값을 찾는 과정
#include <string.h>
#include <klee/klee.h>
int check_password(const char *pwd) {
if (strlen(pwd) < 4) return 0; // 길이 4 이상
if (pwd[0] != '1') return 0; // 1로 시작
return 1; // 유효
}
int main() {
char pwd[10];
klee_make_symbolic(pwd, 10, "pwd"); // pwd[0..9] 기호화
int valid = check_password(pwd);
klee_assert(valid); // assert 위반 시 버그 경로 찾음
return 0;
}
위와 같은 코드가 존재하고, π는 해당 실행경로를 밟기 위한 제약식일 때
- 경로1 (실패1): strlen(pwd) < 4 → π = |σ_pwd| < 4. 솔버가 "ab\0..." 같은 짧은 입력 생성.
- 경로2 (실패2): strlen(pwd) ≥ 4 ∧ pwd ≠ '1' → π = |σ_pwd| ≥ 4 ∧ σ_pwd ≠ '1'. "2abcd" 같은 입력.
- 경로3 (성공): strlen(pwd) ≥ 4 ∧ pwd = '1' → π = |σ_pwd| ≥ 4 ∧ σ_pwd = '1'. "12345" 같은 입력 생성 (assert 통과).
위와 같은 경로와 제약식을 얻는 것이 symbolic execution이다.
위의 화이트박스 fuzzing과 함께 연계하여 사용하는 경우가 많다.
복잡한 연산을 하거나 조건이 많아질 경우, 경로가 폭발할 수가 있다.
자료 출처
'졸업프로젝트' 카테고리의 다른 글
| ghidra headless 사용 (0) | 2026.02.13 |
|---|---|
| 사전공부 6 (0) | 2026.02.06 |
| 사전공부 4 (0) | 2026.01.23 |
| 사전공부 3 (0) | 2026.01.16 |
| 사전 공부 1 & 2 (0) | 2026.01.02 |