首页
论坛
专栏
课程

[调试逆向] [系统底层] [商业保护] 一种可能被用于EAC保护的检测调试器方案

2019-2-25 00:47 3570

[调试逆向] [系统底层] [商业保护] 一种可能被用于EAC保护的检测调试器方案

2019-2-25 00:47
3570


测试环境:

Windows7 x64 虚拟机 win10 1809 x64 虚拟机

0x0 起因

  小伙伴 @DBDig 最近也在玩APEX这个游戏,因为这个游戏有EAC保护所以有反调试,遂准备安排它。R3降权pass以后,下断没反应还又卡死又崩溃的。于是他自写了一个exe来模拟调试器的行为,万万没想到,EAC竟然对我们的exe做了这种事!他居然hook了我们的 WaitForDebugEvent这个函数,



头部 retn 10 简单粗暴,怪不得啥都没反应,都收不到调试事件了还调试个锤子?据实测,CE、x64dbg均被同样处理了。且只有游戏被调试了才会被处理,如果就开在那里则不会被处理。可以排除是定时扫进程这种操作了。

问题来了,EAC是怎么找到我们的调试器进程呢?甚至连“自写调试器”也不能幸免?

0x1 猜测

  喜闻乐见的瞎蒙时间。被调试程序和调试器之间一定存在着某种神秘的联系,才能让EAC顺藤摸瓜找到我们的调试器。首先想到的就是父进程,父进程是一种手段,然而在调戏游戏的时候大多数采用附加而没有多少机会直接使用调试器启动游戏,因此父进程被排除了。

既然我们从模拟调试器的行为上发现了游戏对我们的程序做了手脚,就继续从这方面入手吧。于是我找到了这个函数:DebugActiveProcess。翻一下msdn,该函数定义如下:


  

调用完这个函数以后发现它做了一件很重要的事情,那就是创建了一个DEBUG_OBJECT对象。

实际上DEBUG_OBJECT这个东西实际上是由NtCreateDebugObject这个函数创建的。那么DEBUG_OBJECT究竟是什么,我们挂上windbg来找找看吧。

uf nt!NtCreateDebugObject


  

所以这个“DbgkDebugObjectType”就是我们要找的东西了。

dq fffff800`04057f40

dt _OBJECT_TYPE fffffa80`18dd8080

