/*
             ██████╗ ███████╗███╗   ██╗███████╗██████╗ ██╗ ██████╗
            ██╔════╝ ██╔════╝████╗  ██║██╔════╝██╔══██╗██║██╔════╝
            ██║  ███╗█████╗  ██╔██╗ ██║█████╗  ██████╔╝██║██║     
            ██║   ██║██╔══╝  ██║╚██╗██║██╔══╝  ██╔══██╗██║██║     
            ╚██████╔╝███████╗██║ ╚████║███████╗██║  ██║██║╚██████╗
             ╚═════╝ ╚══════╝╚═╝  ╚═══╝╚══════╝╚═╝  ╚═╝╚═╝ ╚═════╝
  ██╗    ██╗  ██╗███████╗██╗   ██╗██████╗ ██╗███████╗████████╗██╗ ██████╗    ██╗  
 ██╔╝    ██║  ██║██╔════╝██║   ██║██╔══██╗██║██╔════╝╚══██╔══╝██║██╔════╝    ╚██╗ 
██╔╝     ███████║█████╗  ██║   ██║██████╔╝██║███████╗   ██║   ██║██║          ╚██╗
╚██╗     ██╔══██║██╔══╝  ██║   ██║██╔══██╗██║╚════██║   ██║   ██║██║          ██╔╝
 ╚██╗    ██║  ██║███████╗╚██████╔╝██║  ██║██║███████║   ██║   ██║╚██████╗    ██╔╝ 
  ╚═╝    ╚═╝  ╚═╝╚══════╝ ╚═════╝ ╚═╝  ╚═╝╚═╝╚══════╝   ╚═╝   ╚═╝ ╚═════╝    ╚═╝  
           █████╗ ███╗   ██╗ █████╗ ██╗  ██╗   ██╗███████╗██╗███████╗
          ██╔══██╗████╗  ██║██╔══██╗██║  ╚██╗ ██╔╝██╔════╝██║██╔════╝
          ███████║██╔██╗ ██║███████║██║   ╚████╔╝ ███████╗██║███████╗
          ██╔══██║██║╚██╗██║██╔══██║██║    ╚██╔╝  ╚════██║██║╚════██║
          ██║  ██║██║ ╚████║██║  ██║███████╗██║   ███████║██║███████║
          ╚═╝  ╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝╚══════╝╚═╝   ╚══════╝╚═╝╚══════╝
 */

// DIE's signature file

// =================== [ ABOUT ] ===================
// This heuristic analysis will help
// identify the presence of obfuscation
// or wrapper tricks through advanced
// complex checks. Static emulation,
// signature analysis, smart checks
// and much more!
// ================================================

// ================= [ CONTACTS ] =================
// Author: DosX
// E-Mail: collab@kay-software.ru
// GitHub: https://github.com/DosX-dev
// Telegram: @DosX_exe
// ================================================
// If I don't respond to email, message to Telegram
// ================================================

// Please do not read the code out loud unless you have exorcism skills

function detect() {
    if (stubForLegacyEngines()) return null; // old DIE version

    if (PE.isHeuristicScan()) {
        if (PE.isNET()) {
            scanForObfuscations_NET();
            scanForAntiAnalysis_NET();
        } else {
            scanForObfuscations_Native();
        }

        scanForPackersAndCryptors_NET_and_Native();
    }
}

function stubForLegacyEngines() {
    if (typeof PE.isNetObjectPresent === 'undefined') {
        _setResult("DIE-Message", ">>> Update DIE Engine to 3.09 and higher for using Heuristic-analyser by DosX <<<", "", "");
        return true;
    }

    return false;
}



