import sys
import os
import binascii
import struct
from Crypto.Cipher import ARC4
from hashlib import sha256
from zlib import crc32
import time
import pefile
''' [+]20 June 2019
    [-]This script is intended to extract relevant configuration information from Sodinkibi Samples
    [-]This script may need to be updated as variants are updated
    [!]jmyers'''
__VERSION__ = '1.0'


def config_data(config_meta):
    global rc4_key
    global data_len
    rc4_key = config_meta[0:32]
    data_len = struct.unpack("<l",config_meta[36::])
    print("RC4 Key:\t%s") % rc4_key
    print("Data length:\t%s") % data_len


def config_parse(data):
    print("Payload Offset:\t%s") % config_offset
    config_data(data[config_offset:config_offset + 40])
    enc_data = data[config_offset + 40:config_offset + 40 + data_len[0]]
    calc_c32 = (hex(crc32(enc_data)))
    c32 = binascii.hexlify(data[config_offset + 32:config_offset + 36])
    c32 = "".join(map(str.__add__, c32[-2::-2] ,c32[-1::-2]))
    if calc_c32[2::] == c32:
        print("CRC32 Match:\t%s") % "".join(map(str.__add__, c32[-2::-2] ,c32[-1::-2]))
    else:
        print("[!]Mismatch\tEmbedded CRC32:%s\tCalculated:%s") % (c32, calc_c32[2::])
    return (ARC4.new(rc4_key).decrypt(enc_data))



def config_setup(data):
    global config_offset
    print("\nFile Hash:\t%s") % sha256(data).hexdigest()
    if data[112128:112160].isalnum() == True:
        config_offset = 112128
        out = config_parse(data)
    elif data[111616:111648].isalnum() == True:
        config_offset = 111616
        out = config_parse(data)
    elif data[109568:109600].isalnum() == True:
        config_offset = 109568
        out = config_parse(data)
    elif data[111104:111136].isalnum() ==True:
        config_offset = 111104
        out = config_parse(data)
    elif data[118784:118816].isalnum() == True:
        config_offset = 118784
        out = config_parse(data)
    elif data[109056:109088].isalnum() == True:
        config_offset = 109056
        out = config_parse(data)
    elif data[108544:108576].isalnum() == True:
        config_offset = 108544
        out = config_parse(data)
    else:
        print("[!!!]There is an issue with the Configuration Offset")
        out = "DID NOT DECODE PROPERLY"
    return out

def main():
    try:
        if sys.argv[1] == "-d":
            files = os.listdir(sys.argv[2])
            for x in files:
                data = open(sys.argv[2]+x, "r").read()
                out_file = config_setup(data)
                outf = open(sys.argv[2]+x+"_config_out.txt", "w")
                outf.write("File Hash:" + sha256(data).hexdigest())
                try:
                    pe = pefile.PE(sys.argv[2]+x,fast_load=True)
                    outf.write("Compiled Time:"+time.asctime(time.gmtime(pe.FILE_HEADER.TimeDateStamp)))
                except:
                    print("shit")
                outf.write(out_file)
                outf.close()
        else:
            filepath = sys.argv[1]
            data = open(filepath, "r").read()
            config_setup(data)
    except IndexError:
        print('Sodinokibi Config Parser v%s\n' % __VERSION__)
        print('Usage:\n\t%s path to file' % (sys.argv[0]))
        print('\t%s -d directory of files' % (sys.argv[0]))
        quit()


if __name__ == "__main__":
    main()
