보안정보
전문화된 보안 관련 자료, 보안 트렌드를 엿볼 수 있는
차세대 통합보안관리 기업 이글루코퍼레이션 보안정보입니다.
난독화와 코드 가상화 – 악성코드에 사용되는 바이너리 보호기법
2019.02.13
15,399
1. 개요
악성코드 제작 및 유포 기술이 나날이 발전함에 따라, 이를 분석 및 대응하기 위한 역공학 기술 또한 복잡해져 가고 있다. 공격자는 유포지 및 명령/제어 서버(C&C) 주소를 감추고 백신 및 방화벽 등의 탐지를 피하여 피해를 지속시키기 위해 악성코드에 다양한 보호 및 난독화 기법을 적용한다. 보안분석가가 이를 분석하기 위해서는 이러한 기법을 우회하거나 무력화 해야 정상적으로 해당 멀웨어에 대한 정적 분석이 가능해 진다.
바이너리 파일의 악성 페이로드를 숨기기 위한 기법은 다음 3가지로 나뉠 수 있다.
Packer |
악성 페이로드를 압축하여 저장하고, 실행 파일이 동작할 때 동적으로 압축을 풀어 메모리에 적재하는 방식 |
Obfuscator |
코드의 변수, 클래스, 문자열 등을 해석하기 어렵게 변경 하거나 실행되지 않는 임의의 코드(Garbage code)를 삽입하는 등 가독성을 낮춰 분석을 지연시키는 방식 |
Protector |
Packer와 Obfuscator를 혼용하며, 추가적으로 안티 디버깅, 분석 환경 탐지, 코드 가상화와 등의 기법을 사용 |
[표 1] 바이너리 파일의 악성 페이로드를 숨기기 위한 기법 3가지
이번 호에서는 여러 가지 보호 기법 중 난독화와 코드 가상화 기술을 중심으로 해당 기법이 적용된 바이너리 분석 방법에 대해 서술하고자 한다.
예시에는 FinFisher (MD5: 4A49135D2ECC07085A8B7C5925A36C0A) 악성코드를 이용한다.
2. 난독화 (Obfuscation)
1) 난독화 개요
코드 난독화는 프로그램의 역공학 분석을 어렵게 하기 위해 사용되는 기법으로, 프로그램의 의미(semantics)를 유지하면서 배치(layout), 논리(logic), 자료(data), 구조(organization) 등을 변화시켜 분석가 및 자동화된 도구가 분석하기 어렵게 하는 것을 의미한다.[1]
① 심볼 제거/변경
C/C++로 컴파일된 바이너리의 경우 심볼 정보를 지움으로써 해당 파일을 디컴파일 했을 때 사람이 이해할 수 있는 정보를 최대한 줄일 수 있다. Java, .Net 프로그램의 경우 프로그램 수행에 심볼 정보가 필요하므로 해당 정보를 제거할 수는 없으나 클래스, 변수 이름을 변경함으로써 가독성을 낮출 수 있다.
② 필요 이상의 복잡성을 추가하거나, 실행되지 않는 코드 삽입
동일한 기능의 코드를 의도적으로 복잡하게 작성하거나 필요 없는 코드를 삽입함으로써 분석을 지연시킬 수 있다.
③ 데이터 인코딩/암호화
바이너리에 포함된 데이터를 알아보기 힘든 방식으로 인코딩/암호화하고 필요한 경우에만 디코딩/복호화하여 사용하는 방법으로 중요정보를 숨길 수 있다.
[1] 코드 난독화를 이용한 악성 코드 분석 기법에 관한연구, 2005, 한태숙 et al
2) 난독화 예시
[그림 1] FinFisher 악성코드의 코드 난독화
[그림 1]을 보면 상호 배타적인 두 가지 조건 분기문(ja, jbe)을 사용하여 같은 위치로 분기하는 코드를 확인할 수 있다. 해당 어셈블리는 무조건 분기문(jmp)으로 대체될 수 있는데 위와 같이 조건 분기문 두 개를 사용함으로써 분석도구가 분기문 다음에 오는 의미 없는 데이터(garbage code)를 opcode로 인식하게 하고, 이를 통해 바이너리를 비정상적으로 해석(Disassemble)하게 된다.
위의 난독화 코드를 복호화 하기 위해 아래와 같은 idapython 코드[2]를 이용할 수 있다.
[2] A walk-through tutorial, with code, on statically unpacking the finspy VM, 2018, Rolf Rolles
분기문의 opcode는 다음과 같은 형식으로 나뉠 수 있다.
Short jump의 opcode는 0x7로 시작하며, 다음 바이트가 목적지의 offset이 된다.
Near jump의 opcode는 0x0F로 시작하며, 세번째 바이트 부터 4바이트가 목적지의 offset이 된다.
위의 파이썬 코드에서 [1]과 [3]은 해당 주소의 opcode가 short/near jump인지 확인하는 내용이며,
[2]와 [4]는 두 개의 연속된 분기문이 동일한 주소를 가리킬 경우 하나의 jmp (0xEB or 0xE9) 코드로 변환하는 내용이다.
위의 파이썬 코드를 적용하면 아래와 같이 코드 흐름이 단순화 되는 것을 확인할 수 있다.
[그림 2] 난독화 해제 적용
3. 코드 가상화 (Code Virtualization)
1) 코드 가상화 개요
코드 가상화란 원래의 명령어를 가상화시키고 가상화된 코드를 본체에 있는 하드웨어적인 CPU가 처리하는 것이 아니라 개발자가 만든 소프트웨어적인 핸들러가 처리하게 되는 것을 말한다.[3]
바이트 코드가 실행되는 가상 머신(VM)은 일반적으로 다음의 구조를 갖고 있다.
① Initializer는 기존의 레지스터 및 플래그 값을 스택에 저장하고 가상환경에 필요한 스택과 레지스터를 초기화하는 역할을 한다. 또한 가상환경 구동에 사용될 구조체 및 변수를 초기화 한다. Initializer로 전달되는 opcode는 일반적으로 암호화되어 있거나 압축되어 있으므로 이를 복호화하고 압축 해제하는 작업 또한 이 단계에서 이루어 진다.
② Dispatcher 단계에서는 해석된 opcode를 기반으로 각각의 기능을 수행하는 Handler로 코드 흐름을 분기하는 역할을 한다.
③ 각각의 Handler에서는 가상의 Instruction을 구현한다.
[3] 코드 가상화 기법이 적용된 악성코드 분석 방법 연구, 2012, 박용수 et al
2) 코드 가상화 예시
[그림 3] Virtual opcode
Finfisher 악성코드의 .text 영역 끝 부분에 위와 같이 암호화된 opcode가 저장되어 있는 것을 확인할 수 있다. 해당 데이터는 Initializer 단계에서 복호화되고, 복호화 된 정보들은 구조체 및 변수로 저장되어 이용된다.
[그림 4] Initializer 실행
Initializer가 호출될 때 전달되는 값은 bytecode offset을 구하기 위해 사용된다.
Initializer는 Bytecode를 복호화하고, 가상머신에서 쓰이는 구조체 및 변수를 정의한다. 해당 코드를 역분석하면 아래와 같은 구조체[4]를 사용하고 있음을 알 수 있다. 생성된 vm_context 구조체의 주소는 ebx 레지스터에 저장된다.
[4] ESET’s guide to deobfuscation and devirtualizing finfisher, 2018, Filip kafka
아래는 dispatcher에서 handler를 호출하는 코드로, vm_context 구조체(ebx)에서 handler 테이블 주소(ebx+24h)를 가져와 인덱스 값(ebx+3Ch)을 더해 해당 handler 주소를 구하고 호출하는 역할을 한다. Handler 테이블은 오른쪽 그림과 같이 확인할 수 있으며, 해당 악성코드는 34개의 handler를 갖고 있다.
[그림 5] Dispatcher 코드와 Handler 테이블
handler는 각기 다른 native instruction을 구현한다. 아래는 “JL” instruction을 구현한 handler의 내용이다.
분석가는 각 handler가 어떤 기능을 갖고 있는지 역공학을 통해 알아냄으로써 대상 악성코드의 코드가 실제로 어떤 기능을 하는지 분석할 수 있다. 아래는 분석된 Finfisher 악성코드의 Handler 테이블이다.
Offset |
Instruction |
Offset |
Instruction |
Offset |
Instruction |
0x0 |
EXEC |
0xC |
JNS |
0x18 |
JGE |
0x1 |
JG |
0xD |
JL |
0x19 |
DEREF |
0x2 |
WRITE |
0xE |
EXEC |
0x1A |
JMP |
0x3 |
JNO |
0xF |
JBE |
0x1B |
* |
0x4 |
JLE |
0x10 |
SHL |
0x1C |
LOAD |
0x5 |
MOV |
0x11 |
JA |
0x1D |
JNE |
0x6 |
JO |
0x12 |
MOV |
0x1E |
CALL |
0x7 |
PUSH |
0x13 |
JZ |
0x1F |
MOV |
0x8 |
ZERO |
0x14 |
ADD |
0x20 |
JNB |
0x9 |
JP |
0x15 |
JB |
0x21 |
JNP |
0xA |
WRITE |
0x16 |
JS |
|
|
0xB |
ADD |
0x17 |
EXEC |
|
|
[표 2] Finfisher 악성코드의 Handler Table
4. 마무리
악성코드 제작 기술이 점차 정교해져 가고 있으며 최근 국가 단위 해킹 그룹의 활동이 활발해져 가고 있는 상황에서 이를 방어하기 위한 연구 또한 활발히 진행되고 있다. 백신 및 방화벽 등의 보안 조치를 구비하고 있다고 하더라도 알아본 바와 같이 공격자는 탐지를 우회하기 위한 다양한 시도를 할 수 있으므로, 사용자 및 보안 관리자는 잠재적인 위협을 인지하고 기본적인 보안수칙을 지켜 개인 및 공공의 정보자산 보호에 힘써야 할 것이다.
5. 참고 자료
[1] 코드 난독화를 이용한 악성 코드 분석 기법에 관한연구, 2005, 한태숙 et al
http://www.ndsl.kr/ndsl/search/detail/report/reportSearchResultDetail.do?cn=TRKO200700002732
[2] A walk-through tutorial, with code, on statically unpacking the finspy VM, 2018, Rolf Rolles
[3] 코드 가상화 기법이 적용된 악성코드 분석 방법 연구, 2012, 박용수 et al
https://kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9791170091424&orderClick=JAj
[4] ESET’s guide to deobfuscation and devirtualizing finfisher, 2018, Filip kafka
https://www.welivesecurity.com/wp-content/uploads/2018/01/WP-FinFisher.pdf