// Detect It Easy: detection rule file format
// History:
// 1:00 06.10.2013 x64 support (ajax)
//      2014-05-25 rewritten (adoxa)

init("packer", "UPX");

function isPatchedUPX() {
    var nResult = 0,
        nNumberOfFunctions = PE.getNumberOfImportThunks(0);

    if ((nNumberOfFunctions > 1) && (nNumberOfFunctions < 7)) {
        if ((PE.getSizeOfCode()) && (PE.getSizeOfUninitializedData()) && (PE.section.length > 2) && (PE.section[0].FileSize == 0)) {
            var nCount = 0;
            if (PE.getImportFunctionName(0, 0) == "LoadLibraryA") {
                nCount++;
            }
            if (PE.getImportFunctionName(0, 1) == "GetProcAddress") {
                nCount++;
            }
            if (nNumberOfFunctions == 4) {
                if (PE.getImportFunctionName(0, 2) == "VirtualProtect") {
                    nCount++;
                }
                if (PE.getImportFunctionName(0, 3) == "ExitProcess") {
                    nCount++;
                }
            }
            if ((nNumberOfFunctions >= 3) && (nCount < 4)) {
                if (PE.getImportFunctionName(0, 2) == "ExitProcess") {
                    nCount++;
                } else if (PE.isDll()) {
                    if (PE.getImportFunctionName(0, 2) == "VirtualProtect") {
                        nCount++;
                    }
                }
            }
            if (nNumberOfFunctions == 6) {
                if (PE.getImportFunctionName(0, 2) == "VirtualProtect") {
                    nCount++;
                }
                if (PE.getImportFunctionName(0, 3) == "VirtualAlloc") {
                    nCount++;
                }
                if (PE.getImportFunctionName(0, 4) == "VirtualFree") {
                    nCount++;
                }
                if (PE.getImportFunctionName(0, 5) == "ExitProcess") {
                    nCount++;
                }
            }

            if (nCount == 2 && nNumberOfFunctions == 2) {
                nResult = 1;
            }
            if (nCount == 3 && nNumberOfFunctions >= 3) {
                nResult = 1;
            }
            if (nCount == 4 && nNumberOfFunctions == 4) {
                nResult = 1;
            }
            if (nCount == 6 && nNumberOfFunctions == 6) {
                nResult = 1;
            }

            if (nResult) {
                if (PE.getAddressOfEntryPoint() == 0x00001018) {
                    // (Win)Upack
                    nResult = 0;
                }
            }
        }
    }

    return nResult;
}

function getUPXOptions(nOffset) {
    var nMethod = PE.readByte(nOffset + 2),
        nLevel = PE.readByte(nOffset + 3),
        sCompression = "";

    switch (nMethod) // From http://sourceforge.net/p/upx/code/ci/default/tree/src/conf.h
    {
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 9:
        case 10:
            sCompression = "NRV";
            break;
        case 14:
            sCompression = "LZMA";
            break;
        case 15:
            sCompression = "zlib";
            break;
    }

    if (sCompression != "") {
        sOptions = sOptions.append(sCompression);
        if (nLevel == 8) {
            sOptions = sOptions.append("best");
        } else {
            sOptions = sOptions.append("brute");
        }
    }
}

function getUPXVersion() {
    var nOffset1 = PE.findString(0, 1024, "$Id: UPX "),
        nOffset2 = PE.findString(0, 1024, "UPX!");

    if (nOffset2 != -1) {
        getUPXOptions(nOffset2 + 4);
    }

    if (nOffset1 != -1) {
        sVersion = PE.getString(nOffset1 + 9, 4);
    }
    if ((sVersion == "") && (nOffset2 != -1)) {
        sVersion = PE.getString(nOffset2 - 5, 4);
    }

    if (sVersion != "") {
        if ((+sVersion).toString() != sVersion) {
            if (PE.isPEPlus()) {
                sVersion = "3.91+";
            } else {
                sVersion = "0.89+";
            }
            sOptions = "modified";
        }
    }
}

function detect(bShowType, bShowVersion, bShowOptions) {
    if (!PE.isPEPlus()) {
        var nEP = PE.compareEP("807C") ? 27 : 0;
        if (PE.compareEP("60BE........8DBE........57", nEP)) {
            bDetected = true;
        } else if (PE.compareEP("60e8000000005883e8..508db8........578db0........83cd..31db9090909001db75")) {
            sVersion = "0.70";
            bDetected = true;
        }
    } else {
        var nEP = PE.compareEP("4889") ? 24 : 0;
        if (PE.compareEP("53565755488D35........488DBE........57", nEP)) {
            bDetected = true;
        }
    }

    getUPXVersion();
    if (sVersion != "") {
        bDetected = true;
    }

    if (!bDetected) {
        bDetected = isPatchedUPX();
    }

    if ((bDetected) && (sVersion == "")) {
        if (PE.isPEPlus()) {
            sVersion = "3.91+"; // UPX 3.91 was the first version with a PE+ support.
        }
        sOptions = "modified";
    }

    return result(bShowType, bShowVersion, bShowOptions);
}