📜 ⬆️ ⬇️

Mini-study, improvement and bot for the game Shadow Worlds

Yellow title: explore the MMORPG, add cheat features and write an elusive bot.
Picture to attract


What is this game and what binds me to it


I came across Shadow Worlds about 10 years ago, and it completely captured me. We played it with friends in a terrible modem connection, drenched rats and zombies, ran away from the orcs, opened the chests, rejoiced at the victories over the bosses and shouted “awesome game!” When they died from the disconnect.

What is so special about her is difficult to explain. Even for those times, it had rather primitive graphics, almost no variety, but something attracted us to it. Maybe it's a duckling syndrome .
')
Since then a lot of water has flowed under the bridge, but from time to time I downloaded the game client, created a new account and played until I got bored, and then deleted all traces of the game from the screw. Every time I was convinced that nothing has changed in it over the years - the same locations, the same monsters, the same abuse in the global chat, nothing new, except that the balance is twisted back and forth.

A month ago, raking tons of spam in an old mailbox, I found a letter from the SW Team. It said that recently launched a new server with new maps, items and other amenities. In my brain, the trigger worked, which is another time to plunge into nostalgia.
Register, download client, and here I am in the game.

The first thing that sees every new player.


You can again kill mice in the dungeons, zombies in the cemetery, collect money, buy a fishing net ... everything is as before, in general.

Feeling young again, playing enough, towards Sunday evening, I was curious: how does the game communicate with the server? Indeed, judging by the primitiveness of the interface (which I will mention later) and the lack of progress over many years, one can easily expect simplicity in the protocol used. I started Wireshark, and I saw in the logs porridge from bytes, nothing meaningful. Okay, then we will look from the inside.

I open MUDClient.exe, the main exe-Schnick of the game in IDA, DeDe and OllyDBG. In IDA, in order to feel cool, in Olly, because it is better not to find a debugger, in DeDe, because the game is written in Borland C ++ Builder.
Everything goes with a bang, the exe is not packed and is not protected from above by any protector.

In DeDe, we see the curious names of the procedures, this gives us confidence that it will be possible to get a lot of useful information in a hurry (no protection).

Olly has a small problem: the fact is that the game checks the update every time it starts, downloads it and restarts it.


Thus, by running MUDClient.exe in the debugger, it ends immediately, and run.exe starts instead, and even with the administrator privilege request, and I have to enter the admin password. We understand.

Open OllyDbg run.exe and set a breakpoint on CreateProcessW. We get this:

The magic parameter is E8DA81A3FFE9B1. We won’t find out if it is some kind of checksum or just a secret, but take and create a bat file
start MUDClient.exe E8DA81A3FFE9B1 

The game starts, the admin password is not required, is not trying to update. Victory

Now we can specify the command line argument E8DA81A3FFE9B1 in Olly, the game will think that checking for updates is not required, and we can safely begin debugging. And when you just want to play, it is more convenient to run this bat-file. Worry about the fact that we will miss an important update is not worth it: still there are no fresh ehe-shnikov in them, the game is updated about once a week (the feature of this server), but the update contains only new files of game locations.

What's the plan? And let's hunt for those who hunt for players, that is, the administrators, who in the game are called Game Master'ami ( GM ). Their task is to track the violators of the rules, especially AFK + Macros, that is, when a player launches a simple program that digs ore for him or fishes, while he goes on business. This is prohibited by the rules.

I myself have never violated this rule (honestly), and if I saw something like this, I tried to bring zombies to such a player, and then cleaned the corpse. It's fun. Why should I catch GMs? And then, that the test is pretty easy to blink if you are at work, playing on the second monitor, and you accidentally blocked it with another window, while the players actively troll each other in the general chat. And if now and then to be distracted by the game, then it does not work. It means that we need to make an alarm: as soon as they try to check us, send some kind of signal, for example, blow up the game window and blink with it, play a melody, send SMS.

How is the test? The administrator teleports to you, while he looks like a regular player of zero level, only his guild is GAME MASTERS, and asks a simple question "here?" Or "check for afk". If there is no answer within a couple of minutes, and the character continues to level the skill, then it is not there at the keyboard and he goes to the ban.

To the debugger! We enter the game as a character of Imthebot, then we are looking for a living soul for a long time - at this particular moment in the online game there are only 19 people (not thousands and not millions), which is not so little, sometimes less. As a result, we stumble upon some kind of Bank.

Bank and does not suspect that it is special


