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}