주요 내용으로 건너뛰기

B2R2 바이너리 분석 도구 및 논문 리뷰

1. 개요

KAIST 정보보호대학원의 차상길 교수님 및  SoftSec Lab(소프트웨어 보안 연구실), 그리고 KAIST 사이버보안연구센터(센터장 김용대 교수님)가 과기부 연구과제(2016년~) 수행의 성과로 개발한 '차세대 바이너리 분석 플랫폼'인 B2-R2가 공개되었다. 기술 설명회가 2018년 12월 4일에 서울에서 열렸으며, 2019년 2월 오픈소스로(MIT license) 공개되었다. 먼저는 NuGet을 통해서만 접근 가능한 상태였으나, 국제 보안 분야의 세계 4대 학회 중 하나인 NDSS Symposium 2019 의 부대행사인 BAR(Binary Analysis Research)에서 관련 논문 <B2R2: Building an Efficient Front-End for Binary Analysis>를 발표함으로써 소스코드도 Github을 통해 함께 공개되었다. 참고로 해당 논문은 BAR 2019 Best Paper Award로 선정되었다.

바이너리 역분석은 상당히 어려운 문제이다. 여기에서 '어렵다'는 것은 단순히 추상적인 느낌적 느낌만을 말하는 것이 아니라 컴퓨터수학적으로 문제가 해결 가능한지 여부를 따지는 NP-Complete / NP-Hard 등을 의미하는데, 어쨌거나 100% 완벽하게 바이너리 코드를 해독해 내는 것이 굉장히 Challenging한 Problem이라는 뜻으로 이해하면 되겠다.

그럼에도 불구하고 이러한 문제에 도전하여 바이너리 분석을 나름대로 성공적으로(?) 수행하고 있는 다수의 연구들이 있고, IDA Pro라는 매우 훌륭한 상용 도구가 어마어마한 가격에도 불구하고 업계에서 필수품으로 사들일 만큼 엄청난 인기를 끌고 있다는 사실은 이 문제를 그나마 '효율적으로' 해결할 수 있는 적당한 방법이 존재한다는 것이다. 그렇다면 결국 어느 정도의 정확성과 얼마만큼의 신속성을 가지느냐가 관건이 될 것이다.

이러한 기술의 동향은 대체적으로 바이너리에 대한 중간언어 변환, 프로그램 제어 흐름 분석, 기호 실행(Symbolic Execution) 등을 종합적으로 다루고 있으며, 대표적으로 CMU의 BAP, UC Santa Barbara의 Angr 등등이 있고 실제로 그러한 기반 연구 덕분에 2016년 CGC(Cyber Grand Challenge)에서 ForAllSecure(CMU 출신 창업자들)의 Mayhem과 Shellphish(UCSB 출신)의 Mechanical Phish이 상위권에 랭크될 수 있었던 것 같다. 그렇다면 이러한 기존 프로젝트와 비교했을 때 B2R2가 개선한 특장점은 무엇일까? 기사에 따르면 다음과 같이 설명하고 있다.

B2R2는 이들과 비교해 함수형 언어 F#을 사용해 분석 용이성을 극대화하였고, .NET Core를 바탕으로 윈도, 리눅스, 맥, 안드로이드, iOS 등 모든 환경에서 사용 가능하도록 하였다. 분석 속도는 기존 플랫폼과 비교해 2배~100배 이르고 32개 프로그래밍 언어와 연동할 수 있다.

NDSS BAR에서 발표된 논문 <B2R2: Building an Efficient Front-End for Binary Analysis>에 실려있는 현존 오픈소스 바이너리 분석 도구와의 비교분석 자료는 아래와 같다.

실제로 해당 연구팀의 3년간의 노력으로 개발한 B2-R2 덕분에 지난 2018년 11월 30일에 열린 CGC의 한국판 대회인 ‘2018년 정보보호 R&D 데이터 챌린지 AI 기반 취약점 자동탐지’ 분야에서 압도적인 점수차로 우승을 차지하기도 했다. 

이제 본격적으로 해당 도구를 설치하고 사용해보자.

2. 설치

1) .NET 설치

B2R2는 F# 언어로 개발되었으며 이는 마이크로소프트의 실험적 프로젝트에서 출발한 함수형 언어이다. 언어의 기본적인 구조는 OCaml과 비슷하지만 이것을 닷넷 프레임워크 안으로 집어넣었다고 생각하면 된다. 조금 더 부연설명을 하자면 Java가 크로스 플랫폼을 지원할 수 있는 비결로써 JVM(Java Virtual Machine)을 사용하는 것과 유사하게 계층을 하나 덧씌워서 거기에 알맞은 중간 언어로 작성된 실행 파일을 만들면 그것을 하드웨어가 이해할 수 있는 네이티브 코드로 변환할 수 있도록 하는 것이다. 이러한 .NET Framework를 사용하는 방식을 먼저 C#이라는 객체지향 프로그래밍 언어를 사용했는데, 동일한 방식으로 함수형 프로그래밍 언어인 F#에도 적용한 것이다.

