Beispiel #1
0
        public void RemovePatch()
        {
            try
            {
                PurgeFiles();
                string[] entryPoint = m_strEntryPoint.Split(new string[] { "::" }, StringSplitOptions.None);

                string strTempFile = m_strGameAssemblyPath + Constants.VORTEX_BACKUP_TAG;
                File.Copy(m_strGameAssemblyPath, strTempFile, true);

                using (AssemblyDefinition unityAssembly = AssemblyDefinition.ReadAssembly(strTempFile, new ReaderParameters {
                    ReadWrite = true
                }))
                {
                    if (!IsInjected(unityAssembly, entryPoint))
                    {
                        return;
                    }

                    TypeDefinition type = unityAssembly.MainModule.GetType(entryPoint[0]);
                    if ((type == null) || !type.IsClass)
                    {
                        throw new EntryPointNotFoundException("Invalid type");
                    }

                    MethodDefinition methodDefinition = type.Methods.FirstOrDefault(meth => meth.Name == entryPoint[1]);
                    if ((methodDefinition == null) || !methodDefinition.HasBody)
                    {
                        throw new EntryPointNotFoundException("Invalid method");
                    }

                    var instructions = methodDefinition.Body.Instructions
                                       .Where(instr => instr.OpCode == OpCodes.Call)
                                       .ToArray();
                    Instruction patcherInstr = instructions.FirstOrDefault(instr =>
                                                                           instr.Operand.ToString().Contains(Constants.VORTEX_PATCH_METHOD));

                    methodDefinition.Body.Instructions.Remove(patcherInstr);
                    unityAssembly.Write(m_strGameAssemblyPath);
                }
            }
            catch (Exception exc)
            {
                Enums.EErrorCode errorCode = Enums.EErrorCode.UNKNOWN;
                Util.RestoreBackup(m_strGameAssemblyPath);
                if (exc is FileNotFoundException)
                {
                    errorCode = Enums.EErrorCode.MISSING_FILE;
                }
                else if (exc is EntryPointNotFoundException)
                {
                    errorCode = Enums.EErrorCode.INVALID_ENTRYPOINT;
                }

                string strMessage  = "Failed to remove patcher.";
                string strResponse = JSONResponse.CreateSerializedResponse(strMessage, errorCode, exc);
                Console.Error.WriteLine(strResponse);
                Environment.Exit((int)(errorCode));
            }
        }
Beispiel #2
0
        internal static string CreateSerializedResponse(string message, dynamic code, Exception exc = null)
        {
            JSONResponse response = new JSONResponse();

            response.Message         = message;
            response.ErrorCode       = (int)(code);
            response.RaisedException = exc;

            return(JsonConvert.SerializeObject(response));
        }
Beispiel #3
0
 internal static void DeleteTemp(string strFilePath)
 {
     try
     {
         File.Delete(strFilePath);
     }
     catch (Exception exc)
     {
         string strMessage  = "Failed to delete temporary file";
         string strResponse = JSONResponse.CreateSerializedResponse(strMessage, Enums.EErrorCode.UNKNOWN, exc);
         Console.Error.WriteLine(strResponse);
     }
 }
Beispiel #4
0
 internal static void ReplaceFile(string strOld, string strNew)
 {
     try
     {
         BackupFile(strOld, true);
         File.Delete(strOld);
         File.Copy(strNew, strOld);
     }
     catch (Exception exc)
     {
         RestoreBackup(strOld);
         string strMessage  = "Failed to replace file";
         string strResponse = JSONResponse.CreateSerializedResponse(strMessage, Enums.EErrorCode.FILE_OPERATION_ERROR, exc);
         Console.Error.WriteLine(strResponse);
     }
 }
Beispiel #5
0
        /// <summary>
        /// Certain games distribute modified assemblies which disable
        ///  reflection and impede modding.
        /// </summary>
        private void EnableReflection(string strDataPath)
        {
            string          strMscorlib = Path.Combine(strDataPath, Constants.MSCORLIB);
            FileVersionInfo fvi         = FileVersionInfo.GetVersionInfo(strMscorlib);
            string          version     = fvi.FileVersion;
            string          strLib      = LIB_REPLACEMENTS
                                          .Where(replacement => replacement.Substring(Constants.MSCORLIB.Length + 1, 1) == version.Substring(0, 1))
                                          .SingleOrDefault();

            if (null != strLib)
            {
                try
                {
                    WebClient wc  = new WebClient();
                    Uri       uri = new Uri(Constants.GITHUB_LINK + strLib);
                    wc.DownloadDataAsync(uri, Path.Combine(strDataPath, strLib));
                    wc.DownloadDataCompleted += (sender, e) =>
                    {
                        string strFileName = e.UserState.ToString();
                        File.WriteAllBytes(strFileName, e.Result);
                        Util.ReplaceFile(strMscorlib, strFileName);
                        m_eInjectorState = Enums.EInjectorState.FINISHED;
                    };
                }
                catch (Exception exc)
                {
                    m_eInjectorState = Enums.EInjectorState.FINISHED;
                    string           strMessage  = "Unhandled mscorlib version.";
                    Enums.EErrorCode err         = Enums.EErrorCode.MISSING_FILE;
                    string           strResponse = JSONResponse.CreateSerializedResponse(strMessage, err, exc);
                    Console.Error.WriteLine(strResponse);
                    Environment.Exit((int)(err));
                }
            }
            else
            {
                m_eInjectorState = Enums.EInjectorState.FINISHED;
                string           strMessage  = "Unhandled mscorlib version.";
                Enums.EErrorCode err         = Enums.EErrorCode.UNHANDLED_FILE_VERSION;
                string           strResponse = JSONResponse.CreateSerializedResponse(strMessage, err);
                Console.Error.WriteLine(strResponse);
                Environment.Exit((int)(err));
            }
        }
Beispiel #6
0
 public Injector(string strDataPath, string strEntryPoint)
 {
     try
     {
         m_strDataPath         = strDataPath;
         m_strEntryPoint       = strEntryPoint;
         m_strGameAssemblyPath = Path.Combine(strDataPath, Constants.UNITY_ASSEMBLY_LIB);
         m_strModsDirectory    = Path.Combine(strDataPath, Constants.MODS_DIRNAME);
         m_resolver            = new MissingAssemblyResolver(strDataPath);
     }
     catch (Exception exc)
     {
         Enums.EErrorCode errorCode   = Enums.EErrorCode.INVALID_ARGUMENT;
         string           strMessage  = "Injector received invalid argument.";
         string           strResponse = JSONResponse.CreateSerializedResponse(strMessage, errorCode, exc);
         Console.Error.WriteLine(strResponse);
         Environment.Exit((int)(errorCode));
     }
 }
