SunBeam on 15/12/2015 at 00:46
Hello folks.
Long time, no post from my part on these forums (ever since (
http://www.ttlg.com/forums/showthread.php?t=142535) DarkHook for Thief 1, Gold and 2 - original or New Dark).
With the help of
snobel, I decided I would take up on this one. I've worked on several titles 1-2 years ago running on Unreal Engine 3 (unfortunately, Thief 4 wasn't one of them, due to insufficient determination and a bad break-up), figuring out the effects of cheats code-wise and managing to activate/toggle them without the need for a console and commands to type in (most of these games don't have a console available to be able to do this). Now, for those starting a flame-fest on why people would cheat in this game, this is not the post to do it in. In short, I do what I'm about to post for fun. It keeps my brain in motion, keeps me challenged, finding interesting stuff to analyze while at it. Nothing else. Same as you, I've played the series over and over again, without cheats, I know these games by heart, I know how taffers out there love the darkness and challenges it brings, having to fiddle with the scarce inventory and timed motions and countless saves/loads just to get it perfect. Been there, done that.
This thread is about a different aspect. Code analysis.
Alrighty.
What I did was to get Thief 3 installed (I have version 1.1 of it) and (
http://www.shadowdarkkeep.com/files/thief3editorrelease_jan2005.zip) T3 Editor Pack. I'm using, as indicated by the aforementioned user,
T3MainOptVersion.exe, renamed to
T3Main.exe (I've renamed the original one to
T3Main_original.exe, just to have both at my disposal, since I'm about to compare them).
The above archive also comes with an .ini file (T3Menu.ini), containing various cheats. I'll focus on
God for now:
Code:
[Invulnerability...]
{
<'GOD OFF',GOD OFF>
<'GOD PLAYER',GOD PLAYER>
<'GOD HALF',GOD HALF>
<'GOD AIS',GOD AIS>
<'GOD ALL',GOD ALL>
}
Having done also a bit of programming in the past, I can easily spot one thing from the beginning. GOD OFF would be a 0, GOD PLAYER would be 1, GOD HALF 2, GOD AIS 3 and GOD ALL 4. Let's see if I'm right :)
Firing up OllyDbg 1.10 and loading T3Main.exe (the one from the Editor Pack, then looking for string references to 'god':
Inline Image:
http://i.imgur.com/6ZO8hn5.pngDouble-clicking first reference takes me here:
Code:
109B03D0 . 68 DC92FF10 PUSH 10FF92DC ; ASCII "GOD"
109B03D5 . 50 PUSH EAX
109B03D6 . E8 C57B1F00 CALL 10BA7FA0
109B03DB . 83C4 08 ADD ESP,8
109B03DE . 85C0 TEST EAX,EAX
109B03E0 . 0F84 CF010000 JE 109B05B5
109B03E6 . 8D4B 08 LEA ECX,DWORD PTR DS:[EBX+8]
109B03E9 . 68 D492FF10 PUSH 10FF92D4 ; ASCII "PLAYER"
109B03EE . 51 PUSH ECX
109B03EF . E8 AC7B1F00 CALL 10BA7FA0
109B03F4 . 83C4 08 ADD ESP,8
109B03F7 . 85C0 TEST EAX,EAX
109B03F9 . 74 5D JE SHORT 109B0458
109B03FB . 6A 01 PUSH 1 <-- lookie lookie
109B03FD . E8 6E960300 CALL 109E9A70 <-- lookie lookie
109B0402 . 68 B892FF10 PUSH 10FF92B8 ; ASCII "PLAYER IS NOW INVULNERABLE"
109B0407 . 57 PUSH EDI
109B0408 . E8 13731F00 CALL 10BA7720
Inside CALL at 109B03FD something interesting happens:
Code:
109E9A70 /$ 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] <-- grabbing PUSHed stack value
109E9A74 |. A3 B4971811 MOV DWORD PTR DS:[111897B4],EAX <-- updating a static DWORD to this value
109E9A79 \. C3 RETN
Programming experience also tells me you activate a portion of code, then write up to the console window what happened. Therefore, enable god cheat, the print "PLAYER IS NOW INVULNERABLE".
If we look for references (what code calls this function - 109E9A70) we find the following list:
Code:
Local calls from 109B03FD, 109B046F, 109B04AF, 109B04F2, 109B0535, 10A9C112, 10A9DE93, 10A9E183
Let's see:
Code:
109B03FB . 6A 01 PUSH 1 <-- value is 1 if only player has god on
109B03FD . E8 6E960300 CALL 109E9A70
109B0402 . 68 B892FF10 PUSH 10FF92B8 ; ASCII "PLAYER IS NOW INVULNERABLE"
..
109B046D . 6A 02 PUSH 2 <-- value is 2 if all AIs are invlunerable, so I was wrong in the beginning :)
109B046F . E8 FC950300 CALL 109E9A70
109B0474 . 68 7C92FF10 PUSH 10FF927C ; ASCII "ALL AIS ARE NOW INVULNERABLE"
..
109B04AD . 6A 03 PUSH 3 <-- value is 3 if everyone is invincible
109B04AF . E8 BC950300 CALL 109E9A70
109B04B4 . 68 4492FF10 PUSH 10FF9244 ; ASCII "EVERYONE IS NOW INVULNERABLE"
..
109B04F0 . 6A 04 PUSH 4 <-- value is 4 if half-god
109B04F2 . E8 79950300 CALL 109E9A70
109B04F7 . 68 FC91FF10 PUSH 10FF91FC ; ASCII "PLAYER IS NOW PARTIALLY VULNERABLE"
..
109B0533 . 6A 00 PUSH 0 <-- value is 0 if god is disabled
109B0535 . E8 36950300 CALL 109E9A70
109B053A . 68 C091FF10 PUSH 10FF91C0 ; ASCII "INVULNERABLILITY DISABLED"
So these are basically the locations that would access that DWORD (and 3 more I didn't list due to redundancy). Now what else we want to know if where's this DWORD coming into play, how does the game check it to determine if god is on or not. Simple, scroll a bit up from our function:
Code:
109E9A5F CC INT3
109E9A60 /$ A1 B4971811 MOV EAX,DWORD PTR DS:[111897B4] <-- where DWORD is actually accessed (interrogated)
109E9A65 \. C3 RETN
109E9A66 CC INT3
109E9A67 CC INT3
109E9A68 CC INT3
109E9A69 CC INT3
109E9A6A CC INT3
109E9A6B CC INT3
109E9A6C CC INT3
109E9A6D CC INT3
109E9A6E CC INT3
109E9A6F CC INT3
109E9A70 /$ 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] <-- our function
109E9A74 |. A3 B4971811 MOV DWORD PTR DS:[111897B4],EAX
109E9A79 \. C3 RETN
109E9A7A CC INT3
And the functions that call it are 4 in number:
Code:
Local calls from 10F345F7, 10F3461B, 10F34637, 10F34653
Code:
10F345F0 . 8A45 14 MOV AL,BYTE PTR SS:[EBP+14]
10F345F3 . 84C0 TEST AL,AL
10F345F5 . 75 5C JNZ SHORT 10F34653
10F345F7 . E8 6454ABFF CALL 109E9A60 <-- here
10F345FC . 85C0 TEST EAX,EAX
10F345FE . 74 53 JE SHORT 10F34653
..
10F34611 . 8B86 C0000000 MOV EAX,DWORD PTR DS:[ESI+C0]
10F34617 . 85C0 TEST EAX,EAX
10F34619 . 74 38 JE SHORT 10F34653
10F3461B . E8 4054ABFF CALL 109E9A60 <-- here
10F34620 . A8 01 TEST AL,1
10F34622 . 74 13 JE SHORT 10F34637
10F34624 . 8B96 C0000000 MOV EDX,DWORD PTR DS:[ESI+C0]
10F3462A . F682 C8000000 01 TEST BYTE PTR DS:[EDX+C8],1
10F34631 . 0F85 CA000000 JNZ 10F34701
10F34637 > E8 2454ABFF CALL 109E9A60 <-- here
10F3463C . A8 02 TEST AL,2
10F3463E . 74 13 JE SHORT 10F34653
10F34640 . 8B86 C0000000 MOV EAX,DWORD PTR DS:[ESI+C0]
10F34646 . F680 C8000000 01 TEST BYTE PTR DS:[EAX+C8],1
10F3464D . 0F84 AE000000 JE 10F34701
10F34653 > E8 0854ABFF CALL 109E9A60 <-- and here
10F34658 . A8 04 TEST AL,4
10F3465A . 74 0C JE SHORT 10F34668
The reason I'm listing them is we'll look for this code in the
shipping version of Thief 3 :) Loading up T3Main_orig.exe (as I renamed it), I found the following:
Code:
109A52EF CC INT3
109A52F0 /$ A1 3454F310 MOV EAX,DWORD PTR DS:[10F35434] <-- DWORD being accessed
109A52F5 \. C3 RETN
109A52F6 CC INT3
109A52F7 CC INT3
109A52F8 CC INT3
109A52F9 CC INT3
109A52FA CC INT3
109A52FB CC INT3
109A52FC CC INT3
109A52FD CC INT3
109A52FE CC INT3
109A52FF CC INT3
109A5300 /$ 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] <-- our function
109A5304 |. A3 3454F310 MOV DWORD PTR DS:[10F35434],EAX
109A5309 \. C3 RETN
109A530A CC INT3
And calling 109A52F0 are:
Code:
Local calls from 10DD6BDB, 10DD6BFF, 10DD6C1B, 10DD6C37
Still 4 CALLs, so I guess
God was not removed from the release version, just unable to be toggled to various values in absence of a console :)
That's it for now.
BR,
Sun