M-HT / SR

A project to statically recompile following games to create Windows or Linux (x86 or arm) versions of the games - Albion, X-Com: UFO Defense (UFO: Enemy Unknown), X-Com: Terror from the Deep, Warcraft: Orcs & Humans, Septerra Core: Legacy of the Creator, Battle Isle 3: Shadow of the Emperor
304 stars 17 forks source link

How to make friends with external hooks? #67

Open Albeoris opened 1 month ago

Albeoris commented 1 month ago

Greetings! I am researching Septerra Core and developing modifications for it. Starting from loading resources from external files, ending with a turn-based combat system.

Unfortunately, there are many things that I currently do not do well, for example, I use dxwnd to launch the game in windowed mode and freeze the process in order to have time to hook all the necessary functions.

@M-HT, tell me please, how can I connect my modification with your recompiled version?

Now I'm injecting into the application, creating the .NET domain in it, and using Detours to hook native functions using static addresses found during the research, after which I call .NET hooks like this: https://github.com/Albeoris/Septerra/blob/master/Septerra.Injection/Hooks/Septerra_DbRecord_Read.hpp

As far as I understand, as a result of static recompilation, all addresses will be shifted and I will not be able to use the same addresses, right? What should I do to make my mod compatible with your .exe?

P.S. Please keep in mind that I am a C# developer, and Septerra Core is my biggest research experience, so if possible, explain in simple terms, like you would to... a C# developer. X"D

M-HT commented 1 month ago

In simple terms: I don't know. I'm not sure it's possible.

Also, some parts of the original executable have been rewritten in C. Your example is here.

Albeoris commented 1 month ago

@M-HT,

Option 1:

Can you create address mappings as a text file? Is it possible?

Like:

function (sub_445c80) 0x445c80 -> 0x544f90
section (.text) 0x845c80 -> 0x920c80

(If static data inside sections remains in the same places.)

Option 2:

Is it possible to apply your patches as hooks? In this case, I can transfer the logic that you change as a part of static compilation to my mod in the form of runtime hooks.

M-HT commented 1 month ago

Neither option is possible.

Albeoris commented 1 month ago

@M-HT, what are the chances that you will release new versions of the patch for Septerra Core? In theory, I could reverse the game again based on your recompilation.

Albeoris commented 1 month ago

I guess I can also try searching for functions by unique signatures...

M-HT commented 1 month ago

@M-HT, what are the chances that you will release new versions of the patch for Septerra Core? In theory, I could reverse the game again based on your recompilation.

Zero. Why would I work on a patch for the original executable when I already have a working recompiled executable ? And what would I even put in the patch ?

Albeoris commented 4 weeks ago

Sorry, I mean new version of SR for Septerra Core. :)

M-HT commented 4 weeks ago

Yes, eventually. I made some small fixes, I'm not sure if it's enough for a new release.

Albeoris commented 4 weeks ago

Gotcha! You can close this issue.

I guess, I'll try to replace static binding to dynamic function searching by unique signatures.

Albeoris commented 3 weeks ago

