Motivation

pwnable.kr AEG 문제를 풀던 중 angr을 사용해보면 좋을 것 같아 슬쩍 훑어보고 코드만 가져다쓰려고 했는데 역시나 생각보다 어렵고 내용이 많아... 한번 공부해보면 좋을 것 같아 쓰게 됨


작성

https://docs.angr.io/ 내용 기반으로, 주관적으로 중요해보이는 내용 위주로 정리함

혹시 참고하시는 분들은 오역에 주의





Top Level Interfaces

Angr 에서는 project가 분석의 프레임으로 사용된다.

Project에는 기본적으로 다음과 같은 속성들이 있다.


>>> import monkeyhex # this will format numerical results in hexadecimal
>>> proj.arch
<Arch AMD64 (LE)>
>>> proj.entry
0x401670
>>> proj.filename
'/bin/true'

Angr에선 바이너리에서 가상메모리로 맵핑시키기 위해 CLE 모듈을 사용하는데 모듈의 결과값을 .loader 를 이용해 사용할 수 있다.


>>> proj.loader
<Loaded true, maps [0x400000:0x5004000]>
>>> proj.loader.shared_objects # may look a little different for you!
{'ld-linux-x86-64.so.2': <ELF Object ld-2.24.so, maps [0x2000000:0x2227167]>,
 'libc.so.6': <ELF Object libc-2.24.so, maps [0x1000000:0x13c699f]>}

>>> proj.loader.min_addr
0x400000
>>> proj.loader.max_addr
0x5004000

Angr에서 다양한 다수개 클래스들의 사용 편의를 위해 project.factory 생성자를 제공함

이를 이용해 간편히 자주 사용하는 클래스에 접근 가능하다.


Block - 입력받은 주소의 basic block을 추출

>>> block = proj.factory.block(proj.entry) # lift a block of code from the program's entry point
<Block for 0x401670, 42 bytes>

>>> block.pp()                          # pretty-print a disassembly to stdout
0x401670:       xor     ebp, ebp
0x401672:       mov     r9, rdx
0x401675:       pop     rsi
0x401676:       mov     rdx, rsp
0x401679:       and     rsp, 0xfffffffffffffff0
0x40167d:       push    rax
0x40167e:       push    rsp
0x40167f:       lea     r8, [rip + 0x2e2a]
0x401686:       lea     rcx, [rip + 0x2db3]
0x40168d:       lea     rdi, [rip - 0xd4]
0x401694:       call    qword ptr [rip + 0x205866]

>>> block.instructions                  # how many instructions are there?
0xb
>>> block.instruction_addrs             # what are the addresses of the instructions?
[0x401670, 0x401672, 0x401675, 0x401676, 0x401679, 0x40167d, 0x40167e, 0x40167f, 0x401686, 0x40168d, 0x401694]

SimState - Project 객체는 단순히 프로그램의 초기 이미지이므로 사용자가 angr을 이용 시 시뮬레이팅할 특정 조건을 정의해줘야 한다.

>>> state = proj.factory.entry_state()
<SimState @ 0x401670>

SimState는 메모리, 레지스터, 파일시스템 데이터 등 실행 시 상태에 따라 변경될 수 있는 값들을 포함한다.

사용자는 state.regs, state.mem 등 코드를 통해 사용 가능하다.

>>> state.regs.rip        # get the current instruction pointer
<BV64 0x401670>
>>> state.regs.rax
<BV64 0x1c>
>>> state.mem[proj.entry].int.resolved  # interpret the memory at the entry point as a C int
<BV32 0x8949ed31>

위 값들을 살펴보면 단순한 정수값이 아닌 bitvector임을 알 수 있는데 상세한 내용은 뒤에서 알아보자.

bitvector와 파이썬 정수형과 변환 예제는 다음과 같다.

>>> state.regs.rsi = state.solver.BVV(3, 64)
>>> state.regs.rsi
<BV64 0x3>
>>> state.mem[0x1000].long = 4
>>> state.mem[0x1000].long.resolved
<BV64 0x4>



Simulation Managers - Simulation Manager는 Angr에서 state 정보를 포함해 프로그램을 실행, 시뮬레이션하는 인터페이스다.

