#!/usr/bin/python
# UNPACK PE files encrypted with the srand(0x42) rand() cypher.
# Try to decrypt every object located in resources file.
# Thanat0s 2015

import os
import sys
import operator
import numpy as np

try:
    import pefile
except ImportError:
    print 'pefile not installed, see http://code.google.com/p/pefile/'
    sys.exit()

pebuffer = ""
holdrand = 1


# C on win srand re-implementation
def srand(seed):
    global holdrand
    holdrand = int(seed)


# C on win rand re-implementation
def rand():
    global holdrand
    # holdrand = holdrand * 214013 + 2531011
    holdrand = np.multiply(holdrand, 214013)
    holdrand = int(holdrand) + 2531011
    return (holdrand >> 16) & 0x7fff


# Functions
def getparam(count):
    if len(sys.argv) != count + 1:
        print 'decrytp rndg crypt of kovter packer'
        print 'To Use: ' + sys.argv[0] + ' pefile callfile'
        sys.exit(1)
    else:
        return sys.argv[1], sys.argv[2]


# Return a byte at a Virtual address
def Byte(address):
    return ord(pebuffer[(address-baseaddr):(address-baseaddr)+1])


def write_Byte(address, value):
    global pebuffer
    value = int(value & 0xff)
    pebuffer[(address-baseaddr)] = value


# Return a dword at a Virtual address
def Dword(address):
    low = ord(pebuffer[(address-baseaddr):(address-baseaddr)+1])
    low2 = ord(pebuffer[(address-baseaddr)+1:(address-baseaddr)+2])
    high = ord(pebuffer[(address-baseaddr)+2:(address-baseaddr)+3])
    high2 = ord(pebuffer[(address-baseaddr)+3:(address-baseaddr)+4])
    return ((high2 << 24) + (high << 16) + (low2 << 8) + low)


# Decypher string  #####
def decrypt(address, slen):
    addressOfStartOfString = address  # Dword(address - 0x4)
    addressOfStartOfKey = slen
    pearray = list(pebuffer[(address-baseaddr):(address-baseaddr)+slen])

    fstring = []
    cursor = 0
    srand(0x42)
    print ("decrypt %08x for %d bytes" % (address, slen))
    for i in xrange(30):
        print ("."),
        sys.stdout.flush()
        for x in xrange(slen):
            tmp = (rand() % addressOfStartOfKey) & 0xff
            pearray[x] = int((pearray[x] - tmp) & 0xff)
    if pearray[0:2] == [77, 90]:
        print "Executable found"
        pearray = bytearray(pearray)
        newFile = open("Xtracted_" + str(address) + ".exe", "wb")
        newFile.write(pearray)
    else:
        print "No PE"


# List all resources
def check_rsrc(pe):
    ret = {}
    if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
        i = 0
        for resource_type in pe.DIRECTORY_ENTRY_RESOURCE.entries:
            if resource_type.name is not None:
                name = "%s" % resource_type.name
            else:
                name = "%s" % pefile.RESOURCE_TYPE.get(resource_type.struct.Id)
            if name is None:
                name = "%d" % resource_type.struct.Id
            if hasattr(resource_type, 'directory'):
                for resource_id in resource_type.directory.entries:
                    if hasattr(resource_id, 'directory'):
                        for resource_lang in resource_id.directory.entries:
                            data = pe.get_data(resource_lang.data.struct.OffsetToData,
                                               resource_lang.data.struct.Size)
                            ret[i] = (name, resource_lang.data.struct.OffsetToData,
                                      resource_lang.data.struct.Size)
                            i += 1
    return ret


def collect(file):
    out = []
    try:
        print "Open File"
        FILE = open(file, "rb")
        data = FILE.read()
        FILE.close()
    except:
        print "Error reading"
        sys.exit(1)
    try:
        pe = pefile.PE(data=data, fast_load=True)
        pe.parse_data_directories(directories=[
            pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
            pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
            pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
            pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']])
    except:
        out.append("Cannot parse %s (maybe not PE?)" % file)
        out.append("")

    global pebuffer, baseaddr
    baseaddr = pe.OPTIONAL_HEADER.ImageBase
    print "Base Address: %08X" % baseaddr
    pebuffer = pe.get_memory_mapped_image()
    pebuffer = bytearray(pebuffer)

    resources = check_rsrc(pe)
    if len(resources):
        print ("%-18s %-8s %-8s %-8s" % ("Name", "RVA", "Size", "VMA"))
        for rsrc in resources.keys():
            (name, rva, size) = resources[rsrc]
            print ("%-18s %-8s %-8s %-8s" % (name, hex(rva), hex(size), hex(baseaddr+rva)))
            print (decrypt(baseaddr+rva, size))


if __name__ == "__main__":
    if len(sys.argv) != 1:
        collect(sys.argv[1])
