Allods Developers Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Modifying Clients

Hello hello,

This is my first post on these forums.

I've been playing around a while with the 1.0 and 4.0 allods server/clients.

1.0 (Oct 15 2009 leaked internal version)
This version is very interesting to me as it still has the oldest original class designs from the days of Nival and will definitely be useful to me, but problem is this version is extremely bugged: only killable mobs are in the starting instance and the shard logs constantly spit out errors about "too small roaming area" for the others as they regen hp constantly, so I can't even do a levelling run to 40 to refresh my memory about this once glorious game.

It is however very easily modifiable, I managed to add my own .xdb files and modify the index to unlock an elf psionicist race-class combination, it was missing some startup screen animation and description in one place, but it was definitely playable (at least as playable as existing race-class-combinations are in this version). Then seeing as I won't progress past starting island due to bugs, I moved on to exploring 4.0.

4.0 (with AllodsME client)
This one was much more playable, however it is nearly useless to me, I actually remember this version well-enough.

I've noticed (and read some online about it) that all the resources (that are unpacked and freely-modifiable in 1.0) are in binary format in 4.0 and they need to be unpacked (first from .pak files, then from .bin inside the .pak) to a modifiable form (.xdb) and then (probably) re-packed again after modification to use in the client. Are there tools freely available already to do that or do they need to be developed with use of reverse-engineering? I've seen people talk about "pack.bin" here on the forums, is it related to that problem?

I've noticed some serious obstacles with the 4.0 client:

It has some sort of protection that prevents it from running if anything is changed. If I do one of smallest possible changes: modify account_srv_host field in Profiles/accountServer.cfg file to point to my local server on 127.0.0.1 instead of "Allods-account.gpotato.com", it looks like the client never starts, running the .exe just does nothing. I had to actually do a hack to add an entry that binds Allods-account.gpotato.com to 1270.0.1 on my machine to even connect to my server using this client.

I guess the client probably does some sort of checksum on the files in the directory where it runs and exits if it doesn't match (the process is gone from the process list very fast).

I've seen some weird solutions like writing your server ip inside the binary with in HEX or running a starter.exe and injecting some custom dll... they do solve the problem of running the client (but not modifying it) in a very crude and convoluted way. I think the proper and clean way to do it would be to binary patch the AOGame.exe itself so it never even runs the old IE-based launcher and just straight up boots into the game whether anything was modified or not, then reads the server IP from accountServer.cfg (like vanilla World of Warcraft client does reading realmlist.wtf)

Has anyone solved those problems cleanly like that already? Would I be wasting time reinventing the wheel trying to run the game with x64dbg trying to figure this out?

I'd love to backport old game and class mechanics (especially talent and ruby grids) from the internal 1.0 version into the 4.0 version and test them out by doing some levelling runs from 1 to 40, but in order to do that I need to be able to modify the 4.0 client.

Also, while trying to find an unmodified 4.0 client, I've managed to find an old polish version (the link is shockingly still alive after 12 years):

You do not have permission to view link Log in or register now.


Might be of use to someone here, it is a clean, original installer. Still has all those "protection" issues tho.
 
in the client there is a file game.version, which contains the files and their hash sums. At startup, the client checks them and exits if something doesn't match.

long ago this problem has been solved by injecting asm, which skips the verification stage, there is even a launcher with a dll that can do this.

There is also an alternative solution, you can rebuild game.version every time you change game files, but it is not so easy there too, because we do not have a private key, which is used to sign the hash sum, we have to use our own and substitute the public key in aogame.exe (you can do it in the original launcher too, because it also checks files from game.version)
 
in the client there is a file game.version, which contains the files and their hash sums. At startup, the client checks them and exits if something doesn't match.

long ago this problem has been solved by injecting asm, which skips the verification stage, there is even a launcher with a dll that can do this.

There is also an alternative solution, you can rebuild game.version every time you change game files, but it is not so easy there too, because we do not have a private key, which is used to sign the hash sum, we have to use our own and substitute the public key in aogame.exe (you can do it in the original launcher too, because it also checks files from game.version)
Thanks for a very clear answer.

If I were to use an own private key, is it documented somewhere which part (address) inside AOgame.exe needs to be patched?
 
I don't think anyone else has done this but me, so you won't find any documentation

The client uses in-memory decompression, which makes it difficult to modify aogame.exe, unlike Launcher.exe, which can be easily patched.
If you can do this, the whole process can be simplified a lot.

Directly overwriting the key in the process memory will not work either, I resorted to such a hack:
1. auto inject dll to process
2. hook CreateFileA and wait until the client tries to read ../Profiles/game.version
3. replace the public key address with your own.
4. wait until the client tries to read any other file and change the pointer back.

DWORD ptr = 0x0047c749;
char newKey[] = {...}; // len = 140

replace key:
DWORD OldProt;
VirtualProtect((LPVOID)ptr, 5, PAGE_EXECUTE_READWRITE, &OldProt);
*(char*)ptr = (char)0x68;
*(DWORD*)((DWORD)ptr + 1) = (DWORD)&newKey;
VirtualProtect((LPVOID)ptr, 5, OldProt, &OldProt);

restore as it was:
DWORD OldProt;
VirtualProtect((LPVOID)ptr, 5, PAGE_EXECUTE_READWRITE, &OldProt);
*(char*)ptr = (char)0x68;
*(char*)((DWORD)ptr + 1) = (char)0xE8;
*(char*)((DWORD)ptr + 2) = (char)0x2A;
*(char*)((DWORD)ptr + 3) = (char)0x32;
*(char*)((DWORD)ptr + 4) = (char)0x01;
VirtualProtect((LPVOID)ptr, 5, OldProt, &OldProt);

If you really need it, I can write a guide on how to work with version 5 of game.version
 
