<转>[C#][Tutorial] How to become an EndScene() hooker

简介: NOTICE: I am a C# noob and this tutorial is for other C# noobs. I have no doubt that I have broken countless C# coding conventions and good practices ...

NOTICE: I am a C# noob and this tutorial is for other C# noobs. I have no doubt that I have broken countless C# coding conventions and good practices and it is best to assume the way I did everything is one of the least efficient possible. Credits go to many people, some of which I will mention towards the end of the tutorial.

Things you will need:

    • Microsoft Visual Studio 2010 - You can easily write this with any other version of Visual Studio that supports C# and .Net 2.0 and higher but for the sake of this tutorial I will be using VS2010
    • SlimDX - This is the managed wrapper around DirectX which we will be using to handle DirectX as well as find the function address of EndScene. SlimDX can be downloaded here: SlimDX Homepage
    • EasyHook - This is a great library for injecting DLLs and hooking functions in C#. With this you can hook managed as well as unmanaged functions (more about that later). Download here: easyhook-continuing-detours - Project Hosting on Google Code
    • A quick explanation of the object model
      I'm not going to explain the object model in detail, I'm just going to explain where the info we want is and what we're going to do with it. Whenever an instance of an object is created in memory, memory is allocated for that object and all of its properties (variables). However, it would be insanely messy and inefficient to allocate memory for every function of that object and copy those functions there. Instead, a VMT is allocated, which points to all to the methods which are already in the memory of the DLL (in the case of Direct3D) and any and all instances of the object will point to the same function.

      This makes it very simple for us once we have injected a DLL: Create a Direct3D object and find EndScene in its VMT. Hook our object's EndScene and we will be hooking the same EndScene thatWoW uses. Simple... right?

      The Work Space
      This is where the inefficiency comes in. My workspace, especially the layout of the files is pretty awful, but deal with is.

      First off, you want to create a project and a solution. File->New->Project and call it whatever you want, something along of "host" or "injector" so that you know which is which and select Console application for its type. Then, in the solution browser, right click on the solution and select Add->New Project. Name this one something along the lines of "DLL" or "InjectedDll" or whatever and select "Class Library" for its type.

      Next, go to the Properties of the DLL and under Build change the build path to ../[Host name]/bin/Debug/ or click browse and point it to the bin/debug folder of the first project (the console application). This way, whenever you build solution both the DLL and the injector are built into the same folder and you don't have to do any of the copying from one folder to the other nonsense.

      References
      Now due to the way that interfaces are set up to communicate between the injected DLL and the host, the DLL must reference the executable in order to be able to create an instance of the interface class.

      First, in the host process (the console application), add a reference to EasyHook.dll and copy all of the EasyHook binaries to your /bin/Debug folder (I'll let you figure out which ones on your own, but for now just copy all of them). Also I would copy the SlimDX.dll from the SlimDX binaries folder just to save yourself some trouble. That's it. That's all you gotta do for the host. Now, the using code:
    • using System;
      using System.Collections.Generic;
      using System.Diagnostics;
      using System.Runtime.Remoting;
      using EasyHook;

      System.Diagnostics is needed for finding the PID of WoW and System.Runtime.Remoting is required in order to allow the DLL to communicate with the host process.

      Now do the same with the inject DLL project and add the EasyHook.dll as a reference. Then, add SlimDX (from the .Net tab) and from the Projects tab add a reference to the host project from the DLL.

    • using System;
      using System.Runtime.InteropServices;
      using System.Windows.Forms;
      using EasyHook;
      using SlimDX;
      using SlimDX.Direct3D9;

      Same thing as before but now you add SlimDX so that you can create a device and use the WoW D3D device. (Windows.Forms is for a MessageBox in EndScene to know whether its working)

      Setting up the communication interface
      Like I said, I am a noob. Seriously, a total noob, so all I know so far is how to do one way communication but for the purposes of this tutorial that's all you need. What happens is you define your communication interface in the host process and then the DLL can call functions from inside the memory space of the host.

      Here's the interface I'll use:

    •     public class WoWInterface : MarshalByRefObject
          {
              public void IsInstalled(Int32 InClientPID)
              {
                  return;
              }
      
              public void WriteConsole(String Write)
              {
                  Console.WriteLine(Write);
              }
      
          }

      Very simple, no? All you're doing is create a class that displays something on the console. Put this in the main namespace of your project.

      The actual injection!
      Once again, this part is very simple thanks to EasyHook. The first thing you want to do is declare a static string called "ChannelName" which will be the random name of our IPC communication channel. Then, in your main function you'll want to put something like this:

    •             int wowPid;
                  Config.Register("WowInjector App", "InjectedDll.dll", "DllInjector.exe");
      
                  Process[] procs = Process.GetProcessesByName("WoW");
      
                  wowPid = procs[0].Id;
      
                  RemoteHooking.IpcCreateServer<WoWInterface>(ref ChannelName, WellKnownObjectMode.SingleCall);
      
                  RemoteHooking.Inject(wowPid, InjectionOptions.Default, "InjectedDll.dll", "InjectedDll.dll", ChannelName);

      The first thing you'll want to do is add your executable and dll to the Global Assembly Cache. According to the internet god, "The Global Assembly Cache or GAC is a machine-wide .NET assemblies cache for Microsoft's CLR platform. The approach of having a specially controlled central repository addresses the shared library concept and helps to avoid pitfalls of other solutions that lead to drawbacks like DLL hell." (Wikipedia). Basically, it's supposed to make management of DLLs a bit easier (ala the theory of .NET: Let the coder have as little control as possible). The first argument is a quick note about what it is, doesn't matter what you put here. The rest of the function is a list of all the assemblies you want to add. Essentially, just add your executable and dll (EasyHook and all those are automatically added. Don't worry).

      The next 2 lines create an array of processes with the name "WoW.exe" (not case sensitive) and choses the first one and get its Process ID. Because it's an array you can always make some kind of dialog which asks for the instance to inject into on your own.

      Now on to the more complex line, creating the IPC server. The server basically serves the interface which other functions call. The object in the < > is the one that will be open for the clients to use while the "ref ChannelName" is essentially a pointer to the string where the name of our IPC server will be stored. This is totally irrelevant for this tutorial but "WellKnownObjectMode.SingleCall" means that a new instance of the WoWInterface object will be created for each client while "WellKnownObjectMode.Singleton" would set it so that a single instance is used for all clients.

      The injection line is also very simple. The first argument is the PID of the process we want to inject, the second is the options (Look it up for yourself), the third argument is the DLL to inject in the case of 32 bit while the 4th is the DLL for 64 bit which in our case would be the same for both. If you make your own bot I would recommend differentiating between the 2 DLLs because the SlimDX reference could come in both 32bit and 64bit varieties. And last but not least we the arguments we will pass to the DLL which in our case would be only the ChannelName for the IPC connection.

      The DLL!
      The first thing you want to do with the DLL is to make sure that the namespace for the DLL is the same as the namespace in your host project. Don't ask me why, it's just a quirk with EasyHook that I have run into. Make your life simple and just do it.

      The next step is specifying the entry point of your DLL which EasyHook wants. In this case it is always jsut " : EasyHook.IEntryPoint"

    •  public class Main : EasyHook.IEntryPoint
          {
              WoWInterface Interface;
              LocalHook EndSceneHooker;

      This declares our Main class and sets it as the entry point EasyHook is looking for. THIS IS ABSOLUTELY NECESSARY! Otherwise your DLL will never get injected.

      The variables we declare are an Instance of the IPC interface and the LocalHook object we will use to hook EndScene.


      The Main function

    •         public Main(RemoteHooking.IContext InContext, String InChannelName)
              {
                  Interface = RemoteHooking.IpcConnectClient<WoWInterface>(InChannelName);
                  Interface.WriteConsole("Dll successfully injected.");
              }

      This functions sole purpose is to create the communications interface and confirm that the DLL is injected.

      Once again, the IPC call has the object we will be using for the interface in < > and the only argument is the ChannelName (which was passed to our DLL by EasyHook). Then we call a function from that interface which we declared earlier called WriteConsole which is just Console.WriteLine.

      Code:
    • public void Run(RemoteHooking.IContext InContext, String InChannelName)
              {
                  Device dev;
                  dev = new Device(new Direct3D(), 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing, new PresentParameters() { BackBufferWidth = 1, BackBufferHeight = 1 });
      
                  IntPtr addy = dev.ComPointer;
      
                  addy = (IntPtr)Marshal.ReadInt32(addy);
      
                  addy = (IntPtr)((int)addy + 0xA8);
                  addy = (IntPtr)Marshal.ReadInt32(addy);
      
                  EndSceneHooker = LocalHook.Create((IntPtr)addy, new DEndScene(EndSceneHook), this);
                  EndSceneHooker.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
      
                  while (true)
                  {
                  }
              }

      This is the Run function which is the lifeblood of your DLL (at least until you inject). I BELIEVE (I do not know for sure) that if you return from this function your DLL might be unloaded by EasyHook so that's why every example I've seen had some sort of unending while loop.

      Then we create Direct3D device. This is a copy pasta from SlimDX except I changed the form pointer to IntPtr.Zero and the buffer to 1x1. Then by setting addy to the ComPointer (the real D3D device object) we get the pointer we need to get our VMT.

      We then read the Int32 at that address (and cast it to IntPtr) and this new address is the VMT. Then, since EndScene() is the 42 function we add 0xA8 (A8h = 168 = 42 * 4) to that to get the address of EndScene().

      Sidenote: You can always use Black/White magic or whatever their names are (White rain?) to read the values, just use "addy = (IntPtr) WhiteMagic.Read<Int32>(addy);" since it's basically the same thing.

      THAT IS ALL THIS IS!

      Boom we have the address of EndScene (protip: this is the address of EndScene() in any version ofWoW and in almost any game that uses DirectX9).

      These last two lines hook the function to our own. 

      Code:
    •   EndSceneHooker = LocalHook.Create(addy, new DEndScene(EndSceneHook), this);
                  EndSceneHooker.ThreadACL.SetExclusiveACL(new Int32[] { 0 });

      The first argument of LocalHook.Create() is the address of the function, the second is an unmanaged pointer (I'll explain it in a bit) and the last is a pointer to the Main object that makes up most of the DLL. (Look at EasyHook examples, I won't explain how all of that works.)

      OK I honestly have no idea what ThreadACL.SetExclusiveACL does or why you need it. That line is a copy pasta from the EasyHook examples so go read about it yourself. From what I can tell, I don't need to worry about it.

      The last code is here:

      Code:
    •         [UnmanagedFunctionPointer(CallingConvention.StdCall,
              CharSet = CharSet.Unicode,
              SetLastError = true)]
              delegate int DEndScene(
                  IntPtr Direct3dDevice);
      
              public int EndSceneHook(IntPtr Direct3dDevice)
              {
                  using (Device d3d = Device.FromPointer(Direct3dDevice))
                  {
                      MessageBox.Show("LOL");
                      return d3d.EndScene().Code;
                  }
              }

      The Unmanaged pointer declaration just makes it possible for us to pass the EndSceneHook function to the LocalHook.Create. In the function itself, we do using directive to set our device object (d3d) to the one passed to the function. Thus, our d3d object is actually the Direct3D object that WoW uses. Thus we can manipulate the graphics and do whatever the **** we want.

      The return line just calls the original EndScene and returns the error code (or success code or w.e)


      And... that's it. This code will continuously spam that MessageBox and WoW will never render because of the MessageBoxes blocking the thread.

      Sorry for my crappy explanations but now that you have the code in front of you it should be pretty easy to figure it out and this tutorial is more for WoW hacking noobs, not for people who don't even know the most basic features or syntax of the language. (I left out Strong name signing for a reason). I also made this pretty fragmented so that you had to know at least something to be able to use this tutorial and not be able to just copy paste.

      Also, I have no idea how to release the Device created in that code but since its a managed DLL its already heavy on the memory.

      If I missed something, message me or something and I will correct it. I made sure to create a project from scratch and copy pasted all this code and it worked perfectly (though since this is on a computer it might not work as exactly on yours. If it doesn't, please provide more information than just "it doesn't work"). Tested on a 64bit Windows 7.

      Big note: Any time you get an exception that says something along the lines of your binaries not being in the GAC it is probably because you are not running the executable with administrator privileges. Save yourself the trouble and just run Visual Studio as Administrator

      Now, WTB TUTORIAL ON HOW TO CALL WOW FUNCTIONS AND GET RETURN VALUES!

      Credits go to:
      http://www.rohitab.com/discuss/index...howtopic=34411
      http://spazzarama.wordpress.com/2010...t3d-api-hooks/
      and the EasyHook and SlimDX examples. COPY PASTE FTW!

相关文章
|
网络协议 C# Windows
C# Tutorial - Simple Threaded TCP Server
<p style="margin-top:0px; margin-bottom:18px; font-family:museo-sans,'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:16px; line-height:24px; color:rgb(74,74,74)"> In this tutorial I'm goin
2347 0
|
12月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
313 3
|
12月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
262 3
|
6月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
98 3
|
5月前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
184 12
|
6月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
212 4
|
8月前
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
134 12
|
8月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
84 2
|
8月前
|
SQL 开发框架 安全
并发集合与任务并行库:C#中的高效编程实践
在现代软件开发中,多核处理器普及使多线程编程成为提升性能的关键。然而,传统同步模型在高并发下易引发死锁等问题。为此,.NET Framework引入了任务并行库(TPL)和并发集合,简化并发编程并增强代码可维护性。并发集合允许多线程安全访问,如`ConcurrentQueue&lt;T&gt;`和`ConcurrentDictionary&lt;TKey, TValue&gt;`,有效避免数据不一致。TPL则通过`Task`类实现异步操作,提高开发效率。正确使用这些工具可显著提升程序性能,但也需注意任务取消和异常处理等常见问题。
98 1
|
7月前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。
347 0