그러므로 F# 코드를 구동하기 위해서는 .NET을 먼저 설치해야 한다. .NET 덕분에 크로스 플랫폼 지원이 가능하므로 Windows 뿐만 아니라 macOS와 리눅스뿐만 아니라 iOS와 안드로이드 등 모바일에서도 작동이 된다. 다만 엄밀히 따지면 Windows전용은 .NET Framework라고 부르고, macOS 및 리눅스 등 기타 플랫폼을 지원하기 위한 오픈소스 범용 개발 플랫폼으로 .NET Core가 별도 프로젝트로 존재한다. 따라서 자신이 B2R2를 설치하고자 하는 환경에 알맞게 선택하기 바란다. Java에서 코드를 빌드하기 위해서는 JDK(Java Development Kit)를, 그저 실행만 하기 위해서는 JRE(Java Runtime Environment)만을 깔면 되듯이 .NET도 Build를 하기 위해서는 Dev Pack(또는 SDK)를 설치해야 하고, 그저 Run만을 하기 위해서는 Runtime만 설치하면 된다. (개발자팩을 설치하면 자동으로 Runtime도 설치된다) 

B2R2를 구동하기 위해서는 .NET Core SDK 2.0 혹은 그 이상의 버전을 설치해야 한다. 참고로 이 글을 작성하는 2019년 2월 기준으로 .NET Core의 최신 버전은 2.2이다. 아래 링크에서 각 운영체제별 설치 파일을 다운로드할 수 있다.

.NET 이 잘 설치되었는지 확인해보자. 필자의 경우 각각 Windows 10 Enterprise, macOS Mojave(10.14.2), Ubuntu Linux 18.04 환경에서 .NET Core SDK 2.2 을 설치를 테스트하였으며, Power Shell 이나 CMD, 혹은 Terminal 환경에서 아래와 같이 설치 여부를 볼 수 있다.


2) B2R2 패키지 설치

B2R2 패키지를 설치하고 테스트해보자. 크로스플랫폼 환경에서 모두 작동될 것으로 예상되지만 참고적으로 본 포스팅에서 확인한 환경은 Ubuntu Linux 18.04 64bit, .NET Core 버전은 2.2.104이다.

먼저 진행을 위해 디렉토리 하나를 새로 만든다. 간단히 b2r2_review라고 지어보았다.

이제 dotnet을 통해 F# 언어로 콘솔 애플리케이션 개발 작업 환경을 구축하자.

다음으로 해당 프로젝트에 B2R2.FrontEnd 패키지를 add한다. nuget 의 정보를 보면 2월 초에 0.1.0-alpha 버전이 최초 공개되었다가, 2월 말부터 0.1.0 버전으로 제공되는 중이다. 아래 명령어로 0.1.0 버전을 add할 수 있다.

dotnet으로 F# 프로젝트를 만들면 자동으로 Program.fs 라는 F# 소스코드 파일이 생성되는데, 이는 데니스 리치의 The C programming Language 책에서 C 프로그래밍 언어의 예제를 "Hello, World\n"으로 설명했듯이 마찬가지의 F#으로 된 Hello World 프로그램이다.

vi 편집기 등을 통해 Program.fs 의 Hello world 관련 내용을 전부 삭제하고, 아래와 같은 소스코드를 붙여넣어 보자.

여기에서 isa를 amd64 아키텍처로 설정하고, [| 0x65uy; 0xffuy; 0x15uy; 0x10uy; 0x00uy; 0x00uy; 0x00uy |] 로 표현된 부분은 이진 기계어 코드를 16진수로 표현한 65 ff 15 10 00 00 00 이다. 이는 libc 등에서 시스템 콜을 할 때 사용되는 명령으로 어셈블리 언어로 표현하면 call  QWORD PTR gs:[rip+0x10] 이다. 그리고 BinHandler를 선언한 후 해당 isa와 바이트코드를 처리할 수 있도록 init을 해주고, ParseInstr 함수를 통해 해당 바이트코드를 명령어 구조에 따라 파싱하여 ins 에 저장한다. ins는 Instruction 클래스의 객체로, 담겨있는 operand 관련 정보들을 Translate 하게 되면 파싱된 내용을 중간 언어(IR, Intermediate Representation)로 리프팅하게 된다. (만약 분석할 기계어 코드를 bytes 방식이 아니라 파일로부터 읽어오고 싶으면 BinHandler.Init에서 bytes(byte [])가 아닌, fileName(string) 을 사용하면 된다. 자세한 것은 API Doc 참고) 

아래의 명령어로 해당 F# 코드를 수행할 수 있다.

아래와 같은 결과물이 나올 것인데, 이것이 바로 리프트 된 IR 구문이다. 

논문에서 설명하고 있는 B2R2의 IR(일명 LowUIR)의 문법 구조는 아래와 같다.


