Some checks are pending
CI / Rebuild stubs (push) Waiting to run
CI / ${{ format('{0}', matrix.os) }}-0 (ubuntu-22.04) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.os) }}-0 (ubuntu-22.04-arm) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.os) }}-0 (ubuntu-24.04) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.os) }}-0 (ubuntu-24.04-arm) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.os) }} (ubuntu-22.04, true) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.os) }} (ubuntu-24.04, true, true) (push) Blocked by required conditions
CI / ${{ format('{0} {1}{2}', matrix.os, matrix.xcode_version && 'xcode-' || '', matrix.xcode_version) }} (gcc-13, g++-13, macos-14, true) (push) Blocked by required conditions
CI / ${{ format('{0} {1}{2}', matrix.os, matrix.xcode_version && 'xcode-' || '', matrix.xcode_version) }} (gcc-14, g++-14, macos-15, true) (push) Blocked by required conditions
CI / ${{ format('{0} {1}{2}', matrix.os, matrix.xcode_version && 'xcode-' || '', matrix.xcode_version) }} (gcc-14, g++-14, macos-15-intel, true) (push) Blocked by required conditions
CI / ${{ format('{0} {1}{2}', matrix.os, matrix.xcode_version && 'xcode-' || '', matrix.xcode_version) }} (gcc-15, g++-15, macos-26, true) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.name) }} (windows-11-arm64, windows-11-arm, arm64, 2022) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.name) }} (windows-11-arm64ec, windows-11-arm, true, arm64, 2022) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.name) }} (windows-2022-amd64, windows-2022, amd64, 2022) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.name) }} (windows-2022-i386, windows-2022, amd64_x86, 2022) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.name) }} (windows-2025-amd64, windows-2025, amd64, 2022) (push) Blocked by required conditions
CI / ${{ format('{0}', matrix.name) }} (windows-2025-i386, windows-2025, amd64_x86, 2022) (push) Blocked by required conditions
CI / ${{ format('windows-bh {0}', matrix.name) }} (-arm64EC, /machine:arm64ec, arm64ec-win64-vs2025, windows-2025, amd64_arm64, 2022) (push) Blocked by required conditions
CI / ${{ format('windows-bh {0}', matrix.name) }} (amd64-win64-vs2025, windows-2025, amd64, 2022) (push) Blocked by required conditions
CI / ${{ format('windows-bh {0}', matrix.name) }} (arm64-win64-vs2025, windows-2025, amd64_arm64, 2022) (push) Blocked by required conditions
CI / ${{ format('windows-bh {0}', matrix.name) }} (i386-win32-vs2025, windows-2025, amd64_x86, 2022) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (-march=i586, i386-linux-gnu.2.17) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (-march=i586, i386-linux-gnu.2.3.4) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (aarch64-macos-none) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (aarch64-windows-gnu) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (alpine:3.18, qemu-aarch64, -fPIE, aarch64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (alpine:3.18, qemu-x86_64, -fPIE, x86_64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (i386-windows-gnu) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-aarch64, aarch64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-arm, arm-linux-musleabihf) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-armeb, armeb-linux-musleabihf) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-i386, -march=i586, -fPIE, i386-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-i386, -march=i586, i386-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-mips, mips-linux-musleabi) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-mips, mips-linux-musleabihf) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-mipsel, mipsel-linux-musleabi) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-mipsel, mipsel-linux-musleabihf) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-ppc, powerpc-linux-musleabihf) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-ppc64, -fPIE, powerpc64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-ppc64, powerpc64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-ppc64le, -fPIE, powerpc64le-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-ppc64le, powerpc64le-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-riscv64, UPX-UNSUPPORTED, -fPIE, riscv64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-riscv64, UPX-UNSUPPORTED, riscv64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-x86_64, x86_64-linux-gnu.2.17) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-x86_64, x86_64-linux-gnu.2.3.4) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (qemu-x86_64, x86_64-linux-musl) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (x86_64-macos-none) (push) Blocked by required conditions
CI / ${{ format('zigcc {0} {1}', matrix.zig_target, matrix.zig_pic) }} (x86_64-windows-gnu) (push) Blocked by required conditions
Working features confirmed: - High BSS modification (increased .text section size) - Modified import order (LoadLibraryA, GetProcAddress, VirtualProtect, ExitProcess) - Enhanced dummy imports (GetCurrentProcess, GetTickCount, QueryPerformanceCounter, GetModuleHandleA) - Section flag modifications to break UPX patterns - UPX string removal (changed to 'PACK') - Modified timestamp (0x12345678) Ready for additional evasion techniques based on should_evade.txt analysis.
2794 lines
94 KiB
Plaintext
2794 lines
94 KiB
Plaintext
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
|
||
"<Main>$", // 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(), "'<Module>'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 <Module> .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 “<Module>”, then this is a fakeee!
|
||
|
||
var isModuleCtorMultiple = false;
|
||
|
||
var currentCtorOffset = PE.findSignature(PE.getDosStubOffset() + PE.getDosStubSize(), PE.getSize() - PE.getOverlaySize(), "00'<Module>'00");
|
||
|
||
if (currentCtorOffset !== -1) {
|
||
var secondCtorNameOffset = PE.findSignature(currentCtorOffset + 10, PE.getSize() - PE.getOverlaySize(), "'<Module>'");
|
||
|
||
if (secondCtorNameOffset !== -1 && PE.readByte(secondCtorNameOffset + 8) !== 0x00) {
|
||
log(logType.net, "Fake <Module> 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 + "'";
|
||
}
|
||
} |