Quickly grab the debugger, until Bank escaped, and search for the string “Bank” by the process memory. And we find.


Next to the name are still interesting tsiferki, and just above we see our nickname surrounded by the same tsiferok. Morale increases, curiosity and desire to dig grows.

But there is a chagrin: pretty soon garbage appears in the place of this data block, and the “Bank” line is already detected at a number of other addresses. That is, we did not find a static address, which would be convenient, but simply a phantom, a product of vital functions of some procedures. So we will look for the source, where this data is formed.

We set bryak on the recv function in the WSOCK32.dll module. Many operations occur, for example:

In the buffer that recv fills, so far something meaningless, as in the results of Wireshark logging. We go up the call stack above and see that as a result of processing by one of the calls, a human-readable string is formed in the memory


Pity the Bank went away on business


If we continue tracing, we will be in the kernel of the system. The fact is that the call to recv is made in the stream, whose task is to form a string in memory, and then it will take it and process the main stream of the game. You can put the hardware breakpoint on the generated string and end up in the main thread handler.

For some time, while sitting at work, I was amused by logging incoming messages to the file using OllyDbg. This is done by setting a conditional breakpoint (Shift + F4)


As a result, I have formed giant data wrappers.
An example of a footcloth when a Zloy player passed
0042A8CC New thread with ID 00000418 created
0044A435 COND: 0: 448: P222: Imthebot: 1014: 46: 133: 1000: 1000: 0: 0: 0: 0: 0: 0:; G0000000
Thread 00000418 terminated, exit code 4BDFF7C (79560572.)
0042A8CC New thread with ID 000007F0 created
0044A435 COND: 0: 448: P212: Imthebot: 1014: 47: 133: 1000: 1000: 0: 0: 0: 0: 0: 0:; G0000000
Thread 000007F0 terminated, exit code 4BDFF7C (79560572.)
0042A8CC New thread with ID 00000288 created
0044A435 COND: 0: 493: P322: Imthebot: 1014: 48: 134: 1000: 1000: 0: 0: 0: 0: 0: 0:; P610: Zloy : 1009: 56: 138: 1000: 1000: 0: 0: 0: 0: 0: 0:; G0000000000
Thread 00000288 terminated, exit code 4BDFF7C (79560572.)
0042A8CC New thread with ID 000009B0 created
0044A435 COND: ???
Thread 000009B0 terminated, exit code 4FFFF7C (83885948.)
0042A8CC New thread with ID 000008E0 created
0044A435 COND: 0: 493: P222: Imthebot: 1014: 50: 134: 1000: 1000: 0: 0: 0: 0: 0: 0:; P620: Zloy : 1009: 55: 138: 1000: 1000: 0: 0: 0: 0: 0: 0:; G0000000000
Thread 000008E0 terminated, exit code 4FFFF7C (83885948.)
0042A8CC New thread with ID 000007A0 created
0044A435 COND: Ok

0044A435 COND: 0: 493: P202: Imthebot: 1014: 50: 134: 1000: 1000: 0: 0: 0: 0: 0: 0:; P710: Zloy : 1009: 54: 137: 1000: 1000: 0: 0: 0: 0: 0: 0:; G0000000000
Thread 000007A0 terminated, exit code 4FFFF7C (83885948.)
0042A8CC New thread with ID 00000C3C created
0044A435 COND: ???
Thread 00000C3C terminated, exit code 51FFF7C (85983100.)
0044A435 COND: Ok



And suddenly, a message from a GM user with the text “GM-GM - check for AFK // GM - check for AFK” comes to my personal chat. At the same time there was nobody near me. I already jumped in surprise. After welcoming the mysterious GM, I climbed onto the forum and discovered the latest topic, which referred to the reform of GM-checks. In short: now GM doesn’t fly to you, but introduces a special spell, as a result of which he begins to see the character, but doesn’t know who is in front of him - not a nickname, level or even appearance can be determined. Only the actions that you take and the opponents are visible (which are replaced by mice for greater anonymity). This is how the problem of dishonest game masters was solved.

It was an additional challenge: not only did you have to keep track of the players next to you, so also personal chat. A question from GM, who flew in to me, got into the log, here it is:
0044A435 COND: 48:457:01GM-GM - check for AFK // - ;I41:46:35;P002:Imthebot:1001:53:35:1000:1000:0:0:0:0:0:0:;G00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000


It's time to write a patch. What is required of him? It should transfer the string that arrives from the server to my external application, which can figure out what to do with it. For this I will use the sending of the WM_COPYDATA message to my window.

