欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]某超级模块中游戏双开功能实现

[原创]某超级模块中游戏双开功能实现

超级模块中有个双开功能,能够双开腾讯的大部分游戏,例如DNF。于是就下载模块下来分析了一下。
随便在网上找一个用超级模块写的双开工具。然后OD载入,下bp DeleteFileA断点。
为什么要下deleteFile断点,而不是CreateFile,是因为该模块注册驱动文件后就会将驱动文件删除。
下了断点之后点击启动双开的时候,OD断下。查看堆栈,可以看到写出的驱动路径。

将驱动文件拷贝出来,文件大小只有3kb,用IDA打开。

INIT:00010985 ; NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)

INIT:00010985                 public DriverEntry

INIT:00010985 DriverEntry     proc near

INIT:00010985

INIT:00010985 DriverObject    = dword ptr  8

INIT:00010985 RegistryPath    = dword ptr  0Ch

INIT:00010985

INIT:00010985                 mov     edi, edi

INIT:00010987                 push    ebp

INIT:00010988                 mov     ebp, esp

INIT:0001098A                 mov     eax, dword_10904

INIT:0001098F                 test    eax, eax

INIT:00010991                 mov     ecx, 0BB40h

INIT:00010996                 jz      short loc_1099C

INIT:00010998                 cmp     eax, ecx

INIT:0001099A                 jnz     short loc_109BF

INIT:0001099C

INIT:0001099C loc_1099C:                              ; CODE XREF: DriverEntry+11j

INIT:0001099C                 mov     edx, ds:KeTickCount

INIT:000109A2                 mov     eax, offset dword_10904

INIT:000109A7                 shr     eax, 8

INIT:000109AA                 xor     eax, [edx]

INIT:000109AC                 and     eax, 0FFFFh

INIT:000109B1                 mov     dword_10904, eax

INIT:000109B6                 jnz     short loc_109BF

INIT:000109B8                 mov     eax, ecx

INIT:000109BA                 mov     dword_10904, eax

INIT:000109BF

INIT:000109BF loc_109BF:                              ; CODE XREF: DriverEntry+15j

INIT:000109BF                                         ; DriverEntry+31j

INIT:000109BF                 not     eax

INIT:000109C1                 mov     dword_10900, eax

INIT:000109C6                 pop     ebp

INIT:000109C7                 jmp     sub_106F8

INIT:000109C7 DriverEntry     endp

入口函数处没什么特殊的,跳到了sub_106F8,下面来看看这个函数里面具体实现。
.text:000106F8 ; int __stdcall sub_106F8(PDRIVER_OBJECT DeviceObject, int)

.text:000106F8 sub_106F8       proc near               ; CODE XREF: DriverEntry+42j

.text:000106F8

.text:000106F8 SymbolicLinkName= UNICODE_STRING ptr -10h

.text:000106F8 DestinationString= UNICODE_STRING ptr -8

.text:000106F8 DeviceObject    = dword ptr  8

.text:000106F8

.text:000106F8                 mov     edi, edi

.text:000106FA                 push    ebp

.text:000106FB                 mov     ebp, esp

.text:000106FD                 sub     esp, 10h

.text:00010700                 push    esi

.text:00010701                 mov     esi, [ebp+DeviceObject]

.text:00010704                 push    edi

.text:00010705                 mov     edi, ds:RtlInitUnicodeString

.text:0001070B                 push    offset word_106A6 ; SourceString

.text:00010710                 lea     eax, [ebp+DestinationString]

.text:00010713                 push    eax             ; DestinationString

.text:00010714                 mov     dword ptr [esi+38h], offset sub_10486			//设置IRP处理函数

.text:0001071B                 mov     dword ptr [esi+40h], offset sub_10486

.text:00010722                 mov     dword ptr [esi+70h], offset sub_104AA

.text:00010729                 mov     dword ptr [esi+34h], offset sub_10670

.text:00010730                 call    edi ; RtlInitUnicodeString				//初始化设备名称

.text:00010732                 lea     eax, [ebp+DeviceObject]

.text:00010735                 push    eax             ; DeviceObject

.text:00010736                 push    0               ; Exclusive

.text:00010738                 push    0               ; DeviceCharacteristics

.text:0001073A                 push    22h             ; DeviceType

.text:0001073C                 lea     eax, [ebp+DestinationString]

.text:0001073F                 push    eax             ; DeviceName

.text:00010740                 push    0               ; DeviceExtensionSize

.text:00010742                 push    esi             ; DriverObject

.text:00010743                 call    ds:IoCreateDevice					//创建设备

.text:00010749                 test    eax, eax

.text:0001074B                 jl      short loc_10780

.text:0001074D                 push    offset word_106CA ; SourceString

.text:00010752                 lea     eax, [ebp+SymbolicLinkName]

.text:00010755                 push    eax             ; DestinationString

.text:00010756                 call    edi ; RtlInitUnicodeString				//初始化符号链接名称

.text:00010758                 lea     eax, [ebp+DestinationString]

.text:0001075B                 push    eax             ; DeviceName

.text:0001075C                 lea     eax, [ebp+SymbolicLinkName]

.text:0001075F                 push    eax             ; SymbolicLinkName

.text:00010760                 call    ds:IoCreateSymbolicLink					//创建符号链接				

.text:00010766                 mov     esi, eax

.text:00010768                 test    esi, esi

.text:0001076A                 jge     short loc_10779						//创建成功就跳过去

.text:0001076C                 push    [ebp+DeviceObject] ; DeviceObject

.text:0001076F                 call    ds:IoDeleteDevice					//创建失败则删除设备

.text:00010775                 mov     eax, esi

.text:00010777                 jmp     short loc_10780

.text:00010779 ; ---------------------------------------------------------------------------

.text:00010779

.text:00010779 loc_10779:                              ; CODE XREF: sub_106F8+72j

.text:00010779                 call    sub_105C8

.text:0001077E                 xor     eax, eax

.text:00010780

.text:00010780 loc_10780:                              ; CODE XREF: sub_106F8+53j

.text:00010780                                         ; sub_106F8+7Fj

.text:00010780                 pop     edi

.text:00010781                 pop     esi

.text:00010782                 leave

.text:00010783                 retn    8

.text:00010783 sub_106F8       endp

创建设备,创建符号链接,用于与R3层通讯。那么主要的功能实现就在  call    sub_105C8 这个函数里面了。
.text:000105C8 sub_105C8       proc near               ; CODE XREF: sub_106F8:loc_10779p

.text:000105C8                 mov     eax, ds:KeServiceDescriptorTable		//得到系统服务描述表的地址

.text:000105CD                 push    esi					

.text:000105CE                 mov     esi, [eax]				//得到表中第一个函数的位置

.text:000105D0                 add     esi, 0ACh				// 加上0xAC,表中每个函数地址占4个字节,0ac / 4 = 43

.text:000105D6                 mov     eax, [esi]				

.text:000105D8                 mov     dword_1090C, eax				//保存ssdt表中的43号函数地址到全局变量

.text:000105DD                 call    sub_104EA				//通过cr0寄存器去掉写保护

.text:000105E2                 call    ds:KeRaiseIrqlToDpcLevel			//提升中断请求等级

.text:000105E8                 mov     cl, al          ; NewIrql	

.text:000105EA                 mov     dword ptr [esi], offset MyNtCreateMutant	//SSDT HOOK 43号函数

.text:000105F0                 call    ds:KfLowerIrql

.text:000105F6                 sti						//恢复cr0

.text:000105F7                 push    eax

.text:000105F8                 mov     eax, dword_10908

.text:000105FD                 mov     cr0, eax

.text:00010600                 pop     eax

.text:00010601                 xor     eax, eax

.text:00010603                 inc     eax

.text:00010604                 pop     esi

.text:00010605                 retn

.text:00010605 sub_105C8       endp

通过上面可以看出驱动仅仅是对 SSDT表中的43号函数进行了hook。

用XT看了一下43号 即:NtCreateMutant函数。
而用户层的CreateMetux函数最终调用的就是内核层的NtCreateMutant。
也就是说 DNF判断游戏双开的方法之一就是互斥体。
最后再来看看HOOK函数中是怎样处理的。
.text:00010574                 mov     edi, edi

.text:00010576                 push    ebp

.text:00010577                 mov     ebp, esp

.text:00010579                 push    ecx

.text:0001057A                 push    ecx

.text:0001057B                 push    esi

.text:0001057C                 push    offset SourceString 		; sourceString = "\\basenamedobjects\\dbefeuate_ccen_khxfor"...

.text:00010581                 lea     eax, [ebp-4] 			; 局部变量1

.text:00010584                 push    eax             			; DestinationString

.text:00010585                 call    ds:RtlInitUnicodeString 		; 初始化一个内容为\\basenamedobjects\\dbefeuate_ccen_khxfor的PUNICODE_STRING到局部变量1

.text:0001058B                 mov     esi, [ebp-8] 			; 局部变量2

.text:0001058E                 test    esi, esi

.text:00010590                 jz      short loc_105A5 			; 如果局部变量2为0,跳

.text:00010592                 push    0               			; 不区分大小写

.text:00010594                 push    dword ptr [esi+8] ; String2	; 

.text:00010597                 lea     eax, [ebp-8] 			; 局部变量2

.text:0001059A                 push    eax             			; String1

.text:0001059B                 call    ds:RtlEqualUnicodeString		;变量2 与参数3中的Unicode_String比较

.text:000105A1                 test    al, al	

.text:000105A3                 jnz     short loc_105BB 			; 如果两个字符串相等  函数直接返回

.text:000105A5

.text:000105A5 loc_105A5:                              			; 如果字符串不相等

.text:000105A5                 push    [ebp+arg_C]

.text:000105A8                 mov     eax, dword_1090C			;dword_1090C 保存真实的NtCreateMutant函数地址

.text:000105AD                 push    esi

.text:000105AE                 push    [ebp+arg_4]

.text:000105B1                 mov     dword_10910, eax

.text:000105B6                 push    [ebp+arg_0]

.text:000105B9                 call    eax ; dword_1090C		;调用真实的NtCreateMutant

.text:000105BB

.text:000105BB loc_105BB:                              ; CODE XREF: sub_10574+2Fj

.text:000105BB                 xor     eax, eax

.text:000105BD                 pop     esi

.text:000105BE                 leave

.text:000105BF                 retn    10h

.text:000105BF sub_10574       endp


到这里之后可以看出在 hook NtCreateMutant的 处理函数当中 进行了判断。
NtCreateMutant的原型为:
NTSTATUS
NtCreateMutant (
    __out PHANDLE MutantHandle,
    __in ACCESS_MASK DesiredAccess,
    __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
    __in BOOLEAN InitialOwner
    )
typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;        // Points to type SECURITY_DESCRIPTOR
    PVOID SecurityQualityOfService;  // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
参数3中的UNICODE_STRING进行对比,如果值为\\basenamedobjects\\dbefeuate_ccen_khxfor,那么NtCreateMetux返回失败。
另外,根据双开工具上面的 隐藏游戏窗口功能 可以看出DNF双开限制是 通过互斥体 跟 FindWindow()来进行判断。

上传的附件 del.jpg
43.jpg
superecBGDXi.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功