const logType = { warning: -2, about: -1, nothing: 0, any: 1, net: 2 }, heurLabel = "HEUR"; var lastOffsetDetected = "0x00"; const detect = main; function main() { if (stubForWrongEnvironment()) return null; // 'PE' is undefined if (stubForLegacyEngines()) return null; // old DIE version if (PE.isHeuristicScan()) { if (!PE.isVerbose()) { log(logType.warning, "To get the full heuristic scan result use '--verbose'"); } log(logType.about, "Generic Heuristic Analysis by DosX (@DosX_dev)"); log(logType.nothing, "Scanning has begun!"); if (PE.isNET()) { scanForObfuscations_NET(); scanForAntiAnalysis_NET(); } else { scanForObfuscations_Native(); } scanForPackersAndCryptors_NET_and_Native(); scanForLicensingSystems_NET_and_Native(); if (PE.isVerbose()) { scanForLanguages_NET_and_Native(); } // >> Happy end << log(logType.nothing, "Scan completed."); // scanForMaciliousCode_NET_and_Native(); } else { log(logType.warning, "Heuristic scan is disabled. Use '--heuristicscan' to enable"); } } function stubForLegacyEngines() { if (typeof PE.isNetGlobalCctorPresent === 'undefined') { stdout(">>> Update DIE Engine to 3.10 and higher for using Heuristic-analyser by DosX <<<"); return true; } return false; } function stubForWrongEnvironment() { if (typeof PE === 'undefined') { stdout(">>> Wrong environment! 'PE' is undefined. Check DIE-engine for correct installation <<<"); return true; } return false; } function stdout(stringToOut) { if (typeof console === 'object') { console.warn(stringToOut); } else if (typeof File === 'object') { _setResult("~Warning", stringToOut, String(), String()); } else { throw stringToOut; } } function scanForObfuscations_NET() { var options = String(); var isDetected = Boolean(); const isVbNet = isVbNetStandartLibraryPresent(); var isEntryPointModified = false; const vbNetEntries = [ "Main", "main", "MAIN", "MyApplication" ], defaultEntries = [ // like MSIL, C#, C++ NET etc "Main", "main", // F# entry "
$", // For programs with top-level operators (C#) "mainCRTStartup", // C++ CLR .NET (CLI) "wWinMainCRTStartup" // C++ CLR .NET (GUI) ] if (!PE.isDll()) { if (isVbNet) { if (isAllNetReferencesMissing(vbNetEntries)) { isEntryPointModified = true; } } else if (isAllNetReferencesMissing(defaultEntries)) { isEntryPointModified = true; } if (isEntryPointModified) { log(logType.net, "No 'Main' method found") } } if (isEntryPointModified) options = "Modified EP"; var isNetCctorPresent = false; if (PE.isNetGlobalCctorPresent() && !isClrNET()) { isNetCctorPresent = true; } if (isNetCctorPresent) options += (options.length != 0 ? " + " : String()) + "CLR constructor"; 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 (not for CLR apps) if (!PE.isDll() && PE.getNumberOfSections() > 1 && !isClrNET()) { // 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 ? " + " : String()) + "Strange EP position"; var isNativeEntryPointModified = false; if (!PE.isDll() && !isClrNET()) { // not for CLR apps if (!PE.is64()) { // FF2500????00: jmp dword ptr [ ... ] const firstOpCode = getFirstEpAsmOpCode(); if (firstOpCode !== "JMP") { if (PE.VAToOffset(PE.getAddressOfEntryPoint()) != -1) { log(logType.net, "Very strange EP pattern: " + getEpAsmPattern(onlyOpCodes = true, numberOf = 4).split("|").join(" .. ")); } else { log(logType.net, "No native entry point") } isNativeEntryPointModified = true; } } else { // AMD64 if (PE.VAToOffset(PE.getAddressOfEntryPoint()) != 0x00) { isNativeEntryPointModified = true; } } } if (isNativeEntryPointModified) options += (options.length != 0 ? " + " : String()) + "Modified native EP"; // Check if the specified DOS message is not found in the DOS stub var isDosMessageModified = false; if (PE.findSignature(PE.getDosStubOffset(), PE.getDosStubSize(), "'This program cannot be run in DOS mode.'") === -1) { log(logType.net, "DOS-stub modified!"); isDosMessageModified = true; } if (isDosMessageModified) options += (options.length != 0 ? " + " : String()) + "Modified DOS"; // Check PE image for strange sections var strangeSections = false; const badChars = '_-=+~!@#$%^&*()"№;%:?*():;,/\\|\'`<>.0123456789'; // Very very bad! if (PE.getNumberOfSections() > (!isClrNET() ? 6 : 10) || !PE.isSectionNamePresent(".text")) { strangeSections = true; } else { // Iterate through each section for (var i = 0; i < PE.getNumberOfSections() && !strangeSections; i++) { const 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 "." // Check if the character is in the badChars list for (var d = 0; d < badChars.length && !strangeSections; d++) { if (sectionName.substr(1, sectionName.length).indexOf(badChars[d]) !== -1) { strangeSections = true; } } } } if (strangeSections) options += (options.length != 0 ? " + " : String()) + "Strange sections"; const opCodes = new NetOpCodes(); // A popular way to obfuscate numbers/booleans var isIntConfusionPresent = false; const intConfusionXorPattern = opCodes.ldc_i4 + opCodes.ldc_i4 + opCodes.xor + opCodes.ldc_i4; if (validateNetByteCode(intConfusionXorPattern)) { if (validateNetByteCode( // samples by: Inx Obfuscator intConfusionXorPattern + (opCodes.bne_un_s + opCodes.ldc_i4_2 + opCodes.stloc_0 + opCodes.sizeof + opCodes.add) ) || validateNetByteCode( // samples by: MindLated, NetShield intConfusionXorPattern + (opCodes.bne_un + opCodes.ldc_i4 + opCodes.stloc + opCodes.sizeof + opCodes.add) ) || validateNetByteCode( // samples by: VavilonProtect intConfusionXorPattern + (opCodes.bne_un + opCodes.ldc_i4_2 + opCodes.stloc_s + opCodes.sizeof + opCodes.add) ) ) { log(logType.net, "Int confusion detected! Offset: " + lastOffsetDetected); isIntConfusionPresent = true; } } if (isIntConfusionPresent) options += (options.length != 0 ? " + " : String()) + "Int confusion"; // Virtualization is a method of protection in which some code segments are rewritten into instructions inherent in the built-in virtual machine and executed by it 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' ] ) && ( PE.isNetObjectPresent("Ldarg_0") || // MSIL: 'System.Reflection.Emit.OpCodes.Ldarg_0' PE.isNetObjectPresent("CreateDelegate") // MSIL: 'System.Delegate.CreateDelegate' ) && !isFrameworkComponent() ) { isVirtualizationPresent = true; } if (isVirtualizationPresent) options += (options.length != 0 ? " + " : String()) + "Virtualization"; // Hiding calls using delegate tricks var callsEncrypt = false; if ( isAllNetReferencesPresent( references = [ "GetTypeFromHandle", // MSIL: 'System.Type::GetTypeFromHandle( ... )' "BinaryReader", // MSIL: 'System.IO.BinaryReader' "CreateDelegate", // MSIL: '[Delegate].CreateDelegate' "MakeByRefType", // MSIL: 'System.Type::MakeByRefType()' "DynamicMethod" // MSIL: 'System.Reflection.Emit.DynamicMethod' ] ) && !isFrameworkComponent() ) { callsEncrypt = true; } if (callsEncrypt) options += (options.length != 0 ? " + " : String()) + "Calls encrypt"; // https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.suppressildasmattribute var isAntiIldasmPresent = false; if (validateNetObject("SuppressIldasmAttribute")) { isAntiIldasmPresent = true; } if (isAntiIldasmPresent) options += (options.length != 0 ? " + " : String()) + "Anti-ILDASM"; // Anti de4dot via inheritance var isAntiDe4dotPresent = false; if ( validateSignature("'Form'******00'Form'******00'Form'******00") || // samples by: NetShield validateNetObject("Form0") // samples by: MindLated ) { isAntiDe4dotPresent = true; } if (isAntiDe4dotPresent) options += (options.length != 0 ? " + " : String()) + "Anti-de4dot"; // An obfuscation method in which calli is used instead of regular calls var isCalliInvokesPresent = false; if (validateNetByteCode( // samples by: MindLated opCodes.setStrict(opCodes.ldftn, "** ?? 00 0A") + opCodes.setStrict(opCodes.calli, "** 00 00 11") ) || validateNetByteCode( // samples by: ArmDot, DarksProtector opCodes.idelem_i + opCodes.setStrict(opCodes.calli, "** 00 00 11") )) { log(logType.net, "Calli invokes detected! Offset: " + lastOffsetDetected); isCalliInvokesPresent = true; } if (isCalliInvokesPresent) options += (options.length != 0 ? " + " : String()) + "Calli invokes"; var isLdftnPointersPresent = false; if (validateNetByteCode( opCodes.nop + opCodes.setStrict(opCodes.ldftn, "** 00 00 06") + opCodes.stelem_i ) || validateNetByteCode( opCodes.nop + opCodes.setStrict(opCodes.ldftn, "** 00 00 0A") + opCodes.stelem_i ) || validateNetByteCode( // samples by: Quantum (private) opCodes.setStrict(opCodes.ldftn, "** 00 00 0A") + opCodes.setStrict(opCodes.calli, "** 00 00 11") )) { log(logType.net, "Ldftn pointers method-obfuscation detected! Offset: " + lastOffsetDetected); isLdftnPointersPresent = true; } if (isLdftnPointersPresent) options += (options.length != 0 ? " + " : String()) + "Ldftn pointers"; // Turns the code into spaghetti by splitting it into blocks that it executes depending on the situation var isCtrlFlowPresent = false; if (validateNetByteCode( // 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 ) || validateNetByteCode( // 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 ) || validateNetByteCode( // samples by: .NET Reactor (v6.9.8) opCodes.setStrict(opCodes.ldc_i4, "00 00 00 00") + opCodes.br + opCodes.br + opCodes.ldloc ) || validateNetByteCode( // 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 ) || validateNetByteCode( // samples by: .NET Reactor opCodes.setNullValue(opCodes.ldc_i4) + opCodes.ldsfld + opCodes.brtrue + opCodes.pop + opCodes.ldc_i4 + opCodes.br ) || validateNetByteCode( // samples by: .NET Reactor (legacy~~) opCodes.stloc + opCodes.ldloc + opCodes.setStrict(opCodes.switch__nobody, "** ** ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 00") + opCodes.ldc_i4 + opCodes.br ) || validateNetByteCode( // 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 ) || validateNetByteCode( // 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 ) || validateNetByteCode( // 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 ) || validateNetByteCode( // samples by: ConfuserEx (Beds mod) opCodes.ldc_i4 + opCodes.ldc_i4 + opCodes._unknown + opCodes.ldc_i4 + opCodes._unknown + opCodes.stloc_0 + opCodes.nop + opCodes.ldloc_0 + opCodes.ldc_i4 + opCodes.ldc_i4 + opCodes._unknown + opCodes.ldc_i4 + opCodes._unknown + opCodes.ceq + opCodes.brfalse_s ) || validateNetByteCode( // samples by: DotNetPatcher opCodes.setStrict(opCodes.stloc_s, "05") + opCodes.nop + opCodes.ldloc_s + opCodes._unknown + opCodes.ceq + opCodes.brfalse_s + opCodes._unknown + opCodes.setStrict(opCodes.stloc_s, "05") + opCodes.nop + opCodes.ldloc_s + opCodes._unknown + opCodes.ceq + opCodes.brfalse_s ) || validateNetByteCode( // samples by: VMProtect opCodes.ldloc_0 + opCodes.setStrict(opCodes.ldc_i4, "?? ** ** **") + opCodes._unknown + opCodes.stloc_0 + opCodes.ldloc_0 + opCodes.setStrict(opCodes.ldc_i4, "?? ** ** **") + opCodes.xor + opCodes.stloc_0 ) || validateNetByteCode( // samples by: VMProtect opCodes.setStrict(opCodes.ldc_i4, "?? ** ** **") + opCodes._unknown + opCodes.stloc_0 + opCodes.setStrict(opCodes.ldftn, "** ?? ?? ??") ) ) { log(logType.net, "Control flow obfuscation detected! Offset: " + lastOffsetDetected); isCtrlFlowPresent = true; } if (isCtrlFlowPresent) options += (options.length != 0 ? " + " : String()) + "Ctrl flow"; const afterCtorOffset = PE.findSignature(PE.getDosStubOffset() + PE.getDosStubSize(), PE.getSize() - PE.getOverlaySize(), "''00**") + 8; // Indicates that the file uses short object names. Typically this indicates the presence of an obfuscation // There are two ways to detect short names - find one-letter objects or just use signature mask at Ctor offset :D var isShortNamesPresent = false; if (PE.compare("00**00**00", afterCtorOffset) || PE.compare("00****00****00****00", afterCtorOffset) || PE.compare("00******00******00******00", afterCtorOffset) || PE.compare("00********00********00********00", afterCtorOffset) || PE.compare("00****00****00", afterCtorOffset) || PE.compare("00****00**00", afterCtorOffset) || PE.compare("00**00****00", afterCtorOffset)) { log(logType.net, "Short names detected! (mask)"); isShortNamesPresent = true; } if (!isShortNamesPresent && PE.compare("00**00", afterCtorOffset)) { // second way :D var shortNamesFound = 0; const chars = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; for (var i = 1; i < chars.length && !isShortNamesPresent; i++) { if (PE.isNetObjectPresent(chars[i])) { shortNamesFound++; log(logType.net, "Short name found: '" + chars[i] + "' (" + shortNamesFound + "/20)"); } if (shortNamesFound === 20) { isShortNamesPresent = true; } } } if (isShortNamesPresent) options += (options.length != 0 ? " + " : String()) + "Short names"; var badNamings = false; var buffer = ""; // get next 0x12c bytes after .ctor for (var i = 0; i < 0x12c; i++) { var currentByte = PE.readByte(afterCtorOffset + i).toString(16); if (currentByte === '0') currentByte += '0'; buffer += currentByte; buffer += ' '; } var bufferString = String(); // 0x00 to [0x20, 0x20, 0x20] const bufferArray = replaceAllInString(buffer, '00 ', '20 20 20 ').split(' '); // buffer to a string for (var i = 0; i < bufferArray.length; i++) bufferString += String.fromCharCode(parseInt(bufferArray[i], 16)); const patternsToExplore = bufferString.split(" "); var strangePatternsCounter = 0; for (var i = 0; i < patternsToExplore.length && !badNamings; i++) { const currentStringPattern = patternsToExplore[i]; if (currentStringPattern.indexOf("<") === -1 && currentStringPattern.match(/^(?=\d|[a-z])(?=.*[a-z]\d[a-z])(?=(?:.*\d){4,})/i)) strangePatternsCounter++; if (strangePatternsCounter > 4) badNamings = true; } if (badNamings) options += (options.length != 0 ? " + " : String()) + "Bad namings"; // AntiTamper protects the file from modification var isAntiTamperPresent = false; if ( validateNetUnicodeString(" is tampered.") || // samples by: .NET Reactor validateNetUnicodeString("ping 127.0.0.1 > nul") || // samples by: ConfuserEx (Trinity, SkiDzEx like) validateNetUnicodeString("/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del \"") || // samples by: MindLated validateNetUnicodeString( // samples by: ConfuserEx opCodes.ldloc_s + opCodes.ldc_i4_0 + opCodes.ldloc_s + opCodes.ldc_i4_0 + opCodes.ldelem_u4 + opCodes.ldloc_s + opCodes.ldc_i4_0 + opCodes.ldelem_u4 + opCodes._unknown + opCodes.stelem_i4 + opCodes.ldloc_s + opCodes.ldc_i4_1 + opCodes.ldloc_s + opCodes.ldc_i4_1 + opCodes.ldelem_u4 + opCodes.ldloc_s + opCodes.ldc_i4_1 + opCodes.ldelem_u4 + opCodes._unknown + opCodes.stelem_i4 + opCodes.ldloc_s + opCodes.ldc_i4_2 + opCodes.ldloc_s + opCodes.ldc_i4_2 + opCodes.ldelem_u4 + opCodes.ldloc_s + opCodes.ldc_i4_2 + opCodes.ldelem_u4 + opCodes._unknown + opCodes.stelem_i4 + opCodes.ldloc_s + opCodes.ldc_i4_3 + opCodes.ldloc_s + opCodes.ldc_i4_3 + opCodes.ldelem_u4 + opCodes.ldloc_s + opCodes.ldc_i4_3 + opCodes.ldelem_u4 + opCodes._unknown + opCodes.stelem_i4 + opCodes.ldloc_s + opCodes.ldc_i4_4 + opCodes.ldloc_s + opCodes.ldc_i4_4 + opCodes.ldelem_u4 + opCodes.ldloc_s + opCodes.ldc_i4_4 + opCodes.ldelem_u4 + opCodes._unknown + opCodes.stelem_i4 ) || validateNetByteCode( // samples: ConfuserEx (Beds mod, private) opCodes.ldloc_s + opCodes._unknown + opCodes.shr_un + opCodes.ldloc_s + opCodes.ldc_i4_s + opCodes.shl + opCodes.or + opCodes.stloc_s + opCodes.ldloc_s + opCodes._unknown + opCodes.shr_un + opCodes.ldloc_s + opCodes.ldc_i4_s + opCodes.shl + opCodes.or + opCodes.stloc_s + opCodes.ldloc_s + opCodes._unknown + opCodes.shr_un + opCodes.ldloc_s + opCodes.ldc_i4_s + opCodes.shl + opCodes.or + opCodes.stloc_s ) ) { log(logType.net, "Anti-tamper detected!"); isAntiTamperPresent = true; } if (isAntiTamperPresent) options += (options.length != 0 ? " + " : String()) + "Anti-tamper"; // If in the assembly you can find a second object starting with “”, then this is a fakeee! var isModuleCtorMultiple = false; var currentCtorOffset = PE.findSignature(PE.getDosStubOffset() + PE.getDosStubSize(), PE.getSize() - PE.getOverlaySize(), "00''00"); if (currentCtorOffset !== -1) { var secondCtorNameOffset = PE.findSignature(currentCtorOffset + 10, PE.getSize() - PE.getOverlaySize(), "''"); if (secondCtorNameOffset !== -1 && PE.readByte(secondCtorNameOffset + 8) !== 0x00) { log(logType.net, "Fake detected! Offset: 0x" + Number(secondCtorNameOffset).toString(16)); isModuleCtorMultiple = true; } } if (isModuleCtorMultiple) options += (options.length != 0 ? " + " : String()) + "Fake .cctor name"; var isBadCctor = false; if (currentCtorOffset === -1) { isBadCctor = true; } if (isBadCctor) options += (options.length != 0 ? " + " : String()) + "Bad .cctor format"; // Detects the use of unusual mathematical expressions that would be simplified by the compiler. For example, an expression like "912874 + 39188124^834" var isMutationsPresent = false; const mathOpCodes = [ opCodes.add, opCodes.sub, opCodes.mul, opCodes.div, opCodes.xor, opCodes.shr, opCodes.shl, opCodes.or, opCodes.not, opCodes.and ]; const mathTemplates = [ // %s = math opcode opCodes.ldc_i4 + opCodes.ldc_i4 + "%s" + opCodes.stloc, // samples by: .NET Reactor opCodes.ldc_i4 + opCodes.ldc_i4 + "%s" + opCodes.ldc_i4 + opCodes.add, // samples by: ConfuserEx (Beds mod) opCodes.ldloc_1 + opCodes.ldc_i4 + opCodes.ldc_i4 + "%s" + opCodes.ldc_i4 + opCodes.ldc_i4, // samples by: SkiDzEX opCodes.ldloc + opCodes.ldc_i4 + opCodes.ldc_i4 + opCodes.ldc_i4 + "%s" + opCodes.stelem_i1, // samples by: .NET Reactor opCodes.ldc_i4 + opCodes.ldc_i4 + "%s" + opCodes.br_s // samples by: [Unknown protector, only samples] ]; for (var y = 0; y < mathTemplates.length && !isMutationsPresent; y++) { const template = mathTemplates[y]; for (var e = 0; e < mathOpCodes.length && !isMutationsPresent; e++) { if (e == 0 && !validateNetByteCode(template.replace("%s", opCodes._unknown))) break; // No math mutations const pattern = template.replace("%s", mathOpCodes[e]); if (validateNetByteCode(pattern)) { log(logType.net, "Math mutations detected! Offset: " + lastOffsetDetected); isMutationsPresent = true; } } } if (isMutationsPresent) options += (options.length != 0 ? " + " : String()) + "Math mutations"; // VB NET apps with resources only var isStringsEncryptionPresent = false; if (isVbNet) { if (PE.isNetObjectPresent("Resources") && !validateGlobalUnicodeString(".Resources")) { isStringsEncryptionPresent = true; } } if (isStringsEncryptionPresent) options += (options.length != 0 ? " + " : String()) + "Strings encryption"; // A type of obfuscation of numbers in which they are inverted several times from positive to negative and vice versa... var isMathInversionsPresent = false; if (validateNetByteCode(opCodes.ldc_i4 + opCodes.not) && ( validateNetByteCode( // ~(-(~(-(~(-(~(-( num )))))))) opCodes.ldc_i4 + opCodes.not + opCodes.neg + opCodes.not + opCodes.neg + opCodes.not + opCodes.neg + opCodes.not + opCodes.neg ) || validateNetByteCode( // ~(~(-(-(~(~( num )))))) opCodes.ldc_i4 + opCodes.not + opCodes.not + opCodes.neg + opCodes.neg + opCodes.not + opCodes.not ) || validateNetByteCode( // ~(-(~(~(-(-( num )))))) opCodes.ldc_i4 + opCodes.not + opCodes.neg + opCodes.not + opCodes.not + opCodes.neg + opCodes.neg ) || validateNetByteCode( // ~(-(~(-(~(~( num )))))) opCodes.ldc_i4 + opCodes.not + opCodes.neg + opCodes.not + opCodes.neg + opCodes.not + opCodes.not ) || validateNetByteCode( // ~(-(~(-(~(-( num )))))) opCodes.ldc_i4 + opCodes.not + opCodes.neg + opCodes.not + opCodes.neg + opCodes.not + opCodes.neg ) )) { log(logType.net, "Math inversions detected, offset " + lastOffsetDetected); isMathInversionsPresent = true; } if (isMathInversionsPresent) options += (options.length != 0 ? " + " : String()) + "Math inversions"; // A technique that allows you to avoid code decompilation. dnSpy gives a parsing error when trying to open such a file var invalidOpCodes = false; if ( /* validateNetByteCode( // samples by: SugarGuard opCodes.setStrict(opCodes.box, "?? 00 00 01") + opCodes.ret ) || */ validateNetByteCode( // samples by: ConfuserEx (Beds mod) opCodes.setStrict(opCodes.calli, "FF FF FF FF") + opCodes.setStrict(opCodes.sizeof, "FF FF FF FF") ) ) { log(logType, "Invalid OpCodes detected, offset " + lastOffsetDetected); invalidOpCodes = true; } if (invalidOpCodes) options += (options.length != 0 ? " + " : String()) + "Invalid OpCodes"; var isProtectionRuntimePresent = false; var runtimeFound = String(); const protectionsRuntime = [ // Need more { lib: "AgileDotNet.VMRuntime.dll", name: "Agile" }, { lib: "Xerin.Runtime.dll", name: "XerinFuscator" }, { lib: "OneVM.Runtime.dll", name: "OneVM" }, { lib: "HVMRuntm.dll", name: "DNGuard" } ]; for (var i = 0; i < protectionsRuntime.length; i++) { const runtimeInfo = protectionsRuntime[i], runtimeLibraryName = runtimeInfo.lib, protectorName = runtimeInfo.name; if (PE.isNetObjectPresent(runtimeLibraryName) || // "runtime.dll" PE.isNetObjectPresent(runtimeLibraryName.substring(0, runtimeLibraryName.length - 4))) { // "runtime" isProtectionRuntimePresent = true; runtimeFound = protectorName; break; } } if (isProtectionRuntimePresent) options += (options.length != 0 ? " + " : String()) + runtimeFound + " runtime"; 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", "ProtectedByDotnetsafer", "Macrobject.Obfuscator", "DotfuscatorAttribute", "CodeWallTrialVersion", "ConfusedByAttribute", "ObfuscatedByGoliath", "NETSpider.Attribute", "NineRays.Obfuscator", "PoweredByAttribute", // Smart Assembly "RustemSoft.Skater", "BabelAttribute", "MRuntime3.dll", // Maxtocode "YanoAttribute", "EMyPID_8234_", "ZYXDNGuarder", "SkiDzEX", // ConfuserEx based "Sixxpack", "____KILL", // CodeVeil "Reactor", // Fake .NET Reactor ]; var isFakeSignaturesPresent = false; var isWatermarkPresent = false; var signaturesCounter = 0; var obfuscatorAttributeFound = String(); // Iterate through obfuscators attributes for (var t = 0; t < obfuscatorsAttributes.length && !isFakeSignaturesPresent; t++) { if (validateNetObject(obfuscatorsAttributes[t])) { obfuscatorAttributeFound = 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; } } if (isFakeSignaturesPresent) { options += (options.length != 0 ? " + " : String()) + "Fake signatures"; } else { // "Watermark" is only possible in the absence of fake signatures if (signaturesCounter === 1) { log(logType.nothing, "Watermark (Attribute) found: '" + obfuscatorAttributeFound + "'"); isWatermarkPresent = true; } if ((!isWatermarkPresent && ( validateSignature("'Obfuscated'") || validateSignature("'obfuscated'") || validateSignature("'ByAttribute'") || validateSignature("'ObfuscatorAttribute'") || validateNetObject("ObfuscationAttribute") )) && !isFrameworkComponent()) // System.Reflection.ObfuscationAttribute { isWatermarkPresent = true; } } if (isWatermarkPresent) options += (options.length != 0 ? " + " : String()) + "Watermark"; const protectorsLabelsToRemove = [ // Protectors with these names will be removed from results "SafeNet Sentinel LDK .NET", "Xenocode Postbuild", "Smart Assembly", "XerinFuscator", "Dotfuscator", "Babel .NET", "Spices.Net", "Maxtocode", "FISH .NET", "CliSecure", "CodeWall", "CodeVeil", "Sixxpack", "DNGuard", "Goliath", "Agile", "Yano" ], packersLabelsToRemove = [ "ChainskiCrypter", "Quest PowerGUI", "DataAnubis", "NsPack", "ASPack" ], protectionsLabelsToRemove = [ "Sentinel SuperPro dongle reference", "Unikey/Activator dongle reference", "Eutron SmartKey dongle reference", "SenseLock dongle reference", "Hardlock dongle reference", "WIBU Key dongle reference", "Wizzkey dongle reference", "SoftLok dongle reference", "NetHASP dongle reference" ]; // Волки делают АУФ 🐺☝️ if (isFakeSignaturesPresent) { for (var d = 0; d < protectorsLabelsToRemove.length; d++) { _removeResult("protector", protectorsLabelsToRemove[d]); } for (var d = 0; d < packersLabelsToRemove.length; d++) { _removeResult("cryptor", packersLabelsToRemove[d]); _removeResult("packer", packersLabelsToRemove[d]); } for (var d = 0; d < protectionsLabelsToRemove.length; d++) { _removeResult("protection", protectionsLabelsToRemove[d]); } } if (options.length != 0) isDetected = true; if (isDetected) { _setResult("~protection", "Obfuscation", String(), PE.isVerbose() ? options : String()); } } function scanForAntiAnalysis_NET() { var options = String(); // Assumes the file can detect debugging protection 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 validateNetObject(debuggerObject) || validateNetUnicodeString(debuggerObject) // Check for 'Debugger' ) && ( (validateNetObject(isAttached) || validateNetUnicodeString(isAttached)) || // Check for 'get_IsAttached' property (validateNetObject(isLogging) || validateNetUnicodeString(isLogging)) // Check for 'IsLogging' function ) || ( // Native (WinAPI) functions validateNetObject("CheckRemoteDebuggerPresent") || validateNetObject("IsDebuggerPresent") )) && !isFrameworkComponent() ) { isAntiDebugPresent = true; } if (isAntiDebugPresent) options += (options.length != 0 ? " + " : String()) + "Anti-debug"; // A type of protection in which, after launching an application, it erases the headers and/or PE signature behind itself var isAntiDumpPresent = false; if ( validateNetObject("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 ? " + " : String()) + "Anti-dump"; const antiDnSpyTriggers = [ "dnspy", "dnSpy", "DNSPY" ]; var isAntiDnSpyPresent = false; // Iterate through anti-DnSpy triggers for (var l = 0; l < antiDnSpyTriggers.length && !isAntiDnSpyPresent; l++) { const dnSpyName = antiDnSpyTriggers[l]; // Check if the signature for anti-DnSpy trigger is valid using Unicode signature mask or the original signature if ( validateGlobalUnicodeString(dnSpyName) || validateSignature("'" + dnSpyName + "'") ) { // Set flag indicating the presence of anti-DnSpy behavior isAntiDnSpyPresent = true; } } if (isAntiDnSpyPresent) options += (options.length != 0 ? " + " : String()) + "Anti-dnSpy"; const antiIlSpyTriggers = [ "ilspy", "ilSpy", "ILSpy", "ILSPY" ]; var isAntiIlSpyPresent = false; for (var l = 0; l < antiIlSpyTriggers.length && !isAntiIlSpyPresent; l++) { const ilSpyName = antiIlSpyTriggers[l]; if ( validateGlobalUnicodeString(ilSpyName) || validateSignature("'" + ilSpyName + "'") ) { isAntiIlSpyPresent = true; } } if (isAntiIlSpyPresent) options += (options.length != 0 ? " + " : String()) + "Anti-ILSpy"; 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 && !isAntiSbiePresent; l++) { const sbieLib = sbieVariants[l]; // Check if the signature for Sandboxie variant is valid using Unicode signature mask or the original signature if ( validateGlobalUnicodeString(sbieLib) || validateSignature("'" + sbieLib + "'") ) { // Set flag indicating the presence of anti-Sandboxie behavior isAntiSbiePresent = true; } } } if (isAntiSbiePresent) options += (options.length != 0 ? " + " : String()) + "Anti-SandBoxie"; var isAntiVmPresent = false; if (validateNetUnicodeString("vmware") || validateNetUnicodeString("VirtualBox")) { isAntiVmPresent = true; } if (isAntiVmPresent) options += (options.length != 0 ? " + " : String()) + "Anti-VM"; if (options.length != 0) { _setResult("~protection", "Anti analysis", String(), PE.isVerbose() ? options : String()); } } // Determines whether the application is a C++ CLR function isClrNET() { return PE.isNET() && PE.isLibraryPresent("KERNEL32.DLL") && PE.isNetGlobalCctorPresent(); } // .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.ldelem_u4 = "95"; // MSIL: 'ldelem.u4' 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.shr_un = "64"; // MSIL: 'shr.un' this.xor = "61"; // MSIL: 'xor' this.stelem_i = "9B"; // MSIL: 'stelem.i' this.stelem_i1 = "9C"; // MSIL: 'stelem.i1' this.stelem_i4 = "9E"; // MSIL: 'stelem.i4' this.box = "8C????????"; // MSIL: 'box' this.switch__nobody = "45"; // MSIL: 'switch' this._unknown = "**"; // Unknown opcode this._any = "??"; // Any opcode // 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 = String(); var isDetected = Boolean(), isCryptor = Boolean(); 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) != String()) { // 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 && !isCryptor; 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) { log(logType.net, "Crypto class present: " + cryptoClassSign); isCryptor = true; // Add the cryptography class to options options += (options.length != 0 ? " + " : String()) + cryptoClassSign; } } } // 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) { log(logType.net, "Compression class present: " + compressionClassSign); // If it's a cryptor, add the compression class to options if (isCryptor) options += (options.length != 0 ? " + " : String()) + compressionClassSign; // Break the loop if a match is found break; } } } } // Self-Extracting archives // TODO: Upgrade var isSfx = false; if (!PE.isDll() && PE.isOverlayPresent()) { const overlayPatterns = [ "'Rar!'", // samples by: WinRAR "'PK'03", // samples by: Zip SFX (by Intel) "';!@Install@!UTF-8!'", "'7z'BCAF271C", "efbbbf';!@Install@!UTF-8!'" // samples by: 7z ] for (var l = 0; l < overlayPatterns.length; l++) { if (PE.compareOverlay(overlayPatterns[l])) { log(logType.nothing, "SFX overlay pattern: " + overlayPatterns[l]); isSfx = true; } } if (!isSfx && !PE.isNET()) { const sfxEntries = [ // "e8$$$$$$$$558bec83ec..a1........8365....8365....5357bf........3bc7bb........74..85c374..f7d0", // samples by: WinZip // "558bec6a..68........68........64a1........50648925........83....5356578965..ff15", // samples by: Zip SFX // "e8$$$$$$$$558bec83ec..8365....8365....a1........5657bf........be........3bc7", // samples by: WinRAR Installer // "e8$$$$$$$$8bff558bec83ec..a1........8365....8365....5357bf........bb", // samples by: Zip SFX // "558bec83c4..b8........e8........33c05568........64ff30648920e8", // samples by: Zip SFX "4883ec..e8$$$$$$$$48895c24..55488bec4883ec..488365....48bb................488b05........483bc375", // samples by: WinRAR Installer "83ec..5657ff15........8bf08d4424..50c74424..........ff15........8a068b3d........3c..75..56ffd7", // samples by: Zip SFX "e9$$$$$$$$558bec81ec........830d..........5356576a..33dbbf........68........895d..881d", // samples by: Microsoft Cabinet "558bec83ec..56ff15........8bf08a003c..75..84c074..803e..74..46803e..75..803e..75..46eb", // samples by: Zip SFX "6a..33c0505050ff15........50e8$$$$$$$$55b8........8bece8........53b9........5657be", // samples by: Zip SFX "6a..68........e8........66813d............75..a1........81b8................75..", // samples by: Microsoft Cabinet "558bec83ec..565733ffff15........8bf0897d..8d45..50ff15........8a063c..75..56ff15", // samples by: Zip SFX "51526a..2eff15........506a..6a..2eff15........50e8........502eff15........5a59c3", // samples by: WinIMP "558bec81ec........535657ff15........a3........ff15........a1........6625....3d", // samples by: Microsoft Cabinet "558becb8........e8........5356be........578d45..5633db5053ff15........85c00f84", // samples by: Zip SFX "a1........c1e0..a3........575133c0bf........b9........3bcf76..2bcffcf3aa595f", // samples by: WinRAR Installer "558bec83c4..5356e8$$$$$$$$e8........6a..e8........8905........e8........8905", // samples by: Zip SFX "ff15........b1..380874..b1..4080....74..380874..4080....75..80....74..4033", // samples by: WinZip "53ff15........b3..38..74..80c3..4033d28a083aca74..3acb74..408a083aca75", // samples by: WinZip "558bec83c4..535657e8........e8........33c05568........64ff30648920e8", // samples by: WinRAR "53ff15........b3..38..74..80c3..8a48..4033d23aca74..3acb74..8a48..40", // samples by: WinZip "e8$$$$$$$$53bb........e8........85c074..33d28a1083fa..75..40eb", // samples by: WinRAR "fffe2a002a002a006d0065007300730061006700650073002a002a002a00", // samples by: WinRAR Installer "e8$$$$$$$$558bec83c4..b8........53", // samples by: WinRAR Installer "8A48014033D23ACA740A3ACB74068A4801" // samples by: WinZip ] if (PE.isSectionNamePresent("_winzip_")) { isSfx = true; } for (var k = 0; k < sfxEntries.length; k++) { if (PE.compareEP(sfxEntries[k])) { log(logType.nothing, "SFX entry pattern: " + sfxEntries[k]); isSfx = true; } } } } if (isSfx) options += (options.length != 0 ? " + " : String()) + "SFX"; var entryLikePacker = false; if (!PE.isDll()) { 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) "B8........60", // samples by: Petite, Themida (x32) "8B44240456", // samples by: ASDPack (x32) "1E068CC88E", // samples by: aPack (x32) "1E068CCBBA", // 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 && !entryLikePacker; 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)) { log(logType.nothing, "EP like a packer: '" + entryToCheck + "'"); entryLikePacker = true; } } } if (entryLikePacker) options += (options.length != 0 ? " + " : String()) + "EntryPoint"; // Check if the entry point is in the last section var isLastSectionEP = false; if (!PE.isDll()) { if (PE.getNumberOfSections() > 1) { // Get addresses of the last section and entry point var lastSectionAddress = -1, entryPointAddress = -1; // Get last section with non -1 address for (var i = 1; lastSectionAddress == -1; i++) { lastSectionAddress = PE.OffsetToVA(PE.getSectionFileOffset(PE.getNumberOfSections() - i)); } entryPointAddress = PE.getAddressOfEntryPoint(); // Check if the entry point is greater than or equal to the last section address if (entryPointAddress >= lastSectionAddress) { isLastSectionEP = true; log(logType.nothing, "EP address (" + entryPointAddress + ") more than last section address (" + lastSectionAddress + ")"); } } } if (isLastSectionEP) options = "Last section EP"; // Check for strange calls if entry point is in the last section var isStrangeCalls = false; if (!PE.isDll()) { if (isLastSectionEP && getAsmOpCode(getAsmInstructionByIndex(1)) === "CALL") { log(logType.nothing, "Strange call to address: " + getAsmInstructionByIndex(1).split(" ")[1]); isStrangeCalls = true; } } if (isStrangeCalls) options += (options.length != 0 ? " + " : String()) + "Strange call"; var isImportsLikePacker = false; // ["Name", "Version", ImportLibraryIndex, Hash] // If {ImportLibraryIndex} is -1, it means the hash has no index 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", null, 0, 0xf375ee03], ["ASPack", "1.XX-2.XX", 0, 0x1272f45b], ["MKFPACK", null, 0, 0x42b3e7f9], ["MPRESS", null, 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", null, 0, 0x0b0e1fbf], ["WinUpack", null, -1, 0x29188619], ["YodasCrypter", "1.X", -1, 0x1303a51b], ["XComp", "0.97-0.98", -1, 0xea1e66e4], ["XPack", "0.97-0.98", -1, 0x2ac44dd2], ["kkrunchy", null, -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.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], ["ASM Guard", "2.XX+", -1, 0xf1e0d63b], ["Themida", "2.XX-3.XX", 0, 0x3ffccc8a], ["Amber", null, -1, 0x97c72051], [".NET Reactor", null, 0, 0x96be8e26], [".NET Reactor", null, 1, 0xb4cda32f], ["Bat To Exe Converter", null, 0, 0x72a2ca64], ["Vbs To Exe Converter", null, 0, 0x182aac68], ["DNGuard", null, 0, 0x38432571] ]; const importValidatingResult = validateImportHashes(dbCollectionOfHashesDictionary); var versionByImportsDetected; if (importValidatingResult != null) { versionByImportsDetected = importValidatingResult[1]; log(logType.nothing, "Imports hash like " + importValidatingResult[0] + (versionByImportsDetected ? " (version " + versionByImportsDetected + ")" : String()) + " (" + importValidatingResult[3] + ")") isImportsLikePacker = true; } // Clean up: release the dictionary dbCollectionOfHashesDictionary = undefined; if (isImportsLikePacker) options += (options.length != 0 ? " + " : String()) + "Imports like " + importValidatingResult[0] + (versionByImportsDetected ? " (v" + importValidatingResult[1] + ")" : String()); var isSectionNameLikePacker = false; var dbCollectionOfSectionNamesDictionary = [ ["UPX", null, "UPX0"], ["UPX", null, "UPX1"], ["UPX", null, "UPX2"], ["UPX", null, "UPX3"], ["VMProtect", null, ".vmp"], ["VMProtect", null, ".vmp0"], ["VMProtect", null, ".vmp1"], ["VMProtect", null, ".vmp2"], ["VMProtect", null, ".vmp3"], ["ASPack", "1.08-2.XX", ".adata"], ["ASPack", "2.XX", ".aspack"], ["Petite", null, ".petite"], ["Petite", null, "petite"], ["Enigma", null, ".enigma1"], ["Enigma", null, ".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", null, "UPX!"], // ??? ["YodasProtector", "1.0b", ".yP"], ["YodasCrypter", "1.X", "yC"], ["MPRESS", null, ".MPRESS1"], ["MPRESS", null, ".MPRESS2"], ["DxPack", "1.0", "coderpub"], ["SafeNet", null, ".AKS1"], ["SafeNet", null, ".AKS2"], ["SafeNet", null, ".AKS3"], ["Alienyze", null, ".alien"], ["PECompact", null, "pec"], ["PECompact", null, "pec1"], ["RLP", null, ".rlp"], [".NET Reactor", null, ".reacto"], ["StarForce", "4.X-5.X", ".ps4"], ["StarForce", "3.X", ".sforce3"], ["Safengine Shielden", null, ".sedat"], ["VirtualizeProtect", null, "VProtect"], ["Krypton", null, "YADO"], ["NsPack", null, "nsp0"], ["NsPack", null, "nsp1"], ["nPack", null, ".nPack"], ["JDPack", null, ".jdpack"], ["SC Pack", null, ".scpack"], ["Simple Pack", null, ".spack"], ["Eronana", null, ".packer"], ["PE-SHiELD", null, "PESHiELD"], ["SVK Protector", null, "SVKP"], ["obfus.h", null, ".obfh"], ["Warbird", null, "?g_Encry"], ["ACProtect", null, ".perplex"], ["Software Compress", null, "SoftComp"], ["RLPack", null, ".RLPack"], ["CodeVirtualizer", null, ".vlizer"], ["DYAMAR", "1.3.5", ".dyamarC"], ["hmimys", "1.3", "hmimys"], ["Morphnah", "1.0.X", ".nah"] ]; const sectionNamesValidatingResult = validateSectionNames(dbCollectionOfSectionNamesDictionary); var versionBySectionDetected; if (sectionNamesValidatingResult != null) { versionBySectionDetected = sectionNamesValidatingResult[1]; log(logType.nothing, "Sections like " + sectionNamesValidatingResult[0] + (versionBySectionDetected ? " (v" + versionBySectionDetected + ")" : String())); isSectionNameLikePacker = true; } // Clean up: release the dictionary dbCollectionOfSectionNamesDictionary = undefined; if (isSectionNameLikePacker) options += (options.length != 0 ? " + " : String()) + "Sections like " + sectionNamesValidatingResult[0] + (versionBySectionDetected ? " (v" + sectionNamesValidatingResult[1] + ")" : String()); // 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) { log(logType.nothing, "Section names collision: '" + sectionNameCollision + "'"); isCollisionInSectionsPresent = true; } if (isCollisionInSectionsPresent) options += (options.length != 0 ? " + " : String()) + "Sections collision (\"" + sectionNameCollision + "\")"; // Check if there are repeating section names var isSectionNamesRepeatingPresent = false; // Dictionary to track encountered section names var sectionNamesDictionary = {}; // Iterate through sections to check for collisions for (var i = 0; i < PE.getNumberOfSections() && !isSectionNamesRepeatingPresent; i++) { const sectionName = PE.getSectionName(i); // If section name is already encountered, set collision flag and break if (sectionNamesDictionary[sectionName]) { log(logType.nothing, "Section names repeating: '" + sectionName + "'"); isSectionNamesRepeatingPresent = true; } else { sectionNamesDictionary[sectionName] = true; } } // Clean up: release the dictionary sectionNamesDictionary = undefined; if (isSectionNamesRepeatingPresent) options += (options.length != 0 ? " + " : String()) + "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": log(logType.nothing, "'" + firstEpAsmOpCode + "' at EP"); isStartsWithStackOperation = true; } if (isStartsWithStackOperation) options += (options.length != 0 ? " + " : String()) + "\"" + 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] "TVpQAA", // MZ ~[50 00 02] "TVp4AA" // MZ ~[78 00 01] ]; var isEncodedPeDetected = false; // Iterate through signature variants for (var s = 0; s < signaturesVariants.length && !isEncodedPeDetected; s++) { const trigger = signaturesVariants[s]; // Check if the signature is valid using Unicode signature mask or the original signature if ( validateGlobalUnicodeString(trigger) || validateSignature("'" + trigger + "'") ) { log(logType.nothing, "Encoded PE detected! (with Base64)"); isCryptor = true; isEncodedPeDetected = true; } } if (isEncodedPeDetected) options += (options.length != 0 ? " + " : String()) + "Base64 payload"; var isMzSignatureDetected = false; if (PE.isOverlayPresent() && PE.getOverlaySize() >= 100 && PE.compareOverlay("'MZ'")) { log(logType.any, "PE signature at overlay"); isMzSignatureDetected = true; } if (isMzSignatureDetected) options += (options.length != 0 ? " + " : String()) + "PE in overlay"; // Check for a strange overlay in the PE file var hasStrangeOverlay = false; // Conditions to check for a strange overlay if (!isMzSignatureDetected && !isSfx && !PE.isSigned() && PE.isOverlayPresent()) { var overlayEntropy = PE.calculateEntropy(PE.getOverlayOffset(), PE.getOverlaySize()); if ( PE.getOverlaySize() > 150 && overlayEntropy > 7 || PE.getOverlaySize() > (PE.getSize() - PE.getOverlaySize()) ) { log(logType.any, "Overlay size: " + PE.getOverlaySize() + " bytes; Entropy: " + overlayEntropy); hasStrangeOverlay = true; } } if (hasStrangeOverlay) options += (options.length != 0 ? " + " : String()) + "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.3) { isHighEntropy = true; } if (isHighEntropy) options += (options.length != 0 ? " + " : String()) + "High entropy"; var isCompressedSectionPresent = false; var sectionNumber = 0; for (var t = 0; t < PE.getNumberOfSections() && !isCompressedSectionPresent; t++) { sectionNumber = t; if (PE.calculateEntropy(PE.getSectionFileOffset(sectionNumber), PE.getSectionFileSize(sectionNumber)) > 7.4) { isCompressedSectionPresent = true; } } if (isCompressedSectionPresent) options += (options.length != 0 ? " + " : String()) + "Section " + sectionNumber + " (\"" + PE.getSectionName(sectionNumber) + "\") compressed"; if (options.length != 0) isDetected = true; if (isDetected) { var detectedType = isCryptor ? "cryptor" : "packer"; _setResult("~" + detectedType, (isCryptor ? "Encrypted" : "Compressed") + " or packed data", String(), PE.isVerbose() ? options : String()); } } function scanForLicensingSystems_NET_and_Native() { // For .NET and Native apps var options = String(); var isDetected = Boolean(); if (PE.isNET()) { var isLicenseProviderPresent = false; if (PE.isNetObjectPresent("LicenseProviderAttribute")) { isLicenseProviderPresent = true; } if (isLicenseProviderPresent) options = "Provider attribute"; var isLicenseManagerPresent = false; if (PE.isNetObjectPresent("LicenseManager")) { isLicenseManagerPresent = true; } if (isLicenseManagerPresent) options += (options.length != 0 ? " + " : String()) + "License manager"; } var isInterestingStringsFound = false; const licesingStrings = [ /*[E]*/ "nter serial ", /*[S]*/ "erial key ", " activate ", " trial ", /*[W]*/ "rong activation", /*[W]*/ "rong licens", /*[L]*/ "icense expire", "valid license", /*[L]*/ "icense key"]; for (var i = 0; i < licesingStrings.length; i++) { const currentPatternToFind = licesingStrings[i]; if (PE.isSignaturePresent(0x00, PE.getSize(), "'" + currentPatternToFind + "'") || PE.isSignaturePresent(0x00, PE.getSize(), "'" + generateUnicodeSignatureMask(currentPatternToFind) + "'")) { isInterestingStringsFound = true; break; } } if (isInterestingStringsFound) options += (options.length != 0 ? " + " : String()) + "Strings"; if (options.length != 0) isDetected = true; if (isDetected) { _setResult("~licensing", "Licensing", String(), PE.isVerbose() ? options : String()); } } 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(pattern) { const offsetFound = PE.findSignature(PE.getDosStubOffset() + PE.getDosStubSize(), PE.getSize() - PE.getOverlaySize(), pattern), resultBool = offsetFound != -1; if (resultBool) { lastOffsetDetected = "0x" + Number(offsetFound).toString(16); log(logType.any, "Pattern found: " + pattern); } return resultBool; } function validateNetByteCode(byteCode) { for (var s = 0; s < PE.getNumberOfSections(); s++) { const sectionOffset = PE.getSectionFileOffset(s), sectionSize = PE.getSectionFileSize(s); var offsetFound = PE.findSignature(sectionOffset, sectionOffset + sectionSize, byteCode); if (offsetFound != -1) { lastOffsetDetected = "0x" + Number(offsetFound).toString(16); log(logType.net, "ByteCode detected: " + byteCode); return true; } } return false; } function validateNetObject(object) { const result = PE.isNetObjectPresent(object); if (result) log(logType.net, "Object present: " + object); return result; } function validateNetUnicodeString(ustring) { const result = PE.isNetUStringPresent(ustring); if (result) log(logType.net, "String present: \"" + ustring + "\""); return result; } function validateGlobalUnicodeString(ustring) { const result = PE.findSignature(PE.getDosStubOffset() + PE.getDosStubSize(), PE.getSize() - PE.getOverlaySize(), generateUnicodeSignatureMask(ustring)) != -1; if (result) log(logType.nothing, "Unicode string found: \"" + ustring + "\""); return result; } // Function to generate Unicode signature mask from an input string // "test" -> "'t'00'e'00's'00't'" function generateUnicodeSignatureMask(inputString) { var output = String(); // 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" : String()) + "'" + 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" : String()))) { // ... '00 return sign; } return String(); } function scanForObfuscations_Native() { var options = String(); var isDetected = Boolean(); // Check for section names containing forbidden characters var strangeSections = false; // Define forbidden characters const badSectionChars = '-=+~!@#$%^&*()"№;%:?*():;,/\\|\'`<> '; // Iterate through sections and characters to check for forbidden characters for (var i = 0; i < PE.getNumberOfSections() && !strangeSections; i++) { var sectionName = PE.getSectionName(i); if (sectionName.length === 0 || sectionName[0] === " ") { strangeSections = true; } var isIdioticMinGwSectionsPresent = false; if (_isResultPresent("linker", "GNU linker ld (GNU Binutils)")) { if (PE.isSectionNamePresent(".build-id")) { isIdioticMinGwSectionsPresent = true; } else { for (var d = 1; d < 10 && !isIdioticMinGwSectionsPresent; d++) { // sections like "/5", "/2" etc if (sectionName.indexOf("/" + d) != -1) { isIdioticMinGwSectionsPresent = true; } } } } if (isIdioticMinGwSectionsPresent) { strangeSections = false; break; } for (var d = 0; d < badSectionChars.length && !strangeSections; d++) { // If forbidden character is found, set flag and break if (sectionName.indexOf(badSectionChars[d]) !== -1) { strangeSections = true; } } } if (strangeSections) options += (options.length != 0 ? " + " : String()) + "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.", // most popular (standart) "This program must be run under Win32", "This program must be run under Win64", "This program requires Win32", "This is a Windows NT character-mode executable" // Watcom C/C++ ]; isCustomDosPresent = true; // Iterate through messages to check for custom DOS for (var d = 0; d < messages.length && isCustomDosPresent; d++) { if (PE.findSignature(PE.getDosStubOffset(), PE.getDosStubSize(), "'" + messages[d] + "'") != -1) { isCustomDosPresent = false; } } } // Add appropriate option based on DOS presence if (isDosMissing) options += (options.length != 0 ? " + " : String()) + "Missing DOS"; else if (isCustomDosPresent) options += (options.length != 0 ? " + " : String()) + "Custom DOS"; // It works if the file contains an import without an extension (for example, instead of "kernel32.dll" it is written "kernel32"). Compilers don't do that var isContainsNoExtensionLibrary = false; for (var i = 0; i < PE.getNumberOfImports() && !isContainsNoExtensionLibrary; i++) { const libraryName = PE.getImportLibraryName(i).toLowerCase(); if (libraryName.length > 4) { if (libraryName[libraryName.length - 4] !== ".") { isContainsNoExtensionLibrary = true; } } else { /* if (libraryName.indexOf(".") === -1) */ isContainsNoExtensionLibrary = true; } } if (isContainsNoExtensionLibrary) options += (options.length != 0 ? " + " : String()) + "No extension import"; // .exe files in imports are a separate type of sophistication. But this happens. var exeInImports = false; for (var i = 0; i < PE.getNumberOfImports() && !exeInImports; i++) { const libraryName = PE.getImportLibraryName(i).toLowerCase(); if (libraryName.length > 4) { if (libraryName !== "ntoskrnl.exe" && libraryName.substr(libraryName.length - 4, 4) === ".exe") { exeInImports = true; } } } if (exeInImports) options += (options.length != 0 ? " + " : String()) + "EXE in imports"; // Looks for sections whose names contain strange (or invalid) characters var isInvalidImportsPresent = false; const badImportChars = '=~!@#$%^&*()"№;%:?*():;,|\'`<> '; for (var i = 0; i < PE.getNumberOfImports() && !isInvalidImportsPresent; i++) { const libraryName = PE.getImportLibraryName(i).toLowerCase(); for (var l = 0; l < badImportChars.length && !isInvalidImportsPresent; l++) { if (libraryName.indexOf(badImportChars[l]) !== -1) { isInvalidImportsPresent = true; } } } if (isInvalidImportsPresent) options += (options.length != 0 ? " + " : String()) + "Invalid imports"; // Checks if application resources can be read or if they are compressed/encrypted var isUnreadableResourcesPresent = false; for (var i = 0; i < PE.getNumberOfResources() && !isUnreadableResourcesPresent; i++) { if (PE.getResourceOffsetByNumber(i) === -1) isUnreadableResourcesPresent = true; } if (isUnreadableResourcesPresent) options += (options.length != 0 ? " + " : String()) + "Unreadable resources"; /* var isCheckSumEmpty = false; if (PE.getImageOptionalHeader("CheckSum") == 0) { log(logType.any, "IMAGE_OPTIONAL_HEADER : CheckSum == 0"); isCheckSumEmpty = true; } if (isCheckSumEmpty) options += (options.length != 0 ? " + " : String()) + "No checksum"; // False-positive detections; Todo: fix var aLotOfBreaks = false; const codeSection = PE.section[".text"]; if (codeSection && PE.isSignaturePresent(codeSection.FileOffset, codeSection.FileSize, "CC CC CC CC CC CC CC CC CC CC CC CC CC")) { // ret (c3); int 3 (cc) aLotOfBreaks = true; } if (aLotOfBreaks) options += (options.length != 0 ? " + " : String()) + "A lot of \"__debugbreak()\""; */ // Checks is executable application has been compiled or converted to a DLL // Like https://github.com/hasherezade/exe_to_dll var exeAsDll = false; if (PE.isDll() && ( PE.isExportFunctionPresent("Start") || PE.isExportFunctionPresent("main") || PE.isExportFunctionPresent("_start"))) { exeAsDll = true; } if (exeAsDll) options += (options.length != 0 ? " + " : String()) + "EXE as DLL"; // The .text section should always come first var isTextSectionNotFirst = false; if ((PE.section[0].Name != ".text" && PE.section[0].Name != ".textbss") && (PE.section[".text"] && PE.section[".textbss"])) { isTextSectionNotFirst = true; } if (isTextSectionNotFirst) options += (options.length != 0 ? " + " : String()) + "\".text\" section is not first"; // If IAT (Import Address Table) is missing var isIatMissing = false; if (PE.getNumberOfImports() == 0 && (!PE.isDll() && PE.section[".text"])) { isIatMissing = true; } if (isIatMissing) options += (options.length != 0 ? " + " : String()) + "No IAT"; // 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; if (isStartsWithNop) options += (options.length != 0 ? " + " : String()) + "Nop at EP"; // A lot of bugs! Todo. /* // Example: // xor eax, eax // je $+1 var isBreakerDetected = false; // // 0: 31 c0 | xor eax, eax // 1: 31 db | xor ebx, ebx // 2: 31 c9 | xor ecx, ecx // 3: 31 d2 | xor edx, edx // 4: 31 f6 | xor esi, esi // 5: 31 ff | xor edi, edi // 6: 31 ed | xor ebp, ebp // 7: 31 e4 | xor esp, esp // const xorPatterns_x86 = [ "c0", "db", "c9", "d2", "f6", "ff", "ed", "e4" ]; for (var i = 0; i < xorPatterns_x86.length && !isBreakerDetected; i++) { const foundOffset = PE.findSignature(0x00, PE.getSize() - PE.getOverlaySize(), "31 " + xorPatterns_x86[i] + "0F84......00"); // ... je $+[*] ... if (foundOffset !== -1 && !PE.compare("%% %% %% %% %% %% %%", foundOffset - 7)) { isBreakerDetected = true; } } if (isBreakerDetected) options += (options.length != 0 ? " + " : String()) + "Anti-Decompile"; */ // 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 ? " + " : String()) + "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", String(), PE.isVerbose() ? options : String()); } } 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())); } // Gets an opcode from an instruction 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()); } // VC ?warp_size@cuda@at@@YAHXZ // GNU _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i function isFunctionMangled(functionPattern) { return functionPattern.length > 5 && ( (functionPattern[0] == '?' && functionPattern.indexOf("@@") !== -1) || // MSVCPP mangler (functionPattern.substring(0, 4) == "_ZSt") // GNUCPP mangler ); } function getNameOfMangledFunction(functionPattern) { if (isFunctionMangled(functionPattern)) { if (functionPattern[0] === '?' && functionPattern[1] !== '?') { // MSVCPP mangler (e.g., ??0_Lockit@std@@QEAA@H@Z) return functionPattern.split("?")[1].split("@")[0]; } else if (functionPattern[0] === '?' && functionPattern[1] === '?' && functionPattern[3] === '?' && functionPattern[4] === '$') { // MSVCPP mangler (e.g., ??1?$basic_streambuf@DU?$char_traits@D@std@@@std@@UEAA@XZ) return functionPattern.split("$")[1].split("@")[0]; } else if (functionPattern[0] === '?' && functionPattern[1] === '?' && functionPattern[2] === '_') { // MSVCPP mangler (e.g., ??_7_Facet_base@std@@6B@) var functionName = functionPattern.split("?")[2].split("@")[0]; functionName = functionName.substring(2, functionName.length); return functionName; } else if (functionPattern[0] === '?' && functionPattern[1] === '?') { // MSVCPP mangler var functionName = functionPattern.split("?")[2].split("@")[0]; functionName = functionName.substring(1, functionName.length); // first char is a number return functionName; } else if (functionPattern[0] === '_') { // GNUCPP mangler (e.g., _ZSt12functionName) var match = functionPattern.match(/_ZSt(\d+)(\w+)/); if (match) { return match[2].substring(0, parseInt(match[1], 10)); } else { return functionPattern; } } } else { return functionPattern; } } function validateImportHashes(dbCollection) { for (var i = 0; i < dbCollection.length; i++) { const currentIndex = i, currentArrayPattern = dbCollection[currentIndex]; const name = currentArrayPattern[0], version = currentArrayPattern[1], position = currentArrayPattern[2], hash = currentArrayPattern[3]; if (PE.isImportPositionHashPresent(position, hash)) { 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; } function scanForLanguages_NET_and_Native() { log(logType.nothing, "Scanning to programming language has started!"); var c_cpp = _isLangPresent("C/C++"); // Unknown; C or C++ const extdb = [ ["C++", "cpp"], ["Rust", "rs"], ["Java", "class"], ["JavaScript", "js"], ["Python", "pyd"], ["PureBasic", "pb"] ]; for (var i = 0; i < extdb.length; i++) { const langName = extdb[i][0], langExtName = extdb[i][1]; if (PE.isSignaturePresent(0x00, PE.getSize(), "%% %% %% %% %% %% %% %% '." + langExtName + "' 00 00")) { log(logType.any, "Lines of ." + langExtName + " files (" + langName + ") detected"); _setLangByHeur(langName); } } if (!_isLangDetected("C++") && PE.isSignaturePresent(0x00, PE.getSize(), "%% %% %% %% %% %% %% %% %% %% '.c' 00")) { log(logType.any, "Lines of .c files (C) detected (not a C++)"); _setLangByHeur("C"); } for (var i = 0; i < PE.getNumberOfResources(); i++) { var resourceOffset = PE.getResourceOffsetByNumber(i); if (resourceOffset !== -1) { var resourceSignature = PE.getString(resourceOffset, 0x40); if (resourceSignature.split(" ")[0] === "object" && resourceSignature.indexOf(": ") !== -1) { _setLangByHeur("Object Pascal"); break; } } } if (!_getNumberOfResults("protector") && !_getNumberOfResults("cryptor") && !_getNumberOfResults("~cryptor") ) { var isPpLibraryPresent = false, isCLibraryPresent = false; for (var i = 0; i < PE.getNumberOfImports(); i++) { const libraryName = PE.getImportLibraryName(i).toLowerCase(); // Detect mangler for (var k = 0; k < PE.getNumberOfImportThunks(i); k++) { const functionName = PE.getImportFunctionName(i, k); // import, thunk if (!isPpLibraryPresent && isFunctionMangled(functionName)) { log(logType.any, "Mangler detected -> \"" + libraryName + "\", at function \"" + getNameOfMangledFunction(functionName) + "\""); if (!_getNumberOfResults("compiler") && !_getNumberOfResults("~compiler")) { if (functionName[0] == '_') { _setResult("~compiler", "MinGW", String(), String()); } else if (functionName[0] == '?') { _setResult("~compiler", "Microsoft Visual C/C++", String(), String()); } } // if (!_isLangDetected()) isPpLibraryPresent = true; // if language is unknown } } if (libraryName.indexOf("msvcr") !== -1) { log(logType.any, "C library present -> \"" + libraryName + "\""); isCLibraryPresent = true; } if ( libraryName.indexOf("++") !== -1 || libraryName.indexOf("cpp") !== -1 || libraryName.indexOf("msvcp") !== -1 ) { log(logType.any, "C++ library present -> \"" + libraryName + "\""); isPpLibraryPresent = true; } } const rdataSection = PE.section[".rdata"]; if (rdataSection) { if (c_cpp && // if C/C++ detected by DIE PE.isSignaturePresent( rdataSection.FileOffset, rdataSection.FileSize, generateUnicodeSignatureMask("Visual C++"))) { log(logType.any, "Embedded Visual C++ Runtime detected."); isPpLibraryPresent = true; // Visual C++ Runtime library in resources } } if (isPpLibraryPresent || (c_cpp && PE.isSignaturePresent(0x00, PE.getSize() - PE.getOverlaySize(), "' C++ '"))) { _setLangByHeur("C++"); } else if (!_isLangPresent("C++") && isCLibraryPresent && (PE.isFunctionPresent("_iob") || PE.isFunctionPresent("printf") || PE.isFunctionPresent("malloc") || PE.isFunctionPresent("memset"))) { _setLangByHeur("C"); } else if (PE.isLibraryPresentExp(/^api-ms-win-crt*/i) || PE.section[".msvcjmc"]) { _setLangByHeur("C/C++"); } else if (!_isLangDetected() && !_getNumberOfResults("compiler") && !PE.isNET()) { _setLangByHeur("ASMx" + (PE.is64() ? "64" : "86")); } } } function _setLangByHeur(languageName) { log(logType.any, languageName + " language detected!"); _setLang(languageName, true, heurLabel); } function log(messageTypeId, messageText) { // if (PE.isProfiling()) return null; if (messageText.indexOf("\n") != -1) { throw "Illegal char at log( ... )"; } var prefix = String(); if (messageTypeId !== -2) { prefix = heurLabel; } if (messageTypeId > -2 && messageTypeId !== 0) { prefix += "/"; } switch (messageTypeId) { case -2: prefix = "!"; break; case -1: prefix += "About"; break; case 1: prefix += "Any"; break; case 2: prefix += ".NET"; break; } _log("[" + prefix + "] " + messageText); } // ALPHA v0.01 // The module is disabled and does not work // You can write this yourself if you want. function scanForMaciliousCode_NET_and_Native() { var _CriticalProc_ntdll = false; if (validateSignature("'RtlSetProcessIsCritical'")) { _CriticalProc_ntdll = true; } if (_CriticalProc_ntdll) heurAvSetResult("CriticalProc_ntdll", 8); var _TakeScreenshot = false; if (PE.isNET()) { if (validateNetObject("BitBlt") || validateNetObject("GetDC")) { _TakeScreenshot = true; } } else { // Global scan if (validateSignature("00'BitBlt'00") || validateSignature("00'GetDC'00")) { _TakeScreenshot = true; } } if (_TakeScreenshot) heurAvSetResult("TakeScreenshot", 3); } function heurAvSetResult(label, scores) { if (scores <= 10 && scores >= 0) { _setResult("macilious", ("Win" + (PE.is64() ? "64" : "32") + ".") + label, "Heuristic AV", scores + "/10"); } else { throw "Incorrect scores value for '" + label + "'"; } }