function scanForObfuscations_NET() {
    var options = "";

    var isDetected = false;




    const isVbNet = isVbNetStandartLibraryPresent();


    var isEntryPointModified = false;

    const vbNetEntries = [
            "Main",
            "main",
            "MAIN",
            "MyApplication"
        ],
        defaultEntries = [ // like MSIL, C#, C++ NET etc
            "Main",
            "<Main>$" // For programs with top-level operators (C#)
        ]

    if (!PE.isDll()) {
        if (isVbNet) {
            if (isAllNetReferencesMissing(vbNetEntries)) {
                isEntryPointModified = true;
            }
        } else if (isAllNetReferencesMissing(defaultEntries)) {
            isEntryPointModified = true;
        }
    }

    if (isEntryPointModified) options += "Modified EP";




    var isStrangeEpPosition = false;

    const netMetaHeaders = [
        "~",
        "Strings",
        "US",
        "GUID",
        "Blob"
    ];

    // Specify the default .NET section index
    const defaultNetSection = 0;

    // Check conditions for a strange entry point position
    if (PE.getNumberOfSections() > 1) {
        // Iterate through .NET metadata headers
        for (var s = 0; s < netMetaHeaders.length; s++) {
            const headerName = netMetaHeaders[s];

            // Check if the signature is not present in the default .NET section
            if (!PE.isSignatureInSectionPresent(defaultNetSection, "00'#" + headerName + "'00")) {
                isStrangeEpPosition = true;
                break;
            }
        }
    }

    if (isStrangeEpPosition) options += (options.length != 0 ? " + " : "") + "Strange EP position";




    var isNativeEntryPointModified = false;

    if (!PE.isDll()) {
        if (!PE.is64()) {
            // FF2500????00: jmp dword ptr [ ... ]
            if (getFirstEpAsmOpCode() !== "JMP") {
                isNativeEntryPointModified = true;
            }
        } else { // AMD64
            if (PE.VAToOffset(PE.getAddressOfEntryPoint()) != 0x00) {
                isNativeEntryPointModified = true;
            }
        }
    }

    if (isNativeEntryPointModified) options += (options.length != 0 ? " + " : "") + "Modified native EP";




    var isDosMessageModified = false;

    // Check if the specified DOS message is not found in the DOS stub
    if (PE.findSignature(PE.getDosStubOffset(), PE.getDosStubSize(), "'This program cannot be run in DOS mode.'") === -1) {
        isDosMessageModified = true;
    }

    if (isDosMessageModified) options += (options.length != 0 ? " + " : "") + "Modified DOS";




    var strangeSections = false;

    const badChars = '_-=+~!@#$%^&*()"№;%:?*():;,/\\|\'`<>.0123456789'; // Very very bad!

    // Check conditions for strange sections
    if (PE.getNumberOfSections() > 6 || !PE.isSectionNamePresent(".text")) {
        strangeSections = true;
    } else {
        // Iterate through each section
        for (var i = 0; i != PE.getNumberOfSections(); i++) {
            var sectionName = PE.getSectionName(i);

            // Check if the first character is not "." and the length of name is less than 3
            if (sectionName[0] != "." && sectionName.length < 3) {
                strangeSections = true;
                break;
            }

            // Iterate through characters after "."
            for (var x = 1; x < sectionName.length; x++) {
                // Check if the character is in the badChars list
                for (var d = 0; d < badChars.length; d++) {
                    if (sectionName[x] === badChars[d]) {
                        strangeSections = true;
                        break;
                    }
                }
            }
        }
    }

    if (strangeSections) options += (options.length != 0 ? " + " : "") + "Strange sections";




    const opCodes = new NetOpCodes();




    var isIntConfusionPresent = false;

    const intConfusionXorPattern = opCodes.ldc_i4 + opCodes.ldc_i4 + opCodes.xor + opCodes.ldc_i4;

    if (validateSignature(intConfusionXorPattern)) {
        if (validateSignature( // samples by: Inx Obfuscator
                intConfusionXorPattern +
                (opCodes.bne_un_s + opCodes.dc_i4_2 + opCodes.stloc_0 + opCodes.sizeof + opCodes.add)
            ) ||
            validateSignature( // samples by: MindLated, NetShield
                intConfusionXorPattern +
                (opCodes.bne_un + opCodes.ldc_i4 + opCodes.stloc + opCodes.sizeof + opCodes.add)
            ) ||
            validateSignature( // samples by: VavilonProtect
                intConfusionXorPattern +
                (opCodes.bne_un + opCodes.ldc_i4_2 + opCodes.stloc_s + opCodes.sizeof + opCodes.add)
            )
        ) {
            isIntConfusionPresent = true;
        }
    }

    if (isIntConfusionPresent) options += (options.length != 0 ? " + " : "") + "Int confusion";




    var isVirtualizationPresent = false;

    if (
        isAllNetReferencesPresent(
            references = [
                "System.Reflection", // System.Reflection.dll
                "GetILGenerator", // MSIL: 'System.Reflection.Emit.DynamicMethod::GetILGenerator()'
                "BeginInvoke",
                "EndInvoke",
                "OpCode" // MSIL: 'System.Reflection.Emit.OpCode'
            ]
        ) &&
        (
            validateSignature("'Ldarg_0'") || // MSIL: 'System.Reflection.Emit.OpCodes.Ldarg_0'
            validateSignature("'CreateDelegate'") // MSIL: 'System.Delegate.CreateDelegate'
        ) && !isFrameworkComponent()
    ) {
        isVirtualizationPresent = true;
    }

    if (isVirtualizationPresent) options += (options.length != 0 ? " + " : "") + "Virtualization";




    // https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.suppressildasmattribute
    var isAntiIldasmPresent = false;

    if (PE.isNetObjectPresent("SuppressIldasmAttribute")) {
        isAntiIldasmPresent = true;
    }

    if (isAntiIldasmPresent) options += (options.length != 0 ? " + " : "") + "Anti-ILDASM";




    // Anti de4dot via inheritance
    var isAntiDe4dotPresent = false;

    if (
        validateSignature("'Form'??????00'Form'??????00'Form'??????00") || // samples by: NetShield
        PE.isNetObjectPresent("Form0") // samples by: MindLated
    ) {
        isAntiDe4dotPresent = true;
    }

    if (isAntiDe4dotPresent) options += (options.length != 0 ? " + " : "") + "Anti-de4dot";




    var isCalliInvokesPresent = false;

    if (validateSignature( // samples by: MindLated
            opCodes.setStrict(opCodes.ldftn, "?? ?? 00 0A") +
            opCodes.setStrict(opCodes.calli, "?? 00 00 11")
        ) ||
        validateSignature( // samples by: ArmDot, DarksProtector
            opCodes.idelem_i +
            opCodes.setStrict(opCodes.calli, "?? 00 00 11")
        )) {
        isCalliInvokesPresent = true;
    }

    if (isCalliInvokesPresent) options += (options.length != 0 ? " + " : "") + "Calli Invokes";




    var isCtrlFlowPresent = false;

    if (validateSignature( // samples by: ConfuserEx
            opCodes.nop + opCodes.ldloc_0 + opCodes.ldc_i4 + opCodes.mul + opCodes.ldc_i4 + opCodes.xor + opCodes.br_s +
            opCodes.nop + opCodes.ldloc_0 + opCodes.ldc_i4 + opCodes.mul + opCodes.ldc_i4 + opCodes.xor + opCodes.br_s
        ) ||
        validateSignature( // samples by: ConfuserEx (neo mod)
            opCodes.ldc_i4 + opCodes.ldc_i4 + opCodes.xor + opCodes.dup + opCodes.stloc_0 + opCodes.ldc_i4_3 + opCodes.rem_un + opCodes.switch__nobody
        ) ||
        validateSignature( // samples by: .NET Reactor
            opCodes.ldsfld + opCodes.brfalse + opCodes.pop +
            opCodes.setStrict(opCodes.ldc_i4, "01 00 00 00") + // MSIL: 'ldc.4 1'
            opCodes.br + opCodes.nop
        ) ||
        validateSignature( // samples by: .NET Reactor
            opCodes.setNullValue(opCodes.ldc_i4) +
            opCodes.ldsfld + opCodes.brtrue + opCodes.pop + opCodes.ldc_i4 +
            opCodes.br
        ) ||
        validateSignature( // samples by: .NET Reactor (legacy~~)
            opCodes.stloc + opCodes.ldloc +
            opCodes.setStrict(opCodes.switch__nobody, "?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 00") +
            opCodes.ldc_i4 + opCodes.br
        ) ||
        validateSignature( // samples by: MindLated, NetShield
            opCodes.setNullValue(opCodes.ldc_i4) + // MSIL: 'ldc.i4 0'
            opCodes.stloc + opCodes.br + opCodes.nop + opCodes.ldloc +
            opCodes.setStrict(opCodes.ldc_i4, "01 00 00 00") + // MSIL: 'ldc.i4 1'
            opCodes.ceq + opCodes.brfalse
        ) ||
        validateSignature( // samples by: Rose Obfuscator
            opCodes.setNullValue(opCodes.ldc_i4) + // MSIL: 'ldc.i4'
            opCodes.stloc + opCodes.br + opCodes.nop + opCodes.ldloc + opCodes.ldc_i4 + opCodes.ceq + opCodes.brfalse
        ) ||
        validateSignature( // samples by: Smart Assembly
            opCodes.ldc_i4 + opCodes.br + opCodes.ldloc_s + opCodes.ldc_i4_s + opCodes.ldc_i4_0 + opCodes.stelem_i1 +
            opCodes.ldc_i4 + opCodes.br
        ) ||
        validateSignature( // samples by: ConfuserEx (Beds mod)
            opCodes.ldc_i4 + opCodes.ldc_i4 + "??" + opCodes.ldc_i4 + "??" + opCodes.stloc_0 + opCodes.nop + opCodes.ldloc_0 +
            opCodes.ldc_i4 + opCodes.ldc_i4 + "??" + opCodes.ldc_i4 + "??" + opCodes.ceq + opCodes.brfalse_s
        ) ||
        validateSignature( // samples by: DotNetPatcher
            opCodes.setStrict(opCodes.stloc_s, "05") + opCodes.nop + opCodes.ldloc_s + "??" + opCodes.ceq + opCodes.brfalse_s + "??" +
            opCodes.setStrict(opCodes.stloc_s, "05") + opCodes.nop + opCodes.ldloc_s + "??" + opCodes.ceq + opCodes.brfalse_s
        )
    ) {
        isCtrlFlowPresent = true;
    }

    if (isCtrlFlowPresent) options += (options.length != 0 ? " + " : "") + "Ctrl Flow";




    const mathOpCodes = [
        opCodes.add, opCodes.mul,
        opCodes.div, opCodes.xor,
        opCodes.shr, opCodes.shl,
        opCodes.or, opCodes.not,
        opCodes.and
    ];

    var isMutationsPresent = false;

    const prefix = opCodes.ldc_i4 + opCodes.ldc_i4,
        postfix = opCodes.stloc;

    for (var e = 0; e < mathOpCodes.length; e++) {

        if (!validateSignature(prefix + "??" + postfix)) break;

        // TODO: array with patterns

        const pattern = prefix + mathOpCodes[e] + postfix;

        if (validateSignature(pattern)) {
            isMutationsPresent = true;
            break;
        }
    }

    if (isMutationsPresent) options += (options.length != 0 ? " + " : "") + "Math mutations";




    var isMathInversionsPresent = false;

    if (validateSignature(opCodes.ldc_i4 + opCodes.not) && (
            validateSignature( // ~(-(~(-(~(-(~(-( num ))))))))
                opCodes.ldc_i4 +
                opCodes.not + opCodes.neg + opCodes.not + opCodes.neg +
                opCodes.not + opCodes.neg + opCodes.not + opCodes.neg
            ) ||
            validateSignature( // ~(~(-(-(~(~( num ))))))
                opCodes.ldc_i4 +
                opCodes.not + opCodes.not + opCodes.neg + opCodes.neg +
                opCodes.not + opCodes.not
            ) ||
            validateSignature( // ~(-(~(~(-(-( num ))))))
                opCodes.ldc_i4 +
                opCodes.not + opCodes.neg + opCodes.not + opCodes.not +
                opCodes.neg + opCodes.neg
            ) ||
            validateSignature( // ~(-(~(-(~(~( num ))))))
                opCodes.ldc_i4 +
                opCodes.not + opCodes.neg + opCodes.not + opCodes.neg +
                opCodes.not + opCodes.not
            )
        )) {
        isMathInversionsPresent = true;
    }

    if (isMathInversionsPresent) options += (options.length != 0 ? " + " : "") + "Math inversions";




    const obfuscatorsAttributes = [
        "Xenocode.Client.Attributes.AssemblyAttributes.ProcessedByXenocode",
        "CryptoObfuscator.ProtectedWithCryptoObfuscatorAttribute",
        "SecureTeam.Attributes.ObfuscatedByAgileDotNetAttribute",
        "Xenocode.Client.Attributes.AssemblyAttributes",
        "SmartAssembly.Attributes.PoweredByAttribute",
        "ObfuscatedByAgileDotNetAttribute",
        "NineRays.Obfuscator.Evaluation",
        "ObfuscatedByCliSecureAttribute",
        "BabelObfuscatorAttribute",
        "AsStrongAsFuckAttribute",
        "Macrobject.Obfuscator",
        "DotfuscatorAttribute",
        "CodeWallTrialVersion",
        "ConfusedByAttribute",
        "ObfuscatedByGoliath",
        "NETSpider.Attribute",
        "NineRays.Obfuscator",
        "PoweredByAttribute", // Smart Assembly
        "RustemSoft.Skater",
        "BabelAttribute",
        "YanoAttribute",
        "EMyPID_8234_",
        "ZYXDNGuarder",
        "Sixxpack",
        "____KILL", // CodeVeil
        "Reactor",
    ];

    var isFakeSignaturesPresent = false;

    var isWatermarkPresent = false;

    var signaturesCounter = 0;

    // Iterate through obfuscators attributes
    for (var t = 0; t < obfuscatorsAttributes.length; t++) {
        // Check if the obfuscator attribute is present in the .NET object
        if (PE.isNetObjectPresent(obfuscatorsAttributes[t])) {
            signaturesCounter++;
        }

        // Check if the number of detected signatures exceeds 1
        if (signaturesCounter > 1) {
            // Set flag indicating the presence of fake signatures
            isFakeSignaturesPresent = true;

            // Break the loop if the condition is met
            break;
        }
    }

    if (isFakeSignaturesPresent) {
        options += (options.length != 0 ? " + " : "") + "Fake signatures";
    } else {
        // "Watermark" is only possible in the absence of fake signatures
        if ((signaturesCounter === 1 ||
                validateSignature("'Obfuscated'") ||
                validateSignature("'obfuscated'") ||
                validateSignature("'ByAttribute'") ||
                validateSignature("'ObfuscatorAttribute'") ||
                PE.isNetObjectPresent("ObfuscationAttribute")) && !isFrameworkComponent()) // System.Reflection.ObfuscationAttribute
        {
            isWatermarkPresent = true;
        }
    }

    if (isWatermarkPresent) options += (options.length != 0 ? " + " : "") + "Watermark";




    const protectorsLabelsToRemove = [ // Protectors with these names will be removed from results
        "Xenocode Postbuild",
        "Smart Assembly",
        "Dotfuscator",
        "Babel .NET",
        "Spices.Net",
        "Agile .NET",
        "CliSecure",
        "CodeWall",
        "DNGuard",
        "Goliath",
        "Yano"
    ];

    if (isFakeSignaturesPresent) {
        for (var d = 0; d < protectorsLabelsToRemove.length; d++) {
            _removeResult("protector", protectorsLabelsToRemove[d]);
        }
    }


    if (options.length != 0) isDetected = true;




    if (isDetected) {
        _setResult("protection", "Obfuscation", "Heuristic", PE.isVerbose() ? options : "");
    }
}



