学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1157

主题

[转载图文] LOL过检测技術

发表于 2020-6-10 11:06:33 | 查看: 7332| 回复: 3
                                              1.稳定注入姿势
2.处理TenRpcs检测CLL
3.处理TCJ
4.探测GameRpcs
笔者最近迷上LOL,中低分段总是有脚本,被虐的很惨
然后笔者去市场上买来几款外挂,配合防封,然后小逆了一下,发现市场上的防封软件大多是通对游戏 LoadLibraryExW 挂钩子,让某些检测模块不加载,在这种情况下,阻断了通讯,这样可以稳定的玩游戏5局左右,之后就会封7天或者三年,如果处理了TenRpcs里的检测CALL(技能释放一类的)就是7天,如果没有处理就是3年,然后经过笔者多处咨询,发现市面上没有能稳超过10把的防封,于是笔者想看看TP到底是什么手段检测的,于是我与LOL迂回了半月之久,废话不多说,进入正题
  1.注入姿势:
笔者采用 MDL内存映射形式向游戏进程写入了我的DLLShellCode ,用 NtCreateThreadEx 创建远程线程,游戏未发现,目前这个注入姿势是稳定的
  2.处理检测CALL:
笔者在游戏主模块处发现游戏挂了70个钩子指向TenRpcs.dll,不解释,盘他
BOOL RecoveryCode(ULONG32 地址, BYTE* 字节)
{
    BYTE 判断 = { NULL };
    DWORD 基址 = 0x400000;
    DWORD 修改前的页面保护属性 = NULL;
 
    RtlCopyMemory(&判断, LPVOID(基址 + 地址), 1);
 
    if (判断 != 0xE8)
    {
        全局变量::当前大区未开放检测数++;
        return FALSE;
    }
 
    if (VirtualProtect(LPVOID(基址 + 地址), 5, PAGE_EXECUTE_READWRITE, &修改前的页面保护属性))
    {
        RtlCopyMemory(LPVOID(基址 + 地址), 字节, 5);
        if (VirtualProtect(LPVOID(基址 + 地址), 5, PAGE_EXECUTE, &修改前的页面保护属性))
        {
            全局变量::处理检测成功次数++; //std::cout << "√ " << hex << 基址 + 地址 << std::endl;
            return TRUE;
        }
    }
 
    全局变量::处理检测失败次数++; //std::cout << "X " << hex << GetLastError() << std::endl;
 
    return FALSE;
}
 
VOID 处理检测()
{
    LPVOID 内存地址 = NULL;
    DWORD 函数地址 = NULL;
    DWORD 模块句柄 = NULL;
    DWORD 修改前的页面保护属性 = NULL;
 
    do 
    {
        窗口句柄::游戏窗口句柄 = FindWindow("RiotWindowClass", NULL);
    } while (!窗口句柄::游戏窗口句柄);
 
    ///此处过检测代码由于危害腾讯安全给告知于19.02.05 21:30分删除此部分代码
 
    string Message = "处理失败 " + to_string(全局变量::处理检测失败次数) + " 个检测点\n成功处理 " + to_string(全局变量::处理检测成功次数) + " 个检测点\n当前大区未开放的检测点有 " + to_string(全局变量::当前大区未开放检测数) + " 个\n\n是否继续游戏???";
 
    if (MessageBox(窗口句柄::游戏窗口句柄, Message.c_str(), "", MB_ICONQUESTION | MB_YESNO | MB_TOPMOST) != IDYES)
        exit(0);
}
  到这里的时候,笔者进行了游戏,发现封三年,于是笔者不开挂,只干检测一样被封三年,说明了什么
  对!没错 CRC!不得不过 TP 检测系统的CRC有漏洞,过CRC代码如下,你会惊呆的
VirtualProtect(LPVOID(0x401000), 0x1028000, PAGE_EXECUTE, &修改前的页面保护属性)
  哈哈,就是这么简单!这个为什么!咱就不多解释了,不是傻瓜都看得懂原理,我直接贴代码通俗易懂!
  通过以上小操作后,单挂我自己的防封没有三年了,然后开始下一项工作,TCJ!
  TCJ是干嘛的?在这里我科普一下,他会 EnumWindowProc 枚举你所有窗口,OpenProcess ReadFile… 太多了
  总是TCJ负责一些外部检测以及内部检测,包括你注入的DLL文件,MD5哈希,窗口标题,类名,PE头,等等 … 太多了
  所以,想要完全达到防封状态,只干掉检测CALL还不行,必须要处理TCJ
  于是我模仿了市场外挂 向 LoadLibraryExW 挂钩子,让TCJ在加载列表里消失,游戏不加载TCJ了,进入游戏爽
  但是没爽多久5分钟后游戏弹出错误窗口,强制关闭,于是笔者调试跟踪到执行退出函数的点,发现在GameRpcs里,哇,还有这个东西,我都没注意!
  于是分析了一下,这个GameRpcs非常不起眼,但是他起到重要的作用,他负责与TCJ和TenRpcs通讯!如果其中有一个模块有异常,他就会强制关闭游戏(本地命令)不是来自服务器的断开链接!而且通过笔者的CE遍历,遍历到了GameRpcs还会收集对局信息,包括当前玩家按下执行右键命令次数,玩家按下技能右键次数,等等信息,大家可以通过CE去搜索增加的值就可以,说到这里,我感觉这个检测系统好强大,收集这些数据是这是要人工分析吗??
  笔者后续再写是怎么过的GameRpcs,先去看春晚了,大家新年快乐!
  回顾一下前文提到的东西
