看流星社区

 找回密码
 注册账号
查看: 2135|回复: 0

Win7x64系统过TP的一些尝试和目前遇到的问题

[复制链接]

该用户从未签到

发表于 2017-6-1 17:26:03 | 显示全部楼层 |阅读模式
其实渣新学内核编程不过半个月时间,帖中难免有幼稚的想法和错误,遇到的问题我会用蓝色文字表述,还请各位前辈指正,感激不尽!

我把自己目前的进展和遇到的问题一起说说吧:
x64系统过TP大概分两步,首先要过双机调试,然后要过应用层调试。

1、过双机调试,这里也分两步
(1)首先要保证debug模式下启动游戏不蓝屏。

我也是第一次研究TP,对这之前的保护不了解,不过看网上所说这个启动蓝屏似乎是最近几个月新加的。
要过这个需要对内核调试引擎有一定的了解,不过还好我们是站在巨♂人♀的肩膀上,在看雪找到了篇帖子,比较详细的分析了系统启动时内核调试引擎初始化的几个标志。
TP只是检测了其中一个(待定),KdEnteredDebugger,它通过MDL映射来判断这个标志是不是True,如果是就蓝屏,解决方法我照抄了那篇帖子,直接在Hook一下IoAllocateMdl,
把判断的地址改到一个恒为False的地方这样就可以绕过了

[C++]纯文本查看复制代码



?





01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16




PMDL
newIoAllocateMdl(

__in_opt
PVOID

VirtualAddress,

__in
ULONG

Length,

__in
BOOLEAN

SecondaryBuffer,

__in
BOOLEAN

ChargeQuota,

__inout_opt
PIRP Irp OPTIONAL)

{



if

(VirtualAddress == KdEnteredDebugger)

{

//DbgPrint("[KdEnteredDebugger]
address: %p\n", KdEnteredDebugger);

VirtualAddress
= (PUCHAR)KdEnteredDebugger
+ 0x30; //据观察,+0x30
的位置恒为0

}



return

oldIoAllocateMdl(VirtualAddress, Length, SecondaryBuffer, ChargeQuota, Irp);

}









不过这样做带来一个问题,蓝屏是不蓝屏了,TP的驱动模块也能加载,但只能启动登陆客户端Client.exe,在登陆后启动DNF.exe时TP会再进行一次检测,
这次就会造成虚拟机卡死,估计还是和调试模式的检测有关。

我用了个折中的解决办法,就是用户态调试的时候不进入Debug模式,这样就能启动DNF.exe了,不过实在是不方便

(2)使双机调试能下断点
关于双机调试中断点原理和异常的处理流程,我是看了《软件调试》这本书和http://www.xfocus.net/articles/200412/765.html这片帖子,有了些了解。
对于TP来说,他是不断调用KdDisableDebuger()这个函数来清零KdDebuggerEnabled,调试引擎就是在KeUpdateSystemTime()这个函数里不断检测这个标志来确定异常处理的流程

解决方法,一开始我是直接HooK KdDisableDebuger(),开头

[Asm]纯文本查看复制代码



?





1

2




xor

rax, rax

ret








直接STATUS_SUCCESS然后返回,不让它做其他处理,但是毫无效果,还是下不了断点,不知道是怎么回事……

后来想了个办法,直接修改KeUpdateSystemTime()里面检测的地方,和上面一样,让它检测其它地址……

[C++]纯文本查看复制代码



?





01

02

03

04

05

06

07

08

09

10




//替换KeUpdateSystemTime函数中两个KdDebuggerEnabled变量地址为DummyKdDebuggerEnabled

KUSTPatchAddr1
= SearchAddressBySig((PUCHAR)KeUpdateSystemTimeAddr
+ 0x100, 0x100, UPSig1, sizeof(UPSig1))
;

if

(KUSTPatchAddr1 != NULL)

