[原创]分析某游戏驱动保护的学习历程
2022-12-3 00:19:0 Author: bbs.pediy.com(查看原文) 阅读量:17 收藏

一、前言

一直都对游戏保护感兴趣,最近想看一看游戏驱动是怎么写的。于是便尝试逆向分析一下。在这个过程中学到很多。

二、驱动调试环境的搭建

由于驱动运行在系统内核层,所以对驱动的调试一般采用双机调试。物理机对物理机,或者物理机对虚拟机。因为手上设备限制,就一个笔记本。所以首先想到的是开虚拟机(VMware Workstation Pro)进行双机调试。

2.1 虚拟机中游戏运行环境的准备

一般情况下,游戏都会检测虚拟机运行环境。网上收集一波资料,找到hzqst的一个项目。

hzqst/VmwareHardenedLoader: Vmware Hardened VM detection mitigation loader (anti anti-vm) (github.com)

按照使用说明,卸载vmtools,修改虚拟机配置文件,安装驱动,就能正常运行游戏。

但是卸载vmtools后,虚拟机很卡,而且屏幕显示也很别扭。所以还是希望能在安装了vmtools的情况下运行游戏。经过测试发现是可行的。以下是操作步骤:

1.修改虚拟机配置文件

1

2

3

4

5

6

7

hypervisor.cpuid.v0 = "FALSE"

board-id.reflectHost = "TRUE"

hw.model.reflectHost = "TRUE"

serialNumber.reflectHost = "TRUE"

smbios.reflectHost = "TRUE"

SMBIOS.noOEMStrings = "TRUE"

ethernet0.address = "00:11:22:33:44:55" // 这里随意

2.安装vmloader驱动

3.修改vmtools显卡驱动名字(可选)

安装驱动精灵或者驱动人生,备份显卡驱动。用7z或者winrar打开备份的压缩包(不解压),编辑inf文件。

1

2

3

4

// 将inf文件以下三行任意修改

DiskID = "VMware Tools"

CompanyName = "VMware, Inc."

SVGA = "VMware SVGA 3D"

保存文件后。用驱动精灵或者驱动人生还原显卡驱动即可。
完成修改后,成功运行游戏。
游戏运行截图

2.2 双机调试方式的选取

在看雪论坛看了一圈发现,游戏驱动基本都对Windbg双机调试进行了检测和处理。目前我并不清楚是怎么检测的,所以放弃VMWare+kdstub+windbg的双机调试方式,另寻他法。经过搜索,发现VMWare+gdbstub+IDA-gdb是个很好的方式。

2.2.1 IDA+VMWare调试配置

  1. 修改虚拟机配置文件

1

2

3

debugStub.listen.guest64 = "TRUE"

debugStub.hideBreakpoints = "TRUE"

monitor.debugOnStartGuest64 = "TRUE"

  1. IDA启动gdb调试器

    启动调试器
    gdb

虚拟机在本地运行,则hostname填localhost,目标系统是64位,则port填8864(32位是8832)在这个窗口等待备用。

去VMware启动虚拟机后,立刻回到IDA单击OK,那就成功附加上了。

idaok

2.2.2 ntoskrnl.exe调试符号加载

IDA-gdb成功附加之后,默认是没有符号。没有符号的情况下,调试内核并不方便。所以得想办法加载上微软的巨硬PDB。内核ntoskrnl.exe的pdb符号文件先下载好备用。IDA在调试时是可以加载pdb文件。

loadpdb
pdb

所以只需要得到内核模块ntoskrnl.exe的符号文件和内存加载地址就可以了。而IDA附加之后,断下的位置还在系统启动阶段,并没有加载ntoskrnl.exe。那么可以寻找到载入内核模块,并调用内核入口函数的时机。就能获取到KiSystemStartup函数地址减去函数偏移就能得到ntoskrnl的基址。简单逆向winload.exe(有符号)找到一个关键函数OslArchTransferToKernel。

entrypoint2
entrypoint3

那么只需要在这个位置下断点,即可跟踪到内核入口。IDA搜索一下OslArchTransferToKernel函数末尾指令对应的字节码:

49 8B CC 56 6A 10 41 55 48 CB

就能确定OslArchTransferToKernel函数位置。

retfq

在单步执行跳过去之前需要先映射一下内存区域。

memory

然后单步执行跳到KiSystemStartup函数。

startup1

那么当前的KiSystemStartup函数地址为0xFFFFF80006F90010,KiSystemStartup的偏移是0x990010

1

