<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>pumisj 님의 블로그</title>
    <link>https://pumisj.tistory.com/</link>
    <description>pumisj 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 27 Jul 2026 04:21:45 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>pumisj</managingEditor>
    <item>
      <title>[알고리즘] CDCL 알고리즘 구현</title>
      <link>https://pumisj.tistory.com/12</link>
      <description>&lt;h1&gt;구현 코드&lt;/h1&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def init():
    # 할당값이 들어갈 딕셔너리
    symbolic_exp = {}

    # CNF 저장 형식 [[cluase], [cluase], ...] 각 cluase는 [1, -2, 3]으로 저장. 내부 리스트끼리는 conjunctive로 연결되어있음
    clauses = []

    while True:
        line = input(&quot;절 입력 (1 -2 3 형식) 입력이 끝나면 아무것도 입력하지 않고 엔터\n&amp;gt; &quot;)
        if not line:
            break
        clause = list(map(int, line.split()))

        symbolic_exp.update({abs(var): None for var in clause})  # 입력과 동시에 None으로 초기화. 각 수식은 양수로 관리
        print(symbolic_exp)

        levels = {abs(var): -1 for var in symbolic_exp}      # 해당 할당이 어느 시점에 이뤄졌는지
        reasons = {abs(var): None for var in symbolic_exp}   # 해당 할당이 어떤 할당에 의해 이뤄졌는지

        clauses.append(clause)

    print(f'입력된 CNF {clauses}')

    return symbolic_exp, clauses, levels, reasons

def assignRandExp(exps, levels, current_level, trail):
    print(f'------------레벨 {current_level} 할당 시작------------')
    selected = None

    # assignment가 안된 수식 중 하나 골라서 true 넣기
    for key, value in exps.items():
        if value is None:
            selected = key
            exps[key] = True
            levels[key] = current_level
            trail.append(key)
            break

    print(f'선택된 리터럴: {selected}: {exps[selected]}')

def unitPropagation(exps, clauses, current_level, levels, reasons, trail):
    print('--------------프로파게이션 시작--------------')

    # propagation이 연쇄적으로 일어날 수 있으므로 변화가 없을 때까지 반복
    while True:
        changed = False
        for clause in clauses:
            print(f'선택된 절: {clause}')
            none_literals = []
            is_already_true = False

            for literal in clause:
                var = abs(literal)        # 심볼릭 수식
                val = exps[var]     # 그 수식의 실제 값

                if val is None:
                    none_literals.append(literal)
                    continue

                # literal엔 음수 양수로 표시되어있고, val에는 true, false가 있다. 따라서 실제 값으로 변환
                if literal &amp;lt; 0:
                    actual_val = not val
                else:
                    actual_val = val

                if actual_val is True:
                    is_already_true = True
                    break

            # true가 있는 경우 이미 프로파게이션이 불가능
            if is_already_true:
                print('프로파게이션 불가능\n==============')
                continue

            # 모든 리터럴이 false라면 conflict. backjumping 필요
            if not none_literals:
                print(f'{clause}에서 conflict')
                return clause

            # cluase를 다 돌고 여기까지 왔을 때 none값이 단 한 개라면 무조건 True여야한다
            if len(none_literals) == 1:
                var = abs(none_literals[0])

                # 만약 남은 하나의 수식이 양수면 그대로 True, 음수면 False로 변환해서 저장 (-3이 true여야한다면 3은 false여야한다)
                exps[var] = (none_literals[0] &amp;gt; 0)
                print(f'선택된 수식: {var}:{exps[var]}')

                levels[var] = current_level
                reasons[var] = clause
                trail.append(var)
                changed = True
        if not changed: break

    return None

# 충돌난 절 부터 거꾸로 올라가 현재 레벨과 같은 할당이 하나 남을 때까지 반복해서 1UIP를 찾는다
# 위로 올라갈 때는 레벨이 높은 순서대로, 같은 레벨이라면 trail을 역추적해서 가장 나중에 할당된거 찾기
def find1UIP(conflict_clause, levels, reasons, current_level, trail):
    print('=============1UIP 탐색 시작=============')
    conflict_clause = list(conflict_clause)

    trail_index = len(trail) - 1

    while True:

        # 현재 레벨과 같은 리터럴 찾기
        current_level_literals = []
        for literal in conflict_clause:
            var = abs(literal)
            if levels[var] == current_level:
                current_level_literals.append(literal)

        # 리터럴이 1개 이하라면 1UIP
        if len(current_level_literals) &amp;lt;= 1:
            break

        # 할당을 역추적하면서 충돌절에 있는 가장 최근 할당을 탐색
        while abs(trail[trail_index]) not in [abs(lit) for lit in conflict_clause]:
            trail_index -= 1

        target_var = abs(trail[trail_index])
        target_literal = None
        for literal in conflict_clause:
            if abs(literal) == target_var:
                target_literal = literal
                break

        # 가장 최근 할당에 대해서 강제했던 절
        reason = reasons[target_var]

        # 충돌난 절과 그 원인 절을 합친다
        combined = set(conflict_clause) | set(reason)

        # 두 절에서 모순이 일어나는 타겟 리터럴을 제거
        if target_literal in combined:
            combined.remove(target_literal)
        if -target_literal in combined:
            combined.remove(-target_literal)

        conflict_clause = list(combined)
        trail_index -= 1

    # 이제 conflict_cluase에 현재 레벨 리터럴이 하나만 남았다
    # 현재 레벨을 제외하고 이 절의 리터럴중 가장 높은 레벨을 찾는다
    backjump_level = 0
    for literal in conflict_clause:
        var = abs(literal)
        level = levels[var]
        if level &amp;lt; current_level and level &amp;gt; backjump_level:
            backjump_level = level

    return conflict_clause, backjump_level

def backjump(trail, levels, exps, reasons, jump_level):
    new_trail = []

    # 기존 trail을 돌면서 점프해야하는 레벨 이후의 수식은 초기화해준다
    for var in trail:
        if levels[var] &amp;lt;= jump_level:
            new_trail.append(var)
        else:
            exps[var] = None
            levels[var] = -1
            reasons[var] = None

    return new_trail




# 메인

symbolic_exp, clauses, levels, reasons = init()

# 가장 최근 값 추적용 리스트
trail = []
current_level = 0

isSatisfiable = None

# 모든 값이 할당될 때까지
while None in symbolic_exp.values() :
    # 만약 한 절에 하나의 수식만 있다면? 바로 값이 정해질 수 있기 때문에 프로파게이션을 먼저 해준다
    # 또한 백점핑 이후에 학습된 절로 인해 또 충돌이 나는지 먼저 확인해야함
    conflict_clause = unitPropagation(symbolic_exp, clauses, current_level, levels, reasons, trail)

    if conflict_clause is not None:
        if current_level == 0:
            isSatisfiable = False
            break

        learned_clause, jump_level = find1UIP(conflict_clause, levels, reasons, current_level, trail)
        clauses.append(learned_clause)
        trail = backjump(trail, levels, symbolic_exp, reasons, jump_level)
        current_level = jump_level

        continue

    # 더 이상 전파할 수 없을 때 랜덤값 대입
    if None in symbolic_exp.values():
        current_level += 1
        assignRandExp(symbolic_exp, levels, current_level, trail)
    else:
        isSatisfiable = True
        break;

print('===========최종 결과===========')
if isSatisfiable:
    print(symbolic_exp)
