399 lines
No EOL
15 KiB
Python
399 lines
No EOL
15 KiB
Python
import sys
|
|
from socket import *
|
|
import xml.dom.minidom
|
|
|
|
import response
|
|
|
|
|
|
#----------------------------------------------------------------
|
|
# Client class
|
|
# Interacts with the NetworkServer in a Danderspritz window
|
|
# The NetworkServer listens on a TCP port for XML messages.
|
|
# This client connects to the server and wraps the generation
|
|
# of the XML messages.
|
|
#----------------------------------------------------------------
|
|
class Client(object):
|
|
def __init__(self, addr, port, name="Python Client"):
|
|
# connect!
|
|
self.sock = socket(AF_INET, SOCK_STREAM)
|
|
self.sock.connect((addr, port))
|
|
self.sock.setblocking(0)
|
|
|
|
self.pending = bytearray(0)
|
|
|
|
# consider setting another thread going to recieve?
|
|
|
|
doc = xml.dom.minidom.Document()
|
|
msg = doc.createElement("RemoteIdentification")
|
|
doc.appendChild(msg)
|
|
txt = doc.createTextNode(name)
|
|
msg.appendChild(txt)
|
|
|
|
_sendXml(self.sock, doc)
|
|
|
|
#-------------------------------------------------------
|
|
# Requests a file be retrieved.
|
|
# This is the preferred manner to retrieve files from a
|
|
# target. Name and Path must be provided together, or
|
|
# Fullpath must be provided. Do not provide Fullpath and either
|
|
# Name or Path.
|
|
#-------------------------------------------------------
|
|
def RequestFileRetrieval(self, name=None, path=None, fullpath=None, recursive=None, max=None):
|
|
data = dict()
|
|
if name != None:
|
|
data["name"] = name
|
|
if path != None:
|
|
data["path"] = path
|
|
if fullpath != None:
|
|
data["fullpath"] = fullpath
|
|
if recursive:
|
|
data["recursive"] = "True"
|
|
if max != None:
|
|
data["max"] = "%d" % (max)
|
|
return _sendNewRequest(self.sock, "get", data)
|
|
|
|
#-------------------------------------------------------
|
|
# Requests a file listing
|
|
# This is the preferred manner to list files on a
|
|
# target.
|
|
#-------------------------------------------------------
|
|
def RequestFileListing(self, path=None, name=None, recursive=False):
|
|
data = dict()
|
|
if name != None:
|
|
data["name"] = name
|
|
if path != None:
|
|
data["path"] = path
|
|
if recursive:
|
|
data["recursive"] = "True"
|
|
return _sendNewRequest(self.sock, "dir", data)
|
|
|
|
#-------------------------------------------------------
|
|
# Requests a strings on a file
|
|
# This is the preferred manner to do a strings dump on a file
|
|
# Name and Path must be provided together, or Fullpath must
|
|
# be provided. Do not provide Fullpath and either
|
|
# Name or Path.
|
|
#-------------------------------------------------------
|
|
def RequestStrings(self, name=None, path=None, fullpath=None, recursive=None, max=None):
|
|
data = dict()
|
|
if name != None:
|
|
data["name"] = name
|
|
if path != None:
|
|
data["path"] = path
|
|
if fullpath != None:
|
|
data["fullpath"] = fullpath
|
|
if recursive:
|
|
data["recursive"] = "True"
|
|
if max != None:
|
|
data["max"] = "%d" % (max)
|
|
return _sendNewRequest(self.sock, "strings", data)
|
|
|
|
#-------------------------------------------------------
|
|
# Requests a drive listing
|
|
#-------------------------------------------------------
|
|
def RequestDriveListing(self):
|
|
return _sendNewRequest(self.sock, "drives", dict())
|
|
|
|
#-------------------------------------------------------
|
|
# Requests process information
|
|
#-------------------------------------------------------
|
|
def RequestProcessInfo(self, id, elevate=False):
|
|
data = dict({"id" : id})
|
|
if elevate:
|
|
data["elevate"] = "True"
|
|
return _sendNewRequest(self.sock, "processinfo", data)
|
|
|
|
#-------------------------------------------------------
|
|
# Requests process options
|
|
#-------------------------------------------------------
|
|
def RequestProcessOptions(self, id):
|
|
return _sendNewRequest(self.sock, "processoptions", dict({"id" : id}))
|
|
|
|
#-------------------------------------------------------
|
|
# Requests process information
|
|
#-------------------------------------------------------
|
|
def RequestProcessKill(self, id, force=False):
|
|
data = dict({"id" : id})
|
|
if elevate:
|
|
data["force"] = "True"
|
|
return _sendNewRequest(self.sock, "kill", data)
|
|
|
|
#-------------------------------------------------------
|
|
# Requests process information
|
|
#-------------------------------------------------------
|
|
def RequestProcessKill(self, name, type, comment=None):
|
|
data = dict({"name" : name, "type" : type})
|
|
if (comment != None):
|
|
data["comment"] = comment
|
|
return _sendNewRequest(self.sock, "markProcess", data)
|
|
|
|
|
|
#-------------------------------------------------------
|
|
# Sends a directly formed command to the target.
|
|
# This should be used rarely as command syntax could
|
|
# mutate subtly.
|
|
#-------------------------------------------------------
|
|
def RequestRawCommand(self, command):
|
|
return _sendNewRequest(self.sock, "raw", { "command" : command })
|
|
|
|
|
|
#-------------------------------------------------------
|
|
# Requests data variables from the target for a specific
|
|
# command, and all it's children. It's generally a good
|
|
# idea to leave includeChildren == True, unless you ran
|
|
# a 'raw' command, because all commands other than 'raw'
|
|
# call a script that calls the appropriate command after
|
|
# formatting it. So, you'll get the task id of the script,
|
|
# but not the command you actually care about.
|
|
# Leaving operation == None means it will work on only the
|
|
# current operation. If you provide it, it will be used to
|
|
# which command you meant.
|
|
#-------------------------------------------------------
|
|
def RetrieveData(self, taskId, operation=None, includeChildren=True):
|
|
doc = xml.dom.minidom.Document()
|
|
req = doc.createElement("Request")
|
|
doc.appendChild(req)
|
|
|
|
dataReq = doc.createElement("TaskDataRequest")
|
|
req.appendChild(dataReq)
|
|
if (operation != None):
|
|
dataReq.setAttribute("operation", "%s" % (operation))
|
|
dataReq.setAttribute("taskId", "%s" % (taskId))
|
|
if (includeChildren):
|
|
dataReq.setAttribute("includeChildren", "true")
|
|
else:
|
|
dataReq.setAttribute("includeChildren", "false")
|
|
return _sendMessage(self.sock, req)
|
|
|
|
#-------------------------------------------------------
|
|
# Retrieves a response from the wire
|
|
# This function returns an object from gui.networkserver.response
|
|
# which contains the message from the NetworkServer
|
|
#-------------------------------------------------------
|
|
def ReadResponse(self):
|
|
len = None
|
|
# first, let's get any pending data
|
|
while True:
|
|
# break if we already have the IPC record mark
|
|
try:
|
|
len = self.pending.index(b'\x01')
|
|
break
|
|
except ValueError:
|
|
pass
|
|
# read a blob (let's call it 1K for now)
|
|
try:
|
|
input = self.sock.recv(1024)
|
|
self.pending.extend(input)
|
|
except:
|
|
break
|
|
|
|
if len == None:
|
|
return None
|
|
|
|
(msgStr, ret) = (self.pending[:len],self.pending[len+1:])
|
|
self.pending[:] = ret
|
|
|
|
msgXml = xml.dom.minidom.parseString(msgStr.decode("UTF-8"))
|
|
|
|
msgXml.normalize()
|
|
root = msgXml.documentElement
|
|
if root.nodeName == "RemoteIdentification":
|
|
return response.RemoteIdentification(_getStringFromNode(root))
|
|
elif root.nodeName == "RemoteMessage":
|
|
return response.RemoteMessage(_parseMessage(_getStringFromNode(root.getElementsByTagName("Message")[0])))
|
|
elif root.nodeName == "RemotePing":
|
|
return response.RemotePing()
|
|
elif root.nodeName == "RemotePong":
|
|
return response.RemotePong()
|
|
|
|
print "Msg = %s" % (msgStr)
|
|
|
|
return UnknownResponse(msgXml)
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
#--------------------------------------------------------
|
|
def _parseMessage(message):
|
|
msgXml = xml.dom.minidom.parseString(message)
|
|
root = msgXml.documentElement
|
|
try:
|
|
if root.nodeName == "Response":
|
|
if (len(root.getElementsByTagName("CancelledRequest")) > 0):
|
|
cancel = root.getElementsByTagName("CancelledRequest")[0]
|
|
requestId = int(cancel.attributes["reqId"].nodeValue)
|
|
return response.CancelledRequest(requestId)
|
|
|
|
if (len(root.getElementsByTagName("NewRequest")) > 0):
|
|
newReq = root.getElementsByTagName("NewRequest")[0]
|
|
requestId = int(newReq.attributes["reqId"].nodeValue)
|
|
try:
|
|
source = _getStringFromNode(newReq.getElementsByTagName("Source")[0])
|
|
except:
|
|
source = None
|
|
key = _getStringFromNode(newReq.getElementsByTagName("Key")[0])
|
|
data = _parseKeyValuePairs(newReq.getElementsByTagName("Data"))
|
|
return response.NewRequest(requestId, source, key, data)
|
|
|
|
if (len(root.getElementsByTagName("ExecutedRequest")) > 0):
|
|
executed = root.getElementsByTagName("ExecutedRequest")[0]
|
|
requestId = int(executed.attributes["reqId"].nodeValue)
|
|
operation = executed.attributes["operation"].nodeValue
|
|
taskId = int(executed.attributes["taskId"].nodeValue)
|
|
return response.ExecutedRequest(requestId, operation, taskId)
|
|
|
|
if (len(root.getElementsByTagName("RequestCompleted")) > 0):
|
|
completed = root.getElementsByTagName("RequestCompleted")[0]
|
|
requestId = int(completed.attributes["reqId"].nodeValue)
|
|
operation = completed.attributes["operation"].nodeValue
|
|
taskId = int(completed.attributes["taskId"].nodeValue)
|
|
status = completed.attributes["status"].nodeValue
|
|
return response.CompletedRequest(requestId, operation, taskId, status)
|
|
|
|
if (len(root.getElementsByTagName("TaskData")) > 0):
|
|
taskData = root.getElementsByTagName("TaskData")[0]
|
|
op = taskData.attributes["operation"].nodeValue
|
|
taskId = int(taskData.attributes["taskId"].nodeValue)
|
|
|
|
try:
|
|
parent = taskData.getElementsByTagName("Parent")[0]
|
|
parentId = int(parent.attributes["taskId"].nodeValue)
|
|
except:
|
|
parentId = -1
|
|
|
|
dataObj = taskData.getElementsByTagName("Data")[0]
|
|
name = dataObj.attributes["name"].nodeValue
|
|
data = dict()
|
|
|
|
return response.TaskData(name, op, taskId, parentId, _parseDataValues(dataObj.childNodes))
|
|
|
|
raise RuntimeError("Unparsed: %s" % (msgXml.toprettyxml(indent=" ")))
|
|
elif root.nodeName == "Request":
|
|
pass
|
|
elif root.nodeName == "Cancel":
|
|
pass
|
|
elif root.nodeName == "Close":
|
|
pass
|
|
except:
|
|
print root.toprettyxml(indent=" ")
|
|
raise
|
|
|
|
return message
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
#--------------------------------------------------------
|
|
def _parseDataValues(nodes, dataStore=None):
|
|
if dataStore == None:
|
|
dataStore = dict()
|
|
for n in nodes:
|
|
name = n.attributes["name"].nodeValue
|
|
if n.nodeName == "ObjectValue":
|
|
value = _parseDataValues(n.childNodes)
|
|
|
|
elif n.nodeName == "StringValue":
|
|
value = unicode(_getStringFromNode(n))
|
|
|
|
elif n.nodeName == "IntegerValue":
|
|
value = int(_getStringFromNode(n))
|
|
|
|
elif n.nodeName == "BooleanValue":
|
|
value = bool(_getStringFromNode(n))
|
|
|
|
else:
|
|
raise RuntimeError("unknown node: %s" % (n.getNodeName))
|
|
|
|
try:
|
|
current = dataStore[name]
|
|
except KeyError:
|
|
current = []
|
|
dataStore[name] = current
|
|
|
|
if len(current) > 0 and type(current[0]) != type(value):
|
|
raise RuntimeError("Same name, different types: (%s, %s)" % (type(current[0]), type(value)))
|
|
current.append(value)
|
|
return dataStore
|
|
|
|
def _parseKeyValuePairs(data):
|
|
ret = dict()
|
|
for n in data:
|
|
key = _getStringFromNode(n.getElementsByTagName("Key")[0])
|
|
value = _getStringFromNode(n.getElementsByTagName("Value")[0])
|
|
ret[key] = value
|
|
return ret
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
#--------------------------------------------------------
|
|
def _getStringFromNode(node):
|
|
if node == None:
|
|
return ""
|
|
ret = []
|
|
for n in node.childNodes:
|
|
ret.append(n.nodeValue)
|
|
return ''.join(ret)
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
# forms a new request object
|
|
#--------------------------------------------------------
|
|
def _sendNewRequest(sock, key, data):
|
|
doc = xml.dom.minidom.Document()
|
|
req = doc.createElement("Request")
|
|
doc.appendChild(req)
|
|
|
|
newReq = doc.createElement("NewRequest")
|
|
req.appendChild(newReq)
|
|
|
|
k = doc.createElement("Key")
|
|
newReq.appendChild(k)
|
|
keyTxt = doc.createTextNode(key)
|
|
k.appendChild(keyTxt)
|
|
|
|
for keyValue, value in data.items():
|
|
_addKeyValuePair(doc, newReq, keyValue, value)
|
|
|
|
return _sendMessage(sock, req)
|
|
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
# creates a key/value pair to add to the request
|
|
#--------------------------------------------------------
|
|
def _addKeyValuePair(doc, parent, key, value):
|
|
data = doc.createElement("Data")
|
|
parent.appendChild(data)
|
|
k = doc.createElement("Key")
|
|
v = doc.createElement("Value")
|
|
data.appendChild(k)
|
|
data.appendChild(v)
|
|
keyStr = doc.createTextNode(key)
|
|
k.appendChild(keyStr)
|
|
valueStr = doc.createTextNode(value)
|
|
v.appendChild(valueStr)
|
|
return True
|
|
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
# wraps an XML message and sends it
|
|
#--------------------------------------------------------
|
|
def _sendMessage(sock, xmlMessage):
|
|
doc = xml.dom.minidom.Document()
|
|
rmsg = doc.createElement("RemoteMessage")
|
|
msg = doc.createElement("Message")
|
|
txt = doc.createTextNode(xmlMessage.toxml(encoding="UTF-8"))
|
|
doc.appendChild(rmsg)
|
|
rmsg.appendChild(msg)
|
|
msg.appendChild(txt)
|
|
return _sendXml(sock, doc)
|
|
|
|
|
|
#--------------------------------------------------------
|
|
# Internal function
|
|
# sends xml to the target
|
|
#--------------------------------------------------------
|
|
def _sendXml(sock, xml):
|
|
str = xml.toxml(encoding="UTF-8")
|
|
sock.send(str)
|
|
sock.send("%c" % (0x01))
|
|
return True |