지난 글, ‘안티 템퍼링이란 무엇인가?’에서 안티 템퍼링 기술의 역사와 기술 도입을 위한 정책과 범위, 기술 절차에 대해 알아보았습니다. 안티 템퍼링(Anti-Tampering)이란 쉽게 말해 외부에서 기술 자산을 탈취할 수 없도록 방어하는 기술을 의미합니다. 이 안티 템퍼링은 아주 단순한 물리적인 방책부터 고도의 기술을 이용하여 전문적인 지식 없이는 접근이 불가능한 방책까지 수준이 다양합니다. 대상 역시 유∙무형의 자산을 어우르기에 우리의 삶에서 인지하지 못하는 부분까지 이미 안티 탬퍼링 기술이 적용되어 있습니다. 실례로 ‘그림 1’에서 iPhone의 하단부 충전 단자 양쪽을 보면 나사가 두 개 있습니다. 우리가 흔히 일상에서 사용하는 ‘+’혹은 ‘-‘가 아닌 ‘★’모양의 나사인 점을 볼 수 있습니다.

그림 1. iPhone의 Pentalobe Security Screw 그림 2. 다양한 모양의 스크류
이 특이한 나사는 ‘Pentalobe Security Screw’, 국내에서는 ‘별 렌치’라고 부르는 보안 나사입니다. 이를 풀고 조이기 위해선 같은 모양의 도구가 필요하게 되어, 모양이 다른 드라이버로 강제로 풀거나 조이기 위해 힘을 가할 경우 나사의 손상으로 인해 더 이상 조일 수도 풀 수도 없게 됩니다. 이 나사는 2009년부터 Apple 사의 제품에 적용되어 사용 중입니다. 이 외에도 ‘그림 2’와 같이 다양한 보안 나사들이 존재 하며, 이는 외부에서 손쉽게 기기의 외장을 열지 못하게 하려는 단순한 수준의 하드웨어 안티 템퍼링 기술입니다.
그렇다면 소프트웨어 분야에는 어떤 방식으로 안티 템퍼링을 적용할 수 있을까요? 이번 글에서는 소프트웨어를 보호하기 위한 소프트웨어 안티 템퍼링에 대해 소개하겠습니다.
역공학을 통해 소프트웨어를 분석하여 위변조를 막는 기술인 소프트웨어 안티 템퍼링은 기술의 적용 목적에 따라 억지, 예방, 탐지, 대응으로 구분할 수 있습니다. (억지 기술)은 공격 시도 자체를 포기하게하는 방법으로, 안티 탬퍼링 즉, 공격을 방어하는 측면에서 기본적인 방법이자 최선의 방어 기술입니다. 이는 앞서 설명한 보안 나사의 경우가 억지 기술의 일종이며, 소프트웨어의 경우 해시코드, 체크섬 및 스템프를 통해 무결성을 검증하여 공격을 사전에 억지할 수 있습니다. (예방 기술)은 소프트웨어 리버싱을 어렵게 하기 위해 암호화 및 난독화 방법을 적용하는 것으로, 데이터의 가공(변형) 또는 은닉 기술을 이용하여 데이터를 숨기거나 복잡도를 증가시켜 중요한 데이터 획득을 어렵게 하는 방법입니다. (탐지 기술)은 인증, 접근제어, 무결성 검사와 같은 기술을 이용하여 위변조 시도를 탐지하는 기술입니다. (대응 기술)은 공격 시도 탐지 후 로그 기록 분석, 모니터링 강화, 레포팅, 자산 복구, 취약점 패치와 같은 기술을 이용한 사후 보안 강화 기술을 의미합니다. 이 네 가지 대응 기술 중 예방 기술에 대한 상세 가지(안티 리버싱-난독화)를 좀 더 자세히 살펴 보도록 하겠습니다.
안티 리버싱
안전한 소프트웨어를 위한 예방 기술은 암호화 및 난독화 기술을 통해 소프트웨어 리버싱을 어렵게 한다고 소개하였는데, 그렇다면 소프트웨어 리버싱이란 무엇일까요? 이는 소프트웨어의 역공학을 통해 소프트웨어의 동작 흐름, 동작 원리 등 프로그램 소스 코드에 대한 정보를 획득하는 기술이며, 공격자가 코드를 이해하고 획득하는 것이 목표라 할 수 있습니다. 합법적으로 소스 코드를 취득할 수 없는 소프트웨어를 위변조하기 위해서는 역공학을 통한 소프트웨어 분석이 선행되어야 하고, 이를 방해하는 방법이 바로 안티 리버싱 기술입니다. 즉, 안티 템퍼링은 안티 리버싱을 포괄하는 의미입니다.
안티 리버싱은 적용하는 방식에 따라서 크게 네 가지로 구분할 수 있습니다.
● 소프트웨어의 심볼 정보 제거: 텍스트와 같은 의미를 내포하여 유추할 수 있는 정보를 삭제하는 방식입니다. 클래스 이름, 클래스 멤버 이름, 전역 변수와 같은 다양한 정보를 제거함으로써 분석을 어렵게 만듭니다.
● 안티 디버깅: 프로그램을 분석하기 위해 역공학을 진행할 때 디버그 분석을 불가능하게 하는 방식으로, 디버거가 연결된 경우 디버거를 손상하거나 비활성화하는 작업을 의도적으로 수행하도록 하는 것입니다.
● 안티 디스 어셈블리: 역공학을 통해 바이너리를 분석하기 위하여 디스 어셈블링을 하거나, 디컴파일 과정을 거치게 되는데, 이러한 역공학을 막기 위해 프로그램 내 특수한 코드와 데이터를 사용해 실제 실행과 다른 명령어들을 보이게 해 디스 어셈블러나 디컴파일러 도구를 방해하기 위해 사용합니다.
● 난독화: 난독화 기법은 역공학을 시도하더라도 코드의 분석 및 이해를 어렵게 하여 정보를 쉽게 획득하지 못하게 하는 기술입니다. 난독화 기법은 대상, 목적, 방법 등 다양한 관점에서 이루어지고 있습니다.
여러 안티 리버싱 중 가장 활발히 연구되고 있는 분야인 난독화 기법에 대하여 더 알아보도록 하겠습니다.
난독화
난독화 기법의 목적은 프로그래밍 언어로 작성된 실행 가능한 애플리케이션을 원천적으로 변조하여 소프트웨어의 구조, 흐름 등의 중요 정보 획득을 방해하는데 목적이 있습니다. 즉, 역설계를 통해 프로그램의 기능을 파악하려는 공격자가 코드를 읽을 수 있다 하더라도 내용을 이해하지 못 하도록 하는 기술입니다. 난독화가 적용된 코드는 사람이 이해하기 힘들게 변조되어 은닉되지만 소프트웨어의 본래 기능은 원본과 동일하게 작동됩니다. 이러한 코드 난독화 기술은 크게 네 갈래로 분류할 수 있습니다.
● 변환의 수준: 개발 언어 및 난독화 시점에 따라 코드 변환 수준을 결정할 수 있습니다. 예를 들어 C언어의 경우 바이너리 및 소스 코드 수준에서의 난독화를 수행하지만, JAVA의 경우에는 소스 코드 수준에서 중점적으로 난독화 기술이 연구∙개발되고 있습니다. 이렇게 개발 언어의 특징 혹은 구조에 따라 변환 대상의 수준이 다르기 때문에 어떠한 기술이 우월하다고 단정지을 수 없습니다. 또한 변환 수준을 “소스 코드”, “중간 언어”, “기계어”로 구분 지을 수 있는데, 이러한 분류에 따라 난독화 방법 및 목적이 확연히 구분됩니다.
● 변환의 단위: 목적에 따라 변환 명령어, 기본 블록, 순환(Loop), 함수, 프로그램, 시스템 등 단위별로 세분화하여 난독화 수준을 선택할 수 있습니다.
▷ 명령어 수준 변환(Instruction-level transformations): 소스 코드는 중간 언어 혹은 어셈블리 명령어로 구성될 수 있습니다. 이러한 단계에서 개별 명령어나 명령어의 시퀀스에 변환을 적용합니다.
▷ 기본 블록 수준 변환(Basic block-level transformations): 기본 블록은 하나의 진입점과 분기 명령어로 끝나는 순차 명령어 모음을 의미하며 명령어들을 포괄하는 단위입니다.
▷ 순환 수준 변환(Loop-level transformations): 개발자가 프로그램 내에 삽입한 순환 구조에 변환을 주는 수준을 의미합니다.
▷ 함수 수준 변환(Function-level transformations): 특정한 서브 루틴의 기본 블록 및 여러 명령어에 변환을 주며, 함수와 연관된 스택 및 힙 메모리에도 영향을 줍니다.
▷ 프로그램 수준 변환(Program-level transformations): 애플리케이션 내부의 여러 함수를 대상으로 변환합니다. 프로그램의 전역 변수 및 정적 변수와 같은 데이터 세그먼트와 할당된 메모리도 변환의 대상이 됩니다.
▷ 시스템 수준 변환(System-level transformations): 운영 체제 또는 런타임 환경을 대상으로 하며 다른 프로그램과 상호작용을 할 때, 변환의 영향을 끼칩니다.
변환 단위는 개발자가 보호해야 하는 목표에 따라 적절히 세분화해야 하기에 중요합니다. 예를 들어, 순환 수준 변환은 데이터를 숨기기에 적절치 않지만, 알고리즘을 드러나지 않도록 하는데 적합합니다. 또한, 세분된 수준의 변환이 다른 수준에 동시에 영향을 끼쳐 자칫 공격자에게 은닉 정보가 드러날 수 있습니다. 이렇듯 난독화는 대상과 범위를 적절히 선택하여 실행하여야 합니다.
● 변환의 시점 및 방식: 변환 시기에 따라 정적/동적 변환으로 구분될 수 있습니다. 정적 변환은 프로그램 설계 주기 중 한 번 적용되며, 동적 변환은 프로그램 실행 중 변환이 가능합니다. 동적 변환은 더욱 분석을 어렵게 하는 장점은 있지만, 실행 중 쓰기 및 실행이 허용되어 원격 공격에 노출될 수 있으며, 과도한 동작으로 인한 성능 문제를 일으킬 수 있는 문제점을 가지고 있습니다.
● 변환의 대상: 변환 대상은 크게 ‘데이터’와 ‘코드’로 분류할 수 있습니다.
▷ 데이터 변환(Data Transformations): 애플리케이션에 하드 코딩된 값의 표현과 위치를 변환하여 난독화를 진행합니다. 바이트 배열 혹은 정수, 변수 등의 상수 데이터를 다른 데이터의 값으로 치환하여 인코딩하는 정적 데이터 변환과 가변 메모리값의 표현이나 구조를 수정하는 가변 데이터 변환 등이 대표적인 난독화기술 입니다.
▷ 코드 변환(Code Transformations): 코드 논리 및 코드 추상화 변환으로 분류하여 애플리케이션의 제어흐름과 알고리즘, 자료구조 등 코드의 논리 및 제어를 변형하는 코드 논리 변화과 변수명, 주석, 들여쓰기(공백 문자 및 탭) 등의 높은 수준의 코드 추상화 변환이 주요 기술입니다.

