Skip to content

Instantly share code, notes, and snippets.

@stevefan1999-personal
Created June 24, 2018 07:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevefan1999-personal/31623529fd5a1941ff1fd310e5c84a64 to your computer and use it in GitHub Desktop.
Save stevefan1999-personal/31623529fd5a1941ff1fd310e5c84a64 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
public class SigScanSharp
{
public byte[] Target { get; set; }
private bool PatternCheck(int nOffset, byte[] arrPattern)
{
for (int i = 0; i < arrPattern.Length; i++) {
if (arrPattern[i] == 0x0)
continue;
if (arrPattern[i] != this.Target[nOffset + i])
return false;
}
return true;
}
public ulong FindPattern(string szPattern, out long lTime)
{
Stopwatch stopwatch = Stopwatch.StartNew();
byte[] arrPattern = ParsePatternString(szPattern);
for (int nModuleIndex = 0; nModuleIndex < Target.Length; nModuleIndex++) {
if (this.Target[nModuleIndex] != arrPattern[0])
continue;
if (PatternCheck(nModuleIndex, arrPattern)) {
lTime = stopwatch.ElapsedMilliseconds;
return (ulong)nModuleIndex;
}
}
lTime = stopwatch.ElapsedMilliseconds;
return 0;
}
private byte[] ParsePatternString(string szPattern)
{
List<byte> patternbytes = new List<byte>();
foreach (var szByte in szPattern.Split(' '))
patternbytes.Add(szByte == "?" ? (byte)0x0 : Convert.ToByte(szByte, 16));
return patternbytes.ToArray();
}
}
class Program {
static bool IsElevated => WindowsIdentity.GetCurrent().Owner.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid);
static void Main(string[] args) {
if (!IsElevated) {
Console.WriteLine("Error: this program requires administrator permission");
goto exit;
}
var streamServer = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"NVIDIA Corporation",
"NvStreamSrv"
);
var streamBackup = Path.Combine(streamServer, "nvstreamer.bak.exe");
if (File.Exists(streamBackup)) {
Console.WriteLine("Error: nvstreamer is already patched");
goto exit;
}
var streamer = Path.Combine(streamServer, "nvstreamer.exe");
Console.WriteLine("Backing up nvstreamer.exe to nvstreamer.bak.exe");
try {
File.Copy(streamer, streamBackup);
Console.WriteLine("Backing up nvstreamer.exe success");
} catch {
Console.WriteLine("Unable to back up nvstreamer.exe");
goto exit;
}
Console.WriteLine("Patching nvstreamer.exe");
try {
Console.WriteLine("Loading nvstreamer.exe");
var streamerBytes = File.ReadAllBytes(streamer);
Console.WriteLine($"File size: {streamerBytes.Length}");
var scanner = new SigScanSharp() {
Target = streamerBytes
};
var pat = scanner.FindPattern("FF 15 ? ? ? ? EB 22 4C 8B 45 F0", out var time);
if (pat != 0) {
Console.WriteLine($"ScreenMonitor::Impl::IsFrameSkipped->ClipCursor found at {pat:X}, time taken {time} ms");
Console.WriteLine("Writing nops");
Parallel.For(0, 6, i => streamerBytes[pat + (uint)i] = 0x90);
var check = streamerBytes.Skip((int)pat).Take(10).ToArray();
Console.WriteLine($"Final bytes: {BitConverter.ToString(check).Replace("-", " ")}");
Console.WriteLine("Writing back to file");
try {
File.WriteAllBytes(streamer, streamerBytes);
Console.WriteLine("Success!");
} catch {
Console.WriteLine("File failed to write");
}
} else {
Console.WriteLine("ScreenMonitor::Impl::IsFrameSkipped->ClipCursor not found");
}
} catch {
Console.WriteLine("Unable to load nvstreamer.exe");
goto exit;
}
exit:
Console.WriteLine("Press enter to exit...");
Console.ReadLine();
}
}
@yagamiluka
Copy link

Hi Stevefan1999,

I'm facing the same problem about unability to use mouse cursor on second screen while in a GameStream session (moonlight).

I tried your fix, but "ClipCursor" pattern is not found in latest nvstreamer.exe version.

Do you think it could be addressed in other ways?

Thank you very much for the effort!

@stevefan1999-personal
Copy link
Author

@yagamiluka I might try to see and look for a new pattern

@yagamiluka
Copy link

Thank you!
I'm not an expert, but while going through the .exe file, i found this string might be relevant.
image

Is there anything I may help with?

Thank you again

@wachterwacht
Copy link

wachterwacht commented Jan 9, 2020