예를 들어 살펴보자. 먼저 사용할 simulation manager를 생성한다. 

>>> simgr = proj.factory.simulation_manager(state)
<SimulationManager with 1 active>
>>> simgr.active
[<SimState @ 0x401670>]

simulation manager는 state들의 stash 값을 가지는데, active가 기본값이다.

다음 명령을 통해 symbolic execution의 basic block을 실행할 수 있는데 실행에 따른 값 변화 시 원본 state의 값을 수정하는 것이 아닌 다수개의 state로 저장하는 것이다.

>>> simgr.step()
>>> simgr.active
[<SimState @ 0x1020300>]
>>> simgr.active[0].regs.rip                 # new and exciting!
<BV64 0x1020300>
>>> state.regs.rip                           # still the same!
<BV64 0x401670>


분석도구 - angr은 몇 개의 built-in 분석 도구를 포함하고 있으며 이를 사용자 펀의에 따라 사용 가능하다.


>>> proj.analyses.            # Press TAB here in ipython to get an autocomplete-listing of everything:
 proj.analyses.BackwardSlice        proj.analyses.CongruencyCheck      proj.analyses.reload_analyses       
 proj.analyses.BinaryOptimizer      proj.analyses.DDG                  proj.analyses.StaticHooker          
 proj.analyses.BinDiff              proj.analyses.DFG                  proj.analyses.VariableRecovery      
 proj.analyses.BoyScout             proj.analyses.Disassembly          proj.analyses.VariableRecoveryFast  
 proj.analyses.CDG                  proj.analyses.GirlScout            proj.analyses.Veritesting           
 proj.analyses.CFG                  proj.analyses.Identifier           proj.analyses.VFG                   
 proj.analyses.CFGAccurate          proj.analyses.LoopFinder           proj.analyses.VSA_DDG               
 proj.analyses.CFGFast              proj.analyses.Reassembler

상세 내용은 아래 URL의 api documentation에서 사용 가능하다.

