Tuesday, December 22, 2015

GameHacking 101 - Part 2 - Creating the Target

In this post, we are going to use the program provided by ToyMaker to make our own console game. For now, all of the code will be contained in the main method so when we look at it in our Disassembler/Debugger it will all be in the same region. This will help us see how the code is generated without having to jump around the assembly code too much. In future posts we will add functionality and abstract portions from the main method into separate pieces of code to make it easier to update and more difficult to reverse engineer. In the next post, we will examine the assembly code to see how it works and how we can write a hack for it.

As mentioned in the last post, I am using Visual Studio with the ReSharper C++ plugin to write the code. You may use any IDE/Compiler you wish (Code::Blocks is a great free alternative). You can write the application in whichever programming language you wish as long as it will have the ability to read and write process memory that you may have to research how to do on your own. Depending on the compiler/programming language you use, you may see different results when we view the assembled version. I will provide a link to GitHub to view the full code.

Now let's discuss the functionality of the application we will be writing. The premise of our new game is a 'Wild Magikarp' appears that we are going to battle with. The Magikarp and the player are going to have life and spirit (mana, magic, or whatever you want to call it) points that can be used for special attacks. Because it is a Magikarp it will not have a special ability (at least at this time anyway - that could be an interesting patch to make). The program will loop until one of the players has reached 0 life - which because of the use case should always be the player (at least in the initial application development).

Without further ado, let's start writing our program!

To get started, create an empty C++ Console program. In Visual Studio this is done by doing the following:

Create a new Project by going to File → New → Project



A dialog box should show up like the following. Navigate to Templates → Other Languages → Visual C++ → Win32 Console Application. Fill out the Name, Location, Solution, and Solution name dialog options. You can also check the Create directory for solution or Add to source control if you are so inclined.



This should cause the Win32 Application Wizard to appear. Select next.


 The next screen should allow you to configure the project. All you need is to select Empty project and Finish.





After you have your console application project ready we need to decide what includes we are going to use. We are going to be writing to the console, and using a few commands provided by windows. For these purposes we will need iostream and 'using namespace std.' If you are doing this in C you can use the printf or sprintf statements from the stdio.h library instead of cout. We will not need any other includes at this time.

#include <iostream>

using namespace std;

int main(int argc, char **argv)
{
    return 0;
}

Now that we have a console application that will actually compile and run I am going to do some configuration to set the console title and to make it so it does not exit immediately. This is done by adding the following lines:

int main(int argc, char **argv)
{
    // Set the title of the console.
    system("title Magikarp Duel");
       
    // Pause the application, wait for key input to continue.
    system("pause");
    return 0;
}

With that out of the way we need some variables to work with. I added six variables: one for the player's health and spirit, one for Magikarp's health, one to determine whose turn it is, one to determine what attack was used by the player, and one to hold the amount of damage done by the player.

int main(int argc, char **argv)
{
    // Variables
    // Player information
    int playerHealth = 100;
    int playerSpirit = 10;

    // Magikarp information
    int magikarpHealth = 100;

    // Determines whose turn it is: 1: Player 0: Magikarp
    int turnType = 1;

    // Determines what attack should be used.
    int attackType = 0;

    // Determines the amount of damage to be done.
    int damageAmount;  
    // ...
    return 0;
}

Next, I am going to start by displaying the intro script and the outro script when the game exits. I came up with the following:

int main(int argc, char **argv)
{
    // ...
    // Display the intro.
    cout << "A wild Magikarp has appeared!" << endl << endl;

    // Display outro text.
    if (playerHealth <= 0)
    {
     cout << "You have DIED hero!" << endl;
    }
    else if (magikarpHealth <= 0)
    {
     cout << "Congratulations! You have defeated a Wild Magikarp!" << endl;
    }
    cout << "Thanks for playing!" << endl;
    // ...
    return 0;
}

The game exits if the player's health is zero or less or when Magikarp's health is zero or less. Depending on which variable is zero will affect which language is displayed to the user.

