Skip to main content

Command Palette

Search for a command to run...

[angr] angr가 뭐죠?

바이너리 분석 프레임워크 angr에 대해서 알아보자

Updated

배경

바이너리 분석 방법에는 크게 2가지가 있다.

  1. 수동: gdb 같은 디버거로 어셈 코드를 한줄씩 실행하면서 레지스터와 메모리의 변환를 추적하는 분석

  2. 자동: angr와 같은 자동화된 분석 프레임워크를 사용

이 2가지는 상호보완적이다.

이 글에서는 자동 바이너리 분석 프레임워크 angr에 대해서 알아보겠다.

소개

angr는 바이너리 분석을 위한 다재다능한 파이썬 기반 프레임워크이다. 중요한 철학은 바이너리 분석 작업을 프로그래밍 방식으로 자동화하는 것이다. API를 통해 복잡한 분석 스크립트를 쉽게 작성할 수 있다.

원리

아래 컴퍼넌트 들이 서로 상호작용하며 작동한다.

  1. Loader: 분석할 바이너리를 메모리에 올림, 바이너를 파싱하고 각 세그먼트를 angr가 이해할 수 있는 주소 공간에 매핑한다.

  2. State: 프로그램의 특정 시점 스냅샷이다. angr 분석의 기본 단위이며, 초기 상태(entry_state)에서 시작한다.

  3. Claripy: 솔버다. 입력값처럼 알 수 없는 값을 심볼로 표현하고 프로그램에 가해지는 수학적 논리적 제약 조건들을 추적한다.

  4. SimEngine: State를 입력 받아 코드 한 블록을 실행하고 그 결과로 다음 State들을 만들어낸다. 예를 들어 if문을 만나면 2개의 true, false State를 생성한다.

  5. PathGroup: 탐색할 State를 관리하는 매니저이다.

이런 컴포넌트들이 상호작용하며 바이너리 로드 → 초기 상태 설정 → 심볼릭 실행으로 경로 탐색 → 목표 상태 도달 이라는 분석 과정을 자동화한다.

실습

아래와 같은 C언어 코드를 컴파일해서 angr로 분석해보자.

// gcc -g -o auth_check auth_check.c
#include <stdio.h>
#include <stdlib.h>

void success() {
    printf("Success!\n");
}

void failure() {
    printf("Failure.\n");
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("Usage: %s <password>\n", argv[0]);
        return 1;
    }

    int key = 0xdeadbeef;
    int input_val = atoi(argv[1]);

    if ((key ^ input_val) == 0x12345678) {
        success();
    } else {
        failure();
    }

    return 0;
}

이 프로그램은 0xdeadbeef라는 key 값과 사용자가 입력한 input_val을 XOR해서 결과가 0×12345678과 같은지를 검사한다.

angr를 이용한 자동분석

import angr
import claripy

# 1. 바이너리 로딩
project = angr.Project('./auth_check', auto_load_libs=False)

# 2. 심볼릭 인자 설정 및 초기 State 생성
# argv[1]은 최대 10자리 숫자로 가정 (null 포함 11바이트)
# Claripy를 이용해 심볼릭 비트벡터(변수)를 생성한다.
sym_argv1 = claripy.BVS('sym_argv1', 10 * 8)
# entry_state가 아닌 full_init_state를 사용하면 argc, argv 설정이 더 용이하다.
state = project.factory.full_init_state(args=['./auth_check', sym_argv1])

# 3. SimulationManager 생성 및 탐색
simgr = project.factory.simulation_manager(state)

# success 함수의 주소를 찾아 'find' 대상으로, failure 함수 주소는 'avoid' 대상으로 설정
find_addr = project.loader.find_symbol('success').rebased_addr
avoid_addr = project.loader.find_symbol('failure').rebased_addr
simgr.explore(find=find_addr, avoid=avoid_addr)