游戏主要检测分为5大块
  1.CALL检测 ->由TenRpcs负责
2.文件及内存检测 - > 由TCJ负责
3.客户行为游戏数据检测以及本地封包加密解密发送回传 - > 由GameRpcs负责
4.本地机器码查询 - > 由PolicyProbe负责
5.调试游戏等保护 -> 由TerSafe负责
我们需要处理的东西是1.2.3
机器码解决办法是 老办法 钩住 LoadLibraryExW 让  PolicyProbe 不在载入列表里即可过掉机器码检测
TerSafe 我们不做处理,没有必要
  接下来我们编写一个注入到游戏大厅的DLL,然后转换成ShellCode,为了安全起见,先要把大厅的保护干掉
代码如下
VOID 干游戏大厅检测() //稳妥起见还是要干掉把
{
    INT 判断计次 = NULL;
    BYTE 判断字节 = NULL;
    DWORD 临时变量 = NULL;
    DWORD 修改前的页面保护属性 = NULL;
 
    ULONG32 模块地址 = NULL;
 
    do
    {
        模块地址 = ULONG32(GetModuleHandle("TenRpcs.dll"));
    } while (!模块地址);
 
    do
    {
        RtlCopyMemory(&判断字节, LPVOID(模块地址 + /*此处地址屏蔽*/), sizeof(判断字节));
    } while (判断字节 != 0x68);
 
    临时变量 = /*此处地址屏蔽*/;
    BYTE TenRpcs检测点1[] = { 0xE9, 0x60, 0x06, 0x00, 0x00, };
    if (VirtualProtect(LPVOID(模块地址 + 临时变量), sizeof(TenRpcs检测点1), PAGE_EXECUTE_READWRITE, &修改前的页面保护属性))
    {
        RtlCopyMemory(LPVOID(模块地址 + 临时变量), &TenRpcs检测点1, sizeof(TenRpcs检测点1));
 
        if (VirtualProtect(LPVOID(模块地址 + 临时变量), sizeof(TenRpcs检测点1), PAGE_EXECUTE, &修改前的页面保护属性))
        {
            RtlZeroMemory(TenRpcs检测点1, sizeof(TenRpcs检测点1));
        }
    }
 
    CloseHandle(保护检测线程句柄);
}
  干掉这个检测点之后 游戏大厅的保护系统就崩溃了,包括TerSafe,所有检测不会初始化,不知道大厅检测什么,为了安全起见还是干掉把
一定要在第一时间注入,否则保护初始化完成就没机会了
VOID 等待游戏()
{
    INT 对局计次 = 1;
    HWND 窗口句柄 = NULL;
    DWORD 游戏进程ID = NULL;
    ULONG32 模块句柄 = NULL;
    INT 文字颜色[4] = { FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY };
 
    do
    {
        Sleep(100);
        窗口句柄 = FindWindow("RCLIENT", "League of Legends");
    } while (!窗口句柄);
 
    AllocConsole();
    srand((int)time(0));
    freopen("conout$", "w", stdout);
    CONSOLE_CURSOR_INFO Cursor_info = { 1, 0 };
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 文字颜色[rand() % 3 + 1]);
    //DeleteMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_BYCOMMAND);
 
JMP1:
 
    std::cout << std::endl;
    std::cout << "[+] 正在等待游戏对局中" << std::endl;
 
    do
    {
        Sleep(10);
        游戏进程ID = GetPID("League of Legends.exe");
    } while (!游戏进程ID);
 
    if (内存注入远程线程DLL(游戏进程ID, hexData1, sizeof(hexData1)))
    {
        if (内存注入远程线程DLL(游戏进程ID, hexData2, sizeof(hexData2)))
            std::cout << "[+] 第" << 对局计次 << "场游戏对局已开始" << std::endl;
    }
 
    do
    {
        Sleep(100);
        游戏进程ID = GetPID("League of Legends.exe");
    } while (游戏进程ID);
 
    对局计次++;
 
    goto JMP1;
}
  注入之后执行我们的循环代码,判断游戏是否启动