Now we can start working with the internals responsible for modifying the variables to get to the outro text. I am going to start with Magikarp's turn because it is less work.

int main(int argc, char **argv)
{
    // ...
    // Main loop that checks the health of both participants.
    while (playerHealth > 0 && magikarpHealth > 0)
    {
        // TODO: Player's turn
        if (turnType == 1)
        {
            // Display the hero information so the user knows values.
            cout << "It is YOUR turn hero." << endl << endl;
            cout << "Your HP is: " << playerHealth << "." << endl;
            cout << "Your SP is: " << playerSpirit << "." << endl;
            cout << "-----------------------" << endl;

            // Set it to be Magikarp's turn.
            turnType--;
        }
        // Magikarp's turn
        if (magikarpHealth > 0 && turnType == 0)
        {
            // Display the results of Magikarp's turn.
            cout << "Wild Magikarp's turn." << endl;
            cout << "Wild Magikarp bites you for: 10 DMG." << endl;
            cout << "-----------------------" << endl;
            
            // Set the player's health appropriately
            playerHealth -= 10;

            // Set it to be the Player's turn.
            turnType++;
        }
    }
    // ...
    return 0;
}

We have to set the turnType in the players turn to get Magikarp's turn to execute. If we run this, we should see the 'You have DIED hero!' message displayed. Magikarp has actually beaten us! But not for long!