else:
    print(&quot;만족할 수 없는 수식&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;간단한 설명&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 간단한 CDCL 알고리즘을 구현해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당하는것도 점수나 휴리스틱적인 방법이 아닌 단순히 입력된 순서로 True를 할당하는 방식으로 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Backjumping하는데에 필요한 할당 추적 리스트 trail, 레벨 추적 리스트 levels, 할당 원인 리스트 reasons를 각각 만들어 필요시에 사용하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 바로 할당값 대입하는게 아니라 unit propagation부터 진행함으로써 한 절에 하나의 리터럴만 있을 시 곧바로 값을 할당한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 propagation중에 conflict가 발생하면 해당 절부터 현재 레벨의 리터럴이 단 하나만 남을때까지 전의 할당 값을 타고 올라가는 방식으로 구현했다. (왜냐하면 충돌이 일어난 레벨에서 무조건 모순이 되는 할당 내역이 있고, 그 내역이 하나가 남을때가 1UIP이기 때문이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 찾은 식을 기존 CNF에 추가해주고 반복하는데, 만약 레벨 0에서 충돌이 나는 경우 그거는 만족할 수 없는 식으로 간주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-watched literal 방법도 아니고 간단한 알고리즘인줄 알았는데 생각보단 어려워서 구현해보니 시간복잡도가 너무 높은거같다. 해결할 수 있으면 해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;깃허브&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/SeojunKim-pumisj/CDCL-algorithm&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/SeojunKim-pumisj/CDCL-algorithm&lt;/a&gt;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/12</guid>
      <comments>https://pumisj.tistory.com/12#entry12comment</comments>
      <pubDate>Wed, 6 May 2026 03:54:09 +0900</pubDate>
    </item>
    <item>
      <title>[논문 정리] SCASE: Automated Secret Recovery via Side-Channel-Assisted Symbolic Execution</title>
      <link>https://pumisj.tistory.com/11</link>
      <description>&lt;h1&gt;서론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행을 공부하면서 이게 어떻게 사용되는지 좀 더 알고싶어 관련 논문을 찾아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에 부 채널 공격시에 심볼릭 실행이 사용된다는걸 보고 &quot;심볼릭 실행이 어떻게 부 채널 공격에서도 사용되는거지?&quot; 하는 궁금증이 생겨 읽게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시험기간이 걸쳐져서 좀 오래 걸리긴 했는데 이번에도 정리해서 올려보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Weber, Daniel, et al. &quot;{SCASE}: Automated Secret Recovery via {Side-Channel-Assisted} Symbolic Execution.&quot; &lt;i&gt;34th USENIX Security Symposium (USENIX Security 25)&lt;/i&gt;. 2025.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.usenix.org/conference/usenixsecurity25/presentation/weber&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.usenix.org/conference/usenixsecurity25/presentation/weber&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777814161186&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;SCASE: Automated Secret Recovery via Side-Channel-Assisted Symbolic Execution | USENIX&quot; data-og-description=&quot;Open Access Media USENIX is committed to Open Access to the research presented at our events. Papers and proceedings are freely available to everyone once the event begins. Any video, audio, and/or slides that are posted after the event are also free and o&quot; data-og-host=&quot;www.usenix.org&quot; data-og-source-url=&quot;https://www.usenix.org/conference/usenixsecurity25/presentation/weber&quot; data-og-url=&quot;https://www.usenix.org/conference/usenixsecurity25/presentation/weber&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.usenix.org/conference/usenixsecurity25/presentation/weber&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.usenix.org/conference/usenixsecurity25/presentation/weber&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SCASE: Automated Secret Recovery via Side-Channel-Assisted Symbolic Execution | USENIX&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Open Access Media USENIX is committed to Open Access to the research presented at our events. Papers and proceedings are freely available to everyone once the event begins. Any video, audio, and/or slides that are posted after the event are also free and o&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.usenix.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h1&gt;Abstract&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 몇 년간 소프트웨어 기반의 부채널 공격에 대한 폭발적인 연구가 이루어졌지만, 두 가지 한계가 있었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;민감한 정보를 얻기 위해서 대상 프로그램에 대한 깊은 이해가 필요.&lt;/li&gt;
&lt;li&gt;비밀 값 복구의 자동화가 어려움&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 논문에서는 불투명한 타겟 바이너리에서 구체적인 부채널 트레이스를 이용한 심볼릭 실행으로 비밀 값을 가져오는 새로운 방법인 SCASE와, PoC 프레임워크인 Athena를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는 심볼릭 실행 공간을 효율적으로 줄이기 위해 부채널 트레이스에서 관찰된 메모리 접근 정보를 활용하는 것이다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;b&gt;대상 알고리즘&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;추출 데이터&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;소요 시간&lt;/b&gt;&lt;/th&gt;
&lt;th&gt;&lt;b&gt;특이 사항&lt;/b&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RSA&lt;/td&gt;
&lt;td&gt;2048-bit Key&lt;/td&gt;
&lt;td&gt;4분 이내&lt;/td&gt;
&lt;td&gt;SGX 엔클레이브 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RC4&lt;/td&gt;
&lt;td&gt;256-bit Key&lt;/td&gt;
&lt;td&gt;5분 이내&lt;/td&gt;
&lt;td&gt;KSA 구현체 타겟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AES&lt;/td&gt;
&lt;td&gt;256-bit Key&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;OpenSSL S-box 공격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BEEA&lt;/td&gt;
&lt;td&gt;Input Value&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;알고리즘 입력값 복구&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 별도의 사전 지식 없이 다음 암호 키들을 빠른 시간 내에 복구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연구는 부채널 트레이스를 통해 심볼릭 실행을 제약하는 것이 대상 프로그램에 대한 깊은 이해 없이 소프트웨어 기반의 부채널 공격을 자동화하는 효과적인 방법임을 보여준다.&lt;/p&gt;
&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 연구는 다음과 같은 한계가 있었다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자가 소스 코드의 취약점이나 바이너리 레이아웃을 포함한 타겟 프로그램에 대한 전문적인 지식이 필요함.&lt;/li&gt;
&lt;li&gt;취약점을 찾는 도구는 많지만 False Positive, 즉 오탐률이 높아 실제로 해킹이 되는지 확인하기 위해서는 수동 검증이 필요함.&lt;/li&gt;
&lt;li&gt;심볼릭 실행은 모든 경우의 수를 다 계산하는거다보니 너무 느렸음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 따라 SCASE (side-channel-assisted symbolic execution)는 state explosion을 완화하면서 점진적으로 경로의 제약 조건을 수집하여 constraint solver가 자동적으로 비밀 값을 구채화할 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 과정에서 타겟 프로그램에 대한 이해가 비밀값과 어떻게 연관되는지에 대한 사전 지식을 요구하지 않으며 단지 다음 두 가지만을 필요로 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;대상 코드와 데이터 접근을 기록하는 자동화된 부채널 트레이스 추출 프레임워크&lt;/li&gt;
&lt;li&gt;타겟 바이너리 내의 비밀 값 위치의 주석처리.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 PoC 프레임워크인 Athena를 개발했으며 이를 이용하여 위의 표와 같이 암호화 알고리즘에 대해 빠른 시간 내에 비밀 값을 복구할 수 있었다.&lt;/p&gt;
&lt;h1&gt;Background&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Software-based side-channel attacks&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부채널 공격은 프로그램의 실행에 대해서 메타데이터를 누출하는 종류의 공격을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU 캐시부터 시작하여, CPU 내부 구조의 상태, CPU 실행 유닛의 경합, 그리고 전력 소모와 같은 다양한 정보를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 소프트웨어 기반 부채널 공격은 강력하고, 높은 정확도와 throughput을 보인다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Symbolic Execution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행은 시스템적으로 가능한 모든 입력값에 대해서 도달가능한 모든 실행 상태를 조사는 정적 프로그램 분석 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행 엔진 (Symbolic execution engine)은 구체적인 값일수도 있고 관련된 제약조건으로 표현되는 심볼릭 값일수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행은 일반적으로 두 단계로 구성된다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Path exploration phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 모든 가능한 실행 경로를 에뮬레이션하여 제어 흐름 그래프 (CFG)를 탐색한다.&lt;/li&gt;
&lt;li&gt;구체적인 입력으로 결정할 수 없는 분기를 만나면 SEE는 두 경로를 fork하여 모든 분기를 탐색한다.&lt;/li&gt;
&lt;li&gt;이 때 분기 조건의 심볼릭 값들은 현재 상태에 도달하는 결과를 얻을 수 있도록 제약된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Solving phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SAT solver와 같은 logic solver를 활용하여 위의 심볼릭 값들에 대한 구체적인 값을 계산한다.&lt;/li&gt;
&lt;li&gt;심볼릭 실행은 소스코드나 바이너리 레벨에서도 진행될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 장점에도 불구하고 심볼릭 실행은 state explosion이라는 문제를 가진다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;State explosion&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램의 가능한 분기에 대해서 모두 계산을 진행할 때 메모리 상태와 제약조건이 기하급수적으로 늘어나 결국 계산하지 못하는 현상. &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이에 따라 RSA, AES와 같은 암호 알고리즘은 수학적으로 매우 복잡한 조건으로 인해 해결하지 못하는 것으로 고려된다.&lt;/span&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Intel Software Guard Extensions&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Intel의 Software Guard Extensions (SGX)는 런타임에서 하드웨어 독립적인 메모리 구역의 기밀성과 무결성을 제공하는 신뢰 실행 환경 (TEE)으로 엔클레이브 (enclave)라고 불린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SGV 엔클레이브는 다음과 같은 특징이 있다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 사용자 프로세스 내에 있다&lt;/li&gt;
&lt;li&gt;대상 플랫폼의 권한 있는 소프트웨어를 제어하는 루트 공격자에 대해서 보호된 상태를 유지한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SGX의 격리모델과 작은 신뢰 컴퓨팅 기반은 암호 라이브러리와 같은 높은 보안 작업을 이상적으로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이 엔클레이브는 ECALL함수라고 불리는 진입점을 통해서 접근할 수 있으며, OCALL함수는 신뢰할 수 없는 호스트 프로세스에 구현되어 신뢰할 수 없는 콜백 역할을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SGX는 광범위한 부채널 공격의 대상이 되어왔기때문에 실전에서 실전에서 검증된 (battle-tested) TEE라고 지칭하게 되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Controlled-Channel Attacks&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TEE에 대한 부채널 공격으로는 제어 채널 공격 (Controlled-Channel attack)이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 채널 공격에서는 TEE 엔클레이브가 메모리 페이지를 설정할 때 이미 공격자가 제어권을 가지고있는 OS에 의존하는걸 응용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자가 Page-table entry를 조작하여 fault가 발생하게함.&lt;/li&gt;
&lt;li&gt;이 경우 엔클레이브는 실행을 중단하고 제어권을 OS에 넘김&lt;/li&gt;
&lt;li&gt;이 과정에서 엔클레이브는 레지스터 값을 모두 지우지만, 특정 page table entry를 접근한 정보는 남게됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 공격 기법으로는 single stepping이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Single stepping
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔클레이브 코드가 정확히 하나의 명령어를 실행한 직후에 타이머 인터럽트가 발생함.&lt;/li&gt;
&lt;li&gt;이에따라 공격자는 엔클레이브의 실행을 정밀하게 제어할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 공격은 SGX뿐만 아니라 Intel TDX, AMD SEV와 같은 다른 TEE 및 VM에도 적용될 수 있다.&lt;/p&gt;
&lt;h1&gt;SCASE&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SCASE (Side-Channel-Assisted Symbolic Execution)은 대상 프로그램에 대해 비밀 값을 자동적으로 복구하도록 하는 기법으로, 부채널 공격으로 얻은 정보를 SEE에 전달해 기존의 한계와 비밀 키 복구가 자동적으로 이루어지게 도움을 준다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;General Threat Model&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 위협 모델은 다음과 같이 가정했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자가 타겟의 바이너리에 접근할 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 통해 타겟을 심볼릭하게 (모든 경로를 분석) 실행할 수 있도록 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;공격자는 비밀 값을 바로 가져올 수 없으며, 오직 누출된 메타데이터만 사용가능함.&lt;/li&gt;
&lt;li&gt;하드웨어와 소프트웨어에는 메타데이터 누출 이외의 취약점은 없다.&lt;/li&gt;
&lt;li&gt;공격자는 이 정보를 모니터링할 수 있는 능력이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Overview&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SCASE는 부채널 공격과 심볼릭 실행을 결합한 기법으로, 부채널 공격에서 얻은 메모리 트레이스를 바탕으로 SEE가 경로 탐색과 제약 조건을 생성하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 탐색 공간을 획기적으로 줄여 상징화된 비밀키를 복구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 접근은 online과 offline 단계로 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1337&quot; data-origin-height=&quot;411&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bcnx4/dJMcafGt2DW/SwLAZPhOCZDCeh1VfcKve0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bcnx4/dJMcafGt2DW/SwLAZPhOCZDCeh1VfcKve0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bcnx4/dJMcafGt2DW/SwLAZPhOCZDCeh1VfcKve0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBcnx4%2FdJMcafGt2DW%2FSwLAZPhOCZDCeh1VfcKve0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1337&quot; height=&quot;411&quot; data-origin-width=&quot;1337&quot; data-origin-height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Online phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부채널 공격이 대상 프로그램에 수행되어 메모리 접근 패턴에 관한 가능한 한 많은 정보를 추론한다.&lt;/li&gt;
&lt;li&gt;공격을 통해 복구한 정보를 memory trace라고 부른다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;memory trace에는 메모리에 대한 제어 흐름과 데이터 흐름의 내용이 포함되며, 각각을 control-flow trace, data-flow trace라 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l8PIs/dJMcaaLWrVQ/TDF3iLvLsGo0E8C6iEVEK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l8PIs/dJMcaaLWrVQ/TDF3iLvLsGo0E8C6iEVEK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l8PIs/dJMcaaLWrVQ/TDF3iLvLsGo0E8C6iEVEK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl8PIs%2FdJMcaaLWrVQ%2FTDF3iLvLsGo0E8C6iEVEK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;331&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Offline phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;online 과정에서 얻은 트레이스들로 SEE가 대상의 비밀 값을 복구할 수 있도록 하는 과정이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;control-flow trace는 대상이 어떤 분기를 선택했는지에 대한 정보를 전달한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이를 통해 대상이 online phase에서 실행하지 않은 경로는 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;비밀값에 의존하는 제어 흐름은 트레이스에 인코딩된 비밀값에 대한 제약조건으로 이어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;data-flow trace는 어떤 메모리 위치에 접근했는가에 대한 정보를 전달한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 흐름 접근 패턴을 알면, SEE는 관찰된 패턴과 일치하지 않는 상태를 줄일 수 있다. &amp;rarr; 즉 state space를 줄일 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이러한 과정을 통해 SEE는 online 과정에서 얻은 메타 정보로 비밀 값을 효율적으로 계산해낼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Majoir Challenges&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀 값 추출을 자동화하기 위해서 많은 난관이 있었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;State Explosion&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능한 모든 경로를 탐색하는데 필요한 state space가 기하급수적으로 증가하는 문제. 모든 상태를 확인할 수 있다는게 SEE의 장점이지만, 복잡한 프로그램에 대해서는 효율적이지 못함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 암호 알고리즘의 보안 보장이 방대한 state space에 기반하고있기 때문에, 전형적인 암호화 어플리케이션의 비밀 값 의존적 제어흐름 (secret dependent control-flow)은 state explosion을 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 문제는 SEE가 결국 비밀 값을 알아야 한다는 것이다. 심볼릭 실행으로 결과를 얻었다고 할지라도, 결국 그 값은 어떤 값을 넣으면 이런 결과가 나올 수 있다는 수 많은 선택지가 생길 뿐이고, 결국 입력값인 비밀 값을 알아내기 위해서는 암호 알고리즘에 의해 생성된 암호문이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 온라인 단계 (부채널 공격)에서 얻은 메모리 접근 정보를 바탕으로 SEE가 탐색해야할 공간을 줄여나가는 방법으로 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;2. Map Side-Channel Information to the SEE&amp;rsquo;s State Space&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEE에서 부채널의 정보를 활용하기 위해서는 각 실행 단계에서의 매핑이 정확히 이루어져야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEE의 어떤 상태와 정확히 매핑되는 메모리 트레이스의 항목이 존재한다 (왜냐하면 결국 실행 흐름에 따라 메모리 트레이스가 생기기 때문에). &amp;rarr; 그러므로 SEE와 메모리 트레이스의 단사 매핑(injective mapping)을 구축해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 프로그램 내의 한 어셈블리 명령어는 여러번 호출될 수 있기 때문에 시간적 요소를 고려하여 매핑이 고려되어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부채널 공격중에 노이즈가 섞일 수 있는데, 이 경우 제대로된 매핑이 되지 않으니 노이즈가 없는 정확한 매핑이 중요하다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;injective mapping&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단사 매핑. 일대일 함수를 의미한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp; &amp;nbsp;3. Seperating Control- and Data-Flow Related Memory Accesses&lt;/span&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 및 데이터 흐름 관련 메모리 접근은 모두 SEE에게 정보를 제공하지만, 실행에는 서로 다른 방식으로 영향을 미친다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행을 더욱 정밀하게 돕기 위해서는 두 가지 메모리 접근 방식을 구분할 수 있어야한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;해당 메모리가 명령어 패치 (fetching of an instruction)에 의해서 접근이 되었는지&lt;/li&gt;
&lt;li&gt;해당 메모리가 명령어 실행에 의해서 접근이 되었는지&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식의 구분이 필수적인건 아니지만, pruning과 제약조건 생성에 도움이 된다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SCASE: Recovering Secrets from Memory Traces using Symbolic Execution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급한 문제점들을 해결하는 과정을 설명한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Splitting Memory Traces into Control- and Data-Flow Memory Traes&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 페치(fetch)는 현재 명령어의 주소가 메모리 트레이스의 다음 항목과 일치해야함을 의미 (PC와 일치)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령어 실행은 메모리 트레이스 항목이 메모리 접근 명령어의 의미론적 동작과 일치해야함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, 메모리 트레이스 항목에 0x1234라는 주소가 있고, 거기에 매핑되는 항목이 심볼릭 수식이라면 해당 수식은 0x1234여야한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘을 분리하는 방법은 부채널 공격자의 역량에 달려있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TEE 위협 모델처럼 강한 공격자 모델일 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자는 대상의 PTE를 제어하는 비트인 메모리 페이지 permission bits를 읽을 수 있다&lt;/li&gt;
&lt;li&gt;이 비트들은 해당 페이지가 쓰기 또는 실행 가능한지를 제어한다.&lt;/li&gt;
&lt;li&gt;PoC 구현체인 Athena는 이 방식의 분리를 사용한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;권한이 낮은 공격자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;마이크로 아키텍처 컴포넌트의 상태를 관찰하여 메모리 접근이 CPU 프론트엔드에서 비롯됐는지 추론한다&lt;/li&gt;
&lt;li&gt;I-cache나 iTLB는 프론트엔드의 명령어 페치에 의해서만 영향을 받으므로, 이런거를 관찰해서 메모리 접근을 구분한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Creating a Mapping between Memory Trace Entries and Emulated States&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말했듯이 메모리 트레이스에 노이즈가 섞이면 제대로된 분석이 불가능하다. 따라서 오류가 없도록 보장하는 방법이 몇 가지 있다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;교차검증 (cross-verify)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;부 채널 공격을 여러 번 하고 해당 데이터를 교차검증한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;제어 채널 공격처럼 애초에 에러가 존재하지 않는 부 채널 공격을 사용한다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 트레이스에 오류가 없다면 각 메모리 트레이스에 대한 항목을 SEE의 state space에 매핑해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하는 가장 간단한 방법은 가상 메모리 주소를 기반으로 매핑하는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중에 한 명령어는 여러 번 실행될 수 있기 때문에 정확한 시점에 매핑이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 SEE에 의해 에뮬레이션 되는 모든 탐색에 대해서 제어 및 실행 흐름 메모리 트레이스의 현재 위치를 유지해야한다. &amp;rarr; 조건 분기에 의해 fork될 때 메모리 트레이스의 위치도 함께 fork되어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하는 방법은 에뮬레이션된 메모리 접근에 대해서 해당 메모리 트레이스의 인덱스를 증가시키고, 상태공간의 모든 포크마다 다른 인덱스를 유지하는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상 메모리 주소가 유일한 답은 아니지만 여러 부 채널 공격에 사용될 수 있으며, 대안으로 단사 매핑이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pruning the State Space and Adding Additional Constraints&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEE와 매핑된 트레이스들은 실제 실행중에 도달하지 않은 상태를 pruning하고, 추가적인 constraint를 만드는데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 제어 흐름 메모리 트레이스가 모든 분기 결정에 대해 선택된 경로 정보를 항상 포함하고 있는건 아니다. (캐시된 데이터 접근, 메모리 접근이 없는 분기 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 탐색이 끝나면 SEE는 만들어진 제약 조건들을 솔버를 사용해 구체화하여 결국 비밀 값을 복구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션의 메모리 접근 동작에 의존하는 모든 유형의 공격은 이 방식을 통해 자동화가 가능하다.&lt;/p&gt;
&lt;h1&gt;Athena Framework&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 제어 채널 공격을 부 채널로 활용하여 SCASE를 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대상은 부 채널 취약점이 있는 Intel SGX 엔클레이브이며, 물론 이런 일반적인 TEE에만 적용되는건 아니지만 제어 채널 공격은 노이즈가 없는 트레이스를 얻기 위한 온라인 단계를 자동화하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 두 가지 주요 부분으로 구현되어있다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;single stepping과 액세스 비트 모니터링을 기반으로 하는 Intel SGX용 자동 제어 채널 공격 (온라인 단계)&lt;/li&gt;
&lt;li&gt;SEE인 angr을 위한 탐색전략 (오프라인 단계)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena의 위협 모델은 커널 모듈을 로드할 수 있는 권한을 가진 native-code 공격자를 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대상 데이터를 제외한 엔클레이브에 대한 모든 내용을 알 수 있어, 공격자는 실제 비밀 정보가 없는 상태에서 대상 어플리케이션의 복사본을 debug enclave하거나 에뮬레이팅할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Offline phase&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Emulating the Target&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 Guardian을 사용해서 angr가 Intel SGX 엔클레이브 바이너리와 호환가능하게 만든다. (Intel SGX 엔클레이브가 ELF파일로 표현되기 때문에 가능)&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;angr은 원래 .exe이나 .so파일 등을 처리하도록 만들어졌지만, Guardian은 엔클레이브와 같은 환경과의 통신 인터페이스를 연결해주기 때문에 angr이 엔클레이브를 분석할 수 있도록 한다&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 공격자는 호스트 어플리케이션이 엔클레이브에 제공하는 기능인 OCALL을 탈취하여 더미을 반환하게 할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더미만으로는 제대로 작동하지 않는 경우, angr의 SymProcedures 기능을 사용하여 커스텀 훅을 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 OCALL의 결과가 비밀을 도출하거나 기밀 계산의 일부로 사용되지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 보통 원격 검증 (remote attestation) 이후에만 보안 원격 인스턴스로부터 비밀 데이터가 로드되기 때문에, 엔클레이브 바이너리를 에뮬레이션하는 것만으로는 비밀 데이터를 추출하기 어렵다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Intel SGX 기술은 인증을 로컬이 아닌 서버에서 직접 인증을 하는 방법이라 여기서 비밀값을 알아내기는 어렵다.&lt;/p&gt;
&lt;/aside&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mapping the Leakage to angr&amp;rsquo;s State.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 및 데이터 흐름 메모리 관련 접근은 서로 다르게 처리해야하는 정보를 가지고 있기 때문에 메모리 트레이스를 두 단계로 나눈다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;제어 흐름 관련 메모리 접근 (어떻게 분기 결정을 했는지 정보를 전달하는 과정)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;angr의 커스텀 서브클래스인 ExplorationTechnique로 심볼릭 실행을 더 정밀히 제어한다&lt;/li&gt;
&lt;li&gt;이 클래스의 핵심 멤버 함수인 step은 basic block에 도달할때마다 호출되어 실제 트레이스 기록과 대조하여 실행된 경로만 따라가도록 한다.&lt;/li&gt;
&lt;li&gt;현재 basic block에 포함된 명령어의 개수로 트레이스의 인덱스를 증가시켜, 제어 흐름 트레이스 내의 위치를 추적한다&lt;/li&gt;
&lt;li&gt;메모리 트레이스와 일치하지 않는 분기를 잘라내어 필요한 분기만 인코딩해 솔버에 전달한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 흐름 관련 메모리 접근
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;제어 흐름과는 다르게, 메모리 접근에 대한 명시적인 제약조건을 추가한다. 즉, 데이터의 값을 강제하는 제약조건을 추가한다&lt;/li&gt;
&lt;li&gt;이를 모델링 하기 위해 Athena는 angr에서 발생하는 모든 메모리 읽기 쓰기에 callback을 등록한다&lt;/li&gt;
&lt;li&gt;콜백 내의 코드는 특정 실행 상태에 대한 데이터 흐름 트레이스의 현재 위치를 추적한다. 콜백이 호출될때마다 실행 상태의 값을 1씩 올려주기만 하면 된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;남은 작업은 솔버가 데이터 흐름 트레이스와 일치하는 메모리 접근을 하도록 제약조건을 부여하는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 다음과 같은 제약조건을 SAT solver에 추가한다&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;mActual AND NEG64-bit(0xFFF) == mTrace

=&amp;gt; mActual AND 0xFFFFFFFFFFFFF000 == mTrace // mActual의 상위 52비트만 남겨서 mTrace와 비교한다&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mActual: 실행 중 특정 지점에서 실제로 접근한 메모리 주소&lt;/li&gt;
&lt;li&gt;mTrace: 데이터 흐름 트레이스에 기록된 page-aligned entry&lt;/li&gt;
&lt;li&gt;$NEG_{64-bit}$: 64비트 정수의 binary negation&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 공식은 페이지 크기를 4kB로 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 상태의 한 구체적인 시점에 발생하는 메모리 접근에 대해서만 제약을 추가한다. 즉, 루프 내부의 메모리 로드의 경우 매 반복마다 메모리 주소가 같더라도 접근하는 값이 다르면 서로 다른 제약 조건을 가질 수 있다&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 운영체제에서는 4kB를 하나의 페이지 단위로 사용하고있으며, 0xFFF는 4kB크기의 메모리 페이지 오프셋을 의미한다. 따라서 오프셋을 제외한 상위 주소와 mTrace를 비교하겠다는 의미이다.&lt;/p&gt;
&lt;/aside&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Symbolizing and Constraining the Secret&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀 정보를 복구하기 위해서, Athena는 먼저 비밀 바이트가 저장된 메모리 범위를 상징화 (symbolize)하고 대상 프로그램의 CFG를 탐색하면서 메모리 접근 트레이스에서 파생된 데이터 및 제어 흐름 제약 조건을 상징화된 비밀 정보에 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 부 채널 트레이스가 비밀 값을 완전히 찾아낼 수 없어도 비밀 값의 일부를 복구할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 접근 방식은 심볼릭 탐색과 구체적인 실행 트레이스를 통합한다는 점에서 concolic 또는 DSE의 새로운 형태라고 볼 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Online Phase&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena에서는 세밀한 메모리 트레이스를 얻어내기 위해 제어 채널 공격을 사용하며, single-stepping하면서 페이지 테이블 엔트리의 접근 비트 (access bits)를 모니터링한다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;single-stepping&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램의 명령어 한 개씩 끊어서 실행하는 디버깅/공격기법&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 테이블 엔트리는 NX (Non-Executable) 비트를 기준으로 두 그룹으로 나눌 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;만약 페이지가 NX라면 해당 페이지는 데이터만 담고있으니 데이터 흐름 트레이스로 추적된다&lt;/li&gt;
&lt;li&gt;만약 페이지가 NX가 아니라면 해당 페이지는 코드를 담고있으니 제어 흐름 트레이스로 추적된다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이런 구분법은 꼭 권한이 있어야하는게 아니라 권한이 없는 공격자 모델에서도 memory map만 안다면 가능하다. memory map 정보를 얻을 수 없다고 해도, 정적으로 정의된 모든 메모리 페이지에 대해서는 ELF파일이 구조 정보를 모두 제공한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 접근 패턴에 대해 의미있는 정보를 주지 못하는 엔클레이브 TCS 페이지같은 메모리 페이지들은 추적하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트레이스는 이러한 방식으로 얻어진다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;single-step으로 명령어 하나만 실행&lt;/li&gt;
&lt;li&gt;check bit 확인&lt;/li&gt;
&lt;li&gt;check bit가 활성화되어있다면 이를 해당하는 트레이스에 추가&lt;/li&gt;
&lt;li&gt;엔클레이브 실행 재개 전에 해당 비트 초기화&lt;/li&gt;
&lt;li&gt;다음 명령어 실행&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하면 제어, 데이터 흐름 접근 패턴에 대응하는 리스트 두 개를 얻을 수 있다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Usage&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 트레이서가 대상 엔클레이브의 취약한 ECALL을 호출하도록 이를 실행하는 콜백 함수만 구현해주면 비밀 값을 복구하는 모든 과정을 자동화해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프라인 단계에서는 Athena 파이썬 모듈을 호출하여 비밀을 추출한다. 이 단계에서는 단순히 RDI 레지스터에 복구해야할 비밀 값이 무엇인지만 명시해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 대상이 바뀌더라도 콜백함수와 RDI만 바꾸면 되기 때문에 PoC 생성이 더 쉬워진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Alternative Side-Channel Traces&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 제어 채널 공격을 사용해서 대상 프로그램을 모니터링하는데, 다른 소프트웨어 기반의 부 채널 공격도 비슷하게 작동할 수 있다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Space Resolution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 채널 공격은 대부분의 페이지를 관찰 가능하다는 장점이 있지만 그 단위가 페이지이다 보니 세밀한 추적은 힘들다. 하지만 다른 소프트웨어 기반 부 채널 공격은 일부 페이지만 보는 대신, 캐시 라인 단위로 추적이 가능하다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 모든 페이지를 확인하지 않는 부 채널 공격 방법에 대해서도 쉽게 적응할 수 있는데 그 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공격자가 자신이 모니터링하고 있는 메모리 집합이 뭔지 알고있다&lt;/li&gt;
&lt;li&gt;따라서 모든 메모리 접근에 대한 제약 조건을 모니터링된 페이지 집합으로 제한할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 가상 주소 뿐만 아니라 마이크로아키텍처 구조에서 작동하는 부 채널 공격을 지원하도록 확장될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 모니터링되는 캐시 세트의 상태가 어떻게 변하는지 시뮬레이션해야한다. &amp;rarr; 캐시의 동작 원리는 다 알고 있으니 구현이 문제다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Dealing with Noise&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제어 채널 공격과 다른 부 채널 공격의 차이점은, 후자는 노이즈에 취약하는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, 캐시 공격은 FP나 FN의 영향을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 공격들을 여러 번 반복하여 신뢰할 수 있는 정보를 추출하여 유의미한 수치를 내는걸 증명하기 위해 square-and-multiply 알고리즘에 대해 flush-reload 공격을 구현했고, 100개의 트레이스 중에 68개가 완전히 오류가 없었다. &amp;rarr; 3번의 공격 후 majority vote 방식으로 오류가 없는 트레이스를 만들어낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 노이즈가 심한 부 채널 공격에 대해서도 힘들긴 하지만 노이즈 없는 트레이스를 추출할 수 있기 때문에 Athena는 부 채널 공격이 아니더라도 사용될 수 있다.&lt;/p&gt;
&lt;h1&gt;Evaluation&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 섹션에서는 Athena에 초점을 맞춰서 SCASE를 다음에 맞춰 평가한다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;인공적인 트레이스를 사용하여 메모리 트레이스 기반 심볼릭 실행이 잘 수행되는지 평가&lt;/li&gt;
&lt;li&gt;실제 SGX 트레이스를 사용하여 Athena의 성능을 분석&lt;/li&gt;
&lt;li&gt;네 가지 추가적인 현실적인 사례를 통해 Athena를 평가&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Efficiency of Secret Recovery&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 트레이스에 따른 Athena의 angr의 경로 탐색 능력을 평가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀 값에 종속적인 jump table을 통해 제어 및 데이터 흐름 유출을 동시에 보이게 해, SEE에 미치는 영향을 통제되고 정밀한 방식으로 측정할 수 있다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대상 프로그램: 암호학적 대사에 미치는 영향을 보기 위해 square-and-multiply 알고리즘을 구현한 프로그램을 목표로 한다.&lt;/li&gt;
&lt;li&gt;다양한 세밀도(granularity)의 메모리 트레이스를 생성할 수 있는 angr 기반 메모리 트레이싱 도구를 사용&lt;/li&gt;
&lt;li&gt;32GB 메모리와 Intel Core i7-9700K CPU를 사용&lt;/li&gt;
&lt;li&gt;점프 테이블은 16개의 항목을 가지고있고, 각 항목은 버퍼에 서로 다른 숫자 값을 작성하는 함수를 가리킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;세밀도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 분석 단위. Athena에서는 일반적으로 4kB크기의 페이지 단위로 분석을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점프 테이블 실험에서는 다양한 세밀도로 측정을 진행&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 세밀도에 따른 Athena의 64바이트 비밀 값 복구 시간을 나타낸다. 각 데이터 포인트는 서로 다른 virtual random secret을 사용한 10회 실행의 평균값이다. Y축은 log scale이다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb8Uxt/dJMcadu26Kf/n3rthH6Jk2lgnspN6W4iUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb8Uxt/dJMcadu26Kf/n3rthH6Jk2lgnspN6W4iUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb8Uxt/dJMcadu26Kf/n3rthH6Jk2lgnspN6W4iUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb8Uxt%2FdJMcadu26Kf%2Fn3rthH6Jk2lgnspN6W4iUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;172&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무시되는 비트가 많아질수록 (최대 12비트, 4kB 페이지의 오프셋값) 실행시간은 지수함수적으로 증가.&lt;/li&gt;
&lt;li&gt;Athena의 실행 시간은 대부분 path exploration 단계에서 소요되었으며, solving 단계에서의 시간은 무시할 수 있는 수준&lt;/li&gt;
&lt;li&gt;메모리 트레이스에 정보가 많을수록 SEE가 state를 더 효과적으로 잘라낼 수 있기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 비밀 값의 길이에 따른 Athena의 복구 능력을 테스트했다. 마찬가지로 Y축은 로그스케일이다. 또한 이번에는 가이던스가 없는 일반적인 angr의 성능도 측정했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNAGie/dJMcabqyBaz/JvAyL9YGxn1XeEp7hW7cXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNAGie/dJMcabqyBaz/JvAyL9YGxn1XeEp7hW7cXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNAGie/dJMcabqyBaz/JvAyL9YGxn1XeEp7hW7cXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNAGie%2FdJMcabqyBaz%2FJvAyL9YGxn1XeEp7hW7cXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;169&quot; data-origin-width=&quot;594&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가이던스가 없는 경우에는 3바이트 키의 길이에도 불구하고 $10^3$초 내에 복구하는데 실패하였으며, 다른 모델의 경우 길이가 증가함에 따라 시간도 선형적으로 증가하는걸 보였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SGX Traces with Athena&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena를 이용하여 square-and-multiply의 비밀 값을 복구하는걸 평가한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;8GB DRAM을 가진 Intel Core i3-7100T CPU에서 SGX 엔클레이브 실행&lt;/li&gt;
&lt;li&gt;오프라인 단계는 104GB DRAM을 가진 Intel Core i9-12900K CPU에서 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 Athena는 10번의 실험 모두 100% 성공률로 비밀 값의 2048비트를 모두 복구해냈다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;평균 실행 시간: 195.7초&lt;/li&gt;
&lt;li&gt;최소 실행 시간: 194.3초&lt;/li&gt;
&lt;li&gt;최대 실행 시간: 196.8초&lt;/li&gt;
&lt;li&gt;솔빙에 소요된 시간은 10ms 미만&lt;/li&gt;
&lt;li&gt;DRAM 사용량은 최대 62GB&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Recovering AES S-Box Keys&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenSSL의 AES S-Box를 대상으로 실험을 진행하고 AES 마스터 키를 복구해냈다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;AES는 보통 AES라운드 또는 알고리즘의 단일 단계에 대해 미리 계산된 값을 가지고 있는 lookup table을 사용한다. (이걸 S-Box)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 S-Box에서 조회되는 값은 마스터 키로부터 파생된 라운드 키에 의존하기 때문에 여기에 접근하는 것은 곧 마스터 키에 대한 정보를 유출한다는걸 의미.&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;S-Box는 크기가 256바이트이므로 매우 세밀하게 트레이스를 추출해야하며, 여러 라운드 키의 조합으로부터 마스터키를 재구성해야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpsq3m/dJMcahEgaNq/Fq0KilquczMCmT3VmLBCa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpsq3m/dJMcahEgaNq/Fq0KilquczMCmT3VmLBCa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpsq3m/dJMcahEgaNq/Fq0KilquczMCmT3VmLBCa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpsq3m%2FdJMcahEgaNq%2FFq0KilquczMCmT3VmLBCa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;619&quot; height=&quot;223&quot; data-origin-width=&quot;619&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 결과는 정밀도에 따른 Athena 실행 시간과 복구율을 나타낸다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Athena가 데이터 흐름 트레이스만 추출했을 때, 결과는 동일하게 유지.&lt;/li&gt;
&lt;li&gt;하지만 제어 흐름만 추출했을 때 제어 흐름이 어떤 비밀 값에도 의존하지 않기 때문에 복구에 실패.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 복구되지 않은 나머지 비트들에 대해서 어느 비트가 복구되지 않았는지 알 수 있기 때문에 그 부분에만 bruteforce 기법을 시도해볼 수 있음.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Exploiting OpenSSL&amp;rsquo;s Binary Extended Euclidean Algorithm&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 타겟은 OpenSSL의 BEEA (Binary Extended Euclidean Algorithm)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BEEA는 RSA, DSA&amp;lt; ECDSA와 같은 암호화 알고리즘의 키 생성 과정에 사용되는 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 구체적인 비밀 값에 따라 수 시간에서 수 일 내에 비밀 입력값을 복구했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신, 데이터 흐름 트레이스만, 또는 제어 흐름 트레이스만 사용했을 때 두 경우 모두 비밀 값을 복구하지 못했으며, 이를 통해 두 유형의 트레이스를 결합하는 것이 유익하다는 점을 확인할 수 있음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btrf3a/dJMcagejWaS/TAY6Y8kZ2vkmh9Y8rHqGsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btrf3a/dJMcagejWaS/TAY6Y8kZ2vkmh9Y8rHqGsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btrf3a/dJMcagejWaS/TAY6Y8kZ2vkmh9Y8rHqGsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtrf3a%2FdJMcagejWaS%2FTAY6Y8kZ2vkmh9Y8rHqGsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;622&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;8-a) SEE에서 동시에 활성화된 state의 수. 1에서 10 사이를 진동하는 모습을 확인할 수 있음.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누출된 정보에 의해 추가된 제약 조건이 state를 줄이는데 도움이 되는걸 확인할 수 있었음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;8-b) 그렇게 줄어든 state는 탐색 횟수가 증가함에 따라 꾸준히 증가함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MmiHy/dJMcaad6Jbb/d3Mlr1wuxDSaedCYfFiCA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MmiHy/dJMcaad6Jbb/d3Mlr1wuxDSaedCYfFiCA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MmiHy/dJMcaad6Jbb/d3Mlr1wuxDSaedCYfFiCA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMmiHy%2FdJMcaad6Jbb%2Fd3Mlr1wuxDSaedCYfFiCA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;239&quot; data-origin-width=&quot;789&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간에 지남에 따라 데이터 흐름의 제약 조건의 양이 증가하는걸 확인할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Attacking RC4 KSA&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부 채널 노출을 이용해서 RC4 KSA에 대한 전체 키 복구를 보여주는건 처음이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Athena는 lookup table 접근에 의해 발생하는 누출로부터 비밀 키를 자동으로 북구한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RC4의 비밀 값을 추출하기 위해서는 매우 세밀한 누출 정보가 필요하기 때문에 바이트 단위의 데이터 흐름 트레이스는 수동으로 추출했지만, 키를 복구하는 과정은 자동으로 이루어졌다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Intel Core i7-1165G7 CPU와 16GB RAM의 일반 노트북에서 키를 100% 복구&lt;/li&gt;
&lt;li&gt;평균 복구 시간: 4분 35초&lt;/li&gt;
&lt;li&gt;솔빙 단계에서 소요된 시간: 10ms 미만&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Recovering Poker Cards&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안적인 타겟 (암호 키) 뿐만 아니라 복잡한 데이터 구조를 가진 소프트웨어에서도 비밀 정보를 캐낼 수 있음을 보여줬다.&lt;/p&gt;
&lt;h1&gt;Discussion&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Related Work&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Machine Learning for Side-Channel Attack Automation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머신러닝 기반 방식은 확률론적이고 정확도가 낮은 반면에, Athena는 결정론적이며 정확도도 매우 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 부 채널 공격은 환경마다 노이즈가 다른데, ML은 단순히 학습한 데이터를 가지고 추론하여 실제 트레이스에서는 무너지는 반면에, Athena는 심볼릭 실행을 따르므로 더 robust하다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Symbolic Execution for Side-Channel Attack Automation&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;phan et al. 은 부 채널 누출을 최대화하기 위한 입력값 생성을 자동화하는 방식&lt;/li&gt;
&lt;li&gt;Athena는 트레이스를 보고 비밀 값을 재구성하는 방식.&lt;/li&gt;
&lt;li&gt;Dubrova는 딥러닝 기반의 power 분석과 SAT 솔빙을 결합하여 AES-128만을 공격.&lt;/li&gt;
&lt;li&gt;Athena는 일반적인 바이너리의 메모리 접근 트레이스로 제약 조건 생성을 자동화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Differential Fault Analysis&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 방식은 프로그램이 루프를 돌면서 같은 조건에 따라 서로 다른 메모리 접근 패턴을 보일때만 유효.&lt;/li&gt;
&lt;li&gt;Athena는 루프가 아니라도 한 번의 메모리 접근이 비밀 값과 연관된 제약 조건을 만들어낼 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Limitations&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;화이트박스를 가정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;바이너리나 코드가 있어야한다는 한계. 하지만 Kerckhoff의 원칙에 따르면 공격자는 모르는 게 없기때문에 정당한 보안 모델.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SAT 솔버의 물리적 한계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암호 알고리즘 자체가 부 채널 정보와 키 사이의 관계를 수학적으로 복잡하다면 타임아웃이 발생함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 의존성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정보 누출이 없다면 비밀 키를 복구할 수 없다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단일 실행의 한계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 Athena는 한 번 실행해서 나온 트레이스를 분석하는데 최적화되어있다.&lt;/li&gt;
&lt;li&gt;여러 번 입력을 바꾸는 Interactive Attack은 수동 작업이 많이 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부 채널 공격으로부터 비밀 키를 추론하는 SCASE는 부 채널 트레이스를 통해 SEE를 가이드 하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 선보이기 위해 SGX 엔클레이브를 타겟으로 하는 PoC 구현체인 Athena를 배라했으며 여러 알고리즘에 대해서 비밀 키를 복구하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 뿐만 아니라 다른 복잡한 구조의 알고리즘에서도 값을 복구할 수 있음을 보였으며 이 기술이 대상 어플리케이션에 대한 깊은 이해 없이 비밀 값을 복구하는데에 있어 실용적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부 채널 공격이 어떻게 이루어지는지, 또 어떻게 심볼릭 실행을 사용하는지 알 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic에서는 솔버가 퍼징의 원리를 적용했다면 SCASE는 부 채널 트레이스를 통해 솔버에게 정확한 제약 조건 값을 줌으로써 path explosion을 방지했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 퍼징시에 multi-byte 조건문을 통과하기 힘든데, 이 때 트레이스를 제공하는걸로도 우회가 가능할거같다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>보안/논문 리뷰</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/11</guid>
      <comments>https://pumisj.tistory.com/11#entry11comment</comments>
      <pubDate>Sun, 3 May 2026 22:42:02 +0900</pubDate>
    </item>
    <item>
      <title>[논문 정리] FUZZOLIC: Mixing fuzzing and concolic execution</title>
      <link>https://pumisj.tistory.com/10</link>
      <description>&lt;h1&gt;서론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 Fuzzolic을 공부하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic은 Fuzzer와 Symbolic execution을 합친 hybrid concolic executor로 AFL++같은 퍼저의 방식을 일부 채용하면서 symbolic execution의 방법론을 따르는 기법이다.&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이번 글에서는 Fuzzolic의 아키텍처, 구현 방식에 대해 정리해보았다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;참고 자료&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-path-to-node=&quot;3,0,0&quot;&gt;Borzacchiello, L., Coppa, E., &amp;amp; Demetrescu, C. (2021). FUZZOLIC: Mixing fuzzing and concolic execution. &lt;/span&gt;&lt;span data-path-to-node=&quot;3,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;3,0,2&quot;&gt;&lt;span&gt;Computers &amp;amp; Security, 108, 102368. &lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;3,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1&gt;Introduction&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어를 자동으로 분석하는 기법의 양극단에는 coverage-guided fuzzing과 symbolic execution이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SE의 변형인 concolic execution은 퍼저와 유사하게 branch condition을 수집하며 한 방향으로 탐색을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 부정하여 다른 길을 탐색하는 방법은 퍼저보다 실행 속도는 느리지만, solver를 이용하여 실행 자체를 더 적게할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 퍼징과 SE을 결합한 hybrid fuzzing은 심볼릭 실행의 확장성으로 인해 일반 퍼징보다 더 느리다. 따라서 탐색중 심볼릭 실행 속도를 높히는 것이 중요하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concolic exploration은 다음 두 단계로 이루어진다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;emulation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램을 실행하며 symbolic expression과 query들을 수집하는 단계&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;reasoning
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;executor가 해당 query들을 최적화하고 SMT solver을 통해 심볼릭 인풋을 만족시킬 수 있는 값을 찾아내 새로운 인풋을 만들어내는 단계&lt;/li&gt;
&lt;li&gt;여기서 만들어진 인풋값은 coverage-guided fuzzer의 입력값으로 쓰인다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 논문에서는 emulation, reasoning 두 단계의 성능을 높히는 시도를 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;emulation 단계에서는 새로운 QEMU에서 실행되는 concolic framework인 fuzzolic을 만들었으며, reasoning 단계에서는 기존 SMT solver의 높은 비용문제를 해결하기 위해 mutation을 이용한 solver인 Fuzzy-SAT을 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT의 잠재력을 입증하기 위해 해당 기능을 바이너리 기반 concolic framework인 Fuzzolic, QSYM과 소스코드 기반 concolic excutor인 SYMCC에 통합했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험은 다음과 같이 요약될 수 있다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Fuzzolic의 심볼릭 표현과 쿼리를 만드는 효율성을 평가하여 그 성능을 바이너리 코드를 분석하는 QSYM과 SYMQEMU와 비교했으며, 또한 최근 릴리즈된 소스코드가 필요한 concolic 실행기인 SYMCC와도 비교했다.&lt;/li&gt;
&lt;li&gt;Fuzzy-SAT와 SMT 솔버인 Z3 그리고 근사 솔버인 JFS를 QSYM이 만든 쿼리로 비교했다. 그 결과 fuzzy-sat은 속도와 해결 효율성 측면에서 좋은 trade off를 보여준다. (성능이 좋음)&lt;/li&gt;
&lt;li&gt;fuzzyolic과 fuzzy-sat를 다른 최신 퍼징 도구들과 비교해봤을때, 다른 퍼저들보다 더 높은 코드 커버리지를 달성했고 실제로 리눅스 배포버전에서 취약점을 발견했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Background&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;concolic execution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concolic 실행의 문제는 한 인풋에 대한 탐색이 끝났을 때 다른 실행으로 분석을 다시 시작해야한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QSYM은 심볼릭 상태만 유지하고 구체적인 상태는 CPU에 맡겨 프로그램의 실행 시간을 줄이는 DBI 방법을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 QSYM에서 에뮬레이팅 시간을 더 줄이는 source-based instrumentation 접근방식으로 발전시킨 SYMCC와, 바이너리 코드를 분석하는 SYMQEMU도 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;approximate constraint solving&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path constraint가 너무 복잡해서 새로운 경로 조건이 추가됐을 때 해가 없거나 추론하기에 시간이 부족한 경우가 있다. QSYM은 이 경우 앞선 path constraint를 무시하고 오로지 이번 제약 조건만 만족하는 해를 찾는 optimistic solving 방식을 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;hybrid fuzzing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커버리지 기반 grey-box fuzzing 퍼저는 어떠한 상수값(magic number)을 체크하는 로직이 있을 때 무작위로 이를 넘어가기 힘들다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 퍼징과 심볼릭 실행을 결합한 hybrid fuzzing이 등장했다.&lt;/p&gt;
&lt;h1&gt;Efficient generation of symbolic queries&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행은 hybrid fuzzing으로 사용될 때 매우 효율적이다. 하지만 하나의 경로를 분석하는 오버헤드가 너무 커서 이 방식을 확장하기 힘들다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 섹션에서는 Fuzzolic의 내부 작동 원리를 제시하며, symbolic expression과 쿼리를 생성하기 위한 방식을 살펴보고자 한다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Architecture&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TYVKI/dJMcacJHTsy/m4aAGIQ8oBdPK2Ov7koke0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TYVKI/dJMcacJHTsy/m4aAGIQ8oBdPK2Ov7koke0/img.png&quot; data-alt=&quot;Fuzzolic의 아키텍처&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TYVKI/dJMcacJHTsy/m4aAGIQ8oBdPK2Ov7koke0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTYVKI%2FdJMcacJHTsy%2Fm4aAGIQ8oBdPK2Ov7koke0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;109&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic의 아키텍처&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 아키텍처가 달성하고자 하는 목표는 다음과 같다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deployability
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fuzzolic은 심볼릭 실행을 하는데 소스코드를 필요로 하지 않는다.&lt;/li&gt;
&lt;li&gt;동적 바이너리 번역기인 QEMU를 사용하기 때문에 다양한 어플리케이션과 상용 소프트웨어를 대상으로 바이너리 코드를 분석하고 실행할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Efficiency
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fuzzolic은 런타임중에 symbolic expression과 branch query를 빌드하기 위해 QEMU를 활용하여 instrumentation 코드를 삽입하는 Just-In-Time (JIT) compilation을 활용한다.&lt;/li&gt;
&lt;li&gt;콘콜릭 실행의 JIT compilation는 이미 QSYM에 적용되었으며, 주어진 기계어에 대해서 interpretation방식으로 symbolic expression을 만드는 성능보다 뛰어나다는걸 보여줬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Decoupling
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fuzzolic은 symobolic expression을 생성하는 QEMU기반 컴포넌트(tracer)로부터 이 수식을 분석하여 해를 찾는 컴포넌트(solver)을 분리했다.&lt;/li&gt;
&lt;li&gt;왜 이렇게 했냐면 QEMU같은 DBT 프레임워크가 분석을 할 때 사이드 이팩트를 만들어낼 것 같은 외부 라이브러리 (Z3 solver같은거)를 사용하지 못하도록 했기 때문이다.&lt;/li&gt;
&lt;li&gt;fuzzolic은 이미 설계에서 저런 제약을 견딜 수 있도록 tracer와 solver를 분리해놨다&lt;/li&gt;
&lt;li&gt;tracer는 QEMU에서 돌아가면서 symbolic expression과 쿼리를 간략하게 표현해 solver도 사용할 수 있는 두 개의 공유 메모리에 저장한다.&lt;/li&gt;
&lt;li&gt;solver는 이 수식을 Z3 언어로 변환하고 solver 백엔드에 전달하여 새로운 인풋을 만들어낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Portability and extensibility
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fuzzolic은 호환성이 좋은 QEMU를 기반으로 하고있기 때문에 다른 시스템으로 이식될 수 있다.&lt;/li&gt;
&lt;li&gt;실제로 플랫폼 독립적인 QEMU에 사용되는 TCG intermediate representation(IR)를 분석했다&lt;/li&gt;
&lt;li&gt;또한 하나의 어플리케이션의 범주를 넘어 OS를 에뮬레이팅할 수 있게 확장될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Just-in-time-binary instrumentation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic의 핵심 요소는 tracer다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 QEMU가 바이너리 어플리케이션을 실행하는 흐름도다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZnBz4/dJMcahqHB5k/MKtTLRq75zOJzSNJyIa8tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZnBz4/dJMcahqHB5k/MKtTLRq75zOJzSNJyIa8tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZnBz4/dJMcahqHB5k/MKtTLRq75zOJzSNJyIa8tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZnBz4%2FdJMcahqHB5k%2FMKtTLRq75zOJzSNJyIa8tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;262&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다음 basic block을 실행할 때 이미 그 기계어가 캐싱이 안되어있다면 해당 블럭은 처음 실행되었다는 뜻이므로 B단계로 넘어가고 아니면 C단계로 넘어간다&lt;/li&gt;
&lt;li&gt;basic block을 컴파일하고 JIT 캐시에 저장한 뒤 C단계로 넘어간다&lt;/li&gt;
&lt;li&gt;QEMU는 basic block의 기계어를 실행하고 다음 basic block의 주소를 가져와 다시 A단계로 이동한다. 단, 이 단계에서 시스템 콜이 실행되면 QEMU는 현재 블럭을 실행하기 전에 OS와 상호작용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;symbolic expression과 쿼리를 생성하는 instrumentation은 컴파일 단계에서 이루어지기 때문에 실행중에 그것들을 생성하는 ANGR이나 KLEE와는 다르게, fuzzolic은 분석 작업의 일부를 basic block 실행에서 컴파일 시간으로 옮겨질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic에 의해 추가된 심볼릭 인스트루멘테이션은 두 가지 형태로 나뉜다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Inline-only instrumentation&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;basic block에 하나 이상의 TCG 명령어가 있다면, fuzzolic은 외부 함수 호출 없이 symbolic state를 조작하는 코드를 삽입한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 64비트 주소를 rax에서 rbx로 옮기는 명령어가 있다고 해보자&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;movq %rbx, %rax&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 QEMU에서 실행할 수 있게 TCG IR로 변환하면 다음과 같다&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;mov_i64 tcg_reg_2, tcg_reg_1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 fuzzolic은 심볼릭 표현을 추적할 수 있도록 tcg_reg_2와 tcg_reg_1을 추적하는 레지스터를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;mov_i64 tcg_reg_symbolic_2, tcg_reg_symbolic_1
mov_i64 tcg_reg_2, tcg_reg_1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 할 경우 tcg_reg_2의 값이 어디서부터 유래했는지 tcg_reg_symbolic_2를 통해 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Helper-based instrumentation&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;basic block에 하나 이상의 TCG 명령어가 있다면, fuzzolic은 미리 정의한 helper 함수를 호출함으로써 symbolic state를 조작하는 코드를 삽입한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 위의 명령어는 다음과 같이도 인스트루멘테이션될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;call fuzzolic_move_reg        # helper
            $0x0, $0,               # flags
            tcg_reg_symbolic_1,     # arg 1
            tcg_reg_symbolic_2_id   # arg 2
mov_i64 tcg_reg_2, tcg_reg_1  # original code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 똑같이 tcg_reg_symbolic1에서 tcg_reg_symbolic_2로 심볼릭 표현의 주소를 전달하는 역하을 하지만 fuzzolic_move_reg라는 함수를 호출하여 구현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inline-only instrumentation은 외부 코드를 호출하지 않기 때문에 효율적이지만, 복잡한 연산을 처리할 때 에러를 일으킬 가능성이 높고, 제대로 작동하기 위해서는 분기 코드를 포함하지 않아야한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Analysis modes&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qPYiW/dJMcadBJIzA/JpnRoNkAmCfsDTr9ntSKmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qPYiW/dJMcadBJIzA/JpnRoNkAmCfsDTr9ntSKmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qPYiW/dJMcadBJIzA/JpnRoNkAmCfsDTr9ntSKmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqPYiW%2FdJMcadBJIzA%2FJpnRoNkAmCfsDTr9ntSKmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;319&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic은 세 가지 분석 모드를 지원한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A모드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;심볼릭 인스트루멘테이션을 하지않고, 따라서 새로운 심볼릭 표현을 만들거나 분기 쿼리를 solver에 전달하지도 않는다.&lt;/li&gt;
&lt;li&gt;이 모드는 실제 값이 있는 데이터를 처리하는 베이직 블럭, 또는 잘 알려져있는 함수(예를 들어 strcmp)를 처리할 때 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;B모드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;B모드는 베이직 블록이 심볼릭 상태에 미치는 side effect를 추적하기 위해 심볼릭하게 인스트루멘테이션 되는데, 단 코드 커버리지에는 별로 중요하지 않은 블럭을 처리하도록 설계되었다.&lt;/li&gt;
&lt;li&gt;이 모드는 fuzzolic이 메인 프로그램에 대한 코드커버리지를 증가시키는 인풋을 생성할 때 사용하지만, 표준 라이브러리 실행에 대해서 테스트하는건 아니다.&lt;/li&gt;
&lt;li&gt;예를 들어 realloc 함수의 side effect는 추적을 해야하지만, 이 함수의 커버리지를 높힐 필요는 없다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;C모드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중요한 basic block을 처리할 때 사용되며 심볼릭 인스트루멘테이션과 브랜치 쿼리를 모두 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임중에 세 분석 모드를 관리하기 위해서 fuzzolic은 두 개의 JIT 캐시를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 캐시는 인스트루멘테이션이 없는 basic block을 저장하도록, 다른 하나는 인스트루멘테이션이 된 basic block을 저장하도록 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 함수라도 콘크리트값만 있는 프로그램의 시작부분과, 심볼릭 수식이 삽입된 후에 시작부분이 다시 실행될 수 있으므로 하나의 basic block은 두 개의 JIT 캐시에 저장될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 개의 모드는 자동적으로 전환되는데, A모드는 프로그램의 시작부터 계산에 심볼릭 데이터를 추가하는 시스템 콜을 호출할때까지 실행되며, 그 뒤에는 모두 C모드로 실행하고 특정 경우에만 A, B 모드로 전환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 경우라 함은 A모드의 경우 라이브러리 함수를 사용할 때 미리 정의한 API 모델이 있다면 호출되고, API 모델이 없다면 B모드를 실행한다. 두 경우 모두 함수가 끝나면 다시 C모드로 돌아온다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Interaction between tracer and solver&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic은 두 개의 컴포넌트로 이루어져있으며, 두 개의 컴포넌트는 expression pool과 query pool 두 개의 공유 메모리를 사용하여 데이터를 주고받는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tracer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;tracer는 QEMU 기반의 컴포넌트로 프로그램을 분석하여 심볼릭 표현과 쿼리를 두 개의 메모리에 생성한다&lt;/li&gt;
&lt;li&gt;solver에서 메모리 사용량을 최소화하고 parsing 처리를 쉽게 하기 위해서 심볼릭 표현과 쿼리를 두 개의 최적화된 C 구조체로 표현한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;solver
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;solver frontend
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프론트엔드에서는 쿼리 풀에 새로운 쿼리가 들어오면 쿼리에 포함된 모든 표현을 번역하여 Z3 쿼리로 번역한다.&lt;/li&gt;
&lt;li&gt;번역된 쿼리는 몇몇 최적화를 거쳐서 solver backend로 보내진다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;solver backend
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백엔드의 요소는 FUZZY-SAT이나 Z3가 될 수 있으며 해당 쿼리를 만족시키는 값을 추론한다.&lt;/li&gt;
&lt;li&gt;이렇게 찾아낸 식은 fuzzolic이 새로운 인풋을 만드는데에 사용된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;solver는 tracer와 동시에 실행되며 두 개의 서브 컴포넌트로 나뉜다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 실행 분석이 끝나면 tracer는 풀에 새로운 쿼리가 없음을 의미하는 fack query를 보내고, 남은 쿼리를 모두 처리한 뒤에 종료된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Hybrid fuzzing setup&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w05ay/dJMcacQrlVS/47pJC0rVgW6ih6788TwxH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w05ay/dJMcacQrlVS/47pJC0rVgW6ih6788TwxH1/img.png&quot; data-alt=&quot;Fuzzolic이 hybrid fuzzing모드를 실행중일 때의 셋업&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w05ay/dJMcacQrlVS/47pJC0rVgW6ih6788TwxH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw05ay%2FdJMcacQrlVS%2F47pJC0rVgW6ih6788TwxH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;777&quot; height=&quot;324&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic이 hybrid fuzzing모드를 실행중일 때의 셋업&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하이브리드 퍼징은 심볼릭 탐색을 수행하는 인스턴스 fuzzolic 한 개와 서로 다른 실행 설정으로 구성된 coverage-guided fuzzer 두 개로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzer중 하나는 master, 하나는 slave로 명명되며 fuzzolic인스턴스는 아무 coverage-guided fuzzer와 호환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 퍼저가 공통적으로 제공하는 동기화 절차를 통해 퍼저가 자신의 입력 큐를 파일 시스템상에 유지하게 하여 병렬로 실행중인 다른 퍼저들이 접근할 수 있도록 하고, 다른 퍼저들의 입력 큐와 주기적인 동기화를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다른 퍼저들에서 만들어낸 코드커버리지를 증가시키는 인풋을 자신도 사용할 수 있도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic은 slave 퍼저의 인풋 큐에서만 인풋을 가져오는데, 이는 slave가 결국 master의 인풋을 모두 가지고있기 때문에 중복 계산을 피하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concolic 실행기는 루프와 같이 이미 실행한 분기 명령어를 만날 때 반복적인 연산을 줄이기 위해서 현재 명령어와 이전 심볼릭 분기 명령어를 해싱하여 bitmap에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 명렁어와 이전 심볼릭 분기 명령어를 해싱하는 이유는 해당 분기문의 호출 문맥을 고려하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 할 경우 fuzzolic은 분기문에 도달했을 때 해당 해시값이 bitmap에 이미 있으면 쿼리를 스킵하고 없다면 쿼리를 solver에 전달하고 bitmap을 업데이트할 수 있다.&lt;/p&gt;
&lt;h1&gt;Efficient solving of symbolic query&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근의 커버리지 가이드 퍼저들은 단순히 코드 커버리지를 늘리는걸 넘어서 프로그램의 동작을 바탕으로 인풋을 새로 만들어낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘콜릭 실행기는 설계상 심볼릭 표현같이 프로그램 동작에 대한 정확한 묘사를 구축하지만, 전적으로 비용이 많이 드는 SMT solver를 이용하며 이 솔버는 보통 black box로 취급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본문에서는 콘콜릭 실행기가 심볼릭 표현으로부터 학습하고 이를 바탕으로 간단하지만 빠른 인풋 변환을 구현함으로써 SMT solver에 의존하지 않고 쿼리를 풀어낼 수 있음을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는 다음과 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 쿼리 $\neg b \wedge \pi$ 에서 인풋 $i$는 콘콜릭 탐색에 의해서 $\pi$를 만족시킨다. 따라서 $\neg b$와 $\pi$를 만족하도록하는 새로운 테스트케이스 $i'$를 찾는걸 목표로 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reasoning primitives for concolic execution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SMT솔버는 보통 다앙한 응용 맥락에서 적용되어 방정식을 풀 수 있는 solving primitive를 제공하는데, QSYM, FUZZOLIC, SymCC같은 concolic 실행기에서는 필수적인 primitive만 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT은 다음 primitive를 가지고있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Solve($e, &amp;pi; , i, opt$)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$e \wedge \pi$를 만족하는 입력값을 찾아 리턴하는 기능이다.&lt;/li&gt;
&lt;li&gt;$e$는 현재 분기 조건인 b를 뒤집은걸 의미한다. 즉 $e = \neg b$이다.&lt;/li&gt;
&lt;li&gt;opt 플래그는 입력값을 찾는데 실패했을 경우 optimistic solving을 수행할지 여부를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SolveMax($e, &amp;pi; , i$) / SolveMin($e, &amp;pi; , i$)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\pi$를 만족하면서 $e$를 최대 또는 최소화 시키는 입력값을 찾는다. -&amp;gt; 메모리상 가질 수 있는 최대 최솟값을 찾는다&lt;/li&gt;
&lt;li&gt;탐색 범위를 유지하기 위해 심볼릭 메모리 주소 $e$를 구체화하기 전에 사용한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SolveAll(e, &amp;pi; , i)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SolveMin과 Max를 결합하여 추론 과정에서 확인된 중간 할당값을 도출한다.&lt;/li&gt;
&lt;li&gt;점프테이블에 접근하는 심볼릭 메모리 주소가 존재하거나 명령어 포인터(ip)가 탐색중에 심볼릭해지는 상황에서 사용된다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT의 이런 primitive과 SMT 솔버와 대응하는 기능을 차별화하는 두 가지 주요 측면이 있다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Fuzzy-SAT은 근사 솔버이다. 따라서 $e \wedge \pi$가 unsatisfiable하다는걸 증명할 수 없다. 비슷하게 해당 식의 전역 최대, 최소값을 찾지 못하거나 가능한 모든 값에 대응하는 할당값을 전부 나열하지 못할 수 있다.&lt;/li&gt;
&lt;li&gt;Fuzzy-SAT은 테스트중에 프로그램의 심볼릭 탐색을 유도하는 테스트케이스 $i$를 만드는 콘콜릭 엔진이 필요하다. 이는 fuzzy-SAT이 $e$와 $\pi$를 분석할 때 학습한걸로 인풋을 mutation하기 때문이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Overview&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT은 analysis, expression metadata, reasoning 총 세 가지 메인 블럭으로 구성되어있다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;analysis 단계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;심볼릭 표현을 분석해서 reasoning 단계에서 사용가능한 지식을 추출하는 과정&lt;/li&gt;
&lt;li&gt;입력 $i$중에서 어떤 입력 바이트 $i_k$가 수식에 관여하는지 확인한다&lt;/li&gt;
&lt;li&gt;또한 input-to-state 관계를 감지하여 mutation 단계에서 사용할 수식 내 상수를 수집한다&lt;/li&gt;
&lt;li&gt;현재 수식이 이전에 분석 컴포넌트에서 처리된 다른 수식들과 입력 바이트를 공유하는지 여부를 감지하고, 이를 통해서 reasoning 단계에서 해당 바이트들을 변이시킬 때 발생할 수 있는 충돌 지점을 찾아낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;expression metadata
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;analysis 단계에서 처리된 수식을 유지하는데 사용된다&lt;/li&gt;
&lt;li&gt;이 메타데이터는 수식과 관련된 다양한 유형의 속성을 빠르게 조회할 수 있도록 최적화된 데이터 구조 집합으로 구현되어있다&lt;/li&gt;
&lt;li&gt;이는 reasoning 단계에서 업데이트되며, analysis, reasoning 모두에서 조회된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;reasoning 단계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fuzzy-SAT이 파악한 지식으로 테스트 케이스를 효과적으로 퍼징하고 할당값을 생성한다&lt;/li&gt;
&lt;li&gt;mutation engine을 이용해서 수식 $e$에 관여하는 입력 바이트에 대해 변환을 수행하고, $e$와 $\pi$를 만족하는 할당값을 찾거나 $\pi$를 만족하면서 $e$를 최대, 최소화하는 할당값을 찾는다.&lt;/li&gt;
&lt;li&gt;이 단계에서 $e$에 대한 할당값을 찾았지만 $\pi$를 만족하지 못한다면 multi-goal strategy를 수행한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIFLbA/dJMcabYjE0W/8ytSPaGGmfbApL0vVk5kA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIFLbA/dJMcabYjE0W/8ytSPaGGmfbApL0vVk5kA0/img.png&quot; data-alt=&quot;Fuzzy-SAT의 solve primitive 수도코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIFLbA/dJMcabYjE0W/8ytSPaGGmfbApL0vVk5kA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIFLbA%2FdJMcabYjE0W%2F8ytSPaGGmfbApL0vVk5kA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;523&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzy-SAT의 solve primitive 수도코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solve함수는 다음과 같이 진행된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;$\pi$와 $e$를 분석해서 reasoning 단계에서 사용될 수 있는 정보를 expression metadata $M$에 추가한다&lt;/li&gt;
&lt;li&gt;다음은 reasoning으로, Mutate함수로 입력값을 조금 변형했을 때 $\pi$와 $e$를 모두 만족시키는 할당값 $a$를 찾아낸다면 즉시 반환한다&lt;/li&gt;
&lt;li&gt;만약 $a$를 찾지 못했지만 일부 할당값 집합 SA가 $e$는 만족시킨다면 multi-goal 단계로 넘어간다
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;PickBestAssignment함수를 사용하여 SA중에서 $\pi$내의 가장 많은 수식을 만족하는 후보 할당값 a를 선택한다&lt;/li&gt;
&lt;li&gt;FixInputBytes함수로 a에 의해 할당된 입력 바이트를 고정해서 이후 Mutate 호출이 이 바이트를 변경하지 못하도록 한다&lt;/li&gt;
&lt;li&gt;이후에 analysis 단계에서 e와 충돌하는 수식 e&amp;rsquo;를 고려하여 변형을 진행한다&lt;/li&gt;
&lt;li&gt;이 과정은 $e\wedge\pi$가 만족되지 않고, 변형했을 때 e&amp;rsquo;에 대해 최소 하나 이상의 할당값을 SA에 반환하며, 고려할 충돌 수식이 남아있는 동안 반복된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;opt값이 true일 때 mutation engine이 찾은 $e$는 만족하지만 $\pi$는 만족하지 않는 마지막 후보 할당값을 반환한다. 이 경우 $\pi$의 제약조건을 무시하고 e의 정보만 사용하여 입력 바이트를 만들어낸다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT의 multi-goal strategy는 정확도를 희생하고 확장성을 얻기 위해 백트래킹을 하지 않는 greedy한 방식을 채택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘콜릭 엔진이 생성하는 많은 쿼리가 어차피 unsatisfiable하기 때문에 이 전략의 복잡성을 높이는건 fuzzy-SAT에 큰 부담을 줄 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Analyzing symbolic expression&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;analyze함수의 세부 사항&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;expression metadata M에 저장되는 정보는 다음과 같다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e에 포함되는 입력 리스트&lt;/li&gt;
&lt;li&gt;e가 인풋 그룹인지&lt;/li&gt;
&lt;li&gt;e의 하위 수식을 재귀적으로 고려했을 때 e에 포함된 입력 그룹들의 리스트&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Detecting inputs and input groups&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 인풋 바이트 $i_k$가 수식에 관여하는지 확인하고, 이 바이트가 어떻게 그룹화 되어있는지 평가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 해당 수식이 비트를 서로 섞지 않고 입력 바이트나 상수를 concatenation(+)한 것과 동일한지 체크한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수식 $i_1 \ i_0$은 입력 $i_0$와 $i_1$을 포함하며, 이 바이트들의 비트가 서로 섞이지 않고 단순히 덧붙여진 것이므로 입력 그룹이다&lt;/li&gt;
&lt;li&gt;수식 $0 \ i_0$은 입력 $i_0$를 포함하며, $i_0$에 대한 제로 확장(zero-extend) 연산이므로 1바이트 입력 그룹이다.&lt;/li&gt;
&lt;li&gt;수식 $i_1 + i_0$은 입력 $i_0$와 $i_1$을 포함하지만, $i_0$의 비트가 $i_1$의 비트와 산술적으로 더해졌으므로 입력 그룹이 아니다.&amp;nbsp; (여기서는 산술연산으로 사용됨)&lt;/li&gt;
&lt;li&gt;수식 $(0 \ i_0) + (i_1 \ll 8)$은 $i_0$와 $i_1$을 포함하며, 이 수식이 $i_1 \ i_0$과 동일하므로 입력 그룹이다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Detecting uniquely defined inputs&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 바이트에 대한 결정적인 정보중 하나는, 해당 바이트를 포함하는 equality constraint (==연산)으로 인해 그 값이 단일 값으로 고정되었는지를 아는 것이다. 이 값은 fuzzing하지 않는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 uniquely defined라고 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수식 e가 주어졌을 때&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 e가 등치 제약 조건이고, 한 피연산자가 입력그룹이면서 나머지가 상수라면 e가 $\pi$에 추가될 때 해당 그룹의 바이트는 e에 의해 uniquely defined이다.&lt;/li&gt;
&lt;li&gt;$\pi$에 포함된 제약 조건이 어떤 입력을 uniquely defined라고 한다면 e 내의 해당 입력도 uniquely defined이다&lt;/li&gt;
&lt;li&gt;e 내의 입력그룹을 구성하는 모든 입력이 $\pi$의 제약 조건에 의해 uniquely defined라면 해당 입력 그룹 자체가 uniquely defined이다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $i_1 + i_0 == 0xABCD$ 에서 $i_0$과 $i_1$은 uniquely defined이다. (더하기 연산이 아니라 concatenation 연산)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Detecting input-to-state branch conditions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e가 input-state correspondence를 갖는 피연산자를 최소 하나 이상 포함하는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 다음 조건을 사용하여 판단한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;e가 $e&amp;rsquo; \ op_{cmp} \ e&amp;rsquo;&amp;rsquo;$ 패턴과 일치해야한다. $op_{cmp}$는 비교연산자이다&lt;/li&gt;
&lt;li&gt;피연산자중 하나가 입력 그룹이어야한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e가 boolean negation일 때 Fuzzy-SAT는 재귀적으로 subexpression( !(a and b) 같은 수식) 을 분석한다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $10 &amp;ge; i_1 + i_0$에서 &amp;ge;가 비교연산자이고 $i_1 + i_0$가 입력 그룹이므로 input-state 분기 조건이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Detecting interesting constants&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e에서 reasoning 단계에서 사용될 상수를 찾아내고, mutation과정에서 사용할 사전을 동적으로 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 패턴이 감지되면 e에 의해 수행되는 계산의 semantic을 바탕으로 해당 상수의 variant를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $i_1 \oplus 0xF0 == 0x0F$를 분석할 때, Fuzzy-Sat은 상수 &lt;code&gt;0xF0&lt;/code&gt;, &lt;code&gt;0x0F&lt;/code&gt;, 그리고 &lt;code&gt;0xFF&lt;/code&gt;를 수집한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Detecting range constraints&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e가 range constraint인지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, e&amp;rsquo;가 입력 그룹이고 e&amp;rsquo;&amp;rsquo;가 상수인 $e&amp;rsquo; \ op_{cmp} \ e&amp;rsquo;&amp;rsquo;$ 패턴의 제약 조건을 찾는다. 또한 e&amp;rsquo;가 입력 그룹이고 e&amp;rsquo;&amp;rsquo; e&amp;rsquo;&amp;rsquo;&amp;rsquo;가 상수인 $(e' - e'') \ op_{cmp} \ e'''$같은 패턴도 범위 제약 조건으로 취급한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 e와 $\pi$로부터 구한 경계값을 함께 고려함으로써, fuzzy-SAT은 수식에 포함된 입력 그룹에 대해 정제된 범위 구간을 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구간을 효율적으로 유지하기 위해 signed, unsigned 비교 연산자를 모두 처리할 수 있는 wrapped intervals를 사용한다&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;wrapped intervals&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 수직선 상의 구간이 아닌, 유한한 비트 공간을 원형으로 보고 관리하는 데이터 구조&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, 수식 $i_1 + i_0 &amp;gt; 10$과 $i_1 + i_0 \leq 30$이 주어지면, Fuzzy-Sat은 $i_0$와 $i_1$로 구성된 입력 그룹에 대해 범위 구간 $[11, 30]$을 계산한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Detecting conflicting expressions&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e의 입력 바이트 중 일부를 할당할 때 $\pi$에 포함된 수식 중 어떤게 e와 충돌할 수 있는지 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, 수식 $i_1 + i_0 &amp;gt; 10$은 $i_1 + i_2 &amp;lt; 20$과 충돌한다. 이는 모두 입력 바이트 $i_1$를 포함하고있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;conflicting expression의 집합을 구하는건 reasoning 단계에서 multi-goal strategy를 수행할 때 필수적이다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fuzzing symbolic expressions&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT의 reasoning 단계의 핵심은 유효한 할당값 a를 찾기 위한 mutate 함수의 실행이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 mutate함수의 인풋 변환 상세 내용이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fuzzing input-to-state relations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수식 e가 input-to-state branch condition일 때 만약 피연산자가 상수라면 입력 그룹의 바이트에 해당 값을 대입하고, 만약 상수가 아니라면 해당 값의 기댓값을 파악해 대입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 피연산자가 상수이며 ==관계일 때 할당값이 $\pi$를 만족하지 않는다면 해당 쿼리를 unsatisfiable한 것으로 간주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 비교 연산자가 ==가 아닐경우, 인풋 그룹을 조금씩 변형하여 테스트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $i_1 + i_0 == 0xABCD$가 주어지면, $i_0 &amp;larr; 0xCD, \ i_1 &amp;larr; 0xAB$로 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 $\pi$의 제약 조건으로 인해 $i_0$의 범위 구간이 $[0xDD, 0xFF]$라면, 테스트를 하지 않고 해당 쿼리는 unsatisfiable한걸로 간주한다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Range interval brute force&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e에 포함된 입력 그룹에 대해 범위 구간이 알려져있을 때, brute force를 시도하여 할당값을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 범위구간이 2048보다 작다면 모든 가능한 값을 할당하며, 이보다 클 경우에는 구간의 최솟값과 최댓값을 테스트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $(i_1 + i_0) * 0xABCD == 0xCAFE$가 주어지고, $\pi$의 제약 조건에 의해 그룹 $g(i_0, i_1)$에 대한 범위 구간이 $[1, 9]$라면, $g \in [1, 9]$에 대한 할당값들을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중 $e \wedge \pi$를 만족하는게 없다면 해당 쿼리는 unsatisfiable한 것으로 간주한다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Trying interesting constants&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;analysis 단계에서 수집한 각 상수 c와 e에 포함된 입력 그룹 g에 대해서, g의 바이트를 c값으로 대입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 상수 c는 relaxed pattern(범위가 넓게)으로 수집되므로 각 상수에 대해서 little-endian이나 big-endian같이 다양한 인코딩으로 테스트한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Gradient descent&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e에 대해서 유효한 할당값을 찾는 문제에서 최소화/최대화 문제로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $e' &amp;lt; e''$는 $f = e' - e''$일 때 $f &amp;lt; 0$으로 변환될 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 할당값을 찾는데에 구현된 탐색 알고리즘은 경사 하강법을 기반으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 경사 하강법은 global minimum을 찾는데에 실패할 수 있지만 concolic execution에서는 local minimum을 찾는걸로 충분한 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;e의 입력 그룹들이 disjoint한 바이트를 가질 때는 바이트 그룹 단위로 경사를 계산하여 더 효과적으로 할당값을 찾도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어서, $(i_0 + i_1) &amp;minus; 10 &amp;gt; (i_2 + i_3) &amp;minus; 5$ 라는 수식이 있다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 $f = ((i_2 + i_3) - 5) - ((i_1 + i_0) - 10) &amp;lt; 0$ 로 변환할 수 있고 $i_1+ i_0$와 $i_2+ i_3$에 대해서 경사를 계산해서 ${i_0 \leftarrow 0x80, i_1 \leftarrow 0x06, i_2 \leftarrow 0x84, i_3 \leftarrow 0x01}$라는 할당값을 찾아낸다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Deterministic and non-deterministic mutations&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 기법은 AFL에서 가져온 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deterministic mutation에서는 비트나 바이트를 flip하거나 바이트를 MAX_INT같이 알려진 상수로 대체하는 기법을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;non-deterministic mutation에서는 무작위로 선택된 입력 바이트 내부의 무작위 비트를 반전시키거나 무작위로 선택한 유의미한 상수로 설정하는 등의 mutate를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AFL과 몇 가지 차이점이 있다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;mutation는 e에 포함되는 입력 바이트들에 대해서만 적용된다&lt;/li&gt;
&lt;li&gt;multi-byte mutation은 multi-byte 입력 그룹이 존재할때만 고려한다&lt;/li&gt;
&lt;li&gt;non-deterministic mutation의 경우 k개의 서로 다른 할당값을 생성한다
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;k는 max{100, $n_i$ &amp;middot; 20}와 같다. 여기서 $n_i$는 e에 관여하는 입력의 개수이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Evaluation&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 research questions를 다룬다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 프로그램에서 심볼릭 쿼리를 생성하는 Fuzzolic의 효율은 어떤가&lt;/li&gt;
&lt;li&gt;콘콜릭 실행기가 생성한 쿼리를 해결하는 Fuzzy-SAT의효과와 효율은 어떤가&lt;/li&gt;
&lt;li&gt;하이브리드 퍼징 설정에서 Fuzzolic과 Fuzzy-SAT의 성능은 어떤가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커뮤니티에서 집중적으로 퍼징된 다음 12개의 프로그램을 대상으로 벤치마크를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;advmng 2.00&lt;/code&gt;, &lt;code&gt;bloaty rev 7c6fc&lt;/code&gt;, &lt;code&gt;bsdtar rev. f3b1f&lt;/code&gt;, &lt;code&gt;djpeg v9d&lt;/code&gt;, &lt;code&gt;jhead 3.00&amp;ndash;5&lt;/code&gt;, &lt;code&gt;libpng 1.6.37&lt;/code&gt;, &lt;code&gt;lodepng-decode rev. 5a0dba&lt;/code&gt;, &lt;code&gt;objdump 2.34&lt;/code&gt;, &lt;code&gt;optipng 0.7.6&lt;/code&gt;, &lt;code&gt;readelf 2.34&lt;/code&gt;, &lt;code&gt;tcpdump 4.9.3 (libpcap 1.9.1)&lt;/code&gt;, &lt;code&gt;tiff2pdf 4.1.0&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트는 ubuntu 18.04 이미지 기반의 Docker 컨테이너에서 수행되었으며 2개의 Intel Xeon E5-4610v2 @ 2.30 GHz CPU와 256 GB RAM을 탑재한 서버를 사용했으며, 운영체제는 Debian 9.2 x86_64 환경이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Efficiency of Fuzzolic&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic과 다른 최신 콘콜릭 실행기들과 심볼릭 쿼리를 생성하는데 걸린 emulation time을 비교한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDMDge/dJMcah5i3A6/AKJydrvIjREYBjIrwNWy51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDMDge/dJMcah5i3A6/AKJydrvIjREYBjIrwNWy51/img.png&quot; data-alt=&quot;Fuzzolic VS QSYM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDMDge/dJMcah5i3A6/AKJydrvIjREYBjIrwNWy51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDMDge%2FdJMcah5i3A6%2FAKJydrvIjREYBjIrwNWy51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;334&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic VS QSYM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic은 소요시간이 항상 0.4초 미만이고 Qsym은 항상 14초 이상 소요되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Qsym이 최신 버전의 포팅이 어려운 구 버전의 Intel PIN을 기반으로 하고있으며, instrumentation이 최적화되어 있지 않았기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dSzeJQ/dJMcaaZnYK3/MCwDx3Wh1rHkxa5lAUpUL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dSzeJQ/dJMcaaZnYK3/MCwDx3Wh1rHkxa5lAUpUL1/img.png&quot; data-alt=&quot;Fuzzolic VS SymCC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dSzeJQ/dJMcaaZnYK3/MCwDx3Wh1rHkxa5lAUpUL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdSzeJQ%2FdJMcaaZnYK3%2FMCwDx3Wh1rHkxa5lAUpUL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;248&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic VS SymCC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SymCC와 비교했을 때는 3개의 벤치마크에서 뒤쳐졌는데, SymCC는 소스코드 기반임에도 불구하고 그 격차가 2배 미만임을 보여주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wtooT/dJMcagefTrF/YZJ3MDMt2bNw7m9YMocAb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wtooT/dJMcagefTrF/YZJ3MDMt2bNw7m9YMocAb0/img.png&quot; data-alt=&quot;Fuzzolic VS SymQEMU&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wtooT/dJMcagefTrF/YZJ3MDMt2bNw7m9YMocAb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwtooT%2FdJMcagefTrF%2FYZJ3MDMt2bNw7m9YMocAb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;370&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic VS SymQEMU&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic과 SymQEMU는 디자인 자체가 유사하고 동적 바이너리 변환기를 기반으로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도 Fuzzolic은 항상 SymQEMU보다 빠른걸로 측정되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;djpeg, objdump에 대해서 performance profiling을 수행했을 때, SymQEMU가 느린 이유는 바이트 그룹을 처리하는 일부 TCG 명령어를 다루는 방식이라고 판단됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic은 바이트 그룹으로부터의 concat 과 extract 연산을 효율적으로 하기 위한 저수준 최적화를 포함하고있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Fuzzolic이 SymQEMU보다 빠른 이유는 두 가지 요인이다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;일부 TCG 연산을 처리할 때 수행되는 inline instrumentation&lt;/li&gt;
&lt;li&gt;최적화된 분석 모드&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Impact of different analysis modes in Fuzzolic&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceESEo/dJMcaarzK9C/5t1Y2PCpzGHtYXdeKzUFHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceESEo/dJMcaarzK9C/5t1Y2PCpzGHtYXdeKzUFHk/img.png&quot; data-alt=&quot;세 가지 설정에 따른 성능 차이&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceESEo/dJMcaarzK9C/5t1Y2PCpzGHtYXdeKzUFHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceESEo%2FdJMcaarzK9C%2F5t1Y2PCpzGHtYXdeKzUFHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;258&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;세 가지 설정에 따른 성능 차이&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic의 분석 모드가 미치는 영향과 공유 메모리를 통한 솔버 컴포넌트와의 동기화 및 통신 비용을 측정하기 위해 위의 테스트를 반복 측정한 결과이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설정 X: tracer가 가장 오버헤드가 큰 모드 C로만 작동하여 활성화된 공유 메모리가 없다&lt;/li&gt;
&lt;li&gt;설정 Y: tracer가 상황에 따라 모드 A, B, C 모드를 동적으로 전환하지만 공유 메모리는 사용하지 않는다&lt;/li&gt;
&lt;li&gt;설정 Z: tracer가 상황에 따라 모드 A, B, C 모드를 동적으로 전환하며 공유 메모리를 사용하여 솔버 컴포넌트에 쿼리를 전달한다. (다른 도구와 비교할 때 사용한 모드)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험 결과 모드를 전환한 결과가 모드를 전환하지 않은 설정 X보다 평균 1.5배의 속도 향상이 이루어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 분석한 내용을 솔버에게 전달하는 과정을 추가하면 평균 1.2배 느려진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Solving effectiveness of Fuzzy-SAT&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리를 해결하는데에 있어 Fuzzy-SAT과 Z3및 JFS와 비교 실험을 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 optimistic solving을 비활성화한 상태에서 12개 프로그램을 QSYM으로 실행하여 세 가지 솔버를 사용할 때의 solving time과 satisfiable로 입증된 쿼리의 수를 비교했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 Fuzzy-SAT과 Z3를 사용할 때 LAVA-M 데이터셋에서 버그를 찾아내는 Qsym의 성능을 분석했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1685&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u7CoS/dJMcagkZJ6C/qwkRI6UoPhlfv5bDEWKju0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u7CoS/dJMcagkZJ6C/qwkRI6UoPhlfv5bDEWKju0/img.png&quot; data-alt=&quot;Fuzzy-SAT과 Z3의 성능 실험&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u7CoS/dJMcagkZJ6C/qwkRI6UoPhlfv5bDEWKju0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu7CoS%2FdJMcagkZJ6C%2FqwkRI6UoPhlfv5bDEWKju0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1685&quot; height=&quot;657&quot; data-origin-width=&quot;1685&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzy-SAT과 Z3의 성능 실험&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT와 Z3를 비교했을 때 두 가지 흥미로운 점이 있다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 솔버 모두 쿼리의 10%미만만 satisfiable로 증명되었다.&lt;/li&gt;
&lt;li&gt;satisfiable한 쿼리들을 봤을 때 Fuzzy-SAT는 대부분 해결할 수 있으며 Z3보다 더 나은 성능을 보이기도 한다&lt;/li&gt;
&lt;li&gt;Fuzzy-SAT이 Z3보다 평균 31배 적은 시간을 소요한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1673&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4WJ6m/dJMcahc9hTa/2we4qTkGZwwF86rtT2Uqhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4WJ6m/dJMcahc9hTa/2we4qTkGZwwF86rtT2Uqhk/img.png&quot; data-alt=&quot;Fuzzy-SAT과 Z3 JFS의 성능 실험&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4WJ6m/dJMcahc9hTa/2we4qTkGZwwF86rtT2Uqhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4WJ6m%2FdJMcahc9hTa%2F2we4qTkGZwwF86rtT2Uqhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1673&quot; height=&quot;442&quot; data-origin-width=&quot;1673&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzy-SAT과 Z3 JFS의 성능 실험&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림은 제한 시간을 1초로 설정했을 때 Z3가 해결한 쿼리 수와, fuzzy-SAT의 속도 향상이 어떻게 감소하는지 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 시간을 줄여도 Z3의 속도와 해결한 쿼리 수는 fuzzy-SAT이 좋다는걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT과 JFS를 비교했을때 JFS는 Fuzzy-SAT보다 훨씬 적은 쿼리를 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;bsdtar&lt;/code&gt; 의 325개의 쿼리 중 127개에서 JFS는 쿼리에 포함된 수많은 nested expression으로 인해 퍼징할 프로그램을 생성하는데 실패했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solving time을 고려한다면 Fuzzy-SAT는 JFS보다 1.5배 빠르고 여기서 analysis cache를 활성화하면 최대 4.8배까지 증가한다&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Impact of different mutations in Fuzzy-Sat&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1661&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCZwH4/dJMcaaE7MNi/KKz6BZTF08mk7tWK1bNAb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCZwH4/dJMcaaE7MNi/KKz6BZTF08mk7tWK1bNAb1/img.png&quot; data-alt=&quot;Fuzzy-SAT의 변형 방법에 따른 영향력&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCZwH4/dJMcaaE7MNi/KKz6BZTF08mk7tWK1bNAb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCZwH4%2FdJMcaaE7MNi%2FKKz6BZTF08mk7tWK1bNAb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1661&quot; height=&quot;594&quot; data-origin-width=&quot;1661&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzy-SAT의 변형 방법에 따른 영향력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 흥미로운 점은 input-to-state 변환을 적용하여 쿼리의 51% 이상을 해결하였고, 17%는 interesting constants를 활용하여 해결되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fuzzy-SAT on LAVA-M&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT이 콘콜릭 실행기에게 가치있는 쿼리를 해결할 수 있는지 테스트하기 위해 LAVA-M 테스트셋을 대상으로 &lt;code&gt;base64&lt;/code&gt;, &lt;code&gt;md5sum&lt;/code&gt;, &lt;code&gt;uniq&lt;/code&gt;, &lt;code&gt;who&lt;/code&gt; 4 개의 벤치마크 내의 버그를 찾는 실험을 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험 결과는 5회 반복 실행된 실험에서 발견된 버그의 평균 및 최대 개수이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;169&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nBUTl/dJMcacbQe13/aHjHt6GSN8YHbVUOkVXDik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nBUTl/dJMcacbQe13/aHjHt6GSN8YHbVUOkVXDik/img.png&quot; data-alt=&quot;LAVA-M을 이용한 Z3와 FUZZY-SAT 버그 탐지 실험 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nBUTl/dJMcacbQe13/aHjHt6GSN8YHbVUOkVXDik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBUTl%2FdJMcacbQe13%2FaHjHt6GSN8YHbVUOkVXDik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;169&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;169&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LAVA-M을 이용한 Z3와 FUZZY-SAT 버그 탐지 실험 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT는 3개의 항목에서 Z3보다 평균적으로 더 많은 버그를 발견했으며, 이는 하이브리드 퍼징에서 정확도와 성능을 맞바꾸는 것이 가치가 있음을 시사한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Fuzzy-SAT은 &lt;code&gt;who&lt;/code&gt; 에서 136개의 새로운 버그를 찾아내었으며, false positive일 가능성은 낮다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fuzzy-SAT and Fuzzolic in hybrid fuzzing&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic과 Fuzzy-SAT을 평가하기 위해 위의 12개의 벤치마크를 대상으로 8시간동안 10회 반복하여 실행을 진행하였고, 달성된 코드 커버리지를 추적하여 hybrid setup에서의 솔루션을 평가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzolic외에도 다음 세 가지 바이너리 퍼징 도구에 실험을 진행했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;QEMU모드의 AFL++&lt;/li&gt;
&lt;li&gt;Eclipser&lt;/li&gt;
&lt;li&gt;Qsym&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 에뮬레이션 단계에서 효율적인 것으로 알려진 소스 기반 콘콜릭 실행기 SymCC와, fuzzolic과 여러 설계를 공유하는 바이너리 콘콜릭 실행기 SymQEMU도 비교 대상에 포함했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 도구는 두개의 coverage-guided 퍼저 인스턴스 $F_m, \ F_s$와 병렬로 실행되며, 주기적인 입력 큐 동기화를 허용한다. 따라서 각 실행은 CPU시간으로 24시간을 소모한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차트에는 엣지 커버리지를 표시하고 음영으로 95% 신뢰 구간을 표시한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1645&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1l3zs/dJMcabRwOM4/f1quLwZJF9Kmk4p5xIRYEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1l3zs/dJMcabRwOM4/f1quLwZJF9Kmk4p5xIRYEk/img.png&quot; data-alt=&quot;Fuzzy-SAT과 최신 바이너리 퍼저의 엣지 커버리지 실험 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1l3zs/dJMcabRwOM4/f1quLwZJF9Kmk4p5xIRYEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1l3zs%2FdJMcabRwOM4%2Ff1quLwZJF9Kmk4p5xIRYEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1645&quot; height=&quot;575&quot; data-origin-width=&quot;1645&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzy-SAT과 최신 바이너리 퍼저의 엣지 커버리지 실험 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT을 결합한 Fuzzolic은 6개 프로그램에서 다른 도구보다 높은 코드 커버리지에 도달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic과 Qsym을 비교해보면 커버리지의 향상은 fuzzolic이 Qsym보다 더 나은 확장성을 가졌기 때문으로 보인다..&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Fuzzolic with Fuzzy-SAT vs SymCC&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgogbE/dJMcahqHCiK/5pSjas0kl9FQFDOEKvZYoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgogbE/dJMcahqHCiK/5pSjas0kl9FQFDOEKvZYoK/img.png&quot; data-alt=&quot;Fuzzolic VS SymCC&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgogbE/dJMcahqHCiK/5pSjas0kl9FQFDOEKvZYoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgogbE%2FdJMcahqHCiK%2F5pSjas0kl9FQFDOEKvZYoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;706&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic VS SymCC&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic은 SymCC와 비교했을 때 4개 중 3개 벤치마크에서 더 높은 코드 커버리지에 도달했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 SymCC는 소스코드 기반의 콘콜릭 실행기이기 때문에 이미 컴파일된 라이브러리 함수를 호출할 때 제대로 쿼리를 만들어내지 못한다. 이로 인해 커버리지가 낮게 나오는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 동일한 tracer환경에서 solver만 변경했을 때의 결과이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIFBYr/dJMcaffnr9F/PXjfROF3ImL8YpdVLfkBf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIFBYr/dJMcaffnr9F/PXjfROF3ImL8YpdVLfkBf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIFBYr/dJMcaffnr9F/PXjfROF3ImL8YpdVLfkBf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIFBYr%2FdJMcaffnr9F%2FPXjfROF3ImL8YpdVLfkBf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;356&quot; height=&quot;786&quot; data-origin-width=&quot;356&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic을 사용하고 Fuzzy-SAT, Z3를 솔버로 사용했을 때 Fuzzy-SAT은 4개의 대상 프로그램 중 3개에서 우위를차지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHZYVW/dJMcaf0F86a/78PjXTu8hgOOKm24Xf0zCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHZYVW/dJMcaf0F86a/78PjXTu8hgOOKm24Xf0zCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHZYVW/dJMcaf0F86a/78PjXTu8hgOOKm24Xf0zCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHZYVW%2FdJMcaf0F86a%2F78PjXTu8hgOOKm24Xf0zCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;806&quot; data-origin-width=&quot;379&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Qsym을 사용하고 Fuzzy-SAT, Z3를 솔버로 사용했을 때 개선의 정도는 전반적으로 분석 대상이 되는 특정 벤치마크에 따라 다르게 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M0FNI/dJMcadPijom/cYMATK7Ni8PdKiJUQKkrEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M0FNI/dJMcadPijom/cYMATK7Ni8PdKiJUQKkrEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M0FNI/dJMcadPijom/cYMATK7Ni8PdKiJUQKkrEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM0FNI%2FdJMcadPijom%2FcYMATK7Ni8PdKiJUQKkrEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;381&quot; height=&quot;773&quot; data-origin-width=&quot;381&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SymCC을 사용하고 Fuzzy-SAT, Z3를 솔버로 사용했을 때 &lt;code&gt;readelf&lt;/code&gt; 에서만 유의미한 향상이 나타났고 다른 타겟들에서는 매우 유사하게 측정되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이론적으로 효율적인 콘콜릭 실행기라면 더 빠른 솔버의 혜택을 받아야한다. 하지만 결과가 이렇게 나온 원인은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SymCC는 Fuzzolic이나 Qsym같은 다른 바이너리 프레임워크에 비해 제한된 수의 쿼리만 생성한다.&lt;/li&gt;
&lt;li&gt;이는 SymCC가 라이브러리를 심볼릭하게 instrumentation하지 않기 때문에 풀 수 있는 쿼리가 차이가 없는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 Fuzzy-SAT을 사용했을 때 대기 상태의 시간이 크게 증가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1527&quot; data-origin-height=&quot;511&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5bXD6/dJMcai38CGA/21JaT3LdTJ72woecTWqnxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5bXD6/dJMcai38CGA/21JaT3LdTJ72woecTWqnxk/img.png&quot; data-alt=&quot;Fuzzolic VS SymQEMU&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5bXD6/dJMcai38CGA/21JaT3LdTJ72woecTWqnxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5bXD6%2FdJMcai38CGA%2F21JaT3LdTJ72woecTWqnxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1527&quot; height=&quot;511&quot; data-origin-width=&quot;1527&quot; data-origin-height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fuzzolic VS SymQEMU&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막은 Fuzzolic과 SymQEMU의 비교이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 실험 결과와 마찬가지로 fuzzolic은 libpng에서 Z3를 사용하는 다른 콘콜릭 실행기들보다 성적이 좋지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나머지 &lt;code&gt;objdump&lt;/code&gt; , &lt;code&gt;readelf&lt;/code&gt;, &lt;code&gt;tcpdump&lt;/code&gt;, &lt;code&gt;tiff2pdf&lt;/code&gt; 에서는 Fuzzy-SAT을 사용한 Fuzzolic은 항상 SymQEMU보다 높은 커버리지를 달성한다.&lt;/p&gt;
&lt;h1&gt;Conclusions&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzolic은 실행중에 바이너리 프로그램을 instrumentation하여 심볼릭 수식과 쿼리를 구축할 수 있는 QEMU 기반의 새로운 콘콜릭 실행기이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fuzzy-SAT은 콘콜릭 실행 과정에서 생성된 쿼리를 처리하는 새로운 근사 솔버이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로의 개선사항&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;AFL과 같은 바이너리 퍼저에서 볼 수 있는 fork server를 구현하여 서로 다른 실행간에 JIT 캐시를 활성화한상태로 instrumentation 시간을 최소화한다.&lt;/li&gt;
&lt;li&gt;서로 다른 실행 간에 symbolic expression과 쿼리를 캐싱해서 이를 최적화하고 Z3 수식으로 변환하는 시간을 줄일 계획이다. 추가적으로 symbolic expression을 분석할 때 Fuzzy-SAT이 학습한 analysis facts를 캐싱해서 비슷한 수식에 대해서 빠른 계산을 가능하게 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 심볼릭 실행처럼 분기 제약조건을 만족시키는 할당값을 찾는건데, 그 방법으로 SMT solver처럼 수학적으로 계산을 하는게 아니라 퍼징의 방법을 채용하면서 경사하강법이나 부르트포스로 만족하는 값을 찾는 방법이 인상적이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 libpng처럼 이미지를 처리하거나 tcp 패킷을 처리하는 프로그램에 대해서는 Z3처럼 정확성을 요구하는 방법이 더 효율적이라는 실험 결과가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우에 정확한 값을 요구하거나 근사하는 방법으로 최적해를 구할 수 없다면 fuzzy-SAT에서 Z3로 전환하는 방법이 있지 않을까 싶다.&lt;/p&gt;</description>
      <category>보안/논문 리뷰</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/10</guid>
      <comments>https://pumisj.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 28 Apr 2026 07:41:34 +0900</pubDate>
    </item>
    <item>
      <title>[SE] Symbolic Execution &amp;amp; CDCL</title>
      <link>https://pumisj.tistory.com/9</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 프로그램 분석 기법인 Symbolic Execution에 대해서 공부하게되었다. 코드 커버리지와 아웃풋 값 등 한정된 정보를 이용하는 grey-box 퍼저인 AFL++과는 다르게, symbolic execution은 white-box fuzzer에서 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SAT/SMT solver, CDCL에 대해 공부한 것을 간단하게 정리해보았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고자료&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span data-path-to-node=&quot;5,0,0,0&quot;&gt;Baldoni, R., Coppa, E., D'Elia, D. C., Demetrescu, C., &amp;amp; Finocchi, I. (2018). &lt;/span&gt;&lt;span data-path-to-node=&quot;5,0,0,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;5,0,0,2&quot;&gt;&lt;span&gt;A Survey of Symbolic Execution Techniques&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;5,0,0,3&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;5,0,0,4&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span data-path-to-node=&quot;2,0&quot;&gt;James C. King. 1976. Symbolic Execution and Program Testing. Commun. &lt;/span&gt;&lt;span data-path-to-node=&quot;2,1&quot;&gt;&lt;/span&gt;&lt;span data-path-to-node=&quot;2,2&quot;&gt;&lt;span&gt;ACM 19, 7 (July 1976), 385&amp;ndash;394.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span data-path-to-node=&quot;2,2&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://open.library.ubc.ca/cIRcle/collections/48630/items/1.0043896?hl=ko-KR&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://open.library.ubc.ca/cIRcle/collections/48630/items/1.0043896?hl=ko-KR&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://youtu.be/yRVZPvHYHzw?si=XyvOydnWshfh2uAM&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/yRVZPvHYHzw?si=XyvOydnWshfh2uAM&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=yRVZPvHYHzw&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/qUL5w/dJMb8WMqWK7/k8Ss1M923jdTZmFCsK22sK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=636_104_724_200,https://scrap.kakaocdn.net/dn/sE25w/dJMb8PGxtth/CbM1BLDteg0q52BN5UuyP0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=636_104_724_200,https://scrap.kakaocdn.net/dn/KzZ0P/dJMb8ZvCOsR/XHNiwmfluS1lEWgHyumhIk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=636_104_724_200&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;10. Symbolic Execution&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/yRVZPvHYHzw&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/-t85LW_nwck?si=27X6VIfjvMZshiz3&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/-t85LW_nwck?si=27X6VIfjvMZshiz3&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=-t85LW_nwck&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/b1Ox2W/dJMb9lMcRMr/gqozaDGAqy49IA01zWqLck/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=474_214_548_294,https://scrap.kakaocdn.net/dn/n8m7B/dJMb9cBJsGG/BYWFGQtYElMxqUVkOKD7GK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=474_214_548_294&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;6.858 Spring 2020 Lecture 10: Symbolic execution&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/-t85LW_nwck&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Symbolic Execution&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행은 시스템적으로 가능한 모든 입력값에 대해서 도달가능한 모든 실행 상태를 조사는 정적 프로그램 분석 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행은 일반적으로 두 단계로 구성된다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Path exploration phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 모든 가능한 실행 경로를 에뮬레이션하여 제어 흐름 그래프 (CFG)를 탐색한다.&lt;/li&gt;
&lt;li&gt;구체적인 입력으로 결정할 수 없는 분기를 만나면 SEE는 두 경로를 fork하여 모든 분기를 탐색한다.&lt;/li&gt;
&lt;li&gt;이 때 분기 조건의 심볼릭 값들은 현재 상태에 도달하는 결과를 얻을 수 있도록 제약된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Solving phase
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SAT solver와 같은 logic solver를 활용하여 위의 심볼릭 값들에 대한 구체적인 값을 계산한다.&lt;/li&gt;
&lt;li&gt;심볼릭 실행은 소스코드나 바이너리 레벨에서도 진행될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행은 프로그램을 실행할 때 숫자나 텍스트같은 일반적인 입력 대신, 변수를 임의의 값을 나타내는 기호, 즉 Symbolic Formula로 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 두 입력 a와 b를 z에 저장한다고 했을 때, z를 표현할 때 숫자같은 구체적인 값이 아닌 a + b라는 대수적인 표현식으로 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 SUM: PROCEDURE (A, B, C);
2   X &amp;lt;- A + B;
3   Y &amp;lt;- B + C;
4   Z &amp;lt;- X + Y - B;
5   RETURN (Z);
6 END;&lt;/code&gt;&lt;/pre&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;단계&lt;/th&gt;
&lt;th&gt;변수 X&lt;/th&gt;
&lt;th&gt;변수 Y&lt;/th&gt;
&lt;th&gt;변수 Z&lt;/th&gt;
&lt;th&gt;PC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;초기 상태&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;A=$\alpha_1$,&lt;/td&gt;
&lt;td&gt;B=$\alpha_2$,&lt;/td&gt;
&lt;td&gt;C=$\alpha_3$&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;true&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;1번 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;background-color: #e6f5ff; color: #333333; text-align: start;&quot;&gt;true&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;2번 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;$\alpha_1$ + $\alpha_2$&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;3번 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;$\alpha_2 + \alpha_3$&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;4번 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;$(\alpha_1 + \alpha_2) + (\alpha_2 + \alpha_3) - \alpha_2$&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;5번 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;최종 반환값:&lt;/b&gt; $\alpha_1 + \alpha_2 + \alpha_3$&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 표현된 변수에 대한 심볼릭 식은 Symbolic store ($\sigma$)에 저장되며 분기문을 통과할 때 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적인 실행: 변수에 구체적인 숫자가 들어가 조건문을 통과하거나 넘어감.&lt;/li&gt;
&lt;li&gt;심볼릭 실행: 해당 조건의 참 조건을 판단하고, 판단할 수 없다면 실행 엔진이 해당 상태를 Fork하여 두 경로를 모두 탐색한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행을 할 때 해당 경로에 도달하기 위한 제약 조건(constraint)를 PC(Path Condition)이라는 논리식에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행에서는 분기문을 만날 때 두 가지 명제의 참 거짓을 판단한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$pc \implies q$ : 현재 경로에서 항상 조건이 참인가&lt;/li&gt;
&lt;li&gt;$pc \implies \neg q$ : 현재 경로에서 항상 조건이 거짓인가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 하나만 참이라면 fork를 하지않고 지나가며, 둘 다 참이 아닐 경우 fork를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 만약 pc와 q를 동시에 만족시킬 수 없다면 해당 분기의 탐색은 멈춘다. 이를 알아내는 도구로 SMT Solver를 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SAT (boolean Satisfiability Problem)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 boolean formula(PC)를 충족시키는 interpretation(변수의 조합)이 존재하는가 결정하는 문제.&lt;/li&gt;
&lt;li&gt;존재한다면 해당 formula는 satisfiable하다.&lt;/li&gt;
&lt;li&gt;존재하지 않는다면 해당 formula는 unsatisfiable하다.&lt;/li&gt;
&lt;li&gt;formula를 false로 만드는 interpretation이 하나라도 있다면 falsifiable하다.&lt;/li&gt;
&lt;li&gt;모든 interpretation에 대해서 formula가 참이라면 valid하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ON0hG/dJMcajof2UV/loRWa62SE2mzVB549HsfsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ON0hG/dJMcajof2UV/loRWa62SE2mzVB549HsfsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ON0hG/dJMcajof2UV/loRWa62SE2mzVB549HsfsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FON0hG%2FdJMcajof2UV%2FloRWa62SE2mzVB549HsfsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;550&quot; height=&quot;325&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CNF (Conjunctive Normal Form)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Clause의 논리 곱으로 이루어진 논리식 형태
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$(\text{Clause 1}) \wedge (\text{Clause 2}) \wedge (\text{Clause 3})$&lt;/li&gt;
&lt;li&gt;각 절은 논리 합으로 구성되어있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SAT Solver의 CDCL(Conflict-Driven Clause Learning) 알고리즘이 CNF구조에 최적화되어있기에 CNF 식으로 넘겨주게된다.&lt;/li&gt;
&lt;li&gt;모든 if문이 처음부터 CNF는 아니지만, Tseitin 변환 기법을 사용하면 모든 식을 CNF로 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SAT Solver&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Boolean Satisfiability에 여러 이론들을 추가하여 만든 formula의 충족 가능성을 결정하는 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SMT Solver (Satisfiability Modulo Theories Solver)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Boolean formulation만 표현할 수 있었던 SAT에서 추가적으로 integer, float, list array, bit vector과 같은 다양한 데이터를 사용한 복합적인 formulation을 해결할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forking이 발생하면 엔진은 다음과 같은 처리를 진행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;State Cloning
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분기 직전의 변수 값이나 statement counter, pc 등 state를 복사하여 두 개의 독립적인 병렬 실행 상태를 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PC Update
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참일 경우 $pc \leftarrow pc \wedge q$ 를 할당한다.&lt;/li&gt;
&lt;li&gt;거짓일 경우 $pc \leftarrow pc \wedge \neg q$ 를 할당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나뉜 실행 상태는 서로에게 영향을 주지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;1 POWER: PROCEDURE (X, Y);
2   Z &amp;lt;- 1;
3   J &amp;lt;- 1;
4 LAB: IF Y &amp;gt;= J THEN
5     DO; Z &amp;lt;- Z * X;
6         J &amp;lt;- J + 1;
7         GO TO LAB; END;
8   RETURN (Z);
9 END;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$X = \alpha_1, Y = \alpha_2$ 라고 가정할 때, $true \supset \alpha_2 \ge 1$도 아니고 $true \supset \neg(\alpha_2 \ge 1)$도 아니므로 fork.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;루프를 하지 않는다면 1 반환&lt;/li&gt;
&lt;li&gt;루프를 한다면 pc에 $\alpha_2 \ge 1$ 추가하고 다시 루프 진행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$\alpha_2 \ge 1 \supset \alpha_2 \ge 2$ 이 분기문에서 다시 fork 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 루프는 이론상 무한히 반복될 수 있으며, 이렇게 모든 경로에 대해 탐색을 진행하면 Path Explosion 문제가 발생한다는 문제가 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Symbolic Execution Tree&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행 과정을 트리 구조로 정의한 것을 Symbolic Execution Tree라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트리의 구성 요소는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행된 각 statement를 의미한다.&lt;/li&gt;
&lt;li&gt;각 노드에는 당시의 전체 실행 상태($\sigma$, statement counter, PC)가 저장된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Arc (간선)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;statement에서 다음 statement의 전이를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Forking
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;If문에서 분기가 발생할 때 해당 노드에서는 두 개의 arc가 나온다. 각각 T와 F로 표시된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행 트리는 두 가지 성질을 가진다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;트리의 모든 terminal leaf에 대해, 해당 경로를 똑같이 따라가게 만드는 Nonsymblic input이 반드시 존재한다. 왜냐하면 PC가 Unsatisfiable (만족 불가능) 해지는 경우, 아예 실행을 하지 않는다고 정의했기 때문이다.&lt;/li&gt;
&lt;li&gt;서로 다른 두 리프의 경로 조건 (pc1, pc2)는 서로 겹치지 않는다. 즉 $\neg(pc_1 \wedge pc_2)$가 항상 참이다&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Commutativity (가환성)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Commutativity는 프로그램 P에 대하여 입력을 Instantiation(구체화)한 후 실행하는 것과 symbolic하게 실행한 후 결과를 구체화하는 것이 동일한 결과 상태에 도달함을 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 결과를 구체화한다는 것은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행이 완료된 Execution Tree의 각 Terminal Leaf에 대해, 모든 프로그램 변수 값과 pc에 있는 symbolic값인 $\alpha$들을 정수 K로 치환한다.&lt;/li&gt;
&lt;li&gt;치환된 pc가 true가 되는 leaf의 값이 최종 결과가 되며, 이는 프로그램을 구체적인 입력으로 실행했을 때 나오는 결과와 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1101&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OGwNo/dJMcafzuchc/QVZYgmmKYjPUJpkWFG6Pc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OGwNo/dJMcafzuchc/QVZYgmmKYjPUJpkWFG6Pc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OGwNo/dJMcafzuchc/QVZYgmmKYjPUJpkWFG6Pc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOGwNo%2FdJMcafzuchc%2FQVZYgmmKYjPUJpkWFG6Pc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1101&quot; height=&quot;495&quot; data-origin-width=&quot;1101&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 P는 프로그램을 의미하며, E(P(X))는 입력 X에 대한 프로그램 P의 실행 결과를, K는 특정 정수 입력 집합을 나타낸다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Program Proving&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Program proving은 프로그램 P가 입력 명제 I (해당 프로그램은 이런 입력 성질을 가진 숫자에 대해서는 완벽함이라는 범위)를 만족하는 환경에서 실행될 때, 종료 후 출력 명제 O를 항상 만족함을 보이는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대해 ASSERT(cond)라는걸 도입했는데, 이 지점에 도달하는 모든 경로에 대해서 cond는 반드시 수학적으로 참이어야한다는 목표를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 실행 도중 ASSERT(Q)를 만나면 다음과 같은 과정을 진행한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;PC와 Symbolic store을 결합하여 $pc \supset Q$ 가 가능한지를 묻는 VC (Verification Condition)를 생성한다.&lt;/li&gt;
&lt;li&gt;$pc \supset Q$ 가 Tautology인지 확인한 뒤, 참이라면 다음 statement로 이동하고, false라면 pc는 만족하지만 Q를 위반하는 반례가 있는것이므로 중단한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 방식으로 해당 프로그램에 대한 안정성 또는 취약점을 발견할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbolic execution은 assert를 실패하게 만드는 모든 unsafe한 입력을 식별할 수 있다. 이론적으로는 전수적 탐색 (exhaustive exploration)은 결정 가능한 모든 분석에 대해 건전하고 완전한 방법론을 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;건전성: false negative를 방지&lt;/li&gt;
&lt;li&gt;완전성: false positive를 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 모든 구간을 탐색하는 전수적 심볼릭 실행은 소규모 어플리케이션을 넘어서면 실행하기가 까다로워진다. 따라서 성능과 건전성을 trade off하는 등 좀 더 현실적인 대책을 세운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드를 처리할 때는 다음과 같은 처리 문제가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Memory
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SEE가 포인터, 배열 등 복잡한 객체를 어떻게 다루는가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Environment
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SEE가 라이브러리 및 system call 등 소프트웨어 스택 전반에 걸친 상호작용을 어떻게 처리하는가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;State space explosion
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;path explosion를 어떻게 해결할것인가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Constraint solving
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SMT같은 constraint solver는 $3x + 2y &amp;lt; 10$ 같은 linear arithmetic은 쉽게 풀 수 있지만, $x^2 + y^2 = 25$ 같은 non-linear arithmetic은 효율성이 떨어진다.&lt;/li&gt;
&lt;li&gt;이 경우 어떻게 효율을 높힐 수 있는가.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제들을 해결하기 위해서 신뢰성이나 완전성에 영향을 미치더라도 정해진 시간 내에 목표 (취약점 찾기)를 달성하기 위해 모든 경로를 탐색하는 대신 부분 탐색 (partial exploration)을 수행한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Symbolic Execution Engine (SEE)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말한 고전적인 심볼릭 실행의 문제는 External code, non-linear arithmetic같이 처리할 수 없는 경로 조건이 발생할 경우, 실행 가능한 경로를 탐색할 수 없다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결하기 위해 Concrete 상태와 symbolic 상태를 혼합하여 실행하는 Concolic execution이 고안되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2eKMB/dJMcaf7h5O4/msNJOtr9XKJ2B1s215FXk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2eKMB/dJMcaf7h5O4/msNJOtr9XKJ2B1s215FXk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2eKMB/dJMcaf7h5O4/msNJOtr9XKJ2B1s215FXk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2eKMB%2FdJMcaf7h5O4%2FmsNJOtr9XKJ2B1s215FXk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;140&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DSE (Dynamic Symbolic Execution, dynamic test generation)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSE는 가장 대중적인 concolic execution 방식으로, SEE는 기존의 symbolic store와 PC 외에도 구체적인 값을 저장하는 concrete store ($\sigma_c$)를 유지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSE는 구체적 실행이 경로를 주도하는 방식으로 실행되며, 이에 따라 분기를 만날때마다 fork를 하는 것이 아니라 먼저 한 경로를 모두 간 뒤 지나지 않았던 경로를 탐색하는 방식으로 이루어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행할 경우 매 분기마다 SMT solver를 호출할 필요가 없고, 다른 경로를 탐색할 때는 분기 조건을 부정한 뒤 그 때 SMT solver로 해당 PC를 만족하는 새로운 입력값을 생성한다. &amp;rarr; 이를 통해 DSE에서도 symbolic tree를 그릴 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEE는 심볼릭하게 추적하지 않는 외부 코드 호출도 처리할 수 있는데, 이 경우에는 다음과 같은 문제가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;False Negative
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 함수의 내부 로직을 알 수 없기 때문에 특정 입력을 넣어야 발생하는 경로를 놓칠 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Path Divergence
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부함수나 비트연산 등 심볼릭하게 추적되지 않는 변수로 인해 솔버가 생성한 값이 의도와는 다른 경로로 실행을 이끼는 현상.&lt;/li&gt;
&lt;li&gt;서베이 결과에 따르면 실제 환경에서 path divergence rate는 60%를 넘는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concolic execution말고도 $S^2E$ (Selective Symbolic Execution) 방식도 있다. 이 방식은 전체 소프트웨어 스택중, 관심있는 컴포넌트만 심볼릭하게 실행하고, 나머지는 구체적으로 실행하는 방식이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구체적 실행 중 특정 함수만 심볼릭하게 분석&lt;/li&gt;
&lt;li&gt;심볼릭 분석 중에 복잡한 외부 함수는 건너뛰기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Path Selection&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSE 방식에도 불구하고, 여전히 모든 경로에 대해서 탐색을 진행하는건 효율적이지 않다. 그렇기에 가장 유망한 경로를 먼저 탐색하도록 우선순위를 정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 DFS나 BFS, 또는 무작위 경로를 탐색하는 방안도 있겠지만, 코드 커버리지를 극대화하는 방향으로 경로를 선택하는 연구가 많이 이루어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KLEE의 Coverage Optimized Search는 각 state마다 가중치를 계산해서 무작위로 상태를 선택한다. 이 가중치는 아직 커버되지 않은 가장 가까운 명령어의 거리, 해당 상태에서 최근에 새로운 코드가 커버되었는지 여부, 그리고 상태의 call stack을 고려하여 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 비슷하게 CFG에서 탐색 빈도가 낮은 경로를 선택하는 Subpath-guided Search 방식도 사용되며, 이를 위해 탐색된 subpath의 빈도 분포를 유지한다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Subpath&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이 n의 연속된 명령어 시퀀스&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표 지향적인 전략도 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AEG (Automatic Exploit Generation)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 가지 전략을 도입했다.&lt;/li&gt;
&lt;li&gt;Buggy-path first: 과거 state에서 exploit은 불가능했던 버그가 포함된 경로를 먼저 선택한다.&lt;/li&gt;
&lt;li&gt;Loop exhaustion: 루프를 방문하는 경로를 탐색한다. 루프에서의 프로그래밍 실수가 BOF나 메모리 관련 에러로 이루어진다는 사실에 기반한 접근방법.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MAYTHEM
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;exploit 가능한 버그를 찾기 위해 symbolic address에 대한 메모리 접근이 식별되거나, symbolic instruction pointers가 감지되는 경로에 우선순위를 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbolic address는 주소 값 자체가 숫자가 아니라 symbol로 되어있는 것을 말한다. ex) buffer[i]에서 i가 concrete가 아닌 symbol&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbolic instruction pointer는 CPU RIP나 PC 레지스터 등에 들어가는 주소가 심볼릭 수식임을 의미함. &amp;rarr; 따라서 사용자의 입력값이 실행 주소를 결정&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;regular property를 만족하는 경로를 자동적으로 찾기 위해 FSM(유한 상태 머신)을 활용하는 FSM guided DSE 기법을 사용한다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;Regular property는 오토마타 이론의 Regular Language에서 가져온 개념으로, 가질 수 있는 상태와 집합, 변화의 과정을 기록하는 함수, 시작과 끝을 정의해 이벤트의 실행 순서를 나타낸다.&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 다음과 같이 이루어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 사용 규칙이나 메모리 안정성같은 속성을 FSM으로 정의한다.&lt;/li&gt;
&lt;li&gt;현재 상태에서 backward dataflow analysis를 사용하여 미래의 상태를 계산한 뒤, 속성을 만족할 가능성이 가장 높은 경로를 선택한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Symbolic Backward Execution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 심볼릭 실행은 프로그램의 진입점부터 경로를 따르는 방식이었다. 하지만 이 경우 path explosion이 발생할 수 있기 때문에 버그가 발생하는 부분을 분석하는것이 어려울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 목표 지점부터 진입점으로 거꾸로 진행하는 심볼릭 역방향 실행이 고안되었으며, 프로그램 내에서 도달하기 어렵거나 의심되는 지점을 집중 분석할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 CFG를 거꾸로 따라가면서 제약 조건을 역으로 수집하고 계산하여 현재의 심볼릭 상태를 만족시키기 위해 이전 단계에서 충족해야하는 사전 조건을 생성한다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;예를 들어 assert(x == 10)이 실패하는 상태를 만든다고 해보자.

바로 이전 명령어가 x = y + 2일 때, x &amp;ne; 10이어야 하므로 결과적으로 y &amp;ne; 8이어야한다는 사전조건이 만들어진다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 프로그램의 시작 부분에 도달하고 수집된 제약 조건이 satisfiable하다면 목표 지점을 실행시키는 유효한 입력값이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 기존보다 더 복잡한 문제에 직면하게되는데, 함수 호출이나 포인터 연산이 포함된 경우 어떤 경로에서 해당 지점에 도달했는지 정확하게 알 수 없다. &amp;rarr; 일부 도구에서 static summary나 call stack정보를 활용하여 정확도를 높힌다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;The Path Explosion Problem&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;path explosion의 주요 원인은 루프와 함수 호출이다. 루프의 각 반복은 조건문을 확인하는 과정을 거치기 때문에 조건에 하나 이상의 심볼릭 값이 있다면 무한한 분기로 이어질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;int x = sym_input(); // 예: 파일로부터 읽기
while (x &amp;gt; 0) x = sym_input();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 입력값에 따라 분기가 나눠지는데, 임의의 최종 상태에서 PC는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\left( \bigwedge_{i \in [1,k]} \alpha_i &amp;gt; 0 \right) \wedge (\alpha_{k+1} \leq 0)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k는 반복 횟수, $\alpha_i$는 i번째 반복에서 sym_input()에 의해 생성된 심볼을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루프 탐색을 제한하는 방법도 쓰이고 실제로 많이 쓰인다. 하지만 이 방식은 다른 흥미로운 경로를 놓칠 수 있기 때문에 더 발전된 전략들이 탐구되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Path Pruning&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색 경로를 줄이기 위한 가장 간단한 방법은 SMT solver를 호출하여 실행 불가능한 분기를 쳐내는거다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xaVUt/dJMcaipq8rn/cZU07VU6uPw2piPJgky1J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xaVUt/dJMcaipq8rn/cZU07VU6uPw2piPJgky1J0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xaVUt/dJMcaipq8rn/cZU07VU6uPw2piPJgky1J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxaVUt%2FdJMcaipq8rn%2FcZU07VU6uPw2piPJgky1J0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;204&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;204&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림과 같이 솔버가 unsatisfiable을 찾아낸다면, 해당 경로를 폐기할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 접근 방식을 eager evaluation이라고 하며, 이와 반대로 매번 확인하지 않고 나중에 한꺼번에, 또는 필요할 때만 확인하는 방식을 Lazy evaluation이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SMT solver가 한 번에 한 경로씩 조사하는데 사용될 수 있지만, 제어 흐름에 대해 반복적으로 추론하게 되는 경우가 많다. 이 때 최소 불충족 코어 (Minimal Unsat Core)을 이용하는 방법은 다음과 같이 이루어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 경로가 왜 불가능 (Unsat)한지 그 원인이 되는 최소한의 코드를 찾는다.&lt;/li&gt;
&lt;li&gt;해당 코드가 반복되는 다른 경로에 대해서 가지치기를 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;State Merging&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;갈라진 여러 경로를 공통 지점에서 다시 하나로 합쳐서 탐색해야 할 상태의 수를 줄이는 기법이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 원리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분기된 상태들의 경로 제약 조건을 논리합(Disjunction, $\vee$)으로 묶고, 메모리 상태는 ite 표현식을 사용해 단일 상태로 통합한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정밀도 유지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Abstract Interpretation(추상 해석)과 달리 값을 근사화(Over-approximation)하지 않고 모든 논리적 가능성을 수식에 보존하므로 정밀도 손실이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tradeoffs
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탐색해야 할 경로(Path)의 수가 지수적으로 증가하는 것을 막아준다.&lt;/li&gt;
&lt;li&gt;경로 제약 조건에 $\vee$ 연산자가 많아지면 SMT Solver의 계산 복잡도가 급격히 상승한다. (오히려 개별 탐색보다 느려질 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;$ite(C, A, B)$형태의 수식으로, 조건 C가 참이면 A, 거짓이면 B라는 의미를 갖는다. 병합된 상태에서 변수의 값이 경로에 따라 다를 때 이를 표현하기 위해 사용된다.&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 statement를 평가하려는 두 심볼릭 상태가 심볼릭 저장소 측면에서 매우 유사할 때 merging을 하는게 좋을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 상태 $(stmt, \sigma_1, \pi_1)$과 $(stmt, \sigma_2, \pi_2)$가 주어졌을 때, 병합된 상태는 $(stmt, \sigma', \pi_1 \vee \pi_2)$로 구성될 수 있고, 이제 제약조건이나 변수를 추가할 땐 새롭게 만들어진 state로 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 $\sigma'$는 저장소의 차이를 설명하는 ite 표현식으로 구축된 $\sigma_1$과 $\sigma_2$ 사이의 병합된 심볼릭 저장소이며, $\pi_1 \vee \pi_2$는 두 병합된 상태로부터 얻은 경로 제약 조건의 합집합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합은 탐색하는 경로의 수를 효과적으로 감소시킬 수 있지만 다음과 같은 문제가 발생한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;논리합 연산에 의해 SMT solver의 효율이 떨어지게된다&lt;/li&gt;
&lt;li&gt;원래 concrete value였던게 ite수식으로 변하면서 프로그램의 복잡도가 상승한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 무조건 합치기보다는 분석 목적에 따라 병합의 정도를 선택한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Constraint Solving&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PC에 저장된 boolean formula를 풀기 위해서 SMT를 사용하게되는데, SMT는 다음과 같이 동작한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SEE로부터 받은 formula의 atoms를 새로운 boolean variable로 매칭한다.&lt;/li&gt;
&lt;li&gt;SAT decision procedure가 해당 formula를 충족하는지 검사한다.&lt;/li&gt;
&lt;li&gt;theory solver가 해당 절차에 의해 생성된 모델을 검사한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, $(x &amp;gt; 0) &amp;and; (y &amp;lt; 5 &amp;or; x + y == 10)$ 에서 $(x &amp;gt; 0)$ 를 A라고, $y &amp;lt; 5$ 를 B라고, $x + y == 10$ 를 C라고 했을 때 $A &amp;and; (B &amp;or; C)$ 를 만족하는 A, B, C의 순서쌍을 찾고 만약 순서쌍이 존재한다면 실제 값을 구하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Theory solver는 다양한 수학적 도메인에 대한 전문 이론을 담은 솔버이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터는 무한대의 수를 다루는 현실의 수학 이론과는 다르게 한정된 자원으로 인해 여러 계산 이론이 만들어졌다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bit-vector Thoery: 비트연산에 특화&lt;/li&gt;
&lt;li&gt;Array Theory: 배열의 인덱스와 값의 관계에 특화&lt;/li&gt;
&lt;li&gt;Integer Arithmetic: 정수 더하기, 빼기에 특화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z3는 대표적인 SMT solver로 비트벡터, 배열, 수량자(quantifiers), 선형 정수 및 실수 산술 등 다양한 이론을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 constraint solving의 성능을 최적화하기 위한 다양한 기술이 연구되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Constraint Reduction&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solver와 symbolic executor에서 따르는 최적화 접근법은 제약조건 축소이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;expression rewriting
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;constant folding
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x + (2 + 3) 같은 식을 솔버에게 주지 않고 x + 5로 미리 계산해서 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;strength reduction
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x * 2 보다 계산하기 쉬운 x &amp;lt;&amp;lt; 1 로 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;simplication of linear expression
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2x + 3x &amp;gt; 10 같은 식을 5x &amp;gt; 10으로 합치는 과정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;constraint independence
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;formula의 해를 구하는데 상관없는 변수는 무시한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 PC가 $(x &amp;gt; 0) &amp;and; (y &amp;lt; 10)$ 이면서 분기 조건문이 $y &amp;gt; 5$ 라면 x는 해당 문제와 상관이 없다.&lt;/li&gt;
&lt;li&gt;전체 제약 조건을 서로 영향이 없는 subset으로 나누고 해당 문제와 관련이 있는 부분만 solver에 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;implied value concretization
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램이 실행될수록 변수가 가질 수 있는 값의 범위는 한정된다.&lt;/li&gt;
&lt;li&gt;만약 PC에 X &amp;gt; 0 이라는 제약조건이 있었는데, x에 5밖에 올 수 없다면, 그냥 concrete값으로 집어넣는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;bitfield-theory simplification
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;implied value concretization과 비슷하게, 비트 연산이 마스킹하는 심볼릭 변수를 concrete값으로 대체한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reuse of Constraint solution&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 계산된 결과를 재사용하는 것은 최적화에 매우 효과적이다. 계산된 값을 캐시에 저장해뒀다 같은 문제가 있을 때 솔버가 아닌 캐시에서 꺼내쓰는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EXE는 서버 기반 캐싱으로, 여러 심볼릭 실행기가 하나의 캐시를 공유하여 각 인스턴스중 하나만 결과를 계산해도 어느 인스턴스든 해당 값을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KLEE의 경우에는 counterexample caching을 채용하여 incremental optimization stategy를 구현했으며, 단순히 똑같은 수식에 대해서만 값을 갖다쓰는게 아닌, 집합의 포함 관계를 이용하여 부분집합에도 충족 가능성을 분석한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lazy Constraint&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;eager evaluation은 분기를 만날때마다 SMT solver를 호출해야했다. 하지만 / 이나 %연산, 특히 분모가 심볼릭 변수일 때 timeout이 발생하여 효율이 많이 떨어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이에 대한 해결책으로 비용이 많이 드는 분기문을 만나면, 참과 거짓 분기를 모두 탐색해 lazy constraint를 저장한다. 그 후 에러가 발생할 시 저장한 lazy constraint를 한 번에 풀어 실제 가능한 경로인지 계산한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concretization&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔버가 풀 수 없거나, 적어도 효율적으로 풀 수 없는 공식이 존재할 때 건전성을 희생하더라도 구체적인 값을 사용하여 비용을 줄일 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OPR74/dJMcagd5BKq/kmKEprSwNpFiKpE4QdsteK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OPR74/dJMcagd5BKq/kmKEprSwNpFiKpE4QdsteK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OPR74/dJMcagd5BKq/kmKEprSwNpFiKpE4QdsteK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOPR74%2FdJMcagd5BKq%2FkmKEprSwNpFiKpE4QdsteK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;764&quot; height=&quot;134&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 솔버는 $x = (y \times y) \pmod{50}$ 와 같은 비선형적인 수식을 풀기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 concolic방식으로 실제 값을 대입하여 식을 단순화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식을 사용한다면 값이 고정되어 다른 값일 때 나뉘는 분기에 대해서는 새로운 입력을 생성할 방법이 없게되어 false negative를 유발하는 문제가 있다. 이에 대한 해결책으로 다른 고정적인 값을 넣어 프로그램을 재실행해야한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Handling Problematic Constraint&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 구체화를 하면 아무 구체적인 숫자를 줌으로써 솔버는 blind commitment에 빠지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 해결 방법이 몇 가지 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Concolic Walk
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선형 제약의 해에 대해서 휴리스틱하게 걸을 수 있는 polytope (다면체)를 정의한다.&lt;/li&gt;
&lt;li&gt;나머지 제약 조건들은 평가 지점이 제약 조건과 일치하는데 얼마나 가까운지를 나타내는 fintness function (적합도 함수)와 함께 할당된다.&lt;/li&gt;
&lt;li&gt;다면체 위에서 점을 선택하고 그에 대한 비선형 제약 조건을 평가하면서 adaptive search를 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Symcretic Execution
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SBE와 기존의 전방향 실행을 같이 사용하는 방법이다.&lt;/li&gt;
&lt;li&gt;타겟 지점에서부터 SBE를 수행하고, 각 경로마다 trace가 수집된다.&lt;/li&gt;
&lt;li&gt;역방향 탐색중에 문제가 있는 constraint를 만나면 이를 잠재적으로 충족가능한 것으로 표시하고 순회를 진행한다.&lt;/li&gt;
&lt;li&gt;프로그램의 진입점에 도달한다면 전방향 실행으로 trace를 구체적으로 평가하여 표시한 제약 조건을 충족시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;CDCL (Conflict-Driven lause Learning)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDCL은 SMT/SAT solver가 논리식의 충족 가능성을 판별하는 핵심 알고리즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로 다음과 같이 동작한다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Unit propagation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엔진이 임의의 값을 할당하며 탐색하다가 특정 clause 내의 하나의 리터널을 제외한 나머지가 모두 False라면 남은 리터럴은 항상 True가 된다.&lt;/li&gt;
&lt;li&gt;만약 모든 식이 false라면 conflict 상태라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 식이 있다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;$C_1: (p \lor q \lor \neg r)$&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$C_2: (\neg p \lor r)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$C_3: (\neg r)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$C_4: (q \lor p)$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전체 식이 참이되기 위해서는 $\neg r$ 는 항상 참이되어야 한다. 따라서 $r = False(F)$ 이다.&lt;/li&gt;
&lt;li&gt;이 조건에 맞춰 C1에 적용하면 항상 참이다&lt;/li&gt;
&lt;li&gt;C2에 대해서 적용하면 $\neg p$ 은 항상 참이 되어야 한다&lt;/li&gt;
&lt;li&gt;C4도 마찬가지로 적용하면 $q$ 는 항상 참이 되어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 연쇄적으로 리터럴의 값을 확정시키는 것을 unit propagation이라고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Implication Graph &amp;amp; 1UIP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Conflict가 발생하면 엔진은 변수 할당의 인과관계를 논리적 그래프로 구성한다.&lt;/li&gt;
&lt;li&gt;이 때 그려지는 그래프는 방향성 비순환 그래프로 함의 그래프라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;$C_1: (x_1 \lor x_4)$&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$C_2: (x_3 \lor \neg x_4 \lor \neg x_5)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$C_3: (\neg x_3 \lor \neg x_2 \lor \neg x_4)$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 식이 있다고 하자. 솔버가 무작위로 먼저 x5 = 1, x2 = 1 그 뒤에 x0 = 1로 할당했을 때, 함의 그래프는 다음과 같이 그려진다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VSvDf/dJMcadBzzEe/XwU9ZkZInB9Eea8Cw9xzh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VSvDf/dJMcadBzzEe/XwU9ZkZInB9Eea8Cw9xzh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VSvDf/dJMcadBzzEe/XwU9ZkZInB9Eea8Cw9xzh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVSvDf%2FdJMcadBzzEe%2FXwU9ZkZInB9Eea8Cw9xzh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;573&quot; height=&quot;427&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하얀색 노드는 솔버가 결정한 Decision variable&lt;br /&gt;주황색 노드는 unit propagation으로 결정된 값&lt;br /&gt;검은색 노드는 propagation으로 인해 conflict가 발생한 값을 의미한다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU56Li/dJMcahxiLmC/eTyCee9EkG2KlYLsYXQkh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU56Li/dJMcahxiLmC/eTyCee9EkG2KlYLsYXQkh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU56Li/dJMcahxiLmC/eTyCee9EkG2KlYLsYXQkh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU56Li%2FdJMcahxiLmC%2FeTyCee9EkG2KlYLsYXQkh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;752&quot; height=&quot;533&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;conflict가 발생했다면, 충돌 지점과 그 결과를 나누는 지점을 찾는다 (CUT)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 데나 자르지 않고 가장 효율적인 지점인 UIP를 찾아 나누게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UIP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함의 그래프에서 현재 깊이의 결정 노드로부터 충돌 노드로 가는 모든 경로가 있을 때, 모든 경로가 공통적으로 통과해야 하는 노드.&lt;/li&gt;
&lt;li&gt;하나의 그래프에는 여러 개의 UIP가 있을 수 있는데, 그중에서 충돌 노드와 가장 가까운 쪽에 있는 UIP를 1UIP라고 부른다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예시에서는 임의로 할당한 x1로 인해 x3에서 충돌이 나는데, 이 중 가장 가까운 노드는 x4 노드이다. 따라서 이를 기준으로 cut을 하게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 conflict를 발생하게 한 x2, x4, x5에 대해서 다음과 같은 conflict Clause를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$\neg x_2 \lor \neg x_4 \lor \neg x_5$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 앞으로 이 세 변수가 동시에 1이 되려고 할 때 미리 막을 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Backjumping
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;뒤에서부터 순차적으로 탐색하는 Backtracking과는 다르게, conflict clause가 단위 절로 작동할 수 있는 가장 높은 레벨로 회귀한다.&lt;/li&gt;
&lt;li&gt;위의 예시에서 현재 레벨을 제외한 두 번째 레벨인 2레벨로 돌아간다 (x2를 할당한 시점)&lt;/li&gt;
&lt;li&gt;이렇게 함으로써 충돌과 관련 없는 하위 레벨의 결정을 다시 계산할 필요가 없고, 탐색해야할 조합 최적화의 범위가 줄어든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l9FBl/dJMcahRzDPE/6y00eZxNM07z6LmuXL4ek0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l9FBl/dJMcahRzDPE/6y00eZxNM07z6LmuXL4ek0/img.png&quot; data-alt=&quot;복잡한 그래프에 대한 cut 지점 결정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l9FBl/dJMcahRzDPE/6y00eZxNM07z6LmuXL4ek0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl9FBl%2FdJMcahRzDPE%2F6y00eZxNM07z6LmuXL4ek0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;988&quot; height=&quot;716&quot; data-origin-width=&quot;988&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;복잡한 그래프에 대한 cut 지점 결정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 단순히 이렇게 구현하면 탐색중 학습되는 중돌 절의 구성은 평균 40개로, 이 모든 제약조건을 메모리 포인터로 추적한다면 너무 오래걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 각 절마다 리터럴 중 단 두 개만 추적하는 Two-watched Literals 방식의 메모리 최적화를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념 자체는 어렵지 않은데, 용어라든가 최적화하는 기법중에 잘 이해가 되지 않는 부분이 있어서 조금 애먹었다. 마지막에 설명한 Two-watched Literals도 논리적으로는 이해가 되지 않아 제대로 적어놓지 않은 것도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거기다 심볼릭 실행에 대해서는 많이 정리하고 공부했다고 생각했는데, 정작 심볼릭 실행의 정의를 구체화해서 말로 내뱉는게 안됐다 (고쳐야할 점이다).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 글의 구조도 좋지 않아 가독성이 많이 안좋았는데, 다음 정리할 때는 좀 더 논리적이고 알아볼 수 있게 쉽게 풀어 써야겠다.&lt;/p&gt;</description>
      <category>보안</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/9</guid>
      <comments>https://pumisj.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 14 Apr 2026 12:49:15 +0900</pubDate>
    </item>
    <item>
      <title>[Dream hack] 셸 코드란?</title>
      <link>https://pumisj.tistory.com/8</link>
      <description>&lt;h1&gt;서론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 셸 코드를 학습한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해킹 대회(CTF)를 보면 뭔가 어려워보이던데 한번 꼭 배워보고싶었던거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 그 중 아주 간단한 코드만 정리해놓았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;h1&gt;셸 코드란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Exploit이란 상대 시스템을 공격하는 것을 말하며, 셸 코드란 이런 exploit을 위한 어셈블리 코드를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 셸을 획득하기 위해 셸 코드를 작성하는데 execve 셸 코드를 장악한다.&lt;/p&gt;
&lt;h1&gt;ORW 셸 코드&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Open, Read, Write 동작을 수행하는 코드로 등정 파일을 읽고 터미널이나 다른 파일에 출력하는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 system call을 사용하기 위해 레지스터에 다음과 같은 값을 넣어 호출한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daTrGU/dJMcagE2Opi/nXQOhmNSegoQyF19IDwYsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daTrGU/dJMcagE2Opi/nXQOhmNSegoQyF19IDwYsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daTrGU/dJMcagE2Opi/nXQOhmNSegoQyF19IDwYsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaTrGU%2FdJMcagE2Opi%2FnXQOhmNSegoQyF19IDwYsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;970&quot; height=&quot;223&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 자체는 따로 다루지 않겠다. (어셈블리를 이해하면 바로 구현할 수 있기때문에)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레지스터의 rax에는 syscall을 하기 위한 값이 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read의 경우에는 0을, write는 1을, open을 2를 사용한다. 이 rax값에 따라 호출되는 시스템 콜이 다르며 인자는 rdi, rsi, rdx 등의 레지스터에 순서대로 들어간다. 이 순서도 호출 규약이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템 콜 레지스터 호출 규약: rdi &amp;rarr; rsi &amp;rarr; rdx &amp;rarr; r10 &amp;rarr; r8 &amp;rarr; r9&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 콜이 종료되면 반환값은 rax 레지스터에 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성한 어셈블리를 컴파일할 때 C언어로 스켈레톤 코드를 만든 뒤 거기에 어셈블리 코드를 넣고 컴파일하는 방식을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;// File name: sh-skeleton.c
// Compile Option: gcc -o sh-skeleton sh-skeleton.c -masm=intel

__asm__(
    &quot;.global run_sh\n&quot;
    &quot;run_sh:\n&quot;

    &quot;Input your shellcode here.\n&quot;
    &quot;Each line of your shellcode should be\n&quot;
    &quot;seperated by '\n'\n&quot;

    &quot;xor rdi, rdi   # rdi = 0\n&quot;
    &quot;mov rax, 0x3c    # rax = sys_exit\n&quot;
    &quot;syscall        # exit(0)&quot;);

void run_sh();

int main() { run_sh(); }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 스택 정렬이나 세그먼트 설정 등을 개발자가 신경 쓸 필요 없이 컴파일러가 초기화 작업을 대신 해주기때문에 편리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의해야할 점이 있는데, 레지스터는 8바이트만 담을 수 있다. 만약 파일의 주소가 8바이트를 넘는다면 한 레지스터에 모두 담을 수 없기때문에 push로 스택에 한 층씩 쌓으면서 주소를 입력해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 리틀 엔디안 방식으로 스택에 파일의 주소를 작성한 어셈블리 코드다.&lt;/p&gt;
&lt;pre class=&quot;x86asm&quot;&gt;&lt;code&gt;section .text
global _start
_start:
    ;   open
    xor rax, rax
    push rax
    mov rax, 0x676e6f6f6f6f6f6f
    push rax
    mov rax, 0x6c5f73695f656d61
    push rax
    mov rax, 0x6e5f67616c662f63
    push rax
    mov rax, 0x697361625f6c6c65
    push rax
    mov rax, 0x68732f656d6f682f
    push rax&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ORW 셸 코드 작성 팁&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저번에 사용했던 pwntools에는 ORW 셸 코드를 대신 작성해주는 함수를 지원한다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;context.arch = &quot;amd64&quot;

dir = &quot;/home/file&quot;

shellcode = shellcraft.open(dir)
shellcode += shellcraft.read('rax', 'rsp', 0x30)
shellcode += shellcraft.write(1, 'rsp', 0x30)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shellcraft는 contect.arch에 명시한 아키텍처에 따라 셸 코드를 작성해주는 클래스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자는 위의 호출 인자와 같으며, 레지스터를 직접 명시하여 사용할 수 있다.&lt;/p&gt;
&lt;h1&gt;Execve&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfEkXC/dJMcaipjaol/9pPUz5cSsHAkbixPYZ5aV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfEkXC/dJMcaipjaol/9pPUz5cSsHAkbixPYZ5aV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfEkXC/dJMcaipjaol/9pPUz5cSsHAkbixPYZ5aV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfEkXC%2FdJMcaipjaol%2F9pPUz5cSsHAkbixPYZ5aV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;920&quot; height=&quot;656&quot; data-origin-width=&quot;920&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 말했던 셸(shell)이란 사용자가 운영체제에 명령을 내리기 위한 인터페이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 셸을 획득하면 시스템을 제어할 수 있게되므로 해킹할 때 이 셸을 획득하는걸 목표로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 그 중 execve셸 코드는 /bin/sh 같이 임의의 프로그램을 실행하는 코드로, 호출하는 순간 메모리에 /bin/sh가 올라가 서버를 직접 제어할 수 있게된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O5WN2/dJMcadnXnmW/izISgpfukC87bqapziWjHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O5WN2/dJMcadnXnmW/izISgpfukC87bqapziWjHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O5WN2/dJMcadnXnmW/izISgpfukC87bqapziWjHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO5WN2%2FdJMcadnXnmW%2FizISgpfukC87bqapziWjHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;978&quot; height=&quot;119&quot; data-origin-width=&quot;978&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 execve를 실행하기 위한 인자이다. 보통 /bin/sh를 실행하기 때문에 rdi에 해당 주소를 넣고 rsi와 rdx는 null을 넣어도 상관없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 코드는 바로 작성할 수 있기 때문에 따로 적진 않겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스켈레톤 코드로 어셈블리 코드를 컴파일하는건 로컬에서 이 코드가 작동하는지 확인하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 해킹할 때는 asm파일을 직접 컴파일해서, 실제 byte code로 변환하는 과정을 거쳐야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서 objcopy를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;$ sudo apt-get install nasm 
$ nasm -f elf shellcode.asm
$ objdump -d shellcode.o
$ objcopy --dump-section .text=shellcode.bin shellcode.o
$ xxd shellcode.bin
00000000: 31c0 5068 2f2f 7368 682f 6269 6e89 e331  1.Ph//shh/bin..1
00000010: c931 d2b0 0bcd 80                        .1.....
$&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nasm으로 셸 코드를 컴파일 한 뒤 objcopy로 추출하고싶은 섹션만 따로 빼 shellcode.bin파일을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 대상 프로그램에 넣어 실행하면 /bin/sh가 실행되면 시스템을 장악할 수 있다.&lt;/p&gt;
&lt;h1 style=&quot;color: #000000; text-align: start;&quot;&gt;후기&lt;/h1&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아무래도 입문이다보니 셸 코드의 가장 기초적인 부분만 다룬거같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;조금 더 딥하게 배우고싶은데 그건 내가 따로 배워야하는 부분이지만 아쉬운 점도 있긴하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래도 어셈블리로 직접 셸 코드 작성하는것도 재밌었다.&lt;/p&gt;</description>
      <category>보안/System Hacking</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/8</guid>
      <comments>https://pumisj.tistory.com/8#entry8comment</comments>
      <pubDate>Fri, 3 Apr 2026 12:39:37 +0900</pubDate>
    </item>
    <item>
      <title>[Dream hack] GDB/PWN 정리</title>
      <link>https://pumisj.tistory.com/7</link>
      <description>&lt;h1&gt;서론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금부터 정리할 툴들은 물론 깊이 들어가면 복잡하지만 지금은 GDB와 PWN을 겉핥기식으로 정리하도록 하려고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 없어서 며칠간 공부하지 못했지만, 할 거 정리가 끝난 지금 조금이라도 많이 드림핵 강의를 공부해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/lecture/paths/system-hacking-fundamental&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/lecture/paths/system-hacking-fundamental&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;GDB&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GDB(GNU Debugger)는 리눅스의 대표적인 Debugger이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS나 Eclipse처럼 메모리, 콜 스택 등을 cli상에서 볼 수 있는 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GDB에는 다양한 플러그인이 존재한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;gef
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아키텍처에 독립적&lt;/li&gt;
&lt;li&gt;Heap분석이 가능하다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;peda
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 클래식한 도구이다. register, stack, code 흐름을 모두 보여줌&lt;/li&gt;
&lt;li&gt;pattern create로 오프셋을 보여줌
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pattern create: 메모리 내 offset을 식별하기 위해 중복되지 않는 고유한 순환 수열을 생성하는 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하지만 Heap 분석 기능이 부족함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;pwndbg
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deep Memory 분석을 지원
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 주소가 뭐를 가리키는지 추척해서 보여줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Heap 구조를 더 자세히 보여줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gdb는 다양한 명령어를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;run&lt;/code&gt; : 디버깅 시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;break&lt;/code&gt; : breakpoint 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;continue&lt;/code&gt; : 중단된 프로그램 계속 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;entry, start:&lt;/code&gt; ELF의 EP 필드에 적힌 진입점부터 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ni&lt;/code&gt; : 한 줄씩 실행하지만 함수 내부로 들어가지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;si&lt;/code&gt; : 한 줄씩 실행하지만 함수 내부로 들어감&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;finish&lt;/code&gt; : 함수 내부로 들어갔을 때 함수를 종료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;info registers, breakpoints&lt;/code&gt; : 레지스터나 중단점을 확인. 특정 레지스터만 확인하는것도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;disassemble&lt;/code&gt; : binary 파일을 어셈블리 코드로 바꿔서 보여줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;x&lt;/code&gt; : 특정 주소에서 원하는 길이만큼의 데이터를 원하는 형식으로 인코딩해서 출력함&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAXRve/dJMcaaLzra2/alhuQPFsex8h52X1Vtgyr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAXRve/dJMcaaLzra2/alhuQPFsex8h52X1Vtgyr0/img.png&quot; data-alt=&quot;명령어 x에 대한 인자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAXRve/dJMcaaLzra2/alhuQPFsex8h52X1Vtgyr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAXRve%2FdJMcaaLzra2%2FalhuQPFsex8h52X1Vtgyr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;995&quot; height=&quot;528&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;명령어 x에 대한 인자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 사용할 수 있으며 &lt;code&gt;x/&amp;lt;개수&amp;gt;&amp;lt;포맷 및 크기&amp;gt;&lt;/code&gt; 형태로 사용할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;backtrace&lt;/code&gt; : 콜 스택을 보여줌&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;set&lt;/code&gt; : 프로세스의 메모리 상태를 직접 변경할 수 있음&lt;/p&gt;
&lt;h1&gt;pwntools&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해커가 특정 시스템이나 프로그램의 취약점을 이용해 제어권을 빼앗는 공격 행위를 Pwn이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 행위를 돕는 툴이 pwntools이며, 다양한 기능을 제공한다. (소켓 연결, 입출력, 공격 페이로드 전송 등)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;연결을 맺는 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;process(), remote(), ssh()&lt;/code&gt; : 각각 로컬, 호스트의 도메인과 포트, SSH 서버에 접속할 때 사용한다. 성공적으로 실행될 시 pwnlib.tubes 클래스를 반환한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 송수신 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 주고받을때에는 모두 binary형태로 전송된다. 따라서 데이터를 송수신할때 b접두사를 붙여 파이썬의 byte 자료형을 사용해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;recv()&lt;/code&gt; : 연결된 대상에 대해서 데이터를 수신함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;recv(), recvline(), recvn(), recvuntil(), recvall()&lt;/code&gt; 등의 함수가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;send()&lt;/code&gt; : 연결된 대상에 대해서 데이터를 송신함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;send(), sendline(), sendafter(), sendlineafter()&lt;/code&gt; 등의 함수가 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;interactive()&lt;/code&gt; : 터미널에서 사용자가 실시간으로 데이터를 송수신할 수 있게 하는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;close()&lt;/code&gt; : 두 프로세스의 연결을 끊는 함수, 해당 함수를 호출하면 객체 내부의 자원이 정리된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;로그 출력&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;context.log_level&lt;/code&gt; 을 사용해 수준에 맞게 로그를 출력할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XK9kU/dJMcaadJZft/QP7kgrRfcePATgYeh4jVCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XK9kU/dJMcaadJZft/QP7kgrRfcePATgYeh4jVCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XK9kU/dJMcaadJZft/QP7kgrRfcePATgYeh4jVCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXK9kU%2FdJMcaadJZft%2FQP7kgrRfcePATgYeh4jVCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;986&quot; height=&quot;348&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Packing &amp;amp; Unpacking&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 데이터를 주고받을 때 정수값을 bytes로 변환하거나 그 반대 작업을 수행하는 경우가 많다. 이를 구현해놓은게 &lt;code&gt;p&lt;/code&gt;와 &lt;code&gt;u&lt;/code&gt; 이다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;from pwn import *

s8 = 0x41
s16 = 0x4142
s32 = 0x41424344
s64 = 0x4142434445464748

print(p8(s8))
print(p16(s16))
print(p32(s32))
print(p64(s64))

s8 = b&quot;A&quot;
s16 = b&quot;AB&quot;
s32 = b&quot;ABCD&quot;
s64 = b&quot;ABCDEFGH&quot;

print(hex(u8(s8)))
print(hex(u16(s16)))
print(hex(u32(s32)))
print(hex(u64(s64)))

--------------------------------------------

b'A'
b'BA'
b'DCBA'
b'HGFEDCBA'
0x41
0x4241
0x44434241
0x4847464544434241&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패킹 및 언패킹 함수는 기본적으로 리틀 엔디언으로 동작한다. 물론 빅 엔디언으로도 변경할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;GDB&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pwntools에서는 &lt;code&gt;gdb.attach&lt;/code&gt; 함수로 연결중인 프로세스에 대해서 디버깅을 진행할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Assemble &amp;amp; Disassemble&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 어셈블리를 기계어로, 기계어를 어셈블리어로 변환하는 기능도 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;context.arch = &amp;ldquo;amd64&amp;rdquo; || &amp;ldquo;i386&amp;rdquo; || &amp;ldquo;arm&amp;rdquo;&lt;/code&gt; : 아키텍처 선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;asm()&lt;/code&gt; : 해당 어셈블리를 기계어로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;disasm()&lt;/code&gt; : 해당 기계어를 어셈블리로 변환&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ELF&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ELF()&lt;/code&gt; : ELF파일의 경로를 넣으면 pwnlib.elf.elf.ELF 클래스를 반환하며, 해당 클래스에는 ELF 정보를 담고있는 멤버 변수나 다양한 함수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;symbols()&lt;/code&gt; : pwnlib.elf.elf.ELF클래스의 멤버 변수로 심볼들의 주소를 가지고있는 doctdict 클래스이다. 기존 딕셔너리처럼 사용할 수 있으면서도 속성으로 접근할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;from pwn import *

e = ELF(&quot;./example&quot;)
print(hex(e.symbols['write']))
print(hex(e.symbols.write))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 출력되는 주소는 Virtual Address이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;막 그렇게 어려운 내용은 아니다. (물론 내가 어렵지 않은것만 공부한걸수도 있다. 나중에 더 자세히 찾아봐야지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GDB는 fuzzing하면서 많이 썼던거라 복잡하지 않았는데 PWN은 활용할게 많은거같아서 좀 익숙해져야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 셸코드인데 이 때 PWN을 써서 OWR 셸코드를 작성하는것도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 빨리 시스템 해킹을 숙달할 수 있으면 좋겠다.&lt;/p&gt;</description>
      <category>보안/System Hacking</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/7</guid>
      <comments>https://pumisj.tistory.com/7#entry7comment</comments>
      <pubDate>Fri, 3 Apr 2026 02:32:30 +0900</pubDate>
    </item>
    <item>
      <title>[Fuzzer] AFL/AFL++이란?</title>
      <link>https://pumisj.tistory.com/6</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fuzzer의 전반적인 내용을 익혔기에 이번엔 AFL에 대해서 집중적으로 알아보고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 처음에 잘 못 읽었던 논문 &quot;&lt;b&gt;AFL++: Combining Incremental Steps of Fuzzing Research&quot;&lt;/b&gt;와 AFL의 Whitepaper을 읽으면서 배운 내용을 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Andrea Fioraldi, Dominik Maier, Heiko Ei&amp;szlig;feldt, and Marc Heuse, &lt;b&gt;&quot;AFL++: Combining Incremental Steps of Fuzzing Research,&quot;&lt;/b&gt; in &lt;i&gt;Proceedings of the 14th USENIX Workshop on Offensive Technologies (WOOT 20)&lt;/i&gt;, 2020.&lt;/li&gt;
&lt;li&gt;Valentin J.M. Man&amp;egrave;s et al., &lt;b&gt;&quot;The Art, Science, and Engineering of Fuzzing: A Survey,&quot;&lt;/b&gt; IEEE Transactions on Software Engineering, vol. 47, no. 11, pp. 2312-2331, 2019.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/google/AFL/tree/master/docs&quot;&gt;https://github.com/google/AFL/tree/master/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;AFL&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 대표적인 Grey-box fuzzer로 정적, 동적 정보를 바탕으로 퍼징을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼징은 다음과 같은 작업을 반복 수행하는 것을 말한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Preprocessing&lt;/li&gt;
&lt;li&gt;Scheduling&lt;/li&gt;
&lt;li&gt;mutation&lt;/li&gt;
&lt;li&gt;configure update&lt;/li&gt;
&lt;li&gt;evaluation&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 Minset관리나 스케줄링같은 주요 작업에 코드 커버리지를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 커버리지를 측정하기 위해서 afl-gcc, afl-clang이나 afl-clang-lto 등의 컴파일러를 사용하는데, 컴파일시에 기계어로 직접 커버리지 측정 코드를 삽입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 커버리지 코드는 논리적으로 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;cur_location = &amp;lt;COMPILE_TIME_RANDOM&amp;gt;;
shared_mem[cur_location ^ prev_location]++;
prev_location = cur_location &amp;gt;&amp;gt; 1;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cur_location
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 프로젝트를 링크하는 과정을 단순화하며, XOR 출력값이 균등하게 분포되도록 컴파일시에 무작위로 정해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;shared_mem
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;64kB의 shared memory 구역. instrumentated binary (실행파일)를 실행하면서 이 출력 맵에 특정 튜플 (branch_src, branch_dst)이 실행되었음을 기록한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;SHM의 크기가 64kB인 이유:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 타겟 프로그램의 분기점이 2K ~ 10K개 있기 때문에 충분히 커버 가능하고, L2 캐시 용량을 넘지 않기 때문이다.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZxzK6/dJMcabRbEzo/lCoIbby1rMKwi7kWwxRAy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZxzK6/dJMcabRbEzo/lCoIbby1rMKwi7kWwxRAy0/img.png&quot; data-alt=&quot;분기 개수별 충돌 확률&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZxzK6/dJMcabRbEzo/lCoIbby1rMKwi7kWwxRAy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZxzK6%2FdJMcabRbEzo%2FlCoIbby1rMKwi7kWwxRAy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;563&quot; height=&quot;215&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;분기 개수별 충돌 확률&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행 경로를 기록했을 때 다음과 같은 실행 추적을 구별할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;A -&amp;gt; B -&amp;gt; C -&amp;gt; D -&amp;gt; E&lt;/code&gt; (튜플: AB, BC, CD, DE)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A -&amp;gt; B -&amp;gt; D -&amp;gt; C -&amp;gt; E&lt;/code&gt; (튜플: AB, BD, DC, CE)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 비트 시프트 연산을 하는 이유는 튜플의 순서를 구분하기 위해서이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시프트를 하지 않으면 (A &amp;rarr; B) 와 (B &amp;rarr; A) 를 구분할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 경로를 튜플의 집합으로 보기때문에 전체적인 프로세스로는 고유해보일지라도 고유한 경로로 취급하지 않는 경우도 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;A -&amp;gt; B -&amp;gt; C -&amp;gt; D -&amp;gt; E&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A -&amp;gt; B -&amp;gt; C -&amp;gt; A -&amp;gt; E&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;A -&amp;gt; B -&amp;gt; C -&amp;gt; A -&amp;gt; B -&amp;gt; C -&amp;gt; A -&amp;gt; B -&amp;gt; C -&amp;gt; D -&amp;gt; E&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 2번까지 처리된 뒤 3번 경로를 보면 모두 비트맵에 저장되어있는 경로이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 3번은 고유 경로로 취급하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 튜플의 hit count를 고려하여 같은 튜플이라 할지라도 다른 버킷에 담는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+ (지수적으로 증가)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로그램이 종료되고 (A, B) 튜플이 10번 실행되었고, 해당 버킷의 비트가 0이라면 새로운 경로를 찾은걸로 판단하여 비트맵에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 hit count별로 경로를 다르게 기록한다면 다음과 같은 장점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일시에 랜덤으로 부여한 위치값이 겹쳐 서로 다른 튜플이 같은 비트맵 칸에 충돌하더라도, 두 엣지의 실행 횟수가 서로 다르다면 어느정도 구별이 가능하다.&lt;/li&gt;
&lt;li&gt;만약 프로그램이 설정한 타임아웃값을 넘어갈 경우, 그 시드가 커버리지를 늘린다고 하더라도 queue에 추가하지 않는다. 이로 인해 tarpit에 빠져들지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 방법 모두 path explosion을 피하기 위한 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 선별된 테스트 케이스들은 입력 큐에 추가되어 다른 유망한 테스트 케이스를 만드는데에 사용된다. (Scheduling)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 테스트 케이스를 변형할 때 현재까지 확인한 모든 튜플을 커버하면서도 실행 시간이 빠르고 크기가 작은 테스트 케이스를 선택하는 알고리즘을 사용하여 주기적으로 큐를 재평가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 알고리즘은 각 튜플마다 실행 시간과 파일 사이즈에 비례하여 점수를 매긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 뒤 튜플은 다음과 같은 순서로 처리된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;temporary working set에 없는 튜플을 찾는다.&lt;/li&gt;
&lt;li&gt;이 튜플에 대해서 승리한 큐 항목을 찾는다.&lt;/li&gt;
&lt;li&gt;해당 항목의 모든 튜플을 temporary working set에 추가한다.&lt;/li&gt;
&lt;li&gt;누락된 튜플이 있으면 다시 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 과정으로 선별된 corpus는 일반적으로 처음 시작한 시드보다 5~10배 더 작으며, 다른 시드보다 더 많이 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 테스트 케이스의 크기를 줄이는 작업을 한다 (Trimming).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 작업은 전체 코드 커버리지를 줄이지 않는 선에서 파일 크기를 줄여 실행 시간을 단축하고 중요한 형식 제어 구조를 건들일 확률을 높힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL의 기본 trimmer는 가변적으로 데이터 블럭을 삭제하고 trace map에 영향을 미치지 않는다면 그대로 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;afl-tmin은 더 철저하고 반복적인 알고리즘을 사용하며, trmming된 파일에 대해 알파벳 정규화를 시도한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;처음엔 크게 데이터 블록을 0으로 채운다&lt;/li&gt;
&lt;li&gt;binary search 스타일로 블록 크기와 스텝오버를 줄여가며 블록을 삭제한다.&lt;/li&gt;
&lt;li&gt;고유한 문자를 세고 각각을 0으로 바꿔 알파벳 정규화를 수행한다&lt;/li&gt;
&lt;li&gt;0이 아닌 바이트에 대해 바이트 단위 정규화를 수행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 instrumentation으로 받은 피드백을 통해서 다양한 파일 유형에서 똑같이 잘 작동하도록 매개변수를 최적화한다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 매개변수는 bit flipping의 변이율, Arithmetic Mutation에서 r값의 범위 알려진 흥미로운 정수(0, 1, INT_MAX) 등을 의미한다.&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 순차적인 bit/byte flip, Arthmetic, known integer 등 변형이 deterministic하게 이루어지지만, 나중에는 무작위 bit/byte flip, test case splicing의 작업을 하면서 non-deterministic하게 새로운 테스트 케이스를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PUT에 적용하는 모든 시드값이 핵심적인 로직을 실행하는건 아니다. 오히려 무작위로 변형을 진행하다보면 문법 검증 단계에서 막히는데, AFL에서는 이에 대해 두 가지 방법을 제시한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사전 정의된 dictionary terms를 제공함으로써 PUT가 기대하는 입력 검증을 통과할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 SQLite의 키워드 목록을 AFL에 제공했을 때 다음과 같은 유효한 SQL 구문을 생성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;select sum(1) LIMIT (select sum(1) LIMIT -1, 1);&lt;/li&gt;
&lt;li&gt;select round(-1)````;&lt;/li&gt;
&lt;li&gt;select group_concat(DISTINCT + 1) | 1;&lt;/li&gt;
&lt;li&gt;select length(?) in (hex(1) +++ 1, 1);&lt;/li&gt;
&lt;li&gt;select abs(+0 + hex(1) - NOT + 1) t1;&lt;/li&gt;
&lt;li&gt;select DISTINCT &quot;Y&quot;, &quot;b&quot;, (1) &quot;Y&quot;, &quot;b&quot;, (1);&lt;/li&gt;
&lt;li&gt;select - (1) AND &quot;a&quot;, &quot;b&quot;;&lt;/li&gt;
&lt;li&gt;select ?1 in (CURRENT_DATE, 1, 1);&lt;/li&gt;
&lt;li&gt;select - &quot;a&quot; LIMIT - /* &lt;i&gt;/ /&lt;/i&gt; &lt;i&gt;/- /&lt;/i&gt; &lt;i&gt;/ /&lt;/i&gt; */-1;&lt;/li&gt;
&lt;li&gt;select strftime(1, sqlite_source_id());&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;자동으로 토큰을 식별하는 알고리즘을 적용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, PNG 파일의 경우에는 섹션을 시작할 때 사람이 읽을 수 있도록 4바이트의 magic value를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 | .PNG........IHDR
00 00 00 20 00 00 00 20 02 03 00 00 00 0e 14 92 | ................&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 deterministic bit flipping 과정에서 특정 비트를 뒤집었을 때&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;해당 구간을 건드렸을 때 다른 인접한 구역을 건들였을 때와 다를 때나&lt;/li&gt;
&lt;li&gt;해당 바이트 전체를 건들이는동안 발생하는 변화가 일관적일 때&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;afl에서는 이를 syntax token으로 간주하고 딕셔너리에 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 찾아낸 크래시의 중복을 제거하는 과정을 거친다. (De-duping crashes)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 두 조건 중 하나가 충족되면 unique하다고 간주한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크래시 추적이 이전에 없던 튜플이 보인경우.&lt;/li&gt;
&lt;li&gt;크래시 추적에 이전에 항상 있던 튜플이 사라진경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 초기에 path count가 크게 늘어나지만, 나중에는 self-limiting 효과를 가져 새로운 경로가 늘어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발견된 크래시가 실제로 보안적으로 위협이 되는지 판단하는 작업도 진행한다. (Investigating crashes)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔하게 발생되는 이슈는 Null pointer dereference나 BOF인데, 이는 단순히 무해한 버그일 수도 있고 근본적인 취약점이 발생하는 원인이 될 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 발견들을 분류하기 위해서 afl에서는 crash exploration mode를 제공한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;crash를 일으키는 테스트 케이스에 대해서 crash exploration mode로 퍼징을 진행하면 피드백 메커니즘과 EA을 그대로 사용하면서 기존의 crash는 유지할 수 있다.&lt;/li&gt;
&lt;li&gt;그러면서도 instrumentation된 코드 내부에서 얼마나 더 멀리 갈 수 있는지 확인한다.&lt;/li&gt;
&lt;li&gt;이 과정에서 crash를 일으키지 않는 변이는 버려지며, path를 유의미하게 바꾸지 못하는 변이도 버려진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어진 많은 corpus들은 기존 crash 테스트 케이스와 비교했을 때 살짝 다른 연관된 테스트 케이스이다. 이를 통해 해당 crash에 대해 어느정도의 제어를 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 이런 변형으로 OOB를 통과할 수 있는지 확인하고, 만약 가능하다면 그 다음에 어떤 일이 발생하는지 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 다음은 일반 모드로 발견한 crash 테스트 케이스이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;unrtf[7942]: segfault at 450 ip 0805062b sp bf957e60 error 4 in unrtf[8048000+1c000]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 테스트 케이스는 segmentation fault를 유발하는데, crash exploration mode를 통해 파일을 조작했을 때 &lt;b&gt;임의의 주소&lt;/b&gt;에서 결함을 유도할 수 있다는걸 발견할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;unrtf[28809]: segfault at 88077782 ip 0805062b sp bff00210 error 4 in unrtf[8048000+1c000]
unrtf[26656]: segfault at 7271250 ip 0805062b sp bf957e60 error 4 in unrtf[8048000+1c000]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증하는 과정에서 입력은 trimming되지 않는다는 점에서 기존 퍼징과 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Fork server&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 퍼저는 성능을 향상시키기 위해 초기화 한 직후의 메모리를 복사해 퍼징을 반복할때마다 메모리를 다시 복구하는 방식을 사용하여 프로그램을 초기화하는 오버헤드를 줄인다 . (in-memory)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 fork server를 채용해 초기화 오버헤드를 줄인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LD_PRELOAD, PTRACE_POKETEXT, ELF 바이너리 또는 instrumentation 단계에서 fork server을 실행하는 코드가 삽입되어 프로그램의 초기화 과정이 끝난 뒤 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 과정은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 지정된 지점에 도달&lt;/li&gt;
&lt;li&gt;퍼저의 명령에 의해 fork()를 호출해 새로운 프로세스를 생성.&lt;/li&gt;
&lt;li&gt;해당 프로세스가 대신 퍼징을 진행하고 결과를 부모에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하여 초기화 오버헤드를 줄였으며, 일반적인 이미지 라이브러리의 퍼징 속도를 2배 이상 높혔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Parallelization&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 병렬화를 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 AFL은 하나의 코어에 하나의 인스턴스를 지원하여 멀티 코어로 퍼징을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 하나의 PUT에 여러 퍼저를 사용하면 각 인스턴스끼리 테스트 케이스를 공유할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 AFL에서는 테스트 케이스를 실시간으로 동기화하는 방법을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Single-system parallelization
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬 시스템에서 멀티 코어로 작업을 병렬화할 때는 공유 디렉토리를 만들어 인스턴스끼리 queue를 공유한다.&lt;/li&gt;
&lt;li&gt;인스턴스는 Master와 Secondary로 나뉜다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Master: deterministic한 작업을 수행한다.&lt;/li&gt;
&lt;li&gt;Secondary: non-deterministic (havoc)한 작업을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Multi-system parallelization
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 원리는 single-system parallelization과 비슷하지만 공유 파일 동기화에서 차이가 난다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로컬(메인) 컴퓨터의 output 디렉토리의 queue를 다른 모든 머신과 동기화한다.&lt;/li&gt;
&lt;li&gt;동기화는 30분 간격으로 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Binary-only instrumentation&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 바이너리 파일에 대해서도 커버리지를 측정할 수 있도록 QEMU의 user emulation mode를 변형하여 사용한다.&lt;/p&gt;
&lt;aside&gt; 
&lt;p data-ke-size=&quot;size16&quot;&gt;QEMU - CPU및 주변 장치를 소프트웨어적으로 에뮬레이션하는 오픈소스 에뮬레이터.&lt;/p&gt;
&lt;/aside&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;QEMU는 Basic block를 translation unit으로 하여 instrumentation을 할 때 다음과 같은 코드를 삽입한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;if (block_address &amp;gt; elf_text_start &amp;amp;&amp;amp; block_address &amp;lt; elf_text_end) {
    cur_location = (block_address &amp;gt;&amp;gt; 4) ^ (block_address &amp;lt;&amp;lt; 8);
    shared_mem[cur_location ^ prev_location]++; 
    prev_location = cur_location &amp;gt;&amp;gt; 1;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 소스코드가 있는 파일의 instrumentation과 같은 기능을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에뮬레이터로 바이너리 파일을 번역하다보니 새로운 BB를 발견할때마다 번역하는 시간이 오래걸린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해서 AFL의 부모 프로세스와 QEMU 사이에 채널을 만들어, 새롭게 번역된 블록을 translation cache에 저장해 나중에 다른 프로세스가 같은 블럭을 실행할 때 번역 시간을 단축시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;afl-analyze tool&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;trimming 알고리즘을 응용해서 파일의 어느 부분을 변형하면 어떤 결과가 예측된다는 주석을 달아주는 도구이다.&lt;/p&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;AFL++&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL++는 기존 AFL을 토대로 파생된 다른 알고리즘이나 퍼저를 합쳐 성능 향상과 편의성을 추가한 퍼저이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Scheduling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 튜플별로 점수를 매겨 favored 시드를 선정해 해당 시드에 대해 실행을 더 많이했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL++에서는 여기서 더 세분화된 AFLFast를 기반으로 다양한 스케줄링을 추가했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AFLFast
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 경로를 찾거나 드물게 실행되는 희귀 경로(Rare paths)를 탐색하는데 우선순위를 두도록 적합도 기준과 선택 알고리즘을 수정함.&lt;/li&gt;
&lt;li&gt;선택된 구성을 몇 번이나 퍼징할지 결정하는 파워 스케줄 (power schedule) 개념을 도입했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기에는 실행 횟수를 적게해 탐색에 집중하고, 시간이 지날수록 유망한 구성을 지속적으로 퍼징하는 활용을 보장한다.&lt;/li&gt;
&lt;li&gt;동일한 경로를 실행하는 입력값이 많아질수록 해당 경로를 덜 실행하여, 자주 실행되지 않는 구성의 탐색을 촉진한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 과정에서 마르코프 체인 모델을 사용함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄링 종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fast - 발견 빈도에 반비례해서 에너지 지수적 증가.&lt;/li&gt;
&lt;li&gt;coe - 발견 빈도가 낮은 시도에 더 많은 에너지 할당.&lt;/li&gt;
&lt;li&gt;explore - 모든 시드에 대해 일정하게 낮은 에너지를 할당. 초기 탐색에 유리.&lt;/li&gt;
&lt;li&gt;quad - 시드 선택 횟수의 제곱에 비례하며 경로 발견 빈도에 반비례하게 에너지 할당.&lt;/li&gt;
&lt;li&gt;lin - 시드 선택 횟수에 비례하며 경로 발견 빈도에 반비례하게 에너지 할당.&lt;/li&gt;
&lt;li&gt;exploit - 모든 시드에 대해 일정하게 높은 에너지를 할당.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 추가적으로 두 개의 스케줄링을 추가했다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mmopt - 새롭게 발견된 path를 더 깊게 탐색하기 위한 시드에 가중치 부여&lt;/li&gt;
&lt;li&gt;rare - 시드의 실행 속도와 관계 없이 다른 시드에서 발견되지 않은 path에 가중치 부여&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Mutator&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL++는 커스텀 스케줄링, 뮤테이션, 최소화를 할 수 있도록 다양한 Custom Mutator API를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 REDQUEEN 연구를 기반으로 한 Input-To-State (I2S) mutator와, MOpt의 Pilot, Core 모드를 구현했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Instrumentation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AFL에는 튜플의 hit count가 255에서 0으로 오버플로우되는 문제가 있었는데, AFL++에서 오버플로우시 최소 1의 값은 유지하도록 하는 NeverZero를 구현했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;339&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdFQaX/dJMcaa5PGAR/fcOy3YZ1Ev0L7ZkxPY7850/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdFQaX/dJMcaa5PGAR/fcOy3YZ1Ev0L7ZkxPY7850/img.png&quot; data-alt=&quot;instrumentation을 위해 LLVM, QEMU, UNICORN 등 다양한 환경을 제공한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdFQaX/dJMcaa5PGAR/fcOy3YZ1Ev0L7ZkxPY7850/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdFQaX%2FdJMcaa5PGAR%2FfcOy3YZ1Ev0L7ZkxPY7850%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1194&quot; height=&quot;339&quot; data-origin-width=&quot;1194&quot; data-origin-height=&quot;339&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;instrumentation을 위해 LLVM, QEMU, UNICORN 등 다양한 환경을 제공한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL에서 더 발전하여 LLVM은 Ngram/Cix coverage를 통해 단순히 어떤 엣지를 지나왔는지 뿐만 아니라 함수가 어디서 호출됐는지 또는 현재 블록에서부터 N개의 블록 순서를 기록하여 경로를 기록하는 방법도 제공한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Multi-thread의 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 AFL++는 multi-thread를 완벽히 지원하지 않는다. 그 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLVM이 아닌 다른 백엔드 (QEMU, UNICORN 등)에서는 테스트 케이스를 전달할 때 파일시스템을 사용.&lt;/li&gt;
&lt;li&gt;특정 타겟의 경우 fork() system call에 의존함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇기에 커널에서 낭비되는 시간이 많아 생각보다 효율이 나오지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 해결 방법으로 Snapshot LKM을 개발하여 fork()를 호출할 때 발생하는 커널 락의 영향을 최소화 시켰다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 현재 AFL++는 thread-safe하며, 앞으로는 병렬 퍼저 사이의 동기화 오버헤드를 최소화면서 multi-threading을 지원하는 것이 과제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Documentation까지 읽는건 처음인데 좀 어렵긴 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 내가 원래 유전 알고리즘에 관심이 있고 그게 퍼징에 어떻게 적용되는지 알 수 있어서 재밌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 White fuzzer의 Symbolic execution에 대해서 공부해야한다. 어려울수록 재미를 느끼기에 굉장히 좋다&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <category>보안</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/6</guid>
      <comments>https://pumisj.tistory.com/6#entry6comment</comments>
      <pubDate>Tue, 31 Mar 2026 11:52:30 +0900</pubDate>
    </item>
    <item>
      <title>[논문 정리] The Art, Science, and Engineering of Fuzzing: A Survey</title>
      <link>https://pumisj.tistory.com/5</link>
      <description>&lt;h1&gt;서론&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL/AFL++를 쓰면서도 용어가 헷갈리고, 어떤 알고리즘으로 퍼징이 돌아가는지 정확히는 모르고있었다. 그래서 AFL++의 공식 Document를 읽어봤는데 개념에 대해 생략된 부분이 많이 있어 이해하기가 어려웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 퍼징에 대한 용어와 전반적인 원리 이해를 위해 해당 논문을 읽고 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문을 제대로 읽는건 처음이라 초반에 논문 선택에 어려움이 좀 있었지만 어쨋든 다 읽어서 기분이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NUiYF/dJMcadIbz8H/uQVKmVKdkKJ66W47tX5cPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NUiYF/dJMcadIbz8H/uQVKmVKdkKJ66W47tX5cPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NUiYF/dJMcadIbz8H/uQVKmVKdkKJ66W47tX5cPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNUiYF%2FdJMcadIbz8H%2FuQVKmVKdkKJ66W47tX5cPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1154&quot; height=&quot;154&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원문 : &lt;a href=&quot;https://arxiv.org/abs/1812.00140&quot;&gt;https://arxiv.org/abs/1812.00140&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1774792289315&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;The Art, Science, and Engineering of Fuzzing: A Survey&quot; data-og-description=&quot;Among the many software vulnerability discovery techniques available today, fuzzing has remained highly popular due to its conceptual simplicity, its low barrier to deployment, and its vast amount of empirical evidence in discovering real-world software vu&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/1812.00140&quot; data-og-url=&quot;https://arxiv.org/abs/1812.00140v4&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/wTTbO/dJMb9g5a7O2/dIZskNEOJfOEXJ2KmdvMI0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/b9TG2r/dJMb87NWn5X/jrcsMXFnZiF76QN2KTT5zK/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1812.00140&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/1812.00140&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/wTTbO/dJMb9g5a7O2/dIZskNEOJfOEXJ2KmdvMI0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/b9TG2r/dJMb87NWn5X/jrcsMXFnZiF76QN2KTT5zK/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;The Art, Science, and Engineering of Fuzzing: A Survey&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Among the many software vulnerability discovery techniques available today, fuzzing has remained highly popular due to its conceptual simplicity, its low barrier to deployment, and its vast amount of empirical evidence in discovering real-world software vu&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Abstract&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘날에는 여러 소프트웨어 테스트 기술이 많이 있지만, 퍼징은 그 개념적 단순함, 낮은 장벽의 배포, 그리고 실전성을 입증하는 다양한 증거들로 인해여전히 많은 인기를 누리고있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 최근 몇 년 동안 급증한 퍼징에 대한 연구로 인해 퍼징에 대한 이해와 일관적인 시각을 얻기가 힘들다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 논문에서는 현대 퍼징의 계보와 함께 통합되고, 범용적인 퍼징 모델에 대해서 얘기한다.&lt;/p&gt;
&lt;h1&gt;퍼징이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1990년대에 소개된 퍼징이라는 기법은 소프트웨어의 정확성과 안정성을 테스트할 수 있는 가장 광범위하게 배포된 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 틀에서 봤을 때, 퍼징은 문법적, 또는 의미적으로 정상적이지 않은 인풋을 생성해 반복적으로 프로그램을 실행시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어도비, 시스코, 구글, 마이크로소트와 같은 주요 기업들이 그들의 보안 개발 관행을 위해 이 퍼징을 채택하고있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;용어 정리&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PUT(Program Under Test)&lt;/b&gt;: 퍼징의 대상이 되는 프로그램&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fuzzing&lt;/b&gt;: PUT가 기대하는 input space를 벗어난 입력값으로 실행하는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fuzz Testing&lt;/b&gt;: PUT가 정확성 정책을 위반하는지 확인하기 위해 테스트에 퍼징을 사용하는 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fuzzer&lt;/b&gt;: PUT에 퍼징을 수행하는 프로그램&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fuzz Campaign:&lt;/b&gt; 특정 정확성 정책을 바탕으로 PUT에 대해 퍼저를 실행하는 구체적인 과정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Bug Oracle&lt;/b&gt;: 퍼저로 PUT를 실행했을 때 그 결과가 특정 정확성 정책을 위반했는지 결정하는 프로그램으로, 퍼저의 일부분일 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fuzz configureation:&lt;/b&gt; 퍼즈 알고리즘의 파라미터, 설정값. 이 설정값은 퍼저의 동작방식에 따라 달라진다. Queue에 쌓인 시드, 실행 속도, 발견한 커버리지 정보같은 메타데이터, 스케줄링의 대상 등을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;퍼저의 종류&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Black-box fuzzer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램의 내부 로직은 무시하고 입력과 출력 행동만을 관찰하는 퍼저.&lt;/li&gt;
&lt;li&gt;Peach, funfuzz 등 전통적인 퍼저들이 대부분 여기에 속하고, 프로그램을 직접 분석하는 대신 입력값의 구조적인 정보를 활용하여 더 의미있는 테스트 케이스를 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;White-box fuzzer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주로 DSE(Dynamic Symbolic Execution)이나 Concolic 테스팅 기법을 사용하여 프로그램을 탐색하는 퍼저.&lt;/li&gt;
&lt;li&gt;프로그램의 내부 세만틱을 추론할 수 있어 깊은 로직까지 파악할 수 있지만, 동적 instrumentation과 SMT solver로 인해 블랙박스보다 오버헤드가 높다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Grey-box fuzzer
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화이트박스처럼 전체 세만틱을 분석하지는 않지만, 정적 분석을 수행하거나 code coverage와 같은 동적 정보를 수집한다.&lt;/li&gt;
&lt;li&gt;완벽하지는 않아도 근사화된 정보를 활용하여 화이트박스보다 더 빠른 속도로 많은 입력을 테스트할 수 있다.&lt;/li&gt;
&lt;li&gt;AFL이 대표적인 grey-box fuzzer이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DSE란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램의 변수를 10, 20같은 숫자가 아니라 x, y같은 기호로 변환해, 분기가 나올때마다 계산식을 업데이트하며 조건 공식을 구한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;void function(int x) {
    int y = x + 10;
    if (y &amp;gt; 20) {           
        if (x &amp;lt; 15) {       
            // 버그 발생 지점
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 위의 코드에서 첫 번째 if문을 만나면 내부적으로 $x + 10 &amp;gt; 20$ 이라는 조건을 기록하고 두 번째 if문을 만나면 앞의 조건에 $x &amp;lt; 15$ 라는 조건을 쌓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 조건문을 통과하려면 $10 &amp;lt; x &amp;lt; 15$ 라는 공식을 해결하는 x를 찾아야하는데, 이때 SMT solver를 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Concolic이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concrete와 symbolic의 합성어로, 실제 값(concrete)와 상징 값(symbolic) 분석을 동시에 진행하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상징적 분석만으로는 계산하기 복잡한 부분에서 실제 값을 참조하여 식을 단순화 하는 방법이다.&lt;/p&gt;
&lt;h1&gt;Fuzz Testing Algorithm&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckKYTl/dJMcagSu0C1/3tFfBWH6xKoQ3TZLzkXWl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckKYTl/dJMcagSu0C1/3tFfBWH6xKoQ3TZLzkXWl0/img.png&quot; data-alt=&quot;일반적인 퍼즈 테스팅 알고리즘&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckKYTl/dJMcagSu0C1/3tFfBWH6xKoQ3TZLzkXWl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckKYTl%2FdJMcagSu0C1%2F3tFfBWH6xKoQ3TZLzkXWl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;747&quot; height=&quot;410&quot; data-filename=&quot;image.png&quot; data-origin-width=&quot;747&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반적인 퍼즈 테스팅 알고리즘&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼즈 테스팅은 일반적으로 다음과 같은 절차로 진행되며, black-, grey-, white-box 퍼징에 대해서 모두 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 알고리즘은 퍼징 구성들의 집합인 $C$와 제한 시간인 $t_{limit}$을 입력으로 받아 버거의 집합인 B를 출력한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Preprocess&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자로부터 입력받은 퍼징 구성 집합을 입력값으로 받아 수정될 수 있는 퍼징 구성 집합을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 퍼징 알고리즘에 따라 Preprocess 과정에서는 PUT에 instrumentation 코드를 삽입하거나 시드 파일의 실행 속도를 측정하는 동작을 수행할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Schedule&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자로부터 입력받은 $C$와 $t_{limit}$, 그리고 현재 시간인 $t_{elapsed}$를 입력값으로 받아 현재 퍼징 반복에 사용될 퍼징 구성(conf)을 반환한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;InputGen&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;conf&lt;/i&gt;을 입력값으로 받아 구체적인 test cases인 $tcs$를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 과정에서 tcs를 생성할 때 conf값을 사용하는 퍼저도 있고, 또는 모델이나 문법을 파라미터로 사용하는 퍼저도 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;InputEval&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;conf&lt;/i&gt;, &lt;i&gt;tcs&lt;/i&gt;, 그리고 버그 오라클 $O_{bug}$를 입력값으로 받아 &lt;i&gt;tcs&lt;/i&gt;를 입력값으로 PUT를 실행해 $O_{bug}$을 이용하여 정확성 정책을 위반하는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 반환값으로 찾아낸 버그 $B'$와 퍼징에 실행된 정보 &lt;i&gt;execinfos&lt;/i&gt;를 반환한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ConfUpdate&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$C$와 &lt;i&gt;conf, execinfos&lt;/i&gt;를 입력값으로 받아 다음 실행에 사용될 업데이트된 $C$을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL와 같은 많은 grey-box 퍼저는 execinfos를 기반으로 한 퍼징 구성을 도출한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Continue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;ConfUpdate&lt;/i&gt;&lt;/b&gt;에서 반환받은 $C$를 입력값으로 하여 새롭게 퍼징 반복을 해야하는지 나타내는 boolean값을 결과값으로 반환한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Preprocess&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리 과정에서는 다음과 같은 작업을 수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PUT에 instrument코드 삽입&lt;/li&gt;
&lt;li&gt;Seed selection&lt;/li&gt;
&lt;li&gt;Seed trimming&lt;/li&gt;
&lt;li&gt;드라이버 프로그램을 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 InputGen 단계에서도 모델을 만들 때 사용되기도 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Instrumentation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블랙박스 퍼저와는 다르게 그레이, 화이트박스 퍼저는 실행 피드백을 수집하기 위해&lt;br /&gt;PUT에 instrument 코드를 삽입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;instrumentation는 static, dynamic 두 종류가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Static instrumentation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 컴파일 단계에서 수행되며 dynamic instrumentation보다 런타임 오버헤드가 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 PUT가 외부 라이브러리를 사용한다면, 해당 라이브러리도 따로 instrument해야한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dynamic instrumenation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 평가(InputEval)단계에서 수행되며, 런타임중에 동적으로 컴파일 단계에서 수행되는 dynamic instrumentation보다 오버헤드는 심하지만 런타임중에 수행되어 관련된 라이브러리를 쉽게 instrument할 수 있다는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 dynamic instrumentation 도구:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DynInst&lt;/li&gt;
&lt;li&gt;DynamoRIO&lt;/li&gt;
&lt;li&gt;Pin&lt;/li&gt;
&lt;li&gt;Valgrind&lt;/li&gt;
&lt;li&gt;QEMU (AFL이 사용함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼저는 static, dynamic 둘 다 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;grey-box 퍼저는 실행 피드백을 인풋값으로 사용하여 더 나은 TCS를 생성하는데, AFL의 경우에는 그 방법으로 branch coverage를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 수집된 branch coverage 정보는 compact bit vector형태로 저장되는데, 서로 다른 경로가 같은 위치에 기록되는 path collisions가 발생할 수 있어 부정확해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 CollAFL는 path-sensitive hash function을 도입하여 이런 문제를 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Race condition은 프로그램의 Non-deterministic한 동작에 의존하기 때문에 발생 빈도가 낮고, 일반적으로는 테스트하기 힘들다. &amp;rarr; instrumentation을 사용하면 스레드의 실행순서를 정할 수 있어 해당 상황을 재현할 수 있다. 또한 스레드를 무작위로 스케줄링하는것이 race condition 버그를 찾는데 효과적이라는 연구 결과가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;In-memory Fuzzing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GUI나 네트워크 프로그램같은 대규모 프로그램에 대해 퍼징할 때 프로세스를 매번 새로 생성하는건 오버헤드가 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 해결책중 하나는 프로그램이 실행되고 난 직후의 메모리 상태를 복사하는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TCS에 대해서 퍼징을 진행하고, 끝나면 복사한 메모리를 다시 복구하여 실행 오버헤드 없이 다시 퍼징을 진행하는 방법으로 GRR이 이 방식을 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 메모리를 복사하는 대신 fork server를 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;In-memory API Fuzzing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In-memory Fuzzing이 한 프로세스를 종료하고 다시 시작하는거라면 In-memory API Fuzzing은 프로세스를 종료하지 않고 루프 내에서 특정 함수만 반복해서 퍼징하는 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 이를 persistent mode라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 다음과 같은 기술적 한계가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재현 불가 문제
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대상 함수에 대한 유효한 호출 컨텍스트(calling context)를 구성하는 것이 항상 가능하지 않다.&lt;/li&gt;
&lt;li&gt;여러 차례의 함수 호출 과정에서 캡처되지 않은 부작용(전역 변수 상태 변경 등)이 누적될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In-memory API fuzzing은 함수를 시작하기 좋은 진입점을 찾아야하는데, 이는 굉장히 어렵다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Seed Selection&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼징 configuration중 하나인 시드(입력값)는 그 범위가 매우 넓고 많다. 그 시드들을 모두 테스트할 수 없으니 초기 시드 풀의 크기를 효율적으로 줄이는 문제가 Seed Selection Problem이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 방법은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Minset: 코드 커버리지를 최소화하는 최소한의 시드 집합을 찾는다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시드 $s_1$이 주소 ${10, 20}$를 커버하고, $s_2$가 주소 ${20, 30}$을 커버할 때, 주소 ${10, 20, 30}$을 모두 커버하는 $s_3$가 있다면 $s_3$를 사용하는 것이 실행 시간 측면에서 유리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Miller의 보고서에 따르면 code coverage가 1% 증가할때마다 버그를 찾을 확률이 0.92% 증가한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업은 &lt;i&gt;ConfUpdate&lt;/i&gt; 과정에서도 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL의 경우 각 브랜치에 대해서 logarithmic counter로 branch coverage를 측정하여 Minset을 만드는데, 그 이유는 동일한 분기가 10번, 11번 실행될 경우 모두 다른 경우로 취급하면 시드가 충분히 줄어들지 않기 때문에, 유의미한 실행 패턴 변화만을 저장하기 위해 로그 카운터를 사용한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Seed Trimming&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시드의 크기가 작을수록 메모리 소비가 적고 Throughput이 커진다. 따라서 퍼징 시작 전이나 과정 중에 시드 크기를 줄이는 시드 트리밍을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL의 경우 동일 커버리지를 유지하는 선에서 시드를 반복적으로 일부분 제거하는 방식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 시드 크기에 우선순위를 부여하는 알고리즘이 무작위로 시드를 선택하는것보다 오히려 unique bug를 발견할 가능성이 적다는 연구 결과도 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Scheduling&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄링은 다음 퍼징에 사용할 퍼징 구성을 선택하는 과정이다. BFF나 AFLFast의 경우 스케줄링 알고리즘이 주된 성능 향상의 원인이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화이트박스 퍼저는 그레이, 블랙박스와 다르게 더 복잡한 설정이 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The Fuzz Configuration Scheduling(FCS) Problem&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄링은 현재 정보를 분석해서 unique bug 발견을 극대화하거나, 커버리지를 최대화하는 Fuzz Configuration을 선택하는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 스케줄링 알고리즘은 탐색 (Exploration)과 활용 (Exploitation) 둘 중에 어느것에 비중을 둘지를 선택해야한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;탐색: 더 나은 결정을 내리기 위해 각 구성에 대한 정확한 정보를 수집하는데 시간을 투자.&lt;/li&gt;
&lt;li&gt;활용: 현재 가장 좋은 결과를 낼 수 있다고 믿어지는 퍼징 구성에 대해 퍼징을 수행.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 과정에서 사용되는 정보는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;현재 퍼징 구성 $C$&lt;/li&gt;
&lt;li&gt;현재 시간 $t_{elapsed}$&lt;/li&gt;
&lt;li&gt;제한 시간 $t_{limit}$&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Schedule&lt;/i&gt; 단계에서는 어떤 Configuration을 선택할지 결정만 내리고, 실질적인 데이터는 &lt;i&gt;Preprocess*와 *ConfUpdate&lt;/i&gt; 단계에서 이루어진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Black-box FCS Algorithms&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블랙박스 퍼저는 오직 configuration별 퍼징 결과(발견된 크래시/버그 수, 소요 시간) 정보만 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Householder와 Foote는 블랙박스 변이 퍼징을 베르누이 실행의 반복이라고 보고 성공확률($\frac{\text{#unique crashes}}{\text{#runs}}$)이 높은 구성에 가중치를 두었다.&lt;/li&gt;
&lt;li&gt;Woo는 이를 더 발전시켰다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 베르누이 시행 모델에서 Weighted Coupon Collector&amp;rsquo;s Problem with Unkown Weights (WCCP/UW) 모델로 재정의했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;WCCP/UW: 매 시도마다 성공 확률이 점점 낮아진다는 수학적 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;퍼징에 MAB 알고리즘을 적용시켰다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MAB: 탐색, 활용 중 어떤 것에 자원을 더 할당할건지 선택하는 머신러닝 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;퍼징 구성에 대한 성공 확률 시간에 따라 정규화해서, 더 빠른 구성에 우선순위를 부여함.&lt;/li&gt;
&lt;li&gt;퍼징 반복을 고정된 퍼징 수행 횟수로 보는게 아니라, 고정된 수행 시간으로 재정의하여 더 느린 퍼징 구성에 대한 우선순위를 낮췄다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Grey-box FCS Algorithms&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그레이박스 환경에서는 코드 커버리지와 같은 정보를 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AFL
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AFL은 유전 알고리즘 (evolutionary algorithm, EA)를 도입한 퍼저이다.&lt;/li&gt;
&lt;li&gt;여러 개의 구성으로 이루어진 population을 유지하며 각 구성은 고유한 적합도 (fitness)를 가진다.&lt;/li&gt;
&lt;li&gt;mutation이나 recombination과 같은 유전적 변형으로 적합한 구성을 선택한다.&lt;/li&gt;
&lt;li&gt;이렇게 선택된 자손 구성들은 부모보다 더 높은 적합도를 가질 가능성이 크며, 다음 퍼징 반복에 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL은 다음 세 가지를 정의한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;적합도의 기준: 특정 control-flow edge를 실행하는 여러 구성 중, 가장 빠르고 크기가 작은 입력을 적합(favorite)하다고 판단한다.&lt;/li&gt;
&lt;li&gt;구성 선택 방법: 퍼징 구성에 대해서 circular queue를 유지하며, 순차적으로 적합한 구성을 선택한다.&lt;/li&gt;
&lt;li&gt;선택된 구성의 사용: 실행 속도가 빠르고 분기 커버리지가 높은 구성에 더 많은 실행 횟수를 할당한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFLFast는 기존 스케줄링 방식을 모두 개선했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AFLFast
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운 경로를 찾거나 드물게 실행되는 희귀 경로(Rare paths)를 탐색하는데 우선순위를 두도록 적합도 기준과 선택 알고리즘을 수정했다.&lt;/li&gt;
&lt;li&gt;선택된 구성을 몇 번이나 퍼징할지 결정하는 파워 스케줄 (power schedule) 개념을 도입했다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기에는 실행 횟수를 적게해 탐색에 집중하고, 시간이 지날수록 유망한 구성을 지속적으로 퍼징하는 활용을 보장한다.&lt;/li&gt;
&lt;li&gt;동일한 경로를 실행하는 입력값이 많아질수록 해당 경로를 덜 실행하여, 자주 실행되지 않는 구성의 탐색을 촉진한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Input Generation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버그를 일으키는데 직접적으로 관련이 있는 부분으로, 퍼저에서 가장 영향력 있는 과정이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Generation-based fuzzer (model-based)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PUT가 기대하는 입력을 설명하는 모델을 바탕으로 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mutation-based fuzzer (model-less)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 시드를 변형하여 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;InputGen*은 *Preprocess&lt;/i&gt; 또는 &lt;i&gt;ConfUpdate&lt;/i&gt;과정에서 얻은 퍼징 구성을 입력으로 받는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Model-based Fuzzers&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model-based 퍼저는 입력을 규정하는 문법 또는 실행 방식을 설명하는 모델을 사용하여 테스트 케이스를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 정의되어있는 모델을 사용하는 방식이 있으며, 이를 &lt;b&gt;Predefined Model&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 직접 입력을 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Peach, PROTOS, Dharma 등은 사용자가 제공한 명세서를 입력으로 받음&lt;/li&gt;
&lt;li&gt;Autodaf&amp;eacute;, Sulley, SPIKE, LibFuzzer 등은 분석가가 직접 입력 모델을 코드로 작성할 수 있는 API를 노출.&lt;/li&gt;
&lt;li&gt;Tavor는 EBNF(Extended Backus-Naur Form)로 작성된 명세를 입력받아 해당 문법을 준수하는 테스트 케이스를 만듦.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네트워크 및 커널 등 특정 도메인 특화 모델
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네트워크 프로토콜: PROTOS, SNOOZE, KiF 등은 사용자로부터 프로토콜 사양을 입력받음&lt;/li&gt;
&lt;li&gt;커널 API: 시스템 콜의 인수 개수와 타입을 정의한 시스템 콜 템플릿을 모델로 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Built-in Model
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;퍼저 자체에 특정 언어나 문법에 대한 내용이 있음&lt;/li&gt;
&lt;li&gt;웹/브라우저: cross_fuzz와 DOMfuzz는 랜덤한 DOM 객체를 생성하고, jsfunfuzz는 자체 문법 모델을 바탕으로 구문론적으로 올바른 JavaScript 코드를 생성&lt;/li&gt;
&lt;li&gt;파일 형식: QuickFuzz는 파일 형식을 설명하는 기존의 Haskell 라이브러리를 활용&lt;/li&gt;
&lt;li&gt;프로토콜 전용: TLS-Attacker(TLS), llfuzzer(NFC) 등은 특정 프로토콜 모델을 내장하고있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문법적으로 맞는 테스트 케이스뿐만 아니라 의미적으로도 맞는 테스트케이스를 생성하는 다양한 퍼저들도 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 직접 모델을 추론하는 방법도 관심을 받고있으며, 이를 &lt;b&gt;Inferred Model&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;instrumentation과 마찬가지로 모델 추론도 &lt;i&gt;Preprocess&lt;/i&gt;와 &lt;i&gt;ConfUpdate&lt;/i&gt;단계에서 이루어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전처리 과정에서의 추론
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TestMiner: PUT 내부의 리터럴(literals) 같은 데이터를 검색하여 적절한 입력을 예측.&lt;/li&gt;
&lt;li&gt;IMF: 시스템 API 로그를 분석하여 커널 API 모델을 학습하고, 이를 바탕으로 API 호출 시퀀스를 포함하는 C 코드를 생성.&lt;/li&gt;
&lt;li&gt;Skyfire: 시드와 문법을 바탕으로 확률적 문맥 민감 문법(probabilistic context-sensitive grammar)을 추론하여 의미론적으로 유효한 입력을 생성.&lt;/li&gt;
&lt;li&gt;CodeAlchemist: 자바스크립트 코드를 code bricks로 분해한 뒤, 이들을 조립할 수 있는 제약 조건을 정적/동적 분석으로 계산하여 유효한 테스트 케이스를 생성.&lt;/li&gt;
&lt;li&gt;Neural &amp;amp; Learn&amp;amp;Fuzz: 신경망을 사용하여 기존 테스트 파일로부터 모델을 학습하고 새로운 케이스를 생성.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;구성 업데이트에서의 추론 (퍼징을 진행하면서 각 반복이 끝날 때마다 모델을 실시간으로 업데이트)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PULSAR: 캡처된 네트워크 패킷에서 프로토콜 모델을 자동으로 추론하며, 내부적으로 State machine을 구축하여 더 많은 상태를 커버하도록 입력을 생성.&lt;/li&gt;
&lt;li&gt;Doup&amp;eacute; et al.: I/O 동작을 관찰하여 웹 서비스의 상태 머신을 추론하고 웹 취약점을 스캔.&lt;/li&gt;
&lt;li&gt;Ruiter et al.: TLS 프로토콜을 타겟으로 비슷한 상태 추론 기법을 적용&lt;/li&gt;
&lt;li&gt;GLADE: I/O 샘플로부터 Context-free grammar을 합성하여 퍼징에 활용&lt;/li&gt;
&lt;li&gt;go-fuzz: 시드 풀에 추가되는 각 시드마다 개별적인 모델을 구축하여 새로운 입력을 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 퍼저들이 테스트 케이스를 변형하는거라면, 프로그램 자체를 변형하는 퍼저도 있다. 이를 &lt;b&gt;Encoder Model&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MutaGen은 입력값이 아니라 데이터를 생성하는 인코딩 프로그램을 변이하여 내부적으로 변형된 테스트 케이스를 생성하도록 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인코더 프로그램의 Dynamic program slice를 계산하여 그 부분을 변형하여 실행.&lt;/li&gt;
&lt;li&gt;변형된 인코더는 정상적인 포맷에서 비정상적인 테스트 케이스를 생성하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Model-less Fuzzers&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 무작위 방식의 테스트 방법은 특정 분기분에서 효과적이지 못하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 조건문이 있다고 해보자&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;if (input == 42)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조건문을 무작위 테스트 케이스로 통과한다고 했을 때 인풋이 32비트 정수라면 통과할 확률이 $1/2^{32}$이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 처음부터 정상적인 시드값에서 일부 값만 변형하여 테스트 케이스를 생성하는 방법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 값을 변형하는 방법에는 여러가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bit-flipping
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순히 고정된 비트를 뒤집거나, 무작위 비트를 뒤집일수도 있다.&lt;/li&gt;
&lt;li&gt;랜덤하게 시드를 변형시키는데 사용자가 파라미터를 조절할 수 있으며, 이를 변이율 (mutation ratio)라고 부른다.&lt;/li&gt;
&lt;li&gt;변이율는 한 번의 입력 생성 과정에서 플리핑알 비트 위치의 수를 결정하는 값으로, N비트 크기의 시드에서 K개의 무작위 비트를 뒤집을 때 변이율은 $K/N$이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼징의 성능은 변이율에 민감하며, 모든 PUT에 적합한 변이율은 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Arithmetic Mutation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택한 바이트 시퀀스에 대해서 정수값이라고 간주하고 그 값에 대해서 간단한 산술연산을 수행한다.&lt;/li&gt;
&lt;li&gt;계산된 값은 그 선택한 바이트 시퀀스 자리로 대체된다.&lt;/li&gt;
&lt;li&gt;이 방법의 핵심은 변이의 효과를 작은 범위로 제한하는거다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, AFL의 경우 시드에서 4바이트의 값 $i$를 선택해 $i \pm r$ 을 수행한 뒤 기존 값을 대체한다.&lt;br /&gt;여기서 보통 r의 값은 사용자가 정하거나 퍼저가 결정하는데, AFL에서는 기본 범위가 $0 \le r \lt 35$ 이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Block-based Mutation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시드 내의 연속된 바이트 시퀀스를 덩어리째 바꾸며, 6가지 방법이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;insert: 무작위로 생성된 블록을 임의 위치에 끼워넣는다 (AFL)&lt;/li&gt;
&lt;li&gt;delete: 시드에서 특정 블럭을 무작위로 제거 (AFL)&lt;/li&gt;
&lt;li&gt;replace: 선택한 블록을 무작위 값으로 교체 (AFL)&lt;/li&gt;
&lt;li&gt;permute: 블록들의 순서를 무작위로 섞음&lt;/li&gt;
&lt;li&gt;resize: 시드 끝에 무작위 블록을 붙여 전체 길이를 늘림&lt;/li&gt;
&lt;li&gt;splicing: 한 시드에서 가져온 블록을 다른 시드의 특정 부분에 넣거나 교체 (AFL)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Dictionary-based Mutation
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그램이 처리할 때 특별한 의미를 가질 확률이 높은 미리 정의된 값을 변이에 활용한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;-1, 0, 1 같은 정수값&lt;/li&gt;
&lt;li&gt;문자열 및 유니코드&lt;/li&gt;
&lt;li&gt;%x, %s와 같은 포맷 스트링&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화이트박스 환경에서도 Model-less와 Model-based를 모두 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 Model-less는 DSE를 사용하고, Model-based는 문법과 같은 입력 모델을 활용한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Input Evaluation&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 생성 후 해당 테스트 케이스에 대해 결과를 평가하는 과정은 input evaluation이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼저의 성능과 효과에 영향을 많이 미치며 많은 최적화와 설계 결정 (design decisions)이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bug Oracles&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적으로 segmentation fault와 같은 에러를 낼 때 버그로 간주한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제가 자동으로 해당 오류를 잡기때문에 추가적인 instrumentation없어 편리하지만, 모든 메모리 취약점이 즉각적인 크래시로 이어지지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, Stack buffer overflow가 일어나더라도 유효하지만 잘못된 주소로 포인터가 덮어씌워지면 프로그램은 오류 없이 잘못된 값을 반환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위한 방법이 Sanitizer이다. 이는 비정상적인 동작을 감지하면 강제로 프로그램을 중단하여 감지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 안정성 오류는 크게 두 개로 나뉜다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공간적 오류 (Spatial)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포인터가 의도된 객체 범위를 벗어나 역참조 될 때 OOB 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간적 오류 (Temporal) 포인터가 더 이상 유효하지 않은 시점에 액세스 될 때 Use-After-free(UAF) 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 탐지 도구로 ASan, MEDS, SoftBound/CETS 등이 있으며 ASan (Address Sanitizer)은 컴파일 시점에 instrumentation을 수행하는 메모리 오류 탐지기이다. shadow memory를 사용하여 한 바이트당 8개의 메모리 주소의 유효성을 확인할 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Undefined Behavior&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C와같은 언어에서는 프로그램이 어떻게 동작해야하는지 정의되지 않은 부분이 있다. 이를 UB라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 a + 1을 했을 때 a가 4바이트 정수 최댓값이고, 이에 대해 오버플로우시 wrap-around 된다고 명시하지 않는다면 컴파일러는 그냥 최적화하여 코드를 없앤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이런 오류를 잡아내는 탐지 도구가 몇 가지 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Memory Sanitizer (MSan): 초기화되지 않은 메모리 (uninitialized memory) 사용을 탐지하며, ASan처럼 shadow memory를 사용한다.&lt;/li&gt;
&lt;li&gt;Undefined Behavior Sanitizer (UBSan): 정렬되지 않은 포인터 사용, 0으로 나누기, 널 포인터 역참조, 정수 오버플로우 등을 탐지한다.&lt;/li&gt;
&lt;li&gt;Thread Sanitizer (TSan): Data race를 탐지한다. 두 개 이상의 스레드가 공유 메모리에 접근하고 그 중 하나가 write일 때 발생하는 non-deterministic한 버그를 잡아낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Input Validation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XSS나 SQL injection같은 취약점은 단순히 크래시만으로 판단하기 어렵다. 따라서 다음과 같은 툴을 사용한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XSS 탐지 (KameleonFuzz)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단순히 응답 값을 보는게 아니라, 실제 웹 브라우저로 입력을 파싱.&lt;/li&gt;
&lt;li&gt;추출된 DOM 트리를 분석하여 공격자가 의도한 패턴이 나타나는지 비교.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SQL 인젝션 탐지 (&amp;micro;4SQLi)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹, 앱과 DB 사이에서 통신을 가로채는 Database Proxy를 사용한다.&lt;/li&gt;
&lt;li&gt;입력값이 DB 쿼리 구조를 위험하게 변형시켰는지 확인.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Semantic Difference&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 정상적으로 종료되지만, 결과가 의도와 다른 경우를 말한다. 이 경우 Differential Testing 기법이 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 기능을 하는 서로 다른 프로그램들에 같은 입력값을 넣고 결과를 비교한다.&lt;/li&gt;
&lt;li&gt;만약 프로그램끼리 결과가 다르다면 둘 중 하나 또는 둘 다 논리적인 버그가 있다고 판단한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Execution Optimization&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼징은 프로그램을 수만, 수억 번 실행해야한다. 이 때 프로그램을 처음부터 로드하고 초기화하는건 시간이 많이 소요되기에 다양한 최적화를 사용한다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fork-server
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AFL이 도입하였으며 프로그램을 실행해 초기화가 끝난 프로세스에서 새로운 입력값마다 fork로 실행하는 방법이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;In-memory Fuzzing
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아예 프로세스를 종료하지 않고 루프를 돌면서 특정 함수만 계속 호출하는 방식.&lt;/li&gt;
&lt;li&gt;하지만 이전 실행에서 초기화되지 않은 부분이 있는 State pollution 위험이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;새로운 시스템 콜
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;퍼징에 최적화된 새로운 시스템 콜로 오버헤드를 낮출 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Triage&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;crash를 유발하는 테스트 케이스를 분석하고 기록하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deduplication, prioritization, minimization 세 과정으로 나뉜다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deduplication
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크래시가 발생한 지점의 Call Stack이나 코드 경로를 비교하여 같은 원인이라면 하나로 묶는다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Prioritization
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보안상 더 위험해 보이는 버그나, 더 자주 발생하는 버그에 우선순위를 부여한다.&lt;/li&gt;
&lt;li&gt;마이크로소프트의 !exploitable 랭킹 시스템의 심각도를 기준으로 우선순위를 부여.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Minimization
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;퍼징이 만든 입력값에서 불필요한 부분을 trimming해 파일 크기 줄인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Configuration Updating&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 실행 결과로 다음 퍼징에 사용할 퍼징 집합 $C$를 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블랙박스 퍼징의 경우 내부 구조를 모르기때문에 보통 $C$를 수정하지 않지만, 그레이/화이트박스 퍼저는 결과를 바탕으로 새로운 시드를 추가하거나 삭제한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Evolutionary Algorithm&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fitness Function으로 시드를 선별하여 다음 인풋으로 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퍼징 효율을 높히기 위해 AFL은 단순히 분기를 지났는지만 보는게 아니라, 그 분기를 볓 번 통과했는지를 기록해 시드 선택에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 4바이트 정수값의 if문은 무작위 변위로 통과하기 힘든데, 이를 1바이트찍 여러 분기로 쪼개 하나씩 통과할때마다 시드에 넣기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 프로그램의 control-flow graph (CFG)를 분석해 도달하기 힘든 Basic block을 실행하는 시드에 가중치를 준다. 반대로 에러 핸들링에 잡히는 시드에는 음수 가중치를 준다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Maintaing a Minset&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 퍼징 구성을 추가하는 과정에서 queue에 너무 많은 시드가 생길 수 있다. &lt;i&gt;Preprocess&lt;/i&gt; 단계에서 한 것처럼 &lt;i&gt;ConfUpdate&lt;/i&gt;단계에서도 동일하게 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AFL의 경우 Minset을 유지하기 위해 시드를 삭제하기보다는 특정 edge를 가장 빠르고 작게 실행하는 시드를 Favorable로 표시해 그렇지 않은 시드보다 더 많이 시도한다.&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 논문의 목표는 현대 퍼징 문헌에 대한 포괄적이고 일관된 시각을 정립하는 것이었다. 이를 위해 먼저 현재 사용되는 다양한 형태의 퍼징을 설명하기 위해 범용 모델 퍼저 (general-purpose model fuzzer)를 제시했다. 그 후 퍼저들의 분류 체계를 설명했으며, 모델 퍼저의 각 단계를 탐구하며 관련 설계 결정을 논의하는 동시에 커뮤니티 전반이 이룬 수많은 성과를 보여주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 연구가 향후 연구들, 특히 용어 사용과 퍼징 알고리즘의 제시에 있어 더 많은 통일성을 가져오는 데 도움이 되기를 희망한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;후기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 정독에는 이해가 안되서 두 세번은 더 읽은거같다. 물론 내가 잘 못 이해한 부분도 있겠지만 어느정도는 퍼징에 대한 기본적인 동작 원리에 대해 좀 흐름이 읽히는거같다. AFL가 코드 커버리지를 어떻게 사용하는지도 알았고 스케줄링, 인풋 생성에 어떤 원리가 적용되었는지 알 수 있어서 좋았다.&lt;/p&gt;</description>
      <category>보안/논문 리뷰</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/5</guid>
      <comments>https://pumisj.tistory.com/5#entry5comment</comments>
      <pubDate>Sun, 29 Mar 2026 22:54:08 +0900</pubDate>
    </item>
    <item>
      <title>[Fuzzing101] exercise 3 CVE-2017-13011 실습 기록</title>
      <link>https://pumisj.tistory.com/4</link>
      <description>&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;일단 이번 실습에서는 목표였던&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;CVE-2017-13028를 찾지 못했기에, 대신 CVE-2017-13011에 대해서 다루겠다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;tcpdump는 Asan으로 디버깅 하는것부터 끝까지 많이 고생했던거같다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;저번과 마찬가지로 실습을 진행하면서 했던 삽질을 모두 적어보려고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;테스트 환경&lt;/h3&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4mtst/dJMcab4zH27/yfj2JKJRjn80nYSmKuUbwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4mtst/dJMcab4zH27/yfj2JKJRjn80nYSmKuUbwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4mtst/dJMcab4zH27/yfj2JKJRjn80nYSmKuUbwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4mtst%2FdJMcab4zH27%2Fyfj2JKJRjn80nYSmKuUbwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;349&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
작년에 마련한 T9 미니PC를 서버로 사용중이다
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;환경은 Ubuntu 서버 OS에 afl++ 공식 도커 이미지를 이용하여 실습을 진행할 예정이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;CVE-2017-13011&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;해당 취약점은 NIC를 거치는 패킷들의 헤더를 출력해주는 tcpdump 프로그램에서 발생했다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;util-print.c 파일의 bittok2str_internal() 함수에서 해당 취약점이 발견되었으며, OOB의 한 종류인 global-buffer-overflow를 일으킨다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;설명에는 4.9.2 이하 버전에서 발생하는 취약점이라고 나와있지만, 4.9.2 버전에서는 4시간정도 돌려도 아무것도 발견하지 못해 결국 4.9.1버전에서 진행하였다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;그러면 바로 실습을 진행하도록 하겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;퍼징&lt;/h3&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 wget으로 tcpdump-4.9.1 버전을 다운로드 받고, 여기에 사용되는 libpcap 라이브러리도 같이 다운로드한다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773911061245&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;wget https://github.com/the-tcpdump-group/tcpdump/archive/refs/tags/tcpdump-4.9.1.tar.gz
tar -xzvf tcpdump-4.9.1.tar.gz

wget https://github.com/the-tcpdump-group/libpcap/archive/refs/tags/libpcap-1.8.0.tar.gz
tar -xzvf libpcap-1.8.0.tar.gz

mv libpcap-libpcap-1.8.0/ libpcap-1.8.0&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 다운로드받은 libpcap-libpcap-1.8.0 디렉토리명을 그대로 쓰면 tcpdump에서 빌드할 때 링커가 이 폴더를 찾지 못한다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;따라서 mv로 디렉토리명을 변경해준 뒤 빌드 후 tcpdump의 tests 디렉토리에 있는 테스트파일로 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diD4mX/dJMcaaYTyqM/j7sXITAKzU7vuIIXdGXHr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diD4mX/dJMcaaYTyqM/j7sXITAKzU7vuIIXdGXHr0/img.png&quot; data-alt=&quot;실행했을 때 정상적으로 출력된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diD4mX/dJMcaaYTyqM/j7sXITAKzU7vuIIXdGXHr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiD4mX%2FdJMcaaYTyqM%2Fj7sXITAKzU7vuIIXdGXHr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1195&quot; height=&quot;276&quot; data-origin-width=&quot;1195&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행했을 때 정상적으로 출력된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 Asan (Address Sanitizer) 모드로 빌드를 했는데, Asan은 구글이 개발한 도구로 잘못된 메모리 액세스 등 메모리와 관련된 오류를 자세히 탐지한다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773911636904&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd $HOME/fuzzing_tcpdump/libpcap-1.8.0/
export LLVM_CONFIG=&quot;llvm-config-11&quot;
CC=afl-clang-lto ./configure --enable-shared=no --prefix=&quot;$HOME/fuzzing_tcpdump/install/&quot;
AFL_USE_ASAN=1 make

cd $HOME/fuzzing_tcpdump/tcpdump-tcpdump-4.9.2/
CC=afl-clang-lto CFLAGS=&quot;-Wno-error=implicit-function-declaration -Wno-error=implicit-int&quot; ./configure --prefix=&quot;$HOME/fuzzing_tcpdump/install/&quot;
AFL_USE_ASAN=1 make
AFL_USE_ASAN=1 make install&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;이번에도 컴파일러는 afl-clang-lto를 사용했으며, tcpdump의 경우에는 오래된 코드이다보니 변수, 함수 타입을 생략해서 자동으로 int가 됐을 때 오류가 발생하여 Makefile을 만드는데 자꾸 실패했다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;이에 대한 해결 방법으로 configure을 실행할 때 위와 같이 오류 대신 경고만 주는 flag를 설정해야한다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773911900775&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;afl-fuzz -i -m none &amp;lt;인풋 디렉토리&amp;gt; -o &amp;lt;아웃풋 디렉토리&amp;gt; -s 123 -- &amp;lt;실행 파일&amp;gt; -vvvvXX -ee -nn -r @@&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;Asan을 사용하면 64bit 시스템에서 많은 가상메모리 공간을 필요로한다. 그래서 -m none을 적어 퍼징할 때 메모리 제한을 해제한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;455&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0l566/dJMcadONXLe/YduEoAfeJPkwjfCORzAgC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0l566/dJMcadONXLe/YduEoAfeJPkwjfCORzAgC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0l566/dJMcadONXLe/YduEoAfeJPkwjfCORzAgC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0l566%2FdJMcadONXLe%2FYduEoAfeJPkwjfCORzAgC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;616&quot; height=&quot;455&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;455&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
위 사진은 퍼징 중간에 캡쳐한거고, 결국&amp;nbsp; &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;CVE-2017-13028와 관련된 crash 파일을 찾지 못해 현재까지 약 3일동안 퍼징을 돌리고있다.&lt;/span&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;오류 발견&lt;/h3&gt;
&lt;pre id=&quot;code_1773912221621&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;=================================================================
==3752954==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5d04c889fb24 at pc 0x5d04c752fdc2 bp 0x7ffed2889c00 sp 0x7ffed28893a0
WRITE of size 17 at 0x5d04c889fb24 thread T0
    #0 0x5d04c752fdc1 in vsnprintf (/home/fuzzing_tcpdump/install/sbin/tcpdump+0x284dc1) (BuildId: 76aca6779ce4a98b9db83df42ae3ce243196ec7d)
    #1 0x5d04c75316c1 in snprintf (/home/fuzzing_tcpdump/install/sbin/tcpdump+0x2866c1) (BuildId: 76aca6779ce4a98b9db83df42ae3ce243196ec7d)
    #2 0x5d04c78d2e22 in bittok2str_internal /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./util-print.c:540:29
    #3 0x5d04c777b925 in bittok2str /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./util-print.c:575:13
    #4 0x5d04c777b925 in lldp_private_8023_print /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./print-lldp.c:872:9
    #5 0x5d04c777b925 in lldp_print /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./print-lldp.c:1616:31
    #6 0x5d04c76bdc80 in ethertype_print /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./print-ether.c:408:3
    #7 0x5d04c76bd1d7 in ether_print /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./print-ether.c:236:7
    #8 0x5d04c7603e1f in pretty_print_packet /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./print.c:339:18
    #9 0x5d04c7603e1f in print_packet /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./tcpdump.c:2506:2
    #10 0x5d04c7a117bc in pcap_offline_read /home/pumisj/fuzzing-study/fuzzing_tcpdump/libpcap-1.8.0/./savefile.c:507:4
    #11 0x5d04c75fab18 in pcap_loop /home/pumisj/fuzzing-study/fuzzing_tcpdump/libpcap-1.8.0/./pcap.c:875:8
    #12 0x5d04c75fab18 in main /home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./tcpdump.c:2009:12
    #13 0x757a6c5141c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #14 0x757a6c51428a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #15 0x5d04c7509254 in _start (/home/fuzzing_tcpdump/install/sbin/tcpdump+0x25e254) (BuildId: 76aca6779ce4a98b9db83df42ae3ce243196ec7d)

0x5d04c889fb24 is located 4 bytes after global variable 'bittok2str_internal.buf' defined in '/home/fuzzing_tcpdump/tcpdump-tcpdump-4.9.1/./util-print.c:524' (0x5d04c889fa20) of size 256
  'bittok2str_internal.buf' is ascii string '10BASE-T hdx, 10BASE-T fdx, 100BASE-T4, 100BASE-TX hdx, 100BASE-TX fdx, 100BASE-T2 hdx, 100BASE-T2 fdx, Pause for fdx links, Asym PAUSE for fdx, Sym PAUSE for fdx, Asym and Sym PAUSE for fdx, 1000BASE-{X LX SX CX} hdx, 1000BASE-{X LX SX CX} fdx, 1000BASE-'
SUMMARY: AddressSanitizer: global-buffer-overflow (/home/fuzzing_tcpdump/install/sbin/tcpdump+0x284dc1) (BuildId: 76aca6779ce4a98b9db83df42ae3ce243196ec7d) in vsnprintf
Shadow bytes around the buggy address:
  0x5d04c889f880: 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9
  0x5d04c889f900: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9
  0x5d04c889f980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x5d04c889fa00: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x5d04c889fa80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=&amp;gt;0x5d04c889fb00: 00 00 00 00[f9]f9 f9 f9 f9 f9 f9 f9 00 00 00 00
  0x5d04c889fb80: 04 f9 f9 f9 04 f9 f9 f9 04 f9 f9 f9 00 00 00 00
  0x5d04c889fc00: 04 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x5d04c889fc80: 00 00 00 00 f9 f9 f9 f9 00 00 00 00 00 00 00 00
  0x5d04c889fd00: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 04 f9 f9 f9
  0x5d04c889fd80: 00 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Asan을 이용하면 GDB를 사용하지 않아도 어느부분에서 오류가 발생했는지 함수 스택과 Shadow Byte map을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 오류는 BSS또는 Data segment 구역의 전역변수에 잘못접근해 발생한 global-buffer-overflow로 Shadow byte map을 보면 알 수 있다시피 Global redzone인 f9 구역을 건들여 발생한 오류이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 함수 콜 스택이 있어 그대로 코드를 보고 문제 지점을 찾아도 되지만, 정확한 메모리 값을 확인하고싶기에 GDB로 디버깅을 진행하였다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;디버깅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Asan을 사용하면 메모리에 Redzone을 지정해 잘못된 메모리 액세스를 발견하면 바로 프로그램을 종료하는데에 반해, 보통은 그런거 없이 메모리에 잘못접근해도 그냥 그 상태로 진행되기에 Asan없이 그냥 gcc로 컴파일하면 오류가 발생하지 않는 경우도 있다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjB0jY/dJMcaaLnqyU/lKBbqkA2qzwm1o9baGhuE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjB0jY/dJMcaaLnqyU/lKBbqkA2qzwm1o9baGhuE0/img.png&quot; data-alt=&quot;같은 crash를 유발하는 파일인데 Asan 없이 실행할 경우 정상적으로 종료됨.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjB0jY/dJMcaaLnqyU/lKBbqkA2qzwm1o9baGhuE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjB0jY%2FdJMcaaLnqyU%2FlKBbqkA2qzwm1o9baGhuE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1231&quot; height=&quot;572&quot; data-origin-width=&quot;1231&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;같은 crash를 유발하는 파일인데 Asan 없이 실행할 경우 정상적으로 종료됨.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 gcc로 컴파일할때도 Asan을 사용하겠다고 명시해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1773912847255&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export ASAN_OPTIONS=abort_on_error=1
CC=&quot;gcc&quot; CFLAGS=&quot;-fsanitize=address -g -O0&quot; ./configure --prefix=&quot;/home/fuzzing_tcpdump/gcc_install&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Asan은 오류 발견시 콜 스택을 모두 초기화하고 프로그램을 종료하기때문에 에러가 발생하더라도 종료되지 않게 옵션을 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 GDB을 이용해 디버깅을 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ch1acW/dJMcagx18p8/F2NxEd2RxA6wTW5S74I3gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ch1acW/dJMcagx18p8/F2NxEd2RxA6wTW5S74I3gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ch1acW/dJMcagx18p8/F2NxEd2RxA6wTW5S74I3gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fch1acW%2FdJMcagx18p8%2FF2NxEd2RxA6wTW5S74I3gK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1275&quot; height=&quot;773&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;#0~8번은 sanitizer가 vsnprintf를 대신 실행하고, 오류가 발생해 프로그램을 종료하는 상황이므로, 9번부터 코드를 살펴보면 된다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMC4fn/dJMcafzabhD/NIzIv7CwpRe357RAeAkHok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMC4fn/dJMcafzabhD/NIzIv7CwpRe357RAeAkHok/img.png&quot; data-alt=&quot;bittok2str_internal 함수 내부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMC4fn/dJMcafzabhD/NIzIv7CwpRe357RAeAkHok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMC4fn%2FdJMcafzabhD%2FNIzIv7CwpRe357RAeAkHok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;202&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bittok2str_internal 함수 내부&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;bittok2str_internal함수는 입력 파일에서 가져온 값을 따라 통신 규격이나 장비 등 string으로 바꿔 buf에 담아주는 함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서는 snprintf를 사용해 buf+buflen 위치에 sizeof(buf)-buflen만큼의 공간에 문자열을 출력한 뒤 buflen에 sizeof(buf)-buflen만큼의 값을 더해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 부분을 print로 값을 찍어보았다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5VRfv/dJMcac3sNOz/61ol85QGvSpjN2Sorc8fIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5VRfv/dJMcac3sNOz/61ol85QGvSpjN2Sorc8fIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5VRfv/dJMcac3sNOz/61ol85QGvSpjN2Sorc8fIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5VRfv%2FdJMcac3sNOz%2F61ol85QGvSpjN2Sorc8fIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;347&quot; height=&quot;349&quot; data-origin-width=&quot;347&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;그 중 sizeof(buf)-buflen의 값이 0xFFFFFFFC로 0x100 - 0x104에서 overflow가 일어난걸 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이런 값이 들어왔는지 코드를 보면서 추적해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773913865760&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;static char *
bittok2str_internal(register const struct tok *lp, register const char *fmt,
	   register u_int v, const char *sep)
{
        static char buf[256]; /* our stringbuffer */
        int buflen=0;
        register u_int rotbit; /* this is the bit we rotate through all bitpositions */
        register u_int tokval;
        const char * sepstr = &quot;&quot;;

	while (lp != NULL &amp;amp;&amp;amp; lp-&amp;gt;s != NULL) {
            tokval=lp-&amp;gt;v;   /* load our first value */
            rotbit=1;
            while (rotbit != 0) {
                /*
                 * lets AND the rotating bit with our token value
                 * and see if we have got a match
                 */
		if (tokval == (v&amp;amp;rotbit)) {
                    /* ok we have found something */
                    buflen+=snprintf(buf+buflen, sizeof(buf)-buflen, &quot;%s%s&quot;,
                                     sepstr, lp-&amp;gt;s);
                    sepstr = sep;
                    break;
                }
                rotbit=rotbit&amp;lt;&amp;lt;1; /* no match - lets shift and try again */
            }
            lp++;
	}

        if (buflen == 0)
            /* bummer - lets print the &quot;unknown&quot; message as advised in the fmt string if we got one */
            (void)snprintf(buf, sizeof(buf), fmt == NULL ? &quot;#%08x&quot; : fmt, v);
        return (buf);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;rotbit은 0b00000001로, 왼쪽으로 1씩 비트시프트를 진행하니 내부 while문은 총 8번 반복한다. 또 buflen을 증가시키려면 tokval == (v&amp;amp;rotbit)을 만족해야하는데, v의 값이 0xFFFF라면 항상 마스킹이 되기때문에 tokval과 일치할경우 항상 buflen이 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 static으로 선언되어있는 buf에 점점 문자가 입력되면서 결국 최대 범위인 256에서 추가적인 문자열이 더해져 overflow가 발생한것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 문제는 0xFFFF로 들어오는 v의 값일것이다. 이를 확인하기 위해서 11번 프레임을 확인해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;80&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQK7h0/dJMcacWH0js/W61VySzHGOowvMTKw9SuP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQK7h0/dJMcacWH0js/W61VySzHGOowvMTKw9SuP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQK7h0/dJMcacWH0js/W61VySzHGOowvMTKw9SuP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQK7h0%2FdJMcacWH0js%2FW61VySzHGOowvMTKw9SuP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;80&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;80&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;bittok2str을 실행하는 인자값으로 EXTRACT_16BITS(tptr+5)가 있다. 입력 파일에서 이 부분에 해당하는 HEX값을 보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwd5Zq/dJMcagkvrUY/BFrHjOqG3Yv9MJnRSZYGU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwd5Zq/dJMcagkvrUY/BFrHjOqG3Yv9MJnRSZYGU1/img.png&quot; data-alt=&quot;tptr+5가 가리키는 데이터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwd5Zq/dJMcagkvrUY/BFrHjOqG3Yv9MJnRSZYGU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwd5Zq%2FdJMcagkvrUY%2FBFrHjOqG3Yv9MJnRSZYGU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;113&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;tptr+5가 가리키는 데이터&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;00000800C부분부터 16비트, 즉 2바이트 0xFFFF가 v값이 된것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 이 FF FF부분을 낮은 값으로 바꾸면 정상적으로 실행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이를 처리하는 부분을 패치하면 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;패치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 나는 단순히 buf값을 늘리면 된다고 생각했다. 그래서 buf의 크기를 256에서 1024로 늘렸고, 실제로 내가 찾은 crash파일에 대해서는 모두 문제가 해결되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;span style=&quot;color: #222222; text-align: start;&quot;&gt;CVE-2017-13011 공식 패치는 다음과 같이 수정되었다.&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AYsmH/dJMcafeST8L/B0XmaQnBy3dSIZ2xK2eO4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AYsmH/dJMcafeST8L/B0XmaQnBy3dSIZ2xK2eO4k/img.png&quot; data-alt=&quot;https://github.com/the-tcpdump-group/tcpdump/commit/9f0730bee3eb65d07b49fd468bc2f269173352fe&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AYsmH/dJMcafeST8L/B0XmaQnBy3dSIZ2xK2eO4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAYsmH%2FdJMcafeST8L%2FB0XmaQnBy3dSIZ2xK2eO4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;119&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/the-tcpdump-group/tcpdump/commit/9f0730bee3eb65d07b49fd468bc2f269173352fe&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;일단 나와 마찬가지로 buf의 크기를 키웠는데, 버퍼의 주소인 bufp, 버퍼의 남은 공간인 space_left, 저장될 문자열의 크기인 string_size를 새로 선언하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5S1xq/dJMcacvGlK6/YNficXMsqPvMDiHlhkDea1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5S1xq/dJMcacvGlK6/YNficXMsqPvMDiHlhkDea1/img.png&quot; data-alt=&quot;https://github.com/the-tcpdump-group/tcpdump/commit/9f0730bee3eb65d07b49fd468bc2f269173352fe&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5S1xq/dJMcacvGlK6/YNficXMsqPvMDiHlhkDea1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5S1xq%2FdJMcacvGlK6%2FYNficXMsqPvMDiHlhkDea1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;384&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/the-tcpdump-group/tcpdump/commit/9f0730bee3eb65d07b49fd468bc2f269173352fe&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 문제가 되던 snprintf를 삭제하고, 대신 남은 공간을 계속 체크하며 문자를 출력하는걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFFF라면 버퍼크기 1024정도면 커버할 수 있을거라 생각했는데, 문제는 이 패킷 전송 프로토콜이 LLDP라는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLDP는 인접한 통신장비끼리 서로 정보를 공유하는 프로토콜이라고 한다. 이런 LLDP는 TLV를 하나만 담을 수 있는게 아니라 여러개를 담을 수 있는데, 악의적인 공격으로 TLV 10개를 넣는다면 버퍼 크기가 1024라도 언제든지 터질 수 있는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 공식에서는 계속 버퍼에 남는 공간을 계산해서 처리하는 로직을 작성한거라고 생각이든다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGo0WZ/dJMcaaq46Vc/pkNnQeh0nM6NKOJ9dVw3ek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGo0WZ/dJMcaaq46Vc/pkNnQeh0nM6NKOJ9dVw3ek/img.png&quot; data-alt=&quot;패치 적용 후 정상작동 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGo0WZ/dJMcaaq46Vc/pkNnQeh0nM6NKOJ9dVw3ek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGo0WZ%2FdJMcaaq46Vc%2FpkNnQeh0nM6NKOJ9dVw3ek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;626&quot; data-origin-width=&quot;786&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;패치 적용 후 정상작동 확인&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;저번에 실습한 exercise2 패치는 정말 단순히 오류만 안뜨면 된다는 생각으로 검증로직 하나만 추가하고 통과하지 못하면 프로그램을 종료하는 방식으로 수정했었다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이번 공식 패치내역을 보니 프로그램을 종료시키는게 아니라 메모리 검증만 정밀하게 차단하고 서비스는 그대로 유지하는 방식의 패치가 서비스 안정성 측면에서, 그리고 DoS를 막는 효과적인 방법임을 깨달았다.&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222;&quot; data-ke-size=&quot;size16&quot;&gt;사실 내가 찾은 PoC중에는 CVE-2017-12900를 트리거하는 케이스가 포함되어있었다. 해당 취약점은 lspping_print 함수가 호출하는 tok2strbuf에서 발생한 global-buffer-overflow 취약점인데, 나중에 기회가 있다면 근본 원리를 찾아서 디버깅해보면 흥미로울거같다.&lt;/p&gt;
&lt;/div&gt;
&lt;div style=&quot;color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;reaction-1&quot; data-tistory-react-app=&quot;Reaction&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>보안/Fuzzing101</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/4</guid>
      <comments>https://pumisj.tistory.com/4#entry4comment</comments>
      <pubDate>Thu, 19 Mar 2026 23:27:01 +0900</pubDate>
    </item>
    <item>
      <title>[Dream hack] Linux 메모리 구조 &amp;amp; 어셈블리 정리</title>
      <link>https://pumisj.tistory.com/3</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저번 글에서는 너무 배경설명이 길었다. 어차피 복습하는건데 이미 알고 있는 내용은 안적어도 될거같다. 이번에는 짧게 할거다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 시작하겠다.&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dreamhack.io/lecture/paths/system-hacking-fundamental&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dreamhack.io/lecture/paths/system-hacking-fundamental&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;color: #222222; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;리눅스의 메모리 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리눅스뿐만 아니라 다른 대부분의 운영체제도 마찬가지겠지만, 프로그램을 실행하면 프로세스가 생성된다. 이 프로세스는 고유의 코드, 데이터를 가지고있는데, 데이터가 들어가는 구역을&amp;nbsp;&lt;b&gt;세그먼트(Segment) &lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZ4sXg/dJMcagdIPcA/skiWSjRb27OIdGUs0R4uZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZ4sXg/dJMcagdIPcA/skiWSjRb27OIdGUs0R4uZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZ4sXg/dJMcagdIPcA/skiWSjRb27OIdGUs0R4uZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZ4sXg%2FdJMcagdIPcA%2FskiWSjRb27OIdGUs0R4uZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;863&quot; height=&quot;522&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면 알겠지만 총 5개의 세그먼트가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 세그먼트: 프로그램의 기계어 코드가 저장된다. 해당 세그먼트에는 r, x 권한만 주어진다&lt;/li&gt;
&lt;li&gt;데이터 세그먼트: 초기화된 전역 변수 및 전역 상수가 해당 세그먼트에 저장된다. r, w 또는 r 권한만 주어진다. 여기서 두 개의 세그먼트로 다시 한번 나뉜다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;data 세그먼트: 전역 &lt;b&gt;변수&lt;/b&gt;가 여기에 포함된다. 변경될 수 있다.&lt;/li&gt;
&lt;li&gt;rodata 세그먼트: Read Only의 약자로 전역&amp;nbsp;&lt;b&gt;상수&lt;/b&gt;가 여기에 포함된다. 변결될 수 없다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;BSS 세그먼트: Block started By Symbol Segment의 약자로, 컴파일 시점에 값이 정해지지 않은, 초기화되지 않은 전역 변수가 여기에 포함된다. r, w 권한만 주어진다.&lt;/li&gt;
&lt;li&gt;힙 세그먼트: C언어의 malloc 등 코드 실행중 동적으로 할당되는 데이터가 저장된다. r, w 권한만 주어진다.&lt;/li&gt;
&lt;li&gt;스택 세그먼트: 함수의 매개변수나 지역 변수같은 임시 변수들이 저장된다. r, w 권한만 주어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 스택 세그먼트에 대해서는 말 할 내용이 있지만, 어셈블리어에서 자세히 나오기 때문에 넘어가겠다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Assembly (1)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JAVA나 C나 결국 컴파일하면 어셈블리어로 실행된다. 어셈블리어는 메모리와 코드가 1:1로 대응되는 언어로, 쉘 코드 작성할 때나 리버스 엔지니어링 할 때나 꼭 알아야하는 언어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 이거 명령어 하나하나 소개할수는 없다. 그냥 이 사진 하나로 끝내겠다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wjxmw/dJMcad2iFmQ/yBg7KHwOKF0wZEaY50s1YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wjxmw/dJMcad2iFmQ/yBg7KHwOKF0wZEaY50s1YK/img.png&quot; data-alt=&quot;어셈블리어의 명령어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wjxmw/dJMcad2iFmQ/yBg7KHwOKF0wZEaY50s1YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwjxmw%2FdJMcad2iFmQ%2FyBg7KHwOKF0wZEaY50s1YK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;976&quot; height=&quot;488&quot; data-origin-width=&quot;976&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;어셈블리어의 명령어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알기 쉬운거 말고 몇 개만 소개해보자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lea: C언어의 포인터라고 보면 된다. 메모리 주소를 저장한다.&lt;/li&gt;
&lt;li&gt;cmp: 기본적으로 sub연산이 들어간다. 피연산자 두 개가 같을 경우 ZF에 1을 저장한다.&lt;/li&gt;
&lt;li&gt;test: 피연산자 두 개를 and연산 한 뒤 결과는 버리고 플래그 레지스터만 갱신한다. 보통 특정 비트가 0인지 아닌지 확인하거나, 레지스터가 0인지 검사하는데 사용한다.&lt;/li&gt;
&lt;li&gt;j~~: 대부분 분기문이다. jmp는 무조건 분기라는것만 기억하면 될거같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어보겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1773667998568&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mov rax [rdx]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 대괄호가 나왔다. 대괄호의 의미는 rdx에 저장되어있는 주소 ex) 0xff142a 에서 값을 꺼내온다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그니까 이 코드는 rdx에 저장되어있는 주소의 데이터를 rax에 복사하겠다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리를 표현하는 방법은 위처럼 암시적으로 하는 방법도 있지만, 명시적으로 나타낼수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;975&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QjHNo/dJMcaaq2cEg/3BsNz2KVMgb0xMlsWgCkWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QjHNo/dJMcaaq2cEg/3BsNz2KVMgb0xMlsWgCkWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QjHNo/dJMcaaq2cEg/3BsNz2KVMgb0xMlsWgCkWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQjHNo%2FdJMcaaq2cEg%2F3BsNz2KVMgb0xMlsWgCkWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;975&quot; height=&quot;224&quot; data-origin-width=&quot;975&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WORD, DWORD, QWORD 그리고 BYTE가 데이터 크기를 나타낸다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Assembly (2)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에 대해서 짧게 말하겠다. Subroutine, Procedure, Function라는 용어로 혼용되는 함수는 프로그램이 처리해야할 명령어를 묶어놓은 코드를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어셈블리어에서는 이를 Label로 구역을 표시하고 call 명령어를 통해 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료구조중에 Stack이라고 있다. Queue와 반대로 LIFO로 구현되며, 어셈블리어에는 이를 구현한 명령어 push, pop이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773668705287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;push val
========
rsp -= 8
[rsp] = val&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택에서 가장 중요한건 rsp레지스터인데, 이 rsp레지스터는 항상 스택의 가장 위를 가리킨다. 스택은 가변적인 공간이기 때문에 push를 하면 rsp도 변하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드와 같이 push를 하게되면 내부적으로 스택의 윗부분에서 8만큼 내려주고, 그 위치에는 피연산자 val을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773668882593&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pop reg
=======
reg = [rsp]
rsp += 8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pop도 마찬가지로 실행이 되면 rsp가 가지고 있던 값을 rax같은 레지스터에 넣어주고, 스택을 다시 없앤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념을 가지고 call을 구현한다&lt;/p&gt;
&lt;pre id=&quot;code_1773669019973&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;call addr
=========
push return_address
jmp addr&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call은 push와 jmp 명령어로 동작하는데, 함수가 실행된 후 돌아갈 위치를 저장하면서, 동시에 해당 주소로 rip를 이동시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 종료되면 leave로 만든 스택 프레임을 정리하고 ret으로 다시 돌아온다. 여기서 스택 프레임이란, 서로 다른 함수끼리 같은 메모리 구역을 사용하지 않도록 미리 만들어놓은 단위이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773669279401&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;leave
-----
mov rsp, rbp
pop rbp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드만 보면 이해가 안되는데, 간단히 설명하자면 call로 실행했던 함수에서 leave를 만나면, 스택 포인터를 다시 함수의 시작점으로 보내고, 함수를 실행하기 전의 주소를 rbp에 다시 저장해놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1773669535152&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ret
===
pop rip&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 ret은 rip 값을 return address로 변경하는 명령어로, 이로써 함수를 종료하고 다음 코드로 넘어간다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;후기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 호출과 시스템 콜 내용을 넣을까 했지만, 여기까지 했으면 굳이 적을 필요 없을거라고 생각한다. 시스템 콜은 코드 보면 알 수 있는거고, 그 인자값은 어차피 외우기 힘들다. 직접 찾아보면서 익숙해지는게 낫다고 생각이 든다.&lt;/p&gt;</description>
      <category>보안/System Hacking</category>
      <author>pumisj</author>
      <guid isPermaLink="true">https://pumisj.tistory.com/3</guid>
      <comments>https://pumisj.tistory.com/3#entry3comment</comments>
      <pubDate>Mon, 16 Mar 2026 23:02:19 +0900</pubDate>
    </item>
  </channel>
</rss>