LOL过检测技術

LOL过检测技術

启动后第一时间注入我们的 ShellCode 并执行
DLL转ShellCode用010Editor即可
  初步尝试1:抹掉TCJ
TCJ模块加载失败后,游戏正常运行5分钟后自动退出,上一篇文章说到是GameRpcs的问题然后尝试方案2
初步尝试2:抹掉GameRpcs+TCJ
正常游戏,不会掉线,大概打了3-4局之后,被服务器断开链接,封7天
初步尝试3:正常加载 GameRpcs+TCJ 在TCJ线程入口处返回Error
游戏正常运行,7分钟左右掉线,打完一局掉5次线,和尝试1没有太大区别,检测处理0状态,笔者难受的打了5局,不会封7天也不会封三年,但是太难受
再次尝试4:改TCJ线程里的Sleep,Push 64 Push1470 等这些参数
游戏正常运行,不会掉线,三年,说明了还是检测到了外挂,于是我把参数范围扩大到四字节,Sleep(1000000000)例如这样,笔者脑子坏掉了,开始这样做的时候想没有通讯肯定会7天,事实证明了我脑子进水了,的确是7天,没有必要做这个举动
到这里,我静下心来想,有没有一个完美的办法,既能让TCJ检测不到外挂特征,又能正常的与GameRpcs通讯呢
可行的办法1. 钩住所有TCJ调用的重要的Fun包括

LOL过检测技術

LOL过检测技術

  笔者检查了一下,他的导入全都是外挂检测相关,如果要一个一个去处理,那我的工作量太大了,没有半个月一个月弄不完
然后又想有没有什么简单的方法,想想想了一晚上,最后想出了一个办法,预知结果如何,下次分解,今天先讲到这里
  代码段没法发了,因为里面有别人的东西(发了之后避免不了一些不法分子会利用,所以这里只分享处理检测流程和思路)
目前整体已经完善好了,写这个帖子不是教大家怎样去过检测写外挂,而是教大家一些好的思路
核心代码执行流程

LOL过检测技術

LOL过检测技術

1.过机器码: 目前2种办法,一种替换DLL,另一种是让返回值随机,因为我总调试游戏
我直接替换了DLL因为有时候进游戏没加载我忘记加载我的DLL,就完蛋了,如果不商业的话,建议用替换DLL方法,放到Game目录即可,DLL在附件中
  2.处理游戏检测CALL,例如释放技能,走路,等等,已经定位了几个重要的检测CALL,处理后发现还有很多检测CALL,但是不知道再检测什么东西,所以全盘了,如果出问题再注释,也方便,这个在第一篇说过了,这里不再细说
  3.处理GameRpcs的CRC,处理CRC的思路也很多,这里我用的是转移大法,然后再创建一个线程循环清除GameRpcs记录的数据信息
VOID 清除记录()
{
    DWORD 计次变量 = 0;
 
    while (GameRpcsBase)
    {
        Sleep(10000);
        RtlCopyMemory(LPVOID(GameRpcsBase + 0x1713A4), &计次变量, sizeof(DWORD));//时间计时
        RtlCopyMemory(LPVOID(GameRpcsBase + 0x17141C), &计次变量, sizeof(DWORD));//鼠标计次
        RtlCopyMemory(LPVOID(GameRpcsBase + 0x171424), &计次变量, sizeof(DWORD));//键盘计次
        计次变量++;
    }
}
  TCJ(可以不清Buffer,直接改Buffer指向一片干净的内存区域)
监视TCJ读了哪块内存,HOOK,如果当前读内存的地址 大于 你要保护的模块地址 或 小于 你要保护的模块地址 + 模块大小,则清空他的Buffer

LOL过检测技術

LOL过检测技術

HOOK FindWindow 或者写个 EnumWindowsProc 还有要HOOK CreateFile 做好外部检测防御,其实就这些了,到结帖,已经过掉全部检测,其实就这些了
算上三局教程 10局 突然觉得开挂没有意思了,没继续打了,过几天研究研究 apex英雄  再次感谢 DebugSp 大神提供的思路


温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
已有 1 人评分学币 理由
坑比 + 1 感谢您的作品,赞一个!

总评分: 学币 + 1   查看全部评分

论坛交流群:672619046
发表于 2020-6-10 11:38:06
虽然看不懂,但是很牛皮
发表于 2020-6-10 16:53:34
坑比 发表于 2020-6-10 11:38
虽然看不懂,但是很牛皮

    发表于 2020-9-3 18:46:48
    搬运别人请附上原链接

    小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

    GMT+8, 2024-4-25 09:22 , Processed in 0.096706 second(s), 55 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表