254 lines
6.2 KiB
ArmAsm
254 lines
6.2 KiB
ArmAsm
.versions SH4
|
|
|
|
mova r0, [first_patch_header]
|
|
mov r7, r0 # r7 = read ptr
|
|
xor r3, r3
|
|
dec r3
|
|
shl r3, 2 # r3 = 0xFFFFFFFC (mask for aligning r7)
|
|
apply_patch:
|
|
add r7, 3
|
|
and r7, r3 # r7 = (r7 + 3) & (~3) (align to 4-byte boundary)
|
|
mov.l r4, [r7]+ # r4 = dest addr
|
|
mov.l r5, [r7]+
|
|
add r5, r4 # r5 = dest end ptr (dest addr + size)
|
|
cmpeq r4, r5 # if (size == 0) return
|
|
bt done
|
|
|
|
again:
|
|
cmpeq r4, r5
|
|
bt apply_patch # if (r4 == r5) done with the patch; go to next header
|
|
mov.b r0, [r7]+
|
|
mov.b [r4], r0 # *(r4) = *(r7++);
|
|
bs again # r4++; continue
|
|
add r4, 1
|
|
|
|
done:
|
|
rets
|
|
nop
|
|
|
|
.align 4
|
|
first_patch_header:
|
|
|
|
|
|
|
|
.versions PPC
|
|
|
|
mflr r8
|
|
b get_patch_data_ptr
|
|
get_patch_data_ptr_ret:
|
|
mflr r7 # r7 = patch header
|
|
apply_patch:
|
|
addi r4, r7, 8 # r4 = start of patch data
|
|
lwz r3, [r4 - 8] # r3 = patch dest address
|
|
lwz r5, [r4 - 4] # r5 = patch data size
|
|
or r0, r3, r5
|
|
cmplwi r0, 0
|
|
mtlr r8
|
|
beqlr
|
|
add r7, r4, r5 # r7 = next patch header
|
|
.include CopyCode
|
|
b apply_patch
|
|
|
|
get_patch_data_ptr:
|
|
bl get_patch_data_ptr_ret
|
|
|
|
first_patch_header:
|
|
|
|
|
|
|
|
.versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU
|
|
|
|
start:
|
|
.include GetVersionInfoXB
|
|
test eax, eax
|
|
jnz can_patch
|
|
ret
|
|
|
|
can_patch:
|
|
push esi
|
|
push edi
|
|
push ebx
|
|
mov edi, eax # edi = ptr to version info struct
|
|
jmp get_patch_data_ptr
|
|
get_patch_data_ptr_ret:
|
|
pop ebx # ebx = patch header
|
|
|
|
apply_next_patch:
|
|
cmp dword [ebx + 4], 0
|
|
jne copy_code_and_apply_again
|
|
pop ebx
|
|
pop edi
|
|
pop esi
|
|
mov eax, 1
|
|
ret
|
|
|
|
copy_code_and_apply_again:
|
|
push dword [ebx] # dest addr
|
|
mov ecx, [edi + 0x0C]
|
|
call [ecx] # MmQueryAddressProtect
|
|
mov esi, eax # esi = prev protection flags
|
|
|
|
push 4 # new protection flags
|
|
push dword [ebx + 4] # size
|
|
push dword [ebx] # base address
|
|
mov ecx, [edi + 0x08]
|
|
call [ecx] # MmSetAddressProtect
|
|
|
|
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
|
|
|
|
push esi # new protection flags
|
|
push dword [ebx + 4] # size
|
|
push dword [ebx] # base address
|
|
lea ebx, [ebx + ecx + 8] # advance to next block
|
|
mov ecx, [edi + 0x08]
|
|
call [ecx] # MmSetAddressProtect
|
|
jmp apply_next_patch
|
|
|
|
get_patch_data_ptr:
|
|
call get_patch_data_ptr_ret
|
|
|
|
first_patch_header:
|
|
|
|
|
|
|
|
.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
|
|
jmp get_patch_data_ptr
|
|
get_patch_data_ptr_ret:
|
|
pop ebx # ebx = patch header
|
|
|
|
apply_next_patch:
|
|
cmp dword [ebx + 4], 0
|
|
jne copy_code_and_apply_again
|
|
pop ebx
|
|
mov eax, 1
|
|
ret
|
|
|
|
copy_code_and_apply_again:
|
|
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
|
|
|
|
lea ebx, [ebx + ecx + 8] # advance to next block
|
|
jmp apply_next_patch
|
|
|
|
get_patch_data_ptr:
|
|
call get_patch_data_ptr_ret
|
|
first_patch_header:
|