From 3cd33df3e8b9aa75ca935241ba4ea343a0481a11 Mon Sep 17 00:00:00 2001 From: Micael Dias Date: Tue, 24 Dec 2024 17:44:56 +0000 Subject: [PATCH] Add support to game image with crc32 6ea6d1ba - Use direct syscalls to change memory protection options --- RS_ASIO/NtProtectVirtualMemory.asm | 29 +++++++++ RS_ASIO/Patcher.cpp | 42 +++++++++---- RS_ASIO/Patcher.h | 2 +- RS_ASIO/Patcher_21a8959a.cpp | 2 +- RS_ASIO/Patcher_6ea6d1ba.cpp | 99 ++++++++++++++++++++++++++++++ RS_ASIO/Patcher_d1b38fcb.cpp | 2 +- RS_ASIO/RS_ASIO.args.json | 10 +++ RS_ASIO/RS_ASIO.vcxproj | 7 +++ RS_ASIO/RS_ASIO.vcxproj.filters | 8 +++ 9 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 RS_ASIO/NtProtectVirtualMemory.asm create mode 100644 RS_ASIO/Patcher_6ea6d1ba.cpp create mode 100644 RS_ASIO/RS_ASIO.args.json diff --git a/RS_ASIO/NtProtectVirtualMemory.asm b/RS_ASIO/NtProtectVirtualMemory.asm new file mode 100644 index 0000000..5ba61be --- /dev/null +++ b/RS_ASIO/NtProtectVirtualMemory.asm @@ -0,0 +1,29 @@ +IFNDEF RAX +.MODEL FLAT, C +ENDIF + +.CODE + + +NtProtectVirtualMemory PROC + mov eax, 50h + call KiSystemCall + ret +NtProtectVirtualMemory ENDP + +KiSystemCall PROC +IFDEF RAX + mov r10,rcx + syscall + ret +ELSE + db 234 + dd exit + dw 51 +exit: + inc ecx + jmp dword ptr [edi+248] +ENDIF +KiSystemCall ENDP + +END \ No newline at end of file diff --git a/RS_ASIO/Patcher.cpp b/RS_ASIO/Patcher.cpp index 7e4e563..e429d26 100644 --- a/RS_ASIO/Patcher.cpp +++ b/RS_ASIO/Patcher.cpp @@ -2,6 +2,19 @@ #include "dllmain.h" #include "crc32.h" +EXTERN_C ULONG NtProtectVirtualMemory( + IN HANDLE ProcessHandle, + IN OUT PVOID* BaseAddress, + IN OUT PSIZE_T RegionSize, + IN ULONG NewProtect, + OUT PULONG OldProtect +); + +bool VirtualProtectSyscall(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) +{ + return NtProtectVirtualMemory(GetCurrentProcess(), &lpAddress, &dwSize, flNewProtect, lpflOldProtect) == 0; +} + DWORD GetImageCrc32() { char exePath[MAX_PATH]{}; @@ -20,6 +33,7 @@ DWORD GetImageCrc32() void PatchOriginalCode_d1b38fcb(); void PatchOriginalCode_21a8959a(); +void PatchOriginalCode_6ea6d1ba(); std::vector FindBytesOffsets(const BYTE* bytes, size_t numBytes) { @@ -57,7 +71,7 @@ std::vector FindBytesOffsets(const BYTE* bytes, size_t numBytes) return result; } -void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* TargetFn) +void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* TargetFn, size_t numNopsFollowing) { rslog::info_ts() << __FUNCTION__ " - num locations: " << offsets.size() << std::endl; @@ -70,7 +84,7 @@ void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* BYTE* bytes = (BYTE*)offset; DWORD oldProtectFlags = 0; - if (!VirtualProtect(offset, 6, PAGE_WRITECOPY, &oldProtectFlags)) + if (!VirtualProtectSyscall(offset, 6, PAGE_WRITECOPY, &oldProtectFlags)) { rslog::error_ts() << "Failed to change memory protection" << std::endl; } @@ -79,10 +93,13 @@ void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* bytes[0] = 0xe8; void** callAddress = (void**)(bytes + 1); *callAddress = (void*)targetRelAddress; - bytes[5] = 0x90; + for (size_t i = 0; i < numNopsFollowing; ++i) + { + bytes[5+i] = 0x90; + } - FlushInstructionCache(GetCurrentProcess(), offset, 6); - if (!VirtualProtect(offset, 6, oldProtectFlags, &oldProtectFlags)) + FlushInstructionCache(GetCurrentProcess(), offset, 5+numNopsFollowing); + if (!VirtualProtectSyscall(offset, 5 + numNopsFollowing, oldProtectFlags, &oldProtectFlags)) { rslog::error_ts() << "Failed to restore memory protection" << std::endl; } @@ -106,7 +123,7 @@ void Patch_CallRelativeAddress(const std::vector& offsets, void* TargetFn bytes += 5 + relOffset; DWORD oldProtectFlags = 0; - if (!VirtualProtect(bytes, 6, PAGE_WRITECOPY, &oldProtectFlags)) + if (!VirtualProtectSyscall(bytes, 6, PAGE_WRITECOPY, &oldProtectFlags)) { rslog::error_ts() << "Failed to change memory protection" << std::endl; } @@ -119,7 +136,7 @@ void Patch_CallRelativeAddress(const std::vector& offsets, void* TargetFn // ret bytes[5] = 0xc3; - if (!VirtualProtect(bytes, 6, oldProtectFlags, &oldProtectFlags)) + if (!VirtualProtectSyscall(bytes, 6, oldProtectFlags, &oldProtectFlags)) { rslog::error_ts() << "Failed to restore memory protection" << std::endl; } @@ -130,7 +147,7 @@ void Patch_CallRelativeAddress(const std::vector& offsets, void* TargetFn void Patch_ReplaceWithNops(void* offset, size_t numBytes) { DWORD oldProtectFlags = 0; - if (!VirtualProtect(offset, numBytes, PAGE_WRITECOPY, &oldProtectFlags)) + if (!VirtualProtectSyscall(offset, numBytes, PAGE_WRITECOPY, &oldProtectFlags)) { rslog::error_ts() << "Failed to change memory protection" << std::endl; } @@ -143,7 +160,7 @@ void Patch_ReplaceWithNops(void* offset, size_t numBytes) } FlushInstructionCache(GetCurrentProcess(), offset, numBytes); - if (!VirtualProtect(offset, numBytes, oldProtectFlags, &oldProtectFlags)) + if (!VirtualProtectSyscall(offset, numBytes, oldProtectFlags, &oldProtectFlags)) { rslog::error_ts() << "Failed to restore memory protection" << std::endl; } @@ -153,7 +170,7 @@ void Patch_ReplaceWithNops(void* offset, size_t numBytes) void Patch_ReplaceWithBytes(void* offset, size_t numBytes, const BYTE* replaceBytes) { DWORD oldProtectFlags = 0; - if (!VirtualProtect(offset, numBytes, PAGE_WRITECOPY, &oldProtectFlags)) + if (!VirtualProtectSyscall(offset, numBytes, PAGE_WRITECOPY, &oldProtectFlags)) { rslog::error_ts() << "Failed to change memory protection" << std::endl; } @@ -166,7 +183,7 @@ void Patch_ReplaceWithBytes(void* offset, size_t numBytes, const BYTE* replaceBy } FlushInstructionCache(GetCurrentProcess(), offset, numBytes); - if (!VirtualProtect(offset, numBytes, oldProtectFlags, &oldProtectFlags)) + if (!VirtualProtectSyscall(offset, numBytes, oldProtectFlags, &oldProtectFlags)) { rslog::error_ts() << "Failed to restore memory protection" << std::endl; } @@ -192,6 +209,9 @@ void PatchOriginalCode() case 0x21a8959a: PatchOriginalCode_21a8959a(); break; + case 0x6ea6d1ba: + PatchOriginalCode_6ea6d1ba(); + break; default: rslog::error_ts() << "Unknown game version" << std::endl; break; diff --git a/RS_ASIO/Patcher.h b/RS_ASIO/Patcher.h index 6a824d2..357400f 100644 --- a/RS_ASIO/Patcher.h +++ b/RS_ASIO/Patcher.h @@ -3,7 +3,7 @@ void PatchOriginalCode(); std::vector FindBytesOffsets(const BYTE* bytes, size_t numBytes); -void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* TargetFn); +void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* TargetFn, size_t numNopsFollowing=0); void Patch_CallRelativeAddress(const std::vector& offsets, void* TargetFn); void Patch_ReplaceWithNops(void* offset, size_t numBytes); void Patch_ReplaceWithBytes(void* offset, size_t numBytes, const BYTE* replaceBytes); diff --git a/RS_ASIO/Patcher_21a8959a.cpp b/RS_ASIO/Patcher_21a8959a.cpp index 9ec50c9..a3c7930 100644 --- a/RS_ASIO/Patcher_21a8959a.cpp +++ b/RS_ASIO/Patcher_21a8959a.cpp @@ -68,7 +68,7 @@ void PatchOriginalCode_21a8959a() { // patch CoCreateInstance calls rslog::info_ts() << "Patching CoCreateInstance" << std::endl; - Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstance, &Patched_CoCreateInstance); + Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstance, &Patched_CoCreateInstance, 1); // patch PortAudio MarshalStreamComPointers rslog::info_ts() << "Patching PortAudio MarshalStreamComPointers" << std::endl; diff --git a/RS_ASIO/Patcher_6ea6d1ba.cpp b/RS_ASIO/Patcher_6ea6d1ba.cpp new file mode 100644 index 0000000..769aa9a --- /dev/null +++ b/RS_ASIO/Patcher_6ea6d1ba.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "dllmain.h" +#include "Patcher.h" +#include "crc32.h" + +// this file contains the patch code for the game version released ON 2024-12-19 (Rocksmith 2014 Remastered Learn & Play) + +static const BYTE originalBytes_call_CoCreateInstance[]{ + 0x56, // push esi + 0xe8, 0x0e, 0x16, 0x94, 0x00, // call relative + 0x85, 0xc0, // test eax, eax + 0x0f, 0x88, 0xaa, 0x0c, 0x00, 0x00 // js ... +}; + +static const BYTE originalBytes_call_PortAudio_MarshalStreamComPointers[]{ + 0xe8, 0xec, 0xe4, 0xff, 0xff, // call + 0x83, 0xc4, 0x04, // add esp, 4 + 0x85, 0xc0 // test eax, eax +}; + +static const BYTE originalBytes_call_UnmarshalStreamComPointers[]{ + 0xe8, 0xd6, 0xfd, 0xff, 0xff, // call + 0x56, // push esi + 0xe8, 0xb0, 0xfe, 0xff, 0xff // call (to another uninteresting function) +}; + +static const BYTE originalBytes_TwoRealToneCablesMessageBoxStarting[]{ + 0x74, 0x6b, // jz ... + 0xba, 0x5c, 0x16, 0x8b, 0x01 // mov edx, offset "TooManyGuitarInputs" +}; + +static const BYTE originalBytes_TwoRealToneCablesMessageBoxMainMenu[]{ + 0x0f, 0x84, 0x95, 0x00, 0x00, 0x00, // jz ... + 0xba, 0x70, 0x16, 0x8b, 0x01, // mov edx, offset "$[34872]Warning! Two Rocksmith Real Ton..." + 0x8d, 0x75, 0xb0 // lea esi, [ebp+...] +}; + +template +void vector_append(std::vector& inOut, const std::vector& source) +{ + for (auto& it : source) + { + inOut.push_back(it); + } +} + +void PatchOriginalCode_6ea6d1ba() +{ + std::vector offsets_CoCreateInstanceAbs = FindBytesOffsets(originalBytes_call_CoCreateInstance, sizeof(originalBytes_call_CoCreateInstance)); + + std::vector offsets_PaMarshalPointers = FindBytesOffsets(originalBytes_call_PortAudio_MarshalStreamComPointers, sizeof(originalBytes_call_PortAudio_MarshalStreamComPointers)); + std::vector offsets_PaUnmarshalPointers = FindBytesOffsets(originalBytes_call_UnmarshalStreamComPointers, sizeof(originalBytes_call_UnmarshalStreamComPointers)); + + std::vector offsets_TwoRealToneCablesMessageBoxStarting = FindBytesOffsets(originalBytes_TwoRealToneCablesMessageBoxStarting, sizeof(originalBytes_TwoRealToneCablesMessageBoxStarting)); + std::vector offsets_TwoRealToneCablesMessageBoxMainMenu = FindBytesOffsets(originalBytes_TwoRealToneCablesMessageBoxMainMenu, sizeof(originalBytes_TwoRealToneCablesMessageBoxMainMenu)); + + if (offsets_CoCreateInstanceAbs.size() == 0 && offsets_PaMarshalPointers.size() == 0 && offsets_PaUnmarshalPointers.size() == 0) + { + rslog::error_ts() << "No valid locations for patching were found. Make sure you're trying this on the right game version." << std::endl; + } + else + { + // patch CoCreateInstance calls + rslog::info_ts() << "Patching CoCreateInstance" << std::endl; + Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstanceAbs, &Patched_CoCreateInstance, 1); + + // patch PortAudio MarshalStreamComPointers + rslog::info_ts() << "Patching PortAudio MarshalStreamComPointers" << std::endl; + Patch_CallRelativeAddress(offsets_PaMarshalPointers, &Patched_PortAudio_MarshalStreamComPointers); + + // patch PortAudio UnmarshalStreamComPointers + rslog::info_ts() << "Patching PortAudio UnmarshalStreamComPointers" << std::endl; + Patch_CallRelativeAddress(offsets_PaUnmarshalPointers, &Patched_PortAudio_UnmarshalStreamComPointers); + + // patch two guitar cables connected message in single-player + rslog::info_ts() << "Patching Two Guitar Tones Connected Message Box (starting menu) (num locations: " << offsets_TwoRealToneCablesMessageBoxStarting.size() << ")" << std::endl; + for (void* offset : offsets_TwoRealToneCablesMessageBoxStarting) + { + const BYTE replaceBytes[] + { + 0xeb, // jmp rel8 + }; + rslog::info_ts() << "Patching bytes at " << offset << std::endl; + Patch_ReplaceWithBytes(offset, sizeof(replaceBytes), replaceBytes); + } + + rslog::info_ts() << "Patching Two Guitar Tones Connected Message Box (main menu) (num locations: " << offsets_TwoRealToneCablesMessageBoxMainMenu.size() << ")" << std::endl; + for (void* offset : offsets_TwoRealToneCablesMessageBoxMainMenu) + { + const BYTE replaceBytes[] + { + 0x90, // original instruction at this point is 6 byte wide, we only need 5 bytes, so put a nop here + 0xe9, // jmp rel32 + }; + rslog::info_ts() << "Patching bytes at " << offset << std::endl; + Patch_ReplaceWithBytes(offset, sizeof(replaceBytes), replaceBytes); + } + } +} \ No newline at end of file diff --git a/RS_ASIO/Patcher_d1b38fcb.cpp b/RS_ASIO/Patcher_d1b38fcb.cpp index de9984a..8a36f91 100644 --- a/RS_ASIO/Patcher_d1b38fcb.cpp +++ b/RS_ASIO/Patcher_d1b38fcb.cpp @@ -102,7 +102,7 @@ void PatchOriginalCode_d1b38fcb() { // patch CoCreateInstance calls rslog::info_ts() << "Patching CoCreateInstance" << std::endl; - Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstanceAbs, &Patched_CoCreateInstance); + Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstanceAbs, &Patched_CoCreateInstance, 1); //Patch_CallRelativeAddress<(void*)&Patched_CoCreateInstance>(offsets_CoCreateInstanceRel); // patch PortAudio MarshalStreamComPointers diff --git a/RS_ASIO/RS_ASIO.args.json b/RS_ASIO/RS_ASIO.args.json new file mode 100644 index 0000000..cbfc4fa --- /dev/null +++ b/RS_ASIO/RS_ASIO.args.json @@ -0,0 +1,10 @@ +{ + "FileVersion": 2, + "Id": "4339185f-e3ea-4fe0-bbd7-67d4fe3fbf3e", + "Items": [ + { + "Id": "a2d778b6-ffaf-451e-84f5-66f351588f71", + "Command": "" + } + ] +} \ No newline at end of file diff --git a/RS_ASIO/RS_ASIO.vcxproj b/RS_ASIO/RS_ASIO.vcxproj index 2b98aa7..3ded978 100644 --- a/RS_ASIO/RS_ASIO.vcxproj +++ b/RS_ASIO/RS_ASIO.vcxproj @@ -55,6 +55,7 @@ + @@ -137,6 +138,7 @@ true false exports.def + false @@ -210,6 +212,7 @@ + @@ -236,7 +239,11 @@ + + + + \ No newline at end of file diff --git a/RS_ASIO/RS_ASIO.vcxproj.filters b/RS_ASIO/RS_ASIO.vcxproj.filters index 3415702..f03d19e 100644 --- a/RS_ASIO/RS_ASIO.vcxproj.filters +++ b/RS_ASIO/RS_ASIO.vcxproj.filters @@ -215,10 +215,18 @@ Source Files + + Source Files + Source Files + + + Source Files + + \ No newline at end of file