Notice
Recent Posts
Recent Comments
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

topcue

바이너리 난독화 기법(Binary Obfuscation Techniques) 본문

바이너리 분석

바이너리 난독화 기법(Binary Obfuscation Techniques)

topcue 2021. 9. 22. 21:24

Introduction

바이너리 난독화(binary obfuscation)의 주요 목적은 바이너리의 정적 분석 과정을 방해하는 것이다.

바이너리의 심벌(변수 및 함수) 이름을 바꾸는 것은 변수가 어떤 데이터를 보유하고 있는지에 대한 정보 또는 함수명에서 동작에 대한 단서 등이 손실된다는 것을 의미한다.

코드를 난독화하는 데에는 여러 가지 이유가 있을 수 있다.

가장 일반적인 경우는 DRM(Digital Rights Management) 때문이다. 예를 들어, 자격을 확인하는 코드를 난독화하여 크래커(cracker)로 하여금 자격 확인을 우회하기 어렵게 만들 수 있다. 난독화가 쓰이는 또 다른 분야는 anti-cheat 소프트웨어다.

물론 이외에도 안티 바이러스나 샌드박스(sandbox)를 우회하는 악의적인 동작을 숨기는 목적으로 쓰이기도 한다.

난독화의 주요한 목표 중 하나는 분석가의 작업을 어렵게 만드는 것이다. 난독화는 일반적으로 컴파일러에 의해 최적화된 간단한 코드의 최적화를 해제(de-optimizing)하여 남용(abuse)한다. 그로 인해 CPU가 더 많은 계산을 수행해야 하지만 사람인 분석가가 더 이해하기 어렵게 만든다.

다음 중 하나라도 만족한다면 난독화의 목표를 달성했다고 말할 수 있다.

  • 분석가가 분석할 시간이 없다.
  • 분석가가 좌절해서 포기한다.
  • 난독화가 해제되기 전에 프로그램이 대체(superseded)된다.

난독화는 unbreakable한 것이 아니며, 그것을 깨는 데 필요한 시간이 성공했을 때의 이점보다 크다면 성공이다.

난독화에는 다양한 유형이 있으며, 여기서는 가장 일반적인 유형을 다룰 것이다. 실무에서 마주친 난독화의 종류를 식별할 수 있는 것을 목표로 한다.

Junk Code Insertion

정크 코드 삽입(junk code insertion)은 명령어 수행의 결과에 영향을 미치지 않는 선에서 실제 명령어들을 코드(다른 명령어들) 사이에 교차해 배치(interleave)하는 기술이다. 다시 말해 실제 명령어를 다른 명령어들 사이에 숨기는 기술이다. 이때 숨기려는 명령어는 실제로 아주 단순한 동작을 수행할지라도 기타 명령어들과 함께 보면 아주 복잡한 작업을 수행하는 것처럼 보인다.

이런 명령어들은 분석한 뒤 nop으로 패치하는 방식으로 우회할 수 있다.

이는 아주 간단한 기술이지만 경험이 부족한 분석가를 속이기에 충분하다. 이 기술은 opaque predicates변종 코드(metamorphic code)와 결합해 사용할 때 유용하다.

  • Junk Code Insertion technique

Metamorphic Code

변종 코드(metamorphic code)는 명령어를 같은 결과를 산출하지만 난해한 일련의 코드로 확장시키는 기술이다.

그 효과는 단순한 코드를 복잡하게 보이도록 만드는 독창성에 달려있다. 이는 난독화에서 가장 중요한 주제이며 다른 몇 가지 기법의 핵심이다.

  • Metamorphic Code technique

Data Encoding

데이터 인코딩(data encoding)은 산술 연산과 literal value를 여러 난해한 계산으로 확장시키는 기술이다. 다시 말해 정적 데이터 값을 하드 코딩하지 않고 런타임에 계산되도록 하는 것이다.

이는 필요한 곳에서 호출되는 동안 디코딩된 문자열을 임시로 계산한 다음 호출이 반환된 후 해당 메모리를 지우거나 다시 인코딩하는 방식으로 문자열에도 적용된다.

  • Data Encoding technique

Opaque Predicates

opaque predicate는 정적 결과의 분기(that always taken or never taken)에 코드를 삽입하여 branch predicate의 계산 자체를 난독화하는 기술이다. 동일한 결과를 생성하는 trivial branch 명령어 대신 여분의 검사를 추가한다. 또한 절대 취해지지 않는(nerver taken) 분기를 추가해 분석을 더욱 혼란스럽게 만든다.

이 기술은 코드의 cyclomatic 복잡도를 폭발적으로 증가시키고, 대부분의 디컴파일러들이 난해한 코드를 생성하게 만든다.

  • Opaque Predicates technique

Splitting

splitting은 함수나 기본 블록을 비조건 점프(unconditional jump), call, opaque predicates 등으로 구성되는 더 작은 함수나 블록으로 분할하여 interleave하는 기술이다. 이는 코드의 전체적인 흐름을 파악하는 것이 불가능하거나 매우 어려워지기 때문에 함수의 context나 상태를 추적하는 것을 어렵게 만든다.

splitting되면 코드는 함수가 덜 선형적으로 보이게 만들기 위해 종종 scramble되거나 랜덤화된다.

  • Splitting technique

Control Flow Flattening

제어 흐름 평탄화(control flow flattening)는 함수의 논리 흐름을 state machine으로 바꾸고 상태를 기반으로 모든 경로를 단일 dispatch table로 평탄화(flatten)하는 기술이다.

각 분기는 다음 분기에 따라 상태를 업데이트한다. flattening은 해석되는 바이트 코드가 없다는 점을 제외하면 가상화와 모습이 비슷하다.

  • Control Flow Flattening technique

Virtualization

가상화(virtualization)는 난독화 분야에서 가장 최근에 발전한 기술 중 하나다. native code를 매칭되는 interpreter loop과 함께 동적으로 생성된 unique bytecode로 변환하여 동작한다. 그런 다음 함수 호출은 실행할 바이트코드 스트림에 대한 포인터가 있는 가상 머신에 대한 entry point로 대체된다.

제어 흐름 그래프(CFG)는 제어 흐름 평면화(control-flow-flattened) 함수와 거의 동일하게 보이지만 함수 자체에 상태를 포함하는 대신 인터프리터에서 바이트코드 실행을 로드하고 처리한다는 점이 추가되었다.

Polymorphic Code

다형성 코드(polymorphic code)는 실행 중에 스스로 수정하여 수행하는 작업이나 후속 실행(심지어는 동일한 실행 중에도)에서 수행하는 경로를 변경하거나 코드를 수정하는 기술이다. 이 기술은 분석하기 매우 어려워 보일 수 있지만, 쓰기 가능하고 실행 가능한 메모리(또는 메모리 보호 API에 대한 호출)가 필요하므로 이를 감지할 수 있다.


참고

[1] https://hexterisk.github.io/blog/posts/

[2] NorthSec 2020 Advanced Binary Analysis Workshop by Alexandre Beaulieu


Comments