Quick googling gave this code:

 var aCopyData: TCopyDataStruct; hTargetWnd: HWND; begin with aCopyData do begin dwData := 0; cbData := StrLen(PChar(Edit1.Text)) + 1; lpData := PChar(Edit1.Text) end; // Search window by the window title // Fenster anhand des Titelzeilentext suchen hTargetWnd := FindWindowEx(0, 0, nil, PChar('WM_COPYDATA-Receiver')); if hTargetWnd <> 0 then SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@aCopyData)) else ShowMessage('No Recipient found!'); end; 


Importing MUDClient.exe does not have the FindWindowEx function, but there is FindWindowA, so I will use it.

The jump to the patch will be in this place:

There is a string formed here, and three NOPs will benefit.


I will simply write over the top three final commands in the procedure my “JMP 00515080”
It should be remembered that after eating two POP teams, we must restore them.

The patch relay that I placed in a large memory block filled with zeros looks like this:


The receiver code is even simpler:

 procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA; ------ procedure TForm1.WMCopyData(var Msg: TWMCopyData); begin try StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData); DoProcessNewLine(sText); //      except end; end; 


Not a patch, but a loader
In fact, I do not change the exe file. Instead, my application starts MUDClient.exe and uses the WriteProcessMemory command to modify the code in memory. This allows you to distribute the "bot" in the form of one exe, not two, but also allows you to avoid violating the clause in the license agreement (Grammar Nazi, for battle)
13. Login to the game through the old client, as well as any modification of the game exe-file.



Here it is necessary to tell that for the line we get what is valuable in it. For example, information about the characters on the screen, including yourself and mobs:


Above cited part of the line in which there was a message in private from the character GM.

This information is enough to keep track of both the characters we see and GM stealth who write to us in private. In the screenshot, you can see one of the “bot” windows, displaying units on the screen.


A typical example of breaking the rules. Shakes the shield on the mice AFK, and did not even notice that I spoiled one of them health

Continued in pictures








Really fun?


Since I’ve investigated the reception of data from the server, I’d have to look at least with one eye that the client is transmitting from his side. I will not burden the reader with listings, I will tell you briefly that, just like with recv, I set a breakpoint to send, went up the call stack and found a place where the string is not yet encoded. Examples of commands:

VR - is sent continuously, by this the client says that he is alive and wants to update the state
QR - character's exit from the game
UI4: 0 - use 4 inventory cells. For example, drink a bottle, eat fish, read a scroll
MB012: 4: 0 - move the inventory from 12 cells to 4
ACPrivet0000 - say “Hello” to local chat
PC * Vasya * Hello - write "Hello" in a private chat to the player Vasya
SP3 - sit down (when a character is sitting, his health and magical energy are restored more quickly)
ACesani gre olam1003 - recite fireball spell on character with ID1003

and so on.

Each command must end with 0x13 and 0x10 characters.


How to use it? Forging commands, of course. You can write a more convenient chat, teach the character to automatically eat fish, automatically sit down. Of course, much can be done by emulating keystrokes and mouse clicks. Much, but not all.

The second patch will be pretty simple too. The basic idea: in the place where the game is just about ready to encrypt and transfer data to the server, we insert our patch code. He looks for the window of our bot, and, if it finds it, reads the command, and then rewrites the line in the memory of the game process.

Here you can tell about the incident
While I was testing a new patch, a friend wrote to me, “Listen, is it not you there indulging?”. I did not understand his question, and then I paid attention to the global chat, in which the players were outraged en masse by the server restart. At the same time they were thrown out of the game, there was a slight rollback of the game state, and all the mobs, including bosses, appeared again. I did not believe it at first, but did several checks, and as a result of one server restarted. In the chat, a complete hysterics began, but all of Elf was blamed (if the nickname was not mixed up) and asked him to stop the disgrace. I don’t know why Elf became a scapegoat, or whether he deserved the fame of a local hacker, or because he parallels the adminit game and helps himself using his official position, and therefore has notoriety. I do not know.

In general, I made the button “drop the server” in my prog, which I then successfully checked a couple of times (because I didn’t believe that it was so easy to drop the server), that my friend and I had a lot of fun “someone holds this server by the eggs” . And after a few hours, it stopped working, apparently, they made an error on the server, which is good, since I wrote a letter to the main admin in which I admitted that it was me, as I did, and in general what affairs I do, but before clicking “send”, I decided to check the last time and ... found that the server was not broken.

Yes, the point was that I did not complete the command with the trailing sequence 0x13 0x10.


From cool. The game has the ability to "look" at the character next, for this you need to click on it with the ALT clamped.


It is curious that the team is sent at the same time. Click on itself, the log will display the following:
 00515160 COND: ID1006 

Let's try to change 1006 to 1001, we get


But this player is not just not near us, or even in a different location, but generally in a different world (where all who pumped up to level 21 go, since in the first world there is no further experience).
This makes it possible to know who is online now. But stop, because the list of players online is available on the game site. This is true, but administrators are not visible in it. Also, to hide the admins, when you try to send them a private message, even if the admin is online, the message “could not be sent” appears as if it were not in the game. And thanks to the found opportunity, we can always know that a particular GM is now working. The task is facilitated by the fact that it is enough to do a search from ID1000 to ... ID1100, which is enough for the eyes. When someone leaves the game and releases the ID, the next player takes the ID to himself. Thus, we do not need to guess the ID of the game master. And as a bonus, we can monitor the health of any player, even if he ran away from us.

For example, send commands
 ID1020 

Result:
 Osiris:0:Invictus:53:0:705:705:1:124:190:189:0:139:187:131:184:186:0:0:0: 


 ID1021 

Result:
 Admin:0::80:0:1880:1880:0:0:136:555:171:138:167:133:155:163:0:0:0: 


Pretty tasty.

Inventory


Then I wanted to do something really useful. The game is very inconvenient work with inventory. You cannot open the bag on the go and use it, only three fast cells are available to us, and if the object we need is far away, you will have to leaf through it for a very long time.

In this case, it is worth mentioning a rather braking cursor, which is not tied to the hardware one, but is drawn by the game itself, that is, poking them into the small arrows scrolling through the quick inventory — torture, clicking on objects too.

I decided to make my inventory in the form of a separate window that can be used just like a game window. Result:


First, I found out at what address the game stores the beginning of the inventory. Here I resorted to using ArtMoney. By sifting out unknown values ​​and throwing a zombie's hand between the first and zero cells, I quickly found a place in my memory:

Starting from 00550AC4, four-byte values ​​are stored that define the item. True, it was not possible to simply take and match these values ​​to the images in the tga / items folder. The zombie hand, as we see, has an index of 151, but in the folder with pictures the hand has a different meaning.


A simple subtraction of 151-149 does not get rid of, since for other objects the difference may be completely different.

Open the file ITEMS.DAT in WinHex, which is still in the same tga / items folder.


As we see, it does not look like an encrypted one, although at first glance only a description of subjects in Russian and alternative languages ​​is useful in it (German will be German in the German version). We fiddle a bit in the file to find some kind of structure, and we find it quite quickly: if we calculate the distance between the beginning of each item description in bytes, then it is equal to 0x2E2 (738) bytes each time. That is, each block of information about the N-subject begins with 738 * N.

But what is the 151 that matches the hand of a zombie? Very simple: multiply 151 * 738, backing away from the beginning of the file by this value (0x1B34E) and see:

How to get the name of the image file in which the hand is drawn from this information? Personally, I saw with my eyes, but you can also use the search by value, that the address 149 (149.bmp000.png) lies at 1B5A4. Conclusion: after 0x256 bytes from the beginning of the data block, the number of 2 bytes is equal to the number of the PNG file with the image.

Inventory is almost ready. But the simple display is not enough, we must also use items from it. As we found out earlier, if you send the command UI X : 0 to the server, where X is the cell number, the item will be used. Now you can run your magician and drink bottles of mana on the go, a soldier to drink bottles of health replenishment, without opening the in-game inventory, and the more inconvenient "fast".

How to fish
One of the peaceful professions in the game is a fisherman (he is a blacksmith, but this is not important). We need it not just for fishing, but the magic fish "magic". Usually, fishing is automated by a macro, which right-clicks the fishing net, and then the left mouse button on the pond. Magichka is rarely caught, mostly useless small fish that need to be eaten (this pumps the treatment skill). You put on a full backpack of fish, and then tediously click on unnecessary fish, leaving magichek.

But since we now have such a powerful tool, you can easily automate catching magic. To do this, we cycle through the inventory and give the command to be eaten by all the bad fishes. As a result, there is no need to follow the fisherman - he will catch precious fish.

