카테고리 없음

난독화

eulb 2024. 12. 6. 00:25

난독화 기법 갈래

Data-based Obfuscation : 프로그램 내부에서 사용되는 변수와 상수를 난독화

Control-based Obfuscation : 프로그램의 흐름과 의도를 이해하기 어렵도록 난독화

Data-based obfuscation

Constant Unfolding

컴파일 과정에서 최적화 기법으로 사용되는 Constant Folding에 반대되는 개념으로,
코드 상에서 사용하는 상수를 계산과정을 통해 도출해 내어 분석자로부터 데이터를 감추는 기법이다.

위의 과정을 예시로 들면
스택에 특정 상수를 넣은 후, 스택의 값에 특정 값을 더하여
0x0
라는 상수를 만들어 내는 과정이다.

 

Data Encoding

위의 Constant Unfolding과 유사하게 데이터를 나타내는 방식을 바꾼 것으로,
Polynomial Encoding(다항식 부호화), Residue Encoding(나머지 부호화), Homomorphism Encoding(준동형 부호화)등이 있다.

이때 다항식 부호화는 상수를 계산식의 연산과정을 통해 나타낸것, 나머지 부호화는 계산식의 나머지를 계산하여 사용하도록 나타낸것, 준동형 부호화는 같은 형태(데이터 형식 및 데이터 크기)를 가지는 다른 값을 계산을 통해 사용하는 것을 나타낸다.

 

Dead Code Insertion

컴파일 과정에서 최적화 기법으로 사용되는 Dead Code Elimination에 반대되는 개념으로,
프로그램 실행 단계에서 사용되지 않는 코드를 의도적으로 주입하여 해당 명령이 의미 있는 결과를 도출하는지 여부를 분석자에게 추가적으로 분석하도록 하는 기법이다.

위와 같은 코드가 있을 때,
{x = 1;}
{y = 2;}는 함수 실행 결과에 영향을 주지 않으므로
컴파일러는 해당 코드를 무시하여 최적화를 진행하지만 이를 의도적으로 주입하여 난독화한다.

 

Arithmetic Substitution

부울대수 성질을 이용하여 단순한 코드를 의미가 같지만 더 분석하기 어려운 코드로 변환하는 기법이다.

위의 코드는 EAX의 모든 비트를 반전시키는 코드인데
{NOT EAX}
명령어와 같은 동작을 하는 것을 알 수 있다.

 

Patten Based Obfuscation

한 개 이상의 이웃한 코드를 동일한 의미를 갖지만 더 복잡한 코드로 변환한다.
이때 위의 Arithmetic Substitution과 다른 점은 산술 계산에 근거를 두고 변환하는지, 명령어의 결과에 근거를 두고 변환하는 지의 차이이다.
이를 이용할 때 프로그램의 패턴을 충분히 만들어 두게 된다면, 프로그램을 임의의 큰 크기로 변환할 수 있다.
명령어의 결과를 기반으로 변환하기 때문에 레지스터의 값이 달라지는등 동일한 의미를 갖는 코드가 동일한 동작을 하지 않을 수 있다.

 

 

control-based obfuscation

Inline/Outline Function

함수의 논리적 구조를 파악할 때 사용되는 호출 그래프를 분석하기 어렵게 바꾸는 과정이다.
피호출 함수를 호출함수 속에 병합시키거나 (Inline), 함수의 일부를 추출하여 독립적인 함수로 만들어 호출한다. (Outline)

 

Destruction Sequential Locality

컴파일 과정에서 이루어지는 순차적 최적화에 반대되는 개념으로,
프로그램의 실행 순서를 의도적으로 꼬아 분기를 따라가는 불필요한 작업을 분석자에게 강제하여 분석 속도를 늦추는 방법이다.

 

Processor-based Control Bypass

프로세서 제어 이동에 사용되는 JMP명령어나 CALL명령어를 일반적이지 않은 용례(분기 주소 미 명시, 명령어가 의도된 용도와는 다른 사용)로 사용하여 프로그램의 제어 흐름을 분석하기 어렵도록 하는 기법이다.

OS-based Control Bypass