바이너리 분석을 전반부(Front-end)와 후반부(Back-end)로 나눈다면, 주어진 바이너리를 역 어셈블 하여 중간 언어로 변환하는 disassembler와 lifter 까지를 Front-end라고 하고, 리프팅된 IR을 이용하여 CFG Recovery나 구조 분석 등을 하는 것이 Back-end라고 할 수 있다. 그동안 Back-end 단계를 효과적으로 수행하기 위한 알고리즘들은 많이 연구가 되어 왔으나, Front-end 단계를 효율적으로 진행하는 연구는 드물었다. Front-end 단계의 성능이나 효율성이 Back-end를 포함한 분석 전체 과정에 미치는 영향이 지대함을 고려할 때, Front-end를 효과적으로 설계함으로써 얻는 이득이 얼마나 큰지를 보여주는 것이 본 논문의 핵심이다. 

현존하는 Radare2, BAP, BINSEC, BitBlaze, IDA Pro 등등도 역시 모두 각자의 IR인 ESIL, BIL, DBA, Vine, Microcode 으로 변환한 후에 리프팅하는 단계를 거친다. 이 과정을 B2R2와 성능 비교 분석한 표가 아래와 같다. 참고로 각 툴마다 동작하는 방식에서 차이가 있을 수 있기 때문에 독립 변수만을 체크하고 나머지 변인을 통제하기 위해 bap, binsec 등 다른 도구에 약간의 patch 작업을 수행하여 측정의 정확성을 높였다. (참고 : BAR19-Artifact)

3. 포함된 도구

1) FileViewer

해당 바이너리 파일의 정보를 표출해준다. DEP(NX)나 PIE가 걸려있는지 여부를 확인해준다. FileViewer는 B2R2 디렉토리의 src/Utiities/FileViewer 에 위치하며, dotnet run -- [filepath] 으로 실행한다.

2) BinExplorer

해당 바이너리 파일의 Control Flow Graph를 표출해준다. 웹브라우져를 열고 8282번 포트를 사용하면 확인할 수 있다.

http://localhost:8282/

또한 interactive shell 상태에서 다양한 command 를 제공한다. (일부는 아직 개발 중)

ROP를 위한 gadget list 를 확인할 수도 있다.

다만 화면이 너무 빨리 지나가버리는데, 위의 내용을 확인하기가 힘든 경우 해당 내용을 파일로 저장한 log를 확인하면 된다. BinExplorer 디렉토리에 생성된 B2R2.log 파일을 확인할 것.



개인 평

이 프로젝트가 함수형 언어인 F#으로 작성되었다는 사실은 장점이면서 동시에 단점일 수도 있다. 함수형 프로그래밍 패러다임의 태동만을 따져봤을 때 객체지향 언어와 비교해서 크게 차이 나지 않음에도 불구하고 개발업계에서는 아직까지 함수형 프로그래밍을 많이 사용하지는 않는다. 게다가 연구 계열 쪽에서도 그나마 많이 사용되는 함수형 언어는 OCaml이나 Haskell이 조금 더 대세를 이루는 것 같다. 그런 의미에서 왜 굳이 마이크로소프트에서 실험적으로 내세운 F#일까? 에 대해서 의아할 수 있으나, 결과적으로 봤을 때 F#을 사용했기 때문에 우수한 상호운용성이나 이식성, 그리고 빠른 런타임 속도를 얻을 수 있었기 때문에 좋은 선택이었다고 볼 수밖에 없다. 따라서 본 B2-R2 프로젝트를 User차원에서만 사용할 것이라면 상관없겠지만(다행히 C 또는 C++ 및 파이썬 등을 사용할 수 있는 API를 제공하고 있다.), 그 이상을 목적으로 한다면 필요한 수준에서 함수형 패러다임과 F# 코드의 문법(Ocaml과 유사) 정도를 숙지해야 할 것이다. 본인이 직접 Contribution까지 하려면 F#을 능숙하게 다룰 수 있어야 할 것이다. 이러한 이유로 기업 등에서 이 도구를 잘 활용하려면 최소한 담당하는 직원들은 F#을 무조건 배워야 할 듯하다.

관련 기사에 따르면 현재는 바이너리 분석에 대한 원천 기술을 개발한 것이지만 아쉽게도 후속 과제로 이어지지는 못했다고 한다. 현재의 원천 기술은 '바이너리를 중간언어(IR)로 변환'하는데까지만 초점을 맞췄다. 한편 IDA Pro는 바이너리를 사람이 일반적으로 이해할 수 있는 C언어 등의 High-level 언어로 변환하는 Decompiler 를 제공하고 있는데, 라이선스 비용이 인당 수 천 만원을 호가한다. 이러한 기술까지 구현할 수 있는 후속과제까지 이어지기 위해서는 정부의 투자와 다양한 기업과의 협력을 통해 Back-end 단계의 기술에도 많은 발전이 지속 이루어지면 좋을 것 같다.

‘파이썬 Wrapper 가 있으면 좋겠다 ㅎㅎ’ .. 고 생각했는데 개발자분의 조언에 따르면 IronPython 을 사용하면 된다고 한다. Sample 디렉토리에 파이썬 예제 참고.. (해보고 업데이트 하겠음..)


Software Security Engineer

CPUU 님의 창작활동을 응원하고 싶으세요?

댓글

SNS 계정으로 간편하게 로그인하고 댓글을 남겨주세요.