The first step to doing damage to Magikarp is to determine what attack the user is using. We should validate this so that only valid attacks are used. Every time it is the user's turn this attack should be reset before trying to check it.
int main(int argc, char **argv)
{
    // ...

        // Players turn
        if (turnType == 1)
        {
            // Reset the attackType
            attackType = 0;

            // ...

            // Attack loop used to get a valid attack value from the user.
            while (attackType < 1 || attackType > 3)
            {
                // Display the options available to the user.
                cout << "Decide your next move:" << endl;
                cout << "1. Punch (10 DMG 0 SP)." << endl;
                cout << "2. Psyki (20 DMG 5 SP)." << endl;
                cout << "3. Nothing (0 DMG 0 SP)." << endl;
                cout << "-----------------------" << endl;

                // Read the user's input.
                cin >> attackType;

                // Check that attackType is valid.
                if (attackType < 1 || attackType > 3)
                {
                    cout << "Not a valid option, try again." << endl << endl;
                }
                else if(attackType == 2 && playerSpirit - 5 < 0)
                {
                    cout << "You do not have enough SP for that! Select another move." << endl << endl;
                    attackType = 0;
                }
            }
        }

    // ...
}
This will allow the user to select an attack to perform, but will not actually do any damage to the Magikarp. That is our next step.
int main(int argc, char **argv)
{
    // ...

        // Players turn
        if (turnType == 1)
        {
            // ...
            
            // Determine the amount of damage to deal (if any).
            if (attackType == 1)
            {
                damageAmount = 10;
            }
            else if (attackType == 2)
            {
                damageAmount = 20;
                playerSpirit -= 5;
            }

            // Display the damage done to the Magikarp
            cout << "You do: " << damageAmount << " DMG." << endl;
            magikarpHealth -= damageAmount;

            // Check if magikarpHealth is less than 0 to prevent displaying a negative value.
            if (magikarpHealth < 0) 
            {
                magikarpHealth = 0;
            }

            // Display Magikarp's health so the user knows how much is left
            cout << "Wild Magikarp's HP is: " << magikarpHealth << "." << endl << endl;
            cout << "-----------------------" << endl;
            
            // ...
        }

    // ...
At this point their exists a bug in our code. Can you find it?

+ Spoiler
If you thought that by performing attack #2 and then repeatedly doing attack 3 would always do 20 damage without removing spirit energy; you are correct. We need to reset the damage when the user selects attack #3 back to zero or reset it when it becomes the user's turn like we do to reset attackType.

You would hope our unit or integration tests would catch something like that or as a last resort Quality Assurance. But things like this do slip through the cracks in the real world.

Now the only remaining exercise is to make a loop to continue playing when the Magikarp is defeated. And possibly award experience. I will leave that as a challenge for the user (although I will include it in the sample on GitHub if you would like to check your work).

That should do it! You should have a complete working game. As promised - here is the full Main.cpp I have (or you can get it from GitHub):

#include <iostream>

using namespace std;

int main()
{
    // Variables
    // Player information
    int playerHealth = 100;
    int playerSpirit = 10;

    // Magikarp information
    int magikarpHealth = 100;

    // Determines whose turn it is: 1: Player 0: Magikarp
    int turnType = 1;

    // Determines what attack should be used.
    int attackType = 0;

    // Determines the amount of damage to be done.
    int damageAmount;

    // Set the title of the console.
    system("title Magikarp Duel");

    // Display the intro.
    cout << "A wild Magikarp has appeared!" << endl << endl;

    // Main loop that checks the health of both participants.
    while (playerHealth > 0 && magikarpHealth > 0)
    {
        // Players turn
        if (turnType == 1)
        {
            // Reset the attackType
            attackType = 0;

            // Reset the damageAmount
            damageAmount = 0;

            // Display the hero information so the user knows values.
            cout << "It is YOUR turn hero." << endl << endl;
            cout << "Your HP is: " << playerHealth << "." << endl;
            cout << "Your SP is: " << playerSpirit << "." << endl;
            cout << "-----------------------" << endl;

            // Attack loop used to get a valid attack value from the user.
            while (attackType < 1 || attackType > 3)
            {
                // Display the options available to the user.
                cout << "Decide your next move:" << endl;
                cout << "1. Punch (10 DMG 0 SP)." << endl;
                cout << "2. Psyki (20 DMG 5 SP)." << endl;
                cout << "3. Nothing (0 DMG 0 SP)." << endl;
                cout << "-----------------------" << endl;

                // Read the user's input.
                cin >> attackType;

                // Check that attackType is valid.
                if (attackType < 1 || attackType > 3)
                {
                    cout << "Not a valid option, try again." << endl << endl;
                }
                else if(attackType == 2 && playerSpirit - 5 < 0)
                {
                    cout << "You do not have enough SP for that! Select another move." << endl << endl;
                    attackType = 0;
                }
            }

            // Determine the amount of damage to deal (if any).
            if (attackType == 1)
            {
                damageAmount = 10;
            }
            else if (attackType == 2)
            {
                damageAmount = 20;
                playerSpirit -= 5;
            }

            // Display the damage done to the Magikarp
            cout << "You do: " << damageAmount << " DMG." << endl;
            magikarpHealth -= damageAmount;

            // Check if magikarpHealth is less than 0 to prevent displaying a negative value.
            if (magikarpHealth < 0) 
            {
                magikarpHealth = 0;
            }

            // Display Magikarp's health so the user knows how much is left
            cout << "Wild Magikarp's HP is: " << magikarpHealth << "." << endl << endl;
            cout << "-----------------------" << endl;

            // Set it to be Magikarp's turn.
            turnType--;
        }

        // Magikarp's turn
        if (magikarpHealth > 0 && turnType == 0)
        {
            // Display the results of Magikarp's turn.
            cout << "Wild Magikarp's turn." << endl;
            cout << "Wild Magikarp bites you for: 10 DMG." << endl;
            cout << "-----------------------" << endl;
            
            // Set the player's health appropriately
            playerHealth -= 10;

            // Set it to be the Player's turn.
            turnType++;
        }
    }

    // Display outro text.
    if (playerHealth <= 0)
    {
        cout << "You have DIED hero!" << endl;
    }
    else if (magikarpHealth <= 0)
    {
        cout << "Congratulations! You have defeated a Wild Magikarp!" << endl;
    }
    cout << "Thanks for playing!" << endl;

    // Pause the application, wait for key input to continue.
    system("pause");
    return 0;
}

In the next post we will look at the assembly code generated by this code to develop an understanding of how it works.

No comments:

Post a Comment