As I don't have no programming experience whatsoever, but the solution to this problem is the only way to turn my Ipad Pro to a secondary wireless 120 hz monitor I kindly want to ask:

How are the odds, that you will be able and or willing to solve the locked cursor problem and share it here?
Your patch seems to be the only thing online to potentially address the moonlight lock cursor problem.

Is it maybe something that could temporarily be solved by rolling back to an older Version of GForce Experience and from 2 years ago when to wrote that script?

Many thanks for initially sharing your workaround tho, it's highly appreciated!

@tomatosoupcan
Copy link

Looks like cursor lock is back in more recent versions but I'm not savvy enough to adapt any of this patching stuff to try to get this working again. Anyone have input?

@stevefan1999-personal
Copy link
Author

new signature: FF 15 ? ? ? ? 48 8D 4F 10

.text:00000001400C6906 48 8D 8D B0 01 00 00                                            lea     rcx, [rbp+410h+var_260]
.text:00000001400C690D FF 15 2D 69 3C 00                                               call    cs:ClipCursor
.text:00000001400C6913
.text:00000001400C6913                                                 loc_1400C6913:                          ; CODE XREF: sub_1400C6410+6C↑j
.text:00000001400C6913                                                                                         ; sub_1400C6410+4CE↑j
.text:00000001400C6913 48 8D 4F 10                                                     lea     rcx, [rdi+10h]

Try using this golang code I wrote:

package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"rsc.io/binaryregexp"
)

type PatchUnitExecutableInfo struct {
	dirPath string
	name    string
}

type PatchUnit struct {
	regex      *binaryregexp.Regexp
	executable PatchUnitExecutableInfo
}

var nvStream = PatchUnit{
	binaryregexp.MustCompile("(\u00FF\u0015.{4})\u0048\u008D\u004F\u0010"),
	PatchUnitExecutableInfo{path.Join(os.Getenv("ProgramW6432"), "NVIDIA Corporation", "NvStreamSrv"), "nvstreamer.exe"},
}

func patchNvStream(nvStream PatchUnit) error {
	joinDirPath := func(name string) string {
		return path.Join(nvStream.executable.dirPath, name)
	}

	if fp, err := os.OpenFile(joinDirPath(nvStream.executable.name), os.O_RDWR, 0666); err != nil {
		return err
	} else {
		defer fp.Close()
		createBackup := func() error {
			t := time.Now()
			formatted := fmt.Sprintf("%d-%02d-%02dT%02d-%02d-%02d",
				t.Year(), t.Month(), t.Day(),
				t.Hour(), t.Minute(), t.Second())
			fp.Seek(0, io.SeekStart)
			if fpBackup, err := os.Create(joinDirPath(fmt.Sprintf("%s-%s.bak", nvStream.executable.name, formatted))); err != nil {
				return err
			} else {
				defer fpBackup.Close()
				_, err := io.Copy(fpBackup, fp)
				return err
			}
		}

		reader := bufio.NewReader(fp)
		i, err := fp.Seek(0, io.SeekCurrent)
		if err != nil {
			return err
		}
		strMatch := nvStream.regex.FindReaderSubmatchIndex(reader)
		if len(strMatch) <= 0 {
			return errors.New("can't find the signature")
		}
		fmt.Printf("[+] Found the signature from Nvidia Stream Server at 0x%X!\n", strMatch[2])
		if err := createBackup(); err != nil {
			return err
		}
		fmt.Println("[+] Backup of Nvidia Stream Server created!\n")
		if _, err = fp.Seek(i, io.SeekStart); err != nil {
			return err
		}
		if _, err = fp.WriteAt([]byte{0x90, 0x90, 0x90, 0x90, 0x90, 0x90}, int64(strMatch[2])); err != nil {
			return err
		}
		fmt.Printf("[+] Written NOPs to 0x%X!\n", strMatch[2])
	}
	return nil
}

func main() {
	if err := patchNvStream(nvStream); err != nil {
		panic(err)
	} else {
		fmt.Println("[+] Nvidia Stream Server is now patched to unlock screen!")
	}
}

@stevefan1999-personal
Copy link
Author

I have confirmed the clipping is unbounded after my patch by using moonlight to drag the touch control outside my main monitor and so it goes to the second

@RL-shuyi
Copy link

I have confirmed the clipping is unbounded after my patch by using moonlight to drag the touch control outside my main monitor and so it goes to the second
特別感謝您的辛勤付出,我受困于這個問題很長時間了,直到發現您的帖子,很難描述我的激動心情!請原諒我的無知,對於程序代碼一竅不通,斗膽請求您給與我一個能夠運行的程序,再次表達對您的敬意!!郵箱:liuxiaoke10@icloud.com 再次表達歉意,給您添麻煩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment