mirror of
https://github.com/horsicq/Detect-It-Easy.git
synced 2026-06-24 01:54:08 +00:00
Detect multiple encrypted PE algorithms in resources
Add KPA-based detection for encrypted PE files embedded in resources, supporting multiple algorithms (XOR/XNOR and ADD/SUB) and key lengths up to 20 bytes. Replace isXorPePresent with a generic isEncPePresent and track detectedAlgo. Introduce mode-aware decryptor and stronger PE header verification (checks for PE signature, Optional Header magic, NumberOfSections and Characteristics). Adjust resource size/scan thresholds and skip bitmap-like resources. Report encrypted payloads with detected algorithm type.
This commit is contained in:
parent
f58d4d00fe
commit
00603c95ef
1 changed files with 74 additions and 41 deletions
|
|
@ -7779,13 +7779,14 @@ function scanForMaliciousCode_NET_and_Native() {
|
|||
|
||||
|
||||
|
||||
var isXorPePresent = false;
|
||||
// Detect encrypted PE files in the resource section via KPA (Known Plaintext Attack)
|
||||
// Supports MULTIPLE algorithms (XOR, XNOR, ADD, SUB) and key lengths up to 20 bytes
|
||||
|
||||
var isEncPePresent = false;
|
||||
|
||||
var detectedAlgo = String();
|
||||
|
||||
// ==============================================================================
|
||||
// PRE-CALCULATION BLOCK: O(1) Offset mapping for key lengths up to 20 bytes
|
||||
// We leverage the e_res2 block (+40 to +59), which contains exactly 20 consecutive zeroes.
|
||||
// 20 bytes is the MATHEMATICAL MAXIMUM key length this specific O(1) KPA attack can support.
|
||||
// ==============================================================================
|
||||
var k0_off = new Array(21),
|
||||
k1_off = new Array(21),
|
||||
k3_off = new Array(21);
|
||||
|
|
@ -7793,46 +7794,69 @@ function scanForMaliciousCode_NET_and_Native() {
|
|||
for (var len = 1; len <= 20; len++) {
|
||||
k0_off[len] = 40 + ((len - (40 % len)) % len);
|
||||
k1_off[len] = 40 + ((1 + len - (40 % len)) % len);
|
||||
k3_off[len] = 40 + ((3 + len - (40 % len)) % len); // Maps to +3 (e_cblp upper byte)
|
||||
k3_off[len] = 40 + ((3 + len - (40 % len)) % len);
|
||||
}
|
||||
|
||||
// Strict PE header verification function.
|
||||
function verifyPeSignature(dataBuffer, peStartOffset, maxValidLfaNew, keyLength) {
|
||||
// Strict PE header verification function.
|
||||
// mode 0: Bitwise algorithms (XOR, XNOR)
|
||||
// mode 1: Arithmetic algorithms (ADD, SUB)
|
||||
function verifyPeSignature(dataBuffer, peStartOffset, maxValidLfaNew, keyLength, mode) {
|
||||
|
||||
// Universal decryptor: Maps any PE offset to its corresponding key byte stored in e_res2
|
||||
var getK = function (offset) {
|
||||
// Universal decryptor: Maps any PE offset to its corresponding encrypted zero in e_res2
|
||||
var getDecrypted = function (offset) {
|
||||
var eRes2Offset = 40 + ((offset % keyLength + keyLength - (40 % keyLength)) % keyLength);
|
||||
return dataBuffer[peStartOffset + eRes2Offset];
|
||||
var encryptedZero = dataBuffer[peStartOffset + eRes2Offset];
|
||||
var cipherByte = dataBuffer[peStartOffset + offset];
|
||||
|
||||
return mode === 0 ? (cipherByte ^ encryptedZero) : ((cipherByte - encryptedZero) & 0xFF);
|
||||
};
|
||||
|
||||
var lfaNewByte0 = dataBuffer[peStartOffset + 0x3C] ^ getK(0x3C),
|
||||
lfaNewByte1 = dataBuffer[peStartOffset + 0x3D] ^ getK(0x3D),
|
||||
lfaNewByte2 = dataBuffer[peStartOffset + 0x3E] ^ getK(0x3E),
|
||||
lfaNewByte3 = dataBuffer[peStartOffset + 0x3F] ^ getK(0x3F),
|
||||
// Get e_lfanew
|
||||
var lfaNewByte0 = getDecrypted(0x3C),
|
||||
lfaNewByte1 = getDecrypted(0x3D),
|
||||
lfaNewByte2 = getDecrypted(0x3E),
|
||||
lfaNewByte3 = getDecrypted(0x3F),
|
||||
lfaNewOffset = lfaNewByte0 | (lfaNewByte1 << 8) | (lfaNewByte2 << 16) | (lfaNewByte3 * 16777216);
|
||||
|
||||
// Sanity check for the e_lfanew pointer
|
||||
if (lfaNewOffset > 0x40 && lfaNewOffset < maxValidLfaNew) {
|
||||
var peHeaderOffset = peStartOffset + lfaNewOffset,
|
||||
peSigByte0 = dataBuffer[peHeaderOffset + 0] ^ getK(lfaNewOffset + 0),
|
||||
peSigByte1 = dataBuffer[peHeaderOffset + 1] ^ getK(lfaNewOffset + 1),
|
||||
peSigByte2 = dataBuffer[peHeaderOffset + 2] ^ getK(lfaNewOffset + 2),
|
||||
peSigByte3 = dataBuffer[peHeaderOffset + 3] ^ getK(lfaNewOffset + 3);
|
||||
var peHeaderOffset = peStartOffset + lfaNewOffset;
|
||||
|
||||
// 1. Verify PE Signature (PE\0\0)
|
||||
if (getDecrypted(peHeaderOffset + 0) === 0x50 &&
|
||||
getDecrypted(peHeaderOffset + 1) === 0x45 &&
|
||||
getDecrypted(peHeaderOffset + 2) === 0x00 &&
|
||||
getDecrypted(peHeaderOffset + 3) === 0x00) {
|
||||
|
||||
// 2. Verify Optional Header Magic (PE32=0x10B, PE64=0x20B)
|
||||
var magic = getDecrypted(peHeaderOffset + 0x18) | (getDecrypted(peHeaderOffset + 0x19) << 8);
|
||||
if (magic !== 0x010B && magic !== 0x020B) return false;
|
||||
|
||||
// 3. Verify NumberOfSections (IMAGE_FILE_HEADER)
|
||||
// Offset 0x06 from PE signature
|
||||
var numSections = getDecrypted(peHeaderOffset + 0x06) | (getDecrypted(peHeaderOffset + 0x07) << 8);
|
||||
if (numSections === 0 || numSections > 96) return false;
|
||||
|
||||
// 4. Verify Characteristics (IMAGE_FILE_HEADER)
|
||||
// Offset 0x16 from PE signature. 0x0002 is IMAGE_FILE_EXECUTABLE_IMAGE
|
||||
var characteristics = getDecrypted(peHeaderOffset + 0x16) | (getDecrypted(peHeaderOffset + 0x17) << 8);
|
||||
if (!(characteristics & 0x0002)) return false;
|
||||
|
||||
if (peSigByte0 === 0x50 && peSigByte1 === 0x45 && peSigByte2 === 0x00 && peSigByte3 === 0x00) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for (var i = 0; i < PE_Cached.numberOfUnmanagedResources && !isXorPePresent; i++) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < PE_Cached.numberOfUnmanagedResources && !isEncPePresent; i++) {
|
||||
var resourceOffset = PE.getResourceOffsetByNumber(i),
|
||||
resourceSize = PE.getResourceSizeByNumber(i);
|
||||
|
||||
if (resourceOffset > 0 && resourceSize > 0x800) { // More than 2 KB
|
||||
// Target actual payloads (> 768 bytes) and skip bitmaps
|
||||
if (resourceOffset > 0 && resourceSize > 0x300 && !PE.compare("28 00 00 00 ?? ?? 00 00 ?? ?? 00 00 01 00 ?? 00", resourceOffset)) {
|
||||
|
||||
var maxScanSize = Math.min(resourceSize, 0x1000),
|
||||
var maxScanSize = Math.min(resourceSize, 0x2000),
|
||||
hexSignature = PE.getSignature(resourceOffset, maxScanSize),
|
||||
dataBuffer = new Array(maxScanSize);
|
||||
|
||||
|
|
@ -7846,34 +7870,43 @@ function scanForMaliciousCode_NET_and_Native() {
|
|||
var maxSearchIndex = maxScanSize - 0x100;
|
||||
|
||||
for (var j = 0; j < maxSearchIndex; j++) {
|
||||
var byte0 = dataBuffer[j],
|
||||
byte1 = dataBuffer[j + 1],
|
||||
key0 = byte0 ^ 0x4D,
|
||||
key1 = byte1 ^ 0x5A;
|
||||
var b0 = dataBuffer[j],
|
||||
b1 = dataBuffer[j + 1],
|
||||
e0_bit = b0 ^ 0x4D,
|
||||
e1_bit = b1 ^ 0x5A;
|
||||
|
||||
if (key0 === 0x00 && key1 === 0x00) continue;
|
||||
if (e0_bit === 0x00 && e1_bit === 0x00) continue;
|
||||
|
||||
var e0_math = (b0 - 0x4D) & 0xFF,
|
||||
e1_math = (b1 - 0x5A) & 0xFF,
|
||||
d3 = dataBuffer[j + 3];
|
||||
|
||||
// Test all lengths up to 20 bytes (The physical limit of the e_res2 zero-block)
|
||||
for (var L = 1; L <= 20; L++) {
|
||||
if (dataBuffer[j + k0_off[L]] === key0 &&
|
||||
dataBuffer[j + k1_off[L]] === key1 &&
|
||||
dataBuffer[j + 3] === dataBuffer[j + k3_off[L]]) {
|
||||
|
||||
if (verifyPeSignature(dataBuffer, j, maxScanSize - j - 4, L)) {
|
||||
isXorPePresent = true;
|
||||
break;
|
||||
if (d3 === dataBuffer[j + k3_off[L]]) {
|
||||
var c0 = dataBuffer[j + k0_off[L]];
|
||||
|
||||
if (c0 === e0_bit && dataBuffer[j + k1_off[L]] === e1_bit) {
|
||||
if (verifyPeSignature(dataBuffer, j, maxScanSize - j - 4, L, 0)) {
|
||||
detectedAlgo = "XOR/XNOR"; isEncPePresent = true; break;
|
||||
}
|
||||
} else if (c0 === e0_math && dataBuffer[j + k1_off[L]] === e1_math) {
|
||||
if (verifyPeSignature(dataBuffer, j, maxScanSize - j - 4, L, 1)) {
|
||||
detectedAlgo = "ADD/SUB"; isEncPePresent = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isXorPePresent) break;
|
||||
|
||||
if (isEncPePresent) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isXorPePresent) {
|
||||
if (isEncPePresent) {
|
||||
verdicts.push({
|
||||
type: "XOR-encrypted payload",
|
||||
version: String(),
|
||||
type: "Encrypted payload",
|
||||
version: detectedAlgo,
|
||||
details: mayBeInfected
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue