public class GraphicBufferTest implements VulnerabilityTest {

    private final static String TAG = "GraphicsBuffer";

    private enum AndroidVersions{
        JELLYBEAN_AND_EARLIER(0),
        KITKAT_AND_LOLLIPOP(1),
        MARSHMELLOW(2),
        OTHER(99);

        private final int ver;
        private AndroidVersions(int ver)
        {
            this.ver = ver;
        }
        public int getIntCode(){
            return ver;
        };
    }

    static {
        System.loadLibrary("graphicsbufferoverflow");
    }

    @Override
    public String getCVEorID() {
        return "CVE-2015-1474";
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM8);
        return archs;
    }

    private native int checkGraphicsBuffer(int ver);

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal;

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            Log.d(TAG, "Running with KK and Lollipop types");
            checkVal = checkGraphicsBuffer(AndroidVersions.KITKAT_AND_LOLLIPOP.getIntCode());
        }else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
            checkVal = checkGraphicsBuffer(AndroidVersions.JELLYBEAN_AND_EARLIER.getIntCode());
        }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            checkVal = checkGraphicsBuffer(AndroidVersions.MARSHMELLOW.getIntCode());
        }else{
            checkVal = checkGraphicsBuffer(AndroidVersions.OTHER.getIntCode());
        }

        //TODO: If we declare a different type of socket do we still need internet permission?
        if(checkVal == 0) {
            return false;
        }else if(checkVal == 1) {
            return true;
        }else {
            //TODO: grab more information about failure, errno and error string
            throw new Exception("Error running test");
        }
    }
} public class CVE_2015_6602 implements VulnerabilityTest {

    private static final String TAG = "cve-2015-6602";

    public CVE_2015_6602() {}

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM);
        return archs;
    }

    @Override
    public String getCVEorID() {
        return "CVE-2015-6602";
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        String binaryTester = extractBinary(context);
        TestResult result = execute(context, 5, binaryTester);
        Log.d(TAG, "Test result: " + result.toString());
        if (result.isOk()) {
            return result.getCode() != 0;
        } else if (result == TestResult.CRASH) {
            return true;
        } else if (result == TestResult.HANG) {
            throw new Exception("Test is hanging");
        } else if (result == TestResult.ERROR) {
            throw new Exception("Test error");
        }
        throw new Exception("Unknown test result");
    }

    private String extractBinary(Context context) throws IOException {
        File dataDir = context.getFilesDir();

        final String extractedBinaryTester = dataDir.getAbsolutePath() + File.separator + getNativeAppName();
        extractAsset(context, "stagefright" + File.separator + getNativeAppName(),
                extractedBinaryTester);
        Process p = Runtime.getRuntime().exec("chmod 700 " + extractedBinaryTester);
        try { p.waitFor(); } catch (Exception e){}
        return extractedBinaryTester;
    }

    public static String getNativeAppName() {
        return "utilcheck-pie";
    }
} public class CVE_2015_6608 implements VulnerabilityTest {

    private static final String TAG = "CVE-2015-6608";

    @Override
    public String getCVEorID() {
        return "CVE-2015-6608";
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        /*
            The patch includes logging messages for when the vulnerable code paths are hit.
            We can simply look for the inclusion of the strings in the libraries to determine
            whether or not the device has a patched libstagefright.so
         */

        File stagefrightlib = new File("/system/lib/libstagefright.so");
        File stagefrightlib64 = new File("/system/lib64/libstagefright.so");

        File softAAClib = new File("/system/lib/libstagefright_soft_aacdec.so");

        File libvorbisidec = new File("/system/lib/libvorbisidec.so");

        if(!stagefrightlib.exists() || !stagefrightlib.isFile()){
            throw new Exception("libstagefright.so doesn't exist or is not a file");
        }

        if(!softAAClib.exists()){
            throw new Exception("libstagefright_soft_aacdec.so does not exist");
        }

        if(!libvorbisidec.exists()){
            throw new Exception("libvorbisidec.so does not exist");
        }


        ByteArrayOutputStream libStageFrightBAOS = new ByteArrayOutputStream((int)stagefrightlib.length());
        BinaryAssets.copy(new FileInputStream(stagefrightlib), libStageFrightBAOS);
        byte[] libstagefrightSO = libStageFrightBAOS.toByteArray();


        ByteArrayOutputStream libaacdecBAOS = new ByteArrayOutputStream((int)softAAClib.length());
        BinaryAssets.copy(new FileInputStream(softAAClib), libaacdecBAOS);
        byte[] libaacdecSO = libaacdecBAOS.toByteArray();

        ByteArrayOutputStream libvorbisidecBAOS = new ByteArrayOutputStream((int)libvorbisidec.length());
        BinaryAssets.copy(new FileInputStream(libvorbisidec), libvorbisidecBAOS);
        byte[] libvorbisidecSO = libvorbisidecBAOS.toByteArray();



        KMPMatch binMatcher = new KMPMatch();

        int indexOf = binMatcher.indexOf(libstagefrightSO, "b/23680780".getBytes());
        boolean libstagefrightVulnerableToBug23680780 = indexOf == -1;
        indexOf = binMatcher.indexOf(libvorbisidecSO, "b/23881715".getBytes());
        boolean libstagefrightvulnerableToBug23881715 = indexOf == -1;
        indexOf = binMatcher.indexOf(libaacdecSO, "b/23876444".getBytes());
        boolean libstagefrightVulnerableToBug23876444 = indexOf == -1;


        Log.d(TAG, "libstagefrightVulnerableToBug23680780: " + libstagefrightVulnerableToBug23680780);
        Log.d(TAG, "libsatagefrightvulnerableToBug23881715: " + libstagefrightvulnerableToBug23881715);
        Log.d(TAG, "libstagefrightVulnerableToBug23876444: " + libstagefrightVulnerableToBug23876444);

        if(Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT){
            return libstagefrightvulnerableToBug23881715;
        }

        //The rest only affect L and M
        if     (Build.VERSION.SDK_INT != Build.VERSION_CODES.M &&
                Build.VERSION.SDK_INT != Build.VERSION_CODES.LOLLIPOP &&
                Build.VERSION.SDK_INT != Build.VERSION_CODES.LOLLIPOP_MR1){
            return false;
        }

        return  libstagefrightVulnerableToBug23680780 ||
                libstagefrightvulnerableToBug23881715 ||
                libstagefrightVulnerableToBug23876444;

    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        List<CPUArch> supportedArchs = new ArrayList<>();
        supportedArchs.add(CPUArch.ARM);
        supportedArchs.add(CPUArch.ARM7);
        supportedArchs.add(CPUArch.ARM8);
        supportedArchs.add(CPUArch.X86);
        return supportedArchs;
    }
} public class CVE_2015_6616  implements VulnerabilityTest {

/*
    CVE	Bug(s) with AOSP links	Severity	Affected versions	Date reported
    CVE-2015-6616
    ANDROID-24630158	Critical	6.0 and below	Google Internal
    ANDROID-23882800	Critical	6.0 and below	Google Internal
    ANDROID-17769851	Critical	5.1 and below	Google Internal
    ANDROID-24441553	Critical	6.0 and below	Sep 22, 2015
    ANDROID-24157524	Critical	6.0	Sep 08, 2015

    ANDROID-24630158 https://android.googlesource.com/platform%2Fframeworks%2Fav/+/257b3bc581bbc65318a4cc2d3c22a07a4429dc1d
    ANDROID-23882800 https://android.googlesource.com/platform%2Fframeworks%2Fav/+/0d35dd2068d6422c3c77fb68f248cbabf3d0b10c
    ANDROID-17769851 https://android.googlesource.com/platform%2Fframeworks%2Fav/+/dedaca6f04ac9f95fabe3b64d44cd1a2050f079e
    ANDROID-24441553 https://android.googlesource.com/platform%2Fframeworks%2Fav/+/5d101298d8b0a78a1dc5bd26dbdada411f4ecd4d
    ANDROID-24157524 https://android.googlesource.com/platform%2Fexternal%2Flibavc/+/2ee0c1bced131ffb06d1b430b08a202cd3a52005
*/

    private static final String TAG = "CVE-2015-6616";

    @Override
    public String getCVEorID() {
        return "CVE-2015-6616";
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        File stagefrightlib = new File("/system/lib/libstagefright.so");
        if(!stagefrightlib.exists() || !stagefrightlib.isFile()){
            throw new Exception("libstagefright.so doesn't exist or is not a file");
        }

        File stagefrightsoftmp4lib = new File("/system/lib/libstagefright_soft_mpeg4dec.so");
        if(!stagefrightsoftmp4lib.exists() || !stagefrightsoftmp4lib.isFile()){
            throw new Exception("libstagefright_soft_mpeg4dec.so doesn't exist or is not a file");
        }


        ByteArrayOutputStream libStageFrightBAOS = new ByteArrayOutputStream((int)stagefrightlib.length());
        BinaryAssets.copy(new FileInputStream(stagefrightlib), libStageFrightBAOS);
        byte[] libstagefrightSO = libStageFrightBAOS.toByteArray();

        KMPMatch binMatcher = new KMPMatch();

        int indexOf = binMatcher.indexOf(libstagefrightSO, "bogus max input size: %zu".getBytes());
        boolean libstagefrightVulnerableToBug17769851 = indexOf == -1;

        indexOf = binMatcher.indexOf(libstagefrightSO, "b/24441553, b/24445122".getBytes());
        boolean libstagefrightVulnerableToBug24441553 = indexOf == -1;

        ByteArrayOutputStream libStageFrightsoftmp4BAOS = new ByteArrayOutputStream((int)stagefrightsoftmp4lib.length());
        BinaryAssets.copy(new FileInputStream(stagefrightsoftmp4lib), libStageFrightsoftmp4BAOS);
        byte[] libstagefrightsoftmp4SO = libStageFrightsoftmp4BAOS.toByteArray();

        indexOf = binMatcher.indexOf(libstagefrightsoftmp4SO, "b/24630158".getBytes());
        boolean libstagefrightVulnerableToBug24630158 = indexOf == -1;

        Log.d(TAG, "libstagefrightVulnerableToBug24630158: " + libstagefrightVulnerableToBug24630158);
        Log.d(TAG, "libstagefrightVulnerableToBug17769851: " + libstagefrightVulnerableToBug17769851);
        Log.d(TAG, "libstagefrightVulnerableToBug24441553: " + libstagefrightVulnerableToBug24441553);


        return libstagefrightVulnerableToBug24630158 ||
                libstagefrightVulnerableToBug17769851 ||
                libstagefrightVulnerableToBug24441553;
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        List<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }
} public class StageFright {

    private static final String TAG = "StageFright";

    static {
        System.loadLibrary("stagefright");
    }

    private static void exec(String executionCommand) throws Exception {
        Process p = Runtime.getRuntime().exec(executionCommand);
        p.waitFor();
    }

    public static List<VulnerabilityTest> getTests(Context context) {
        try {
            File dataDir = context.getFilesDir();


            final String stageFrightAssetPath = "stagefright/stagefright_media_files.zip";
            final String extractionDir = dataDir.getAbsolutePath() + File.separator + "stagefright_media" + File.separator;

            extractZipAsset(context, stageFrightAssetPath, extractionDir);

            File stagefrightMediaFilesFolder = new File(extractionDir);

            List<VulnerabilityTest> tests = new ArrayList<>();

            final String extractedBinaryTester = dataDir.getAbsolutePath() + File.separator + getNativeAppName();
            extractAsset(context, "stagefright" + File.separator + getNativeAppName(),
                    extractedBinaryTester);
            exec("chmod 700 " + extractedBinaryTester);

            Log.d(TAG, "Is directory: "  + stagefrightMediaFilesFolder.isDirectory());
            File[] mediaFiles = stagefrightMediaFilesFolder.listFiles();

            for (final File mediaFile : stagefrightMediaFilesFolder.listFiles()) {
                tests.add(new VulnerabilityTest() {
                    @Override
                    public String getCVEorID() {
                        return FilenameUtils.removeExtension(mediaFile.getName());
                    }

                    @Override
                    public List<CPUArch> getSupportedArchitectures() {
                        ArrayList<CPUArch> archs = new ArrayList<>();
                        archs.add(CPUArch.ARM);
                        archs.add(CPUArch.ARM7);
                        archs.add(CPUArch.ARM8);
                        return archs;
                    }

                    @Override
                    public boolean isVulnerable(Context context) throws Exception {
                        TestResult result = execute(context, 5, extractedBinaryTester, mediaFile.getAbsolutePath());
                        Log.d(TAG, "Test result: " + result.toString());
                        if (result.isOk()) {
                            return false;
                        } else if (result == TestResult.CRASH) {
                            return true;
                        } else if (result == TestResult.HANG) {
                            throw new Exception("Test is hanging");
                        } else if (result == TestResult.ERROR) {
                            throw new Exception("Test error");
                        }
                        throw new Exception("Unknown test result");
                    }
                });
            }
            return tests;
        }catch(Exception e){
            e.printStackTrace();
        }

        return new ArrayList<>();
    }

    public static String getNativeAppName() {
        return Build.VERSION.SDK_INT >= 16 ? "stagefrightCheck-pie" : "stagefrightCheck";
    }
} public class OpenSSLTransientBug implements VulnerabilityTest {

    static {
        System.loadLibrary("x509serializationhelper");
    }

    private native long getPositiveIntLocation();

    private static final String TAG = "openSSLSerializationBug";
    @Override
    public String getCVEorID() {
        return "CVE-2015-3825";
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<CPUArch>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM8);
        return archs;
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {

        /*
             AOSP switched from bouncy castle to conscrypt in 4.4
             This can be validated by looking at the preloaded classes:
             https://github.com/android/platform_frameworks_base/blob/jb-mr2-release/preloaded-classes
             https://github.com/android/platform_frameworks_base/blob/kitkat-release/preloaded-classes
             Notice that conscrypt classes exist in KitKat 4.4 SDK 19
             but not in Jellybean 4.3 MR SDK 18
         */
        
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
            return false;
        }

        Class openSSLX509CertificateClass = Class.forName("com.android.org.conscrypt.OpenSSLX509Certificate");

        ObjectStreamClass clDesc = ObjectStreamClass.lookup(openSSLX509CertificateClass);

        if (clDesc == null) {
            //TODO this is bad
            Log.d(TAG, "clDESC is null");
            throw new Exception("clDesc is null for OpenSSLECPrivateKey");
        }

        // Set our fake class's serialization UID.
        Field targetUID = ZpenSSLX509Certificate.class.getDeclaredField("serialVersionUID");
        targetUID.setAccessible(true);
        targetUID.set(null, clDesc.getSerialVersionUID());


        final byte[] impostorBytes;
        // Serialization
        {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            long decrementAddress = getPositiveIntLocation();
            Log.d(TAG, "Decrement address: " + Long.toHexString(decrementAddress));
            oos.writeObject(new ZpenSSLX509Certificate(decrementAddress - 0x10));
            oos.close();
            impostorBytes = baos.toByteArray();
        }

        // Fix class name
        {
            boolean fixed = false;
            for (int i = 0; i < impostorBytes.length - 4; i++) {
                if (impostorBytes[i] == 'Z' && impostorBytes[i + 1] == 'p'
                        && impostorBytes[i + 2] == 'e' && impostorBytes[i + 3] == 'n') {
                    impostorBytes[i] = 'O';
                    fixed = true;
                    break;
                }
            }
        }

        // Deserialization
        {
            ByteArrayInputStream bais = new ByteArrayInputStream(impostorBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            Object cert = ois.readObject();
            ois.close();
            Method m = openSSLX509CertificateClass.getDeclaredMethod("getContext");
            m.setAccessible(true);
            long ctx = (long) m.invoke(cert);
            if(ctx == 0L){
                return false;
            }
        }
            return true;
    }
} public class ZipBug8219321 implements VulnerabilityTest {

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }

    public String getCVEorID(){
        return "CVE-2013-4787";
    }

    private static void replaceBytes(byte[] original, byte[] replacement, byte[] buffer) throws Exception {
        // Gotcha here: original and replacement must be the same length
        if(original.length != replacement.length)
           throw new Exception("Original Length is not equal to replacement length");

        boolean found;
        for(int i=0; i < buffer.length - original.length; i++) {
            found = false;
            if (buffer[i] == original[0]) {
                found = true;
                for (int j=0; j < original.length; j++) {
                    if (buffer[i+j] != original[j]) {
                        found = false;
                        break;
                    }
                }
            }
            if (found) {
                for (int j=0; j < original.length; j++) {
                    buffer[i+j] = replacement[j];
                }
            }
        }
    }

    /**
     * Make sure we don't fail silently for duplicate entries.
     * b/8219321
     */
    public boolean isVulnerable(Context context) throws Exception {
        String entryName = "test_file_name1";
        String tmpName = "test_file_name2";

        // create the template data
        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
        ZipOutputStream out = new ZipOutputStream(bytesOut);
        ZipEntry ze1 = new ZipEntry(tmpName);
        out.putNextEntry(ze1);
        out.closeEntry();
        ZipEntry ze2 = new ZipEntry(entryName);
        out.putNextEntry(ze2);
        out.closeEntry();
        out.close();

        // replace the bytes we don't like
        byte[] buf = bytesOut.toByteArray();
        replaceBytes(tmpName.getBytes(), entryName.getBytes(), buf);

        // write the result to a file
        File outputDir = context.getCacheDir();
        File badZip = File.createTempFile("prefix", "extension", outputDir);
        badZip.deleteOnExit();
        FileOutputStream outstream = new FileOutputStream(badZip);
        outstream.write(buf);
        outstream.close();

        // see if we can still handle it
        try (ZipFile bad = new ZipFile(badZip)) {
            return true;
        } catch (ZipException expected) {
            if(expected.getMessage().contains("Duplicate entry name"))
                return false;
            else
                throw expected;
        }
    }
} public class ZipBug9695860 implements VulnerabilityTest {
    /*

     */
    @Override
    public String getCVEorID() {
        return "ZipBug 9695860";
    }



    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }

    private long getCRC(byte[]data){
        CRC32 crc = new CRC32();
        crc.reset();
        crc.update(data);
        return crc.getValue();
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        String fileName1 = "test_file_name1";
        String fileName2 = "test_file_name2";

        byte []file1Data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes();
        byte []file2data = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB".getBytes();

        // create the template data
        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
        ModdedZipArchiveOutputStream out = new ModdedZipArchiveOutputStream(bytesOut);

        ModdedZipArchiveEntry ze1 = new ModdedZipArchiveEntry(fileName1);
        ze1.setCrc(getCRC(file1Data));
        ze1.setSize(file1Data.length);
        ze1.setMethod(ZipEntry.STORED);

        ModdedZipArchiveEntry ze2 = new ModdedZipArchiveEntry(fileName2);
        ze2.setCrc(getCRC(file2data));
        ze2.setSize(file2data.length);
        ze2.setMethod(ZipEntry.STORED);

        out.putArchiveEntry(ze1);
        out.write(file1Data);
        out.closeArchiveEntry();

        out.putArchiveEntry(ze2);
        out.write(file2data);
        out.closeArchiveEntry();

        List<ZipArchiveEntry> normalEntries = new ArrayList<>();
        List<ZipArchiveEntry> moddedEntries = new ArrayList<>();
        normalEntries.add(ze1);
        moddedEntries.add(ze2);

        out.flush();
        out.finish(normalEntries, moddedEntries);

        // write the result to a file
        File outputDir = context.getCacheDir();
        File badZip = File.createTempFile("prefix", "extension", outputDir);
        badZip.deleteOnExit();
        FileOutputStream outstream = new FileOutputStream(badZip);
        outstream.write(bytesOut.toByteArray());
        outstream.close();

        // see if we can still handle it
        ZipFile bad = new ZipFile(badZip);
        final Enumeration<? extends ZipEntry> entries = bad.entries();
        while ( entries.hasMoreElements() )
        {
            final ZipEntry entry = entries.nextElement();
            if(entry.getName().equals(fileName2))
                return true;
        }
        bad.close();
        return false;

    }
} public class ZipBug9950697 implements VulnerabilityTest {

    public String getCVEorID(){
        return "ZipBug 9950697";
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }

    public boolean isVulnerable(Context context) throws Exception {
        ByteArrayOutputStream fileNameFunz = new ByteArrayOutputStream();
        ModdedZipArchiveOutputStream zaos = new ModdedZipArchiveOutputStream(fileNameFunz);
        ZipArchiveEntry zae = new ZipArchiveEntry("test_file");
        byte [] originalData = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes();
        byte [] modifiedData = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB".getBytes();
        zae.setMethod(ZipEntry.STORED);
        zae.setSize(originalData.length);
        CRC32 checker = new CRC32();
        checker.update(originalData);
        zae.setCrc(checker.getValue());
        zaos.putArchiveEntry(zae, originalData);
        zaos.write(originalData);
        zaos.closeArchiveEntry();
        zaos.writeRaw(modifiedData, 0, modifiedData.length);

        for(int i = 0; i < originalData.length - modifiedData.length; i++){
            zaos.writeRaw(new byte[]{0}, 0, 1);
        }

        zaos.flush();

        List<ZipArchiveEntry> entries = new ArrayList<>();
        entries.add(zae);

        zaos.finish(entries, new ArrayList<ZipArchiveEntry>());

        byte [] testZip = fileNameFunz.toByteArray();

        // write the result to a file
        File outputDir = context.getCacheDir();
        File badZip = File.createTempFile("prefix", "extension", outputDir);
        badZip.deleteOnExit();
        FileOutputStream outstream = new FileOutputStream(badZip);
        outstream.write(testZip);
        outstream.close();

        // see if we can still handle it
        ZipFile bad = new ZipFile(badZip);
        if(bad.size() != 1)
            throw new ZipException("Unexpected number of entries");

        ZipEntry ze = bad.entries().nextElement();
        DataInputStream dis = new DataInputStream(bad.getInputStream(ze));
        byte [] buf = new byte[(int)ze.getSize()];
        dis.readFully(buf);
        bad.close();
        return new String(buf).startsWith("AAAAAAAAAAAAAAA");
    }
} public class SystemUtils {
    private SystemUtils() {
    }

    public static int ProcfindPidFor(String executable) throws IOException {
        File f = new File("/proc");
        for (File d : f.listFiles()) {
            String cmdLineString = d.getAbsolutePath() + "/cmdline";
            File cmdLine = new File(cmdLineString);
            if (cmdLine.exists()) {
                BufferedReader in = null;
                try {
                    in = new BufferedReader(new FileReader(cmdLine));
                    String line = in.readLine();
                    if ((line != null) && line.startsWith(executable)) {
                        return Integer.decode(d.getName());
                    }
                } finally {
                    if (in != null) {
                        in.close();
                    }
                }
            }
        }
        throw new RuntimeException("should never get here");
    }

    //https://stackoverflow.com/questions/2641111/where-is-android-os-systemproperties
    /**
     * Get the value for the given key.
     * @return an empty string if the key isn't found
     * @throws IllegalArgumentException if the key exceeds 32 characters
     */
    public static String propertyGet(Context context, String key) throws IllegalArgumentException {

        String ret= "";

        try{

            ClassLoader cl = context.getClassLoader();
            @SuppressWarnings("rawtypes")
            Class SystemProperties = cl.loadClass("android.os.SystemProperties");

            //Parameters Types
            @SuppressWarnings("rawtypes")
            Class[] paramTypes= new Class[1];
            paramTypes[0]= String.class;

            Method get = SystemProperties.getMethod("get", paramTypes);

            //Parameters
            Object[] params= new Object[1];
            params[0]= new String(key);

            ret= (String) get.invoke(SystemProperties, params);

        }catch( IllegalArgumentException iAE ){
            throw iAE;
        }catch( Exception e ){
            ret= "";
            //TODO
        }

        return ret;

    }

} public class CVE_2011_1149 implements VulnerabilityTest {
    static {
        System.loadLibrary("cve-2011-1149");
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        return archs;
    }



    /*
        Fixes:

        https://android.googlesource.com/kernel/common/+/c98a285075f26e2b17a5baa2cb3eb6356a75597e
        https://android.googlesource.com/platform/system/core/+/25b15be9120bcdaa0aba622c67ad2c835d9e91ca

        Discussion:
        https://groups.google.com/forum/#!topic/android-security-discuss/Ffl2WMiNaCc

        Exploits:
        http://c-skills.blogspot.com/2011/01/adb-trickery-again.html
        https://github.com/tmzt/g2root-kmod/blob/master/scotty2/psneuter/psneuter.c

        Other:
        https://github.com/ucam-cl-dtg/android-com.device.vulnerability.vulnerabilities/blob/master/input/com.device.vulnerability.vulnerabilities/KillingInTheNameOf_psneuter_ashmem.json
        http://www.cvedetails.com/cve/CVE-2011-1149/
     */


    @Override
    public String getCVEorID() {
        return "CVE-2011-1149";
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal = checkASHMemMap();

        if(checkVal == 0) {
            return false;
        }else if(checkVal == 1) {
            return true;
        }else {
            //TODO: grab more information about failure, errno and error string
            throw new Exception("Error running test");
        }
    }

    private native int checkASHMemMap();

} public class CVE_2013_6282 implements VulnerabilityTest {
   static {
     System.loadLibrary("CVE-2013-6282");
   }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM8);
        return archs;
    }

    @Override
    public String getCVEorID() {
        return "CVE-2013-6282";
    }

    private native int checkGetPutUser();

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal = checkGetPutUser();

        //TODO: If we declare a different type of socket do we still need internet permission?
        if(checkVal == 0) {
            return false;
        }else if(checkVal == 1) {
            return true;
        }else {
            //TODO: grab more information about failure, errno and error string
            throw new Exception("Error running test");
        }
    }
} public class CVE_2014_3153 implements VulnerabilityTest {
    static {
        System.loadLibrary("cve-2014-3153");
    }


    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM8);
        return archs;
    }

    @Override
    public String getCVEorID() {
        return "CVE-2014-3153";
    }

    private native int checkFutex();

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal = checkFutex();

        if(checkVal == 22) {
            return false;
        }else if(checkVal == 14) {
            return true;
        }else {
            throw new Exception("Error running test. Errno: " + checkVal);
        }
    }
} public class CVE_2014_4943 implements VulnerabilityTest {
    static {
        System.loadLibrary("CVE-2014-4943");
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM8);
        return archs;
    }

    @Override
    public String getCVEorID() {
        return "CVE-2014-4943";
    }

    private native int checkL2TP();

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal = checkL2TP();

        if(checkVal == 0) {
            return false;
        }else if(checkVal == 1) {
            return true;
        }else {
            throw new Exception("Error running test. Errno: " + checkVal);
        }
    }
} public class CVE_2015_3636 implements VulnerabilityTest {
    static {
        System.loadLibrary("pingpong");
    }

    /*
       Fix: https://github.com/torvalds/linux/commit/a134f083e79fb4c3d0a925691e732c56911b4326
       Discussion: http://www.openwall.com/lists/oss-security/2015/05/02/11
       implementation: https://github.com/scoty755/libping_unhash_exploit_POC
     */
    @Override
    public String getCVEorID() {
        return "CVE-2015-3636";
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ARM);
        archs.add(CPUArch.ARM7);
        archs.add(CPUArch.ARM8);
        return archs;
    }


    private native int checkPingPong();

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal = checkPingPong();

        if(checkVal == 0) {
            return false;
        }else if(checkVal == 1) {
            return true;
        }else {
            throw new Exception("Error running test. Errno: " + checkVal);
        }
    }
} public class CVE20151528 implements VulnerabilityTest {

    private final static String TAG = "CVE-2015-1528";

    static {
        System.loadLibrary("cve20151528");
    }

    @Override
    public String getCVEorID() {
        return "CVE-2015-1528";
    }

    private native int doCheck();

    @Override
    public boolean isVulnerable(Context context) throws Exception {
        int checkVal = doCheck();

        if(checkVal == 0) {
            return false;
        }else if(checkVal == 1) {
            return true;
        }else {
            Log.d(TAG, "Got a return value of " + checkVal);
            //TODO: grab more information about failure, errno and error string
            throw new Exception("Error running test");
        }
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        List<CPUArch> supportedArchs = new ArrayList<>();
        supportedArchs.add(CPUArch.ARM);
        supportedArchs.add(CPUArch.ARM7);
        supportedArchs.add(CPUArch.X86);
        return supportedArchs;
    }
} public class CVE20153860 implements VulnerabilityTest {

    @Override
    public String getCVEorID() {
        return "CVE-2015-3860";
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {

        //versions 4.4 and below seems to not be vulnerable
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
            return false;
        }

        boolean vulnerable = false;
        try {

            final String axmlPath = "res/layout/keyguard_password_view.xml";
            final String systemUiPath = "/system/priv-app/SystemUI/SystemUI.apk";

            final byte[] unzippedXml =
                    unzipFileToRam(systemUiPath, axmlPath);
            final Document doc =
                    new CompressedXmlParser().parseDOM(new ByteArrayInputStream(unzippedXml));
            final NodeList nodeList = doc.getElementsByTagName("EditText");
            final int numberOfNodes = nodeList.getLength();

            if (numberOfNodes == 0) {
                throw new Exception("Could not find password field " +
                        "(possible incompatible ROM Android version)");
            }

            for (int i = 0; i < numberOfNodes; i++) {
                final NamedNodeMap namedNodeMap = nodeList.item(i).getAttributes();
                final Node id = namedNodeMap.getNamedItem("android:maxLength");
                if (id == null || Integer.parseInt(id.getNodeValue()) > 500) {
                    vulnerable = true;
                    break;
                }
            }
        } catch (IOException | ParserConfigurationException e) {
            throw new Exception("Couldn't read keyguard XML layout " +
                    "(possible incompatible ROM or Android version)");
        } catch (NumberFormatException e) {
            throw new Exception("Invalid password field max length " +
                    "(possible incompatible ROM or Android version).");
        }
        return vulnerable;
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        final ArrayList<CPUArch> supportedArchs = new ArrayList<>();
        supportedArchs.add(CPUArch.ALL);
        return supportedArchs;
    }

    private byte[] unzipFileToRam(String zipPath, String fileToUnzipPath) throws IOException {

        final FileInputStream fileInputStream = new FileInputStream(zipPath);
        final ZipInputStream zipInputStream = new
                ZipInputStream(new BufferedInputStream(fileInputStream));

        ZipEntry zipEntry;
        ByteArrayOutputStream byteArrayOutputStream = null;
        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
            final String filename = zipEntry.getName();
            if (!fileToUnzipPath.equals(filename)) {
                continue;
            }

            final int bufferSize = 1024;
            final byte buffer[] = new byte[bufferSize];
            byteArrayOutputStream = new ByteArrayOutputStream();
            int numBytesRead;
            while ((numBytesRead = zipInputStream.read(buffer, 0, bufferSize)) != -1) {
                byteArrayOutputStream.write(buffer, 0, numBytesRead);
            }
        }
        if (byteArrayOutputStream == null) {
            throw new IOException("fileToUnzipPath not found in archive.");
        }
        byte[] byteArray = byteArrayOutputStream.toByteArray();
        zipInputStream.close();
        byteArrayOutputStream.close();

        return byteArray;
    }
} public class CVE_2016_0807  implements VulnerabilityTest {

/*
Elevation of Privilege Vulnerability in the Debuggerd

An elevation of privilege vulnerability in the Debuggerd component could enable a local malicious application to execute arbitrary code within the device root context. This issue is rated as a Critical severity due to the possibility of a local permanent device compromise and the device would possibly need to be repaired by re-flashing the operating system.

CVE	Bug(s)	Severity	Updated versions	Date reported
CVE-2016-0807	ANDROID-25187394	Critical	6.0 and 6.0.1	Google Internal

Patched here: https://android.googlesource.com/platform/system/core.git/+/d167d5eabc794ba4ddef1a2900eb729720da84a2%5E%21/#F0
*/

    @Override
    public String getCVEorID() {
        return "CVE-2016-0807";
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {

        File debuggerd = new File("/system/bin/debuggerd");
        if(!debuggerd.exists() || !debuggerd.isFile()){
            throw new Exception("debuggerd doesn't exist or is not a file");
        }

        String patchedString = "Possible corrupted note, desc size value is too large: %u";
        String unpatchedString = "Possible corrupted note, name size value is too large: %u";

        ByteArrayOutputStream debuggerdBAOS = new ByteArrayOutputStream((int)debuggerd.length());
        BinaryAssets.copy(new FileInputStream(debuggerd), debuggerdBAOS);
        byte[] debuggerdBin = debuggerdBAOS.toByteArray();

        KMPMatch binMatcher = new KMPMatch();

        int indexOf = binMatcher.indexOf(debuggerdBin, patchedString.getBytes());
        boolean hasPatchedString = indexOf == -1;

        indexOf = binMatcher.indexOf(debuggerdBin,  unpatchedString.getBytes());
        boolean hasUnpatchedString = indexOf == -1;


        return hasPatchedString && !hasUnpatchedString;
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        List<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }
} public class SamsungCREDzip implements VulnerabilityTest {

    private final static int BUFFER_SIZE = 1024;
    private final static String DESTINATION = "/sdcard/Download/";
    private final static String FILENAME = "cred.zip";
    private final static String ASSETNAME = "Samsung_cred.zip";


    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }

    @Override
    public String getCVEorID() {
        return "CVE-2015-7888";
    }

    private boolean thisHasSDCardPermission(Context ctx)
    {
        String readPermission = "android.permission.READ_EXTERNAL_STORAGE";
        String writePermission = "android.permission.WRITE_EXTERNAL_STORAGE";
        return (ctx.checkCallingOrSelfPermission(readPermission) == PackageManager.PERMISSION_GRANTED && 
                ctx.checkCallingOrSelfPermission(writePermission) == PackageManager.PERMISSION_GRANTED);
    }
    
    private boolean isSamsungPhone(){
        return Build.MANUFACTURER.equals("samsung");
    }
    
    @Override
    public boolean isVulnerable(Context context) throws Exception {
        boolean isVuln = false;
        
        if(!isSamsungPhone()) return false;
        
        if(!thisHasSDCardPermission(context))
            throw new Exception("No SDCard permission assigned to app to perform Samsung cred.zip remote code execution test");

        InputStream in = null;
        OutputStream out = null;
        try{
            AssetManager assetFiles = context.getAssets();
            File outFile = new File(DESTINATION, FILENAME);
            in = assetFiles.open(ASSETNAME);
            out = new FileOutputStream(outFile);
            
            byte[] buffer = new byte[BUFFER_SIZE];
            int read;
            while((read = in.read(buffer)) != -1){
                out.write(buffer, 0, read);
            }

            Thread.sleep(3000);
         
            outFile = null;
            outFile = new File(DESTINATION, FILENAME);
            if(outFile.exists()){
                isVuln = false;
                outFile.delete();
            }else{
                isVuln = true;
            }
        }catch(IOException e){
            throw new Exception("Error when extracting the asset file: " + e);
        }finally{
            if (in != null)
                in.close();
            if (out != null)
                out.close();
        }
        
        return isVuln;
    }
} public class WeakSauce implements VulnerabilityTest {
    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }

    private boolean isHTCPhone(){
        return Build.MANUFACTURER.equalsIgnoreCase("htc");
    }

    @Override
    public String getCVEorID() {
        return "WeakSauce";
    }

    private boolean thisHasInternetPermission(Context ctx)
    {
        String permission = "android.permission.INTERNET";
        int res = ctx.checkCallingOrSelfPermission(permission);
        return (res == PackageManager.PERMISSION_GRANTED);
    }

    @Override
    public boolean isVulnerable(Context context) throws Exception {

        if(!isHTCPhone()){
            return false;
        }

        if(!thisHasInternetPermission(context))
            throw new Exception("No internet permission assigned to app to perform WeakSauce Test");

        File dmAgentSocket = new File("/dev/socket/dmagent");
        return dmAgentSocket.exists();
    }
}
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import fuzion24.device.vulnerability.util.CPUArch;
import fuzion24.device.vulnerability.vulnerabilities.framework.graphics.GraphicBufferTest;
import fuzion24.device.vulnerability.vulnerabilities.framework.media.CVE_2015_6602;
import fuzion24.device.vulnerability.vulnerabilities.framework.media.CVE_2015_6608;
import fuzion24.device.vulnerability.vulnerabilities.framework.media.CVE_2015_6616;
import fuzion24.device.vulnerability.vulnerabilities.framework.media.StageFright;
import fuzion24.device.vulnerability.vulnerabilities.framework.serialization.OpenSSLTransientBug;
import fuzion24.device.vulnerability.vulnerabilities.framework.zip.ZipBug8219321;
import fuzion24.device.vulnerability.vulnerabilities.framework.zip.ZipBug9695860;
import fuzion24.device.vulnerability.vulnerabilities.framework.zip.ZipBug9950697;
import fuzion24.device.vulnerability.vulnerabilities.helper.SystemUtils;
import fuzion24.device.vulnerability.vulnerabilities.kernel.CVE_2011_1149;
import fuzion24.device.vulnerability.vulnerabilities.kernel.CVE_2013_6282;
import fuzion24.device.vulnerability.vulnerabilities.kernel.CVE_2014_3153;
import fuzion24.device.vulnerability.vulnerabilities.kernel.CVE_2014_4943;
import fuzion24.device.vulnerability.vulnerabilities.kernel.CVE_2015_3636;
import fuzion24.device.vulnerability.vulnerabilities.system.CVE20151528;
import fuzion24.device.vulnerability.vulnerabilities.system.CVE20153860;
import fuzion24.device.vulnerability.vulnerabilities.system.CVE_2016_0807;
import fuzion24.device.vulnerability.vulnerabilities.system.SamsungCREDzip;
import fuzion24.device.vulnerability.vulnerabilities.system.WeakSauce;
package fuzion24.device.vulnerability.vulnerabilities;





public class VulnerabilityOrganizer {

    private static final String TAG = "VulnerabilityOrganizer";

    private VulnerabilityOrganizer() {
    }

    //TODO: Maybe add dates to each of these and sort chronologically
    public static List<VulnerabilityTest> getTests(Context ctx){
        List<VulnerabilityTest> allTests = new ArrayList<>();
        allTests.add(new ZipBug9950697());
        allTests.add(new ZipBug8219321());
        allTests.add(new ZipBug9695860());
       // allTests.add(new JarBug13678484());
        allTests.add(new CVE_2013_6282());
        allTests.add(new CVE_2011_1149());