OS에서 디버깅을 위해 지원하는 예외처리 기능(SEH, VEH, Unhandled Exception Handler, Signal Handler, SETJMP/LONGJMP)을 이용하여 예외를 처리하는 과정에서 추가적인 작업을 통해 프로그램의 제어 흐름을 이해하는 것을 방해하는 기법이다.

 

Control-flow Graph Neutralize

분기에 사용되는 제어구조를 한 개의 스위치 문으로 바꾸는 Dispatcher를 사용하여 분석자가 프로그램 제어 흐름을 분석하기 어렵도록 하는 기법이다.

 

Virtual Machines

프로그램 실행 중 일부를 Interpreter를 사용하여 구현하는 방식이다.
Interpreter를 프로그램 내부에 구현하고 이 프로그램이 실행 바이트 코드를 동작시키는 방식으로 구현된다.
이 방식은 프로그램 실행 효율성을 떨어뜨리는 문제 때문에 프로그램 중 일부에만 구현되는 것이 권장된다.

 

난독화 해제에서 사용되는 개념

Approximation

프로그램의 원 코드을 추정하였을때 얼마나 동일하게 구현하였는지를 뜻하는 ‘근사’의 개념이다. 이때 over-approximation은 프로그램의 원 소스코드에 가깝게 구현하는 것이고, under-approximation은 프로그램의 동작에 가깝게 구현하는 것이다.

 

Soundness – Completeness

분석 결과의 정확성을 우선시하는 Soundness(견실성)과 동작 가능성을 우선시하는 Completeness(완전성)이다.
Soundness는 정확한 분석을 지향하여 over-approximation를 지향하고, Completeness는 동작 가능성에 대한 검증을 우선시 하는 특성상 under-approximation를 지향한다.

난독화 해제

Abstract Interpretation(추상 해석)

프로그램을 분석하면서 구체적 동작을 추상화하고, 이를 다시 프로그램의 동작으로 바꾸는 과정을 거칠 수 있는데 이를 Abstract Interpretation이라고 한다. 이를 바탕으로 프레임워크를 형식화 시킬 수 있다.

프로그램이 갖는 Semantics는 프로그램이 동작되는 시스템 환경과의 상호작용을 포함한 구체적인 동작의 일체를 가리킨다. 이를 더욱 구체화 시켜 순서에 따라 전개할 때 Trace Semantics가 되는데 프로그램이 갖는 상태와 전이에 관한 모든 정보를 포함한다.

이 구체적 의미를 다시 추상화 시킨 집합을 Abstract domain이라고 하는데 이것이 모호하게 해석될 경우 프로그램의 해석이 불명확 해진다.
일반적으로 Abstract Interpretation은 프로그램의 구체적 의미를 over-approximation하는 정적 분석과정에서 주로 사용된다.

 

Partial Evaluation(부분 평가)

프로그램의 입력 데이터와 출력 데이터를 바탕으로 데이터의 변화를 기반으로 프로그램의 동작을 분석하는 방법이다. 입력 데이터가 유지되는지, 바뀌는 부분에 대해 어떤 행위가 이루어지는지, 입력 값에 대하여 최적화가 이루어지는지를 파악한다. 프로그램의 근본적 동작을 확인하는 방법이므로 난독화를 무력화시킬 수 있는 가장 확실한 방법이다.

 

Slicing

프로그램을 기준에 따라 나누어 단순화시킨 후 분석하는 방법이다. 나누는 기준을 잡는 방식에 따라  분석자가 임의로 지점을 선정하는 Static Slicing과 정적 분석을 통해 구체적인 실행 부분을 확인하여 실행 과정에 따라 지점을 선정하는 Dynamic Slicing으로 나뉜다.

 

Dynamic Symbolic Execution

동적 분석 과정 중 데이터의 변화 과정을 수식을 통하여 나타내는 방식이다.
프로그램의 실행 과정을 Symbolic State를 갱신하는 과정을 통하여 따라가면서, 프로그램을 수식화 시키는 방법을 사용한다.

이 방식은 무한루프를 수식화 하기 어렵다는 단점을 가지고 있기 때문에 이를 임의의 루프 한계를 통해 under-approximation하여 단순화 시켜야 한다.