코드 난독화는 애플리케이션을 해킹으로부터 보호하기 위해 가장 많이 사용되는 애플리케이션 보안 기술 중에 하나입니다. 이는 전 세계 보안 전문가들이 가장 권장하는 AppSec 이니셔티브 중 하나이며, 애플리케이션의 최소 보안 사항으로 요구되는 경우가 많습니다. 대개 이 기술은 해킹 시도에 대한 주요한 방어 메커니즘 역할을 수행하며 코드 주입, 리버스 엔지니어링, 고객 및 애플리케이션 사용자의 개인정보 변조와 같은 일반적인 공격으로부터 보호합니다.
코드 난독화란?
코드 난독화는 실행 가능한 코드를 수정하여 이해, 해석, 실행에 사용할 수 없도록 하는 것입니다. 소스 코드 자체가 난독화되어 있어 제3자가 이해할 수 없고 실행만 가능하게 됩니다. 코드 난독화는 최종 사용자를 위한 애플리케이션 인터페이스 또는 코드의 의도된 출력에 영향을 미치지 않습니다. 이는 애플리케이션의 실행 코드를 손에 넣을 수도 있는 해커가 코드를 사용할 수 없도록 하기 위한 예방 조치일 뿐입니다.
코드 난독화가 필요한 이유
코드 난독화는 특히 오픈소스 애플리케이션에 유용하며, 이는 개인의 이익을 위한 코드를 해킹할 가능성을 크게 줄여줍니다. 애플리케이션을 리버스 엔지니어링하기 어렵게 만드는 방법을 통해 개발자는 제품의 지적 재산을 보안 위협, 무단 액세스, 애플리케이션 취약점 발견으로부터 보호합니다. 이 과정은 소스 코드에 대한 악의적인 접근을 제한하고, 구현된 난독화 기법의 종류에 따라 다양한 수준의 코드 보호를 보장합니다. 시간, 비용, 리소스 측면에서 난독화된 코드를 디컴파일하여도 이해하기 어렵기 때문에, 공격자는 코드를 더 이상 보유할 의미가 없어지게 됩니다.
코드 난독화 기술 유형
난독화는 여러 수준에서 작용하며, 의미 구조/유사 코드 구조 또는 데이터 구조/제어 흐름 수준에서 적용됩니다. 난독화 기술도 코드에서 수행하는 연산에 따라 달라집니다. 기본적으로 보안팀은 개발팀과 협의하여 코드에 어떤 수준의 난독화를 사용할지 결정합니다.
이름 변경 난독화
이 기술은 변수의 이름을 혼동하게 만들어 원래 사용 의도를 지능적으로 가려줍니다. 메소드와 변수는 서로 다른 표기법과 숫자를 사용하여 이름이 변경되므로 디컴파일러가 제어 흐름을 이해하기 어렵습니다. 이 난독화 기술은 Java, .NET, Android 플랫폼에서 개발된 애플리케이션 코드를 난독화하기 위해 주로 사용됩니다. 이는 레이아웃 난독화의 전반적인 범주에 속하며, 소스 코드를 직접 대상으로 하여 애플리케이션에 대한 방어 계층을 제공합니다.
이름 변경 난독화 메소드의 예
출처: PreEmptive
데이터 난독화
이 기술은 해커가 프로그램의 실제 의도를 파악할 수 없도록 코드에 사용되는 데이터 구조를 대상으로 합니다. 여기에는 프로그램을 통해 데이터가 메모리에 저장되는 방식과 저장된 데이터가 최종 출력을 표시하기 위해 해석되는 방식을 변경하는 것이 포함될 수 있습니다. 이 기술에는 다양한 변형이 있습니다.
1. 집계 난독화
이 방법은 프로그램에 데이터가 저장되는 방식을 변경합니다. 예를 들어, 배열은 많은 하위 배열로 분할될 수 있으며 프로그램의 다른 위치에서 참조될 수 있습니다.
2. 저장 난독화
이 방법은 데이터가 메모리에 저장되는 방식을 변경합니다. 예를 들어, 개발자는 변수의 저장 위치를 로컬 저장소와 전역 저장소 사이에서 이리저리 바꿀 수 있어서 변수 동작의 실제 특성을 난독화할 수 있습니다.
3. 순서 난독화
이 방법은 프로그램/코드 스니펫의 동작을 변경하지 않고 데이터의 순서를 변경합니다. 개발자는 변수 참조의 모든 인스턴스에 대해 호출되는 별도의 모듈을 개발하는 방식으로 이러한 방법을 구현합니다.
4. 문자열 암호화
이 방법은 읽을 수 있는 문자열을 모두 암호화하여 읽을 수 없는 코드를 생성합니다. 따라서 프로그램이 실행될 때 런타임에 암호를 해독해야 합니다.
5. 제어/코드 흐름 난독화
제어가 코드베이스의 한 섹션에서 다른 섹션으로 전달되는 방법은 프로그램의 의도를 결정하는 데 중요한 역할을 합니다. 이 흐름을 혼란스럽게 하는 것은 일반적으로 해커를 혼동하게 만들 수 있는 방법 중 가장 효과가 좋은 방법입니다. 이 난독화 방법은 해커가 코드의 특정 흐름을 채택하는 방법과 이유를 해독하지 못하게 합니다.
이를 구현하는 가장 일반적인 방법 중 하나는 임의적이고 예상치 못한 구문을 사용하여, 절대로 실행되지 않는 불필요한 대소문자 전환 구문(데드 코드)을 추가하는 것입니다. 이러한 구문은 해커를 혼란스럽게 만드는 것을 목적으로 합니다. 프로그램 실행 구문의 순서에 대한 이러한 변경은 조건부 프로그램 성향을 가진 경우에 특히 유용합니다.
제어 흐름 난독화 예
출처: PreEmptive
디버그 난독화
디버그 정보는 종종 프로그램 흐름, 디컴파일, 재컴파일을 통해 프로그램의 결함 등에 대한 중요한 정보를 아는 데 유용합니다. 식별자, 줄 번호를 변경하거나 디버그 정보에 대한 액세스를 모두 중지하여 이러한 식별 가능한 정보를 마스킹하는 것이 중요합니다.
주소 난독화
C, C++와 같은 비메모리 안전 언어를 사용하여 메모리 프로그래밍 오류를 이용하는 공격을 수행하는 것이 보편화 되었습니다. 검사되지 않은 배열의 액세스와 같은 오류로 인해 보안 취약성이 발생하는 경우가 많습니다. 주소 난독화 방법은 변환된 코드가 실행될 때마다 코드의 가상 주소와 프로그램의 데이터가 랜덤화되기 때문에 리버스 엔지니어링 과정을 어렵게 만듭니다. 이는 대부분의 메모리 오류 취약점을 이용한 공격의 효과를 장담할 수 없게 만들기 때문에, 공격 성공의 가능성을 낮추는데 효과적입니다.
주소 난독화의 예
출처: ScienceDirect
커스텀 인코딩
개발자는 커스텀 알고리즘을 사용하여 문자열을 인코딩하고 원래 코드를 가져올 수 있는 디코더 기능을 제공합니다.
런타임에 인수 전달
런타임에 인수를 예상하도록 프로그램을 변경할 수 있습니다. 이렇게 하려면 사용자가 변수의 암호를 해독할 코드와 암호 해독 키를 모두 가지고 있어야 합니다.
또한 보안 팀은 여러 가지 보안 위협으로부터 애플리케이션을 보호하기 위한 계층적 방어 방식을 구현하기 위해 둘 이상의 기술을 동시에 구현할 수도 있습니다.
난독화 방법의 품질 결정
코드 난독화의 성공 여부는 코드 변환 품질을 결정하는 몇 가지 매개변수에 달려 있습니다. 난독화 기술의 품질은 다음 요소의 조합에 의해 결정되어야 합니다.
힘과 탄력성:
난독화된 코드는 가장 취약한 부분에 의해 파괴될 수 있습니다. 따라서 난독화를 시도했을 때 난독화된 코드에 의해 어느 정도의 저항이 나타나는지 확인하는 것이 품질을 확인하는 가장 좋은 방법입니다. 코드를 해독하는 데 더 많은 노력과 시간이 필요할수록 난독화가 더 잘 된 것입니다.
차별화와 효과
이는 난독화된 코드가 원본 코드와 얼마나 다른지 보여줍니다. 제어 흐름의 깊이, 중첩 수준, 상속 수준은 소스 코드의 복잡성을 증가시키는 데 사용됩니다. 코드 난독화는 이러한 복잡성을 증가시킵니다.
스텔스
공격자가 난독화된 부분을 혼동하도록 원본 소스 코드와 구분할 수 없어야 합니다. 이는 공격자가 리버스 엔지니어링을 수행하기 어렵게 만듭니다. 이 요인은 상황에 따라 다르며, 종종 자동화된 리버스 엔지니어링 공격을 피하는 데 중요한 요소입니다.
비용
이것은 난독화되지 않은 코드에 비해 난독화된 코드를 실행하는 데 소요된 시간과 자원으로 정의됩니다. 난독화된 코드를 구현할 때 몇 가지 성능 고려 사항을 염두에 두어야 합니다. 지능적으로 난독화된 코드는 신중한 기술을 사용하여 불필요하게 비용/자원을 지출하지 않고 공격자를 혼란스럽게 하는 목적을 제공해야 합니다.
난독화가 코드 성능에 영향을 미치는가?
코드 난독화는 코드 구조에 큰 변화를 가져오므로 애플리케이션의 성능에도 큰 변화를 가져올 수 있습니다. 일반적으로 이름 변경 난독화는 변수, 메소드, 클래스만 변경되므로 성능에 거의 영향을 미치지 않습니다. 반면에 제어 흐름 난독화는 코드 성능에 영향을 미칩니다. 의미 없는 제어 루프를 추가하여 코드를 따르기 어렵게 만드는 것은 종종 기존 코드베이스에 오버헤드를 추가하기 때문에 구현해야 할 필수 기능이지만 주의가 필요합니다.
코드 난독화의 경험 법칙은 원본 코드에 적용되는 기술의 수가 많을수록 해독에 더 많은 시간이 소비된다는 것입니다. 기술 및 컨텍스트화에 따라 코드 성능에 미치는 영향은 일반적으로 10%에서 80%까지 다양합니다. 따라서, 위에서 논의한 요소인 효과와 복원력은 코드 난독화의 지침이 되어야 합니다(난독화 이름 변경 제외). 어떤 종류의 난독화도 기회비용이 있기 때문입니다.
위에서 설명한 대부분의 난독화 기술은 코드 성능을 중요시하며, 애플리케이션에 가장 적합한 기술을 선택하는 것은 개발 및 보안 전문가의 몫입니다. 바이너리 링크와 같은 기술은 여러 입력 라이브러리를 더 적은 수의 출력 라이브러리로 결합합니다. 이렇게 하면 애플리케이션이 가벼워지고 해커가 앱 코드를 조작할 수 있는 인터페이스가 줄어들 수 있지만, 코드 실행을 위해 런타임에 디컴파일을 하는 것은 번거롭고 종종 코드 실행 시간을 늘립니다. 이러한 균형은 보안 로드맵에서 고려하여 적절한 보호 계층을 추가하는 동시에 실제 환경에 코드를 실행할 수 있도록 사전에 결정해야 합니다. 즉, 난독화가 더 강력하고 복잡할수록 성능 오버헤드는 더 커집니다.
코드 난독화의 이점
보안 팀이 오픈 소스 플랫폼에서 호스팅되는 애플리케이션의 코드 난독화를 구현하는 방식에서 특히 많은 이점이 발생합니다. 신뢰할 수 없는 환경에서는 공격자가 코드를 검토하고 애플리케이션을 분석하기 어렵게 만드는 난독화된 애플리케이션을 배포하는 것이 항상 더 좋습니다. 이 프로세스를 통해 불법적인 이익을 얻기 위해 가짜 애플리케이션을 디버깅하고 변조 및 재배포하려는 범죄자가 이용할 수 있는 허점을 남기지 않을 수 있습니다. 이러한 보호 레이어는 특히 비즈니스에 치명적인 고객의 개인정보를 처리하는 애플리케이션에 필수적입니다.
또한 대부분의 난독화기는 유용하지 않은 메타데이터, 사용되지 않는 불필요한 코드 또는 중복 코드를 제거하여 코드를 최적화합니다. 이러한 최소화를 통해 컴파일 프로세스 속도가 빨라지고 코드 실행 속도가 빨라지며 결과도 빨라져 코드 실행 속도도 빨라집니다.
코드 난독화의 또 다른 주요 이점은 애플리케이션을 리버스 엔지니어링하기 어렵게 만든다는 것인데, 오픈 소스 플랫폼에 코드를 구축하는 것을 더 이상 걱정할 필요가 없게 됩니다. 반복적인 코드 난독화는 여러 개의 보안 레이어를 적용하는 경우에 특히 많이 사용됩니다. 이 기술에서 보안 팀은 이전 알고리즘의 출력이 다음 줄에 입력 역할을 하는 하나 이상의 난독화 알고리즘을 적용합니다. 이러한 방식으로 공격자는 프로그램의 원래 의도와 프로그램에 표시되는 내용에 대해 혼동할 수 있으며, 결과적으로 해독 시도가 실패할 수 있습니다.
코드 난독화는 난독화된 코드를 해독하는 데 심각한 노력, 기술, 자원, 시간이 필요하기 때문에 위협을 처리하고 재미로 해킹하는 사람들을 제거하는 실용적인 방법입니다. 해커가 성공하더라도, 해독된 부분은 원래 코드와 많이 닮지 않을 수 있습니다. 실효성 측면에서 실제 측정값을 찾기는 어렵지만 대부분의 기업은 보안을 위해 코드를 난독화하며 사유화합니다.
코드 난독화의 단점
모든 난독화 기술이 코드 성능에 영향을 미치기는 하지만 그 정도는 미미합니다. 난독화된 코드 부분과 난독화된 알고리즘의 복잡성에 따라 코드를 해독하는 데 상당한 노력이 수반될 수 있습니다.
자동화된 해독기 대부분은 난독화된 애플리케이션을 리버스 엔지니어링할 수 있습니다. 난독화는 리버스 엔지니어링을 지연시킬 수 있을 뿐 불가능하게 만드는 것은 아닙니다.
일부 안티바이러스 소프트웨어는 난독화된 코드가 있는 사이트를 방문할 때 사용자에게 경고를 보낼 수도 있는데, 이는 난독화가 악성 코드를 숨기기 위해 사용될 수도 있기 때문입니다. 이는 사용자가 합법적인 애플리케이션을 사용하는 것을 막고 신뢰할 수 있는 회사에서 멀어지게 될 수 있습니다.
코드를 난독화해야 하는가?
코드 난독화의 장단점을 고려할 때 적절한 질문은 ‘코드 난독화를 적용해야 하는가’입니다. 짧게 대답하자면 ‘예’입니다. 코드 난독화는 최소한 프로그램을 이해하기 어렵지만 기능을 그대로 유지하는 코드 조각으로 변환합니다. 리버스 엔지니어링 및 사이버 범죄자에게 제기되는 문제는 코드를 난독화하기에 충분한 이유입니다. 강력한 바이너리 수준의 난독화는 코드 미니화로 인해 프로그램 성능에 이점을 제공하므로 전략적인 코드 난독화를 구현해야 할 충분한 이유가 됩니다.
더 나은 결과를 위해 보안 전문가들은 코드 교체, 코드 변조 탐지, 런타임 애플리케이션 자가 보호(RASP), 워터마크, 암호화, 서버측 보호 등과 같은 다른 보안 메커니즘과 함께 코드 난독화를 구현하도록 권장합니다. 이렇게 하면 공격자는 제시간에 공격 행위를 완료하기가 어려워집니다. 성능에 민감한 코드를 식별하고 강력한 난독화 기술이 적용되는 경우 성능에 영향을 줄 수 있는 코드 부분을 표시할 수 있도록 애플리케이션의 런타임 동작을 분석하는 난독화 도구가 있습니다.
코드 난독화 도구
Android Studio에는 ProGuard 및 DexGuard와 같은 몇 가지 도구가 있습니다. Java의 오픈 소스 난독화기에는 클래스 파일 축소 프로그램인 ProGuard가 포함되며 사용하지 않는 클래스를 제거합니다. 또한 나머지 클래스의 이름을 의미 없는 이름으로 바꾸는 데 도움이 됩니다. 결과로 생성된 JAR 파일은 리버스 엔지니어링이 어렵습니다.
Python 난독화
PyArmor
이것은 Python 스크립트를 난독화하고 난독화된 스크립트를 고정 머신 스크립트에 바인딩하는 명령줄 인터페이스 도구입니다. 런타임 동안 각 함수의 co_code와 상수를 보호하여 Python 스크립트의 난독화를 도와줍니다. 또한 실행 중에 난독화된 스크립트의 라이센스 파일을 확인합니다. 개발자는 이를 통해 원래의 Python 스크립트를 난독화된 스크립트로 원활하게 교체할 수 있습니다.
JavaScript 난독화
Obfuscator.io
이 도구는 JavaScript를 난독화하고, 기존 JS 파일을 전혀 새로운 표현식으로 변환하여 기능을 변경하지 않고서는 이해하기 어렵고 재사용할 수 없게 만드는 것으로 유명합니다. 콘솔 출력을 비활성화하여 디버그 보호, 변수 및 함수 이름 변경, 문자열 제거, 비활성 코드 주입, 자가 방어 기능과 같은 다양한 변환을 수행합니다. 사용자가 JS 파일을 업로드하고 필요한 난독화 수준에 따라 적절한 옵션을 선택할 수 있는 사용이 간편한 인터페이스를 갖추고 있습니다. 전역 변수 이름 바꾸기, 문자열 배열 회전 또는 섞기, 문자열 배열 인코딩, 모든 문자열을 유니코드 표현식으로 변환하여 이스케이프 시퀀스에 유니코드 작업을 수행하는 등의 다양한 작업을 수행할 수 있습니다. 또한 제어 흐름 평탄화도 포함되어 프로그램 소스 코드의 이해를 방해합니다. 그러나 이러한 변환은 런타임 속도를 약 1.5배 느리게 하여 코드 성능에 영향을 미칩니다.
SourceMap
이것은 난독화된 JavaScript 코드를 디버깅하는 데 도움을 주는 또 다른 도구입니다. 별도의 소스 맵은 프로덕션 환경에서 코드를 디버깅하는 데 유용할 수 있으며, 개발 팀은 소스 맵을 외부에서는 알 수 없는 비공개 위치에 업로드할 수 있습니다.
UglifyJS
CLI 모드에서 작동하며 JS 코드를 최소화, 난독화, 복원(beautify)하기 위한 다양한 옵션이 있습니다. JS에서 AST(추상 구문 트리)를 생성하는 파서, 변수, 메소드 이름을 단일 문자로 줄이는 맹글러 구성 요소와 변환을 사용하여 AST를 작은 것으로 최적화하는 압축기 구성 요소로 구성됩니다.
JavaScript의 한 가지 단점은 완전한 해독 방지가 될 수 없다는 것입니다. JS는 기본적으로 브라우저에서 실행되며 브라우저의 JS 엔진이 리소스를 렌더링하기 위해 JS를 읽어야 하기 때문입니다. 따라서 이 캡처 포인트는 생략할 수 없습니다.
PHP 난독화
Naneu의 PHP 난독화기
이것은 PHP를 파싱하고 변수 이름과 메소드를 난독화하는 난독화 도구입니다. 이 라이브러리는 UnPHP와 같은 해독기로부터 PHP를 보호하며 네이티브 PHP 환경에서 코드를 실행하는 데 유용합니다.
Yakpro PO
이 도구는 PHP Parser 4.x를 사용하여 PHP 코드를 구문 분석하며 GIT 클론으로 사용할 수 있습니다. 이로 인해 PHP 컴파일러는 재배포 코드를 이해할 수 있지만 소스 코드를 변조하는 사람은 이해할 수 없습니다. 제어 흐름 난독화를 구현하고, 주석, 들여쓰기, 문자열 리터럴을 모두 제거하고, 프로그램 디렉토리 전체를 반복적으로 난독화하며, 이름 변경 난독화를 구현합니다.
PHP Compactor 라이브러리는 PHP 코드베이스를 간결화 및 압축하는 또 다른 오픈 소스 라이브러리 난독화 도구입니다. 공백, 빈 줄, 주석이 제거되고 문자열이 인코딩됩니다.
HTML 난독화
HTML은 HTML 태그를 사용하여 복원(beautify)된 단순히 마크업된 텍스트입니다. HTML을 난독화하는 것은 보통 JavaScript로 변환하거나, 각 HTML 줄을 해당하는 숫자 코드로 변환하거나, 두 가지 메소드를 조합하여 수행합니다. 이것은 종종 효과적이지만, 변환의 각 단계에서 코드 오버헤드가 추가되기 때문에 순수 HTML의 무시할 수 있는 크기를 2~3배 증가시킵니다.
기존의 보안 접근 방식은 주로 서버측 보안에 중점을 두기 때문에 HFO(HTML Field Obfuscation)는 무시되는 경우가 많습니다. 앱에는 몇 가지 보안 취약성이 있습니다. Man-In-The-Browser와 같은 공격은 스크립트를 주입하여 사용자 이름 및 암호와 같은 중요한 필드에 연결하고 사용자가 입력할 때 자격 증명을 탈취합니다. 부정행위 방지 애플리케이션은 HFO를 사용하여 스크립트가 실행되지 않도록 합니다.
이 접근 방식은 대상 필드를 식별하는 데 브라우저 기반 애플리케이션을 사용하는 공격을 교란하도록 설계되었습니다. 미끼 필드는 필드 값을 암호화하는 것 외에도 공격자의 주의를 분산시키는 데에도 사용됩니다.
C, C#, and C++ 난독화
C 코드를 난독화하는 가장 좋은 방법은 컴파일하여 바이너리만 배포하는 것입니다. 이렇게 배포된 코드를 원래 형태로 리버스 엔지니어링하는 것은 매우 어렵습니다. 디컴파일 후 C/C++ 기계어 출력은 자체 난독화 되어 있어, 일정 수준의 난독화 기능이 내장되어 있다고 할 수 있습니다. 이러한 특징은 지적 재산에 대한 기술적 보호에 도움이 된다고 할 수 있습니다.
ANTLR(Another Tool for Language Recognition)은 C/C++ 소스 코드를 입력 파일로 가져와서 입력의 암시적 계층을 캡처하여 AST로 변환하는 파서 생성기입니다. 얻어진 구조에 기초하여, 각 노드는 구조의 의미 있는 구성요소를 나타냅니다. 이는 노드를 재구성하고 난독화 후 성능과 복원력을 검토함으로써 난독화 알고리즘을 구현하는 기반이 됩니다.
C/C++에 비해 C# 코드는 리버스 엔지니어링이 더 쉽습니다. 그 이유는 C# 어셈블리에 C++에 없는 메타데이터가 포함되어 있기 때문입니다. 또한 C#는 C/C++보다 소스 코드로 디컴파일하기가 쉬운 중간 언어로 매핑됩니다. 자동 난독화 도구의 일부 기능에는 사용자가 정의할 수 있는 보존된 이름 목록, C#에 대해 미리 정의된 예약 이름 목록, 주석 제거가 있습니다.
Crypto Obfuscator는 정교한 난독화 기술을 사용하여 C# 코드를 리버스 엔지니어링으로부터 보호합니다. 여기에는 기호 이름을 인식할 수 없는 이름으로 바꾸기, 중요한 메소드를 숨기기 위한 외부 메소드로부터 호출 숨기기, 문자열 암호화, 제어 흐름 난독화 등이 포함됩니다.
결론
요컨대, 코드 난독화만으로는 복잡한 보안 위협을 처리할 수 없습니다. 코드를 해독하는 것은 어렵지만, 자동화된 도구를 이용할 수 있고 해커도 전문 지식을 갖고 있으므로 리버스 엔지니어링이 불가능하지는 않습니다.
따라서 코드 난독화는 모든 애플리케이션 보안 요구를 위한 올인원 솔루션이 아닙니다. 개발 팀은 보안 요구 사항, 애플리케이션의 특성 및 성능 벤치마크에 따라 신뢰할 수 없는 환경에서 코드를 보호하기 위해 다수의 코드 난독화 기술을 구현하는 것을 고려할 수 있습니다. 각 기술의 장단점을 고려하여 이러한 작업을 수행해야 합니다. 이 전략은 암호화, RASP, 데이터 보존 정책 등과 같은 다른 AppSec 이니셔티브를 보완해야 합니다. 앱실링(AppSealing)과 같은 RASP 도구와 함께 사용하면 현재의 보안 위협에 대한 강력한 해결책을 구축할 수 있습니다.