function scanForAntiAnalysis_NET() {
    var options = "";


    var isAntiDebugPresent = false;

    const debuggerObject = "Debugger", // MSIL: 'System.Diagnostics.Debugger' from System.Diagnostics.dll
        isAttached = "get_IsAttached", // MSIL: '*.Debugger::get_IsAttached()'
        isLogging = "IsLogging" // MSIL: '*.Debugger::IsLogging()'

    if (
        (( // .NET Functions
                PE.isNetObjectPresent(debuggerObject) || PE.isNETUnicodeStringPresent(debuggerObject) // Check for 'Debugger'
            ) && (
                (PE.isNetObjectPresent(isAttached) || PE.isNETUnicodeStringPresent(isAttached)) || // Check for 'get_IsAttached' property
                (PE.isNetObjectPresent(isLogging) || PE.isNETUnicodeStringPresent(isLogging)) // Check for 'IsLogging' function
            ) ||
            ( // Native (WinAPI) functions
                PE.isNetObjectPresent("CheckRemoteDebuggerPresent") || PE.isNetObjectPresent("IsDebuggerPresent")
            )) && !isFrameworkComponent()
    ) {
        isAntiDebugPresent = true;
    }

    if (isAntiDebugPresent) options += (options.length != 0 ? " + " : "") + "Anti-debug";




    var isAntiDumpPresent = false;

    if (
        PE.isNetObjectPresent("VirtualProtect") && ( // from 'kernel32.dll', WinAPI
            // Need to check
            isAllNetReferencesPresent( // samples by: ConfuserEx, SkiDzEX
                references = [
                    "System.Runtime.InteropServices", // System.Runtime.InteropServices.dll
                    "Marshal", // MSIL: '*.Marshal::GetHINSTANCE( ... )'
                    "GetHINSTANCE", // MSIL: '*.Marshal::GetHINSTANCE( ... )'
                    "IntPtr", // MSIL: 'System.IntPtr'
                    "op_Explicit" // MSIL: 'System.IntPtr::op_Explicit'
                ]
            ) ||
            isAllNetReferencesPresent( // samples by: Inx Obfuscator
                references = [
                    "System.Diagnostics", // System.Diagnostics
                    "memcpy", // from 'msvcrt.dll', WinAPI
                    "IntPtr", // MSIL: 'System.IntPtr'
                    "get_MainModule", // MSIL: '*.Process::get_MainModule()'
                    "get_BaseAddress" // MSIL: '*.ProcessModule::get_BaseAddress()'
                ]
            ) ||
            isAllNetReferencesPresent( // samples by: MindLated
                references = [
                    "System.Runtime.InteropServices", // System.Runtime.InteropServices.dll
                    "Marshal", // MSIL: '*.Marshal::GetHINSTANCE( ... )'
                    "GetHINSTANCE", // MSIL: '*.Marshal::GetHINSTANCE( ... )'
                    "IntPtr", // MSIL: 'System.IntPtr'
                    "CopyBlock",
                    "InitBlock"
                ]
            )
        )
    ) {
        isAntiDumpPresent = true
    }

    if (isAntiDumpPresent) options += (options.length != 0 ? " + " : "") + "Anti-dump";




    const antiDnSpyTriggers = [
        "dnspy",
        "dnSpy",
        "DNSPY"
    ];

    var isAntiDnSpyPresent = false;

    // Iterate through anti-DnSpy triggers
    for (var l = 0; l < antiDnSpyTriggers.length; l++) {
        const dnSpyName = antiDnSpyTriggers[l];

        // Check if the signature for anti-DnSpy trigger is valid using Unicode signature mask or the original signature
        if (
            validateSignature(generateUnicodeSignatureMask(dnSpyName)) ||
            validateSignature("'" + dnSpyName + "'")
        ) {
            // Set flag indicating the presence of anti-DnSpy behavior
            isAntiDnSpyPresent = true;

            // Break the loop if a match is found
            break;
        }
    }

    if (isAntiDnSpyPresent) options += (options.length != 0 ? " + " : "") + "Anti-dnSpy";




    const sbieVariants = [
        "sbiedll.",
        "SbieDll.",
        "SBIEDLL."
    ];

    var isAntiSbiePresent = false;

    // Check if the signature for 'GetModuleHandle' is present
    if (validateSignature("'GetModuleHandle'")) { // from 'kernel32.dll') {
        // Iterate through Sandboxie variants
        for (var l = 0; l < sbieVariants.length; l++) {
            const sbieLib = sbieVariants[l];

            // Check if the signature for Sandboxie variant is valid using Unicode signature mask or the original signature
            if (
                validateSignature(generateUnicodeSignatureMask(sbieLib)) ||
                validateSignature("'" + sbieLib + "'")
            ) {
                // Set flag indicating the presence of anti-Sandboxie behavior
                isAntiSbiePresent = true;

                // Break the loop if a match is found
                break;
            }
        }
    }

    if (isAntiSbiePresent) options += (options.length != 0 ? " + " : "") + "Anti-SandBoxie";




    if (options.length != 0) {
        _setResult("protection", "Anti analysis", "Heuristic", PE.isVerbose() ? options : "");
    }
}



