Metro Exodus Enhanced Edition Dev

Since I’m unsure if I’ll ever actually finish my Metro Exodus Enhanced Edition Table I will just post some things I found so far… (It’s not that much, really. But maybe someone else wants to work on it!)

The following scripts were made for v2.0.0.1 - Steam!

  1. Health Hook
; Game	 : MetroExodus.exe
; Version: 1.0
; Date	 : 09-11-2024
; Author : Leunsel
;
; Address of signature = MetroExodus.exe + 0x00A56E79
; "0F 2F ? ? ? ? ? 0F 83 ? ? ? ? 48 8B ? ? ? ? ? 48 85 ? 75"
; ---> MetroExodus.exe+A56E79 - 0F 2F 80 B8 03 00 00 - comiss xmm0,[rax+000003B8]
; ..."0F 2F 80 B8 03 00 00 0F 83 42" <<< Original Pattern

[ENABLE]

aobScanModule(HealthHook,MetroExodus.exe,0F 2F ? ? ? ? ? 0F 83 ? ? ? ? 48 8B ? ? ? ? ? 48 85 ? 75)
alloc(newmem,$1000,HealthHook)

label(return code)
label(HealthPtr)

newmem:
  mov [HealthPtr],rax
code:
  comiss xmm0,[rax+000003B8]
  jmp return

HealthPtr:
 dq 0

HealthHook:
  jmp newmem
  nop 2
return:
registersymbol(HealthHook HealthPtr)

[DISABLE]

HealthHook:
  db 0F 2F 80 B8 03 00 00

unregisterSymbol(*)
dealloc(*)
  1. Mask Health Hook
; Game	 : MetroExodus.exe
; Version: 1.0
; Date	 : 09-11-2024
; Author : Leunsel
; 
; Address of signature = MetroExodus.exe + 0x0096DA0A
; "F3 0F ? ? ? ? ? ? F3 0F ? ? F3 0F ? ? F3 0F ? ? 0F 28 ? F3 0F ? ? ? ? F3 0F ? ? 48 83 C4"
; ---> MetroExodus.exe+96DA07 - 0F 28 D4 - movaps xmm2,xmm4
; ---> MetroExodus.exe+96DA0A - F3 0F 5C 87 B8 03 00 00 - subss xmm0,[rdi+000003B8]
; ---> MetroExodus.exe+96DA12 - F3 0F 5C E1 - subss xmm4,xmm1
; ..."F3 0F 5C 87 B8 03 00 00" <<< Original Pattern

[ENABLE]

aobScanModule(MaskHealthHook,MetroExodus.exe,F3 0F ? ? ? ? ? ? F3 0F ? ? F3 0F ? ? F3 0F ? ? 0F 28 ? F3 0F ? ? ? ? F3 0F ? ? 48 83 C4)
alloc(newmem_B,$1000,MaskHealthHook)

label(return_B code_B)
label(MaskHealthPtr)

newmem_B:
  mov [MaskHealthPtr],rdi
code_B:
  subss xmm0,[rdi+000003B8]
  jmp return_B

MaskHealthPtr:
 dq 0

MaskHealthHook:
  jmp newmem_B
  nop 3
return_B:
registersymbol(MaskHealthHook MaskHealthPtr)

[DISABLE]

MaskHealthHook:
  db F3 0F 5C 87 B8 03 00 00

unregisterSymbol(*)
dealloc(*)
  1. Transform Hook
; Game	 : MetroExodus.exe
; Version: 1.0
; Date	 : 09-11-2024
; Author : Leunsel
; 
; Address of signature = PhysX3_x64.dll + 0x0011835A
; "F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? 48 8D ? ? F3 0F"
; ---> PhysX3_x64.dll+11835A - F3 0F 10 50 0C - movss xmm2,[rax+0C]
; ..."F3 0F 10 50 0C" <<< Original Pattern

[ENABLE]

aobScanModule(TransformHook,PhysX3_x64.dll,F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? F3 0F ? ? ? 48 8D ? ? F3 0F)
alloc(newmem,$1000,TransformHook)

label(return code)
label(TransformPtr)
label(TransformOffsets SavedPositionFlt BackupPositionFlt)

newmem:
  mov [TransformPtr],rax
code:
  movss xmm2,[rax+0C]
  jmp return

TransformPtr:
 dq 0
TransformOffsets:
 dd 0C ; X -> Example: 40 
 dd 10 ; Y -> Example: 44
 dd 14 ; Z -> Example: 48
SavedPositionFlt:
 dd (float)0 ; X
 dd (float)0 ; Y
 dd (float)0 ; Z
BackupPositionFlt:
 dd (float)0 ; X
 dd (float)0 ; Y
 dd (float)0 ; Z

; Those Symbols are only required if you actually
; "plan" on adding a dedicated Teleport.

TransformHook:
  jmp newmem
