Closed jackson2k2 closed 2 years ago
If you can, please upload the games exe's somewhere.
Executable's here, API dll too, if need is necessary. As for the quick response.... thanks.
Ah, a sample that has a TLS callback! Haven't had any of these thrown my way that I can recall. This is failing to unpack because of the TLS callback being replaced into the .bind section ahead of the actual entry point. Due to this, it fails to unpack because the current setup to get the stub header fails if a TLS callback is present.
When I have some free time I'll adjust the unpacker to work with this sample too. Thanks for this exe. :)
This is now fixed and the file should be unpacking properly in the latest release, thanks for the file submission! :) https://github.com/atom0s/Steamless/releases/latest
Unpacked files. They have no guarantee of working? Bind kept, I get "Application load error 3:0000065432". Without bind, nothing. No error, but no sign of life either.
Which one is also recommended for static analysis? Bind kept?
If the bind section is still there then either you had the option left on to keep it or it failed something. Make sure that you fully updated all your Steamless files and not just the exe.
Outside of that it may be something else with the TLS causing it to fail still.
Nothing failed, I was describing the problem I had with the "keep bind" option on and off. I only did the former because the captions said I should keep the bind when unpacking executables. All Steamless files were fully updated.
Ok looking at the unpacked file, it seems to have replaced the OEP after being unpacked with the TLS callback, so it seems the stub holds the TLS callback address as the OEP if its present.
I'll have to take another look when I get some more time to see if the actual OEP is present elsewhere in the header then to see where it may be storing that vs. using the TLS itself and see if that needs to be rebuilt too.
Edit - Another note to myself, appears the TLS will have to be rebuilt to some degree, at least the callback address as it is invalid after unpacking.
Have you made any progress since you began investigating?
Hey there, didn't forget about this, just super busy with some real life things at the moment so I haven't had much free time lately. Once things settle down I'll take another look at this to get the TLS stuff fixed.
i, rather suggest you to investigate it for yourself if u are impatient @kazblox or just wait till he fixes.
Sorry if I showed a false impression that I was being impatient. It wasn't evident to me at first, but asking for any status update when the author is busy gives out such a sign.
Once again, my condolences.
Not trying to be impatient, but still waiting... Take your time, if you must.
Seems to unpack now, but the unpacked exe does not launch at all
I finally had some free time to take a look at this again, and sadly I'm still at a bit of a loss on how this file is supposed to unpack correctly for now. (When I get more free time to fully debug the file, I can figure out the real way it's handling this setup.)
There are a few things going on with this file that causes it to unpack partially 'incorrect'.
Firstly, this file uses a TlsCallback to call the .bind stub. This is the first file I've seen do this. This also causes Steamless to not handle the Tls properly because I've not accounted for this happening. So this file has a weird setup where:
When unpacked, the file will be in an semi-invalid state. Since the TlsCallback is inside of the .bind section, its caller address gets left in the Tls section information but the pointer is invalid. However, along with that, the OriginalEntryPoint
address stored in the SteamStub header is also incorrect. This is actually the original TlsCallback address that is supposed to be put back into the unpacked files TlsCallback.
Because of this, the true OEP doesn't seem stored anywhere that I see without debugging. However, since the game does compile with Visual Studio, it's easy to find it manually.
To fix this file after unpacking, you need to do the following:
To restore the TlsCallback, open the unpacked file in CFF Explorer, go to TLS Directory
, copy the AddressOfCallbacks
value. Go to the Address Converter
page and enter that address you copied into the top VA entry.
In the hex window, change the first 8 bytes to: 60 96 00 40 01 00 00 00
To fix the files OEP, go to Optional Header
, and change AddressOfEntryPoint
to 00D34EBC
.
These are the values for the file given here and what appears to still be the latest version on Steam.
I do own this game so when I get more time to look I will try to find how the true OEP is actually handled in this, I will also need to make adjustments to Steamless to handle this kind of TLS usage.
Had some time tonight to debug this and figure it out now. The way this file works is very different than normal stub handling because of the usage of a TlsCallback. When a TlsCallback is present, the stub injection is performed differently to account for the need to deal with the original TlsCallback after the .text
is unpacked. Because of this, the stub is injected as a new TlsCallback itself instead. When this happens, the way the file is rebuilt is done differently to account for the different adjustments needed.
Here's a rundown of what is happening when a TlsCallback is present:
Unknown0003
is set to 1
to state a TlsCallback is present.AddressOfEntryPoint
is set to the new .bind stub unpacker address.OriginalEntryPoint
is set to the original TlsCallback function RVA.XorKey
is set to the unpacked files true original entry point address, xor'd with a special key. (See notes below.)When this setup happens, the .bind section stub that is used to do the unpacking/processing is turned into a TlsCallback and injected into PE header as the first callback. It must run before the original callbacks can run because their parent section may be encrypted at the time of trying to execute otherwise. The original entry point is overwritten with a new stub as well that is used to decrypt the true OEP address via an XOR key. (They use the headers XorKey
value to hold the 2nd half of the calculation when this happens.)
This is what the new OEP stub looks like that decrypts the real original OEP:
.bind:000000014209B350 ; void start()
.bind:000000014209B350 public start
.bind:000000014209B350 start proc near
.bind:000000014209B350
.bind:000000014209B350 var_8 = qword ptr -8
.bind:000000014209B350
.bind:000000014209B350 call $+5
.bind:000000014209B355 push rax
.bind:000000014209B356 push rdx
.bind:000000014209B357 mov rax, [rsp+18h+var_8]
.bind:000000014209B35C sub rax, 5
.bind:000000014209B362 mov rdx, rax
.bind:000000014209B365 sub rdx, 130h
.bind:000000014209B36C mov edx, [rdx]
.bind:000000014209B36E xor edx, 0B6AE239Dh
.bind:000000014209B374 movsxd rdx, edx
.bind:000000014209B377 add rax, rdx
.bind:000000014209B37A mov [rsp+18h+var_8], rax
.bind:000000014209B37F pop rdx
.bind:000000014209B380 pop rax
.bind:000000014209B381 retn
.bind:000000014209B381 start endp
What this is doing is:
14209B350
and store in RAX
.RAX
into RDX
.0x130
from RDX
and read the value at that address into EDX
. (This is reading the stub headers XorKey
value.)0x0B6AE239D
RAX
and return it as the OEP.In this case that would be this:
RAX
would equal 0x0014209B350
(call $+5
would move the stack 5 bytes forward, which is then sub'd back.)EDX
would equal 0x0004867B8F1
_(At the time of calling after the .bind section processed and the XorKey
is ready.)EDX
would equal 0xFEC99B6C
after being xor'd.RDX
would then equal 0xFFFFFFFFFEC99B6C
(The movsxd would cause it to become a negative.)RAX
would then equal 0x0014209B350 + 0xFFFFFFFFFEC99B6C
= 0x140D34EBC
which is the address of the real OEP.So then when this file is ran, the following happens:
pop -> ret
trick to jump to the given address, returning normal flow to the file and letting it run as expected.Now that I got this figured out I can adjust the unpacker variant for this file to work with this setup. I'll wait til samples of other variants are submitted or found before adding this setup to the others to ensure the logic is the same
This is now fixed in the latest code push. will be in the next update as well.
If you can, please upload the games exe's somewhere.
Taking advantage of the topic...
Hey man, what is the "Keep Bind Section" option for? I used the latest version of Steamless to remove protection from Final Fantasy VII and VIII games and did not check this option. In this case, I removed the protection from Launchers and "Game.exe" (if that's what I can call it). Can I have problems with these executables at some point because I didn't check the "Keep Bind Section" option?
Thanks in advance!
The option is there because some games actively look for the .bind
section to exist as another layer of 'protection' from being unpacked. (Grim Dawn for example does this.) Some titles also use a variant of SteamStub that moves the import section into the .bind
section, so it is (at this time) required to keep the section after unpacking for the file to still run properly.
The option is there because some games actively look for the
.bind
section to exist as another layer of 'protection' from being unpacked. (Grim Dawn for example does this.) Some titles also use a variant of SteamStub that moves the import section into the.bind
section, so it is (at this time) required to keep the section after unpacking for the file to still run properly.
Well, I ended up not checking the option "Keep Bind Section." But apparently FFs VII and VIII are working properly. Thanks for the clarification and attention! And thanks for the excellent work!
Cheers and Happy Holidays!
Detected as SteamStub 3.0, but unknown if there are any additional layers of protection such as Denuvo; which The Phantom Pain does have. "Failed to unpack file."
"Use Experimental Features" is no help, either.