// .NET OpCodes for static emulations
function NetOpCodes() {
    this.add = "58"; // MSIL: 'add'
    this.sub = "59"; // MSIL: 'sub'
    this.mul = "5A"; // MSIL: 'mul'
    this.and = "5F"; // MSIL: 'and'
    this.bne_un = "40????????"; // MSIL: 'bne.un'
    this.bne_un_s = "3309"; // MSIL: 'bne.un.s'
    this.br = "38????????"; // MSIL: 'br'
    this.br_s = "2B??"; // MSIL: 'br.s'
    this.brfalse = "39????????"; // MSIL: 'brfalse'
    this.brfalse_s = "2C??"; // MSIL: 'brfalse.s'
    this.brtrue = "3A????????"
    this.call = "28????????"; // MSIL: 'call'
    this.calli = "29????????"; // MSIL: 'calli'
    this.ceq = "FE01"; // MSIL: 'ceq'
    this.div = "5B"; // MSIL: 'div'
    this.dup = "25"; // MSIL: 'dup'
    this.idelem_i = "97"; // MSIL: 'idelem.i'
    this.ldc_i4 = "20????????"; // MSIL: 'ldc.i4'
    this.ldc_i4_0 = "16"; // MSIL: 'ldc.i4.0'
    this.ldc_i4_1 = "17"; // MSIL: 'ldc.i4.1'
    this.ldc_i4_2 = "18"; // MSIL: 'ldc.i4.2'
    this.ldc_i4_3 = "19"; // MSIL: 'ldc.i4.2'
    this.ldc_i4_4 = "1A"; // MSIL: 'ldc.i4.4'
    this.ldc_i4_s = "1F??"; // MSIL: 'ldc.i4.s'
    this.ldftn = "FE06????????"; // MSIL: 'ldftn'
    this.ldloc = "FE??????"; // MSIL: 'ldloc'
    this.ldloc_0 = "06"; // MSIL: 'ldloc.0'
    this.ldloc_1 = "07"; // MSIL: 'ldloc.1'
    this.ldloc_2 = "08"; // MSIL: 'ldloc.2'
    this.ldloc_3 = "09"; // MSIL: 'ldloc.3'
    this.ldloc_s = "11??"; // MSIL: 'ldloc.s'
    this.ldsfld = "7E????????"; // MSIL: 'ldsfld'
    this.ldstr = "72????????"; // MSIL: 'ldstr'
    this.nop = "00"; // MSIL: 'nop'
    this.not = "66"; // MSIL: 'not'
    this.neg = "65"; // MSIL: 'neg'
    this.or = "60"; // MSIL: 'or'
    this.pop = "26"; // MSIL: 'pop'
    this.ret = "2A"; // MSIL: 'ret'
    this.rem_un = "5E"; // MSIL: 'rem.un'
    this.shl = "62"; // MSIL: 'shl'
    this.shr = "63"; // MSIL: 'shr'
    this.sizeof = "FE1C????????"; // MSIL: 'sizeof'
    this.stloc = "FE0E????"; // MSIL: 'stloc'
    this.stloc_0 = "0A"; // MSIL: 'stloc.0'
    this.stloc_1 = "0B"; // MSIL: 'stloc.1'
    this.stloc_2 = "0C"; // MSIL: 'stloc.2'
    this.stloc_3 = "0D"; // MSIL: 'stloc.3'
    this.stloc_s = "13??"; // MSIL: 'stloc.s'
    this.xor = "61"; // MSIL: 'xor'
    this.stelem_i1 = "9C"; // MSIL: 'stelem.i1'

    this.switch__nobody = "45"; // MSIL: 'switch'



    // setStrict sets the strict value of the opcode for substitution
    // btw I like what I do
    this.setStrict = function(opCodeMask, value) {
        // Remove spaces from opcode mask and value
        opCodeMask = removeSpaces(opCodeMask);
        value = removeSpaces(value);

        // Find the index of the special pattern "??" in the opcode mask
        var indexOfSpecialPattern = opCodeMask.indexOf("??");

        // Check if the opcode mask has a body (contains the special pattern "??")
        var isOpCodeMaskHasBody = indexOfSpecialPattern !== -1; // -1 if not found

        // Extract the opcode in hexadecimal
        var opCodeInHex = isOpCodeMaskHasBody ? opCodeMask.substr(0x00, indexOfSpecialPattern) : opCodeMask;

        // Check if the opcode mask has a body and the length of the body matches the length of the value
        if (isOpCodeMaskHasBody && opCodeMask.substr(opCodeInHex.length).length != value.length) {
            throw "The size of the input values does not match.";
        }

        // Combine the opcode in hexadecimal with the value
        return opCodeInHex + value;
    }

    // Sets the mask value to zero for the specified opcode
    this.setNullValue = function(opCodeMask) {

        if (opCodeMask.indexOf("??") === -1) {
            throw "Instruction does not have a body to overwrite the value.";
        }

        return replaceAllInString(opCodeMask, "??", "00");
    }

}


function removeSpaces(inputString) {
    return inputString.split(" ").join("");
}


function replaceAllInString(inputString, search, replacement) {
    while (inputString.indexOf(search) !== -1) {
        inputString = inputString.replace(search, replacement)
    }

    return inputString;
}



// This feature was originally intended only for .NET, but
// now partially works with Native files.
function scanForPackersAndCryptors_NET_and_Native() { // For .NET and Native apps
    var options = "";

    var isDetected = false,
        isCryptor = false;




    if (PE.isNET()) {

        var isAssemblyInvokeFound = false;

        if (isAllNetReferencesPresent( // TODO: update [!!!]
                references = [
                    "System.Reflection", // System.Reflection.dll
                    "get_EntryPoint", // MSIL: '*.Assembly::get_EntryPoint()'
                    "Assembly", // MSIL: 'System.Reflection.Assembly' from System.Reflection.dll
                    "Invoke", // MSIL: '*.MethodBase::Invoke(object, object[])'
                    "Load" // MSIL: '*.Assembly::Load(uint8[])'
                ]
            )) {
            isAssemblyInvokeFound = true;

            options = "Assembly invoke";
        }




        // Check if any class from System.Security.Cryptography namespace is used (non-full name) - for cryptors
        if (findAndMark("System.Security.Cryptography", isFullName = false) != "") {

            // Specify cryptography classes to look for
            const cryptoClasses = [
                "TripleDESCryptoServiceProvider",
                "RSACryptoServiceProvider",
                "DSACryptoServiceProvider",
                "DESCryptoServiceProvider",
                "AesCryptoServiceProvider",
                "Rfc2898DeriveBytes",
                "SHA256Managed",
                "TripleDES",
                "Rijndael",
                "ECDsaCng",
                "AesAEAD",
                "Aes192Cbc",
                "Aes256Cbc",
                "Aes128Cbc",
                "AesManaged",
                "AesCng",
                "SHA256",
                "SHA512",
                "SHA1CryptoServiceProvider",
                "SHA512CryptoServiceProvider",
                "RC2CryptoServiceProvider",
                "SHA384CryptoServiceProvider",
                // "MD5CryptoServiceProvider",
                "SHA256CryptoServiceProvider",
                "RNGCryptoServiceProvider"
            ];

            // Iterate through cryptography classes
            for (var i = 0; i < cryptoClasses.length; i++) {
                var cryptoClassSign = cryptoClasses[i],
                    result = findAndMark(
                        sign = cryptoClassSign,
                        isFullName = true
                    );

                // Check if assembly invoke is found and the cryptography class is present
                if (isAssemblyInvokeFound && result.length != 0) {
                    isCryptor = true;

                    // Add the cryptography class to options
                    options += (options.length != 0 ? " + " : "") + cryptoClassSign;

                    // Break the loop if a match is found
                    break;
                }
            }
        }




        // Check if any class from System.IO.Compression namespace is used (non-full name)
        if ((findAndMark("System.IO.Compression", isFullName = false).length != 0)) {

            // Specify compression classes to look for
            const compressionClasses = [
                "DeflateStream",
                "GZipStream"
            ];

            // Iterate through compression classes
            for (var i = 0; i < compressionClasses.length; i++) {
                var compressionClassSign = compressionClasses[i],
                    result = findAndMark(compressionClassSign, isFullName = true);

                // Check if assembly invoke is found and the compression class is present
                if (isAssemblyInvokeFound && result.length != 0) {

                    // If it's a cryptor, add the compression class to options
                    if (isCryptor) options += (options.length != 0 ? " + " : "") + compressionClassSign;

                    // Break the loop if a match is found
                    break;
                }
            }
        }

    }




    if (!PE.isDll()) {
        var entryLikePacker = false;

        const entries = [
            "53565755488D35........488DBE", // samples by: UPX (x64)
            "B8........68........64", // samples by: Petite (x32)
            "60..................E8", // samples by: Anticrack Software (x32)
            "33C08BC068........68", // samples by: ExE Pack (x32)
            "74..E9........60E8", // samples by: PE-PACK
            "EB0668........C39C", // samples by: PECompact (x32)
            "93071F05....8ED0BC", // samples by: aPack (x32)
            "60BE........8DBE", // samples by: UPX (x32)
            "B8........6A..68", // samples by: Petite (x32)
            "BE........AD8BF8", // samples by: WinUPack (x32)
            "68........9C60E8", // samples by: XComp, XPACK (x32)
            "53558BE833DBEB60", // samples by: WWPack (x32)
            "BD........C745", // samples by: kkrunchy (x32)
            "57565351524150", // samples by: mpress (x64)
            "B8........5064", // samples by: PECompact (x32)
            "8CCBBA....03DA", // // samples by: aPack (x32)
            "B8........669C", // samples by: Petite, Themida (x32)
            "8CC0FA8ED0BC", // samples by: PACKWIN (x32)
            "558BEC6AFF68", // samples by: JDPack (x32)
            "B8........60", // samples by: Petite, Themida (x32)
            "8B44240456", // samples by: ASDPack (x32)
            "1E068CC88E", // samples by: aPack (x32)
            "1E068CCBBA", // samples by: aPack (x32)
            "1E068CC88E", // samples by: aPack (x32)
            "EB..9C60E8", // samples by: PECompact (x32)
            "9C60E8CA", // samples by: Petite (x??)
            "60FCBED4", // samples by: ANDPakk (x32)
            "60EB..5D", // samples by: ASPack (x32)
            "60EB..E8", // samples by: G!X Protector
            "64FF35", // samples by: Petite (x32)
            "6033C0", // samples by: yzPack (x32)
            "669C60", // samples by: Petite (x??)
            "EB..60", // samples by: kkryptor, dePACK (x32)
            "60E8", // samples by: mpress, Packman, Pack Master, Yodas Crypter, DxPack, ASPack, MSLRH, tElock (x32)
            "6068" // samples by: BeRo, ExE Pack, AHPacker (x32)
        ];

        // Iterate through the entries to check against the entry point
        for (var e = 0; e < entries.length; e++) {
            const entryToCheck = entries[e];

            // If the entry point matches the current entry, set the flag to true and break the loop
            if (PE.compareEP(entryToCheck)) {
                entryLikePacker = true;

                // Break the loop if a match is found
                break;
            }
        }

        if (entryLikePacker) options += (options.length != 0 ? " + " : "") + "EntryPoint";




        // Check if the entry point is in the last section
        var isLastSectionEP = false;

        if (PE.getNumberOfSections() > 1) {
            // Get addresses of the last section and entry point
            var lastSectionAddress = PE.OffsetToVA(PE.getSectionFileOffset(PE.getNumberOfSections() - 1)),
                entryPointAddress = PE.getAddressOfEntryPoint();

            // Check if the entry point is greater than or equal to the last section address
            if (entryPointAddress >= lastSectionAddress) {
                isLastSectionEP = true;
            }
        }

        // If entry point is in the last section, set options to "Last section EP"
        if (isLastSectionEP) options = "Last section EP";




        // Check for strange calls if entry point is in the last section
        var isStrangeCalls = false;

        if (isLastSectionEP && getAsmOpCode(getAsmInstructionByIndex(1)) === "CALL") {
            isStrangeCalls = true;
        }

        if (isStrangeCalls) options += (options.length != 0 ? " + " : "") + "Strange call";

    }




    var isImportsLikePacker = false;

    var dbCollectionOfHashesDictionary = [
        // packers
        ["UPX", "0.59-0.93", 0, 0xd4fdcab1],
        ["UPX", "0.94-1.93", 0, 0x1d51299a],
        ["UPX", "1.94-2.03", 0, 0xb3318086],
        ["UPX", "1.94-2.03", 0, 0x3778aab9],
        ["UPX", "2.90-3.XX", 0, 0xf375ee03],
        ["UPX", "2.90-3.XX", 0, 0xf737d853],
        ["UPX", "3.91+", 0, 0xf737d853],
        ["UPX", "3.91+", -1, 0x82a048fc],
        ["UPX", "3.91+", -1, 0x554a1748],
        ["NSPACK", "", 0, 0xf375ee03],
        ["ASPack", "1.XX-2.XX", 0, 0x1272f45b],
        ["MKFPACK", "", 0, 0x42b3e7f9],
        ["MPRESS", "", 0, 0x174efb84],
        ["PACKMAN", "0.0.0.1", 0, 0x174efb84],
        ["PACKMAN", "1.0", 0, 0x69076a83],
        ["PECompact", "0.90-0.91", -1, 0xbea416d1],
        ["PECompact", "0.92-0.94", -1, 0x93312c2e],
        ["PECompact", "0.97-0.971b", -1, 0xe6aa8495],
        ["PECompact", "0.975-1.10b3", -1, 0x29188619],
        ["PECompact", "1.10b7-1.34", -1, 0xe4c11305],
        ["PECompact", "1.30-1.40", 0, 0x9b3305ed],
        ["PECompact", "1.40-1.84", 0, 0xcc5b2a3c],
        ["PECompact", "2.40-3.XX", 0, 0x2652ce4f],
        ["PECompact", "2.40-3.XX", -1, 0xdb8fbb75],
        ["EXE32PACK", "1.3X-1.4X", 0, 0x174efb84],
        ["tElock", "1.0", -1, 0x051946f7],
        ["JDPACK", "2.00", 0, 0xc002db0e],
        ["CRINKLER", "", 0, 0x0b0e1fbf],
        ["WinUpack", "", -1, 0x29188619],
        ["YodasCrypter", "1.X", -1, 0x1303a51b],
        ["XComp", "0.97-0.98", -1, 0xea1e66e4],
        ["XPack", "0.97-0.98", -1, 0x2ac44dd2],
        ["kkrunchy", "", -1, 0x29188619],
        ["ANDPakk2", "0.18", -1, 0x29188619],
        // protectors
        ["ASProtect", "1.XX-2.XX", 0, 0x1272f45b],
        ["SHRINKER", "3.2", 0, 0xb2a64858],
        ["SHRINKER", "3.2", 0, 0x158af2d0],
        ["SHRINKER", "3.2", 0, 0x49e8aa1f],
        ["SHRINKER", "3.5", 0, 0xe9ea0851],
        ["SHRINKER", "3.5", 0, 0x3344b95d],
        ["SHRINKER", "3.5", 0, 0x586088f3],
        ["Enigma", "1.00-3.60", 0, 0xc002db0e],
        ["Enigma", "2.XX", 0, 0xdd92de10],
        ["Enigma", "3.70+", 0, 0xd04c7a50],
        ["PCGUARD", "5.04-5.05", 0, 0x5a169c7a],
        ["PCGUARD", "5.04-5.05", 0, 0x0b0b2965],
        ["eXPressor", "1.2", -1, 0x66b35c6e],
        ["eXPressor", "1.2", -1, 0x32f4466c],
        ["eXPressor", "1.3", -1, 0x921d0280],
        ["eXPressor", "1.3", -1, 0xf51eba68],
        ["eXPressor", "1.3", -1, 0xbc84ce09],
        ["eXPressor", "1.4.5.X", 0, 0x427816ab],
        ["eXPressor", "1.4.5.X", -1, 0x3c705cae],
        ["eXPressor", "1.4.5.X", -1, 0x4d02e093],
        ["eXPressor", "1.4.5.X", -1, 0x958a9ea2], // VB6
        ["eXPressor", "1.4.5.X", 1, 0x0c16df2d],
        ["eXPressor", "1.5.0.X", -1, 0x7ababb5a],
        ["eXPressor", "1.5.0.X", -1, 0x95ca15e4],
        ["eXPressor", "1.5.0.X", -1, 0xbd41da20],
        ["eXPressor", "1.6", -1, 0xca58fa0c],
        ["eXPressor", "1.6.1", -1, 0x48ffd359],
        ["VMProtect", "1.70", -1, 0x1ff3103f],
        // ["VMProtect", "1.70", -1, 0x0c16df2d],
        ["VMProtect", "2.0.3-2.13", -1, 0x9d12b153],
        ["VMProtect", "3.0.0", -1, 0x1e5500c1],
        ["VMProtect", "3.0.9", -1, 0xc5fb6a4b],
        ["VMProtect", "3.2.0-3.5.0", -1, 0x5caa99c7],
        ["YodasProtector", "1.0b", -1, 0x1303a51b]
    ];

    const importValidatingResult = validateImportHashes(dbCollectionOfHashesDictionary);

    if (importValidatingResult != null) {
        isImportsLikePacker = true;
    }

    // Clean up: release the dictionary
    dbCollectionOfHashesDictionary = undefined;

    if (isImportsLikePacker) options += (options.length != 0 ? " + " : "") + "Imports like " + importValidatingResult[0] + (importValidatingResult[1].length != 0 ? " (v" + importValidatingResult[1] + ")" : "");




    var isSectionNameLikePacker = false;

    var dbCollectionOfSectionNamesDictionary = [
        ["UPX", "", "UPX1"],
        ["UPX", "", "UPX2"],
        ["UPX", "", "UPX3"],
        ["VMProtect", "", ".vmp"],
        ["VMProtect", "", ".vmp0"],
        ["VMProtect", "", ".vmp1"],
        ["VMProtect", "", ".vmp2"],
        ["VMProtect", "", ".vmp3"],
        ["ASPack", "1.08-2.XX", ".adata"],
        ["ASPack", "2.XX", ".aspack"],
        ["Petite", "", ".petite"],
        ["Petite", "", "petite"],
        ["Enigma", "", ".enigma1"],
        ["Enigma", "", ".enigma2"],
        [".NET Reactor", "2.XX", ".reacto"],
        ["Themida", "3.X", ".imports"],
        ["Themida", "3.X", ".themida"],
        ["Themida", "3.X", ".winlice"],
        ["Themida", "3.X", ".loadcon"],
        ["ASM Guard", "2.XX", "ASMGUARD"],
        ["ASM Guard", "2.XX", ".asmg"],
        ["tElock", "", "UPX!"], // ???
        ["YodasProtector", "1.0b", ".yP"],
        ["YodasCrypter", "1.X", "yC"],
        ["MPRESS", "", ".MPRESS0"],
        ["MPRESS", "", ".MPRESS1"],
        ["DxPack", "1.0", "coderpub"]
    ];

    const sectionNamesValidatingResult = validateSectionNames(dbCollectionOfSectionNamesDictionary);

    if (sectionNamesValidatingResult != null) {
        isSectionNameLikePacker = true;
    }

    // Clean up: release the dictionary
    dbCollectionOfSectionNamesDictionary = undefined;

    if (isSectionNameLikePacker) options += (options.length != 0 ? " + " : "") + "Sections like " + sectionNamesValidatingResult[0] + (sectionNamesValidatingResult[1].length != 0 ? " (v" + sectionNamesValidatingResult[1] + ")" : "");




    // Check if there is a collision in sections
    var isCollisionInSectionsPresent = false;

    // Get section name collision between "0" and "1"
    const sectionNameCollision = PE.getSectionNameCollision("0", "1");

    // Check if there is a collision
    if (sectionNameCollision.length != 0) {
        isCollisionInSectionsPresent = true;
    }

    // If there is a collision, add "Sections collision" to options
    if (isCollisionInSectionsPresent) options += (options.length != 0 ? " + " : "") + "Sections collision (" + sectionNameCollision + ")";




    // Check if there are repeating section names
    var isCollisionInSectionsPresent = false;

    // Dictionary to track encountered section names
    var sectionNamesDictionary = {};

    // Iterate through sections to check for collisions
    for (var i = 0; i < PE.getNumberOfSections(); i++) {
        const sectionName = PE.getSectionName(i);

        // If section name is already encountered, set collision flag and break
        if (sectionNamesDictionary[sectionName]) {
            isCollisionInSectionsPresent = true;

            // Break the loop if a match is found
            break;
        } else {
            sectionNamesDictionary[sectionName] = true;
        }
    }

    // Clean up: release the dictionary
    sectionNamesDictionary = undefined;

    if (isCollisionInSectionsPresent) options += (options.length != 0 ? " + " : "") + "Section names repeating";




    // Check if the first instruction at entry point starts with a stack operation
    var isStartsWithStackOperation = false;

    // Get the opcode of the first instruction at entry point
    const firstEpAsmOpCode = getFirstEpAsmOpCode();

    // Switch statement to check for specific stack operation opcodes
    switch (firstEpAsmOpCode) {
        case "PUSHAL":
        case "PUSHA":
        case "PUSHF":
        case "POPA":
            isStartsWithStackOperation = true;
    }

    if (isStartsWithStackOperation) options += (options.length != 0 ? " + " : "") + "\"" + firstEpAsmOpCode.toLowerCase() + "\" at EP";




    // Many not-so-smart virus writers use base64 to pack
    // or hide malicious code, but do not realize that this
    // is very easily detected by heuristic analysis.

    const signaturesVariants = [
        "TVoAAAAAA", // MZ ~[00 00 00 00 00]
        "TVqQAA" // MZ ~[90 00 03]
    ];

    var isPackedPeDetected = false;

    // Iterate through signature variants
    for (var s = 0; s < signaturesVariants.length; s++) {
        const trigger = signaturesVariants[s];

        // Check if the signature is valid using Unicode signature mask or the original signature
        if (
            validateSignature(generateUnicodeSignatureMask(trigger) + "00") || validateSignature("'" + trigger + "'")
        ) {
            isCryptor = true;
            isPackedPeDetected = true;

            // Break the loop if a match is found
            break;
        }
    }

    if (isPackedPeDetected) options += (options.length != 0 ? " + " : "") + "Base64 payload";




    var isMzSignatureDetected = false;

    if (PE.isOverlayPresent() &&
        PE.getOverlaySize() >= 100 &&
        PE.compareOverlay("'MZ'")) {
        isMzSignatureDetected = true;
    }

    if (isMzSignatureDetected) options += (options.length != 0 ? " + " : "") + "PE in overlay";




    // Check for a strange overlay in the PE file
    var hasStrangeOverlay = false;

    // Conditions to check for a strange overlay
    if (!isMzSignatureDetected && !PE.isSigned() && PE.isOverlayPresent()) {
        if (
            PE.getOverlaySize() > 150 && PE.calculateEntropy(PE.getOverlayOffset(), PE.getOverlaySize()) > 7 ||
            PE.getOverlaySize() > (PE.getSize() - PE.getOverlaySize())
        ) {
            hasStrangeOverlay = true;
        }
    }

    if (hasStrangeOverlay) options += (options.length != 0 ? " + " : "") + "Strange overlay";




    // Flag to indicate high entropy
    var isHighEntropy = false;

    // Checks for high entropy (ignore overlay)
    if (!(PE.isDll() && (PE.isSectionNamePresent(".rdata") || PE.isSectionNamePresent(".rsrc"))) && // .dll with resources
        PE.calculateEntropy(0x00, PE.getSize() - PE.getOverlaySize()) > 7.2) {
        isHighEntropy = true;
    }

    if (isHighEntropy) options += (options.length != 0 ? " + " : "") + "High entropy";




    if (options.length != 0) isDetected = true;


    if (isDetected) {
        var detectedType = isCryptor ? "cryptor" : "packer";

        _setResult(detectedType, (isCryptor ? "Cryptor" : "Packer") + " detected", "Heuristic", PE.isVerbose() ? options : "");
    }
}


