Exetools  

Go Back   Exetools > General > Source Code

Notices

Reply
 
Thread Tools Display Modes
  #1  
Old 01-21-2015, 07:38
atom0s's Avatar
atom0s atom0s is offline
Family
 
Join Date: Jan 2015
Location: 127.0.0.1
Posts: 397
Rept. Given: 26
Rept. Rcvd 126 Times in 63 Posts
Thanks Given: 54
Thanks Rcvd at 732 Times in 280 Posts
atom0s Reputation: 100-199 atom0s Reputation: 100-199
[C#] Loading Hooking via Mono.Cecil

About This Tutorial
This tutorial will cover how to implement function alterations to a managed target using Mono.Cecil. As far as I know Mono.Cecil only works for recreating a binary before it is loaded. I am not sure if it will allow runtime reconstruction, nor have I tried to get it to so. So if you know if it can please do let me know. This tutorial will cover hooking onto an XNA application.

Legal Bullshit No One Reads
This is a tutorial for research purposes only. I am not responsible for what you do with this material. You agree that by reading this that you take full responsibility of your actions. I am not responsible for what you do with this information!

Tools Needed
  • Visual Studio 2010 (http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-csharp-express)
  • XNA Game Studio 4 (http://www.microsoft.com/download/en/details.aspx?id=23714)
  • Mono.Ceci
    • Download: http://www.mediafire.com/?pxn1ym9ds9op5iq
    • Credits / Info: http://www.mono-project.com/Cecil
  • .NET Target To Hook (I will be using Terraria as the example.)
  • IL Disassembler if you wish to view the IL code with ease. (VS2010 comes with ildasm which you can run from the VS command line.)
    • ILSpy: http://wiki.sharpdevelop.net/ilspy.ashx

Getting Started
To start, we'll be working on attaching onto XNA's main functions for a normal game. Since Terraria is written in C# using XNA we can see that it inherits 'Game' in its main class.

If we use a .NET object browser we can see what Game contains. For the sake of this tutorial you can find the members here:
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game_members%28v=xnagamestudio.40%29.aspx

So we are mainly interested in:
  • Initialize
  • LoadContent
  • Update
  • Draw

Like most hacks we will want pre/post hooks for each of these functions. This will allow us to handle things before and after the actual call occurs.

Preparing Our Project
We'll start by making a new XNA game, it is easier to do this since it will add all the XNA references for us even though we don't need any of the actual game code it makes. So start by opening VS2010 and going to:
File -> New -> Project -> C# -> XNA Game Studio 4.0 -> Windows Game (4.0)

Next, we'll remove the Content project because its useless to us (unless you plan to add custom content). So right-click the Content project and remove it. You will also need to remove the content reference in the main project's Content References if it wasn't removed automatically.

Next, we'll delete Game1.cs since we don't need it.

Create a new class, call it whatever you want. In this case I'm calling it Hooks.cs


Preparing Our Hooks Class
To start we will need four main class variables, one for the loaded assembly definition, one for the main module definition, one for the complete assembly, and one for the assemblies main class type. So we will add:

PHP Code:
private AssemblyDefinition m_vAsmDefinition null;
private 
ModuleDefinition m_vModDefinition null;
private 
Assembly m_vAssembly null;
private 
Type m_vMainType null
Next we will write an initialize function to load our target into our assembly definition and obtain its main module for the module definition:
PHP Code:
public bool Initialize()
{
    try
    {
        
this.m_vAsmDefinition AssemblyDefinition.ReadAssembly("Terraria.exe");
        
this.m_vModDefinition this.m_vAsmDefinition.MainModule;
        return 
true;
    }
    catch { return 
false; }

Next we'll write the finalization function to finish up our modifications and prepare the new assembly for usage:
PHP Code:
public bool Finialize()
{
    try
    {
        
// Validate we've loaded the main executable first..
        
if (this.m_vAsmDefinition == null)
            return 
false;

        
using (MemoryStream mStream = new MemoryStream())
        {
            
// Write the edited data to the memory stream..
            
this.m_vAsmDefinition.Write(mStream);

            
// Load the new assembly from the memory stream buffer..
            
this.m_vAssembly Assembly.Load(mStream.GetBuffer());
            return 
true;
        }
    }
    catch { return 
false; }

Next, we need to create a call to run the new assembly rather then using the one on the disk. Since we have the assembly written in memory we can load it in memory as well, so we'll write our Run function like this:
PHP Code:
public bool Run()
{
    try
    {
        if (
this.m_vAssembly == null)
            return 
false;

        
// Get the main class type..
        
this.m_vMainType this.m_vAssembly.GetType("Terraria.Main");

        
// Create the constructor call..
        
var constructor this.m_vMainType.GetConstructor(new Type[] { }).Invoke(null);

        
// Obtain the main run method and invoke it..
        
var method this.m_vMainType.GetMethod("Run"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static);
        
method.Invoke(constructornull);

        return 
true;
    }
    catch { return 
false; }

Now that we have our base done, we can setup the program to run this new instance of the assembly.
Inside of Program.cs, inside of Main() replace everything with:
PHP Code:
static void Main(string[] args)
{
    var 
hooks = new Hooks();
    
hooks.Initialize();
    
hooks.Finialize();
    
hooks.Run();

Now you can test if the base hooking works by running your wrapper etc. to make sure its working as expected.

Hooking Onto Our Functions..
Next we want to hook onto the functions. Simple enough, Pre* hooks just get called at the start of the function, while Post* at the end.
(Please see the note below at the end of this tutorial, you may need to do more work in some cases for some hooks to work properly!)

We'll start with Initialize. Inside our Hooks class lets add two functions PreInitialize and PostInitialize following the same method definition from MSDN for Initialize. For now we'll out some console outputs to determine if they get called. (Be sure that these are marked public and static or you will get runtime errors!)
PHP Code:
public static void PreInitialize()
{
    
System.Diagnostics.Debug.WriteLine("Custom PreInitialize was called!");
}
public static 
void PostInitialize()
{
    
System.Diagnostics.Debug.WriteLine("Custom PostInitialize was called!");

Our next step involves digging into Mono.Cecil's abilities. We'll make a new function to apply our hooks. So lets call that ApplyHooks for now. To start, we need to locate the method inside our main type. I wrote wrappers for making these parts easier. These are the wrappers I wrote for these next steps:
PHP Code:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Object Reflection From New Terraria Objects
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// <summary>
/// Gets a method definition from the new Terraria executable.
/// </summary>
/// <param name="t"></param>
/// <param name="methodName"></param>
/// <returns></returns>
public MethodDefinition GetMethodDefinition(TypeDefinition tString methodName)
{
    return (
from MethodDefinition m in t.Methods
            where m
.Name == methodName
            select m
).FirstOrDefault();
}

/// <summary>
/// Gets a field definition from the new Terraria executable.
/// </summary>
/// <param name="t"></param>
/// <param name="fieldName"></param>
/// <returns></returns>
public FieldDefinition GetFieldDefinition(TypeDefinition tString fieldName)
{
    return (
from FieldDefinition f in t.Fields
            where f
.Name == fieldName
            select f
).FirstOrDefault();
}

/// <summary>
/// Gets a property definition from the new Terraria executable.
/// </summary>
/// <param name="t"></param>
/// <param name="propName"></param>
/// <returns></returns>
public PropertyDefinition GetPropertyDefinition(TypeDefinition tString propName)
{
    return (
from PropertyDefinition p in t.Properties
            where p
.Name == propName
            select p
).FirstOrDefault();
}

/// <summary>
/// Gets a type definition from the new Terraria executable.
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
public TypeDefinition GetTypeDefinition(String typeName)
{
    return (
from TypeDefinition t in this.m_vModDefinition.Types
            where t
.Name == typeName
            select t
).FirstOrDefault();
}

/// <summary>
/// Gets a type from within the new Terraria executable.
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
public Type GetType(String typeName)
{
    return (
from Type t in this.m_vAssembly.GetTypes()
            
where t.Name == typeName
            select t
).FirstOrDefault();
}

/// <summary>
/// Gets a method from within Terraria.Main.
/// </summary>
/// <param name="methodName"></param>
/// <returns></returns>
public MethodInfo GetMethod(String methodName)
{
    return (
from MethodInfo m in this.m_vMainType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static)
            
where m.Name == methodName
            select m
).FirstOrDefault();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Object Reflection From New Terraria.Main Object
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// <summary>
/// Gets the value of a field within Terraria.Main.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fieldName"></param>
/// <returns></returns>
public T GetMainField<T>(String fieldName)
{
    var 
field = (from FieldInfo f in this.m_vMainType.GetFields(BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static)
                 
where f.Name == fieldName
                 select f
).FirstOrDefault();

    if (
field == null)
        return default(
T);

    return (
T)field.GetValue(this.m_vMainType);
}

/// <summary>
/// Sets the value of a field within Terraria.Main.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fieldName"></param>
/// <param name="objValue"></param>
public void SetMainField<T>(String fieldNameT objValue)
{
    var 
field = (from FieldInfo f in this.m_vMainType.GetFields(BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static)
                 
where f.Name == fieldName
                 select f
).FirstOrDefault();

    if (
field == null
        return;

    
field.SetValue(this.m_vMainTypeobjValue);

So now we can easily locate the method for Initialize using:
PHP Code:
MethodDefinition initMethod GetMethodDefinition(GetTypeDefinition("Main"), "Initialize"); 
Next we want to inject our 'PreInitialize' call to the start of this method. We can do that by using:
PHP Code:
MethodDefinition initMethod GetMethodDefinition(GetTypeDefinition("Main"), "Initialize");
var 
initProc initMethod.Body.GetILProcessor();
initProc.InsertBefore(initMethod.Body.Instructions[0], initProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreInitialize"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    )); 
We can run the test app again and see if it gets called, in the output window you should see:
Custom PreInitialize was called!

Great now we want to add our PostInitialize call to the end of the function. If we look at the all in a IL decoder (such as ILSpy) we can see the Initialize call ends with:
PHP Code:
IL_1e8fret 
So we need to inject before this return:
PHP Code:
initProc.InsertBefore(initMethod.Body.Instructions[initMethod.Body.Instructions.Count 2], initProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostInitialize"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    )); 
So now we have in total for our Initialize hooks:
PHP Code:
public void ApplyHooks()
{
    
MethodDefinition initMethod GetMethodDefinition(GetTypeDefinition("Main"), "Initialize");
    var 
initProc initMethod.Body.GetILProcessor();
    
initProc.InsertBefore(initMethod.Body.Instructions[0], initProc.Create(OpCodes.Call,
        
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreInitialize"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
        ));
    
initProc.InsertBefore(initMethod.Body.Instructions[initMethod.Body.Instructions.Count 2], initProc.Create(OpCodes.Call,
        
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostInitialize"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
        ));

Next we'll do the same for LoadContent, add the Pre and Post methods:
PHP Code:
public static void PreLoadContent()
{
    
System.Diagnostics.Debug.WriteLine("Custom PreLoadContent was called!");
}
public static 
void PostLoadContent()
{
    
System.Diagnostics.Debug.WriteLine("Custom PostLoadContent was called!");

And then for our hook it will be similar to Initializes hook:
PHP Code:
MethodDefinition contMethod GetMethodDefinition(GetTypeDefinition("Main"), "LoadContent");
var 
contProc contMethod.Body.GetILProcessor();
contProc.InsertBefore(contMethod.Body.Instructions[0], contProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreLoadContent"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    ));
contProc.InsertBefore(contMethod.Body.Instructions[contMethod.Body.Instructions.Count 2], contProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostLoadContent"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    )); 
Hooking Onto Update and Draw
Next, we want to hook onto Update and Draw. The difference here is that we now have functions that take arguments. In this case a 'GameTime' object which holds the time of the current XNA game. Looking at some of the function in a diassembler we'll see that using the GameTime object will look like this:
PHP Code:
IL_1922ldarg.0
IL_1923
ldarg.1
IL_1924
call instance void [Microsoft.Xna.Framework.Game]Microsoft.Xna.Framework.Game::Update(class [Microsoft.Xna.Framework.Game]Microsoft.Xna.Framework.GameTime)
IL_1929ret 
We see that two arguments are being pushed to Update. That's because this is a class method so ldarg.0 is the base Game object, and ldarg.1 is the GameTime object. So let's add our handlers first for Update:
PHP Code:
public static void PreUpdate(GameTime gameTime)
{
}
public static 
void PostUpdate(GameTime gameTime)
{

Next we'll do the similar stuff to get the function. But we will add an extra opcode to push the gameTime argument:
PHP Code:
MethodDefinition updateMethod GetMethodDefinition(GetTypeDefinition("Main"), "Update");
var 
updateProc updateMethod.Body.GetILProcessor();
var 
updateInst updateMethod.Body.Instructions[0];
updateProc.InsertBefore(updateInstupdateProc.Create(OpCodes.Ldarg_1)); // push gameTime
updateProc.InsertBefore(updateInstupdateProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreUpdate"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    )); 
Now, if you wish you could push Ldarg_0 as well to your function, but you will need to add the param for the Game object.

Next we want to inject our PostUpdate call. The above IL code I posted about the Update function is the end of the function, so we know it ends with a Ret. We want to push our code, again, before the return.

PHP Code:
updateProc.InsertBefore(updateMethod.Body.Instructions[updateMethod.Body.Instructions.Count 1], updateProc.Create(OpCodes.Ldarg_1)); // push gameTime
updateProc.InsertBefore(updateMethod.Body.Instructions[updateMethod.Body.Instructions.Count 1], updateProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostUpdate"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    )); 
For draw, we'll do the same thing. Add the two methods calls:
PHP Code:
public static void PreDraw(GameTime gameTime)
{
}
public static 
void PostDraw(GameTime gameTime)
{

Then we'll add our hook code the same way as Update:
PHP Code:
MethodDefinition drawMethod GetMethodDefinition(GetTypeDefinition("Main"), "Draw");
var 
drawProc drawMethod.Body.GetILProcessor();
var 
drawInst drawMethod.Body.Instructions[0];
drawProc.InsertBefore(drawInstdrawProc.Create(OpCodes.Ldarg_1)); // push gameTime
drawProc.InsertBefore(drawInstdrawProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PreDraw"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    ));

drawProc.InsertBefore(drawMethod.Body.Instructions[drawMethod.Body.Instructions.Count 1], drawProc.Create(OpCodes.Ldarg_1));
drawProc.InsertBefore(drawMethod.Body.Instructions[drawMethod.Body.Instructions.Count 1], drawProc.Create(OpCodes.Call,
    
this.m_vModDefinition.Import(typeof(Hooks).GetMethod("PostDraw"BindingFlags.Public | BindingFlags.NonPublic BindingFlags.Instance BindingFlags.Static))
    )); 
There we have it, all of the main XNA calls are now hooked as we wanted.

Hooking Other Methods
So now that we have hooked all the XNA methods, lets hook something else. In this case I will be using the collectors edition method CheckBunny. This method determines if you have the Collectors Edition of the game by looking in your Registry for a specific key. The IL of this code looks like:
PHP Code:
.try
{
    
IL_0000ldsfld class [mscorlib]Microsoft.Win32.RegistryKey [mscorlib]Microsoft.Win32.Registry::CurrentUser
    IL_0005
stloc.0
    IL_0006
ldloc.0
    IL_0007
ldstr "Software\\Terraria"
    
IL_000ccallvirt instance class [mscorlib]Microsoft.Win32.RegistryKey [mscorlib]Microsoft.Win32.RegistryKey::CreateSubKey(string)
    
IL_0011stloc.0
    IL_0012
ldloc.0
    IL_0013
brfalse.s IL_0044

    IL_0015
ldloc.0
    IL_0016
ldstr "Bunny"
    
IL_001bcallvirt instance object [mscorlib]Microsoft.Win32.RegistryKey::GetValue(string)
    
IL_0020brfalse.s IL_0044

    IL_0022
ldloc.0
    IL_0023
ldstr "Bunny"
    
IL_0028callvirt instance object [mscorlib]Microsoft.Win32.RegistryKey::GetValue(string)
    
IL_002dcallvirt instance string [mscorlib]System.Object::ToString()
    
IL_0032ldstr "1"
    
IL_0037call bool [mscorlib]System.String::op_Equality(stringstring)
    
IL_003cbrfalse.s IL_0044

    IL_003e
ldc.i4.1
    IL_003f
stsfld bool Terraria.Main::cEd

    IL_0044
leave.s IL_004f
// end .try
catch [mscorlib]System.Object
{
    
IL_0046pop
    IL_0047
ldc.i4.0
    IL_0048
stsfld bool Terraria.Main::cEd
    IL_004d
leave.s IL_004f
// end handler

IL_004fret 
However, instead of injecting anything, we'll just rewrite the entire method. We knw that we want to set the property cEd to true. Which can be done with:
PHP Code:
IL_003eldc.i4.1
IL_003f
stsfld bool Terraria.Main::cEd 
So we'll rewrite this method using:
PHP Code:
MethodDefinition bunnyMethod GetMethodDefinition(GetTypeDefinition("Main"), "CheckBunny");
FieldDefinition bunnyField GetFieldDefinition(GetTypeDefinition("Main"), "cEd");

// Clear the old function instructions..
bunnyMethod.Body.Instructions.Clear();

// Remove the exception handler..
bunnyMethod.Body.ExceptionHandlers.Clear();

// Rewrite the function..
var bunnyProc bunnyMethod.Body.GetILProcessor();
bunnyProc.Append(bunnyProc.Create(OpCodes.Nop)); // nop for alignment
bunnyProc.Append(bunnyProc.Create(OpCodes.Ldc_I4_1)); // push true (1) onto the stack
bunnyProc.Append(bunnyProc.Create(OpCodes.StsfldbunnyField)); // set field to the value on stack
bunnyProc.Append(bunnyProc.Create(OpCodes.Ret)); // return 
My Methods Aren't Getting Called In The Right Order!
With Terraria as an example you may notice that your XNA hooks may not be called in the right order. This is due to how Terraria is written. For example, Draw has multiple returns within it to handle drawing certain parts of the game. For instance, it will attempt to Draw the menu specifically with:
PHP Code:
IL_52ddldarg.0
IL_52de
call instance void Terraria.Main::DrawMenu()
IL_52e3ret 
Which will return before our PostDraw is called while we are at the main menu. There are a few ways to correct this. Without actually digging into the code here are some suggestions you can do to fix problems like:
  • Hook DrawMenu and call PostDraw at the end of it to ensure your PostDraw is called. (I recommend this method.)
  • Inject your PostDraw call after every Ret inside the function to ensure it gets called.
  • Disassemble the code and look to see where you should place each Post call to ensure that it will always be called. (This could become a tiresome process if its a large method.)

~~~

Another fun thing you can do is use a custom attribute to automatically find methods in your application that will be applying detours/edits to the new assembly and execute them like this:
InjectionAttribute.cs
PHP Code:
// Adjust the namespace / class name as needed!
namespace toxyClient.Classes
{
    
using System;
    
using System.Collections.Generic;
    
using System.Linq;
    
using System.Text;

    
/// <summary>
    /// InjectionAttribute Class Implementation
    /// 
    /// Attribute used to mark a function as an injection function.
    /// 
    /// The main program class will scan the application for this
    /// attribute and automatically call the functions.
    /// </summary>
    
public class InjectionAttribute Attribute
    
{
        
/// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="detourName"></param>
        /// <param name="detourDesc"></param>
        
public InjectionAttribute(String detourNameString detourDesc "")
        {
            
this.Name detourName;
            
this.Desc detourDesc;
        }

        
/// <summary>
        /// Gets or sets the name of this detour.
        /// </summary>
        
public String Name getset; }

        
/// <summary>
        /// Gets or sets the desc of this detour.
        /// </summary>
        
public String Desc getset; }
    }

Then in your main Program.cs code use this before you call Finalize:
PHP Code:
// Scan for detour attributes and apply their detour..
(from Type t in Assembly.GetExecutingAssembly().GetTypes()
 
from MethodInfo m in t.GetMethods()
 
from InjectionAttribute d in m.GetCustomAttributes(typeof(InjectionAttribute), false)
 
select m).ToList().ForEach(=> m.Invoke(nullnull)); 
Then any function that you want to apply detours/injection stuff with to the new assembly just mark with the new custom attribute like this:
PHP Code:
/// <summary>
/// Applies the detours required by this class.
/// </summary>
[Injection("Some Name Here""A short description about what this function is for.")]
public static 
void DoInjection()
{


This style of loading can be used on various .NET targets, not just games. Allowing you to use a loader situation to make alterations to a file rather then completely editing the binary. (You can however save the edited binary after your edits have been made if you wish too.) This is mostly a tutorial showing off Mono.Cecil and some of the advantages it has to using it.

Terraria is a fairly small XNA game that I used as the example in this tutorial. This tutorial demonstrates hooking the various XNA render pipeline functions, as well as towards the end hooking onto a game-specific function with the use of Mono.Cecil. You can fully take over functions, 'detour' them, alter bits of them etc. with Mono.Cecil allowing you to take full advantage of a .NET program.
Reply With Quote
The Following 5 Users Gave Reputation+1 to atom0s For This Useful Post:
canopus (01-21-2015), emo (01-22-2015), h8er (01-22-2015), ontryit (01-21-2015), uranus64 (01-21-2015)
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On


Similar Threads
Thread Thread Starter Forum Replies Last Post
[dnlib+cecil] Logging.dll injecor and Tracer phono Community Tools 0 11-02-2016 16:41
DLLs loading sequence te$ter General Discussion 6 10-30-2013 18:52
Ollydbg loading problem hobferret General Discussion 5 07-07-2008 20:40


All times are GMT +8. The time now is 18:53.


Always Your Best Friend: Aaron, JMI, ahmadmansoor, ZeNiX, chessgod101
( 1998 - 2024 )