난독화란 말 그대로 무언가를 알아보기 어렵게 만드는 것입니다. 프로그래밍에서 난독화는 사람이 사용할 수 없지만 그 실행이나 결과물은 그대로 유지되도록 특정 프로그램의 코드를 변환하는 작업을 말합니다.
특히 특정 도구와 플랫폼을 이용하면 프로그램의 목적이나 로직을 숨기도록 난독화하여 다른 사람의 조작을 방지할 수 있습니다. 이를 위해 자동화 도구로 코드를 조작하기도 합니다.
또한 모바일 사용자들에게 보안의 중요성이 크게 높아지면서 iOS 앱 난독화는 모바일 앱 개발자와 퍼블리셔에게 하나의 관행이 되었습니다.
iOS 앱 난독화
오늘날의 모바일 중심 세계에서 안드로이드와 iOS 공히 모바일 애플리케이션은 리버스 엔지니어링을 비롯한 공격 대상입니다. 다만 iOS 앱이 다른 앱들보다 안전하다는 오해가 존재합니다.
iOS 앱은 주로 Objective-C와 Swift라는 언어로 만들어집니다. 이 언어들은 소스로 번역하기 어렵도록 보통 기계 코드로 컴파일링됩니다. 사실 이 덕분에 iOS 앱은 리버스 엔지니어링이 어렵다는 오해가 생겨났습니다.
iOS 앱에 대한 가장 큰 오해:
- 기계 코드는 리버스 엔지니어링이 어렵다.
- 애플의 코드 암호화는 앱을 리버스 엔지니어링으로부터 막아내기에 충분하다.
- 다운로드 후 앱의 기계 코드에 대한 접근이 제한되므로 해당 앱을 분석하기가 쉽지 않다.
하지만 iOS 개발자들은 주로 앱 개발 과정에서 UX와 UI에 초점을 맞추면서 보안을 등한시하는 문제가 일어납니다.
오해: 기계 코드도 리버스 엔지니어링이 가능합니다!
기술이 빠르게 발전하면서 기계 코드라도 리버스 엔지니어링이 가능한 기술도 등장했습니다. Objective-C와 Swift를 기계 코드로 컴파일링할 시에는 바이너리에 이 언어들에 필요한 메타데이터가 대량으로 잔류하게 됩니다. 이 덕분에 오히려 리버스 엔지니어링이 쉬워집니다.
공격자들은 이를 이용하여 코드를 이용하고 리버스 엔지니어링하는 것입니다. 그러므로 개발자라면 반드시 이 위험을 인지하고 사전에 필요한 조치를 취해야 합니다.
특히 코드 난독화는 iOS 개발자가 공격자의 디컴파일링 및 리버스 엔지니어링을 방지할 수 있는 일반적인 방법이 되었습니다. 또한 난독화는 다양한 방식으로 앱의 코드를 복잡하게 만들어 이해와 분석이 어렵게 만들어 줍니다.
iOS 앱 난독화의 필요성
iOS 앱은 설계 특성상 리버스 엔지니어링 공격에 취약합니다. 앱의 클래스와 프로토콜이 오브젝트 파일에 바로 저장되므로 공격자는 앱의 설계를 쉽게 파악할 수 있습니다.
iOS의 대부분 공격은 아래와 같은 Objective-C 런타임의 약점을 악용하여 일어납니다.
- 앱 설계가 바이너리에 저장되어 공격자가 앱의 아키텍처를 재구성할 수 있습니다.
- Objective-C의 리플렉션 메커니즘 덕분에 공격자가 쉽게 앱의 상태를 조작할 수 있습니다.
- Objective-C의 메시징 프레임워크는 매우 단순합니다. 이로 인해 메시지를 쉽게 추적하여 조작할 수 있습니다.
Objective-C는 매우 단순한 메시징 프레임워크를 이용하므로 이를 통해 간단하게 앱의 런타임으로 메인 코드를 조작하는 것이 가능합니다. 초보적 공격자라도 이 런타임을 조작하여 인증과 정책 확인 과정을 우회할 수 있습니다.
그러므로 금융이나 뱅킹 앱과 같이 민감성 데이터를 사용하는 앱을 개발할 시에는 안티 디버깅 기법을 이용해야 합니다. 이는 코드의 리버스 엔지니어링을 복잡하게 만들어줍니다.
한가지 방법은 C/C++에서 공격자에 의한 런타임 조작을 제한하는 것입니다. 또는 iOS 앱의 중요한 부분을 저수준 C로 작성하여 Objective-C 런타임이나 class-dump-z, Cycript, Frida 등 리버스 엔지니어링 도구에 의한 노출을 방지하는 방법도 많이 사용됩니다.
iOS App Obfuscation Advantages
난독화는 아래와 같은 직접적 장점을 제공합니다.
- 기계 코드의 무단 복제와 조작을 방지할 수 있습니다.
- 로직과 알고리즘의 노출을 줄일 수 있습니다.
- 코드의 취약점을 찾아내기 매우 어렵게 만들 수 있습니다.
또한 자동 코드 난독화는 보안 강화와 위험 방지라는 명확한 장점 외에도 고유한 특징을 제공합니다. 리버스 엔지니어링을 어렵게 만들면서 경제적 타당성을 낮추어 소프트웨어의 IP(지적자산)를 보호하는 데 도움이 됩니다.
자동 난독화는 그 외에도 아래와 같은 장점이 있습니다.
- 라이선싱 메커니즘을 보호합니다.
- 무단 접근을 막아줍니다.
- 소스코드의 크기를 효율적으로 줄여줍니다.
iOS 앱 난독화의 단점
난독화는 코드의 읽기, 쓰기, 리버스 엔지니어링을 복잡하게 만들어 장시간이 소요되도록 유도하지만 그렇다고 리버스 엔지니어링이 불가능하지는 않습니다.
AVG AntiVirus와 같은 안티바이러스 프로그램은 난독화된 코드를 바이러스로 오해하여 경고를 발동할 수 있습니다.
이는 프로그램의 보호 뿐 아니라 악성코드를 숨기는 데에도 난독화를 사용하기 때문입니다. 이로 인해 안티바이러스 프로그램이 경고를 발생시키게 됩니다.
한편 개발자에 따라 파일 크기를 줄이려는 목적으로만 코드 난독화를 사용할 수도 있습니다. 즉 안티바이러스 프로그램이 전혀 문제가 없는 난독화된 코드에 대해 경고를 발동시키는 경우 사용자는 해당 코드로 작성된 소프트웨어를 아예 사용하지 못할 수도 있습니다.
iOS 앱 난독화 방법
iOS 앱의 난독화에 널리 사용되는 방법을 소개합니다.
1. 제어 흐름 난독화
앱의 실행 의도를 파악하려면 그 제어 흐름을 이해해야 합니다. 즉 제어 흐름 난독화는 앱의 ‘논리적 실행 흐름’을 보호합니다. 이는 다양하면서 체계적인 방식으로 앱의 흐름을 통제하여 이루어집니다.
즉 이는 코드의 실행 방식과 흐름의 방향을 이해하기 어렵게 만듦으로써 논리를 난독화하고 공격자를 혼란시키는 좋은 방법입니다.
난독화의 가장 직접적 방법 중 하나는 원래 코드에 임의 스트링과 예상치 못한 코드 및 무작위 케이스 스위치 코드(데드 코드)를 삽입하는 것입니다. 이러한 코드는 중요한 부분인 것처럼 보이지만 실제 코드의 흐름이나 실행 과정에서 아무런 역할을 하지 않으므로 공격자를 혼란시키게 됩니다. 이러한 방식은 특히 조건문 프로그램의 경우에 실행 순서를 조작하는 데 사용할 수 있습니다.
출처: PreEmptive
2. 리네임 난독화
리네임은 클래스, 메소드, 필드, 주석, 패키지 등 개체의 이름을 완전히 바꾸어 자바 바이트코드의 크기를 줄이고 리버스 엔지니어링을 어렵게 만드는 방식입니다.
소스코드에서 사용하는 이름은 코드 내 구성요소에 대해 여러 민감성 정보를 담게 됩니다.
또한 이 이름들은 최종 바이트코드에서도 그대로 유지되어 프로젝트별로 특정 클래스의 ‘메인’ 메소드를 발동시키게 됩니다. 하지만 컴파일링 후에 이 이름들은 공격자가 해당 애플리케이션을 이해하기 쉽게 만드는 것 외에는 기능이 없습니다. 리네이밍을 이용하면 이 이름들의 의미를 제거할 수 있습니다.
출처: PreEmptive
3. 레이아웃 및 데이터 난독화
데이터 난독화는 코드에서 사용하는 데이터 구조에 대해 사용하여 공격자가 프로그램의 실제 의도를 이해하거나 활용하지 못하도록 만드는 기법입니다.
이 방식은 데이터를 메모리에 저장하는 방식 및 데이터를 해석하여 최종 결과물을 파악하는 방식을 수정합니다. 이는 아래와 같은 변형이 가능합니다.
1. 집계 난독화
이 방법은 데이터의 저장 방식을 수정합니다. 이를테면 어레이를 여러 서브어레이로 분할할 수 있습니다. 각 서브어레이는 다시 프로그램의 여러 위치에서 참조할 수 있습니다.
2. 저장 난독화
이 방법은 데이터가 메모리에 저장되는 방식을 난독화합니다. 이를테면 퍼블리셔는 변수의 로컬 및 글로벌 저장 방식 중에서 선택할 수 있습니다. 이렇게 하면 변수의 작동 방식이 난독화됩니다.
3. 배치 난독화
이 기법은 데이터의 배치 방식을 조작하지만 코드 스니펫의 거동은 그대로 유지됩니다. 이는 변수 참조의 전체 인스턴스에 대해 호출되는 개별 모듈을 프로그래밍하여 이루어집니다.
4. 문자열 암호화
스트링 난독화는 여러 무작위적 방식으로 스트링을 스크램블링합니다. 즉 이는 스트링을 의미 없는 표현으로 숨기고 대체합니다. CPU는 자동으로 실행 중에 스트링의 스크램블을 풀어낼 수 있지만 공격자는 정적 분석을 통해 스트링의 의미를 거의 파악할 수 없게 됩니다.
마무리: 난독화면 충분한가?
난독화는 리버스 엔지니어링으로부터 애플리케이션을 보호하고 IP 도난을 방지할 수 있는 매우 효과적이며 안전한 조치임은 분명하지만 실제 공격으로부터 앱을 완전히 보호하기에는 충분하지 못합니다.
그러므로 360도 전방위 코드 보호가 필요합니다. 이를테면 난독화와 함께 포괄적인 런타임 보호 조치를 실시해야 iOS 앱을 완전히 보호할 수 있습니다.