It’s a bit of a technical perversion: if you just send the UI X : 0 command to the server, it really eats it, and even went to the client “OK”, only the client has no idea what this OK belongs to, he didn’t send anything and ignore it. Therefore, the client will continue to think that the fish / bottle is still intact. I solved the problem very roughly: after sending the UI X command: 0 it is necessary to write zeros with the help of WriteProcessMemory to the corresponding cell. And the fish will disappear immediately.


For the greater functionality of the inventory, it remains to do the elementary: when you click on the subject of "our" inventory, the quick inventory of the game should scroll to this item, and double-click will be transferred to the game. It was also made elementary: ArtMoney found the address where the quick inventory item is stored, and good old WriteProcessMemory I write the number I need there. Double click sent by command
 PostMessageW(h, WM_LBUTTONDBLCLK,wParam,lParam); 


Working with Shadow Worlds inventory has never been so convenient, the speed of ejecting objects (very useful to miners) has increased many times, I felt myself to be the inventor of the lever and the wheel.

Auto fade and ban


A bit boring snot and in-game twists and turns

Having made my prog very functional, I was distracted from it, and got carried away with the game. With a friend, they began to rock blacksmiths - during working hours they do not need to be distracted especially: dig for yourself ore, then occasionally forge simple objects out of it, and the program simplified interaction with the interface.

First trouble, foreigners. There are quite a few foreign players on the server who behave aggressively towards the Russians. Very soon, my friend and I realized that digging the ore just wouldn't work out - fighting characters now and then fly into the mines, often in groups, much higher than you in level and arranging a massacre. As a result, the experience accumulated by sweat and blood flies into the pipe, and you also have to look for money to buy a new pick and hammer. We didn’t take offense at foreigners, perceived more like hostile mobs, only they made fires among themselves and rejoiced when it came out to sneak away from them.

Second trouble, ours. At first, the Russian high-level players did not touch us, because they tried to fight with the Americans, as they are called on the server, although there are not only Americans. But very soon, foreigners took a dominant position, and “ours” realized that they could not oppose a strong guild, and began to tear themselves away on the neutral characters of newcomers. — () , «?» . — « PvP-», . , PvP . , — .

: , . , ,
8. [...] / , .



So, we write automation of exit from the game in case of danger. To do this, there is everything you need: as soon as the character appears on the screen, we get a notice about it. You can exit the game in at least two ways: send a QR command to the server, or emulate a click on the "exit" button. It is necessary to press quickly, as if a character is hit once, for a minute he must remain in the game.

Made by Also added a white list to which you can add those you trust. And the fun began: we mined ore, evil foreigners or "ours" rushed into the mine in order to sprinkle our sword with our blood, but only managed to see instantly disappearing figures of noob-smiths. They set up ambushes, but we both appeared and disappeared, a sort of ninja.

A criminal thought also occurred to me (only for the sake of science): to make a fully automatic Imthebot that would fish or dig ore, while avoiding dangers and answering simple GM questions. How many days / months he would have lasted, what level of skills would he be able to pump? For several days I hatched the idea, but refused, because my passion for pumping the blacksmith was too strong, and I did not want to waste time on “stupidity”.

And then a terrible thing happened: as always, I was digging ore, and Admin teleported to me. Auto exit triggered, I immediately quickly clicked "enter" and greeted. But it was late.

The character received a 999 days ban for using programs that automatically quit the game at the sight of a character.

Oh, how did I reproach myself for not adding Admin to the white list on time, as I was constantly thinking about this topic, but my hands did not reach. I tried to talk to the admin, but ran into a wall of silence. It seems that he has become accustomed to the whining of those who are banned. After long invitations to the dialogue, he only added that "a complaint was received against you, I checked, and the suspicion was confirmed." Funny complaint: I can’t kill with my cool magician / warrior a naked low-level artisan, so I’ll ask the admin to figure it out.

The loss of the blacksmith became for me another logical point of “fucking game”, after which the game again moves into memories.

Little truth and reasoning about good and evil


The obvious questions are: will the game suffer after the article, will the cheaters and bag-readers sweep? Not.First, there is almost no specific code in the article, only ideas. Secondly, all these ideas are not entirely relevant, that is, they will work, but clumsily. For example, the code with which I fake commands that go to the server is unstable. Since it is fraying normal client requests, after some time the client becomes ill and disconnects occur. How to do it right, I will not tell - the article from this will not be better. Improving the auto-out algorithm also will not do anything for anyone: the admin can easily create another character to test, and the months of pumping will go to null, for admins are very tough - no democracy and no presumption of innocence.

Source: https://habr.com/ru/post/192254/


All Articles