No chance. :( The code of these two .exe has nothing in common. :(

SR:

.text:00403040 ; void __cdecl sub_403040(int, int, int)
.text:00403040                 push    ebp
.text:00403041                 mov     ebp, esp
.text:00403043                 push    ebx
.text:00403044                 sub     esp, 14h
.text:00403047                 mov     ebx, [ebp+arg_0]
.text:0040304A                 mov     dword ptr [esp], offset _CriticalSection ; lpCriticalSection
.text:00403051                 shl     ebx, 4
.text:00403054                 add     ebx, ds:_RecordBuffer
.text:0040305A                 call    ds:__imp__EnterCriticalSection@4 ; EnterCriticalSection(x)
.text:00403060                 sub     esp, 4
.text:00403063                 cmp     dword ptr [ebx], 0FFFFFFFFh
.text:00403066                 jz      short loc_403089
.text:00403068                 mov     eax, [ebp+arg_4]
.text:0040306B                 mov     [ebx+8], eax
.text:0040306E                 mov     eax, [ebp+arg_8]
.text:00403071                 mov     [ebx+0Ch], eax
.text:00403074                 mov     dword ptr [esp], offset _CriticalSection ; lpCriticalSection
.text:0040307B                 call    ds:__imp__LeaveCriticalSection@4 ; LeaveCriticalSection(x)
.text:00403081                 mov     ebx, [ebp+var_4]
.text:00403084                 sub     esp, 4
.text:00403087                 leave
.text:00403088                 retn

Native:

.text:00445BE0     ; void __cdecl DB_SeekOpenedDbResource(int dbRecordNumber, int offset, int seekType)
.text:00445BE0
.text:00445BE0 000                 push    ebp
.text:00445BE1 004                 mov     ebp, esp
.text:00445BE3 004                 push    0FFFFFFFFh
.text:00445BE5 008                 push    offset stru_497298
.text:00445BEA 00C                 push    offset __except_handler3
.text:00445BEF 010                 mov     eax, large fs:0
.text:00445BF5 010                 push    eax
.text:00445BF6 014                 mov     large fs:0, esp
.text:00445BFD 014                 sub     esp, 0Ch
.text:00445C00 020                 push    ebx
.text:00445C01 024                 push    esi
.text:00445C02 028                 push    edi
.text:00445C03 02C                 mov     edi, [ebp+dbRecordNumber]
.text:00445C06 02C                 mov     esi, edi
.text:00445C08 02C                 shl     esi, 4
.text:00445C0B 02C                 add     esi, databaseRecordHandlers
.text:00445C11     ;   __try { // __finally(loc_445C70)
.text:00445C11 02C                 mov     [ebp+ms_exc.registration.TryLevel], 0
.text:00445C18 02C                 push    offset CriticalSection ; lpCriticalSection
.text:00445C1D 030                 call    ds:EnterCriticalSection
.text:00445C23 02C                 cmp     dword ptr [esi], 0FFFFFFFFh
.text:00445C26 02C                 jnz     short loc_445C47
.text:00445C28 02C                 push    edi
.text:00445C29 030                 push    offset aAttemptToSeekI ; "Attempt to seek in unopened RecordHandl"...
.text:00445C2E 034                 push    offset str_errorMessage ; Dest
.text:00445C33 038                 call    _sprintf
.text:00445C38 038                 push    65h ; 'e'
.text:00445C3A 03C                 push    offset str_errorMessage
.text:00445C3F 040                 call    ExitGameWithError2
.text:00445C44     ; ---------------------------------------------------------------------------
.text:00445C44 040                 add     esp, 14h
.text:00445C47
.text:00445C47     loc_445C47:                             ; CODE XREF: DB_SeekOpenedDbResource+46↑j
.text:00445C47 02C                 mov     eax, [ebp+offset]
.text:00445C4A 02C                 mov     [esi+8], eax
.text:00445C4D 02C                 mov     ecx, [ebp+seekType]
.text:00445C50 02C                 mov     [esi+0Ch], ecx
.text:00445C50     ;   } // starts at 445C11
.text:00445C53 02C                 mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFFh
.text:00445C5A 02C                 call    loc_445C70
.text:00445C5F     ; ---------------------------------------------------------------------------
.text:00445C5F
.text:00445C5F     loc_445C5F:                             ; CODE XREF: DB_SeekOpenedDbResource+9B↓j
.text:00445C5F 02C                 mov     ecx, [ebp+ms_exc.registration.Next]
.text:00445C62 02C                 mov     large fs:0, ecx
.text:00445C69 02C                 pop     edi
.text:00445C6A 028                 pop     esi
.text:00445C6B 024                 pop     ebx
.text:00445C6C 020                 mov     esp, ebp
.text:00445C6E 004                 pop     ebp
.text:00445C6F 000                 retn
Albeoris commented 3 weeks ago

Hmm... maybe I can export the function names and addresses from DWARF symbols and use them. This will allow me to find the necessary addresses in runtime, and not be tied to a specific version of SR.

Albeoris commented 3 weeks ago

image

Looks valid...

M-HT commented 3 weeks ago

The code of these two .exe has nothing in common. :(

Parts that were rewritten in C can look different and the implementation may be completely different from the original.