Source code for binaryninja.scriptingprovider

# Copyright (c) 2015-2019 Vector 35 Inc## Permission is hereby granted, free of charge, to any person obtaining a copy# of this software and associated documentation files (the "Software"), to# deal in the Software without restriction, including without limitation the# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or# sell copies of the Software, and to permit persons to whom the Software is# furnished to do so, subject to the following conditions:## The above copyright notice and this permission notice shall be included in# all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS# IN THE SOFTWARE.importcodeimporttracebackimportctypesimportthreadingimportabcimportsys# Binary Ninja componentsimportbinaryninjafrombinaryninjaimport_binaryninjacoreascorefrombinaryninja.enumsimportScriptingProviderExecuteResult,ScriptingProviderInputReadyStatefrombinaryninjaimportlog# 2-3 compatibilityfrombinaryninjaimportrangefrombinaryninjaimportwith_metaclassclass_ThreadActionContext(object):_actions=[]def__init__(self,func):self.func=funcself.interpreter=Noneifhasattr(PythonScriptingInstance._interpreter,"value"):self.interpreter=PythonScriptingInstance._interpreter.valueself.__class__._actions.append(self)self.callback=ctypes.CFUNCTYPE(None,ctypes.c_void_p)(lambdactxt:self.execute())defexecute(self):old_interpreter=Noneifhasattr(PythonScriptingInstance._interpreter,"value"):old_interpreter=PythonScriptingInstance._interpreter.valuePythonScriptingInstance._interpreter.value=self.interpretertry:self.func()finally:PythonScriptingInstance._interpreter.value=old_interpreterself.__class__._actions.remove(self)

class_ScriptingProviderMetaclass(type):@propertydeflist(self):"""List all ScriptingProvider types (read-only)"""binaryninja._init_plugins()count=ctypes.c_ulonglong()types=core.BNGetScriptingProviderList(count)result=[]foriinrange(0,count.value):result.append(ScriptingProvider(types[i]))core.BNFreeScriptingProviderList(types)returnresultdef__iter__(self):binaryninja._init_plugins()count=ctypes.c_ulonglong()types=core.BNGetScriptingProviderList(count)try:foriinrange(0,count.value):yieldScriptingProvider(types[i])finally:core.BNFreeScriptingProviderList(types)def__getitem__(self,value):binaryninja._init_plugins()provider=core.BNGetScriptingProviderByName(str(value))ifproviderisNone:raiseKeyError("'%s' is not a valid scripting provider"%str(value))returnScriptingProvider(provider)def__setattr__(self,name,value):try:type.__setattr__(self,name,value)exceptAttributeError:raiseAttributeError("attribute '%s' is read only"%name)

class_PythonScriptingInstanceOutput(object):def__init__(self,orig,is_error):self.orig=origself.is_error=is_errorself.buffer=""self.encoding='UTF-8'self.errors=Noneself.mode='w'self.name='PythonScriptingInstanceOutput'self.newlines=Nonedefclose(self):passdefclosed(self):returnFalsedefflush(self):passdefisatty(self):returnFalsedefnext(self):raiseIOError("File not open for reading")defread(self):raiseIOError("File not open for reading")defreadinto(self):raiseIOError("File not open for reading")defreadlines(self):raiseIOError("File not open for reading")defseek(self):passdefsofspace(self):return0deftruncate(self):passdeftell(self):returnself.orig.tell()defwritelines(self,lines):returnself.write('\n'.join(lines))defwrite(self,data):interpreter=Noneifhasattr(PythonScriptingInstance._interpreter,"value"):interpreter=PythonScriptingInstance._interpreter.valueifinterpreterisNone:iflog.is_output_redirected_to_log():self.buffer+=datawhileTrue:i=self.buffer.find('\n')ifi==-1:breakline=self.buffer[:i]self.buffer=self.buffer[i+1:]ifself.is_error:log.log_error(line)else:log.log_info(line)else:self.orig.write(data)else:PythonScriptingInstance._interpreter.value=Nonetry:ifself.is_error:interpreter.instance.error(data)else:interpreter.instance.output(data)finally:PythonScriptingInstance._interpreter.value=interpreterclass_PythonScriptingInstanceInput(object):def__init__(self,orig):self.orig=origdefisatty(self):returnFalsedefread(self,size):interpreter=Noneifhasattr(PythonScriptingInstance._interpreter,"value"):interpreter=PythonScriptingInstance._interpreter.valueifinterpreterisNone:returnself.orig.read(size)else:PythonScriptingInstance._interpreter.value=Nonetry:result=interpreter.read(size)finally:PythonScriptingInstance._interpreter.value=interpreterreturnresultdefreadline(self):interpreter=Noneifhasattr(PythonScriptingInstance._interpreter,"value"):interpreter=PythonScriptingInstance._interpreter.valueifinterpreterisNone:returnself.orig.readline()else:result=""whileTrue:data=interpreter.read(1)result+=dataif(len(data)==0)or(data=="\n"):breakreturnresult