dt _OBJECT_TYPE_INITIALIZER 0xfffffa80`18dd8080

0x3 实验

  那么,我们大概了解DEBUG_OBJECT了。经常和游戏保护打交道的同学应该知道,某P保护有一个名为“DebugPort清零”的行为,实际上是干了“ValidAccessMask”。所以,DebugPort里面存了什么?就是我们的DEBUG_OBJECT了。

  打开一个记事本程序,使用原版OD附加,然后:

dt _eprocess fffffa8018fd7b30 -y DebugPort

哦豁,发现DebugPort并不是null(ptr)。

  其实,不止这里有这个DEBUG_OBJECT,在每条线程的TEB里面也存了这个东西:

就在这个DbgSsReserved里面

到这里只能说明“我被调试了”,但是不能说明“谁调试了我”。至此,想要从DebugPort定位调试器这条路好像走不通了。

前文已经说了,调试器和被调试程序存在某种必要的联系,要想看清楚其中的关键,还得从调试流程入手。

所以我们来看下喜闻乐见的csrss:

DebugActiveProcess -> CsrClientCallServer

所以调试器使用这个方式向CSRSS请求DebugProcess服务,然后呢,有这样一个结构叫做: CSR_PROCESS, 这个结构中包含了调试器进程的进程ID和被调试程序的进程ID。然而,这个结构是没有导出的,以下是定义:

typedef struct _CSR_PROCESS

{

 CLIENT_ID          ClientId;

 struct _LIST_ENTRY ListLink;

 struct _LIST_ENTRY ThreadList;

 struct _CSR_NT_SESSION* NtSession;

 ULONG ExpectedVersion;

 void* ClientPort;

 char* ClientViewBase;

 char* ClientViewBounds;

 void* ProcessHandle;

 ULONG SequenceNumber;

 ULONG Flags;

 ULONG DebugFlags;

 ULONG ReferenceCount;

 ULONG ProcessGroupId;

 ULONG ProcessGroupSequence;

 ULONG fVDM;

 ULONG ThreadCount;

 ULONG LastMessageSequence;

 ULONG NumOutstandingMessages;

 ULONG ShutdownLevel;

 ULONG ShutdownFlags;

 LUID  Luid;

 void* ServerDllPerProcessData[1];

} CSR_PROCESS, *PCSR_PROCESS;

       所以我的猜测就是EAC定位到了这个存储这个结构的链表,然后从里面找到了调试器进程。

以上猜测已被证伪。

0x4 结论

       实际上,EAC仅仅通过一个Obcall就取到了调试进程的信息,当调试器附加时,CreateRemoteThread创建的线程句柄会被Obcall捕获,从而检测到调试器,至于从csrss里面爆搜结构特征,然后定位链表再枚举,这种使用未公开结构的方法EAC还没有做到那么高深的地步,是我们想多了。

参考资料

《软件调试》 张银奎

 MSDN

 WRK

 ReactOS

  
代码见:

附上一份素材,见附件。
虽然BDBig基于愧疚心里,删除了他的贴子仅仅留下代码,然而我们的 山总 还在疯狂的骂他,所以我觉得,还是应该让这份记录永久保留。
 


[招聘]欢迎市场人员加入看雪学院团队!

最后于 2019-3-6 04:59 被黑洛编辑 ,原因:
上传的附件:
打赏 + 20.00
打赏次数 1 金额 + 20.00
收起 
赞赏  aabiaobiao   +20.00 2019/02/25
最新回复 (10)
黑洛 1 2019-2-25 02:09
2
0
然而,书上(包括ReatOS)中提到的CsrClientCallServer这个函数,在win7及以上的调试流程中并没有被使用,CsrDebugProcess这个函数并没有在csrsrv.dll中导出,所以DebugActiveProcess -> CsrClientCallServer 这个流程在win7及以上系统是不成立的。
hzqst 3 2019-2-25 07:18
3
1
遍历所有进程的句柄表就能找到谁拥有这个调试句柄 何必搞辣么麻烦
又出bug了 2 2019-3-5 15:26
4
0
伙伴 @DBDig 最近也在玩APEX这个游戏,因为这个游戏有EAC保护所以有反调试,遂准备安排它。R3降权pass以后,下断没反应还又卡死又崩溃的。于是他自写了一个exe来模拟调试器的行为,万万没想到,EAC竟然对我们的exe做了这种事!他居然hook了我们的 WaitForDebugEvent这个函数
====
具体表现是不是:
1. 你下个硬件断点,这个地方可以断下来,再按下F8或者F7直接崩溃了
2. 你在KiUserDispatcherException 下个硬件断点,然后发现游戏竟然把按F8或者F7导致的异常又丢给你了,你再按下F9,游戏就崩溃了
最后于 2019-3-5 15:27 被又出bug了编辑 ,原因:
黑洛 1 2019-3-5 16:25
5
0
又出bug了 伙伴 @DBDig 最近也在玩APEX这个游戏,因为这个游戏有EAC保护所以有反调试,遂准备安排它。R3降权pass以后,下断没反应还又卡死又崩溃的。于是他自写了一 ...
没有,没那么恶心。他没有搞异常流程,其实是我们想太多了。已经找到了原因,晚上会把这个帖子补充完。不过以后就说不好啦。
黑洛 1 2019-3-5 17:04
6
0
又出bug了 伙伴 @DBDig 最近也在玩APEX这个游戏,因为这个游戏有EAC保护所以有反调试,遂准备安排它。R3降权pass以后,下断没反应还又卡死又崩溃的。于是他自写了一 ...
实际上,他用一个ob,就拿到了调试器,然后对调试器进行了补丁, 和tp那种暂停调试器线程的方法有异曲同工之妙。除此之外,就是两个调试函数C3和jmp ExitProcess,这应该是学习的themida,一模一样。至于动异常处理这个,应该还没有做到那一步。
目前我见过有对异常处理流程动手脚的,第一个就是tp,第二个就是11平台,后者比前者早了很多年。
又出bug了 2 2019-3-5 21:46
7
0
黑洛 实际上,他用一个ob,就拿到了调试器,然后对调试器进行了补丁, 和tp那种暂停调试器线程的方法有异曲同工之妙。除此之外,就是两个调试函数C3和jmp ExitProcess,这应该是学习的themid ...
黑洛 1 2019-3-6 04:03
8
0
已更新
又出bug了 2 2019-3-6 18:14
10
0
黑洛 已更新
看了下代码核心就是:
1. 暂停掉8个内核线程(最近我看下,就8个内核线程,不知道创建8个干嘛,Be只有一个)
2. 当游戏关闭卸载驱动,恢复掉8个内核线程
===
去年我第一次接触这个玩意儿就是这样干的,长时间调试游戏很不稳定
最后于 2019-3-6 18:15 被又出bug了编辑 ,原因:
黑洛 1 2019-3-6 18:19
11
0
又出bug了 黑洛 已更新 看了下代码核心就是:1. 暂停掉8个内核线程(最近我看下,就8个内核线程,不知道创建8个干嘛,Be只有一个)2.& ...
实际上流程很简单的 1.打开进程和线程的时候写入权限 2.线程暂停 
回调不能直接摘,会蓝
游客
登录 | 注册 方可回帖
返回