搜尋


返回清單
切換到指定樓層
通知這文章過時或找檔案 發表主題

[其他] 【內核技術】遊戲公司TP偵測,阻止了玩家「遊戲多開」,該如何解決?

[複製連結]
SheepKingCN ( Lv.80 論壇達人 ) 發表於 2013-9-2 03:01:36 | 顯示全部樓層 |閱讀模式
本文章最後由 SheepKingCN 於 2013-9-2 04:27 編輯
  1. 此為本人無聊學習、備份文章用,大神請無視我SheepKingCN
複製代碼
TP多开限制分析
作者:Tiany
QQ:304400230
花了10多天 研究过XX的多开 研究分析出一些东西 可是也被卡住了 遇到瓶颈 搞的郁闷了 把我分析到的东西和大家分享一下吧

大家都知道一般多开检测 都是一些互斥体 信号量 这些内核对象 当然TX也不例外 他也会对这些对象有检测  当然还有其他的检测 但是关键的代码都被VM了  所以想逆的难度就相当的大(对我而言) 所以我们必须要想些其他的方法



一般而言这些内核对象为了要个多个进程访问 所以都有Name 因为我们不好断定程序使用了1个或几个内核对象 来对比判断 所以这里我用的方法是 Inline Hook ZwCreateMutex ZwCreateEvent
ZwCreateSection  ZwCreateSemaphore .... 这些函数 检测一下 如果Name 不为空的话 我们都让他的名字改变一下 达到每个进程创建的内核对象都变形

这里为什么要HOOK Zw开头的函数 我解释一下  因为CreateMutexA 调用 CreateMutexW 然后调用 ntdll.dll-ZwCreateMutex 函数 继续就到内核 nt!ZwCreateMutexA
(ntdll.dll-ZwCreateMutex 和 ntdll.dll-NtCreateMutex) 的导出地址是一样的


代码:
  1. HMODULE hNTDLL = LoadLibrary("ntdll.dll");
  2.   if (!hNTDLL)
  3.   {
  4.     OutputDebugStringA("[TEN] [DLL] 加载 ntdll.dll 失败\n");
  5.     return TRUE;
  6.   }

  7. //NtCreateEvent
  8. InlineHook((__pfnNtCreateEvent)(LPVOID)GetProcAddress(hNTDLL,"NtCreateEvent"),
  9.     OnNtCreateEvent,
  10.     (void **)&pfnNtCreateEvent);
複製代碼
回调函数:



代码:
  1. NTSTATUS
  2. NTAPI
  3. OnNtCreateEvent(
  4.   OUT PHANDLE             EventHandle,
  5.   IN ACCESS_MASK          DesiredAccess,
  6.   IN POBJECT_ATTRIBUTES   ObjectAttributes OPTIONAL,
  7.   IN EVENT_TYPE           EventType,
  8.   IN BOOLEAN              InitialState)
  9. {
  10.   NTSTATUS nRet;


  11.   if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer)
  12.   {
  13.     WCHAR lpTemp[MAX_PATH];
  14.     ZeroMemory(lpTemp,MAX_PATH);
  15.     wsprintfW(lpTemp,L"[Ten] [DLL] %lS%08X",ObjectAttributes->ObjectName->Buffer,GetCurrentProcessId());
  16.     OutputDebugStringW(ObjectAttributes->ObjectName->Buffer);
  17.     pfnRtlInitUnicodeString(ObjectAttributes->ObjectName,lpTemp);
  18.   }
  19.   nRet = pfnNtCreateEvent(
  20.     EventHandle,
  21.     DesiredAccess,
  22.     ObjectAttributes,
  23.     EventType,
  24.     InitialState
  25.     );

  26.   return nRet;
  27. }
複製代碼
其他的函数都一样处理

我们看看效果





搞完这里只是基本工作 我们运行发现还是运行不要了 说明了还有其他地方检测 经过我细心的观察 发现一个内核对象很可疑





这个Event 并未被处理  他的Name 也不固定 反正重启过也都会变  可是他为什么没有进到我们的回调函数里呢? 这就是TX猥琐之处  他重新加载Ntdll.dll 然后执行创建对象

这个时候我们只能到更深层的 ntdll.KiFastSystemCall 地方去看看他是在那里调用的
(Zw函数都会 调用 ntdll.KiFastSystemCall 来跳转到内核 )




定位到这里  反复跟踪后 可以了解 他首先 打开 Ntdll.dll 然后读取数据到内存 然后开辟内存空间 来执行 创建内核对象的代码  

这里我用的方法就是 Inline Hook CreateFile 函数 判断 如果打开的是 Ntdll.dll 就让他打开我实现处理好HOOK 的 Ntdll.dllTen 文件 让他继续跳到我们的回调函数去 创建内核对象

上代码


代码:
  1. //新建Ntdll.dll
  2. BOOL CreateNtdll(LPBYTE NewCode,DWORD len)
  3. {
  4.   PBYTE szBuffer = NULL;
  5.   BYTE szSearCode[] = {0xB8,0x23,0x00,0x00,0x00,0xBA,0x00,0x03,0xFE,0x7F,0xFF,0x12,0xC2,0x14,0x00};
  6.   DWORD dwSize,lpSize;
  7.   HANDLE hFile = CreateFileA("C:\\WINDOWS\\system32\\ntdll.dll",
  8.           GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,
  9.           FILE_ATTRIBUTE_NORMAL,NULL);
  10.   if (hFile == INVALID_HANDLE_VALUE)
  11.   {
  12.     return FALSE;
  13.   }
  14.   dwSize = GetFileSize(hFile,NULL);
  15.   szBuffer = new BYTE[dwSize];
  16.   ReadFile(hFile,szBuffer,dwSize,&lpSize,NULL);
  17.   CloseHandle(hFile);

  18.   //特征码搜索
  19.   LPBYTE pCode = SearchFeature(szBuffer,dwSize,szSearCode,sizeof(szSearCode));
  20.   if (pCode == NULL)
  21.   {
  22.     delete szBuffer;
  23.     return FALSE;
  24.   }
  25.   //替换操作
  26.   memcpy(pCode,NewCode,len);

  27.   hFile = CreateFileA("C:\\WINDOWS\\system32\\ntdll.dllTen",GENERIC_WRITE,FILE_SHARE_WRITE,
  28.             NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  29.   if (hFile == INVALID_HANDLE_VALUE)
  30.   {
  31.     delete szBuffer;
  32.     return FALSE;
  33.   }

  34.   WriteFile(hFile,szBuffer,dwSize,&lpSize,NULL);
  35.   CloseHandle(hFile);
  36.   delete szBuffer;
  37.   return TRUE;
  38. }
複製代碼
CreateFile 回调函数


代码:
  1. NTSTATUS
  2. NTAPI
  3. OnNtCreateFile(
  4.     OUT PHANDLE  FileHandle,
  5.     IN ACCESS_MASK  DesiredAccess,
  6.     IN POBJECT_ATTRIBUTES  ObjectAttributes,
  7.     OUT PIO_STATUS_BLOCK  IOStatusBlock,
  8.     IN PLARGE_INTEGER  AllocationSize  OPTIONAL,
  9.     IN ULONG  FileAttributes,
  10.     IN ULONG  ShareAccess,
  11.     IN ULONG  CreateDisposition,
  12.     IN ULONG  CreateOptions,
  13.     IN PVOID  EaBuffer  OPTIONAL,
  14.     IN ULONG  EaLength)
  15. {
  16.   NTSTATUS nRet;

  17.   if (ObjectAttributes && ObjectAttributes->ObjectName && ObjectAttributes->ObjectName->Buffer)
  18.   {
  19.     if (wcsstr(ObjectAttributes->ObjectName->Buffer,L"ntdll.dll"))
  20.     {
  21.       WCHAR lpTemp[MAX_PATH];
  22.       ZeroMemory(lpTemp,MAX_PATH);
  23.       wsprintfW(lpTemp,L"%lSTen",ObjectAttributes->ObjectName->Buffer);
  24.       pfnRtlInitUnicodeString(ObjectAttributes->ObjectName,lpTemp);
  25.       OutputDebugStringW(ObjectAttributes->ObjectName->Buffer);
  26.     }
  27.   }

  28.   nRet = pfnNtCreateFile(
  29.     FileHandle,
  30.     DesiredAccess,
  31.     ObjectAttributes,
  32.     IoStatusBlock,
  33.     AllocationSize,
  34.     FileAttributes,
  35.     ShareAccess,
  36.     CreateDisposition,
  37.     CreateOptions,
  38.     EaBuffer,
  39.     EaLength
  40.     );

  41.   return nRet;
  42. }
複製代碼
经过以上的处理后  我们可以看到 内核对象很多都已经让我们 更名换姓了
当然如果不放心 也可以在ZwOpen*****  函数也进行HOOK 让他如果用打开的时候也访问到是我们创建的内核对象 处理的方法一样 可惜的是程序肯定还有其他的检测 我也尝试处理了 CreateToolhelp32Snapshot Process32First Process32Next 进程遍历的函数 也没能绕过 想不出解决的办法  故把分析的东西都公布 如果大家想一起研究或发现什么新的东西 大家可以论坛PM我 一起研究

上传的图像           









大家正在看啥


回覆 使用道具 檢舉
複製專屬你的推廣連結:發至FB與各論壇宣傳:累積點數換GP商品 & 藍鑽
每五點閱率就可以兌換藍鑽積分或遊戲點卡 夢遊推廣文章換GP商品

你需要登入後才可以回覆 登入 | 加入會員

本版積分規則

Copyright (C) 2010-2020 夢遊電玩論壇

廣告合作:請直接聯繫我們,並附上您預刊登位置的預算。  

快速回覆 返回頂端 返回清單