internal static async Task <int> Execute(ModuleDefMD targetModule) { var bodyPartTypeDef = targetModule.Find("BodyPart", true); var controllerTypeDef = targetModule.Find("Controller", true); var onCollisionEnterMethod = bodyPartTypeDef.FindMethod("OnCollisionEnter"); var canFlyFieldDef = controllerTypeDef.FindField("canFly"); var bodyPartControllerFieldDef = bodyPartTypeDef.FindField("controller"); var instructionSignature = new List <Instruction> { new Instruction(OpCodes.Ldfld, bodyPartControllerFieldDef), new Instruction(OpCodes.Ldfld, canFlyFieldDef), new Instruction(OpCodes.Brtrue, new Instruction(OpCodes.Ldarg_1, null)), new Instruction(OpCodes.Ldarg_0, null) }; var matchedInstructions = InjectionHelpers.FetchInstructionsBySignature(onCollisionEnterMethod.Body.Instructions, instructionSignature, false); if (matchedInstructions != null) { // NOP the matched instructions matchedInstructions.ForEach(matchedInstruction => matchedInstruction.OpCode = OpCodes.Nop); } else { return(await Task.FromResult(2)); } return(await Task.FromResult(0)); }
internal static async Task <int> Execute(ModuleDefMD targetModule) { // Fetch target type defs var controllerTypeDef = targetModule.Find("Controller", true); // Fetch target method defs var controllerSetCollisionMethodDef = controllerTypeDef.FindMethod("SetCollision"); // Fetch target field defs var controllerIsAiFieldDef = controllerTypeDef.FindField("isAI"); var controllerPlayerIdFieldDef = controllerTypeDef.FindField("playerID"); /* * * 32 004C ldarg.0 * 33 004D ldfld bool Controller::isAI * 34 0052 brfalse.s 40 (0063) ldloc.2 * */ // Signature of the condition to which the check will be added var controllerSetCollisionAiCheckInstructionSignature = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, controllerIsAiFieldDef), new Instruction(OpCodes.Brfalse, null) }; var matchedControllerSetCollisionAiCheckMethodInstructions = InjectionHelpers.FetchInstructionsBySignature(controllerSetCollisionMethodDef.Body.Instructions, controllerSetCollisionAiCheckInstructionSignature, false); if (matchedControllerSetCollisionAiCheckMethodInstructions != null) { var branchInstruction = matchedControllerSetCollisionAiCheckMethodInstructions.Last(); var injectionIndex = controllerSetCollisionMethodDef.Body.Instructions.IndexOf(branchInstruction) + 1; // Set unique gameObject layer in Controller.SetCollision for AIs with Player IDs. var controllerSetCollisionAiCheckInstructionsToInject = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, controllerPlayerIdFieldDef), new Instruction(OpCodes.Ldc_I4_0), new Instruction(OpCodes.Bge_S, branchInstruction.Operand), }; // Add new instructions after the matched signature for (var i = 0; i < controllerSetCollisionAiCheckInstructionsToInject.Count; i++) { controllerSetCollisionMethodDef.Body.Instructions.Insert(injectionIndex + i, controllerSetCollisionAiCheckInstructionsToInject[i]); } controllerSetCollisionMethodDef.Body.UpdateInstructionOffsets(); } else { return(await Task.FromResult(1)); } return(await Task.FromResult(0)); }
internal static async Task <int> Execute(ModuleDefMD targetModule) { if (InjectionHelpers.AddField(targetModule, "Fighting", "TrainerWeaponIndex", targetModule.CorLibTypes.Int32, FieldAttributes.Public) == null) { return(await Task.FromResult(1)); } return(await Task.FromResult(0)); }
internal static async Task <int> Execute(ModuleDefMD targetModule) { if (InjectionHelpers.ClearMethodBody(targetModule, "ChatManager", "Awake") == false) { return(await Task.FromResult(1)); } return(await Task.FromResult(0)); }
private bool InjectTrainerLogicModuleTypes() { var trainerOptionsTypeDef = _logicModule.Find("TrainerOptions", false); var trainerManagerTypeDef = _logicModule.Find("TrainerManager", false); var singletonTypeDef = _logicModule.Find("Singleton`1", true); var singletonSucceeded = InjectionHelpers.AddTypeToModule(singletonTypeDef, _targetModule); var trainerOptionsSucceeded = InjectionHelpers.AddTypeToModule(trainerOptionsTypeDef, _targetModule); var trainerManagerSucceeded = InjectionHelpers.AddTypeToModule(trainerManagerTypeDef, _targetModule); return(singletonSucceeded && trainerOptionsSucceeded && trainerManagerSucceeded); }
private async Task SaveAndReloadTargetModule(bool reload = true, bool reloadInMemory = true, bool writeToFile = false) { var data = await InjectionHelpers.Save(_targetModule, writeToFile); if (reload) { if (reloadInMemory == false) { _targetModule = ModuleDefMD.Load(_targetModulePath); } else if (reloadInMemory) { _targetModule = ModuleDefMD.Load(data); _targetModule.Location = _targetModulePath; } } }
internal static async Task <int> Execute(ModuleDefMD targetModule) { // Fetch target type defs var controllerTypeDef = targetModule.Find("Controller", true); // Fetch target method defs var controllerJumpMethodDef = controllerTypeDef.FindMethod("Jump"); // Fetch target field defs var controllerMovementStateFieldDef = controllerTypeDef.FindField("m_MovementState"); /* * * Remove redundant code that always sets 'movementState' to 'GroundJump' in the 'Controller.Jump' method. * * Target instructions to patch: * * 62 00EE ldarg.0 * 63 00EF ldloc.0 * 64 00F0 brfalse 67 (00FB) ldc.i4.8 * 65 00F5 ldc.i4.4 * 66 00F6 br 68 (00FC) stfld valuetype Controller/MovementStateEnum Controller::m_MovementState * 67 00FB ldc.i4.8 * 68 00FC stfld valuetype Controller/MovementStateEnum Controller::m_MovementState * */ var controllerJumpInstructionSignature = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldloc_0), new Instruction(OpCodes.Brfalse, new Instruction(OpCodes.Ldc_I4_8, null)), new Instruction(OpCodes.Ldc_I4_4), new Instruction(OpCodes.Br, new Instruction(OpCodes.Stfld, null)), new Instruction(OpCodes.Ldc_I4_8), new Instruction(OpCodes.Stfld, controllerMovementStateFieldDef) }; var matchedControllerJumpMethodInstructions = InjectionHelpers.FetchInstructionsBySignature(controllerJumpMethodDef.Body.Instructions, controllerJumpInstructionSignature, false); if (matchedControllerJumpMethodInstructions != null) { // NOP the matched instructions matchedControllerJumpMethodInstructions.ForEach(matchedInstruction => matchedInstruction.OpCode = OpCodes.Nop); } else { return(await Task.FromResult(1)); } /* * * The 'Movement.Jump' method unsafely accesses the AudioClip array when selecting a random 'jump' sound to play. * Add appropriate length check to fix the index out of bounds exception. * * Target instructions to patch: * * 0 0000 ldarg.0 * 1 0001 ldarg.1 * 2 0002 ldarg.2 * 3 0003 call instance bool Movement::DoJump(bool, bool) * 4 0008 stloc.0 * 5 0009 ldarg.0 * 6 000A ldfld class [UnityEngine]UnityEngine.AudioSource Movement::au * 7 000F ldarg.0 * 8 0010 ldfld class [UnityEngine]UnityEngine.AudioClip[] Movement::jumpClips * 9 0015 ldc.i4.0 * 10 0016 ldarg.0 * 11 0017 ldfld class [UnityEngine]UnityEngine.AudioClip[] Movement::jumpClips * 12 001C ldlen * 13 001D conv.i4 * 14 001E call int32 [UnityEngine]UnityEngine.Random::Range(int32, int32) * 15 0023 ldelem.ref * 16 0024 callvirt instance void [UnityEngine]UnityEngine.AudioSource::PlayOneShot(class [UnityEngine]UnityEngine.AudioClip) * 17 0029 ldloc.0 * 18 002A ret * * * Resulting instructions after patching: * * 0 0000 ldarg.0 * 1 0001 ldarg.1 * 2 0002 ldarg.2 * 3 0003 call instance bool Movement::DoJump(bool, bool) * 4 0008 ldarg.0 * 5 0009 ldfld class [UnityEngine]UnityEngine.AudioClip[] Movement::jumpClips * 6 000E ldlen * 7 000F brfalse.s 24 (0039) ret * 8 0011 ldarg.0 * 9 0012 ldfld class [UnityEngine]UnityEngine.AudioSource Movement::au * 10 0017 ldarg.0 * 11 0018 ldfld class [UnityEngine]UnityEngine.AudioClip[] Movement::jumpClips * 12 001D ldc.i4.0 * 13 001E ldarg.0 * 14 001F ldfld class [UnityEngine]UnityEngine.AudioClip[] Movement::jumpClips * 15 0024 ldlen * 16 0025 conv.i4 * 17 0026 ldc.i4.1 * 18 0027 sub * 19 0028 ldc.i4.0 * 20 0029 call int32 [mscorlib]System.Math::Max(int32, int32) * 21 002E call int32 [UnityEngine]UnityEngine.Random::Range(int32, int32) * 22 0033 ldelem.ref * 23 0034 callvirt instance void [UnityEngine]UnityEngine.AudioSource::PlayOneShot(class [UnityEngine]UnityEngine.AudioClip) * 24 0039 ret * */ // Fetch target type defs var movementTypeDef = targetModule.Find("Movement", true); // Fetch target method defs var movementJumpMethodDef = movementTypeDef.FindMethod("Jump"); var movementDoJumpMethodDef = movementTypeDef.FindMethod("DoJump"); // Fetch target field defs var movementAuFieldDef = movementTypeDef.FindField("au"); var movementJumpClipsFieldDef = movementTypeDef.FindField("jumpClips"); // Fetch reference assembly refs var mscorlibAssemblyRef = targetModule.GetAssemblyRef(new UTF8String("mscorlib")); var unityEngineAssemblyRef = targetModule.GetAssemblyRef(new UTF8String("UnityEngine")); // Construct type ref users var systemMathTypeRefUser = new TypeRefUser(targetModule, new UTF8String("System"), new UTF8String("Math"), mscorlibAssemblyRef); var unityEngineAudioClipTypeRefUser = new TypeRefUser(targetModule, new UTF8String("UnityEngine"), new UTF8String("AudioClip"), unityEngineAssemblyRef); var unityEngineRandomTypeRefUser = new TypeRefUser(targetModule, new UTF8String("UnityEngine"), new UTF8String("Random"), unityEngineAssemblyRef); var unityEngineAudioSourceTypeRefUser = new TypeRefUser(targetModule, new UTF8String("UnityEngine"), new UTF8String("AudioSource"), unityEngineAssemblyRef); // Construct member ref users var maxMethodRefUser = new MemberRefUser(targetModule, new UTF8String("Max"), MethodSig.CreateStatic(targetModule.CorLibTypes.Int32, targetModule.CorLibTypes.Int32, targetModule.CorLibTypes.Int32), systemMathTypeRefUser); var randomRangeMethodRefUser = new MemberRefUser(targetModule, new UTF8String("Range"), MethodSig.CreateStatic(targetModule.CorLibTypes.Int32, targetModule.CorLibTypes.Int32, targetModule.CorLibTypes.Int32), unityEngineRandomTypeRefUser); var playOneShotMethodRefUser = new MemberRefUser(targetModule, new UTF8String("PlayOneShot"), MethodSig.CreateInstance(targetModule.CorLibTypes.Void, unityEngineAudioClipTypeRefUser.ToTypeSig()), unityEngineAudioSourceTypeRefUser); // Construct list of instructions to be injected var retInstruction = new Instruction(OpCodes.Ret); var movementJumpInstructions = new List <Instruction>() { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldarg_1), new Instruction(OpCodes.Ldarg_2), new Instruction(OpCodes.Call, movementDoJumpMethodDef), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, movementJumpClipsFieldDef), new Instruction(OpCodes.Ldlen), new Instruction(OpCodes.Brfalse_S, retInstruction), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, movementAuFieldDef), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, movementJumpClipsFieldDef), new Instruction(OpCodes.Ldc_I4_0), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, movementJumpClipsFieldDef), new Instruction(OpCodes.Ldlen), new Instruction(OpCodes.Conv_I4), new Instruction(OpCodes.Ldc_I4_1), new Instruction(OpCodes.Sub), new Instruction(OpCodes.Ldc_I4_0), new Instruction(OpCodes.Call, maxMethodRefUser), new Instruction(OpCodes.Call, randomRangeMethodRefUser), new Instruction(OpCodes.Ldelem_Ref), new Instruction(OpCodes.Callvirt, playOneShotMethodRefUser), retInstruction }; // Replace all instructions in the method with the new instructions movementJumpMethodDef.Body.Instructions.Clear(); movementJumpInstructions.ForEach(movementJumpInstruction => movementJumpMethodDef.Body.Instructions.Add(movementJumpInstruction)); movementJumpMethodDef.Body.UpdateInstructionOffsets(); return(await Task.FromResult(0)); }
public async Task <bool> PatchTargetModule(bool takeBackup) { int patchingStatus = 0; if (_targetModule == null) { await _logger.Log("Target module is not loaded. Patching aborted."); return(await Task.FromResult(false)); } if (takeBackup) { await CreateBackup(); } await _logger.Log("Initiating patching"); await _logger.Log($"Game version: {await GetGameVersion()}"); await _logger.Log($"Patcher version: {Application.ProductVersion}"); // Add a private field indicating that the module has been modified. await _logger.Log("Adding trainer patch indicator"); patchingStatus = await AddTrainerPatchIndicator.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not add trainer patch indicator: {patchingStatus}", LogLevel.Error); return(await Task.FromResult(false)); } await SaveAndReloadTargetModule(); // Set target fields and methods as public in order to allow TrainerLogic.dll's code to reference them and compile. // Note: While it would have been convenient to simply set *every* field and method as public/internal, it causes in-game glitches (e.g. ambiguous references and null reference exceptions) await _logger.Log("Setting fields as public"); _targetFields.ForEach(targetField => InjectionHelpers.SetFieldAccessModifier(_targetModule, targetField.Key, targetField.Value, FieldAttributes.Public)); await _logger.Log("Setting methods as public"); _targetMethods.ForEach(targetMethod => InjectionHelpers.SetMethodAccessModifier(_targetModule, targetMethod.Key, targetMethod.Value, MethodAttributes.Public)); await _logger.Log("Adding TrainerWeaponIndex field to fighting class"); patchingStatus = await AddTrainerWeaponIndex.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not add TrainerWeaponIndex field to fighting class: {patchingStatus}"); return(await Task.FromResult(false)); } await SaveAndReloadTargetModule(true, true, true); // Compile and load TrainerLogic module await _logger.Log("Compiling Trainer Logic module"); if (!await CompileTrainerLogicModule()) { await _logger.Log("Could not compile Trainer Logic module", LogLevel.Error); return(await Task.FromResult(false)); } await SaveAndReloadTargetModule(); // Keeping the fields as public causes exceptions due to reference ambiguity, so they need to be made internal following successful compilation. await _logger.Log("Setting fields as internal"); _targetFields.ForEach(targetField => InjectionHelpers.SetFieldAccessModifier(_targetModule, targetField.Key, targetField.Value, FieldAttributes.Assembly)); await _logger.Log("Setting methods as internal"); _targetMethods.ForEach(targetMethod => InjectionHelpers.SetMethodAccessModifier(_targetModule, targetMethod.Key, targetMethod.Value, MethodAttributes.Assembly)); await SaveAndReloadTargetModule(); // Inject TrainerOptions class await _logger.Log("Injecting trainer logic modules"); if (InjectTrainerLogicModuleTypes() == false) { await _logger.Log("Could not inject trainer logic modules", LogLevel.Error); return(await Task.FromResult(false)); } await _logger.Log("Injecting custom AI logic"); if (InjectCustomAILogic() == false) { await _logger.Log("Could not inject AI logic module", LogLevel.Error); return(await Task.FromResult(false)); } await SaveAndReloadTargetModule(); // Inject declaration of TrainerManager class into GameManager class and inject its instantiation code into GameManager's Start() method await _logger.Log("Adding TrainerManager to GameManager"); patchingStatus = await AddTrainerManagerToGameManager.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not add TrainerManager to GameManager: {patchingStatus}", LogLevel.Error); return(await Task.FromResult(false)); } await SaveAndReloadTargetModule(); await _logger.Log("Add trainer game version constant"); patchingStatus = await AddTrainerVersionConstant.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not add trainer game version constant: {patchingStatus}", LogLevel.Error); return(await Task.FromResult(false)); } await _logger.Log("Enabling chat bubble in all lobbies"); patchingStatus = await EnableChatBubbleInAllLobbies.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not enable chat bubble in all lobies: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Enabling flying mode weapon pickup"); patchingStatus = await EnableFlyingModeWeaponPickup.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not enable flying mode weapon pickup: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Enabling flying mode weapon throw"); patchingStatus = await EnableFlyingModeWeaponThrow.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not enable flying mode weapon throw: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Enabling unlimited health option"); patchingStatus = await EnableNoPlayerDamage.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not enable unlimited health option: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Adding win counter"); patchingStatus = await AddWinCounter.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not add win counter: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Applying bot jump fix"); patchingStatus = await ApplyBotJumpFix.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not apply bot jump fix: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Applying bot death fix"); patchingStatus = await ApplyBotDeathFix.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not apply bot death fix: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Applying bot collision fix"); patchingStatus = await ApplyBotCollisionFix.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not apply bot collision fix: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Applying bot HasControl fix"); patchingStatus = await ApplyBotHasControlFix.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not apply bot HasControl fix: {patchingStatus}", LogLevel.Warning); } await _logger.Log("Adding bot chat messages"); patchingStatus = await AddBotChatMessages.Execute(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not add bot chat messages: {patchingStatus}", LogLevel.Warning); } await SaveAndReloadTargetModule(); #if DEBUG await _logger.Log("Protecting assembly"); patchingStatus = await TrainerProtectionUtilities.ObfuscateTrainer(_targetModule); if (patchingStatus > 0) { await _logger.Log($"Could not obfuscate trainer: {patchingStatus}", LogLevel.Error); return(await Task.FromResult(false)); } await SaveAndReloadTargetModule(); if (await _protectionUtilities.ConfuseAssembly(_targetModulePath, _targetModule) == false) { await _logger.Log("Could not confuse assembly", LogLevel.Error); return(await Task.FromResult(false)); } #endif await _logger.Log("Patching completed"); return(await Task.FromResult(true)); }
internal static async Task <int> Execute(ModuleDefMD targetModule) { /* * {IL_0046: ldarg.0} * {IL_0047: ldarg.0} * {IL_0048: call UnityEngine.GameObject UnityEngine.Component::get_gameObject()} * {IL_004D: callvirt TrainerManager UnityEngine.GameObject::AddComponent<TrainerManager>()} * {IL_0052: stfld TrainerManager GameManager::trainerManager} * {IL_0057: ret} */ var gameManagerTypeDef = targetModule.Find("GameManager", true); var trainerManagerTypeDef = targetModule.Find("TrainerManager", true); var trainerManagerFieldDef = InjectionHelpers.AddField(targetModule, "GameManager", "trainerManager", trainerManagerTypeDef.ToTypeSig(), FieldAttributes.Private); if (trainerManagerFieldDef == null) { return(await Task.FromResult(1)); } var gameManagerStartMethodDef = gameManagerTypeDef.FindMethod("Start"); var unityEngine = targetModule.GetAssemblyRef(new UTF8String("UnityEngine")); var unityEngineComponentTypeRefUser = new TypeRefUser(targetModule, new UTF8String("UnityEngine"), new UTF8String("Component"), unityEngine); var unityEngineGameObjectTypeRefUser = new TypeRefUser(targetModule, new UTF8String("UnityEngine"), new UTF8String("GameObject"), unityEngine); var gameObjectTypeSig = unityEngineGameObjectTypeRefUser.ToTypeSig(); var getGameObjectMethodSig = MethodSig.CreateInstance(gameObjectTypeSig); var gameManagerStartMethodSig = MethodSig.CreateInstanceGeneric(1, new GenericMVar(0, gameManagerStartMethodDef)); // {UnityEngine.GameObject UnityEngine.Component::get_gameObject()} var getGameObjectMethodRefUser = new MemberRefUser(targetModule, new UTF8String("get_gameObject"), getGameObjectMethodSig, unityEngineComponentTypeRefUser); // {TrainerManager UnityEngine.GameObject::AddComponent<TrainerManager>()} var addComponentMethodRefUser = new MemberRefUser(targetModule, new UTF8String("AddComponent"), gameManagerStartMethodSig, unityEngineGameObjectTypeRefUser); var trainerManagerGenericInstMethodSig = new GenericInstMethodSig(trainerManagerTypeDef.ToTypeSig()); var addComponentMethodSpecUser = new MethodSpecUser(addComponentMethodRefUser, trainerManagerGenericInstMethodSig); var trainerManagerDefinitionMethodInstructions = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Call, getGameObjectMethodRefUser), new Instruction(OpCodes.Callvirt, addComponentMethodSpecUser), new Instruction(OpCodes.Stfld, trainerManagerFieldDef), new Instruction(OpCodes.Ret), }; var retInstruction = gameManagerStartMethodDef.Body.Instructions.LastOrDefault(); if (retInstruction != null && retInstruction.OpCode == OpCodes.Ret) { gameManagerStartMethodDef.Body.Instructions.Remove(retInstruction); } foreach (var instruction in trainerManagerDefinitionMethodInstructions) { gameManagerStartMethodDef.Body.Instructions.Add(instruction); } return(await Task.FromResult(0)); }
internal static async Task <int> Execute(ModuleDefMD targetModule) { // Fetch assembly refs var unityEngine = targetModule.GetAssemblyRef("UnityEngine"); // Fetch target type defs var characterActionsTypeDef = targetModule.Find("CharacterActions", true); var controllerTypeDef = targetModule.Find("Controller", true); var aiTypeDef = targetModule.Find("AI", true); // Fetch target method defs var aiStartMethodDef = aiTypeDef.FindMethod("Start"); var controllerStartMethodDef = controllerTypeDef.FindMethod("Start"); var characterActionsCreateWithControllerBindingsMethodDef = characterActionsTypeDef.FindMethod("CreateWithControllerBindings"); // Fetch target field defs var aiGoForGunsFieldDef = aiTypeDef.FindField("goForGuns"); var controllerHasControlFieldDef = controllerTypeDef.FindField("mHasControl"); var controllerPlayerActionsFieldDef = controllerTypeDef.FindField("mPlayerActions"); // Fetch type ref users var unityEngineComponentTypeRefUser = new TypeRefUser(targetModule, "UnityEngine", "Component", unityEngine); var unityEngineBehaviourTypeRefUser = new TypeRefUser(targetModule, "UnityEngine", "Behaviour", unityEngine); // Create method ref users var getComponentMethodRefUser = new MemberRefUser(targetModule, "GetComponent", MethodSig.CreateInstanceGeneric(1, new GenericMVar(0, aiStartMethodDef)), unityEngineComponentTypeRefUser); var setEnabledMethodRefUser = new MemberRefUser(targetModule, "set_enabled", MethodSig.CreateInstance(targetModule.CorLibTypes.Void, targetModule.CorLibTypes.Boolean), unityEngineBehaviourTypeRefUser); // Create method spec users var getComponentMethodSpecUser = new MethodSpecUser(getComponentMethodRefUser, new GenericInstMethodSig(aiTypeDef.ToTypeSig())); /* * * {IL_00C3: ldarg.0} * {IL_00C4: call AI UnityEngine.Component::GetComponent<AI>()} * {IL_00C9: ldc.i4.1} * {IL_00CA: callvirt System.Void UnityEngine.Behaviour::set_enabled(System.Boolean)} * */ var controllerStartInstructionSignature = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Call, getComponentMethodSpecUser), new Instruction(OpCodes.Ldc_I4_1), new Instruction(OpCodes.Callvirt, setEnabledMethodRefUser) }; var matchedControllerStartMethodInstructions = InjectionHelpers.FetchInstructionsBySigComparerSignature(controllerStartMethodDef.Body.Instructions, controllerStartInstructionSignature); if (matchedControllerStartMethodInstructions != null) { var lastMatchedInstruction = matchedControllerStartMethodInstructions.Last(); var injectionIndex = controllerStartMethodDef.Body.Instructions.IndexOf(lastMatchedInstruction) + 1; var controllerStartInstructionsToInject = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Call, getComponentMethodSpecUser), new Instruction(OpCodes.Ldc_I4_1), new Instruction(OpCodes.Stfld, aiGoForGunsFieldDef), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldc_I4_1), new Instruction(OpCodes.Stfld, controllerHasControlFieldDef), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Call, characterActionsCreateWithControllerBindingsMethodDef), new Instruction(OpCodes.Stfld, controllerPlayerActionsFieldDef), }; // Add new instructions after the matched signature for (var i = 0; i < controllerStartInstructionsToInject.Count; i++) { controllerStartMethodDef.Body.Instructions.Insert(injectionIndex + i, controllerStartInstructionsToInject[i]); } controllerStartMethodDef.Body.UpdateInstructionOffsets(); } else { return(await Task.FromResult(1)); } return(await Task.FromResult(0)); }
internal static async Task <int> Execute(ModuleDefMD targetModule) { // Fetch target type defs var controllerTypeDef = targetModule.Find("Controller", true); var healthHandlerTypeDef = targetModule.Find("HealthHandler", true); // Fetch target method defs var healthHandlerDieMethodDef = healthHandlerTypeDef.FindMethod("Die"); // Fetch target field defs var controllerIsAiFieldDef = controllerTypeDef.FindField("isAI"); var controllerPlayerIdFieldDef = controllerTypeDef.FindField("playerID"); var healthHandlerControllerFieldDef = healthHandlerTypeDef.FindField("controller"); /* * * 55 009F brfalse.s 69 (00CB) call bool MatchmakingHandler::get_IsNetworkMatch() * 56 00A1 ldarg.0 * 57 00A2 ldfld class Controller HealthHandler::controller * 58 00A7 ldfld bool Controller::isAI * 59 00AC brfalse.s 69 (00CB) call bool MatchmakingHandler::get_IsNetworkMatch() * */ // Signature of the condition to which the check will be added var healthHandlerAiCheckInstructionSignature = new List <Instruction> { new Instruction(OpCodes.Brfalse, null), new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, healthHandlerControllerFieldDef), new Instruction(OpCodes.Ldfld, controllerIsAiFieldDef), new Instruction(OpCodes.Brfalse, null) }; var matchedHealthHandlerDieMethodInstructions = InjectionHelpers.FetchInstructionsBySignature(healthHandlerDieMethodDef.Body.Instructions, healthHandlerAiCheckInstructionSignature, false); if (matchedHealthHandlerDieMethodInstructions != null) { var branchInstruction = matchedHealthHandlerDieMethodInstructions.Last(); var injectionIndex = healthHandlerDieMethodDef.Body.Instructions.IndexOf(branchInstruction) + 1; // Add check for a valid playerID when checking that the controller is an AI var healtHandlerAiCheckInstructionsToInject = new List <Instruction> { new Instruction(OpCodes.Ldarg_0), new Instruction(OpCodes.Ldfld, healthHandlerControllerFieldDef), new Instruction(OpCodes.Ldfld, controllerPlayerIdFieldDef), new Instruction(OpCodes.Ldc_I4_M1), new Instruction(OpCodes.Bgt_S, branchInstruction.Operand), }; // Add new instructions after the matched signature for (var i = 0; i < healtHandlerAiCheckInstructionsToInject.Count; i++) { healthHandlerDieMethodDef.Body.Instructions.Insert(injectionIndex + i, healtHandlerAiCheckInstructionsToInject[i]); } healthHandlerDieMethodDef.Body.UpdateInstructionOffsets(); } else { return(await Task.FromResult(1)); } return(await Task.FromResult(0)); }