Registration Code (Part 1): w%kQ6
Registration Code (Part 2): b<#$1[*(cw~
In order to register on this forum, you must use the codes above. Combine them into one code (copy paste).

XNA Hooking Through Reflection / Reference

Programming topics that relate to the C# language.
Post Reply
User avatar
atom0s
Site Admin
Posts: 397
Joined: Sun Jan 04, 2015 11:23 pm
Location: 127.0.0.1
Contact:

XNA Hooking Through Reflection / Reference

Post by atom0s » Fri Jan 09, 2015 10:23 am

This is a basic tutorial showing how to 'attach' onto a vulnerable XNA game. This method uses the game executable as a reference and reflection to obtain the games 'Game' object. This allows us to use this as a base class for our 'wrapper'.

I am not sure if this will work for all XNA games, I've only tested it on Terraria.
If I get a chance to test it out on more games I'll post my findings later.

Starting Point - Looking at Terraria in Reflector / ILSpy
Terraria is a managed game written using C# / XNA and is not protected thus you can decompile the source back to its original state. We can look at the games main module, in this case Main.cs to see if the class is exposed as public. We can see that ILSpy, in this case, shows that Main is inheriting Game and is exposed as public. Which allows us to add this assembly as a reference to a new project and use that class.

Next Step - A New Project
You'll need to have XNA Game Studio for this part, so look at the links section of this tutorial for where you can download that.

I recommend just making a new XNA Windows Game project since it will add most of the references we need for us. We wont be using any real window so don't worry about that showing up. But if you want to use a console for custom commands etc, you could create a console project and use that instead.

But in this case I'll be using an empty XNA game template.
  • Create a new project by going to: File -> New -> Project
  • Select C# from the languages on the left.
  • Select XNA Game Studio 4.0 as the template parent node on the left.
  • Select Windows Game 4.0 on the right as the project type.
  • Select a path and name the project, and create the project.
When Visual Studio is done loading the project, first start by deleting the content project it creates automatically. We don't need it.
Next rename Game1.cs to Hook.cs.
Next we need to add our games executable as a reference to our project. So:
  • Select the project from the solution explorer.
  • Select the 'References' folder.
  • Right-click and choose 'Add Reference..'
  • Select the Browse tab and locate the executable to add the reference.
Lets start with Hook.cs, this is where we will be inheriting Terraria's main class.

Hook.cs - The Guts
To start, delete everything inside of this file. This is the default game crap and we don't need it. We are going to be inheriting from Terraria's base so we will be using this instead. (Note: Change the namespace to match your projects!)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Xna.Framework;
  5. using Microsoft.Xna.Framework.Audio;
  6. using Microsoft.Xna.Framework.Content;
  7. using Microsoft.Xna.Framework.GamerServices;
  8. using Microsoft.Xna.Framework.Graphics;
  9. using Microsoft.Xna.Framework.Input;
  10. using Microsoft.Xna.Framework.Media;
  11. using Terraria;
  12.  
  13. namespace tLock
  14. {
  15.     public class Hook : Terraria.Main
  16.     {
  17.         /// <summary>
  18.         /// The sprite batch object of this game.
  19.         /// </summary>
  20.         SpriteBatch spriteBatch = null;
  21.  
  22.         /// <summary>
  23.         /// Terraria's default font to draw with.
  24.         /// </summary>
  25.         SpriteFont spriteFont = null;
  26.  
  27.         /// <summary>
  28.         /// Initialize override.
  29.         /// </summary>
  30.         protected override void Initialize()
  31.         {
  32.             // Set the content root to Terraria's..
  33.             Content.RootDirectory = Environment.CurrentDirectory + @"\Content\";
  34.  
  35.             // Allow the game to initialize..
  36.             base.Initialize();
  37.  
  38.             // Attempt to find Terraria's base font object..
  39.             this.spriteFont = base.Content.Load<SpriteFont>("Fonts\\Mouse_Text");
  40.         }
  41.  
  42.         /// <summary>
  43.         /// LoadContent override.
  44.         /// </summary>
  45.         protected override void LoadContent()
  46.         {
  47.             base.LoadContent();
  48.  
  49.             // Obtain the sprite batch object..
  50.             this.spriteBatch = new SpriteBatch(this.GraphicsDevice);
  51.         }
  52.  
  53.         protected override void Draw(GameTime gameTime)
  54.         {
  55.             // Allow the game to render first..
  56.             base.Draw(gameTime);
  57.  
  58.             // Lets draw some basic text to show we are hooked..
  59.             spriteBatch.Begin();
  60.             spriteBatch.DrawString(this.spriteFont, "Hello world", new Vector2(1.0f, 1.0f), Color.White);
  61.             spriteBatch.End();
  62.         }
  63.     }
  64. }