(http://angr.io/api-doc/angr.html?highlight=cfg#module-angr.analysis)





바이너리 로딩 - CLE와 angr Projects


Loader 사용

>>> import angr, monkeyhex
>>> proj = angr.Project('/bin/true')
>>> proj.loader
<Loaded true, maps [0x400000:0x5008000]>


Loader 객체

cle.loader는 단일 메모리 공간에 맵핑된 바이너리 객체의 집합이다. 각 바이너리 객체는 loader의 백엔드를 이용해 로딩되고 파일 타입을 이용해 다룰 수 있다(ex. cle.ELF)

>>> proj.loader.all_objects
[<ELF Object fauxware, maps [0x400000:0x60105f]>,
 <ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>,
 <ELF Object ld-linux-x86-64.so.2, maps [0x2000000:0x22241c7]>,
 <ELFTLSObject Object cle##tls, maps [0x3000000:0x300d010]>,
 <KernelObject Object cle##kernel, maps [0x4000000:0x4008000]>,
 <ExternObject Object cle##externs, maps [0x5000000:0x5008000]>

# This is the "main" object, the one that you directly specified when loading the project
>>> proj.loader.main_object
<ELF Object true, maps [0x400000:0x60105f]>

# This is a dictionary mapping from shared object name to object
>>> proj.loader.shared_objects
{ 'libc.so.6': <ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>
  'ld-linux-x86-64.so.2': <ELF Object ld-linux-x86-64.so.2, maps [0x2000000:0x22241c7]>}

# Here's all the objects that were loaded from ELF files
# If this were a windows program we'd use all_pe_objects!
>>> proj.loader.all_elf_objects
[<ELF Object true, maps [0x400000:0x60105f]>,
 <ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>,
 <ELF Object ld-linux-x86-64.so.2, maps [0x2000000:0x22241c7]>]

# Here's the "externs object", which we use to provide addresses for unresolved imports and angr internals
>>> proj.loader.extern_object
<ExternObject Object cle##externs, maps [0x5000000:0x5008000]>

# This object is used to provide addresses for emulated syscalls
>>> proj.loader.kernel_object
<KernelObject Object cle##kernel, maps [0x4000000:0x4008000]>

# Finally, you can to get a reference to an object given an address in it
>>> proj.loader.find_object_containing(0x400000)
<ELF Object true, maps [0x400000:0x60105f]>

다음과 같이 객체를 이용해 바로 데이터 획득도 가능하다

>>> obj = proj.loader.main_object

# The entry point of the object
>>> obj.entry
0x400580

>>> obj.min_addr, obj.max_addr
(0x400000, 0x60105f)

# Retrieve this ELF's segments and sections
>>> obj.segments
<Regions: [<ELFSegment offset=0x0, flags=0x5, filesize=0xa74, vaddr=0x400000, memsize=0xa74>,
           <ELFSegment offset=0xe28, flags=0x6, filesize=0x228, vaddr=0x600e28, memsize=0x238>]>
>>> obj.sections
<Regions: [<Unnamed | offset 0x0, vaddr 0x0, size 0x0>,
           <.interp | offset 0x238, vaddr 0x400238, size 0x1c>,
           <.note.ABI-tag | offset 0x254, vaddr 0x400254, size 0x20>,
            ...etc

# You can get an individual segment or section by an address it contains:
>>> obj.find_segment_containing(obj.entry)
<ELFSegment offset=0x0, flags=0x5, filesize=0xa74, vaddr=0x400000, memsize=0xa74>
>>> obj.find_section_containing(obj.entry)
<.text | offset 0x580, vaddr 0x400580, size 0x338>

# Get the address of the PLT stub for a symbol
>>> addr = obj.plt['abort']
>>> addr
0x400540
>>> obj.reverse_plt[addr]
'abort'

# Show the prelinked base of the object and the location it was actually mapped into memory by CLE
>>> obj.linked_base
0x400000
>>> obj.mapped_base
0x400000


Symbols and Relocations

CLE의 loader.find_symbol 을 이용해 심볼 정보를 확인할 수 있으며 심볼 이름과 주소 모두 이용 가능하다

>>> malloc = proj.loader.find_symbol('malloc')
>>> malloc
<Symbol "malloc" in libc.so.6 at 0x1054400>


바이너리 로딩 옵션

바이너리 옵션 - 특정 바이너리 객체를 로딩할때 옵션을 정의하고 싶으면 사용 가능하다

* backend - 사용할 백엔드 지정(ex. elf, pe, mach-o...)

* custom_base_addr - 사용할 베이스 어드레스 주소

* custom_entry_point - 사용할 엔트리 포인트

* custom_arch - 사용할 아키텍쳐














'Fuzzing & Binary analysis' 카테고리의 다른 글

Angr 설치 및 실행  (0) 2017.12.27


Python virtualenvwrapper 설치, 환경설정

# pip install virtualenvwrapper

# vi ~/.bashrc


export WORKON_HOME=$HOME/.virtualenvs

source /usr/local/bin/virtualenvwrapper.sh


# source ~/.bashrc



Pypy 설치

http://pypy.org/download.html 에서 환경에 맞춰 다운로드, 압축해제



Angr-dev 설치(출처 : http://kshmk.tistory.com/92)

# mkvirtualenv -p /opt/pypy/bin/pypy angr

# deactivate

# git clone https://github.com/angr/angr-dev

# cd angr-dev

# setsid sh -c 'tty; ps -jp "$$"; ./setup.sh -i -p angr' < /dev/null  // github id, pw 물어봐서 이 옵션 사용


# workon angr



Example code  실행

Angr 프로젝트에 예제로 들어 있는 바이너리를 분석해보자


실행 코드

import angr


project = angr.Project("angr-dev/angr-doc/examples/defcamp_r100/r100", auto_load_libs=False)


@project.hook(0x400844)

def print_flag(state):

    print "FLAG SHOULD BE:", state.posix.dump_fd(0)

    project.terminate_execution()


project.execute() 


실행 결과

(angr) root@ubuntu:/home/u64/angr# python test.py 

WARNING | 2017-12-27 04:46:07,890 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups.

FLAG SHOULD BE: Code_Talkers


'Fuzzing & Binary analysis' 카테고리의 다른 글

[작성중] Angr docs를 공부해보자  (0) 2018.01.04

+ Recent posts