Anti disassembly

Kail-KM
|2015. 9. 1. 03:26

Understanding Anti Disassembly


안티 디스어셈블리 구현 시 악성코드 제작자는 디스어셈블러를 속여 실제 실행과 다른 명령어들의 목록을 디스어셈블러에 보이게 일련의 과정을 생성한다. 안티 디스어셈블리 기법은 디스어셈블러의 가정과 제약 사항을 이용한다. 예를 들어 디스어셈블러는 한번에 명령어 하나로 프로그램의 각 바이트를 표현하지만 이러한 잘못된 오프셋에서 디스어셈블하게 교묘히 조작하면 유효한 명령을 화면에서 숨길 수 있다. 아래의 코드를 살펴보자.


이 코드는 IDA Pro로 확인했을 경우에 보이는 코드이다. 40100E의 위치에 있는 JZ 401011 명령어가 존재하고 있다. 하지만 디스어셈블러는 401010의 명령어를 보여주며 심지어 잘못된 주소를 호출하고자 하는 것을 확인할 수가 있다. 이제 이룰 수정한 아래의 코드를 보자.

이전의 코드와는 다르게 40100E의 JZ 명령어가 올바른 곳을 가리키고 있는것을 확인할 수가 있다. 이는 401010의 위치에 0xE8 이라는 값이 존재하기에 디스어셈블러에서 잘못 나타나게 된 것이다.여기서 0xE8은 CALL 명령어의 OPCODE로 0xE8까지 같이 인식이 되었기에 잘못된 주소를 호출하고자 한 것이다.


*OllyDBG의 경우 저 부분은 올바르게 인식을 했지만 다른 부분을 올바르게 인식하지 못하였다. 그러므로 하나의 디스어셈블러에만 의존하는 것은 자칫 오류를 범할 수가 있다.


아래는 CALL 명령어 뒤에 "hello"라는 문자열이 존재하는 경우이다. 원래의 경우라면 hello라는 문자열은 호출되지 않으므로 아무 이상없어야 한다. 하지만 아래 그림 에서와 같이 Call 명령어 뒤에 PUSH 명령어가 존재하는 것을 확인할 수가 있다. 이는 "h"에 해당하는 0x68이 PUSH의 OPCODE이기에 push는 하나의 오퍼랜드를 필요로 하기에 뒤에 4바이트가 push의 오퍼랜드로 인식이 되어 맨 뒤의 0x00은 그 뒤의 명령어들과 짝을 이루어 나타나게 된다.

이를 정상적으로 나타내도록 수정하면 아래와 같이 'hello' 문자열이 나타나고 pop eax 와 retn 명령어가 제대로 나타나는 것을 확인할 수가 있다.



Anti-Disassembly Techniques


디스어셈블러가 부정확하게 디스어셈블하게 하는 악성코드의 주요 방법은 디스어셈블러의 선택과 가정을 악용하는 것이다. 이제 이러한 종류에 어떤 것들이 있는지 확인을 해볼 것이다. 


Jump Instructions with Same Target

이러한 방법 중 하나인 동일한 대상으로 점프하는 명령어에 대하여 알아보면 아래의 명령어와 같다. 이는 두 조건 분기 명령어가 연달아 두 개 모두 같은 지점을 가리키는 형태다. 이는 실제로 JMP 명령어와 다를 바가 없지만 디스어셈블러는 이 명령어를 올바르게 인식하지 못한다.

위에서는 call 명령어가 잘못된 주소를 가리키고 있는 것을 확인할 수가 있다. 이에 대하여 우리는 수정을 하여 아래와 같이 나타낼 수가 있다. 실제로 두개의 조건 분기들은 모두 올바르게 가리키고 있는 것을 확인할 수가 있다. 여기서 우리는 IDA를 통하여 잘못된 주소를 가리키는 부분이 붉게 표시되는 것을 확인할 수가 있는데, 이 표식은 현재 분석하고 있는 파일이 안티디스어셈블리 기법이 적용됐을 가능성을 나타내는 단서 중 하나이다.


A Jump Instruction with a Constant Condition

위와 유사하게 찾을 수 있는 또 다른 안티디스어셈블리 기법은 항상 동일한 조건으로 구성한 하나의 조건 분기 명령어를 이용하는 것이다. 다음 코드는 이 기법을 사용한다. 아래의 그림을 보면 xor eax, eax 명령어로 시작되는 코드를 주의해야 한다. 이 명령어는 EAX를 0으로 만들고 부가적으로 ZF를 설정한다. 이 다음 조건 분기 명령어인 JZ는 ZF가 설정되어 있을 때 분기를 한다.

디스어셈블러는 거짓분기를 우선 처리하면서 참일 때의 분기 구문과 충돌하게 되며 거짓 분기를 첫 번째로 처리하기 때문에 해당 분기를 더 신뢰한다. 아래의 그림을 보면 무조건 점프로 인하여 0xE9는 무시되어야 하자만 거짓 분기를 우선적으로 처리하므로 인하여 E9가 그 뒤의 바이트 까지 오퍼랜드로 이용을 한다는 것이다.

