// Detect It Easy: detection rule file
// Authors: horsicq, ajax, adoxa, DosX

meta("packer", "UPX");

function isPatchedUPX() {
    var isDetected = 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 funcCounter = 0;

            if (PE.getImportFunctionName(0, 0) == "LoadLibraryA") {
                funcCounter++;
            }

            if (PE.getImportFunctionName(0, 1) == "GetProcAddress") {
                funcCounter++;
            }

            if (nNumberOfFunctions == 4) {
                if (PE.getImportFunctionName(0, 2) == "VirtualProtect") {
                    funcCounter++;
                }

                if (PE.getImportFunctionName(0, 3) == "ExitProcess") {
                    funcCounter++;
                }
            }

            if (nNumberOfFunctions >= 3 && funcCounter < 4) {
                if (PE.getImportFunctionName(0, 2) == "ExitProcess") {
                    funcCounter++;
                } else if (PE.isDll()) {
                    if (PE.getImportFunctionName(0, 2) == "VirtualProtect") {
                        funcCounter++;
                    }
                }
            }

            if (nNumberOfFunctions == 6) {
                if (PE.getImportFunctionName(0, 2) == "VirtualProtect") {
                    funcCounter++;
                }

                if (PE.getImportFunctionName(0, 3) == "VirtualAlloc") {
                    funcCounter++;
                }

                if (PE.getImportFunctionName(0, 4) == "VirtualFree") {
                    funcCounter++;
                }

                if (PE.getImportFunctionName(0, 5) == "ExitProcess") {
                    funcCounter++;
                }
            }

            if (funcCounter == 2 && nNumberOfFunctions == 2 ||
                funcCounter == 3 && nNumberOfFunctions >= 3 ||
                funcCounter == 4 && nNumberOfFunctions == 4 ||
                funcCounter == 6 && nNumberOfFunctions == 6) {
                isDetected = true;
            }

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

    return isDetected;
}

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.is64()) {
                sVersion = "3.91+";
            } else {
                sVersion = "0.89+";
            }
            sOptions = "modified";
        }
    }
}

function detect() {
    if (PE.isNet()) return; // Doesn't support .NET

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

    getUPXVersion();

    if (sVersion) {
        bDetected = true;
    }

    if (!bDetected) {
        bDetected = isPatchedUPX();
    } else {
        if (!sVersion) {
            if (PE.is64()) {
                sVersion = "3.91+"; // UPX 3.91 was the first version with a PE+ support.
            }

            sOptions = "modified";
        } else if (!PE.isSectionNamePresent("UPX0") && !sOptions) {
            sOptions = sOptions.append("modified");
        }
    }

    return result();
}