I think many of you at least once in your life engaged in the modification of your favorite computer game. It could be editing resources (for example, character models and objects from
GTA ), writing various scripts (for example, the
Dwarf Fortress task scheduler, launched using
DFHack ) or developing mods for your
Minecraft server that was running
Minecraft Forge .
In many cases, game developers do not provide an official API for expanding functionality, leaving an indifferent community alone with their own ideas and desires, which sometimes result in fairly large-scale projects.
')
Sitting one evening at one of my favorite games called
UnReal World (hereinafter
referred to as URW), I once again encountered an incredibly inconvenient behavior for me. The natural desire of almost any player when meeting with a more or less strong enemy is to save instantly, so as not to lose everything he has achieved at the moment (yes, some will consider it “rudeness” / “cheat” / etc, but do not argue about tastes) . The problem is that save-boot quickly in the URW just will not work. When saving a character, you automatically exit to the main menu, and exit without saving to return to the previous save point, you can only kill the game process through
taskmgr . As a result, in most cases, difficult battles in the later stages of the game (when it is really a pity to lose everything that I have accumulated over a long time) ended in insane combinations and memorized keystrokes.
And then I thought. And what's stopping us from adding our own quick load and save menu? Armed with a PE file debugger, editor and analyzers, I set to work.
How was the process, and what came of it, read under the cut (carefully, a
lot of screenshots ). Before reading this article, I also strongly recommend that you familiarize yourself with the
previous ones , since they have already explained some of the points outlined here.
A few words about the game itself. UnReal World is a computer game, developed since 1992 by two Finnish developers, and continuing its development in our days. The game is a representative of the genre of
roguelike and will make you plunge into the life of the northern tribes of the late Iron Age. At the time of this writing, the URW is absolutely free (however, this was not always the case), and for donating any size you will receive a video with thanks, taken by the authors of the game, and if you pay more than a certain amount, you will receive a so-called. lifetime membership, which will give you access to a hidden forum section with beta releases and other goodies. In general, the game is really worth playing it.
If you have carefully read the previous articles, you probably already know where we will begin the study of the binary. True, with the analysis of the executable urw.exe file with the utilities
DiE and
PEiD :
No packers and projectors were reported, so we can assume that we won't have to shoot anything this time.
We now turn to the test for the use of
ASLR technology. We load urw.exe into
PE Tools , click on the “Optional Header” button and see the “DLL Flags” option already familiar to us from previous articles: 0x8140:
Change it to 0x8100 (why this is explained, for example,
here ) and click on the two “Ok” buttons, thus causing the executable file to be loaded at the same address every time, equal to Image Base (in our case, it is
0x00400000 ).
We load urw.exe into
OllyDbg and think over the procedure:
- Find the character loading procedure and figure out how to interact with it.
- Find the process of saving the character and also find out how to interact with it.
- Assign the opening of the menu "Quick save-load" to some unused hotkey and draw it
Let's start with the first item.
We create a character, save it and, after going to the main menu, we try to load it. Your attention should fall on the message "Loading% character_name%":
Let's find references to this line in the disassembled listing:
Very similar to that! We set the breakpoint, exit to the main menu and try to load our character again:
Assuming that this whole procedure is responsible for loading the character, we jump to call it using the window with the call stack
and we see that she does not require any “environment” for her work:
Let us turn to the second point - the search for the procedure involved in the preservation of the character.
The procedure is, in principle, similar to the previous step. First, we look at what happens at the moment of saving the character and pay attention to the fact that in this case the game also writes a corresponding message:
Now we are looking for the string “Saving your character ...” in the “Referenced text strings” of the urw module:
We put the bryak on the instructions that refer to this line, and again we try to save the character:
Next, jump to the place of the current procedure call
and look at the "environment":
As you can see, before calling the character saving procedure, the number 1 is put on the stack, which is most likely its argument. If we look into the procedure body, we will really see the
EBP + 8 addresses in a couple of places (the
EBP register contains the old value of the
EBP register,
EBP + 4 is the address of the instruction that you need to transfer control after the current procedure completes, from the
EBP address
+8 arguments begin, if they, of course, exist, and local variables are usually found for negative offsets:
A quick look can be guessed that the value of this argument is responsible for the need to display messages about the beginning and end of the download.
It is equally important to pay attention to the
ADD ESP, 4 instruction, which immediately follows the call to the character saving procedure. It "cleans up" the place in the stack, which was occupied by the arguments of the procedure (in this case, only one). Requirements for who should “clean” the stack are determined by various
calling conventions .
So, we have dealt with the procedures for loading and saving the game world, so let's move on to the next step - finding the unallocated hotkey and drawing our menu.
Fortunately, in one of the previous versions, the menu of “extended commands” was “cut out” from the game, which was previously called by entering the '#' character. Now when you try to activate it, the game displays the following message:
What is not an ideal place for patching?
We are looking for references to the line “Extended commands menu has been removed”
and jump on the only one of them:
Left-click on the start of the procedure and press Ctrl-R to find the places from which it is called:
Well, this is just one call. We jump on it and find ourselves in one of the cases of the switch-block:
Well, we found a place to implement our patch. Now let's think about how we will draw the menu for quick loading and saving.
Obviously, this menu should fit into the rest of the visual component of the game, as is done, for example, from the “Fishing” menu:
There are too many references to the “Fishing options” line, so let's try to find references to “Retrieve a net”:
We jump to the specified instruction and get into one of the cases:
We set up a bryak, trying to open the “Fishing” menu and see where we were called from:
We put the breakpoint at the beginning of this procedure, press F9 in OllyDbg and again try to activate the fishing menu. As a result of step-by-step debugging, we can assume that the procedure
0x004FC530 is responsible for getting the text for the next menu item from the switch block we’ve seen before, and the procedure located at
0x004BEF10 is responsible for drawing the menu items:
Arg1 indicates the ASCII code of the character that must be entered to select the corresponding menu item, and Arg2 is responsible for its name.
For drawing the menu itself with its name, the procedure
0x004BF3B0 is responsible, which takes as its argument the menu title. Also for some reason, the value at
0xAE16068 is changed to 1 before calling this procedure and to 0 after it:
Please note that in the case of these procedures, responsibility for the cleanup stack also lies on the caller's shoulders.
The
0x004BF3B0 procedure returns control to the code that called it only after selecting one of the menu options or pressing the Esc key, putting in the
EAX register a number denoting the ASCII code of the character entered (in the case of Esc, it will be zero).
Let's think over our patch code:
; PUSH "Save" PUSH 0x53 ; 'S' CALL 0x004BEF10 ; , ADD ESP,8 PUSH "Load" PUSH 0x4C ; 'L' CALL 0x004BEF10 ; , ADD ESP,8 ; PUSH "Quick save-load" MOV DWORD PTR DS:[0xAE16068],1 CALL 0x004BF3B0 ADD ESP,4 MOV DWORD PTR DS:[0xAE16068],0 ; , CMP EAX,53 JE save ; 'S' CMP EAX,4C JNZ exit ; 'L' JMP load save: PUSH 1 ; CALL 0x005030E0 ; , ADD ESP,4 JMP exit load: CALL 0x0050CB90 ; , JMP exit exit: ; default-case switch-, ; , '#' JMP 0x0050C8C1
We are looking for a place for our code cave at the end of the executable image. I decided to start it from the address
0x0051039B :
Here is what happened:
0051039B . 53 61 76 65 00 ASCII "Save",0 005103A0 . 4C 6F 61 64 00 ASCII "Load",0 005103A5 . 51 75 69 63 6B 20 73 61 76 65 2D 6C 6F 61 64 00 ASCII "Quick save-load",0 005103B5 68 9B035100 PUSH urw.0051039B ; ASCII "Save" 005103BA 6A 53 PUSH 53 005103BC E8 4FEBFAFF CALL urw.004BEF10 005103C1 83C4 08 ADD ESP,8 005103C4 68 A0035100 PUSH urw.005103A0 ; ASCII "Load" 005103C9 6A 4C PUSH 4C 005103CB E8 40EBFAFF CALL urw.004BEF10 005103D0 83C4 08 ADD ESP,8 005103D3 68 A5035100 PUSH urw.005103A5 ; ASCII "Quick save-load" 005103D8 C705 6860E10A 01000000 MOV DWORD PTR DS:[AE16068],1 005103E2 E8 C9EFFAFF CALL urw.004BF3B0 005103E7 83C4 04 ADD ESP,4 005103EA C705 6860E10A 00000000 MOV DWORD PTR DS:[AE16068],0 005103F4 83F8 53 CMP EAX,53 005103F7 74 07 JE SHORT urw.00510400 005103F9 83F8 4C CMP EAX,4C 005103FC 75 13 JNZ SHORT urw.00510411 005103FE EB 0C JMP SHORT urw.0051040C 00510400 6A 01 PUSH 1 00510402 E8 D92CFFFF CALL urw.005030E0 00510407 83C4 04 ADD ESP,4 0051040A EB 05 JMP SHORT urw.00510411 0051040C E8 7FC7FFFF CALL urw.0050CB90 00510411 ^ E9 ABC4FFFF JMP urw.0050C8C1
Let's add an unconditional jump to our code cave in the case block, which is responsible for handling the opening of the extended command menu:
Push '#' in the game and ...
What it is? Apparently, after opening the previous menu the list of items was not cleared, but only expanded with new ones. Let's take another look at how the “Fishing” menu is
rendered and note that in the case of our menu we don’t call at least one more procedure -
0x004BED40 . Perhaps it is she who is responsible for the cleanup, because her challenge is right before drawing all the items:
Despite the fact that before calling this procedure, the values ​​of the
EBX register are
placed on the stack, it is not at all an argument of this procedure, as can be seen by looking at its implementation:
In order not to “shift” all the instructions and not to change the addresses in the code cave already written, let's add a call to this procedure to the case-block code
and try entering the '#' character again:
Fine!
We play with save-load
and notice that at first glance everything works as it should.
In the process of testing come to the village
and press # - S again:
Where are the people?
Apparently, the process of saving the game world also contains the cleanup code of game objects, since It was not designed to be used outside the context of entering the main menu. You can get out of this situation in two ways - to find out exactly where the cleanup code is (and there are not enough nested calls, believe me), or to cheat and perform saving followed by loading instead of the usual saving. I propose to stay on the last version. We change our code cave, having finished the
JMP 0x00510411 instruction, located at
0x0051040A :
0051039B . 53 61 76 65 00 ASCII "Save",0 005103A0 . 4C 6F 61 64 00 ASCII "Load",0 005103A5 . 51 75 69 63 6B 20 73 61 76 65 2D 6C 6F 61 64 00 ASCII "Quick save-load",0 005103B5 68 9B035100 PUSH urw.0051039B ; ASCII "Save" 005103BA 6A 53 PUSH 53 005103BC E8 4FEBFAFF CALL urw.004BEF10 005103C1 83C4 08 ADD ESP,8 005103C4 68 A0035100 PUSH urw.005103A0 ; ASCII "Load" 005103C9 6A 4C PUSH 4C 005103CB E8 40EBFAFF CALL urw.004BEF10 005103D0 83C4 08 ADD ESP,8 005103D3 68 A5035100 PUSH urw.005103A5 ; ASCII "Quick save-load" 005103D8 C705 6860E10A 01000000 MOV DWORD PTR DS:[AE16068],1 005103E2 E8 C9EFFAFF CALL urw.004BF3B0 005103E7 83C4 04 ADD ESP,4 005103EA C705 6860E10A 00000000 MOV DWORD PTR DS:[AE16068],0 005103F4 83F8 53 CMP EAX,53 005103F7 74 07 JE SHORT urw.00510400 005103F9 83F8 4C CMP EAX,4C 005103FC 75 13 JNZ SHORT urw.00510411 005103FE EB 0C JMP SHORT urw.0051040C 00510400 6A 01 PUSH 1 00510402 E8 D92CFFFF CALL urw.005030E0 00510407 83C4 04 ADD ESP,4 0051040A 90 NOP 0051040B 90 NOP 0051040C E8 7FC7FFFF CALL urw.0050CB90 00510411 ^ E9 ABC4FFFF JMP urw.0050C8C1
Great! Objects still disappear, but immediately appear due to subsequent download.
Let's try to save our changes to disk:
It is sad to realize, but the instructions added by us closer to the end of the image of the executable file came out beyond its “physical boundaries”, as it was already in
one of the previous articles .
Let's calculate the upper "border" by looking at the information about the urw.exe sections in PE Tools:
Virtual Offset (
0x00001000 ) + Raw Size (
0x0010F400 ) + Image Base (
0x00400000 ) =
0x00510400
Those. highlighted instructions "got out" for the "border":
NOPs , of course, can be removed, but we still have 20 bytes left without them. Another 5 bytes we can borrow in front of our code cave:
But what to do with the remaining 15 bytes? You can reduce the size of the lines responsible for the menu items and its title, making them, for example, equal to "S", "L" and "S & L", but this is not a very elegant solution.
It is possible that in the middle of a disassembled listing there are also places where you can add your own code. These can be zeros, which we usually found at the end of the image of the executable file,
NOPs or, for example,
INT3 instructions that follow each other between the procedure bodies. This is the last case observed in urw.exe. For example,
Let's break up the lines and save and load processing to the following places:
00409FBA . 53 61 76 65 00 ASCII "Save",0 00409FBF . 4C 6F 61 64 00 ASCII "Load",0 00409FC4 . 51 75 69 63 6B 20 73 61 76 65 2D 6C 6F 61 64 00 ASCII "Quick save-load",0 005007A2 6A 01 PUSH 1 005007A4 E8 37290000 CALL urw.005030E0 005007A9 ^ E9 C6F5FFFF JMP urw.004FFD74 004FFD74 83C4 04 ADD ESP,4 004FFD77 ^ E9 16FFFFFF JMP urw.004FFC92 004FFC92 E8 F9CE0000 CALL urw.0050CB90 004FFC97 E9 25CC0000 JMP urw.0050C8C1 0050C438 > \BE 20C75300 MOV ESI,urw_.0053C720 ; ASCII "Extended commands"; Case 23 ('#') of switch 0050C3FB 0050C43D . E8 FE28FBFF CALL urw.004BED40 0050C442 E9 4F3F0000 JMP urw.00510396 0050C447 . E9 75040000 JMP urw.0050C8C1 00510396 68 BA9F4000 PUSH urw.00409FBA ; ASCII "Save" 0051039B 6A 53 PUSH 53 0051039D E8 6EEBFAFF CALL urw.004BEF10 005103A2 83C4 08 ADD ESP,8 005103A5 68 BF9F4000 PUSH urw.00409FBF ; ASCII "Load" 005103AA 6A 4C PUSH 4C 005103AC E8 5FEBFAFF CALL urw.004BEF10 005103B1 83C4 08 ADD ESP,8 005103B4 68 C49F4000 PUSH urw.00409FC4 ; ASCII "Quick save-load" 005103B9 C705 6860E10A 01000000 MOV DWORD PTR DS:[AE16068],1 005103C3 E8 E8EFFAFF CALL urw.004BF3B0 005103C8 83C4 04 ADD ESP,4 005103CB C705 6860E10A 00000000 MOV DWORD PTR DS:[AE16068],0 005103D5 83F8 53 CMP EAX,53 005103D8 ^ 0F84 C403FFFF JE urw.005007A2 005103DE 83F8 4C CMP EAX,4C 005103E1 ^ 0F85 B0F8FEFF JNZ urw.004FFC97 005103E7 ^ E9 A6F8FEFF JMP urw.004FFC92
This time the image is successfully saved, and we can enjoy playing URW with a new quick load and save menu.
Afterword
Laziness is one of the most annoying qualities in almost any person and field of activity. Do not be lazy to spend a little time on solving the problem that you have met not the first time - perhaps this will save you much more time in the future.
Thank you for your attention, and again I hope that the article was useful to someone.