class neo4ida_t(idaapi.plugin_t): flags = 0 comment = "Neo4j graph export and query interface" help = "Neo4j graph export and query interface" wanted_name = "Neo4IDA" wanted_hotkey = "" def init(self): self.conf_file = os.path.expanduser("~") + os.path.sep + "neo4ida.json" config = self.get_config() if not config: config = self.create_default_config() self.connect() action = UiAction(id="neo4ida:upload", name="Upload", tooltip="Upload to neo4j", menuPath="Edit/neo4ida/", callback=self.upload, icon="") if not action.registerAction(): return 1 action = UiAction(id="neo4ida:dropdb", name="Drop Database", tooltip="Delete all entries in database instance.", menuPath="Edit/neo4ida/", callback=self.drop_db, icon="") if not action.registerAction(): return 1 action = UiAction(id="neo4ida:config", name="Configure", tooltip="Configure neo4j connection details.", menuPath="Edit/neo4ida/", callback=self.config_form, icon="") if not action.registerAction(): return 1 action = UiAction(id="neo4ida:query", name="Cypher Query", tooltip="Execute a Cypher query.", menuPath="Edit/neo4ida/", callback=self.query_form, icon="") if not action.registerAction(): return 1 action = UiAction(id="neo4ida:browser", name="Neo4j Browser", tooltip="Open Neo4j browser.", menuPath="Edit/neo4ida/", callback=self.open_browser, icon="") if not action.registerAction(): return 1 action = UiAction(id="neo4ida:diff", name="Binary Diff", tooltip="Open binary diffing interface.", menuPath="Edit/neo4ida/", callback=self.binary_diff, icon="") if not action.registerAction(): return 1 return idaapi.PLUGIN_KEEP def connect(self): conf = self.get_config() authenticate(conf['host'] + ":" + conf['port'], conf['username'], conf["password"]) try: self.neo = Graph("http://" + conf['host'] + ":" + conf["port"] + "/db/data") except: print "Failed to connect!" def term(self): return None def binary_diff(self, ctf): print "Open binary diffing interface" def drop_db(self, ctx): self.neo.cypher.execute("START n=node(*) detach delete n;") print "All database nodes and relationships deleted." def open_browser(self, ctx): self.neo.open_browser() def config_form(self, ctx): ConnectionManagementForm(self) def query_form(self, ctf): CypherQueryForm(self) def upload(self, ctx): start = time.time() func_count = 0 bb_count = 0 call_count = 0 target = idaapi.get_root_filename() hash = idc.GetInputMD5() tx = self.neo.cypher.begin() insert_binary = "MERGE (n:Binary {name:{N},hash:{H}}) RETURN n" insert_func = "MERGE (n:Function {name:{N},start:{S},flags:{F}}) RETURN n" insert_bb = "MERGE (n:BasicBlock {start:{S}, end:{E}}) RETURN n" create_relationship = "MATCH (u:Function {name:{N}}), (r:Function {start:{S}}) CREATE (u)-[:CALLS]->(r)" create_contains = "MATCH (u:BasicBlock {start:{S}}), (f:Function {name:{N}}) CREATE (f)-[:CONTAINS]->(u)" create_inside = "MATCH (u:Function {start:{S}}), (b:Binary {hash:{H}}) CREATE (f)-[:INSIDE]->(b)" self.neo.cypher.execute(insert_binary, {"N": target, "H": hash}) self.neo.cypher.execute("CREATE INDEX ON :Function(start)") #self.neo.cypher.execute("CREATE INDEX ON :Function(name)") self.neo.cypher.execute("CREATE INDEX ON :BasicBlock(start)") for f in Functions(): tx.append(create_inside, {"S": f, "H": hash}) callee_name = GetFunctionName(f) flags = get_flags(f) type = GetType(f) if type: return_type = type.split()[0] print type end_return = type.find(' ') start_args = type.find('(') print type[end_return + 1:start_args] print type[start_args + 1:].split(',') else: print GuessType(f) tx.append(insert_func, {"N": callee_name, "S": f, "F": flags}) func_count += 1 fc = idaapi.FlowChart(idaapi.get_func(f)) for block in fc: tx.append(insert_bb, {"S": block.startEA, "E": block.endEA}) tx.append(create_contains, {"S": block.startEA, "N": f}) bb_count += 1 tx.process() tx.commit() tx = self.neo.cypher.begin() for f in Functions(): for xref in CodeRefsTo(f, 0): caller_name = GetFunctionName(xref) if caller_name != '': tx.append(create_relationship, {"N": caller_name, "S": f}) call_count += 1 tx.process() tx.commit() print "Upload ran in: " + str(time.time() - start) print "Uploaded " + str(func_count) + " functions, " + str( call_count) + " function calls and " + str( bb_count) + " basic blocks." def run(self): pass def update_config(self, new_config): print "updating config to be: " print json.dumps(new_config) os.remove(self.conf_file) with open(self.conf_file, "w+") as f: f.write(json.dumps(new_config)) def create_default_config(self): default_conf = { "host": "localhost", "port": "7474", "username": "******", "password": "******" } with open(self.conf_file, "w+") as f: f.write(json.dumps(default_conf)) return default_conf def get_config(self): try: with open(self.conf_file, "r") as f: return json.loads(f.read()) except: return None def find_path(self, startFunc, endFunc): all_paths = "" print "Finding all paths from " + startFunc + " to " + endFunc self.neo.cypher.execute(all_paths, {})
def show_graph(): graph = Graph(password=pw) graph.run('MATCH (n:Android) return n') graph.open_browser()
class neo4ida_t(idaapi.plugin_t): flags = 0 comment = "Neo4j graph export and query interface" help = "Neo4j graph export and query interface" wanted_name = "Neo4IDA" wanted_hotkey = "" def init(self): self.conf_file = os.path.expanduser("~") + os.path.sep + "neo4ida.json" config = self.get_config() if not config: config = self.create_default_config() self.connect() action = UiAction( id="neo4ida:upload", name="Upload", tooltip="Upload to neo4j", menuPath="Edit/neo4ida/", callback=self.upload, icon="" ) if not action.registerAction(): return 1 action = UiAction( id="neo4ida:dropdb", name="Drop Database", tooltip="Delete all entries in database instance.", menuPath="Edit/neo4ida/", callback=self.drop_db, icon="" ) if not action.registerAction(): return 1 action = UiAction( id="neo4ida:config", name="Configure", tooltip="Configure neo4j connection details.", menuPath="Edit/neo4ida/", callback=self.config_form, icon="" ) if not action.registerAction(): return 1 action = UiAction( id="neo4ida:query", name="Cypher Query", tooltip="Execute a Cypher query.", menuPath="Edit/neo4ida/", callback=self.query_form, icon="" ) if not action.registerAction(): return 1 action = UiAction( id="neo4ida:browser", name="Neo4j Browser", tooltip="Open Neo4j browser.", menuPath="Edit/neo4ida/", callback=self.open_browser, icon="" ) if not action.registerAction(): return 1 action = UiAction( id="neo4ida:diff", name="Binary Diff", tooltip="Open binary diffing interface.", menuPath="Edit/neo4ida/", callback=self.binary_diff, icon="" ) if not action.registerAction(): return 1 return idaapi.PLUGIN_KEEP def connect(self): conf = self.get_config() authenticate(conf['host'] + ":" + conf['port'],conf['username'],conf["password"]) try: self.neo = Graph("http://" + conf['host'] + ":" + conf["port"] + "/db/data") except: print "Failed to connect!" def term(self): return None def binary_diff(self,ctf): print "Open binary diffing interface" def drop_db(self,ctx): self.neo.cypher.execute("START n=node(*) detach delete n;") print "All database nodes and relationships deleted." def open_browser(self,ctx): self.neo.open_browser() def config_form(self,ctx): ConnectionManagementForm(self) def query_form(self,ctf): CypherQueryForm(self) def upload(self,ctx): start = time.time() func_count = 0 bb_count = 0 call_count = 0 target = idaapi.get_root_filename() hash = idc.GetInputMD5() tx = self.neo.cypher.begin() insert_binary = "MERGE (n:Binary {name:{N},hash:{H}}) RETURN n" insert_func = "MERGE (n:Function {name:{N},start:{S},flags:{F}}) RETURN n" insert_bb = "MERGE (n:BasicBlock {start:{S}, end:{E}}) RETURN n" create_relationship = "MATCH (u:Function {name:{N}}), (r:Function {start:{S}}) CREATE (u)-[:CALLS]->(r)" create_contains = "MATCH (u:BasicBlock {start:{S}}), (f:Function {name:{N}}) CREATE (f)-[:CONTAINS]->(u)" create_inside = "MATCH (u:Function {start:{S}}), (b:Binary {hash:{H}}) CREATE (f)-[:INSIDE]->(b)" self.neo.cypher.execute(insert_binary, {"N":target, "H":hash}) self.neo.cypher.execute("CREATE INDEX ON :Function(start)") #self.neo.cypher.execute("CREATE INDEX ON :Function(name)") self.neo.cypher.execute("CREATE INDEX ON :BasicBlock(start)") for f in Functions(): tx.append(create_inside, {"S":f, "H":hash}) callee_name = GetFunctionName(f) flags = get_flags(f) type = GetType(f) if type: return_type = type.split()[0] print type end_return = type.find(' ') start_args = type.find('(') print type[end_return +1:start_args] print type[start_args+1:].split(',') else: print GuessType(f) tx.append(insert_func, {"N": callee_name, "S":f, "F":flags}) func_count += 1 fc = idaapi.FlowChart(idaapi.get_func(f)) for block in fc: tx.append(insert_bb, {"S":block.startEA,"E":block.endEA}) tx.append(create_contains,{"S":block.startEA,"N":f}) bb_count += 1 tx.process() tx.commit() tx = self.neo.cypher.begin() for f in Functions(): for xref in CodeRefsTo(f,0): caller_name = GetFunctionName(xref) if caller_name != '': tx.append(create_relationship,{"N":caller_name,"S":f}) call_count += 1 tx.process() tx.commit() print "Upload ran in: " + str(time.time() - start) print "Uploaded " + str(func_count) + " functions, " + str(call_count) +" function calls and " + str(bb_count) + " basic blocks." def run(self): pass def update_config(self,new_config): print "updating config to be: " print json.dumps(new_config) os.remove(self.conf_file) with open(self.conf_file,"w+") as f: f.write(json.dumps(new_config)) def create_default_config(self): default_conf = { "host": "localhost", "port": "7474", "username":"******", "password":"******" } with open(self.conf_file,"w+") as f: f.write(json.dumps(default_conf)) return default_conf def get_config(self): try: with open(self.conf_file,"r") as f: return json.loads(f.read()) except: return None def find_path(self,startFunc, endFunc): all_paths = "" print "Finding all paths from " + startFunc + " to " + endFunc self.neo.cypher.execute(all_paths,{})