From b9a621e7ccb26e9b9101e6d1dfc1af38c3fab0ae Mon Sep 17 00:00:00 2001 From: James Osborne Date: Sun, 7 Jun 2026 06:12:32 -0400 Subject: [PATCH] Use VirtualProtect for PC WriteCodeBlocks --- .../PsoPeepsPCWriteCodeNoopTest.s | 9 +- .../System/WriteCodeBlocks.inc.s | 104 +++++++++++++++++- 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/system/client-functions/PsoPeepsPCWriteCodeNoopTest.s b/system/client-functions/PsoPeepsPCWriteCodeNoopTest.s index fd75c601..3161d9a5 100644 --- a/system/client-functions/PsoPeepsPCWriteCodeNoopTest.s +++ b/system/client-functions/PsoPeepsPCWriteCodeNoopTest.s @@ -1,6 +1,6 @@ .meta visibility="all" -.meta name="PC data noop" -.meta description="PC-only test.\nWrites zeros over\nwritable data." +.meta name="PC text noop" +.meta description="PC-only test.\nWrites NOPs over\nexisting code NOPs." entry_ptr: reloc0: @@ -12,9 +12,10 @@ start: .versions 2OJW - .data 0x0068854E + .data 0x004E03DE .data 2 - add [eax], al + nop + nop diff --git a/system/client-functions/System/WriteCodeBlocks.inc.s b/system/client-functions/System/WriteCodeBlocks.inc.s index 52e1fd14..69bed3c2 100644 --- a/system/client-functions/System/WriteCodeBlocks.inc.s +++ b/system/client-functions/System/WriteCodeBlocks.inc.s @@ -118,7 +118,109 @@ first_patch_header: -.versions 2OJW 2OJZ 50YJ 59NJ 59NL +.versions 2OJW + +start: + push ebx + push esi + push edi + sub esp, 8 # [esp] = oldProtect, [esp + 4] = tempProtect + jmp get_patch_data_ptr +get_patch_data_ptr_ret: + pop ebx # ebx = patch header + + # Resolve kernel32!VirtualProtect using pso.exe's 2OJW IAT: + # GetModuleHandleA = 0x0064D114 + # GetProcAddress = 0x0064D128 + # + # Build "KERNEL32.dll\0" on stack. + sub esp, 16 + mov dword [esp], 0x4E52454B # "KERN" + mov dword [esp + 4], 0x32334C45 # "EL32" + mov dword [esp + 8], 0x6C6C642E # ".dll" + mov dword [esp + 12], 0 + push esp + call [0x0064D114] # GetModuleHandleA + add esp, 16 + test eax, eax + jz fail + + mov esi, eax # esi = kernel32 module handle + + # Build "VirtualProtect\0" on stack. + sub esp, 16 + mov dword [esp], 0x74726956 # "Virt" + mov dword [esp + 4], 0x506C6175 # "ualP" + mov dword [esp + 8], 0x65746F72 # "rote" + mov dword [esp + 12], 0x00007463 # "ct\0\0" + push esp + push esi + call [0x0064D128] # GetProcAddress + add esp, 16 + test eax, eax + jz fail + + mov edi, eax # edi = VirtualProtect + +apply_next_patch: + cmp dword [ebx + 4], 0 + jne copy_code_and_apply_again + + add esp, 8 + pop edi + pop esi + pop ebx + mov eax, 1 + ret + +copy_code_and_apply_again: + # VirtualProtect(dest, size, PAGE_EXECUTE_READWRITE, &oldProtect) + lea eax, [esp] + push eax + push 0x40 + push dword [ebx + 4] + push dword [ebx] + call edi + test eax, eax + jz fail + + xor ecx, ecx # ecx = offset + mov edx, [ebx] # edx = dest addr +copy_next_byte: + mov al, [ebx + ecx + 8] # copy one byte to dest + mov [edx + ecx], al + inc ecx # offset++ + cmp [ebx + 4], ecx # check if all bytes have been copied + jne copy_next_byte + + # VirtualProtect(dest, size, oldProtect, &tempProtect) + mov esi, [ebx + 4] # esi = size; VirtualProtect preserves esi + mov edx, [esp] # edx = oldProtect + lea eax, [esp + 4] + push eax + push edx + push esi + push dword [ebx] + call edi + + lea ebx, [ebx + esi + 8] # advance to next block + jmp apply_next_patch + +fail: + add esp, 8 + pop edi + pop esi + pop ebx + xor eax, eax + ret + +get_patch_data_ptr: + call get_patch_data_ptr_ret +first_patch_header: + + + +.versions 2OJZ 50YJ 59NJ 59NL start: push ebx