A quick overview of this, we are inheriting Terraria.Main which is actually just a class that inherits XNA's 'entry-point' class "Game". This allows us to use Terraria's class as a base to launch from while still using Terraria so we are "hooking" it in a sense.

SpriteBatch and SpriteFont are our objects used to render onto the games window. SpiteBatch is initialized after we allow the base (Terraria) to initialize. SpriteFont is set when we allow the base (Terraria) to load its content.

Inside of the Initialize function we need to point the root directory for the content to the actual applications folder. If not we will crash because the game will fail to load its content properly and bitch. We use the environments current directory for a reason (and because I'm lazy) which we will get to in a bit.

Anytime you see a call to base.Something, we are calling Terraria. We let Terraria do its business first and ours afterward as typical hooking works. This example will hook onto Terraria, find the default font, and draw Hello World.

Next we need to fix our Program.cs

Program.cs - The Runner
Program.cs should just be the program entry point with Main.
Delete whatever is in Main, typically just the game.Run() call but we don't need it.

First we want to set the current directory to the games real path. In this case I'm hard-coding it for the tutorials sake. You can bind it dynamically if you wish or from a config file etc, this is just an example:
  1. // Set Terraria's Path..
  2. String strRealPath = "C:\\Program Files\\Steam\\steamapps\\common\\terraria";
  3. Directory.SetCurrentDirectory(strRealPath);
Next we reflect and cast our Hook object to a base Game object and run it. This will cause Terraria to launch and our Hook class to be used as the front-end game class with Terraria's as the base.
  1. // Run our game..
  2. var tLock = Assembly.GetExecutingAssembly().GetType("tLock.Hook", true, true);
  3. (Activator.CreateInstance(tLock) as Game).Run();
So in total our new Program.cs looks like:
  1. using System;
  2. using System.IO;
  3. using System.Reflection;
  4. using Microsoft.Xna.Framework;
  5.  
  6. namespace tLock
  7. {
  8. #if WINDOWS || XBOX
  9.     static class Program
  10.     {
  11.         /// <summary>
  12.         /// The main entry point for the application.
  13.         /// </summary>
  14.         static void Main(string[] args)
  15.         {
  16.             // Set Terraria's Path..
  17.             String strRealPath = "C:\\Program Files\\Steam\\steamapps\\common\\terraria";
  18.             Directory.SetCurrentDirectory(strRealPath);
  19.  
  20.             // Run our game..
  21.             var tLock = Assembly.GetExecutingAssembly().GetType("tLock.Hook", true, true);
  22.             (Activator.CreateInstance(tLock) as Game).Run();
  23.         }
  24.     }
  25. #endif
  26. }
A side note, if you are using a low-end graphics card or a laptop the project settings will default to try to use the HiDef profile for XNA. You will need to turn this off because most shitty cards don't support Shaders 3.0. You an do that by right-clicking the project solution and choosing Properties. The main tab should contain two bullets to change between the two profiles.

Links:
- Terraria: http://www.terraria.org/ Terraria
- Visual Studio 2010: http://www.microsoft.com/visualstudio/e ... 0-editions Visual Studio 2010 Editions | Microsoft Visual Studio
- XNA Game Studio 4.0: http://www.microsoft.com/download/en/de ... x?id=23714 Download: Microsoft XNA Game Studio 4.0 - Microsoft Download Center - Download Details
- ILSpy: http://wiki.sharpdevelop.net/ILSpy.ashx ILSpy - SharpDevelop Wiki

Extending With Reflection
Now that we have access to the games main class, and its rendering, lets get the player list of active players. We can see in ILSpy its define publicly as:
  1. public static Player[] player = new Player[256];
So we want to reflect the members of our base for player to get this member. Inside our Draw function we can draw a list of active players like this:
  1. protected override void Draw(GameTime gameTime)
  2. {
  3.     // Allow the game to render first..
  4.     base.Draw(gameTime);
  5.  
  6.     var miPlayers = (from m in typeof(Terraria.Main).GetMembers()
  7.                      where m.Name == "player"
  8.                      select m).SingleOrDefault();
  9.  
  10.     object objOutput = null;
  11.     Terraria.Player[] players = ((miPlayers as FieldInfo).GetValue(objOutput) as Terraria.Player[]);
  12.  
  13.     var activePlayers = (from p in players
  14.                          where p.active == true
  15.                          select p);
  16.  
  17.     spriteBatch.Begin();
  18.  
  19.     String strActivePlayers = String.Empty;
  20.     foreach (Terraria.Player p in players)
  21.     {
  22.         strActivePlayers += p.name + Environment.NewLine;
  23.     }
  24.  
  25.     spriteBatch.DrawString(this.spriteFont, strActivePlayers, new Vector2(1.0f, 1.0f), Color.White);
  26.     spriteBatch.End();
  27. }
Derp~
Need a great web host? Check out: AnHonestHost.com


Donations can be made via Paypal:
https://www.paypal.me/atom0s
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest