最近打开一个游戏,发现该游戏不断调用SetCursorPos这个函数,所以想着在内核中PASS掉它,经过两周断断续续的研究,终于在今晚成功PASS掉它。
经过两周断断续续的查资料,看视频,分析,终于达到目的。
关于鼠标的处理:
获得鼠标的位置函数GetCursorPos,设置鼠标的位置SetCursorPos
获得暂且不分析,这里只说SetCursorPos,其实这两个也是异曲同工。
SetCursorPos的调用过程: User32!SetCursorPos-->User32!NtUserCallTwoParam-->win32k!NtUserCallTwoParam
通过反汇编可以看出来调用NtUserCallTwoParam这个函数时候 根据第一个参数的不同而区分不同的函数
SetCursorPos的参数正是74h,另外两个参数随机,只需要判断该参数是74h就知道是SetCursorPos在调用
关于win32k!NtUserCallTwoParam的HOOK,该函数是ShadowSSDT表中的函数
在程序中想获得ShadowSSDT就必须先附加一个进程 它的结构和SSDT相同但是不是导出的
1.所以先获得ShadowSSDT表地址:
<1>声明SSDT表结构
typedef struct _SDT_ENTRY //SSDT
{
PVOID *ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfServices;
PUCHAR ParamTableBase;
}SDT_ENTRY, *PSDT_ENTRY;
<2>声明一个SSDT结构的变量
EXTERN_C SDT_ENTRY *KeServiceDescriptorTable;
<3>要获得ShadowSSDT表的地址,必须得通过特征码定义,IDA中可以看出KeRemoveSystemServiceTable该函数是导出的,
在该函数中有它的地址,然后通过特征码定位到,这个即为ShadowSSDT的地址。
具体代码:
PVOID GetShadowSSDTAddr()
{
ULONG uKeRemoveSystemServiceTable = (ULONG)KeRemoveSystemServiceTable;
PUCHAR p = NULL;
PVOID uKeServiceDescriptionShadowTableAddr = NULL;
for (int i = 0; i < 50; i++, uKeRemoveSystemServiceTable++)
{
p = (PUCHAR)*(PULONG)uKeRemoveSystemServiceTable;
if (MmIsAddressValid(p) && MmIsAddressValid(p + sizeof(SDT_ENTRY) - 1))
{
__try
{
if (memcmp(p, KeServiceDescriptorTable, sizeof(SDT_ENTRY)) == 0)
{
if (p == (PUCHAR)KeServiceDescriptorTable)
{
continue;
}
uKeServiceDescriptionShadowTableAddr = p;
return (PVOID)p;
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return NULL;
}
}
}
if (uKeServiceDescriptionShadowTableAddr == NULL)
{
KdPrint(("获取ShadowSSDT 地址失败"));
}
return NULL;
}
2.关于附加进程:
要附加进程得先获得某个进程的EPROCESS,据查看csrss.exe该进程在WIN7 32系统里面长存,可以获得该进程的EPRCESS来进行附加
要获得csrss.exe进程,首先需要遍历进程,我采用的是ZwQuerySystemInformation来遍历,通过获得进程结构体,来获得进程名
就可以来判断获得的进程是不是csrss.exe,直到遍历得到csrss.exe退出循环。
得到目标进程的结构体后 里面有ProcessId表示进程标识PID ,通过PID可以调用NtOpenProcess来获得进程句柄
然后就可以通过进程句柄调用ObReferenceObjectByHandle函数来获得EPROCESS结构
最后通过KeStackAttachProcess来附加进程 到这里进程就附加成功了
具体代码如下..
PRKAPC_STATE AttachToProcess()
{
//1.get csrss.exe eprocess
UNICODE_STRING targetProcessName;
int recount = 1;
int checkProcess = 0;
PSYSTEM_PROCESS_INFORMATION pInfo = NULL;
PVOID pBuffer = NULL;
ULONG rtnLen = 0;
ULONG dwSize = 1000 * sizeof(SYSTEM_PROCESS_INFORMATION);
NTSTATUS status = STATUS_SUCCESS;
PRKAPC_STATE ApcState = NULL;
RtlInitUnicodeString(&targetProcessName, L"csrss.exe");
do
{
if (pBuffer != NULL)
{
ExFreePoolWithTag(pBuffer, '0123');
pBuffer = NULL;
}
pBuffer = ExAllocatePoolWithTag(PagedPool, dwSize, '0123');
if (pBuffer == NULL)
{
KdPrint(("分配内存空间失败\n"));
return NULL;
}
status = ZwQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &rtnLen);
if (status == STATUS_INFO_LENGTH_MISMATCH)
{
KdPrint(("缓冲区大小不足"));
dwSize = 2 * dwSize;
KdPrint(("重新分配大小%d次\n",recount++));
continue;
}
} while (status == STATUS_INFO_LENGTH_MISMATCH && dwSize<1000000);
if (status == STATUS_SUCCESS)
{
pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
do
{
if (RtlCompareUnicodeString(&targetProcessName, &pInfo->ImageName, TRUE) == 0)
{
KdPrint(("找到目标进程\n"));
KdPrint(("目标进程ID%d\n", pInfo->ProcessId));
HANDLE hProcess = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientId;
ClientId.UniqueProcess = pInfo->ProcessId;
ClientId.UniqueThread = 0;
InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
//通过进程ID获得进程句柄
status = NtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &ObjectAttributes, &ClientId);
if (status == STATUS_SUCCESS)
{
KdPrint(("获取进程句柄成功\n"));
//通过进程句柄获得EPROCESS
PEPROCESS pEprocess = NULL;
status = ObReferenceObjectByHandle(hProcess, PROCESS_ALL_ACCESS, NULL, KernelMode, (PVOID*)&pEprocess, NULL);
if (status == STATUS_SUCCESS && pEprocess != NULL)
{
KdPrint(("获取进程EPROCESS成功\n"));
//附加进程
if (checkProcess == 1)
{
ApcState = (PRKAPC_STATE)ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC_STATE), 'APC');
if (ApcState)
{
KeStackAttachProcess(pEprocess, ApcState);
}
}
//释放
ObDereferenceObject(pEprocess);
}
//关闭句柄
ZwClose(hProcess);
}
if (checkProcess == 1)
{
break;
}
checkProcess++;
}
//KdPrint(("%wZ\n", pInfo->ImageName));
pInfo = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pInfo + pInfo->NextEntryOffset);
} while (pInfo->NextEntryOffset);
}
else
{
KdPrint(("获取进程信息失败"));
}
if (pBuffer != NULL)
{
ExFreePoolWithTag(pBuffer, '0123');
pBuffer = NULL;
}
return ApcState;
}
3.HookNtUserCallTwoParam来达到一些不可告人的目的
有了ShadowSSDT表结构,那么就可以获得tUserCallTwoParam的地址,然后就可以在该函数头部做跳转,
在我们的中继函数中做判断,如果是某个进程调用,那么就直接返回它,不执行,而且调用这个函数的第一个
参数也必须是74h,这样判断之后就可以了。
中继函数代码:
__declspec(naked) void FuckNtUserCallTwoParam()
{
__asm
{
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
cmp dword ptr [esp + 0x10], 0x74
jnz NOTFIND
pushad
pushfd
call IsGame
mov g_bGameProcessAccess, al
popfd
popad
cmp g_bGameProcessAccess,1
jnz NOTFIND
pop ebp
ret 0x14
NOTFIND:
jmp NtUserCallTwoParamRetAddr
}
}
BOOLEAN IsGame()
{
PEPROCESS pCurrentProcess = IoGetCurrentProcess();
PUCHAR pszCurName = PsGetProcessImageFileName(pCurrentProcess);
if (strstr("bluem2_ysmir2_cd.dat", (const char*)pszCurName) != NULL)
{
//KdPrint(("来自目标进程的调用\n"));
return TRUE;
}
return FALSE;
}
我的目的是为了让某一个进程失去使用SetCursorPos的权利,那么通过上面的说明,你也可以让某一个进程失去使用任何
ShadowSSDT函数或者各种获得移动鼠标键盘函数的权利,这就是内核编程的强大,但也只是冰山一角,学海无涯,加油。