    001/*
    002 * Forge Mod Loader
    003 * Copyright (c) 2012-2013 cpw.
    004 * All rights reserved. This program and the accompanying materials
    005 * are made available under the terms of the GNU Lesser Public License v2.1
    006 * which accompanies this distribution, and is available at
    007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
    008 *
    009 * Contributors:
    010 *     cpw - implementation
    011 */
    012
    013package cpw.mods.fml.common.asm;
    014
    015import java.io.ByteArrayInputStream;
    016import java.io.File;
    017import java.io.InputStreamReader;
    018import java.io.ObjectInputStream.GetField;
    019import java.io.StringReader;
    020import java.net.JarURLConnection;
    021import java.nio.charset.Charset;
    022import java.security.CodeSource;
    023import java.security.cert.CertPath;
    024import java.security.cert.CertPathValidator;
    025import java.security.cert.Certificate;
    026import java.security.cert.CertificateFactory;
    027import java.security.cert.PKIXCertPathValidatorResult;
    028import java.security.cert.PKIXParameters;
    029import java.security.cert.TrustAnchor;
    030import java.security.cert.X509Certificate;
    031import java.util.Arrays;
    032import java.util.Collections;
    033import java.util.Map;
    034
    035import javax.swing.JOptionPane;
    036
    037import org.objectweb.asm.ClassReader;
    038import org.objectweb.asm.ClassVisitor;
    039import org.objectweb.asm.FieldVisitor;
    040import org.objectweb.asm.Opcodes;
    041
    042import cpw.mods.fml.common.CertificateHelper;
    043import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper;
    044import cpw.mods.fml.relauncher.FMLRelaunchLog;
    045import cpw.mods.fml.relauncher.IFMLCallHook;
    046import cpw.mods.fml.relauncher.RelaunchClassLoader;
    047
    048public class FMLSanityChecker implements IFMLCallHook
    049{
    050    private static final String FMLFINGERPRINT = "AE:F6:54:79:96:E9:1B:D1:59:70:6C:B4:6B:F5:4A:89:C5:CE:08:1D".toLowerCase().replace(":","");
    051    private static final String FORGEFINGERPRINT = "DE:4C:F8:A3:F3:BC:15:63:58:10:04:4C:39:24:0B:F9:68:04:EA:7D".toLowerCase().replace(":", "");
    052    static class MLDetectorClassVisitor extends ClassVisitor
    053    {
    054        private boolean foundMarker = false;
    055        private MLDetectorClassVisitor()
    056        {
    057            super(Opcodes.ASM4);
    058        }
    059
    060        @Override
    061        public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4)
    062        {
    063            if ("fmlMarker".equals(arg1))
    064            {
    065                foundMarker = true;
    066            }
    067            return null;
    068        }
    069    }
    070
    071    private RelaunchClassLoader cl;
    072
    073    @Override
    074    public Void call() throws Exception
    075    {
    076        CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
    077        boolean goodFML = false;
    078        if (codeSource.getLocation().getProtocol().equals("jar"))
    079        {
    080            Certificate[] certificates = codeSource.getCertificates();
    081            if (certificates!=null)
    082            {
    083
    084                for (Certificate cert : certificates)
    085                {
    086                    String fingerprint = CertificateHelper.getFingerprint(cert);
    087                    if (fingerprint.equals(FMLFINGERPRINT))
    088                    {
    089                        FMLRelaunchLog.info("Found valid fingerprint for FML. Certificate fingerprint %s", fingerprint);
    090                        goodFML = true;
    091                    }
    092                    else if (fingerprint.equals(FORGEFINGERPRINT))
    093                    {
    094                        FMLRelaunchLog.info("Found valid fingerprint for Minecraft Forge. Certificate fingerprint %s", fingerprint);
    095                        goodFML = true;
    096                    }
    097                    else
    098                    {
    099                        FMLRelaunchLog.severe("Found invalid fingerprint for FML: %s", fingerprint);
    100                    }
    101                }
    102            }
    103        }
    104        else
    105        {
    106            goodFML = true;
    107        }
    108        if (!goodFML)
    109        {
    110            FMLRelaunchLog.severe("FML appears to be missing any signature data. This is not a good thing");
    111        }
    112        byte[] mlClass = cl.getClassBytes("ModLoader");
    113        // Only care in obfuscated env
    114        if (mlClass == null)
    115        {
    116            return null;
    117        }
    118        MLDetectorClassVisitor mlTester = new MLDetectorClassVisitor();
    119        ClassReader cr = new ClassReader(mlClass);
    120        cr.accept(mlTester, ClassReader.SKIP_CODE);
    121        if (!mlTester.foundMarker)
    122        {
    123            JOptionPane.showMessageDialog(null, "<html>CRITICAL ERROR<br/>" +
    124                    "ModLoader was detected in this environment<br/>" +
    125                        "ForgeModLoader cannot be installed alongside ModLoader<br/>" +
    126                        "All mods should work without ModLoader being installed<br/>" +
    127                        "Because ForgeModLoader is 100% compatible with ModLoader<br/>" +
    128                        "Re-install Minecraft Forge or Forge ModLoader into a clean<br/>" +
    129                        "jar and try again.",
    130                        "ForgeModLoader critical error",
    131                        JOptionPane.ERROR_MESSAGE);
    132            throw new RuntimeException("Invalid ModLoader class detected");
    133        }
    134        return null;
    135    }
    136
    137    @Override
    138    public void injectData(Map<String, Object> data)
    139    {
    140        cl = (RelaunchClassLoader) data.get("classLoader");
    141        FMLDeobfuscatingRemapper.INSTANCE.setup((File)data.get("mcLocation"), cl, (String) data.get("deobfuscationFileName"));
    142    }
    143
    144}