{{#include ../../../banners/hacktricks-training.md}} 이 치트 시트의 일부는 [angr documentation](https://docs.angr.io/_/downloads/en/stable/pdf/)을 기반으로 합니다. # 설치 ```bash sudo apt-get install python3-dev libffi-dev build-essential python3 -m pip install --user virtualenv python3 -m venv ang source ang/bin/activate pip install angr ``` # 기본 작업 ```python import angr import monkeyhex # this will format numerical results in hexadecimal #Load binary proj = angr.Project('/bin/true') #BASIC BINARY DATA proj.arch #Get arch "" proj.arch.name #'AMD64' proj.arch.memory_endness #'Iend_LE' proj.entry #Get entrypoint "0x4023c0" proj.filename #Get filename "/bin/true" #There are specific options to load binaries #Usually you won't need to use them but you could angr.Project('examples/fauxware/fauxware', main_opts={'backend': 'blob', 'arch': 'i386'}, lib_opts={'libc.so.6': {'backend': 'elf'}}) ``` # 로드된 및 주요 객체 정보 ## 로드된 데이터 ```python #LOADED DATA proj.loader # proj.loader.min_addr #0x400000 proj.loader.max_addr #0x5004000 proj.loader.all_objects #All loaded proj.loader.shared_objects #Loaded binaries """ OrderedDict([('true', ), ('libc.so.6', ), ('ld-linux-x86-64.so.2', ), ('extern-address space', ), ('cle##tls', )]) """ proj.loader.all_elf_objects #Get all ELF objects loaded (Linux) proj.loader.all_pe_objects #Get all binaries loaded (Windows) proj.loader.find_object_containing(0x400000)#Get object loaded in an address "" ``` ## 주요 객체 ```python #Main Object (main binary loaded) obj = proj.loader.main_object # obj.execstack #"False" Check for executable stack obj.pic #"True" Check PIC obj.imports #Get imports obj.segments #, , ]> obj.find_segment_containing(obj.entry) #Get segment by address obj.sections #, <.interp | offset 0x238, vaddr 0x400238, size 0x1c>, <.note.ABI-tag | offset 0x254, vaddr 0x400254, size 0x20>, <.note.gnu.build-id ... obj.find_section_containing(obj.entry) #Get section by address obj.plt['strcmp'] #Get plt address of a funcion (0x400550) obj.reverse_plt[0x400550] #Get function from plt address ('strcmp') ``` ## 기호 및 재배치 ```python strcmp = proj.loader.find_symbol('strcmp') # strcmp.name #'strcmp' strcmp.owne # strcmp.rebased_addr #0x1089cd0 strcmp.linked_addr #0x89cd0 strcmp.relative_addr #0x89cd0 strcmp.is_export #True, as 'strcmp' is a function exported by libc #Get strcmp from the main object main_strcmp = proj.loader.main_object.get_symbol('strcmp') main_strcmp.is_export #False main_strcmp.is_import #True main_strcmp.resolvedby # ``` ## 블록 ```python #Blocks block = proj.factory.block(proj.entry) #Get the block of the entrypoint fo the binary block.pp() #Print disassembly of the block block.instructions #"0xb" Get number of instructions block.instruction_addrs #Get instructions addresses "[0x401670, 0x401672, 0x401675, 0x401676, 0x401679, 0x40167d, 0x40167e, 0x40167f, 0x401686, 0x40168d, 0x401694]" ``` # 동적 분석 ## 시뮬레이션 관리자, 상태 ```python #Live States #This is useful to modify content in a live analysis state = proj.factory.entry_state() state.regs.rip #Get the RIP state.mem[proj.entry].int.resolved #Resolve as a C int (BV) state.mem[proj.entry].int.concreteved #Resolve as python int state.regs.rsi = state.solver.BVV(3, 64) #Modify RIP state.mem[0x1000].long = 4 #Modify mem #Other States project.factory.entry_state() project.factory.blank_state() #Most of its data left uninitialized project.factory.full_init_statetate() #Execute through any initializers that need to be run before the main binary's entry point project.factory.call_state() #Ready to execute a given function. #Simulation manager #The simulation manager stores all the states across the execution of the binary simgr = proj.factory.simulation_manager(state) #Start simgr.step() #Execute one step simgr.active[0].regs.rip #Get RIP from the last state ``` ## 함수 호출 - `entry_state`와 `full_init_state`에 `args`를 통해 인수 목록을, `env`를 통해 환경 변수의 사전을 전달할 수 있습니다. 이러한 구조의 값은 문자열 또는 비트벡터일 수 있으며, 시뮬레이션된 실행의 인수와 환경으로 상태에 직렬화됩니다. 기본 `args`는 빈 목록이므로, 분석 중인 프로그램이 최소한 `argv[0]`을 찾기를 기대한다면 항상 제공해야 합니다! - `argc`를 심볼릭으로 설정하고 싶다면, `entry_state`와 `full_init_state` 생성자에 심볼릭 비트벡터를 `argc`로 전달할 수 있습니다. 그러나 주의해야 합니다: 이렇게 할 경우, `argc`에 대한 값이 `args`에 전달한 인수의 수보다 클 수 없다는 제약 조건을 결과 상태에 추가해야 합니다. - 호출 상태를 사용하려면 `.call_state(addr, arg1, arg2, ...)`로 호출해야 하며, 여기서 `addr`은 호출하려는 함수의 주소이고 `argN`은 해당 함수에 대한 N번째 인수로, 파이썬 정수, 문자열, 배열 또는 비트벡터일 수 있습니다. 메모리를 할당하고 실제로 객체에 대한 포인터를 전달하려면, 이를 PointerWrapper로 감싸야 합니다. 즉, `angr.PointerWrapper("point to me!")`와 같이 사용합니다. 이 API의 결과는 다소 예측할 수 없지만, 우리는 이를 개선하고 있습니다. ## 비트벡터 ```python #BitVectors state = proj.factory.entry_state() bv = state.solver.BVV(0x1234, 32) #Create BV of 32bits with the value "0x1234" state.solver.eval(bv) #Convert BV to python int bv.zero_extend(30) #Will add 30 zeros on the left of the bitvector bv.sign_extend(30) #Will add 30 zeros or ones on the left of the BV extending the sign ``` ## 심볼릭 비트벡터 및 제약조건 ```python x = state.solver.BVS("x", 64) #Symbolic variable BV of length 64 y = state.solver.BVS("y", 64) #Symbolic oprations tree = (x + 1) / (y + 2) tree # tree.op #'__floordiv__' Access last operation tree.args #(, ) tree.args[0].op #'__add__' Access of dirst arg tree.args[0].args #(, ) tree.args[0].args[1].op #'BVV' tree.args[0].args[1].args #(1, 64) #Symbolic constraints solver state = proj.factory.entry_state() #Get a fresh state without constraints input = state.solver.BVS('input', 64) operation = (((input + 4) * 3) >> 1) + input output = 200 state.solver.add(operation == output) state.solver.eval(input) #0x3333333333333381 state.solver.add(input < 2**32) state.satisfiable() #False #Solver solutions solver.eval(expression) #one possible solution solver.eval_one(expression) #solution to the given expression, or throw an error if more than one solution is possible. solver.eval_upto(expression, n) #n solutions to the given expression, returning fewer than n if fewer than n are possible. solver.eval_atleast(expression, n) #n solutions to the given expression, throwing an error if fewer than n are possible. solver.eval_exact(expression, n) #n solutions to the given expression, throwing an error if fewer or more than are possible. solver.min(expression) #minimum possible solution to the given expression. solver.max(expression) #maximum possible solution to the given expression. ``` ## 후킹 ```python >>> stub_func = angr.SIM_PROCEDURES['stubs']['ReturnUnconstrained'] # this is a CLASS >>> proj.hook(0x10000, stub_func()) # hook with an instance of the class >>> proj.is_hooked(0x10000) # these functions should be pretty self-explanitory True >>> proj.hooked_by(0x10000) >>> proj.unhook(0x10000) >>> @proj.hook(0x20000, length=5) ... def my_hook(state): ... state.regs.rax = 1 >>> proj.is_hooked(0x20000) True ``` 또한, `proj.hook_symbol(name, hook)`을 사용하여 기호의 이름을 첫 번째 인수로 제공함으로써 기호가 위치한 주소를 후킹할 수 있습니다. # 예시 {{#include ../../../banners/hacktricks-training.md}}