REDengine 4 uses as optimization several hashing algorithms. In
my post here I was mentioning 2 hashes located at
0x18 and
0x20 offsets in the RTTI Class. Well, took a bit of figuring out, but:
Code: Select all
Cyberpunk2077.exe+17D06D5 | 48:BB 27418AEED12FB02E | MOV RBX,2EB02FD1EE8A4127 |
Cyberpunk2077.exe+17D06DF | 0F85 5CFFFFFF | JNE cyberpunk2077.7FF7624C0641 |
Cyberpunk2077.exe+17D06E5 | 48:8D15 9451A701 | LEA RDX,QWORD PTR DS:[7FF763F35880] | 00007FF763F35880:"gameStackedItemData"
2EB02FD1EE8A4127
Where's that coming from? Well, try out this page:
[Link]. Put in
gameStackedItemData, click
Encode and scroll down a bit:
So
2EB02FD1EE8A4127 is
FNV1A64( "gameStackedItemData" ).
Similarly, the hash stored at offset 0x20.. I couldn't find a static reference to it in game's code (it's not hardcoded like the MOV RBX,hash above), but a quick CE scan for the hash got me here:
So if you repeat the process and this time around encode
script_ref:gameStackedItemData on that page, you get:
So that gives you what the 2 mysterious hashes in RTTI_Class +0x18 and +0x20 stand for
There's another function used for hashing game objects by string. For example, "
Items.money" is hashed to
F5E188EC. And you can find this value stored inside the processed structure that, for example, you read when you open the Inventory
Yes, can be used as a filtering parameter.
This function is reached through here in the Steam 1.03 executable:
Code: Select all
Cyberpunk2077.exe+93E20 | 48:8D15 21E10F03 | LEA RDX,QWORD PTR DS:[7FF763E81F48] | 00007FF763E81F48:"AIGeneralSettings.baseActionParamsPackageName"
Cyberpunk2077.exe+93E27 | 48:8D0D 1A412104 | LEA RCX,QWORD PTR DS:[7FF764F97F48] |
Cyberpunk2077.exe+93E2E | E9 5D3EA302 | JMP cyberpunk2077.7FF7637B7C90 |
Inside
Cyberpunk2077.exe+2AC7C90 this happens:
And the hashing core itself:
So, if we name "Cyberpunk2077.exe+2AC7C90" ==
HashStr, then the C++ prototype is:
HashStr( out [rcx], in [rdx] ). And inside, if we name "Cyberpunk2077.exe+2AC8150" ==
hashstring, then the C++ prototype is:
hashstring( lenA [rcx], str [rdx], len [r8] ). Where
lenA is usually 0 and it represents
up to how many characters from the string you want to hash. 0 means whole string, 1 means up to last character but without it, 2 means up to the 2nd from last character, not including it. And so on
So: HashStr( out, "Items.money" ) -> out: F5E188EC | 0B 00 00 00. The result of the hashing function is
string hash |
size of string. Something tells me the string size + hash can be used in a reverse (decode) function. Although, from the looks of it, seems a one-way hash. Remains to be seen.
How can you now use the above hash in your code? Simple.
cfemen is already working on gathering hashes for filtering the stuff in his script, as I don't have time to spend on this. Still working on that console enabling, remember?
In short, Inventory crap is read through this generic function (it's a member-function in any
gameUniqueItemData-typed object; that's why it will read anything and without a filter, your script will crap out):
Code: Select all
Cyberpunk2077.exe+17C1BB0 - 8B 41 78 - mov eax,[rcx+78]
Cyberpunk2077.exe+17C1BB3 - C3 - ret
Here's how I hooked it:
Code: Select all
[ENABLE]
alloc( ReadHook, 0x1000, Cyberpunk2077.exe )
ReadHook:
push rax
push rbx
push rdi
xor rdi,rdi
mov rbx,HashTable
_loop:
mov rax,[rbx+rdi*8] // read hash from our table
test rax,rax // check if we reached the end of the table
je short _skip // exit if so
cmp [rcx+40],eax // if current game hash == our HashTable[i], then
jne short @f
// do something (e.g.: mov [rcx+78],#100)
add [rcx+78],#100
jmp short _skip // and exit
@@:
inc rdi
jmp short _loop
_skip:
pop rdi
pop rbx
pop rax
readmem( Cyberpunk2077.exe+17C1BB0, 4 )
align 10 CC
HashTable:
dq 00000000F5E188EC // Items.money
dq 0
Cyberpunk2077.exe+17C1BB0:
jmp ReadHook
[DISABLE]
Cyberpunk2077.exe+17C1BB0:
mov eax,[rcx+78]
ret
dealloc( ReadHook )
So now, every time you open the Inventory, your quantity for "Items.money" is going to be updated to 100
BR,
Sun