Hello.
At the beginning I wanted to ask a few questions how to approach the topic of DLL injection and hooking into functions in memory, but to be fair to this community, I am sharing with you a very very simple code that allows you to control your character using WSAD keys.
The principle of operation is that in the compilation process we get the ddraw.dll file that we put next to the Tibia.exe client (memory addresses correspond only to the client 8.60, on the others it will not work due to other addresses). Thanks to this, the client that loads the ddraw.dll system library by default, encounters this file in the same directory and it get priority in loading. Then, in the input function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L105)) the system library is loaded, and then the function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L127)) that the Tibia client needs. The graphic below shows why the choice was ddraw.dll - the client imports only one function from it (although it would probably be the same with the glu32.dll or winmm.dll libraries).
Next, I found the addresses in the client's memory responsible for sending the letter (0x458200), the place where sending the letter is called (0x4CFB8A) and where the CreateWindowExA function from the user32.dll library (0x5B8574) is called.
1. Hook at rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L115), then https://github.com/rookgaard/tibia-wsad/blob/master/dllmain. cpp#L58 is responsible for intercepting user actions. This is where I get information about which button was pressed (W/S/A/D) and then I change it to an arrow key (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L21-L41)).
2. Hook at rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L112) is needed, because despite the user action being intercepted, the letter is sent to the text field anyway, so I can block it on rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L69-L83)
3. Information about the address for sending the letter in rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L111) is needed to write the letter in rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad) /blob/master/dllmain.cpp#L85 if the letter is not W/S/A/D.
The whole thing is additionally checked so that the action takes place only when the character is logged in (i.e. that when entering a password that contains, for example, the letter A, it is not changed to a left arrow) - rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L9-L11)
The code currently works on the principle that if there is a file next to the client, it is not possible to type these letters because they are converted into arrows - but this is not a problem, because you can easily add an additional flag/condition (e.g. activated with another key like ESC) which will control whether WSAD is active or not (however, this is not the topic I wanted to ask about, hence the lack of this code in the project).
What I don't like, however:
1. Mimicking the ddraw.dll system library
In the Erpegia client (formerly Fania OTS for 8.60) which I found somewhere in the internet (credits for @mateuszx), loading the erpegia.dll library was done by editing .exe files, where the application at some point "jumps" to another memory location:
on the left the original client with bytes 6A606858A95D00, on the right a modified client with bytes E9FA570500
then loads the library into a free space in the code, and then recalls previously overwritten bytes:
on the left the original client with zeros, on the right, a modified client with the name of the file to be loaded, where you can see the same overwritten bytes 6A606858A95D00
Thanks to this, in the InitMain function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L101)) you can completely skip loading the system library and its functions - unfortunately it requires modification and sending the appropriate client to the users.
2. The way I capture functions
For one address I used the HookCall function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L88)), while in the second I had to use a different method - rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L114-L119) - where the common part is to reserve memory and release it for modification.
I am not a C ++ programmer myself and I feel very weak about this topic, so I do not understand why brewsterl/tibianic-dll (https://github.com/brewsterl/tibianic-dll/blob/master/main.cpp#L1911) uses two different solutions.
Could any of the subject matter experts help me evaluate/review this code? Advise what solutions are okay, which should be changed, and which are completely unacceptable? How else (apart from impersonating ddraw.dll and the mentioned client modification) can I inject dll on the user's side so that he can use WSAD?
P.S. In the repository there is a .dll file, so everyone can test it on it's own - just place it near Tibia client and press WSAD while logged in.
Sincerely,
Michał "Gubihe"
At the beginning I wanted to ask a few questions how to approach the topic of DLL injection and hooking into functions in memory, but to be fair to this community, I am sharing with you a very very simple code that allows you to control your character using WSAD keys.
GitHub - rookgaard/tibia-wsad
Contribute to rookgaard/tibia-wsad development by creating an account on GitHub.
github.com
The principle of operation is that in the compilation process we get the ddraw.dll file that we put next to the Tibia.exe client (memory addresses correspond only to the client 8.60, on the others it will not work due to other addresses). Thanks to this, the client that loads the ddraw.dll system library by default, encounters this file in the same directory and it get priority in loading. Then, in the input function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L105)) the system library is loaded, and then the function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L127)) that the Tibia client needs. The graphic below shows why the choice was ddraw.dll - the client imports only one function from it (although it would probably be the same with the glu32.dll or winmm.dll libraries).
Next, I found the addresses in the client's memory responsible for sending the letter (0x458200), the place where sending the letter is called (0x4CFB8A) and where the CreateWindowExA function from the user32.dll library (0x5B8574) is called.
1. Hook at rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L115), then https://github.com/rookgaard/tibia-wsad/blob/master/dllmain. cpp#L58 is responsible for intercepting user actions. This is where I get information about which button was pressed (W/S/A/D) and then I change it to an arrow key (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L21-L41)).
2. Hook at rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L112) is needed, because despite the user action being intercepted, the letter is sent to the text field anyway, so I can block it on rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L69-L83)
3. Information about the address for sending the letter in rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L111) is needed to write the letter in rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad) /blob/master/dllmain.cpp#L85 if the letter is not W/S/A/D.
The whole thing is additionally checked so that the action takes place only when the character is logged in (i.e. that when entering a password that contains, for example, the letter A, it is not changed to a left arrow) - rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L9-L11)
The code currently works on the principle that if there is a file next to the client, it is not possible to type these letters because they are converted into arrows - but this is not a problem, because you can easily add an additional flag/condition (e.g. activated with another key like ESC) which will control whether WSAD is active or not (however, this is not the topic I wanted to ask about, hence the lack of this code in the project).
What I don't like, however:
1. Mimicking the ddraw.dll system library
In the Erpegia client (formerly Fania OTS for 8.60) which I found somewhere in the internet (credits for @mateuszx), loading the erpegia.dll library was done by editing .exe files, where the application at some point "jumps" to another memory location:
on the left the original client with bytes 6A606858A95D00, on the right a modified client with bytes E9FA570500
then loads the library into a free space in the code, and then recalls previously overwritten bytes:
on the left the original client with zeros, on the right, a modified client with the name of the file to be loaded, where you can see the same overwritten bytes 6A606858A95D00
Thanks to this, in the InitMain function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L101)) you can completely skip loading the system library and its functions - unfortunately it requires modification and sending the appropriate client to the users.
2. The way I capture functions
For one address I used the HookCall function (rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L88)), while in the second I had to use a different method - rookgaard/tibia-wsad (https://github.com/rookgaard/tibia-wsad/blob/master/dllmain.cpp#L114-L119) - where the common part is to reserve memory and release it for modification.
I am not a C ++ programmer myself and I feel very weak about this topic, so I do not understand why brewsterl/tibianic-dll (https://github.com/brewsterl/tibianic-dll/blob/master/main.cpp#L1911) uses two different solutions.
Could any of the subject matter experts help me evaluate/review this code? Advise what solutions are okay, which should be changed, and which are completely unacceptable? How else (apart from impersonating ddraw.dll and the mentioned client modification) can I inject dll on the user's side so that he can use WSAD?
P.S. In the repository there is a .dll file, so everyone can test it on it's own - just place it near Tibia client and press WSAD while logged in.
Sincerely,
Michał "Gubihe"