Thanks for a detailed example, but I think I won't be going this route then, in-memory decompression complicates everything too much.

I thought it can be done cleanly and without adding extra dlls or executables, by just binary-patching AOgame.exe and bypassing launcher, but if the data inside the .exe file is actually compressed that's the reason it hasn't been done yet.

Is the launcher that bypasses game.version check available for 4.0 version? I am guessing it's not the starter scripts supplied with AllodsME client, because they don't allow modifying accountServer.cfg
 
1.0 (Oct 15 2009 leaked internal version)
This version is very interesting to me as it still has the oldest original class designs from the days of Nival and will definitely be useful to me, but problem is this version is extremely bugged: only killable mobs are in the starting instance and the shard logs constantly spit out errors about "too small roaming area" for the others as they regen hp constantly, so I can't even do a levelling run to 40 to refresh my memory about this once glorious game.

It is however very easily modifiable, I managed to add my own .xdb files and modify the index to unlock an elf psionicist race-class combination, it was missing some startup screen animation and description in one place, but it was definitely playable (at least as playable as existing race-class-combinations are in this version). Then seeing as I won't progress past starting island due to bugs, I moved on to exploring 4.0.
Hi! Can you share 1.0 version server files? All old links is dead now =(
 
I don't think anyone else has done this but me, so you won't find any documentation

The client uses in-memory decompression, which makes it difficult to modify aogame.exe, unlike Launcher.exe, which can be easily patched.
If you can do this, the whole process can be simplified a lot.

Directly overwriting the key in the process memory will not work either, I resorted to such a hack:
1. auto inject dll to process
2. hook CreateFileA and wait until the client tries to read ../Profiles/game.version
3. replace the public key address with your own.
4. wait until the client tries to read any other file and change the pointer back.

DWORD ptr = 0x0047c749;
char newKey[] = {...}; // len = 140

replace key:
DWORD OldProt;
VirtualProtect((LPVOID)ptr, 5, PAGE_EXECUTE_READWRITE, &OldProt);
*(char*)ptr = (char)0x68;
*(DWORD*)((DWORD)ptr + 1) = (DWORD)&newKey;
VirtualProtect((LPVOID)ptr, 5, OldProt, &OldProt);

restore as it was:
DWORD OldProt;
VirtualProtect((LPVOID)ptr, 5, PAGE_EXECUTE_READWRITE, &OldProt);
*(char*)ptr = (char)0x68;
*(char*)((DWORD)ptr + 1) = (char)0xE8;
*(char*)((DWORD)ptr + 2) = (char)0x2A;
*(char*)((DWORD)ptr + 3) = (char)0x32;
*(char*)((DWORD)ptr + 4) = (char)0x01;
VirtualProtect((LPVOID)ptr, 5, OldProt, &OldProt);

If you really need it, I can write a guide on how to work with version 5 of game.version
I am ressurrecting this post because I stumbled into something that might be connected and relevant...

I have been trying to troubleshoot a problem that happens when running 7.0 client on Linux using wine and connecting to my own server on a different machine on the same local network.

The problem is when I log into the server, select a character and login screen finishes, after 1-3 seconds of gameplay, the client is being kicked from the server with following entries in the server's log at about the same time:

Code:
[11-09 17:16:54,891, 1232603] net_worker_5 - INFO  frontEnd.connectClient.ClientsListener  : Client connection closed: name "playerone"; socket /192.168.0.10:49450 -> /192.168.0.50:9324; closed; reason: End-of-stream; null
[11-09 17:16:54,899, 1232611] fe2(69) - INFO  frontEnd.FrontEnd  : playerone: client lost. connection : (name "playerone"; socket /192.168.0.10:49450 -> /192.168.0.50:9324; closed), reason: (request from net: End-of-stream, no exception). Total user count on frontEnd: 0
[11-09 17:17:13,101, 1250813] Thread-51 - INFO  billingserver.connector.BillingServerConnectorImpl  : Connected to biling server: 0.0.0.0/9322
[11-09 17:17:13,104, 1250816] net_worker_1 - INFO  frontEnd.NewClientInfo  : Client rejected unknown (87) reason (WRONGVERSION) Wrong version (client = 0; server = 9970)
[11-09 17:17:13,107, 1250819] net_worker_5 - INFO  billingserver.connector.BillingServerConnectorImpl  : Disconnected from billing server: connection lost, exception java.io.IOException: negative message size: -224002044
[11-09 17:17:13,107, 1250819] net_worker_5 - ERROR verify  : negative message size: -224002044
java.lang.Throwable: java.io.IOException: negative message size: -224002044

I've tried using google's AI mode with Ghidra to troubleshoot this and what I found out is that the problem is with AllodsV7.dll injected dll.

The problem does not happen every time, it looks very random and the randomness is "locked" per client launch.

Either the value is being injected successfully and I can log in and out any number of times without the server kicking me as long as I don't restart the client or it fails and every login will result in the kick.

What I mean by this is whether the client will be kicked or not is determined at the point of AllodsV7 injecting something (probably a version, but I don't know that for sure) into game's memory.

I found out that it uses some sort of "hardware breakpoints" to freeze the game's threads before injecting that value and it is a timing-based process, that timing is very consistent on Windows and the problem never happens there, but on Linux/wine there's some sort of race condition with how it's implemented.

I would like to know if this Client rejected unknown (87) reason (WRONGVERSION) Wrong version (client = 0; server = 9970) is related to game.version in any way.

Is the game.version responsible for what the client sends to the server in this network packet? If yes, how difficult would it be to completely reverse-engineer game.version format, document it and write utilities that allow to version your own game client (and server)?

I think think it might be very worth it to look into building toolset like this and stop using hacks like AllodsV7.dll if it is no longer about cleanliness but about stability.
 
Back
Top