ntoskrnl.base=0xFFFFF80006F90010-0x990010=0xFFFFF80006600000

现在终于获得了基址,配和之前下载备用的pdb,IDA再加载一下,就可以有符号了。

startup2

三、驱动分析过程

目前为止,我们已经有了可以运行游戏的虚拟机环境和能带微软符号调试内核的调试环境。那么就可以做一些感兴趣且有意思的事情了。前人栽树,后人乘凉。先找一下有没有分析过该游戏保护的,拿过来看一看,找一找思路。hzqst大佬!

[原创]某南极动物厂新版XX分析——系统线程部分(新瓶旧酒)-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

看完他的分析,我也想定位一下游戏驱动创建的隐藏了入口的系统线程。那就在启动游戏之前,IDA里在PsCreateSystemThread函数处下断点,看第六个参数StartRoutine是不是SeSetAuditParameter函数内部jmp rcx所在地址。

thread1

一共确定了五个线程的地址,再减去游戏驱动的加载地址(IopLoadDriver下断获得)得到函数偏移,就能在IDA里面静态分析了。

1

2

3

4

5

6

7

8

9

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

1.FFFFF8001C0B5B60

2.FFFFF8001C0***70

3.FFFFF8001C0***80

4.FFFFF8001C0***80

5.FFFFF8001C0***F0

FFFFE20CF9B6ABE0  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6ABE8  FFFFE20CF9B6ACA9  MEMORY:FFFFE20CF9B6ACA9

FFFFE20CF9B6ABF0  FFFFF8001C1965A1  MEMORY:FFFFF8001C1965A1

FFFFE20CF9B6ABF8  FFFFE20C00000000  MEMORY:FFFFE20C00000000

FFFFE20CF9B6AC00  FFFFAD86F81E36F8  MEMORY:FFFFAD86F81E36F8

FFFFE20CF9B6AC08  FFFFF8000693CC31  SeSetAuditParameter+41

FFFFE20CF9B6AC10  FFFFF8001C0B5B60  MEMORY:FFFFF8001C0B5B60

FFFFE20CF9B6AC90  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC98  0000000000000002  MEMORY:word_2

FFFFE20CF9B6ACA0  FFFFF8001C14B419  MEMORY:FFFFF8001C14B419

FFFFE20CF9B6ACA8  FFFFF80000000000  MEMORY:FFFFF80000000000

FFFFE20CF9B6ACB0  FFFFAD86F81E3238  MEMORY:FFFFAD86F81E3238

FFFFE20CF9B6ACB8  FFFFF8000693CC31  SeSetAuditParameter+41

FFFFE20CF9B6ACC0  FFFFF8001C0***70  MEMORY:FFFFF8001C0***70

FFFFE20CF9B6AC10  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC18  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC20  FFFFF8001C1962F0  MEMORY:FFFFF8001C1962F0

FFFFE20CF9B6AC28  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC30  FFFFAD86F8168578  MEMORY:FFFFAD86F8168578

FFFFE20CF9B6AC38  FFFFF8000693CC31  SeSetAuditParameter+41

FFFFE20CF9B6AC40  FFFFF8001C0***80  MEMORY:FFFFF8001C0***80

FFFFE20CF9B6AC50  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC58  0000000000000002  MEMORY:word_2

FFFFE20CF9B6AC60  FFFFF8001C180C6D  MEMORY:FFFFF8001C180C6D

FFFFE20CF9B6AC68  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC70  FFFFAD86F8168338  MEMORY:FFFFAD86F8168338

FFFFE20CF9B6AC78  FFFFF8000693CC31  SeSetAuditParameter+41

FFFFE20CF9B6AC80  FFFFF8001C0***80  MEMORY:FFFFF8001C0***80

FFFFE20CF9B6AC90  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6AC98  0000000000000002  MEMORY:word_2

FFFFE20CF9B6ACA0  FFFFF8001C1835B0  MEMORY:FFFFF8001C1835B0

FFFFE20CF9B6ACA8  0000000000000000  MEMORY:unk_0

FFFFE20CF9B6ACB0  FFFFAD86F8168378  MEMORY:FFFFAD86F8168378

FFFFE20CF9B6ACB8  FFFFF8000693CC31  SeSetAuditParameter+41

FFFFE20CF9B6ACC0  FFFFF8001C0***F0  MEMORY:FFFFF8001C0***F0

当我打开另一个IDA载入游戏驱动文件,跳到线程地址,发现直接就能F5(意外之喜)。

ace_base1

