Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ground Zeroes fails to unpack #20

Closed
jackson2k2 opened this issue May 28, 2019 · 20 comments
Closed

Ground Zeroes fails to unpack #20

jackson2k2 opened this issue May 28, 2019 · 20 comments
Assignees
Labels
c: plugin Category: Plugin p: medium Priority: Medium s: fixed Status: Fixed t: bug Type: Bug

Comments

@jackson2k2
Copy link

jackson2k2 commented May 28, 2019

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.

@atom0s
Copy link
Owner

atom0s commented May 28, 2019

If you can, please upload the games exe's somewhere.

@jackson2k2
Copy link
Author

@jackson2k2
Copy link
Author

Executable's here, API dll too, if need is necessary. As for the quick response.... thanks.

@atom0s
Copy link
Owner

atom0s commented May 29, 2019

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. :)

@atom0s atom0s self-assigned this May 29, 2019
@atom0s
Copy link
Owner

atom0s commented May 29, 2019

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

@atom0s atom0s closed this as completed May 29, 2019
@jackson2k2
Copy link
Author

jackson2k2 commented May 29, 2019

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?

@atom0s
Copy link
Owner

atom0s commented May 29, 2019

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.

@jackson2k2
Copy link
Author

jackson2k2 commented May 29, 2019

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.

@atom0s
Copy link
Owner

atom0s commented May 29, 2019

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.

@atom0s atom0s reopened this May 29, 2019
@jackson2k2
Copy link
Author

jackson2k2 commented Jun 6, 2019

Have you made any progress since you began investigating?

@atom0s
Copy link
Owner

atom0s commented Jun 7, 2019

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.

@jackson2k2
Copy link
Author

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.

@jackson2k2
Copy link
Author

jackson2k2 commented Aug 3, 2019

Not trying to be impatient, but still waiting... Take your time, if you must.

@lagseeing
Copy link

Seems to unpack now, but the unpacked exe does not launch at all

@atom0s atom0s added c: plugin Category: Plugin p: medium Priority: Medium t: bug Type: Bug and removed bug labels Mar 22, 2022
@atom0s
Copy link
Owner

atom0s commented Mar 24, 2022

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:

  • The TlsCallback is responsible for calling the unpacker stub.
  • The packed files current OEP looks like a dead stub that isn't really doing anything. (Without debugging for now I can't say that for sure though as it does xor a value and return it.)

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:

  • Restore the original TlsCallback address properly.
  • Restore the true OEP address.

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.

@atom0s
Copy link
Owner

atom0s commented Mar 25, 2022

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:

  • .bind header: Unknown0003 is set to 1 to state a TlsCallback is present.
  • .bind header: AddressOfEntryPoint is set to the new .bind stub unpacker address.
  • .bind header: OriginalEntryPoint is set to the original TlsCallback function RVA.
  • .bind header: XorKey is set to the unpacked files true original entry point address, xor'd with a special key. (See notes below.)
  • PE Header: The first TlsCallback entry is overwritten with the new .bind unpacker stub address. (It is turned into a TLS callback.)
  • PE Header: The packed file entry point is set to a new injected stub that is used to calculate the original real OEP. (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:

  • Take the value 14209B350 and store in RAX.
  • Copy RAX into RDX.
  • Subtract 0x130 from RDX and read the value at that address into EDX. (This is reading the stub headers XorKey value.)
  • Xor that value with 0x0B6AE239D
  • Add that value to 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:

  • The new .bind stub TlsCallback is executed first.
  • The old TlsCallback is invoked afterward as the DRM header is returning that address as the original OEP.
  • The file is 'loaded' at this point and the PE header set OEP is executed, which is the above stub.
  • The true OEP is executed because the stub above is using the 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

@atom0s atom0s closed this as completed in b97f148 Mar 25, 2022
@atom0s
Copy link
Owner

atom0s commented Mar 25, 2022

This is now fixed in the latest code push. will be in the next update as well.

@atom0s atom0s added the s: fixed Status: Fixed label Mar 25, 2022
@SilentMRG
Copy link

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!

@atom0s
Copy link
Owner

atom0s commented Dec 19, 2022

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.

@SilentMRG
Copy link

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: plugin Category: Plugin p: medium Priority: Medium s: fixed Status: Fixed t: bug Type: Bug
Projects
None yet
Development

No branches or pull requests

5 participants
@atom0s @lagseeing @jackson2k2 @SilentMRG and others