# 4. 결과 추출 및 확인
if simgr.found:
    found_state = simgr.found[0]
    # success 경로에 도달하기 위한 구체적인 입력값을 solver를 통해 얻어낸다.
    solution = found_state.solver.eval(sym_argv1, cast_to=bytes)

    print("🚀 Success! Angr found the solution.")
    # atoi() 함수는 숫자 부분만 인식하므로, null 바이트 이전까지 출력
    print(f"🔑 Input value: {solution.split(b'\\x00')[0].decode()}")
else:
    print("😥 Could not find the solution.")

위 스크립트를 실행하면 angr가 심볼릭 실행으로 (0xdeadbeef ^ atoi(sym_argv1)) == 0x12345678 라는 제약 조건을 만들고, 솔버가 방정식을 만족하는 값을 찾아준다.

🚀 Success! Angr found the solution.
🔑 Input value: -862328681

로우레벨 관점에서 본 심볼릭 실행

  1. 심볼릭 레지스터와 메모리: 레지스터와 스택 변수들은 angr의 State객체 안에서 관리된다. 값이 정해져 있으면 구체적인 값으로 알 수 없는 값이라면 Claripy가 만든 심볼릭 표현으로 저장된다. 그리고 state.reges.eax 같은 코드로 이 값에 접근을 할 수 있다.

  2. 경로 분기와 제약 조건: cmp 어셈블리어를 만나면 State가 복제된다. fork한다.

한계

angr는 직접 추적하기 어려운 복잡한 경로를 자동으로 탐색하고 답을 구하는 강력한 도구이다.

하지만 아래와 같은 한계점이 있다.

  • 상태 폭발: 프로그램이 복잡해지고 분기가 많아지면 탐색할 State의 수가 기하급수적으로 증가하고 분석에 필요한 시간과 메모리가 매우 커지게 된다.

More from this blog

Android 악성 앱, 왜 여전히 뚫릴까? Louvain 그래프 탐지법이 그 해답일지도!

“바이러스는 진화한다. 그럼 탐지법도 진화해야 하지 않을까?” 악성 앱은 더 똑똑해졌습니다. 단순히 특정 API를 호출하는지 감지하는 방식은 더 이상 통하지 않죠. API가 어떻게 엮이는지, 함께 등장할 때 어떤 ‘행위 패턴’을 만드는지까지 보는 시대입니다. 그리고 바로 여기서, “동시 출현 그래프”와 “커뮤니티 탐지”라는 키워드가 등장합니다. 개념 드리프트, 악성 탐지의 맹점 악성 앱 탐지 모델이 시간이 지나면 무력해지는 현상, 알고 계셨나...

Jul 28, 2025

[WriteUp] / [CryptoHack] Resisting Bruteforce

해석 블록 암호가 안전하려면, 공격자가 AES의 출력과 무작위 비트 배열을 구분할 수 없어야 한다. 또한, 키를 무작위로 전부 시도하는(브루트포싱) 것보다 더 효율적인 복호화 방법이 있어서는 안 된다. 그래서 이론적으로라도 브루트포싱보다 적은 연산으로 복호화가 가능하다면, 그 암호는 '깨졌다고(broken)' 간주된다. 128비트 키를 브루트포싱하는 건 얼마나 어려울까? 누군가 계산했는데, 비트코인 전체 채굴 연산력을 동원해도 우주의 나이보다 ...

Jul 28, 2025

열심히 만든 리버싱 문제를 날먹하는 방법

열심히 만든 리버싱 문제를 날먹으로부터 지키는 방법 이 글을 읽기 전에 위의 드림핵 커뮤니티에 올라온 글을 보면 이해하기 수월하다. 서론 리버싱 문제는 출제자가 의도한 대로 정석적인 방법으로 역연산을 하여 플래그를 구할 수도 있지만, 편법을 사용하여 날먹하는 것이 가능하다. 편법에는 여러가지 방법이 있지만 위 드림핵 커뮤니티 글을 읽고 아이디어를 얻어 리버싱 문제를 해결했기 때문에 그 방법을 소개하고자 한다. 문제 풀이 해결한 문제는 Rev L...

Jul 28, 2025
A

Aegis

5 posts