Add a lot of stuff
This commit is contained in:
1124
plugins/ret_sync_ext_ida/SyncPlugin.py
Normal file
1124
plugins/ret_sync_ext_ida/SyncPlugin.py
Normal file
File diff suppressed because it is too large
Load Diff
0
plugins/ret_sync_ext_ida/__init__.py
Normal file
0
plugins/ret_sync_ext_ida/__init__.py
Normal file
296
plugins/ret_sync_ext_ida/broker.py
Normal file
296
plugins/ret_sync_ext_ida/broker.py
Normal file
@@ -0,0 +1,296 @@
|
||||
#
|
||||
# Copyright (C) 2016, Alexandre Gazet.
|
||||
#
|
||||
# Copyright (C) 2012-2015, Quarkslab.
|
||||
#
|
||||
# This file is part of ret-sync.
|
||||
#
|
||||
# ret-sync is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Note that broker.py is executed by IDA Pro so it is not possible to see
|
||||
# any output using print() or similar
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import re
|
||||
import shlex
|
||||
import argparse
|
||||
import subprocess
|
||||
import socket
|
||||
import select
|
||||
import binascii
|
||||
import ConfigParser
|
||||
|
||||
try:
|
||||
import json
|
||||
except:
|
||||
print "[-] failed to import json\n%s" % repr(sys.exc_info())
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
RUN_DISPATCHER_MAX_ATTEMPT = 4
|
||||
HOST = "localhost"
|
||||
PORT = 9100
|
||||
|
||||
# default value is current script's path
|
||||
DISPATCHER_PATH = os.path.join(os.path.realpath(os.path.dirname(__file__)), "dispatcher.py")
|
||||
if not os.path.exists(DISPATCHER_PATH):
|
||||
print "[-] dispatcher path is not properly set, current value: <%s>" % DISPATCHER_PATH
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class Client():
|
||||
|
||||
def __init__(self, s):
|
||||
self.sock = s
|
||||
self.buffer = ''
|
||||
|
||||
def feed(self, data):
|
||||
batch = []
|
||||
self.buffer = ''.join([self.buffer, data])
|
||||
if self.buffer.endswith("\n"):
|
||||
batch = [req for req in self.buffer.strip().split('\n') if req != '']
|
||||
self.buffer = ''
|
||||
|
||||
return batch
|
||||
|
||||
|
||||
class BrokerSrv():
|
||||
|
||||
def puts(self, msg):
|
||||
print msg
|
||||
sys.stdout.flush()
|
||||
|
||||
def announcement(self, msg):
|
||||
self.puts("[sync]{\"type\":\"broker\",\"subtype\":\"msg\",\"msg\":\"%s\"}\n" % msg)
|
||||
|
||||
def notice_idb(self, msg):
|
||||
self.puts("[sync]{\"type\":\"broker\",\"subtype\":\"notice\",\"port\":\"%d\"}\n" % msg)
|
||||
|
||||
def notice_dispatcher(self, type, args=None):
|
||||
if args:
|
||||
notice = "[notice]{\"type\":\"%s\",%s}\n" % (type, args)
|
||||
else:
|
||||
notice = "[notice]{\"type\":\"%s\"}\n" % (type)
|
||||
|
||||
self.notify_socket.sendall(notice)
|
||||
|
||||
def bind(self):
|
||||
self.srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.srv_sock.bind(('localhost', 0))
|
||||
self.srv_port = self.srv_sock.getsockname()[1]
|
||||
|
||||
def run_dispatcher(self):
|
||||
cmdline = "\"%s\" -u \"%s\"" % (os.path.join(PYTHON_PATH, PYTHON_BIN), DISPATCHER_PATH)
|
||||
tokenizer = shlex.shlex(cmdline)
|
||||
tokenizer.whitespace_split = True
|
||||
args = [arg.replace('\"', '') for arg in list(tokenizer)]
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(args, shell=False, close_fds=True)
|
||||
pid = proc.pid
|
||||
except:
|
||||
pid = None
|
||||
self.announcement("failed to run dispatcher")
|
||||
|
||||
time.sleep(0.2)
|
||||
return pid
|
||||
|
||||
def notify(self):
|
||||
for attempt in range(RUN_DISPATCHER_MAX_ATTEMPT):
|
||||
try:
|
||||
self.notify_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.notify_socket.connect((HOST, PORT))
|
||||
break
|
||||
except:
|
||||
self.notify_socket.close()
|
||||
if (attempt != 0):
|
||||
self.announcement("failed to connect to dispatcher (attempt %d)" % (attempt))
|
||||
if (attempt == (RUN_DISPATCHER_MAX_ATTEMPT - 1)):
|
||||
self.announcement("failed to connect to dispatcher, too much attempts, exiting...")
|
||||
sys.exit()
|
||||
|
||||
self.announcement("dispatcher not found, trying to run it")
|
||||
pid = self.run_dispatcher()
|
||||
if pid:
|
||||
self.announcement("dispatcher now runs with pid: %d" % (pid))
|
||||
|
||||
time.sleep(0.1)
|
||||
self.notice_dispatcher("new_client", "\"port\":%d,\"idb\":\"%s\"" % (self.srv_port, self.name))
|
||||
self.announcement('connected to dispatcher')
|
||||
self.notice_idb(self.srv_port)
|
||||
|
||||
def accept(self):
|
||||
new_socket, addr = self.srv_sock.accept()
|
||||
self.clients_list.append(Client(new_socket))
|
||||
self.opened_sockets.append(new_socket)
|
||||
|
||||
def close(self, s):
|
||||
client = [client for client in self.clients_list if (client.sock == s)]
|
||||
if len(client) == 1:
|
||||
self.clients_list.remove(client[0])
|
||||
s.close()
|
||||
self.opened_sockets.remove(s)
|
||||
|
||||
def recvall(self, client):
|
||||
try:
|
||||
data = client.sock.recv(4096)
|
||||
if data == '':
|
||||
raise
|
||||
except:
|
||||
self.announcement("dispatcher connection error, quitting")
|
||||
sys.exit()
|
||||
|
||||
return client.feed(data)
|
||||
|
||||
def req_dispatcher(self, s, hash):
|
||||
subtype = hash['subtype']
|
||||
if (subtype == 'msg'):
|
||||
msg = hash['msg']
|
||||
self.announcement("dispatcher msg: %s" % msg)
|
||||
|
||||
def req_cmd(self, s, hash):
|
||||
cmd = hash['cmd']
|
||||
self.notice_dispatcher("cmd", "\"cmd\":\"%s\"" % cmd)
|
||||
|
||||
def req_kill(self, s, hash):
|
||||
self.notice_dispatcher("kill")
|
||||
self.announcement("received kill notice")
|
||||
for s in ([self.srv_sock] + self.opened_sockets):
|
||||
s.close()
|
||||
sys.exit()
|
||||
|
||||
def parse_exec(self, s, req):
|
||||
if not (req[0:8] == '[notice]'):
|
||||
self.puts(req)
|
||||
return
|
||||
|
||||
req = self.normalize(req, 8)
|
||||
|
||||
try:
|
||||
hash = json.loads(req)
|
||||
except:
|
||||
print "[-] broker failed to parse json\n %s" % repr(req)
|
||||
return
|
||||
|
||||
type = hash['type']
|
||||
if not type in self.req_handlers:
|
||||
print ("[*] broker unknown request: %s" % type)
|
||||
return
|
||||
|
||||
req_handler = self.req_handlers[type]
|
||||
req_handler(s, hash)
|
||||
|
||||
def normalize(self, req, taglen):
|
||||
req = req[taglen:]
|
||||
req = req.replace("\\", "\\\\")
|
||||
req = req.replace("\n", "")
|
||||
return req
|
||||
|
||||
def handle(self, s):
|
||||
client = [client for client in self.clients_list if (client.sock == s)]
|
||||
if len(client) == 1:
|
||||
batch = self.recvall(client[0])
|
||||
else:
|
||||
self.announcement("socket error")
|
||||
raise Exception("rabbit eating the cable")
|
||||
|
||||
for req in batch:
|
||||
if req != '':
|
||||
self.parse_exec(s, req)
|
||||
|
||||
def loop(self):
|
||||
self.srv_sock.listen(5)
|
||||
while True:
|
||||
rlist, wlist, xlist = select.select([self.srv_sock] + self.opened_sockets, [], [])
|
||||
|
||||
if not rlist:
|
||||
self.announcement("socket error: select")
|
||||
raise Exception("rabbit eating the cable")
|
||||
|
||||
for s in rlist:
|
||||
if s is self.srv_sock:
|
||||
self.accept()
|
||||
else:
|
||||
self.handle(s)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.opened_sockets = []
|
||||
self.clients_list = []
|
||||
self.pat = re.compile('dbg disconnected')
|
||||
self.req_handlers = {
|
||||
'dispatcher': self.req_dispatcher,
|
||||
'cmd': self.req_cmd,
|
||||
'kill': self.req_kill
|
||||
}
|
||||
|
||||
|
||||
def err_log(msg):
|
||||
fd = open("%s.err" % __file__, 'w')
|
||||
fd.write(msg)
|
||||
fd.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
try:
|
||||
PYTHON_PATH = os.environ['PYTHON_PATH']
|
||||
PYTHON_BIN = os.environ['PYTHON_BIN']
|
||||
except Exception as e:
|
||||
err_log("broker failed to retreive PYTHON_PATH or PYTHON_BIN value.")
|
||||
sys.exit()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--idb', nargs=1, action='store')
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.idb:
|
||||
print "[sync] no idb argument"
|
||||
sys.exit()
|
||||
|
||||
for loc in ['IDB_PATH', 'USERPROFILE', 'HOME']:
|
||||
if loc in os.environ:
|
||||
confpath = os.path.join(os.path.realpath(os.environ[loc]), '.sync')
|
||||
if os.path.exists(confpath):
|
||||
config = ConfigParser.SafeConfigParser({'port': PORT, 'host': HOST})
|
||||
config.read(confpath)
|
||||
PORT = config.getint("INTERFACE", 'port')
|
||||
HOST = config.get("INTERFACE", 'host')
|
||||
break
|
||||
|
||||
server = BrokerSrv(args.idb[0])
|
||||
|
||||
try:
|
||||
server.bind()
|
||||
except Exception as e:
|
||||
server.announcement("failed to bind")
|
||||
err_log(repr(e))
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
server.notify()
|
||||
except Exception as e:
|
||||
server.announcement("failed to notify dispatcher")
|
||||
err_log(repr(e))
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
server.loop()
|
||||
except Exception as e:
|
||||
server.announcement("broker stop")
|
||||
err_log(repr(e))
|
||||
463
plugins/ret_sync_ext_ida/dispatcher.py
Normal file
463
plugins/ret_sync_ext_ida/dispatcher.py
Normal file
@@ -0,0 +1,463 @@
|
||||
#
|
||||
# Copyright (C) 2016, Alexandre Gazet.
|
||||
#
|
||||
# Copyright (C) 2012-2014, Quarkslab.
|
||||
#
|
||||
# This file is part of ret-sync.
|
||||
#
|
||||
# ret-sync is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import os.path as altpath
|
||||
import sys
|
||||
import socket
|
||||
import select
|
||||
import base64
|
||||
import binascii
|
||||
import re
|
||||
import ConfigParser
|
||||
import traceback
|
||||
|
||||
HOST = 'localhost'
|
||||
PORT = 9100
|
||||
|
||||
try:
|
||||
import json
|
||||
except:
|
||||
print "[-] failed to import json\n%s" % repr(sys.exc_info())
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class Client():
|
||||
|
||||
def __init__(self, s_client, s_srv, name):
|
||||
self.client_sock = s_client
|
||||
self.srv_sock = s_srv
|
||||
self.name = name
|
||||
self.enabled = False
|
||||
self.buffer = ''
|
||||
|
||||
def close(self):
|
||||
self.enabled = False
|
||||
if self.client_sock:
|
||||
self.client_sock.close()
|
||||
if self.srv_sock:
|
||||
self.srv_sock.close()
|
||||
|
||||
def feed(self, data):
|
||||
batch = []
|
||||
self.buffer = ''.join([self.buffer, data])
|
||||
if self.buffer.endswith("\n"):
|
||||
batch = [req for req in self.buffer.strip().split('\n') if req != '']
|
||||
self.buffer = ''
|
||||
|
||||
return batch
|
||||
|
||||
|
||||
class DispatcherSrv():
|
||||
|
||||
def __init__(self):
|
||||
self.idb_clients = []
|
||||
self.dbg_client = None
|
||||
self.srv_socks = []
|
||||
self.opened_socks = []
|
||||
|
||||
self.current_dbg = None
|
||||
self.current_dialect = 'unknown'
|
||||
self.current_idb = None
|
||||
self.current_module = None
|
||||
|
||||
self.sync_mode_auto = True
|
||||
self.disconn_pat = re.compile('dbg disconnected')
|
||||
self.req_handlers = {
|
||||
'new_client': self.req_new_client,
|
||||
'new_dbg': self.req_new_dbg,
|
||||
'dbg_quit': self.req_dbg_quit,
|
||||
'idb_n': self.req_idb_n,
|
||||
'idb_list': self.req_idb_list,
|
||||
'module': self.req_module,
|
||||
'sync_mode': self.req_sync_mode,
|
||||
'cmd': self.req_cmd,
|
||||
'bc': self.req_bc,
|
||||
'kill': self.req_kill
|
||||
}
|
||||
|
||||
def bind(self, host, port):
|
||||
self.dbg_srv_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.dbg_srv_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.dbg_srv_sock.bind((host, port))
|
||||
self.srv_socks.append(self.dbg_srv_sock)
|
||||
|
||||
if not (socket.gethostbyname(host) == '127.0.0.1'):
|
||||
self.localhost_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.localhost_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.localhost_sock.bind(('localhost', port))
|
||||
self.srv_socks.append(self.localhost_sock)
|
||||
|
||||
def accept(self, s):
|
||||
new_socket, addr = s.accept()
|
||||
self.opened_socks.append(new_socket)
|
||||
|
||||
def listen(self):
|
||||
for s in self.srv_socks:
|
||||
s.listen(5)
|
||||
|
||||
def close(self, s):
|
||||
s.close()
|
||||
self.opened_socks.remove(s)
|
||||
|
||||
def loop(self):
|
||||
self.listen()
|
||||
self.announcement("dispatcher listening")
|
||||
|
||||
while True:
|
||||
rlist, wlist, xlist = select.select(self.srv_socks + self.opened_socks, [], [])
|
||||
|
||||
if not rlist:
|
||||
self.announcement("socket error: select")
|
||||
raise Exception("rabbit eating the cable")
|
||||
|
||||
for s in rlist:
|
||||
if s in self.srv_socks:
|
||||
self.accept(s)
|
||||
else:
|
||||
self.handle(s)
|
||||
|
||||
def handle(self, s):
|
||||
client = self.sock_to_client(s)
|
||||
for req in self.recvall(client):
|
||||
self.parse_exec(s, req)
|
||||
|
||||
# find client object for its srv socket
|
||||
def sock_to_client(self, s):
|
||||
if self.current_dbg and (s == self.current_dbg.srv_sock):
|
||||
client = self.current_dbg
|
||||
else:
|
||||
clist = [client for client in self.idb_clients if (client.srv_sock == s)]
|
||||
if not clist:
|
||||
client = Client(None, s, None)
|
||||
self.idb_clients.append(client)
|
||||
else:
|
||||
client = clist[0]
|
||||
|
||||
return client
|
||||
|
||||
# buffered readline like function
|
||||
def recvall(self, client):
|
||||
try:
|
||||
data = client.srv_sock.recv(4096)
|
||||
if data == '':
|
||||
raise
|
||||
except:
|
||||
if client == self.current_dbg:
|
||||
self.broadcast("debugger closed the connection")
|
||||
self.dbg_quit()
|
||||
else:
|
||||
self.client_quit(client.srv_sock)
|
||||
self.broadcast("a client quit, nb client(s) left: %d" % len(self.idb_clients))
|
||||
|
||||
return []
|
||||
|
||||
return client.feed(data)
|
||||
|
||||
# parse and execute requests from clients (idbs or dbg)
|
||||
def parse_exec(self, s, req):
|
||||
if not (req[0:8] == '[notice]'):
|
||||
# this is a normal [sync] request from debugger, forward it
|
||||
self.forward(req)
|
||||
# receive 'dbg disconnected', socket can be closed
|
||||
if re.search(self.disconn_pat, req):
|
||||
self.close(s)
|
||||
return
|
||||
|
||||
req = self.normalize(req, 8)
|
||||
try:
|
||||
hash = json.loads(req)
|
||||
except:
|
||||
print "[-] dispatcher failed to parse json\n %s\n" % req
|
||||
return
|
||||
|
||||
type = hash['type']
|
||||
if not type in self.req_handlers:
|
||||
print ("[*] dispatcher unknown request: %s" % type)
|
||||
return
|
||||
|
||||
req_handler = self.req_handlers[type]
|
||||
req_handler(s, hash)
|
||||
|
||||
def normalize(self, req, taglen):
|
||||
req = req[taglen:]
|
||||
req = req.replace("\\", "\\\\")
|
||||
req = req.replace("\n", "")
|
||||
return req
|
||||
|
||||
def puts(self, msg, s):
|
||||
s.sendall(msg)
|
||||
|
||||
# dispatcher announcements are forwarded to the idb
|
||||
def announcement(self, msg, s=None):
|
||||
if not s:
|
||||
if not self.current_idb:
|
||||
return
|
||||
s = self.current_idb.client_sock
|
||||
|
||||
try:
|
||||
s.sendall("[notice]{\"type\":\"dispatcher\",\"subtype\":\"msg\",\"msg\":\"%s\"}\n" % msg)
|
||||
except:
|
||||
return
|
||||
|
||||
# send message to all connected idb clients
|
||||
def broadcast(self, msg):
|
||||
for idbc in self.idb_clients:
|
||||
self.announcement(msg, idbc.client_sock)
|
||||
|
||||
# send dbg message to currently active idb client
|
||||
def forward(self, msg, s=None):
|
||||
if not s:
|
||||
if not self.current_idb:
|
||||
return
|
||||
s = self.current_idb.client_sock
|
||||
|
||||
if s:
|
||||
s.sendall(msg + "\n")
|
||||
|
||||
# send dbg message to all idb clients
|
||||
def forward_all(self, msg, s=None):
|
||||
for idbc in self.idb_clients:
|
||||
self.forward(msg, idbc.client_sock)
|
||||
|
||||
# disable current idb and enable new idb matched from current module name
|
||||
def switch_idb(self, new_idb):
|
||||
msg = "[sync]{\"type\":\"broker\",\"subtype\":\"%s\"}\n"
|
||||
if (not self.current_idb == new_idb) & (self.current_idb.enabled):
|
||||
self.current_idb.client_sock.sendall(msg % "disable_idb")
|
||||
self.current_idb.enabled = False
|
||||
|
||||
if new_idb:
|
||||
new_idb.client_sock.sendall(msg % "enable_idb")
|
||||
self.current_idb = new_idb
|
||||
new_idb.enabled = True
|
||||
|
||||
# a new idb client connects to the dispatcher via its broker
|
||||
def req_new_client(self, srv_sock, hash):
|
||||
port, name = hash['port'], hash['idb']
|
||||
try:
|
||||
client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
client_sock.connect(('localhost', port))
|
||||
self.opened_socks.append(client_sock)
|
||||
except:
|
||||
self.opened_socks.remove(srv_sock)
|
||||
srv_sock.close()
|
||||
return
|
||||
|
||||
# check if an idb client is already registered with the same name
|
||||
conflicting = [client for client in self.idb_clients if (client.name == name)]
|
||||
|
||||
# promote to idb client
|
||||
new_client = self.sock_to_client(srv_sock)
|
||||
new_client.client_sock = client_sock
|
||||
new_client.name = name
|
||||
self.broadcast("add new client (listening on port %d), nb client(s): %d" % (port, len(self.idb_clients)))
|
||||
|
||||
if conflicting:
|
||||
self.broadcast("conflicting name: %s !" % new_client.name)
|
||||
|
||||
if not self.current_idb:
|
||||
self.current_idb = new_client
|
||||
|
||||
# if new client match current module name, then enable it
|
||||
if self.current_module == name:
|
||||
self.switch_idb(new_client)
|
||||
|
||||
# inform new client about debugger's dialect
|
||||
self.dbg_dialect(new_client)
|
||||
|
||||
# clean state when a client is quiting
|
||||
def client_quit(self, s):
|
||||
self.opened_socks.remove(s)
|
||||
# remove exiting client from the list of active clients
|
||||
for idbc in [idbc for idbc in self.idb_clients if (idbc.srv_sock == s)]:
|
||||
self.idb_clients.remove(idbc)
|
||||
self.opened_socks.remove(idbc.client_sock)
|
||||
idbc.close()
|
||||
|
||||
# no more clients, let's kill ourself
|
||||
if not self.idb_clients:
|
||||
for s in self.srv_socks:
|
||||
s.close()
|
||||
sys.exit()
|
||||
|
||||
# determine if debugger is Windows specific
|
||||
def is_windows_dbg(self, dialect):
|
||||
return (dialect in ['windbg', 'x64_dbg', 'ollydbg2'])
|
||||
|
||||
# a new debugger client connects to the dispatcher
|
||||
def req_new_dbg(self, s, hash):
|
||||
msg = hash['msg']
|
||||
if self.current_dbg:
|
||||
self.dbg_quit()
|
||||
|
||||
# promote to dbg client
|
||||
self.current_dbg = self.sock_to_client(s)
|
||||
self.current_dbg.client_sock = s
|
||||
self.idb_clients.remove(self.current_dbg)
|
||||
|
||||
self.broadcast("new debugger client: %s" % msg)
|
||||
|
||||
# store dbb's dialect
|
||||
if 'dialect' in hash:
|
||||
self.current_dialect = hash['dialect']
|
||||
|
||||
# case when IDA is on a linux/bsd host and connected to remote windows
|
||||
# use ntpath instead of posixpath
|
||||
if sys.platform.startswith('linux') or sys.platform == 'darwin':
|
||||
if self.is_windows_dbg(self.current_dialect):
|
||||
global altpath
|
||||
import ntpath as altpath
|
||||
|
||||
self.dbg_dialect()
|
||||
|
||||
# inform client about debugger's dialect
|
||||
def dbg_dialect(self, client=None):
|
||||
msg = "[sync]{\"type\":\"dialect\",\"dialect\":\"%s\"}\n" % self.current_dialect
|
||||
if client:
|
||||
client.client_sock.sendall(msg)
|
||||
else:
|
||||
for idbc in self.idb_clients:
|
||||
idbc.client_sock.sendall(msg)
|
||||
|
||||
# debugger client disconnect from the dispatcher
|
||||
def req_dbg_quit(self, s, hash):
|
||||
msg = hash['msg']
|
||||
self.broadcast("debugger quit: %s" % msg)
|
||||
self.dbg_quit()
|
||||
|
||||
# clean state when debugger is quiting
|
||||
def dbg_quit(self):
|
||||
self.opened_socks.remove(self.current_dbg.srv_sock)
|
||||
self.current_dbg.close()
|
||||
self.current_dbg = None
|
||||
self.current_module = None
|
||||
self.switch_idb(None)
|
||||
self.current_dialect = 'unknown'
|
||||
|
||||
# handle kill notice from a client, exit properly if no more client
|
||||
def req_kill(self, s, hash):
|
||||
self.client_quit(s)
|
||||
self.broadcast("received a kill notice from client, %d client(s) left" % len(self.idb_clients))
|
||||
|
||||
# send list of currently connected idb clients
|
||||
def req_idb_list(self, s, hash):
|
||||
clist = "> currently connected idb(s):\n"
|
||||
if not self.idb_clients:
|
||||
clist += " no idb client yet\n"
|
||||
else:
|
||||
for i in range(len(self.idb_clients)):
|
||||
clist += (" [%d] %s\n" % (i, self.idb_clients[i].name))
|
||||
|
||||
s.sendall(clist)
|
||||
|
||||
# manually set current active idb to idb n from idb list
|
||||
def req_idb_n(self, s, hash):
|
||||
idb = hash['idb']
|
||||
try:
|
||||
idbn = int(idb)
|
||||
except:
|
||||
s.sendall("> n should be a decimal value")
|
||||
return
|
||||
|
||||
try:
|
||||
idbc = self.idb_clients[idbn]
|
||||
except:
|
||||
s.sendall("> %d is invalid (see idblist)" % idbn)
|
||||
return
|
||||
|
||||
self.switch_idb(idbc)
|
||||
s.sendall("> current idb set to %d" % idbn)
|
||||
|
||||
# dbg notice that its current module has changed
|
||||
def req_module(self, s, hash):
|
||||
modpath = hash['path']
|
||||
self.current_module = modname = altpath.basename(modpath)
|
||||
matching = [idbc for idbc in self.idb_clients if (idbc.name.lower() == modname.lower())]
|
||||
|
||||
if not self.sync_mode_auto:
|
||||
self.broadcast("sync_mode_auto off")
|
||||
return
|
||||
|
||||
if len(matching) == 1:
|
||||
# matched is set as active
|
||||
self.switch_idb(matching[0])
|
||||
else:
|
||||
if not len(matching):
|
||||
msg = "mod request has no match for %s"
|
||||
else:
|
||||
msg = "ambiguous mod request, too many matches for %s"
|
||||
|
||||
self.broadcast(msg % modname)
|
||||
# no match current idb (if existing) is disabled
|
||||
if self.current_idb.enabled:
|
||||
self.switch_idb(None)
|
||||
|
||||
# sync mode tells if idb switch is automatic or manual
|
||||
def req_sync_mode(self, s, hash):
|
||||
mode = hash['auto']
|
||||
self.broadcast("sync mode auto set to %s" % mode)
|
||||
self.sync_mode_auto = (mode == "on")
|
||||
|
||||
# bc request should be forwarded to all idbs
|
||||
def req_bc(self, s, hash):
|
||||
msg = "[sync]%s" % json.dumps(hash)
|
||||
self.forward_all(msg)
|
||||
|
||||
def req_cmd(self, s, hash):
|
||||
cmd = hash['cmd']
|
||||
self.current_dbg.client_sock.sendall("%s\n" % cmd)
|
||||
|
||||
|
||||
def err_log(msg):
|
||||
fd = open("%s.err" % __file__, 'w')
|
||||
fd.write(msg)
|
||||
fd.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
server = DispatcherSrv()
|
||||
|
||||
for loc in ['IDB_PATH', 'USERPROFILE', 'HOME']:
|
||||
if loc in os.environ:
|
||||
confpath = os.path.join(os.path.realpath(os.environ[loc]), '.sync')
|
||||
if os.path.exists(confpath):
|
||||
config = ConfigParser.SafeConfigParser({'host': HOST, 'port': PORT})
|
||||
config.read(confpath)
|
||||
HOST = config.get("INTERFACE", 'host')
|
||||
PORT = config.getint("INTERFACE", 'port')
|
||||
server.announcement("configuration file loaded")
|
||||
break
|
||||
|
||||
try:
|
||||
server.bind(HOST, PORT)
|
||||
except Exception as e:
|
||||
err_log("dispatcher failed to bind on %s:%s\n-> %s" % (HOST, PORT, repr(e)))
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
server.loop()
|
||||
except Exception as e:
|
||||
err_log("dispatcher failed\n-> %s" % repr(e))
|
||||
server.announcement("dispatcher stop")
|
||||
Reference in New Issue
Block a user