Beispiel #7
0
        public void Inject()
        {
            m_eInjectorState = Enums.EInjectorState.RUNNING;
            string             strTempFile   = null;
            AssemblyDefinition unityAssembly = null;

            try
            {
                // Ensure we have reflection enabled - there's no point
                //  in continuing if reflection is disabled.
                if (!Util.IsReflectionEnabled(m_strDataPath))
                {
                    EnableReflection(m_strDataPath);
                }
                else
                {
                    m_eInjectorState = Enums.EInjectorState.FINISHED;
                }
                // Deploy patcher related files.
                DeployFiles();

                // Start the patching process.
                string[] patcherPoints = Constants.VORTEX_PATCH_METHOD.Split(new string[] { "::" }, StringSplitOptions.None);
                string[] entryPoint    = m_strEntryPoint.Split(new string[] { "::" }, StringSplitOptions.None);

                strTempFile = Util.GetTempFile(m_strGameAssemblyPath);
                using (unityAssembly = AssemblyDefinition.ReadAssembly(strTempFile,
                                                                       new ReaderParameters {
                    ReadWrite = true, AssemblyResolver = m_resolver
                }))
                {
                    if (IsInjected(unityAssembly, entryPoint))
                    {
                        unityAssembly.Dispose();
                        Util.DeleteTemp(strTempFile);
                        return;
                    }

                    // Back up the game assembly before we do anything.
                    Util.BackupFile(m_strGameAssemblyPath);

                    AssemblyDefinition vrtxPatcher   = AssemblyDefinition.ReadAssembly(Path.Combine(m_strDataPath, Constants.VORTEX_LIB));
                    MethodDefinition   patcherMethod = vrtxPatcher.MainModule.GetType(patcherPoints[0]).Methods.First(x => x.Name == patcherPoints[1]);
                    TypeDefinition     type          = unityAssembly.MainModule.GetType(entryPoint[0]);
                    if ((type == null) || !type.IsClass)
                    {
                        throw new EntryPointNotFoundException("Invalid entry point");
                    }

                    MethodDefinition methodDefinition = type.Methods.FirstOrDefault(meth => meth.Name == entryPoint[1]);
                    if ((methodDefinition == null) || !methodDefinition.HasBody)
                    {
                        throw new EntryPointNotFoundException("Invalid entry point");
                    }

                    methodDefinition.Body.GetILProcessor().InsertBefore(methodDefinition.Body.Instructions[0], Instruction.Create(OpCodes.Call, methodDefinition.Module.ImportReference(patcherMethod)));
                    unityAssembly.Write(m_strGameAssemblyPath);
                    unityAssembly.Dispose();
                    Util.DeleteTemp(strTempFile);
                }
            }
            catch (Exception exc)
            {
                Enums.EErrorCode errorCode = Enums.EErrorCode.UNKNOWN;

                if (unityAssembly != null)
                {
                    unityAssembly.Dispose();
                }

                if (strTempFile != null)
                {
                    Util.DeleteTemp(strTempFile);
                }

                Util.RestoreBackup(m_strGameAssemblyPath);
                if (exc is FileNotFoundException)
                {
                    errorCode = Enums.EErrorCode.MISSING_FILE;
                }
                else if (exc is EntryPointNotFoundException)
                {
                    errorCode = Enums.EErrorCode.INVALID_ENTRYPOINT;
                }

                string strMessage  = "Failed to inject patcher.";
                string strResponse = JSONResponse.CreateSerializedResponse(strMessage, errorCode, exc);
                Console.Error.WriteLine(strResponse);
                Environment.Exit((int)(errorCode));
            }

            while (InjectorState != Enums.EInjectorState.FINISHED)
            {
                // Do nothing.
            }
        }
        static void RunOptions(string [] args)
        {
            bool removePatch = false;
            bool showHelp    = false;

            // (The -q argument should be used on its own as all other arguments will be ignored)
            //  string will hold the path to the assembly we want to check for the .NET version.
            //  if an absolute path is not provided, Vortex will assume that we're looking for Unity's
            //  default assembly (Assembly-CSharp.dll)
            //  This is currently used by Vortex to ascertain which .NET version we want to use when
            //  building VIGO.
            string queryNETAssembly = string.Empty;

            // (This -a should be used on its own as all other arguments will be ignored)
            //  string will hold both the path to the assembly we want to load and the
            //  assembly name reference we want to verify. Value should have the following
            //  format: "{Assembly_Path}::{Assembly_Name}" e.g. "C:/somePath/assembly.dll::mscorlib"
            string queryAssemblyName = string.Empty;

            OptionSet options = new OptionSet()
                                .Add("h", "Shows this message and closes program", h => showHelp = h != null)
                                .Add("g|extension=", "Path to the game's extension folder", g => m_strExtensionPath   = g)
                                .Add("m|managed=", "Path to the game's managed folder/game assembly", m => m_dataPath = m)
                                .Add("i|install=", "Path to Harmony Patcher's build folder.", i => m_installPath      = i)
                                .Add("e|entry=", "This game's entry point formatted as: 'Namespace.ClassName::MethodName'", e => m_entryPoint = e)
                                .Add("x|modsfolder=", "The game's expected mods directory", x => m_modsfolder = x)
                                .Add("r", "Will remove the harmony patcher", r => removePatch = r != null)
                                .Add("q|querynet=", "(optional) Query the .NET version of the assembly file we attempt to patch", q => queryNETAssembly = q)
                                .Add("v", "(optional) Used to decide whether we want to use VIGO or not", v => m_injectVIGO = v != null)
                                .Add("a|queryassemblyname=", "(optional) Used to check assembly references. Expected format: 'Assembly_Path::Assembly_Name'", a => queryAssemblyName = a);

            List <string> extra;

            try {
                extra = options.Parse(args);
            } catch (OptionException) {
                showHelp = true;
            }

            if (showHelp || (args.Length == 0))
            {
                ShowHelp(options);
                return;
            }

            if (!string.IsNullOrEmpty(queryNETAssembly))
            {
                const string FRAMEWORK_PREFIX = "FrameworkVersion=";
                bool         bFoundVersion    = false;
                string       assemblyFile     = (queryNETAssembly.EndsWith(".dll"))
                    ? queryNETAssembly : Path.Combine(queryNETAssembly, Constants.UNITY_ASSEMBLY_LIB);

                //AssemblyName assemblyName = Util.FindAssemblyRef (assemblyFile, "mscorlib");
                AssemblyName assemblyName = null;
                if (assemblyName != null)
                {
                    // Found a reference, but surprisingly the local mscorlib assembly itself might have
                    //  a higher version; we need to check the local file.
                    string localLib = Path.Combine(Path.GetDirectoryName(assemblyFile), "mscorlib.dll");
                    string version  = (File.Exists(localLib))
                      ? System.Diagnostics.FileVersionInfo.GetVersionInfo(localLib).FileVersion
                      : assemblyName.Version.ToString();

                    Console.WriteLine($"{FRAMEWORK_PREFIX}{version}");
                    bFoundVersion = true;
                }

                // We couldn't find a NET reference - lets see if there's a mscorlib assembly
                //  next to the game assembly.
                string potentialMscorlibFilePath = Path.Combine(Path.GetDirectoryName(assemblyFile), "mscorlib.dll");
                if (!bFoundVersion && File.Exists(potentialMscorlibFilePath))
                {
                    string version = System.Diagnostics.FileVersionInfo.GetVersionInfo(potentialMscorlibFilePath).FileVersion;
                    Console.WriteLine($"{FRAMEWORK_PREFIX}{version}");
                    bFoundVersion = true;
                }

                if (!bFoundVersion)
                {
                    // We will have to rely on the assembly's runtime version.
                    Assembly gameAss = Assembly.ReflectionOnlyLoadFrom(assemblyFile);
                    Console.WriteLine($"{FRAMEWORK_PREFIX}{gameAss.ImageRuntimeVersion}");
                }

                // This is a query operation, as mentioned above
                //  we're not going to patch the game assembly.
                return;
            }

            if (!string.IsNullOrEmpty(queryAssemblyName))
            {
                string [] parsed = queryAssemblyName.Split(new string [] { "::" }, StringSplitOptions.None);
                if (parsed.Length != 2)
                {
                    string strError = JSONResponse.CreateSerializedResponse("Invalid value, please respect format: 'Assembly_Path::Ref_Assembly_Name'", 1);
                    Console.Error.WriteLine(strError);
                    return;
                }

                string assemblyFile = (parsed [0].EndsWith(".dll"))
                    ? parsed [0] : Path.Combine(parsed [0], Constants.UNITY_ASSEMBLY_LIB);

                //AssemblyName assemblyName = Util.FindAssemblyRef (assemblyFile, parsed [1]);
                AssemblyName assemblyName = null;
                if (assemblyName != null)
                {
                    Console.WriteLine($"FoundAssembly={assemblyName.FullName}");
                }

                // This is a query operation, as mentioned above
                //  we're not going to patch the game assembly.
                return;
            }

            Run(removePatch);
        }