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>
Blöcke
#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]"
Dynamische Analyse
Simulationsmanager, Zustände
#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 pointproject.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
Funktionen aufrufen
Sie können eine Liste von Argumenten über args und ein Wörterbuch von Umgebungsvariablen über env in entry_state und full_init_state übergeben. Die Werte in diesen Strukturen können Strings oder Bitvektoren sein und werden als Argumente und Umgebung in den Zustand der simulierten Ausführung serialisiert. Die Standardwerte für args sind eine leere Liste, also wenn das Programm, das Sie analysieren, erwartet, mindestens ein argv[0] zu finden, sollten Sie das immer bereitstellen!
Wenn Sie möchten, dass argc symbolisch ist, können Sie einen symbolischen Bitvektor als argc an die Konstruktoren von entry_state und full_init_state übergeben. Seien Sie jedoch vorsichtig: Wenn Sie dies tun, sollten Sie auch eine Einschränkung für den resultierenden Zustand hinzufügen, dass Ihr Wert für argc nicht größer sein kann als die Anzahl der Argumente, die Sie in args übergeben haben.
Um den Aufrufzustand zu verwenden, sollten Sie ihn mit .call_state(addr, arg1, arg2, ...) aufrufen, wobei addr die Adresse der Funktion ist, die Sie aufrufen möchten, und argN das N-te Argument für diese Funktion ist, entweder als Python-Integer, String oder Array oder als Bitvektor. Wenn Sie Speicher zuweisen und tatsächlich einen Zeiger auf ein Objekt übergeben möchten, sollten Sie es in einen PointerWrapper einwickeln, d.h. angr.PointerWrapper("point to me!"). Die Ergebnisse dieser API können etwas unvorhersehbar sein, aber wir arbeiten daran.
Bitvektoren
#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
Symbolische Bitvektoren & Einschränkungen
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.
Hooking
>>> 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
Außerdem können Sie proj.hook_symbol(name, hook) verwenden, wobei der Name eines Symbols als erstes Argument angegeben wird, um die Adresse zu hooken, an der sich das Symbol befindet.