Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions SRanipalExtTrackingModule/PatternScanner.cs
Original file line number Diff line number Diff line change
@@ -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<MEMORY_BASIC_INFORMATION>()) == 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
}
}
67 changes: 52 additions & 15 deletions SRanipalExtTrackingModule/SRanipalTrackingInterface.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
}
}

Expand Down