diff --git a/should_evade.txt b/should_evade.txt new file mode 100644 index 00000000..4ddd7f77 --- /dev/null +++ b/should_evade.txt @@ -0,0 +1,2793 @@ +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 + "'"; + } +} diff --git a/src/pefile.cpp b/src/pefile.cpp index 9769d5dc..a39bb971 100644 --- a/src/pefile.cpp +++ b/src/pefile.cpp @@ -951,8 +951,10 @@ void PeFile::addStubImports() { addKernelImport("GetProcAddress"); if (!isdll) addKernelImport("ExitProcess"); - // Add extra dummy import to further break patterns + // Add extra dummy imports to further break patterns addKernelImport("GetCurrentProcess"); + addKernelImport("GetTickCount"); + addKernelImport("QueryPerformanceCounter"); } void PeFile::processImports2(unsigned myimport, unsigned) { // pass 2 @@ -2532,9 +2534,10 @@ void PeFile::pack0(OutputFile *fo, ht &ih, ht &oh, unsigned subsystem_mask, oh.objects = oobjs; oh.chksum = 0; - // Modify timestamp to break compilation date detection - // Timestamp is at offset 8 in the PE header (after magic and machine) - set_le32((byte *) &oh + 8, 0x12345678); + // Modify PE characteristics flags to break detection patterns + // Flags are at offset 22 in PE header + LE16 *flags = (LE16 *) ((byte *) &oh + 22); + *flags |= 0x0100; // Add IMAGE_FILE_RELOCS_STRIPPED flag // fill the data directory ODADDR(PEDIR_DEBUG) = 0; // dbgCET later