具体分析参考hzqst的帖子,不再多说。看完系统线程部分,自然还想看看其他好功能。再找一波,发现杰克王的帖子。杰克王大佬!

[原创]某企鹅xxx-base分析-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

他的帖子里面对回调部分说得比较少,但是我对游戏驱动回调怎么写的确实感兴趣,开始尝试分析找到回调。

首先找一个能用的ark,看看游戏驱动创建的回调函数地址。同样的减去模块基址得到偏移,再去IDA看一看。

winark1

LoadImage回调函数地址0xFFFFF8001C0B4190去IDA看看。

imageroutine1

好家伙,暗藏只因,趁着双机调试,去那个地址看一看实际的回调函数地址。

imageroutine2
imageroutine3

同样能直接F5,其他回调函数都是差不多的模式,不再重复。具体功能我也不再分析细说。

分析完回调,我也想看一看游戏驱动的通信函数,学习一下。跳过去一看,不能F5,直呼坏事。那么下一节浅说代码保护。

vm1

四、游戏驱动代码保护浅析

我对代码保护没什么了解,也没有基础。我只是根据双机动态调试,跟踪一下代码运行过程,做出一些判断和操作。

4.1 混淆

vm2

在调试过程中发现游戏驱动代码具有一些特别的跳转结构。

1

2

3

4

5

6

7

push    r8

mov     r8, 0FFFFF8001C2C86C1h

pushfq

add     r8, 0FFFFFFFFFFF63D07h

popfq

jmp     r8

pop     r8

其中最奇怪的就是 jmp r8会跳转到他的下一句指令pop r8,那么其实这一段代码什么也没做,就是干扰一下。简单对抗一下IDA-F5小子。发现了关键点,那么我们可以写一下去混淆脚本。同样的,网上找一波资料。找到了 逍遥m 发的帖子。逍遥m大佬!

[原创]IDA 驱动vmp变异 去花指令 IDC脚本-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

那我也是抄一下代码,针对性的改一改。写好一个IDC脚本开干。脚本这里我就不发了,混淆的原理和如何写idc去混淆脚本都贴出来了。相信大家认真点分析都会写去混淆脚本的。毕竟自己动手丰衣足食。贴一下去混淆之后成功F5的截图。

vm3

4.2 vm

去混淆之后,大部分功能都能看了。但是还有些是被v了的,跑在虚拟机里面。我能力有限,只是简单分析一下。我拿驱动入口里调用的一个函数举例。

1

2

3

4

5

6

7

8

9

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

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

在进入虚拟机之前需要保存上下文环境(参数,寄存器,原始堆栈等等),这一部分操作比较明显,直接跳过。

以下分析过程中省略了一些混淆代码。

lea     r11, unk_183419                 

lea     r10, [rbp+88h]

lea     rsp, [rsp-8]

mov     r8b, [r9]

xor     r8b, 5Dh

mov     rdx, 5A3B32CF45F10D9Bh

not     rdx

lea     rdx, [r9+rdx]

mov     r9, 0A5C4CD30BA0EF262h

not     r9

lea     r9, [rdx+r9] 

not     rax

xchg    rax, r9

mov     [rbp+8], rax

movzx   r8, r8b 

sub     r8, 1   

cmp     r8, 0C8h

jnb     loc_2295F6 

lea     r9, off_22B632

mov     r8, [r9+r8*8]

0x227E8F 4C 8D 0D 6A 81 DD FF          lea     r9, cs:0

add     r8, r9

jmp     r8

0x227946

xchg    r9, r10

mov     r10, [rbp+8]

not     r10

xchg    r10, r9

not     r9

mov     r8w, [r9]  

xor     r8w, 144Ch 

mov     rdx, 442E88550684B936h

not     rdx

lea     rdx, [r11+rdx]  

movzx   r8, r8w

mov     rcx, 0BBD177AAF97B46C8h

not     rcx

add     r8, rcx

lea     r8, [rdx+r8]

lea     r9, [r9+2]

mov     dl, [r9]

mov     [r8], dl

lea     r9, [r9+1]

五、总结

在整个分析过程学到很多东西。从游戏运行环境搭建,到双机调试环境搭建,到动态调试游戏驱动,再到分析代码保护。涉及到的东西确实多,收获也多。

传统功夫,讲究点到为止,此贴到此结束。

[2022冬季班]《安卓高级研修班(网课)》月薪两万班招生中~


文章来源: https://bbs.pediy.com/thread-275397.htm
如有侵权请联系:admin#unsafe.sh