function isVbNetStandartLibraryPresent() {
    return PE.isNetObjectPresent("Microsoft.VisualBasic");
}


// Check if the file is a .NET Framework component
function isFrameworkComponent() {
    return PE.isNET() && PE.isDll() && PE.isSigned() && PE.findSignature(PE.getOverlayOffset(), 300, "'Microsoft Corporation'") != -1;
}

// Validate the presence of a signature in the file
function validateSignature(sign) {
    return PE.findSignature(0x00, PE.getSize() - PE.getOverlaySize(), sign) != -1;
}


// Function to generate Unicode signature mask from an input string
// "test" -> "'t'00'e'00's'00't'"

function generateUnicodeSignatureMask(inputString) {
    var output = "";

    // Iterate through each character in the input string
    for (var c = 0; c < inputString.length; c++) {
        // Append the Unicode representation of the character to the output
        output += (c != 0 ? "00" : "") + "'" + inputString[c] + "'";
    }

    // Return the generated Unicode signature mask
    return output;
}


// Function to check if all specified .NET references are missing

function isAllNetReferencesMissing(references) {
    // Iterate through the array of .NET references
    for (var i = 0; i < references.length; i++) {
        // Get the current reference
        const ref = references[i];

        // If the .NET object corresponding to the reference is present, return false
        if (PE.isNetObjectPresent(ref)) {
            return false;
        }
    }

    // If all .NET references are missing, return true
    return true;
}


