TamperMachine.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.Exceptions;
  3. using Ryujinx.HLE.HOS.Kernel;
  4. using Ryujinx.HLE.HOS.Kernel.Process;
  5. using Ryujinx.HLE.HOS.Services.Hid;
  6. using Ryujinx.HLE.HOS.Tamper;
  7. using System;
  8. using System.Collections.Concurrent;
  9. using System.Collections.Generic;
  10. using System.Threading;
  11. namespace Ryujinx.HLE.HOS
  12. {
  13. public class TamperMachine
  14. {
  15. // Atmosphere specifies a delay of 83 milliseconds between the execution of the last
  16. // cheat and the re-execution of the first one.
  17. private const int TamperMachineSleepMs = 1000 / 12;
  18. private Thread _tamperThread = null;
  19. private ConcurrentQueue<ITamperProgram> _programs = new ConcurrentQueue<ITamperProgram>();
  20. private long _pressedKeys = 0;
  21. private void Activate()
  22. {
  23. if (_tamperThread == null || !_tamperThread.IsAlive)
  24. {
  25. _tamperThread = new Thread(this.TamperRunner);
  26. _tamperThread.Name = "HLE.TamperMachine";
  27. _tamperThread.Start();
  28. }
  29. }
  30. internal void InstallAtmosphereCheat(IEnumerable<string> rawInstructions, ProcessTamperInfo info, ulong exeAddress)
  31. {
  32. if (!CanInstallOnPid(info.Process.Pid))
  33. {
  34. return;
  35. }
  36. ITamperedProcess tamperedProcess = new TamperedKProcess(info.Process);
  37. AtmosphereCompiler compiler = new AtmosphereCompiler();
  38. ITamperProgram program = compiler.Compile(rawInstructions, exeAddress, info.HeapAddress, tamperedProcess);
  39. if (program != null)
  40. {
  41. _programs.Enqueue(program);
  42. }
  43. Activate();
  44. }
  45. private bool CanInstallOnPid(long pid)
  46. {
  47. // Do not allow tampering of kernel processes.
  48. if (pid < KernelConstants.InitialProcessId)
  49. {
  50. Logger.Warning?.Print(LogClass.TamperMachine, $"Refusing to tamper kernel process {pid}");
  51. return false;
  52. }
  53. return true;
  54. }
  55. private bool IsProcessValid(ITamperedProcess process)
  56. {
  57. return process.State != ProcessState.Crashed && process.State != ProcessState.Exiting && process.State != ProcessState.Exited;
  58. }
  59. private void TamperRunner()
  60. {
  61. Logger.Info?.Print(LogClass.TamperMachine, "TamperMachine thread running");
  62. int sleepCounter = 0;
  63. while (true)
  64. {
  65. // Sleep to not consume too much CPU.
  66. if (sleepCounter == 0)
  67. {
  68. sleepCounter = _programs.Count;
  69. Thread.Sleep(TamperMachineSleepMs);
  70. }
  71. else
  72. {
  73. sleepCounter--;
  74. }
  75. if (!AdvanceTamperingsQueue())
  76. {
  77. // No more work to be done.
  78. Logger.Info?.Print(LogClass.TamperMachine, "TamperMachine thread exiting");
  79. return;
  80. }
  81. }
  82. }
  83. private bool AdvanceTamperingsQueue()
  84. {
  85. if (!_programs.TryDequeue(out ITamperProgram program))
  86. {
  87. // No more programs in the queue.
  88. return false;
  89. }
  90. // Check if the process is still suitable for running the tamper program.
  91. if (!IsProcessValid(program.Process))
  92. {
  93. // Exit without re-enqueuing the program because the process is no longer valid.
  94. return true;
  95. }
  96. // Re-enqueue the tampering program because the process is still valid.
  97. _programs.Enqueue(program);
  98. Logger.Debug?.Print(LogClass.TamperMachine, "Running tampering program");
  99. try
  100. {
  101. ControllerKeys pressedKeys = (ControllerKeys)Thread.VolatileRead(ref _pressedKeys);
  102. program.Execute(pressedKeys);
  103. }
  104. catch (CodeRegionTamperedException ex)
  105. {
  106. Logger.Debug?.Print(LogClass.TamperMachine, $"Prevented tampering program from modifing code memory");
  107. if (!String.IsNullOrEmpty(ex.Message))
  108. {
  109. Logger.Debug?.Print(LogClass.TamperMachine, ex.Message);
  110. }
  111. }
  112. catch (Exception ex)
  113. {
  114. Logger.Debug?.Print(LogClass.TamperMachine, $"The tampering program crashed, this can happen while the game is starting");
  115. if (!String.IsNullOrEmpty(ex.Message))
  116. {
  117. Logger.Debug?.Print(LogClass.TamperMachine, ex.Message);
  118. }
  119. }
  120. return true;
  121. }
  122. public void UpdateInput(List<GamepadInput> gamepadInputs)
  123. {
  124. // Look for the input of the player one or the handheld.
  125. foreach (GamepadInput input in gamepadInputs)
  126. {
  127. if (input.PlayerId == PlayerIndex.Player1 || input.PlayerId == PlayerIndex.Handheld)
  128. {
  129. Thread.VolatileWrite(ref _pressedKeys, (long)input.Buttons);
  130. return;
  131. }
  132. }
  133. // Clear the input because player one is not conected.
  134. Thread.VolatileWrite(ref _pressedKeys, 0);
  135. }
  136. }
  137. }