# Copyright (c) 2014 FireEye, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

import sys
import string
import re
import os
import argparse

def getString(buf,pos):
	out = ""
	for c in buf[pos:]:
		if ord(c) == 0:
			break
		out += c
	
	if out == "":
		return None
	else:
		return out

def getUnicodeString(buf,pos):
	out = ""
	for i in range(len(buf[pos:])):
		if not (ord(buf[pos+i]) >= 32 and ord(buf[pos+i]) <= 126) and not (ord(buf[pos+i+1]) >= 32 and ord(buf[pos+i+1]) <= 126):
			out += "\x00"
			break
		out += buf[pos+i]
	
	if out == "":
		return None
	else:
		return out.replace("\x00","")
		
def parseConfig(f,buf):
	#2.9.x
	if len(buf) == 0x1390:
		v = 3
	#3.x
	elif len(buf) == 0x7f0:
		v = 2
	#1.3.x
	elif len(buf) == 0xe10:
		v = 1
	else:
		print "unrecognized config.."
		return
	ftpserveroffs = None
	ftpuseroffs = None
	ftppassoffs = None
	
	if v == 1:
		idoffs = 0x1b4
		groupoffs = 0x1de
		versionoffs = 0x312
		mutexoffs = 0x33e
		installdiroffs = 0x234
		ftpserveroffs = 0x430
		ftpuseroffs = 0x4d4
		ftppassoffs = 0x526
		
	elif v == 2:
		idoffs = 0x1b4
		groupoffs = 0x1ca
		versionoffs = 0x2d8
		mutexoffs = 0x2f0
		installdiroffs = 0x1f8
		ftpserveroffs = 0x380
		ftpuseroffs = 0x424
		ftppassoffs = 0x476
		#delayoffs = 0x378
		
	elif v == 3:
		idoffs = 0x9e0
		groupoffs = 0xa5a
		versionoffs = 0xf2e
		mutexoffs = 0xfaa
		installdiroffs = 0xb50
		
		
	print f
	id = getUnicodeString(buf,idoffs)
	group = getUnicodeString(buf,groupoffs)
	version = getUnicodeString(buf,versionoffs)
	mutex = getUnicodeString(buf,mutexoffs)
	installdir = getUnicodeString(buf,installdiroffs)
	ftpuser = None
	ftppass = None
	ftpserver = None
	
	print "ID: %s" % id
	print "Group: %s" % group
	print "Version: %s" % version
	print "Mutex: %s" % mutex
	print "Install Dir: %s" % installdir
	if ftpserveroffs != None:
		ftpserver = getUnicodeString(buf,ftpserveroffs)
		if ftpserver != "ftp.ftpserver.com".join("\x00"):
			print "FTP Server: %s" % ftpserver
			ftpuser = getUnicodeString(buf,ftpuseroffs)
			ftppass = getUnicodeString(buf,ftppassoffs)
			print "FTP User: %s" % ftpuser
			print "FTP Pass: %s" % ftppass
	
	print "\n"
	return (id,group,version,mutex,installdir,ftpserver,ftpuser,ftppass)
		
def getFile(f):
	rp = open(f, "rb")
	buf = rp.read()
	rp.close()
	return buf

if __name__ == "__main__":
	parser=argparse.ArgumentParser(description="parses Xtreme RAT config and prints config data")
	parser.add_argument('i', metavar='Input', help='a path to a config dump or directory of config dumps')
	parser.add_argument('-csv', metavar="CSV", help='write results to CSV in addition to printing to console')

	args = parser.parse_args()

	if os.path.isfile(args.i):
		f = sys.argv[1]
		buf = getFile(f)
		parseConfig(f,buf)
	elif os.path.isdir(args.i):
		if args.csv:
			csv = open(args.csv,"w")
			headers = "file,id,group,version,mutex,installdir,ftpserver,ftpuser,ftppass\n"
			csv.write(headers)
		else:
			csv = None
		d = args.i
		for f in os.listdir(d):
			path = os.path.join(d, f)
			if os.path.isfile(path):
				buf = getFile(path)
				res = parseConfig(path,buf)
				if csv is not None:
					if res is not None:
						csv.write("%s,%s,%s,%s,%s,%s,%s,%s,%s\n" % (f, res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7]))
		
		if csv is not None:
			csv.close()



