<转>[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
2292 0
|
4月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
157 3
|
4月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
173 3
|
7天前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
20 0
|
1月前
|
存储 C#
揭秘C#.Net编程秘宝:结构体类型Struct,让你的数据结构秒变高效战斗机,编程界的新星就是你!
【8月更文挑战第4天】在C#编程中,结构体(`struct`)是一种整合多种数据类型的复合数据类型。与类不同,结构体是值类型,意味着数据被直接复制而非引用。这使其适合表示小型、固定的数据结构如点坐标。结构体默认私有成员且不可变,除非明确指定。通过`struct`关键字定义,可以包含字段、构造函数及方法。例如,定义一个表示二维点的结构体,并实现计算距离原点的方法。使用时如同普通类型,可通过实例化并调用其成员。设计时推荐保持结构体不可变以避免副作用,并注意装箱拆箱可能导致的性能影响。掌握结构体有助于构建高效的应用程序。
47 7
|
20天前
|
安全 C# 开发者
【C# 多线程编程陷阱揭秘】:小心!那些让你的程序瞬间崩溃的多线程数据同步异常问题,看完这篇你就能轻松应对!
【8月更文挑战第18天】多线程编程对现代软件开发至关重要,特别是在追求高性能和响应性方面。然而,它也带来了数据同步异常等挑战。本文通过一个简单的计数器示例展示了当多个线程无序地访问共享资源时可能出现的问题,并介绍了如何使用 `lock` 语句来确保线程安全。此外,还提到了其他同步工具如 `Monitor` 和 `Semaphore`,帮助开发者实现更高效的数据同步策略,以达到既保证数据一致性又维持良好性能的目标。
24 0
|
3月前
|
存储 C# 开发者
C# 编程基础:注释、变量、常量、数据类型和自定义类型
C# 编程基础:注释、变量、常量、数据类型和自定义类型
|
4月前
|
开发框架 .NET Java
探索 C#编程的奥秘与魅力
【4月更文挑战第20天】C#是微软开发的现代、面向对象的编程语言,以其简洁语法、强大功能和跨平台支持脱颖而出。它支持自动垃圾回收、泛型、委托、LINQ,并广泛应用于桌面、Web、移动和游戏开发。C#拥有活跃的开发者社区和丰富的资源,是Unity游戏开发的首选语言。随着.NET Core,C#可在多个操作系统上运行,持续创新,未来发展潜力巨大。
161 11
|
4月前
|
存储 安全 网络安全
C#编程的安全性与加密技术
【4月更文挑战第21天】C#在.NET框架支持下,以其面向对象和高级特性成为安全软件开发的利器。本文探讨C#在安全加密领域的应用,包括使用System.Security.Cryptography库实现加密算法,利用SSL/TLS保障网络传输安全,进行身份验证,并强调编写安全代码的重要性。实际案例涵盖在线支付、企业应用和文件加密,展示了C#在应对安全挑战的同时,不断拓展其在该领域的潜力和未来前景。
237 5