// Function to check if all specified .NET references are present

function isAllNetReferencesPresent(references) {
    // Iterate through the array of .NET references
    for (var i = 0; i < references.length; i++) {
        // Get the current reference
        const ref = references[i];

        // If the .NET object corresponding to the reference is not present, return false
        if (!PE.isNetObjectPresent(ref)) {
            return false;
        }
    }

    // If all .NET references are present, return true
    return true;
}


// "isFullName = true"  = 00'sign'00
// "isFullName = false" = 00'sign'

function findAndMark(sign, isFullName) {
    if (PE.isSignatureInSectionPresent(0,
            ("00'" + sign + "'") + // 00'string
            (isFullName ? "00" : ""))) { // ... '00
        return sign;
    }
    return "";
}



function scanForObfuscations_Native() {
    var options = "";

    var isDetected = false;




    // Check for section names containing forbidden characters
    var strangeSections = false;

    // Define forbidden characters
    const badChars = '-=+~!@#$%^&*()"№;%:?*():;,/\\|\'`<> ';

    // Iterate through sections and characters to check for forbidden characters
    for (var i = 0; i != PE.getNumberOfSections(); i++) {
        var sectionName = PE.getSectionName(i);

        if (sectionName.length === 0 || sectionName[0] === " ") {
            strangeSections = true;

            break;
        }

        // If section name is too short, skip to the next iteration
        if (sectionName.length > 1) {
            for (var x = 0; x < sectionName.length; x++) {
                for (var d = 0; d < badChars.length; d++) {
                    // If forbidden character is found, set flag and break
                    if (sectionName[x] === badChars[d]) {
                        strangeSections = true;

                        // Break the loop if a match is found
                        break;
                    }
                }
            }
        }


    }

    // If section names contain forbidden characters, add to options
    if (strangeSections) options += (options.length != 0 ? " + " : "") + "Strange sections";




    // Check for DOS header in the PE file
    var isDosMissing = false,
        isCustomDosPresent = false;

    // If DOS stub size is 0, set flag for missing DOS
    if (PE.getDosStubSize() === 0) {
        isDosMissing = true;
    } else {
        // Define messages to check for custom DOS
        const messages = [
            "This program cannot be run in DOS mode.",
            "This program must be run under Win32",
            "This program must be run under Win64",
            "This program requires Win32"
        ];

        isCustomDosPresent = true;

        // Iterate through messages to check for custom DOS
        for (var d = 0; d < messages.length; d++) {
            // If signature is found in DOS stub, set flag to false and break
            if (PE.findSignature(PE.getDosStubOffset(), PE.getDosStubSize(), "'" + messages[d] + "'") != -1) {
                isCustomDosPresent = false;

                // Break the loop if a match is found
                break;
            }
        }
    }

    // Add appropriate option based on DOS presence
    if (isDosMissing) options += (options.length != 0 ? " + " : "") + "Missing DOS";
    else if (isCustomDosPresent) options += (options.length != 0 ? " + " : "") + "Custom DOS";




    // Check if the first instruction at the entry point is "INT 3" (CC or CD 03)
    var isStartsWithBreakPoint = false;

    // If the first instruction is a breakpoint, set the flag to true
    if (getFirstEpAsmInstruction() === "INT 3") // int 3 (CC or CD 03)
        isStartsWithBreakPoint = true;

    if (isStartsWithBreakPoint) options += (options.length != 0 ? " + " : "") + "Suspicious break at EP";




    // Check if the entry point starts with NOP
    var isStartsWithNop = false;

    // Condition to check if the first instruction is NOP
    if (getFirstEpAsmInstruction() === "NOP") { // nop (90)
        isStartsWithNop = true;
    }




    // Check if NOP padding is present at the entry point
    var isNopPaddingPresent = false;

    // Condition to check for NOP padding
    if (!isStartsWithNop && getEpAsmPattern(onlyOpCodes = true, numberOf = 5).indexOf(getInstructionsAsmPattern(["NOP", "NOP"])) !== -1) {
        isNopPaddingPresent = true;
    }

    if (isNopPaddingPresent) options += (options.length != 0 ? " + " : "") + "Nop EP padding";




    // ASM Guard fake signatures
    if (PE.isSectionNamePresent(".asmg") || PE.isSectionNamePresent("ASMGUARD")) {
        for (var f = 0; f < 3; f++)
            _removeResult("packer", ["UPX", "MPRESS", "EP:MPRESS"][f]);
    }


    if (options.length != 0) isDetected = true;


    if (isDetected) {
        _setResult("protection", "Generic", "Heuristic", PE.isVerbose() ? options : "");
    }
}


