一. 游戏外挂有模拟类、内部Call功能调用、脱机类外挂。模拟类外挂通过模拟键盘鼠标的按键信息达到挂机的目的。内部Call功能挂往游戏进程注入dll并调用游戏功能函数,此类外挂需分析清楚主要功能函数。脱机类外挂需要还原游戏客户端协议明文,见http://bbs.pediy.com/showthread.php?t=178255,通过发送协议封包操纵游戏。
按键精灵、按键游侠、许多游戏辅助都属于模拟类外挂。下图是按键精灵的模拟技术的选择:
二.以下分别介绍上面四种模拟技术的检测方法:
1. 普通模式、增强模式:
普通模式调用SendMessage、PostMessage,增强模式调用keybd_event,mouse_event、 SendInput发送键盘鼠标消息。keybd_event,mouse_event是调用SendInput。只要HOOK 内核层的NtUserPostThreadMessage、NtUserPostMessage、NtUserMessageCall、NtUserSendInput这几个API,并在其中判断发送的目的地址是否为我们要保护的进程就行了。附件中代码使用Hook KiFastCallEntry的方式Hook NtUserPostThreadMessage、NtUserPostMessage、NtUserMessageCall、NtUserSendInput这几个内核API。
2. 软件模式:
比较常用的方式,很多游戏辅助用的就是这种方式。因为是操作类驱动中的函数,所以能同时兼容PS/2和USB的键盘鼠标模拟,被按键精灵、游戏辅助广泛使用。它直接调用KeyboardClassServiceCallback、MouseClassServiceCallback。这两个函数是Windows键盘鼠标端口驱动(Kbdhid.sys、i8042prt.sys、mouclass.sys、mouhid.sys)在获得键盘鼠标硬件输入后往上传递给类驱动(Kbdclass.sys、mouclass.sys)的两个回调函数。
如何模拟参考:看雪文章:http://bbs.pediy.com/showthread.php?t=101653。
检测方法为:HOOK键盘鼠标类驱动中KeyboardClassServiceCallback和MouseClassServiceCallback回调,进入到自己的函数中时栈回溯(ebp+4)判断调用地址是否在系统的键盘鼠标端口驱动中,若不在说明是直接调用的,也就是按键精灵模拟操作的。
若是键盘鼠标类驱动和端口驱动中有层过滤驱动(eaps2kbd),则需回溯多个栈帧(ebp+n)查找系统的键盘鼠标端口驱动。附件中代码使用Call Hook替换键盘鼠标回调。kbdclass.sys驱动中KeyboardClassServiceCallback函数的反汇编代码如下:
:00011192 ; int __stdcall KeyboardClassServiceCallback(size_t, void *, int, int)
.text:00011192 _KeyboardClassServiceCallback@16 proc near
.text:00011192 var_8 = dword ptr -8
.text:00011192 var_4 = dword ptr -4
.text:00011192 arg_0 = dword ptr 8
.text:00011192 arg_4 = dword ptr 0Ch
.text:00011192 arg_8 = dword ptr 10h
.text:00011192 arg_C = dword ptr 14h
.text:00011192
.text:00011192 mov edi, edi
.text:00011194 push ebp
.text:00011195 mov ebp, esp
.text:00011197 push ecx
.text:00011198 push ecx
.text:00011199 mov eax, [ebp+arg_0] //DeviceObject
.text:0001119C and [ebp+arg_0], 0
.text:000111A0 push ebx
.text:000111A1 mov ebx, [ebp+arg_8] //InputDataEnd
.text:000111A4 sub ebx, [ebp+arg_4] //ebx为键盘输入的数量
.text:000111A7 push esi
.text:000111A8 mov esi, [eax+28h]
.text:000111AB mov eax, [ebp+arg_C]
.text:000111AE and dword ptr [eax], 0
.text:000111B1 push edi
.text:000111B2 push 4 ; Flags
.text:000111B4 call PoSetSystemState(x) //此函数将被Call Hook替换
__declspec(naked)void MyKeyboardClassServiceCallback()
{
_asm
{
pop g_OrigRetAddr //调用被Call Hook替换的函数前,平衡堆载
call g_ulkbdNeedCallAddr //xp是PoSetSystemState函数
push eax
mov eax,[ebp+4h] //判断调用Callback的函数的地址是否在端口驱动中
cmp eax,g_ulKbdPs2PortStart
jae ps2StartOk //先判断是否为ps2端口驱动调用,
jmp short isUsbKbd //再判断是否为usb端口驱动调用
ps2StartOk:
cmp eax,g_ulKbdPs2PortEnd
jbe over
isUsbKbd:
cmp eax,g_ulKbdUsbPortStart
jae usbStartOk
jmp short isFilter
usbStartOk:
cmp eax,g_ulKbdUsbPortEnd
jbe over
isFilter: //检查是否在过滤驱动中,允许过滤驱动调用kbdCallBack(比如eaps2kbd)
push ebx
push ecx
push edx
mov ecx,20 //从ebp+18h(跳过4个传入的参数)往下检测20个堆载帧
mov al,0
mov ebx,14h
conCheck:
add ebx,4
mov edx,[ebp+ebx]
cmp edx, MmSystemRangeStart //低于内核地址(一般是0x80000000)
jb fOver
cmp edx,g_ulKbdPs2PortStart //有过滤驱动
jae _ps2StartOk
jmp short _isUsbKbd
_ps2StartOk:
cmp edx,g_ulKbdPs2PortEnd
setbe al //满足则置al为1
jbe fOver
_isUsbKbd:
cmp edx,g_ulKbdUsbPortStart
jae _usbStartOk
jmp short fLoop
_usbStartOk:
cmp edx,g_ulKbdUsbPortEnd
setbe al //满足则置al为1
jbe fOver
fLoop:
Loop conCheck
fOver:
pop edx
pop ecx
pop ebx
test al,1
jnz over
mov eax,[ebp+10h] //fake
sub eax,[ebp+0Ch]
cmp eax,ebx //真实Callback函数调用第一个函数前会把输入的数量赋值给ebx
jne cmpEcx //(其它也要判断下,以防微软代码小变动,winxp使用ebx,win7使用eax)
mov ebx,0 //把需要伪造的数量置为0
jmp short doLast
cmpEcx:
cmp eax,ecx
jne cmpEdx
mov ecx,0
jmp short doLast
cmpEdx:
cmp eax,edx
jne cmpEsi
mov edx,0
jmp short doLast
cmpEsi:
cmp eax,esi
jne cmpEdi
mov esi,0
jmp short doLast
cmpEdi:
cmp eax,edi
jne cmpArg1
mov edi,0
jmp short doLast
cmpArg1: //编译器有时会用传入的参数保存临时变量
cmp eax,[ebp+8h] //参数2和3为InputDataStart和InputDataEnd,
jne cmpArg4
mov [ebp+8],0
jmp short doLast
cmpArg4:
cmp eax,[ebp+14h]
jne doLast
mov [ebp+14h],0
jmp short doLast
doLast:
mov [ebp+8],0 //DeviceObject置为NULL
mov eax,[ebp+0Ch] //设置InputDataEnd=InputDataStart 键盘输入数量置为0
mov [ebp+10h],eax
mov m_bFake,1
over:
pop eax
push g_OrigRetAddr //返回到真实kbdCallBack Call后的地址
ret
}
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)