이를 정상적으로 수정하면 아래와 같이 표시가 된다. 여기서도 우리는 올바르지 않은 주소로 점프하려하는 것을 확인할 수가 있었고 이를 단서로 안티 디스어셈블리 기법을 의심할 수가 있다. 아래에는 0xE9가 하나의 Byte로 존재하며 그 뒤의 pop eax와 retn이 올바르게 나타나 있다.


Impossible Disassembly

위에서는 디스어셈블러가 부적절하게 디스어셈블한 코드를 살펴보았다. 그러나 단일 바이트를 두 명령어의 일부로 표현하는 디스어셈블러는 현재 존재하지 않지만 프로세서는 단일 바이트를 다중 명령어로 사용되지 못하게 하는 제약이 없다.

아래의 그림을 보면 첫 번째 명령어는 4바이트의 mov 명령어이다. mov의 마지막 2 바이트는 mov 명령어의 일부이면서 나중에 실행할 명령어 자체이기도 하다. 두번째 명령어인 xor 은 레지스터를 0으로 만들고 ZF를 설정한다. 세번째 명령어는 조건 분기로 ZF가 설정돼 있을 경우 분기한다. 하지만 직전 명령어가 항상 ZF를 설정하므로 실제론 무조건 분기라고 할 수 있다. 디스어셈블러는 jz 명령어 다음에 0xE8로 시작하는 5바이트의 call 명령어의 디스어셈블 여부를 결정한다. 하지만 실제로 이 0xE8은 실행되지 않는 가짜 바이트이며 그 다음 부분이 실제로 실행될 부분이다.



Obscuring Flow Control


Return Pointer Abuse

아래의 코드를 살펴보면 call 명령어가 바로 뒤의 add 명령어를 인식한다. 하지만 add의 오퍼랜드는 IDA에서 인식한 것으로 항상 정확하지는 않다. ESP+4+var_4에서 var_4는 상수로 -4를 이야기하는 것이다. 이는 즉, ESP+4+(-4)이므로 ESP를 기리키게 되는데 이 ESP에 +5를 더하는 것이다. 이로 인하여 스택의 ESP에 위치한 RETN 주소(4011C5)에 5를 더하게 되므로 이는 결국 4011CA를 기리키게 되며 이 주소로 다음 명령어에 의해 이동하게 된다.

이는 가짜 RETN을 통하여 그 부부능로 이동하는 것이라 할 수 있다. 이 예제에서는 위의 명령어 3개를 NOP으로 수정하여 정상적으로 패치할 수가 있다.


Misusing Structured Exception Handlers [ SEH ]

SEH 메커니즘은 디스어셈블러로 따라갈 수 없게끔 디버거를 속여 흐름을 제어하는 방법을 제공한다. SEH는 프로그램이 지능적으로 에러 상황을 처리할 수 있는 방식을 제공한다. 우선 유효하지 않은 메모리에 접근하거나 0으로 나누는 겨우와 같이 다양한 이유로 Exception이 발생할 수 있다. 이에 추가적으로 소프트ㅜ에어 예외 처리는 RaiseException 함수를 호출함으로 발생할 수 있다.

SEH chain은 스레드 내에서 예외 처리를 목적으로 고안한 함수 목록이다. 목록 내의 각 함수는 예외를 처리하거나 목록 내의 다음 핸들러에게 넘길 수 있다. 예외가 최정 핸들러까지 넘어간 경우 처리 불가 예외(Unhacndled exception)로 간주한다.

SEH 체인을 찾으려면 운영체제는 FS 세그먼트 레지스터를 확인한다. 이 부분은 TEB로 스레드 환경블록이라 하며 이에 접근하기 위해 사용 되는 것이 FS 이다. TEB에 있는 첫번째 구조는 스레드 정보블록 TIB이다. 이 TIB의 첫번째 항목이 SEH 체인을 가리키는 포인터이다.SEH Chanin은 EXCEPTION_REGISTRATION 레코드라 부르며 8바이트 데이터 구조의 단순 연결 리스트이다.

SEH 체인은 프로그램의 변화 속에서 서브루틴을 호출하거나 예외 핸들러 블록을 삽입하기 때문에 예외 처리 핸들러가 스택과 같이 동작하며 예외가 발생할 때 가장 먼저 ExceptionHandler 함수를 호출한다. 이는 MS의 소프트웨어 DEP ( Data Execution Prevention)라는 메커니즘에 의해 부과된 제약조건이 적용된다.


Conclusion


현대 디스어셈블러와 같은 고급 프로그램은 명령어가 구성하는 프로그램을 결정하는 작업을 훌륭하게 수행하지만, 디스어셈블러는 여전히 가정 사항을 필요로하고, 프로세스에서 선택해야하는 문제가 있다. 디스어셈블러가 선택하고 가정하는 각각에 대응하는 안티디스어셈블리 기술이 있을 수 있다.


'Reversing > Theory' 카테고리의 다른 글

Anti Virtual Machine  (0) 2015.09.03
Anti Debugging  (0) 2015.09.03
DLL Injection  (0) 2015.08.29
데이터 인코딩  (0) 2015.08.26
위장 악성코드 실행  (1) 2015.08.24