그림 3. 난독화 기술 분류
소개드린 난독화 기술 이외에도 다양한 기술이 존재하며 통상적으로 하나의 난독화 기술만 사용하는 것이 아니라 여러 기술을 복합적으로 사용함으로써 복잡도를 증가시키는 방법으로 소프트웨어를 보호하며, 보호 효과가 매우 높다고 할 수 있습니다. 하지만 중첩된 난독화 기술로 인해 소프트웨어의 성능 저하가 발생하고, 파일의 크기가 증가하는 문제가 있어 최적화 작업이 필요하다는 단점이 있습니다.
글을 마치며
이번 포스트에서 소프트웨어 안티 탬퍼링과 코드 난독화 기술의 큰 카테고리에 대해서 알아보았습니다. 다음 글에서는 세부적인 코드 난독화 기술에 대해 조금 더 심도있게 살펴보고자 하며, 실제 코드에 난독화 기술을 적용하고 가독성 변화와 소프트웨어의 크기 및 성능 변화에 대해 설명하도록 하겠습니다. 다음에 연재될 글도 기대해 주시길 바라겠습니다.
Reference
[1]https://ko.ifixit.com/Guide/iPhone+14+Pro+%EC%8A%A4%ED%81%AC%EB%A6%B0+%EA%B5%90%EC%B2%B4/152972
[2] https://ko.ifixit.com/Guide/Choosing+the+Right+Screwdriver+Bit/93991
[3] J. Nagra and C. Collberg, Surreptitious Software: Obfusca- tion, Watermarking, and Tamperproofing for Software Pro- tection: Obfuscation, Watermarking, and Tamperproofing for Software Protection. Pearson Education, 2009.
[4] Banescu, Sebastian, and Alexander Pretschner. “A tutorial on software obfuscation.” Advances in Computers 108 (2018): 283-353.

KAIST 사이버보안연구센터 사이버위협분석팀 연구원으로 블록체인 및 소프트웨어 테스팅 연구를 진행하고 있다.