Closed fakuivan closed 5 years ago
I forgot to mention, the version of the game is 1.77.9.0
, Dishonored2.exe
's sha256 hash is F90068D43402DB520F7EC2AE6B95FB827140ECC7D3B846B83FBCB84EF933ADFB
. The update without denuvo
Update: The address for the cvar g_stopTime
is Dishonored2.exe+3BF9A98
. Setting it to != 0 will pause the game, sadly there's no code that accesses the cvar g_freezeTime
.
I was able to find and extract all of the addresses for the cvars in this game using a script that would hook the command buffer read (there's no dev console functionality on the release version), to later write arbitrary commands.
The listCvars
command at some point access an array with pointers to all the cvar structs on the game. I compiled a list with the cvars' name, default value, description, address of the struct and the address to the integer representation of the value here https://gist.github.com/fakuivan/8910a208fc88f831f06b047d0955e4fe . This list lacks 4-7 cvars that got trimmed because I wasn't able to determine the size of the cvar list programmatically (and I don't like counting offsets).
I hope this helps
These are the offsets for the focal distance (I'm not sure what it means from an optics perspective to have two different focal points, but they might be weird FOVxy translations), camera coords and rotation matrix:
These offsets are relative to the address contained in rcx
at Dishonored2.exe+A0F8D0
. These values won't be overwritten when g_stopTime
is set to a non-zero value.
I found an instruction that will reliably access the camera struct (at a -0x30
offset from the first proof of concept). Even on the main menu! Also the above "different focal distances for each axis" nonsense turned out to be the FOVs in radians.
This is the injection script for cheat engine:
{ Game : Dishonored2.exe
Version:
Date : 2019-01-13
Author : fakui
This script does blah blah blah
}
[ENABLE]
aobscanmodule(hook_reliable_camera_struct,Dishonored2.exe,48 8B 71 10 48 89 BC 24 80 00 00 00) // should be unique
alloc(newmem,$1000,"Dishonored2.exe"+96F064)
label(code)
label(return)
label(CameraStruct)
registersymbol(CameraStruct)
newmem:
mov [CameraStruct],rcx
jmp code
CameraStruct:
dq 0
code:
mov rsi,[rcx+10]
mov [rsp+00000080],rdi
jmp return
hook_reliable_camera_struct:
jmp newmem
nop
nop
nop
nop
nop
nop
nop
return:
registersymbol(hook_reliable_camera_struct)
[DISABLE]
hook_reliable_camera_struct:
db 48 8B 71 10 48 89 BC 24 80 00 00 00
unregistersymbol(hook_reliable_camera_struct)
unregistersymbol(CameraStruct)
dealloc(newmem)
{
// ORIGINAL CODE - INJECTION POINT: "Dishonored2.exe"+96F064
"Dishonored2.exe"+96F040: 48 85 C0 - test rax,rax
"Dishonored2.exe"+96F043: 74 06 - je Dishonored2.exe+96F04B
"Dishonored2.exe"+96F045: 48 83 C0 E0 - add rax,-20
"Dishonored2.exe"+96F049: EB 03 - jmp Dishonored2.exe+96F04E
"Dishonored2.exe"+96F04B: 49 8B C7 - mov rax,r15
"Dishonored2.exe"+96F04E: 48 8D 69 30 - lea rbp,[rcx+30]
"Dishonored2.exe"+96F052: 48 83 C0 30 - add rax,30
"Dishonored2.exe"+96F056: 48 3B C5 - cmp rax,rbp
"Dishonored2.exe"+96F059: 0F 85 A0 01 00 00 - jne Dishonored2.exe+96F1FF
"Dishonored2.exe"+96F05F: 48 89 74 24 78 - mov [rsp+78],rsi
// ---------- INJECTING HERE ----------
"Dishonored2.exe"+96F064: 48 8B 71 10 - mov rsi,[rcx+10]
"Dishonored2.exe"+96F068: 48 89 BC 24 80 00 00 00 - mov [rsp+00000080],rdi
// ---------- DONE INJECTING ----------
"Dishonored2.exe"+96F070: 48 8D B9 28 4F 02 00 - lea rdi,[rcx+00024F28]
"Dishonored2.exe"+96F077: 48 85 F6 - test rsi,rsi
"Dishonored2.exe"+96F07A: 74 46 - je Dishonored2.exe+96F0C2
"Dishonored2.exe"+96F07C: 48 8B 06 - mov rax,[rsi]
"Dishonored2.exe"+96F07F: 48 8B CE - mov rcx,rsi
"Dishonored2.exe"+96F082: FF 10 - call qword ptr [rax]
"Dishonored2.exe"+96F084: 8B 48 60 - mov ecx,[rax+60]
"Dishonored2.exe"+96F087: 3B 0D A3 80 2B 03 - cmp ecx,[Dishonored2.exe+3C27130]
"Dishonored2.exe"+96F08D: 7C 33 - jl Dishonored2.exe+96F0C2
"Dishonored2.exe"+96F08F: 3B 0D 9F 80 2B 03 - cmp ecx,[Dishonored2.exe+3C27134]
}
and the new offsets (nudged back 0x30
):
Thanks for all the hard work! :) I don't have the game installed currently, so can't immediately jump in (moving systems as well) so it might take a while (if ever) that I pick up this info,
A remark about the FoVs: usually there are 2 indeed, one for horizontal and one for vertical viewing angle, but the game engines usually calculate these from a single FoV value + AR setting. The single FoV value is easier to work with (as you then just have to change 1 value to zoom in ;)).
It's ok, I'm planning on providing support myself. It'd be cool if you could point me to commits where you updated the code base for other games, in a similar way I'd need to do for this one, with the AOB scanner and RIP-relative address extractor.
Sorry for the late reply.
First I hope you've read my long article about this: https://weblogs.asp.net/fbouma/let-s-add-a-photo-mode-to-wolfenstein-ii-the-new-colossus-pc (it talks about how IGCS works)
What I always do is pick the latest camera source and copy that into a new folder, then remove the temp folders from VC++, rename the project in vs and go from there.
Here you have two choices: either the Assassin's Creed Odyssey camera or the Shadow of the Tombraider camera. I'd pick the Odyssey camera, as it has everything you need. The last 5-8 cameras have all the logic you need, and important bugfixes in e.g. hook setting.
Say you pick the odyssey camera, then the first thing you need to do the following:
From here, I always first disable all hook interception in InterceptorHelper (https://github.com/FransBouma/InjectableGenericCameraSystem/blob/master/Cameras/AssassinsCreedOdyssey/InjectableGenericCameraSystem/InterceptorHelper.cpp#L102 and https://github.com/FransBouma/InjectableGenericCameraSystem/blob/master/Cameras/AssassinsCreedOdyssey/InjectableGenericCameraSystem/InterceptorHelper.cpp#L108), and simply build a dll and inject it to see if the overlay works and the window title is picked up.
After that, first I'll see how many interceptions I need to add to the interceptor.asm file. All functions are defined here: https://github.com/FransBouma/InjectableGenericCameraSystem/blob/master/Cameras/AssassinsCreedOdyssey/InjectableGenericCameraSystem/InterceptorHelper.cpp#L41, all continue pointers are defined right below it. The AOB blocks are defined below that.
If you have questions, where what's done, let me know. The Dishonored2 camera I made for ansel has the right order of quaternion operations, so it should be easy to port.
Closed as no activity for a long time.
Camera properties: I've seen this take two
rcx
addresses on the menu screen.As for pausing the game, the game has a simulation timescale value that when set to
0
all entity updating is suspended (including the code that writes to the camera values). To capture this value I used an AOB injection script on cheat engine, this is what it looks like:However this code is not executed on the menu screen. We should look for a more reliable one.
The time scale variable can now be accessed at offset
0x110
fromEngineTimings