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>
Blokke
#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]"
Dinamiese Analise
Simulasie Bestuurder, Toestande
#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
Funksies aanroep
Jy kan 'n lys van argumente deur args en 'n woordeboek van omgewingsveranderlikes deur env in entry_state en full_init_state deurgee. Die waardes in hierdie strukture kan strings of bitvectors wees, en sal in die toestand geserialiseer word as die argumente en omgewing vir die gesimuleerde uitvoering. Die standaard args is 'n leë lys, so as die program wat jy analiseer verwag om ten minste 'n argv[0] te vind, moet jy dit altyd verskaf!
As jy wil hê dat argc simbolies moet wees, kan jy 'n simboliese bitvector as argc aan die entry_state en full_init_state konstruktors deurgee. Wees versigtig, though: as jy dit doen, moet jy ook 'n beperking by die resultaat toestand voeg dat jou waarde vir argc nie groter kan wees as die aantal args wat jy in args deurgegee het nie.
Om die aanroep toestand te gebruik, moet jy dit aanroep met .call_state(addr, arg1, arg2, ...), waar addr die adres van die funksie is wat jy wil aanroep en argN die Nde argument vir daardie funksie is, hetsy as 'n python heelgetal, string, of array, of 'n bitvector. As jy geheue wil toewys en werklik 'n wysiger na 'n objek wil deurgee, moet jy dit in 'n PointerWrapper verpak, d.w.z. angr.PointerWrapper("point to me!"). Die resultate van hierdie API kan 'n bietjie onvoorspelbaar wees, maar ons werk daaraan.
BitVectors
#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
Simboliese BitVektore & Beperkings
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.
Haken
>>> 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
Verder kan jy proj.hook_symbol(name, hook) gebruik, wat die naam van 'n simbool as die eerste argument verskaf, om die adres waar die simbool woon te hook.