-
Notifications
You must be signed in to change notification settings - Fork 5
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
Help with a new GUI Element Hook #9
Comments
Sorry for my late answer. I planned in writing a proper tutorial but looks like I don't have enough time doing that so I will give you a few hints now. First, have a look at this commit. It shows all the necessary changes that are required for adding a new Hook. The good thing is, that the instruction you want to hook is already 5 bytes large. So you can just replace the instruction with a CallPatch. You can see example code here at these lines: The __hook function is a __declspec(naked) function that has no calling convention. However I can see from the code you showed that it is a __fastcall function with 5 arguments. I usually add naked functions as they allow for easy stack hacking. However in your case it is probably easier to just not create a naked function and implement it like that:
Just create the OnDraw function:
You can name the arguments properly as you seem to already found out what they do.
Previously I added game structs to s4.h or directly to the .cpp File of the hook code. You can choose where you want to put it. Don't forget to put #pragma pack(push, 1) before the definition of the struct and add a #pragma pack(pop) after it. Please do not add game structs to S4ModApi.h, since that will make it more difficult for people importing the DLL from non-C contexts (eg C#). Especially if you require #pragma pack. That's the reason I always do the unpacking of game structs:
I think I added a READ_AT function somewhere. You can use it to safely read arbitrary memory addresses. If you want to read more than 4 Bytes (eg a string), have a look at memget_s and memset_s. See here for details. These functions will not crash the game if you attempt to write/read at an invalid address. So [S4_Main.exe+0xE94814]+0xC98 would probably be implemented as:
I added these kind of functions to s4.h. It is probably the best place for now. However, you cannot really make that wrong. Its up to you, where you want to place/implement it :) |
The game uses the ebx pointer from before the function call - how do I preserve that register? |
From your excerpt i think that ebx is already pushed onto the stack. So it should be in your argument list. (The last argument)
You do not have to preserve that register. __fastcall functions (by specification) are only allowed to change the eax, ecx and edx registers. All others are untouched or automatically preserved (the compiler does that for you when you decorate a function with __fastcall). It gets tricky when you create a hook at a non-function call (replacing ordinary opcodes). Then we would also have to preserve xmm/fpu registers. But that's not needed here since you set your hook at an already existing function call. This is also the reason you do not have to preserve the eax, ecx and edx registers. Because the previous function (that you overwrite) is also allowed to change these registers. Edit: Here are some more details on the spec. There you can also find a sentence about the register preservation:
|
Yeah, you're right, but I run into the problem, that the game seems to deadlock when my patch is applied. It has something to do with my function, because if I nop the original call, the game runs fine (just without the gui elements) The hook gets correctly called; all values seem fine (The struct is filled with the correct values) but it just seems like somethings wrong. It could be the stack pointer by the looks of it Everything runs fine if I manually change the ESP to the previous value - but that seems like the incorrect way of doing it |
hmm, thats 3 dwords you are off. I think we cannot use __fastcall because the game does expect the caller to clean the stack - but we do it in the callee. The caller must clean the stack.
There is no build-in calling convention that allows us to easily do that (there would be cdecl, but that would not pass ecx and edx as arguments). We need a naked function and manually call your hook procedure. The calling convention the game uses here is a "compiler invented convention". Meaning the compiler invented it on the fly for optimization purposes. Unfortunately this means, that we cannot make assumptions about xmm / fpu registers too. It is probably best to preserve them too. The rules for the compiler invented convention appear to be as follows:
I suggest changing the calling convention of your hook procedure to stdcall. This is a little easier to call directly from assembler. Note the changed return value that is used to control whether to call the original function. Do not call the original function from within OnDraw (you actually could do this using an __asm block and preparing, executing and cleaning the call yourself within the OnDraw function, but I prefer doing this in a naked function).
Then you create a naked function like this:
Make sure to let the call patch call your naked function:
I hope i did not miscount any of the Edit: If you happen to find documentation about the "compiler invented calling conventions" feel free to share it with me. Maybe there is a decorator that allows us to specify functions with custom calling conventions. Then we would not need many of the naked functions. |
I want to hook a new function, that draws the GUI Elements (as defined in the Menu/GUISetXXX.DAT files)
But I can't quite figure out how to implement a hook in the style of the S4ModApi. Could you help me with that?
[The game does not render any GUI Elements when nop'ing that function call: ]
For reference, ebx points to the GUI Element
A GUI Element looks like this:
And what would be the easiest way to get the value of a static address?
The current .gfx file of the GUI Engine is located at
[S4_Main.exe+0xE94814]+0xC98
- how should I retrieve that value?The text was updated successfully, but these errors were encountered: