前面的文章給網游寫一個掛吧 – 啟動外掛上介紹了輸入法注入的方法,本文解釋第二種方法。
有的游戲限制比較多,可能會將輸入法注入也禁用掉……這個時候就需要另想方法了。其實我們的目的很簡單,就是要讓不知道我們掛存在的游戲,在某個時刻將掛作為游戲的一個組件加載進來。輸入法注入是操作系統強制塞給游戲的,當然游戲有權利選擇不要。那么我們可以用暴力解決,強制游戲加載外掛:
1. 比如利用緩沖區溢出漏洞(參考文章 如何利用緩沖區溢出的程序錯誤來運行黑客程序 和 如何利用緩沖區溢出的程序錯誤來運行黑客程序(續))。
2. 還有就是在游戲每次都會執行的函數上掛個鈎子 ,但是一般的Windows鈎子都會被游戲禁用掉……而本文的方法是Hook DirectX EndScene函數,即游戲在繪圖結束后調用的函數,而且游戲會在一秒內經常調用這個函數,簡直就是把它當消息隊列使!
WOW – 使用DirectX EndScene注入技術
這個方法可以用在WOW 3.3.5.13930上,現在已經不行了,有興趣的朋友可以自己搭一個3.3.5.13930的私服試試。據說有很多方法可以注入(DirectX EndScene函數在d3d9.dll文件中):
1、 在游戲文件夾里放一個d3d9.dll,因為Windows是先搜索游戲的工作目錄再查找system32文件夾的,所以會加載到自定義的d3d9.dll。
2、 直接在游戲啟動前把system32文件夾中的d3d9.dll換成自己的。
3、 使用IDA直接獲取EndScene的地址,並且在游戲啟動后,修改這個地址的匯編碼,使其先調用我們的函數,再由我們的函數將控制權交還給真實的EndScene程序。
4、 在外掛里啟動游戲,啟動時先將游戲進程暫停,執行一系列Hook操作:
a) 先Hook LoadLibrary以便在游戲加載d3d9.dll的時候;
b) 再Hook d3d9.dll里的Direct3DCreate9函數,
c) 再通過Hook過的Direct3DCreate9函數獲取D3D9的指針,
d) 從D3D9指針處Hook D3D9->CreateDevice函數,以獲取指向設備的指針Device。
e) 再從Device指針處Hook Device->EndScene函數。
5、 在Windows操作系統里創建一個自己的Device。
6、 或者就是使用SetWindowsHookEx API安裝一個系統級別的Hook,然后我們的外掛就會被加載進每一個進程!參考文檔:http://www.woodmann.com/forum/archive/index.php/t-11023.htm
這里我只用過第4種方法,因此本文也只介紹第4種方法,這里我們用到EasyHook這個庫,這個庫允許我們使用C#代碼Hook系統API。EasyHook的用法很簡單:
1、 在包含Hook函數的托管DLL里,創建一個類,實現了EasyHook.IEntryPoint這個接口。
2、 在類的構造函數里建立與宿主進程的連接。
3、 然后在IEntryPoint.Run函數里,注冊你的Hook,下面是以CreateFile這個系統API為例:
1、 public void Run(RemoteHooking.IContext InContext, String InChannelName)
3、 CreateFileHook = LocalHook.Create(
4、 LocalHook.GetProcAddress( " kernel32.dll ", " CreateFileW "),
5、 new DCreateFile(CreateFile_Hooked),
6、 this);
7、
8、 CreateFileHook.ThreadACL.SetExclusiveACL( new Int32[] { 0});
9、 }
在第5行里,那個DCreateFile就是CreateFile在C#中的委托表現方式,因為是通過函數指針的方式執行的,因此會聲明成一個委托。
4、 最后在外掛里,使用下面的代碼注冊Hook:
2、 {
3、 Config.Register(
4、 " A FileMon like demo application. ",
5、 " FileMon.exe ",
6、 " FileMonInject.dll ");
7、
8、 RemoteHooking.IpcCreateServer<FileMonInterface>(
9、 ref ChannelName, WellKnownObjectMode.SingleCall);
10、
11、 RemoteHooking.Inject(
12、 Int32.Parse(args[ 0]),
13、 " FileMonInject.dll ",
14、 " FileMonInject.dll ",
15、 ChannelName);
16、 }
5、 代碼里,還有一個關鍵的地方,就是Hook后獲取的指針是一個COM接口,即拿到的是一個虛函數表,因此在Hook EndScene方法的時候,就是把這個COM接口的EndScene的虛函數指針換成我們自己的,如下表的接口定義和替換方法:
2、 {
3、 [StructLayout(LayoutKind.Sequential, Pack = 4)]
4、 public struct IDirect3DDevice9
5、 {
6、 public IntPtr** VFTable;
7、 }
8、 }
9、 public IDirect3DDevice9(D3D9.IDirect3D9* InNativeIDirect3D9,
10、 D3D9.IDirect3DDevice9* InNativeIDirect3DDevice9)
11、 {
12、 NativeIDirect3D9 = InNativeIDirect3D9;
13、 NativeIDirect3DDevice9 = InNativeIDirect3DDevice9;
14、 OverrideFunctions();
15、 }
16、 public D3D9.IDirect3D9* NativeIDirect3D9
17、 {
18、 get; private set;
19、 }
20、 public D3D9.IDirect3DDevice9* NativeIDirect3DDevice9
21、 {
22、 get; private set;
23、 }
24、 private void OverrideFunctions()
25、 {
26、 OriginalEndScene = NativeIDirect3DDevice9->VFTable[ 0][ 42];
27、 RealEndScene =
28、 (DelegateEndScene)Marshal.GetDelegateForFunctionPointer(
29、 OriginalEndScene, typeof (DelegateEndScene));
30、 MyEndScene = EndScene;
31、 IntPtr PointerToMyEndScene = Marshal.GetFunctionPointerForDelegate(MyEndScene);
32、 NativeIDirect3DDevice9->VFTable[ 0][ 42] = PointerToMyEndScene;
33、 }
34、 public uint EndScene(D3D9.IDirect3DDevice9 Device)
35、 {
36、 // 防止多線程訪問
37、 lock (LuaInterface.dataLock)
38、 {
39、 // 先做我們自己的事情,然后再將控制權轉移給真正的EndScene函數
40、 return RealEndScene(Device);
41、 }
42、 }
比如在第37行 – 41行,就是在EndScene調用的時候,先做我們的事情,然后再把控制權交給真正的EndScene。而第26行,EndScene函數IDirect3DDevice9的第42個函數,因為在第3行,代碼已經將IDirect3DDevice9接口(實際就是一個虛函數表)當成一個普通的C/C++結構體處理 – 而且是32位機上的結構體(如果要支持64位改一下就可以了),而第6行代碼,就是把這個虛函數表當作一個普通的數組處理。不過據說在DirectX10里已經把EndScene去掉了……
這種方式,網上已經有完整的源代碼,請在此下載(這個代碼跟本文講解使用的代碼是不同的,因此有興趣的朋友可以自行研究下面的代碼):
https://github.com/spazzarama/Direct3DHook
而EasyHook的使用方式和詳細原理,請參考文檔:
http://www.codeproject.com/Articles/27637/EasyHook-The-reinvention-of-Windows-API-hooking
未完待續……