DBI - Frida 를 이용한 DBI
DBI
Dynamic Binary Instrumentation - 동적 바이너리 조사
printf("test %s", buf); 등과 같이 특정 시점에서의 변수값 등을 확인하거나 프로그램의 행위를 조사하는 일을 Instrumentation 라고 부른다. 소스 코드 수준에서 하는 행위를 SI (Source Instrumentation), 바이너리 수준에서 하는 행위를 BI 라 한다. BI 중에서도 바이너리 실행중에 조사하는 것을 DBI 라 부른다. 조금 더 구체적으로 설명하면, 바이너리가 어떤 함수들을 호출하는지, 또 인자값은 무엇인지 등을 조사하는 것이라 보면 될 것 같다. 예를 들어, 악성코드 분석을 할때 각종 모니터링 툴들을 켜고 악성코드를 실행하여 행위를 분석하는 것 또한 동적 바이너리 조사의 일종이라고 볼 수 있다.
DBI 구현
DBI 를 구현하는 방법에는 디버거 스크립트를 이용하는 방법, 후킹을 이용하는 방법, 코드 캐쉬와 CPU 가상화를 이용한 에뮬레이팅 등이 있다. 먼저 디버거를 이용하는 경우에는 디버거 스크립트와 조건부 브레이크포인트, 로그기능 등을 이용할 수 있다. 후킹을 이용하는 경우 주요 함수들( 네트워크 관련 함수들, File I/O 함수들, 문자열 처리 함수 등)에 후킹을 설정한 뒤 함수 후킹 시 인자값 등을 기록하거나 변조하는 행위들을 할 수 있다. 마지막으로 에뮬레이팅을 이용한 방법은 코드를 읽어서 가상 CPU 로 실행시키는 방법이다. 이전의 두가지 방법과는 다르게 투명성이 보장된다. 디버거, 후킹의 경우 이미 많이 알려져 있고 프로세스의 코드를 직접 변경하기 때문에 보호기법에 의해 쉽게 발견될 수 있다. 하지만 에뮬레이팅의 경우 코드를 복사해서 해석한뒤 실행하므로 일반적인 탐지기법들을 우회할 수 있다.
DBI 로 할 수 있는 것들
- Flow Analysis
- Reversing Automation
- Malware Analysis
- Taint Analysis
- Binary Visualization (ex: VERA)
- Vulnerability(Exploit) Analysis
이외에도 기타 상상할 수 있는 다양한 자동 분석들이 가능하다. DBI 를 이용해서 안티디버깅 코드를 탐지하는 논문도 보았고, unpacking automation 논문도 본 적이 있다. 최근에는 DBI 를 Tracer 로 사용해서 로그를 남기고 그 정보들을 활용하여 Symbolic Execution, SMT Solver 등과 연계하여 취약점을 자동으로 찾거나(AEG, Automatic Exploit Generation) CTF 문제 등을 자동으로 풀어주는 등 Advanced 한 주제들도 지속적으로 연구되고 있다. 관심이 있다면 Triton, Angr 등의 Framework 를 살펴보면 된다.
DBI Framework
PIN & PINTOOL
intel 에서 관리하는 DBI 프레임워크로 기업이 관리하는 특성상 유지보수가 매우 잘 되고 있으며 다양한 플랫폼을 지원한다. 단점은 Intel 아키텍쳐(intel cpu)만 지원한다는 점과 C++ 로 작성해야 해서 매번 컴파일 하는 것이 조금 번거롭다는 것이다. 코드를 해석해서 가상 CPU 에서 실행시킨다. 실제로 원본 파일은 실행조차 되지 않는다. 때문에 투명성이 보장되지만 속도 이슈가 있다.
(참고) http://hyunmini.tistory.com/87
FRIDA
FRIDA 는 scriptable 한 DBI 프레임워크로, http://www.frida.re 에서 무료로 다운로드 및 설치가 가능하다.(심지어 오픈소스이다. 근데 vala 라는 특이한 프로그래밍 언어로 작성되어 있다. 신기함...) FRIDA 는 기타 DBI 프레임워크와 약간 다른 구조를 가지고 있는데, DBI 를 위한 주 조작은 자바스크립트를 통해서 하며, C/S 구조로 동작하게 된다. 즉 처음에 바이너리에 프레임워크 라이브러리를 인젝션하여 파이프를 만들어 놓고, 그 파이프를 통해서 명령을 주고 받으면서 바이너리 조사를 할 수 있도록 되어 있다. 물론 C/S 를 사용하지 않고 그냥 PIN 처럼 내부적으로 동작하도록 할 수도 있다. 여러가지 언어를 이용하여 스크립트를 작성할 수 있으나(바이너리 조작은 자바스크립트로 해야 한다) Python 이 가장 단순하고 좋은 듯 하다. 하지만 제작자는 발표 등을 할 때 node.js 를 많이 사용하는 모습을 보여주고 있다. FRIDA 의 가장 큰 장점은 다양한 플랫폼을 지원한다는 것이다. PIN 과는 달리 ARM 아키텍쳐를 지원하므로 Android, IOS 앱에도 적용이 가능하다. 또한 64 bit 에서도 정상적으로 동작한다.(awesome!!)
또한, 컴파일이 필요없는 스크립트 언어로 작성이 가능하다는 것도 매력적이다. 코드 수정이 빈번한 경우 매번 컴파일을 하는 것만큼 귀찮은 것은 없기 때문이다.
ETC(DinamoRIO, Valgrind, DynInst 등)
아직 해보지 않아서 잘 모름. Valgrind 의 경우 실제로 취약점을 꽤 많이 찾아준 것으로 알고 있다.
DBI 예제 (with FRIDA)
FRIDA 기본 원리
FRIDA 는 C/S 의 구조를 지원하기 위해 윈도우의 경우 DLL 인젝션을, *닉스 계열의 경우 라이브러리 인젝션을 수행한다. 그 후 API 후킹(주소값만 지정하면 유저 custom 함수도 가능), stalking(이벤트 기반) 기능 등을 이용하여 DBI 가 가능하다. 이때, 실제 DBI 를 수행하는 코드는 자바스크립트를 이용하여 작성해 주어야 한다.
FRIDA 설치
c:\> pip install frida // pip 는 python 모듈 관리를 위한 프로그램이며 이를 이용하여 쉽게 설치가 가능함
FRIDA 참고 자료 및 API
http://www.frida.re/docs/javascript-api // 아직 관련 자료가 많지 않아 공식 문서를 보는 것이 가장 좋다.
https://github.com/OALabs/frida-extract // frida 를 이용한 PE Extract 코드 (악성코드 등에서 PE 파일 추출)
FRIDA 예제 코드
아래 코드는 DBI 에 대한 개념이해를 위해 직접 발로 짠 코드이다. 간단하게 socket 관련 함수인 send() 함수를 후킹하여 버퍼를 기록하고, send() 함수를 호출한 코드 주소값들(BackTrace) 을 기록해주는 코드이다.
import sys
import pefile
import frida
def on_message(message, data):
# c/s 구조인 경우 메시지를 보낼 수 있다. 메시지를 처리할 핸들러를 여기에 작성
print "[%s] -> %s" % (message, data)
def main(target_process):
session = frida.attach(target_process) # attach => target 에 DLL 인젝션 수행
script = session.create_script(""" # 자바스크립트 로드(실질적인 DBI 코드)
var sendPtr = Module.findExportByName("ws2_32.dll", "send"); # send 함수 주소
console.log('send address: ' + sendPtr.toString());
console.log('>> Hooking ws2_32!send <<');
Interceptor.attach(sendPtr, { # send 함수에 후킹 설정(inline hooking 수행)
onEnter: function (args) { # 함수 실행 전 수행될 코드(인자값 기록, 변조 등 가능)
console.log("buf : " + Memory.readCString(args[1])); # 메모리에서 직접 read
console.log("send called from:" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n") + " ");
},
onLeave: function (retval) { # 함수 실행 후 수행될 코드(리턴값 기록, 조작 등 가능)
}
});
""")
script.on('message', on_message) # 메시지 핸들러 설정
script.load()
raw_input('\n\n')
session.detach()
if __name__ == '__main__':
target_process = 'test.exe'
main(target_process)
실행
FRIDA 예제 코드2
이번에는 인자값 조작을 해 보자. send 로 전송되는 패킷 내용을 변조하는 코드이다. 위 코드와 거의 동일하며 함수 실행 전 인자값으로 넘어온 buffer 문자열을 조작한다.
import sys
import pefile
import frida
def on_message(message, data):
print "[%s] -> %s" % (message, data)
def main(target_process):
session = frida.attach(target_process)
script = session.create_script("""
var sendPtr = Module.findExportByName("ws2_32.dll", "send");
console.log('send address: ' + sendPtr.toString());
console.log('>> Hooking ws2_32!send <<');
Interceptor.attach(sendPtr, {
onEnter: function (args) {
console.log("buf : " + Memory.readCString(args[1]));
Memory.writeAnsiString(args[1],"HTTP/1.1 200 OK\\nServer: simple web server\\nContent-Type: text/html\\n\\nWelcome To My testServer !! ");
console.log("replaced buf : " + Memory.readCString(args[1]));
},
onLeave: function (retval) {
}
});
""")
script.on('message', on_message)
script.load()
raw_input('\n\n')
session.detach()
if __name__ == '__main__':
target_process = 'vul_http_server.exe'
main(target_process)
실행
'Reversing' 카테고리의 다른 글
WinDBG 고급 명령어들 (0) | 2017.06.07 |
---|---|
DBI - pin & pintool (4) | 2017.05.22 |
Embedded 기기 리버싱 - 펌웨어 리버싱 (1) | 2016.07.14 |
Windbg 기본 - (3) 분석시 유용한 명령어들 (2) | 2016.03.01 |
WinDBG 심볼 간편 설정 (0) | 2016.03.01 |