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) { // 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)); }
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)); }