{

KUSTPatchAddr1
+= sizeof(UPSig1);

//64位汇编,变量均为相对rip地址,下同

DisableWriteProtect64();

*(PULONG)KUSTPatchAddr1
= (PUCHAR)pDummyKdDebuggerEnabled
- ((PUCHAR)KUSTPatchAddr1
- 2) - 6; //指令长度为6

EnableWriteProtect64();

}









要改的地方有3处,我只贴了一处的代码。这样就能下各种断点了。

2、双机调试之后就是用户态调试了
x64系统上TP目前做的保护还不是特别多,这也是我选x64入手的原因之一
TP修改了DebugObject中ValIDAccessMask一项,这个就是调试权限,代码中我们恢复下就可以了。

windbg下输入以下命令就可以定位到ValidAccessMask,下硬件断点就能找到ValidAccessMask清零的地方。
至于如何在自己的代码中定位这个变量,我是通过SSDT表查找NtCreateDebugObject这个内核函数地址,里面定位DbgkDebugObjectType
再根据下面的结构体加几个偏移地址(+0x040
+0x01c)来找到ValidAccessMask。应该有更好的定位方法……



[Asm]纯文本查看复制代码



?





01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82




dq

DbgkDebugObjectType

dt

_OBJECT_TYPE fffffa80`24e33250



清零前:



1:
kd> dt

_OBJECT_TYPE_INITIALIZER fffffa80`24e33250+0x040

nt!_OBJECT_TYPE_INITIALIZER

+0x000
Length

: 0x70

+0x002
ObjectTypeFlags : 0x8 ''

+0x002
CaseInsensitive : 0y0

+0x002
UnnamedObjectsOnly : 0y0

+0x002
UseDefaultObject : 0y0

+0x002
SecurityRequired : 0y1

+0x002
MaintainHandleCount : 0y0

+0x002
MaintainTypeList : 0y0

+0x002
SupportsObjectCallbacks : 0y0

+0x004
ObjectTypeCode : 0

+0x008
InvalidAttributes : 0

+0x00c
GenericMapping : _GENERIC_MAPPING

+0x01c
ValidAccessMask : 0x1f000f

+0x020
RetainAccess : 0

+0x024
PoolType : 0 ( NonPagedPool )

+0x028
DefaultPagedPoolCharge : 0

+0x02c
DefaultNonPagedPoolCharge : 0x58

+0x030
DumpProcedure : (null)

+0x038
OpenProcedure : (null)

+0x040
CloseProcedure : 0xfffff800`01f0ddb0 void nt!DbgkpCloseObject+0

+0x048
DeleteProcedure : 0xfffff800`01d66fe0 void nt!CmpConfigureProcessors+0

+0x050
ParseProcedure : (null)

+0x058
SecurityProcedure : 0xfffff800`01dd25f0 long nt!SeDefaultObjectMethod+0

+0x060
QueryNameProcedure : (null)

+0x068
OkayToCloseProcedure : (null)





清零后:

0:
kd> dt

_OBJECT_TYPE_INITIALIZER fffffa80`24e51250+0x040

nt!_OBJECT_TYPE_INITIALIZER

+0x000
Length

: 0x70

+0x002
ObjectTypeFlags : 0x8 ''

+0x002
CaseInsensitive : 0y0

+0x002
UnnamedObjectsOnly : 0y0

+0x002
UseDefaultObject : 0y0

+0x002
SecurityRequired : 0y1

+0x002
MaintainHandleCount : 0y0

+0x002
MaintainTypeList : 0y0

+0x002
SupportsObjectCallbacks : 0y0

+0x004
ObjectTypeCode : 0

+0x008
InvalidAttributes : 0

+0x00c
GenericMapping : _GENERIC_MAPPING

+0x01c
ValidAccessMask : 0

+0x020
RetainAccess : 0

+0x024
PoolType : 0 ( NonPagedPool )

+0x028
DefaultPagedPoolCharge : 0

+0x02c
DefaultNonPagedPoolCharge : 0x58

+0x030
DumpProcedure : (null)

+0x038
OpenProcedure : (null)