[docs]classInterpreterThread(threading.Thread):def__init__(self,instance):super(PythonScriptingInstance.InterpreterThread,self).__init__()self.instance=instanceself.locals={"__name__":"__console__","__doc__":None,"binaryninja":sys.modules[__name__]}self.interpreter=code.InteractiveConsole(self.locals)self.event=threading.Event()self.daemon=True# Latest selections from UIself.current_view=Noneself.current_func=Noneself.current_block=Noneself.current_addr=0self.current_selection_begin=0self.current_selection_end=0# Selections that were current as of last issued commandself.active_view=Noneself.active_func=Noneself.active_block=Noneself.active_addr=0self.active_selection_begin=0self.active_selection_end=0self.locals["get_selected_data"]=self.get_selected_dataself.locals["write_at_cursor"]=self.write_at_cursorself.exit=Falseself.code=Noneself.input=""self.interpreter.push("from binaryninja import *")

[docs]defrun(self):whilenotself.exit:self.event.wait()self.event.clear()ifself.exit:breakifself.codeisnotNone:self.instance.input_ready_state=ScriptingProviderInputReadyState.NotReadyForInputcode=self.codeself.code=NonePythonScriptingInstance._interpreter.value=selftry:self.active_view=self.current_viewself.active_func=self.current_funcself.active_block=self.current_blockself.active_addr=self.current_addrself.active_selection_begin=self.current_selection_beginself.active_selection_end=self.current_selection_endself.locals["current_view"]=self.active_viewself.locals["bv"]=self.active_viewself.locals["current_function"]=self.active_funcself.locals["current_basic_block"]=self.active_blockself.locals["current_address"]=self.active_addrself.locals["here"]=self.active_addrself.locals["current_selection"]=(self.active_selection_begin,self.active_selection_end)ifself.active_funcisNone:self.locals["current_llil"]=Noneself.locals["current_mlil"]=Noneelse:self.locals["current_llil"]=self.active_func.llilself.locals["current_mlil"]=self.active_func.mlilforlineincode.split(b'\n'):self.interpreter.push(line.decode('charmap'))tryNavigate=Trueifisinstance(self.locals["here"],str)orisinstance(self.locals["current_address"],str):try:self.locals["here"]=self.active_view.parse_expression(self.locals["here"],self.active_addr)exceptValueErrorase:sys.stderr.write(e)tryNavigate=FalseiftryNavigate:ifself.locals["here"]!=self.active_addr:ifnotself.active_view.file.navigate(self.active_view.file.view,self.locals["here"]):sys.stderr.write("Address 0x%x is not valid for the current view\n"%self.locals["here"])elifself.locals["current_address"]!=self.active_addr:ifnotself.active_view.file.navigate(self.active_view.file.view,self.locals["current_address"]):sys.stderr.write("Address 0x%x is not valid for the current view\n"%self.locals["current_address"])ifself.active_viewisnotNone:self.active_view.update_analysis()except:traceback.print_exc()finally:PythonScriptingInstance._interpreter.value=Noneself.instance.input_ready_state=ScriptingProviderInputReadyState.ReadyForScriptExecution

[docs]@abc.abstractmethoddefperform_execute_script_input(self,text):ifself.input_ready_state==ScriptingProviderInputReadyState.NotReadyForInput:returnScriptingProviderExecuteResult.InvalidScriptInputifself.input_ready_state==ScriptingProviderInputReadyState.ReadyForScriptProgramInput:iflen(text)==0:returnScriptingProviderExecuteResult.SuccessfulScriptExecutionself.input_ready_state=ScriptingProviderInputReadyState.NotReadyForInputself.interpreter.add_input(text)returnScriptingProviderExecuteResult.SuccessfulScriptExecutiontry:ifisinstance(text,str):result=code.compile_command(text)else:result=code.compile_command(text.decode("charmap"))except:result=FalseifresultisNone:# Command is not complete, ask for more inputreturnScriptingProviderExecuteResult.IncompleteScriptInputself.input_ready_state=ScriptingProviderInputReadyState.NotReadyForInputself.interpreter.execute(text)returnScriptingProviderExecuteResult.SuccessfulScriptExecution