diff --git a/SRanipalExtTrackingModule/PatternScanner.cs b/SRanipalExtTrackingModule/PatternScanner.cs new file mode 100644 index 0000000..a6b3740 --- /dev/null +++ b/SRanipalExtTrackingModule/PatternScanner.cs @@ -0,0 +1,147 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using VRCFaceTracking; + +namespace SRanipalExtTrackingModule +{ + internal class PatternScanner + { + public static IntPtr Scan(IntPtr hProcess, ProcessModule module, string pattern) + { + byte[] patternBytes = ParsePattern(pattern); + + IntPtr start = module.BaseAddress; + IntPtr end = start + module.ModuleMemorySize; + + IntPtr current = start; + + while (current.ToInt64() < end.ToInt64()) + { + if (VirtualQueryEx(hProcess, current, out MEMORY_BASIC_INFORMATION mbi, (IntPtr)Marshal.SizeOf()) == IntPtr.Zero) + { +#if DEBUG + int error = Marshal.GetLastWin32Error(); + Console.WriteLine($"VirtualQueryEx failed. Error = {error}"); +#endif + break; + } + + bool readable = + mbi.State == MEM_COMMIT && + (mbi.Protect & PAGE_GUARD) == 0 && + (mbi.Protect & PAGE_NOACCESS) == 0; + + if (readable) + { + int size = (int)Math.Min( + mbi.RegionSize.ToInt64(), + end.ToInt64() - current.ToInt64() + ); + + byte[] buffer = new byte[size]; + int bytesRead = 0; + + if (Utils.ReadProcessMemory( + (int)hProcess, + mbi.BaseAddress, + buffer, + size, + ref bytesRead) && bytesRead > 0) + { + IntPtr match = ScanBuffer( + buffer, + bytesRead, + patternBytes, + mbi.BaseAddress + ); + + if (match != IntPtr.Zero) + return match; + } + } + + current = mbi.BaseAddress + mbi.RegionSize; + } + + return IntPtr.Zero; + } + + private static IntPtr ScanBuffer( + byte[] buffer, + int length, + byte[] pattern, + IntPtr baseAddress) + { + for (int i = 0; i <= length - pattern.Length; i++) + { + bool found = true; + + for (int j = 0; j < pattern.Length; j++) + { + if (pattern[j] == 0xCC) + continue; + + if (buffer[i + j] != pattern[j]) + { + found = false; + break; + } + } + + if (found) + return baseAddress + i; + } + + return IntPtr.Zero; + } + + private static byte[] ParsePattern(string pattern) + { + string[] tokens = pattern.Split(' '); + byte[] bytes = new byte[tokens.Length]; + + for (int i = 0; i < tokens.Length; i++) + { + bytes[i] = (tokens[i] == "?" || tokens[i] == "??") + ? (byte)0xCC + : Convert.ToByte(tokens[i], 16); + } + + return bytes; + } + + #region WinAPI + + private const uint MEM_COMMIT = 0x1000; + private const uint PAGE_NOACCESS = 0x01; + private const uint PAGE_GUARD = 0x100; + + [StructLayout(LayoutKind.Sequential)] + private struct MEMORY_BASIC_INFORMATION + { + public IntPtr BaseAddress; + public IntPtr AllocationBase; + public uint AllocationProtect; + public IntPtr RegionSize; + public uint State; + public uint Protect; + public uint Type; + } + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr VirtualQueryEx( + IntPtr hProcess, + IntPtr lpAddress, + out MEMORY_BASIC_INFORMATION lpBuffer, + IntPtr dwLength + ); + + #endregion + } +} diff --git a/SRanipalExtTrackingModule/SRanipalTrackingInterface.cs b/SRanipalExtTrackingModule/SRanipalTrackingInterface.cs index 467b292..44731e6 100644 --- a/SRanipalExtTrackingModule/SRanipalTrackingInterface.cs +++ b/SRanipalExtTrackingModule/SRanipalTrackingInterface.cs @@ -1,7 +1,10 @@ using Microsoft.Extensions.Logging; +using Microsoft.Win32; +using SRanipalExtTrackingModule; +using System; using System.Diagnostics; +using System.Drawing; using System.Runtime.InteropServices; -using Microsoft.Win32; using ViveSR; using ViveSR.anipal; using ViveSR.anipal.Eye; @@ -41,7 +44,7 @@ private static bool Attach() if (processes.Length <= 0) return false; _process = processes[0]; _processHandle = - Utils.OpenProcess(Utils.PROCESS_VM_READ, + Utils.OpenProcess(0x0410,//Utils.PROCESS_VM_READ | Utils.PROCESS_QUERY_INFORMATION, false, _process.Id); return true; } @@ -131,25 +134,59 @@ public override (bool eyeSuccess, bool expressionSuccess) Initialize(bool eyeAva if (found) { + _offset = 0; + UnifiedTracking.EyeImageData.SupportsImage = false; + // Find the EyeCameraDevice.dll module inside sr_runtime, get it's offset and add hex 19190 to it for the image stream. foreach (ProcessModule module in _process.Modules) if (module.ModuleName == "EyeCameraDevice.dll") { - _offset = module.BaseAddress; - - switch (_process.MainModule?.FileVersionInfo.FileVersion) +#if DEBUG + Logger.LogInformation($"SRanipalExtTrackingModule: found EyeCameraDevice.dll"); +#endif + // Try to pattern scan first.. + IntPtr functionAddress = PatternScanner.Scan(_processHandle, module, "48 89 54 24 10 48 89 4C 24 08 56 57 48 ? ? ? 48 ? ? ? ? FF"); + if (functionAddress != IntPtr.Zero) { - case "1.3.2.0": - _offset += 0x19190; +#if DEBUG + Logger.LogInformation($"SRanipalExtTrackingModule: found eye image binary pattern"); +#endif + IntPtr leaAddress = functionAddress + 0x93; + IntPtr nextInstAddress = leaAddress + 0x7; + byte[] buffer = new byte[4]; + int bytesRead = 0; + if (Utils.ReadProcessMemory((int)_processHandle, leaAddress + 3, buffer, buffer.Length, ref bytesRead) && bytesRead == 4) + { + int displacement = BitConverter.ToInt32(buffer, 0); + _offset = nextInstAddress + displacement; UnifiedTracking.EyeImageData.SupportsImage = true; - break; - case "1.3.1.1": - _offset += 0x19100; - UnifiedTracking.EyeImageData.SupportsImage = true; - break; - default: - UnifiedTracking.EyeImageData.SupportsImage = false; - break; + } + else + { +#if DEBUG + Logger.LogInformation($"SRanipalExtTrackingModule: failed to read memory"); +#endif + } + } + + if (!UnifiedTracking.EyeImageData.SupportsImage) + { + _offset = module.BaseAddress; + + switch (_process.MainModule?.FileVersionInfo.FileVersion) + { + case "1.3.2.0": + _offset += 0x19190; + UnifiedTracking.EyeImageData.SupportsImage = true; + break; + case "1.3.1.1": + _offset += 0x19100; + UnifiedTracking.EyeImageData.SupportsImage = true; + break; + default: + UnifiedTracking.EyeImageData.SupportsImage = false; + break; + } } }