const _patternSplitter = "|";


// Makes it possible to disassemble the entry point code and output
// a specified number of instructions through a splitter (_patternSplitter)

function getEpAsmPattern(onlyOpCodes, numberOf) {
    // Initialize the result with a pattern splitter
    var result = _patternSplitter;

    // Get the address of the entry point
    var disasmAddress = PE.getAddressOfEntryPoint();

    // Iterate through instructions up to the specified number
    for (var i = 0; i < numberOf; i++) {

        // Update the address to the next instruction if not the first iteration
        if (i >= 1) {
            disasmAddress = PE.getDisasmNextAddress(disasmAddress);
        }

        // Get the assembly instruction at the current address
        const asmInstruction = PE.getDisasmString(disasmAddress);

        // Append either the opcode or the full instruction to the result
        result += (
            onlyOpCodes ?
            getAsmOpCode(asmInstruction) : // "MOV"
            asmInstruction // "MOV EAX, 4"
        ) + _patternSplitter;
    }

    // Return the generated assembly pattern
    return result;
}


// Function to get assembly instruction by index

function getAsmInstructionByIndex(index) {
    // Get the address of the entry point
    var disasmAddress = PE.getAddressOfEntryPoint();

    // Iterate through instructions until the specified index is reached
    for (var i = 0; i <= index; i++) {

        // Update the address to the next instruction if not the first iteration
        if (i >= 1) {
            disasmAddress = PE.getDisasmNextAddress(disasmAddress);
        }

        // If the current iteration matches the specified index, retrieve the instruction
        if (i === index) {
            const asmInstruction = PE.getDisasmString(disasmAddress);

            // Return the assembly instruction
            return asmInstruction;
        }
    }
}


// Makes it possible to get a subpattern to search for instructions in a
// pattern divided through a separator (_patternSplitter)
//
// like "|OPCODE1|OPCODE2|OPCODE3|".indexOf("|OPCODE2|")
// but  "|OPCODE1|OPCODE2|OPCODE3|".indexOf(getInstructionsAsmPattern("OPCODE2"))
//  or
// like "|OPCODE1|OPCODE2|OPCODE3|".indexOf("|OPCODE2|OPCODE3|")
// but  "|OPCODE1|OPCODE2|OPCODE3|".indexOf(getInstructionsAsmPattern(["OPCODE2", "OPCODE3"]))

function getInstructionsAsmPattern(instruction) {
    return _patternSplitter +
        (
            Array.isArray(instruction) ?
            instruction.join(_patternSplitter) :
            instruction
        ) +
        _patternSplitter;
}


function getFirstEpAsmInstruction() {
    return PE.getDisasmString(PE.OffsetToVA(PE.getEntryPointOffset()));
}


function getAsmOpCode(instruction) {
    return instruction.indexOf(" ") !== -1 ? instruction.split(" ")[0] : instruction;
}


// Returns only the name of the opcode used, without arguments

function getFirstEpAsmOpCode() {
    return getAsmOpCode(getFirstEpAsmInstruction());
}


function validateImportHashes(dbCollection) {
    for (var i = 0; i < dbCollection.length; i++) {
        const currentIndex = i,
            currentArrayPattern = dbCollection[currentIndex];

        const name = currentArrayPattern[0],
            version = currentArrayPattern[1],
            hash1 = currentArrayPattern[2],
            hash2 = currentArrayPattern[3];

        if (PE.isImportPositionHashPresent(hash1, hash2)) {
            return currentArrayPattern;
        }
    }

    return null;
}


function validateSectionNames(dbCollection) {
    for (var i = 0; i < dbCollection.length; i++) {
        const currentIndex = i,
            currentArrayPattern = dbCollection[currentIndex];

        const name = currentArrayPattern[0],
            version = currentArrayPattern[1],
            sectionName = currentArrayPattern[2];

        if (PE.isSectionNamePresent(sectionName)) {
            return currentArrayPattern;
        }
    }

    return null;
}


// Every time I start writing bad code I get hit with my head on the keybofewuihdsowefjfqodgsa79dowqhdsioefurogrwhuoguethuhofrwyi