#!/usr/bin/env python

#############################################################################
##                                                                         ##
## This file is part of DPAPIck                                            ##
## Windows DPAPI decryption & forensic toolkit                             ##
##                                                                         ##
##                                                                         ##
## Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved.            ##
## This document is the property of Cassidian SAS, it may not be copied or ##
## circulated without prior licence                                        ##
##                                                                         ##
##  Author: Ivan Fontarensky <ivan.fontarensky@cassidian.com>              ##
##                                                                         ##
## This program is distributed under GPLv3 licence (see LICENCE.txt)       ##
##                                                                         ##
#############################################################################

import sys, os, re
from optparse import OptionParser

FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])

FIND_REGEX = {
    "Domain"    : "COMPUTERNAME=(\S+?)\x00",
    "DnsDomain" : "DnsDomain=".encode("UTF-16LE")+"(.+?)\\\\",
}

verbose = False;

def hexdump(src, length=8, startindex=0):
    """
    Returns a hexadecimal dump of a binary string.
    length: number of bytes per row.
    startindex: index of 1st byte.
    """
    result=[]
    for i in xrange(0, len(src), length):
       s = src[i:i+length]
       hexa = ' '.join(["%02X"%ord(x) for x in s])
       printable = s.translate(FILTER)
       result.append("%04X   %-*s   %s\n" % (i+startindex, length*3, hexa, printable))
    return ''.join(result)


def hash(src, length=8, startindex=0):
    """
    Returns a hexadecimal dump of a binary string.
    length: number of bytes per row.
    startindex: index of 1st byte.
    """
    result=[]
    for i in xrange(0, len(src), length):
       s = src[i:i+length]
       hexa = ''.join(["%02X"%ord(x) for x in s])

       result.append("%s" % (hexa))
    return ''.join(result)	


def isprintable(s, codec='utf8'):
    try: 
	s.decode(codec)
    except UnicodeDecodeError: 
	return False
    else: 
	return True


def find_reg(content,regexp):
    """
    Returns a list of matches in UTF-16 and UTF-8
    content: raw data
    regexp: regexp to obtain the list
    """  
    list = []
    r = re.compile(regexp, re.UNICODE)

    d1 = []
    try :
	d1 = [ s.decode("UTF-8") for s in r.findall(content) ]		
    except :
	pass

    d2 = []
    try :
	d2 = [ s.decode("UTF-16LE") for s in r.findall(content) ]	
    except :
	pass

    list.extend(set(d1))
    list.extend(set(d2))

    return set(list)
    

def mem_parse(path):
    """
    Returns a list of sha1 from a memory dump.
    path: path file of the memory dump
    """
    domains=[]
    hash_sha1=[]
    matches = []

    
    with open(path, "rb") as memory:

	content = memory.read()
   
	"""
	collect information
	"""
	for item in FIND_REGEX:
	    if verbose :
		print "[+] %s"%(item)
	    list = find_reg(content,FIND_REGEX[item])


	    checked = []
	    for candidat in list:
		try :
		    if isprintable(candidat):
			checked.append(candidat)
		except :
		    pass
	    domains.extend(checked)

	for domain in domains:
	    pattern = "\x01" + "\x00" + domain.encode("UTF-16LE") + "\x00" * 2


	    r = re.compile(pattern)
	    
	    for m in r.finditer(content):
		    matches.append(m)

	    if len(matches)>0:

		    for m in matches:
			start = max(m.start()-32, 0) & 0xFFFFFFF0 
			end = min(m.end()+32+15, len(content)) & 0xFFFFFFF0
			length = end-start

			s_hash = m.start() - 22;
			e_hash = m.start() -2;

			hash_sha1.append(hash(content[s_hash:e_hash], length=20, startindex=start))
	    
    return set(hash_sha1)


if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option("-m","--memory", metavar="MEMORY_FILE", dest="memFile", help="specify a memory file to parse")
    parser.add_option("--hibern", metavar="HIBERNATE_FILE", dest="hiberFile", help="specify a hibernate file to parse")
    parser.add_option("-v", "--verbose",action="store_true", dest="verbose", default=False,help="print messages on stdout")

    (options, args) = parser.parse_args()

    list_hash = []
    
    verbose = options.verbose

    if options.memFile is not None:
	list_hash.extend(mem_parse(options.memFile))

    if options.hiberFile is not None:
	list_hash.extend(mem_parse(options.memFile))



    for hash in list_hash:
      print hash

# vim:ts=4:expandtab:sw=4
