import angrimport monkeyhex # this will format numerical results in hexadecimal#Load binaryproj = angr.Project('/bin/true')#BASIC BINARY DATAproj.arch #Get arch "<Arch AMD64 (LE)>"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 couldangr.Project('examples/fauxware/fauxware', main_opts={'backend': 'blob', 'arch': 'i386'}, lib_opts={'libc.so.6': {'backend': 'elf'}})
strcmp = proj.loader.find_symbol('strcmp')#<Symbol "strcmp" in libc.so.6 at 0x1089cd0>strcmp.name #'strcmp'strcmp.owne #<ELF Object libc-2.23.so, maps [0x1000000:0x13c999f]>strcmp.rebased_addr #0x1089cd0strcmp.linked_addr #0x89cd0strcmp.relative_addr #0x89cd0strcmp.is_export #True, as 'strcmp' is a function exported by libc#Get strcmp from the main objectmain_strcmp = proj.loader.main_object.get_symbol('strcmp')main_strcmp.is_export #Falsemain_strcmp.is_import #Truemain_strcmp.resolvedby #<Symbol "strcmp" in libc.so.6 at 0x1089cd0>
Блоки
#Blocksblock = proj.factory.block(proj.entry)#Get the block of the entrypoint fo the binaryblock.pp()#Print disassembly of the blockblock.instructions #"0xb" Get number of instructionsblock.instruction_addrs #Get instructions addresses "[0x401670, 0x401672, 0x401675, 0x401676, 0x401679, 0x40167d, 0x40167e, 0x40167f, 0x401686, 0x40168d, 0x401694]"
Динамічний аналіз
Менеджер симуляції, стани
#Live States#This is useful to modify content in a live analysisstate = proj.factory.entry_state()state.regs.rip #Get the RIPstate.mem[proj.entry].int.resolved #Resolve as a C int (BV)state.mem[proj.entry].int.concreteved #Resolve as python intstate.regs.rsi = state.solver.BVV(3, 64)#Modify RIPstate.mem[0x1000].long =4#Modify mem#Other Statesproject.factory.entry_state()project.factory.blank_state()#Most of its data left uninitializedproject.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 binarysimgr = proj.factory.simulation_manager(state)#Startsimgr.step()#Execute one stepsimgr.active[0].regs.rip #Get RIP from the last state
Виклик функцій
Ви можете передати список аргументів через args та словник змінних середовища через env у entry_state та full_init_state. Значення в цих структурах можуть бути рядками або бітовими векторами, і будуть серіалізовані в стан як аргументи та середовище для симульованого виконання. За замовчуванням args є порожнім списком, тому якщо програма, яку ви аналізуєте, очікує знайти принаймні argv[0], ви завжди повинні це надати!
Якщо ви хочете, щоб argc був символічним, ви можете передати символічний бітовий вектор як argc конструкторам entry_state та full_init_state. Будьте обережні, проте: якщо ви це зробите, ви також повинні додати обмеження до отриманого стану, що ваше значення для argc не може бути більшим за кількість аргументів, які ви передали в args.
Щоб використовувати стан виклику, ви повинні викликати його з .call_state(addr, arg1, arg2, ...), де addr - це адреса функції, яку ви хочете викликати, а argN - це N-й аргумент для цієї функції, або як ціле число Python, рядок або масив, або бітовий вектор. Якщо ви хочете виділити пам'ять і насправді передати вказівник на об'єкт, ви повинні обернути його в PointerWrapper, тобто angr.PointerWrapper("point to me!"). Результати цього API можуть бути трохи непередбачуваними, але ми над цим працюємо.
Бітові вектори
#BitVectorsstate = 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 intbv.zero_extend(30)#Will add 30 zeros on the left of the bitvectorbv.sign_extend(30)#Will add 30 zeros or ones on the left of the BV extending the sign
Символічні BitVectors та обмеження
x = state.solver.BVS("x", 64)#Symbolic variable BV of length 64y = state.solver.BVS("y", 64)#Symbolic oprationstree = (x +1) / (y +2)tree #<BV64 (x_9_64 + 0x1) / (y_10_64 + 0x2)>tree.op #'__floordiv__' Access last operationtree.args #(<BV64 x_9_64 + 0x1>, <BV64 y_10_64 + 0x2>)tree.args[0].op #'__add__' Access of dirst argtree.args[0].args #(<BV64 x_9_64>, <BV64 0x1>)tree.args[0].args[1].op #'BVV'tree.args[0].args[1].args #(1, 64)#Symbolic constraints solverstate = proj.factory.entry_state()#Get a fresh state without constraintsinput= state.solver.BVS('input', 64)operation = (((input+4) *3) >>1) +inputoutput =200state.solver.add(operation == output)state.solver.eval(input)#0x3333333333333381state.solver.add(input<2**32)state.satisfiable()#False#Solver solutionssolver.eval(expression)#one possible solutionsolver.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.
Хукінг
>>> 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-explanitoryTrue>>> proj.hooked_by(0x10000)<ReturnUnconstrained>>>> proj.unhook(0x10000)>>>@proj.hook(0x20000, length=5)... defmy_hook(state):... state.regs.rax =1>>> proj.is_hooked(0x20000)True
Крім того, ви можете використовувати proj.hook_symbol(name, hook), надаючи ім'я символу як перший аргумент, щоб підключити адресу, де знаходиться символ.