return:
registersymbol(TransformHook TransformPtr)
registersymbol(TransformOffsets SavedPositionFlt BackupPositionFlt)

[DISABLE]

TransformHook:
  db F3 0F 10 50 0C

unregisterSymbol(*)
dealloc(*)
  1. Health Decrease Hook
; Game	 : MetroExodus.exe
; Version: 1.0
; Date	 : 09-11-2024
; Author : Leunsel
; 
; Address of signature = MetroExodus.exe + 0x00874232
; "F3 0F ? ? ? 48 8B ? F3 41 ? ? ? 8B 41"
; ---> MetroExodus.exe+87422F - 48 8B FA - mov rdi,rdx
; ---> MetroExodus.exe+874232 - F3 0F 5C 52 14 - subss xmm2,[rdx+14]
; ---> MetroExodus.exe+874237 - 48 8B D9 - mov rbx,rcx
; ---> MetroExodus.exe+87423A - F3 41 0F 11 10 - movss [r8],xmm2
; ..."F3 0F 5C 52 14 48" <<< Original Pattern

[ENABLE]

aobScanModule(HealthDecHook,MetroExodus.exe,F3 0F ? ? ? 48 8B ? F3 41 ? ? ? 8B 41)
alloc(newmem,$1000,HealthDecHook)

label(return code exit)
label(HealthDecPtr HealthDecFlag HealthDecMultiplier)
label(Player Mask Entity)

newmem:
  push r9
  push r10
  push r11
  push r12
  ; ...
  mov r9,r8 ; To not corrupt the register
  sub r9,3B8 ; Base of each Health Hook
             ; Now we can compare...
             ; movss [r8],xmm2 -> R8 = HealthPtr + 0x3B8
  test r9,r9
  jz code
    mov r10,HealthPtr
    mov r10,[r10]
    test r10,r10
    jz code
       cmp r10,r9
       je Player
          mov r12,MaskHealthPtr
          mov r12,[r12]
          test r12,r12
          jz code
            cmp r12,r9
            je Mask
            jmp Entity ; This might actually not "work properly" yet.
                       ; I think a dedicated Enemy Compare is necessary.

Player:
  mov [HealthDecPtr],r9
  cmp byte ptr [HealthDecFlag],0 ; Disabled
  je code
  cmp byte ptr [HealthDecFlag],1 ; No Damage
  jne @f
    jmp exit ; We just skip the subtraction!
  @@:
  cmp byte ptr [HealthDecFlag],2 ; Multiplier (Default)
  jne @f
    movss xmm15,[rdx+14]
    mulss xmm15,[HealthDecMultiplier]
    movss [rdx+14],xmm15
	jmp code
  @@:
  mov byte ptr [HealthDecFlag],0
  jmp code

Mask:
  mov [HealthDecPtr+08],r9
  cmp byte ptr [HealthDecFlag+1],0 ; Disabled
  je code
  cmp byte ptr [HealthDecFlag+1],1 ; No Damage
  jne @f
    jmp exit ; We just skip the subtraction!
  @@:
  cmp byte ptr [HealthDecFlag+1],2 ; Multiplier (Default)
  jne @f
    movss xmm15,[rdx+14]
    mulss xmm15,[HealthDecMultiplier+4]
    movss [rdx+14],xmm15
	jmp code
  @@:
  mov byte ptr [HealthDecFlag+1],0
  jmp code

Entity:
  mov [HealthDecPtr+10],r9
  cmp byte ptr [HealthDecFlag+2],0 ; Disabled
  je code
  cmp byte ptr [HealthDecFlag+2],1 ; Feature
  jne @f
    movss xmm2,[rdx+14] ; We just move the "Damage" into the current health
    jmp code
  @@:
  cmp byte ptr [HealthDecFlag+2],2 ; Multiplier (Default)
  jne @f
    movss xmm15,[rdx+14]
    mulss xmm15,[HealthDecMultiplier+8]
    movss [rdx+14],xmm15
	jmp code
  @@:
  mov byte ptr [HealthDecFlag+2],0
  jmp code

code:
  subss xmm2,[rdx+14]
exit:
  pop r12
  pop r11
  pop r10
  pop r9
  jmp return

HealthDecPtr:
 dq 0 ; Player - 0x00
 dq 0 ; Mask   - 0x08
 dq 0 ; Entity - 0x10
HealthDecFlag:
 db 2 ; Player - 0x0
 db 2 ; Mask   - 0x1
 db 2 ; Entity - 0x2
HealthDecMultiplier:
 dd (float)0.25 ; Player - 0x0
 dd (float)0.25 ; Mask   - 0x4
 dd (float)3.00 ; Entity - 0x8

HealthDecHook:
  jmp newmem
return:
registersymbol(HealthDecHook HealthDecPtr HealthDecFlag HealthDecMultiplier)

[DISABLE]

HealthDecHook:
  db F3 0F 5C 52 14

unregisterSymbol(*)
dealloc(*)
1 Like