+0x040
CloseProcedure : 0xfffff800`01eb5db0 void nt!DbgkpCloseObject+0

+0x048
DeleteProcedure : 0xfffff800`01d0efe0 void nt!CmpConfigureProcessors+0

+0x050
ParseProcedure : (null)

+0x058
SecurityProcedure : 0xfffff800`01d7a5f0 long nt!SeDefaultObjectMethod+0

+0x060
QueryNameProcedure : (null)

+0x068
OkayToCloseProcedure : (null)



清零代码:

fffff880`0bcdc4cc
54 push

rsp

fffff880`0bcdc4cd
33c0 xor

eax,eax

fffff880`0bcdc4cf
87434c xchg

eax,dword

ptr
[rbx+4Ch]

fffff880`0bcdc4d2
33c0 xor

eax,eax

fffff880`0bcdc4d4
874350 xchg

eax,dword

ptr
[rbx+50h]

fffff880`0bcdc4d7
33c0 xor

eax,eax

fffff880`0bcdc4d9
87435c xchg

eax,dword

ptr
[rbx+5Ch] // ValidAccessMask

fffff880`0bcdc4dc
833d9585000000 cmp

dword
ptr
[fffff880`0bce4a78],0

fffff880`0bcdc4e3
0f8544feffff jne

fffff880`0bcdc32d

fffff880`0bcdc4e9
33c9 xor

ecx,ecx

fffff880`0bcdc4eb
ff15df6b0000 call

qword
ptr
[fffff880`0bce30d0]

fffff880`0bcdc4f1
488b4c2440 mov

rcx,qword

ptr
[rsp+40h]

fffff880`0bcdc4f6
4833cc xor

rcx,rsp

fffff880`0bcdc4f9
e822570000 call

fffff880`0bce1c20

fffff880`0bcdc4fe
488b5c2468 mov

rbx,qword

ptr
[rsp+68h]

fffff880`0bcdc503
4883c450 add

rsp,50h

fffff880`0bcdc507
5f pop

rdi

fffff880`0bcdc508
c3 ret










从清零代码那里的windbg信息来看,没有地址标号的提示(TesSafe+xxxx),这代码似乎不是在一个驱动模块里?
关于恢复的方法,大体上能想到3种:
1、开一个IoTimer或者DpcTimer或者干脆开个线程,不断对ValidAccessMask地址写入它原来的值
2、自己代码内利用调试寄存器下硬件断点,然后hook IDT 1号中断服务子程,在里面恢复ValidAccessMask
3、自己代码内利用调试寄存器下硬件断点,定位到上面的清零代码,nop之(我在windbg里直接nop掉是可以的,没有模块自校验)
目前我尝试了第一种方法,开了个IoTimer,虽说1s一次会造成一些概率问题,但测试而已,简单粗暴就好。


[C++]纯文本查看复制代码



?





1

2

3

4

5

6




VOID

OnTimer(DEVICE_OBJECT *DeviceObject, PVOID

Context)

{

DisableWriteProtect64();

*g_pValidAccessMask
= 0x1f000f;

EnableWriteProtect64();

}









至于为什么会有后两种想法,是因为我觉得其作用不仅仅在于次,通过调试寄存器和1号中断应该还可以作很多事情,不仅仅是写个0x1f000f而已……
不过虽说想法很好,我还没开始实践……这中间似乎涉及到多核CPU还有用户栈内核栈切换的许多知识……

好了,现在可以开OD附加了,那么问题来了,学挖掘……哦不…………
还是先请前辈们看看效果图:
这是开另一个任意程序调试的OD,一切正常:

这是附加了DNF.exe,反汇编窗口基本全是0,右键看不到模块信息……
这个就是传说中的DebugPort清零吗?如果是的话,我在自己研究下恢复,如果不是,哪是什么问题造成了这种现象?

另外从图上游戏界面可以看到,DNF检测到了非法模块,这个不知道会有什么影响……
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-4-20 07:03

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

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