首页
论坛
课程
招聘
[原创]某鹅安全竞赛20年初赛ring0题解
2021-7-13 15:28 7908

[原创]某鹅安全竞赛20年初赛ring0题解

2021-7-13 15:28
7908

最近打算把某鹅安全竞赛做一遍,来练练手。做到20年的时候发现20年的题解不是很多,仅有的几个写的也不是很详细,于是就打算来写一个详细点(新手向)的,供和我一样刚入门的朋友们参考。其实对于vmp壳我还有许多的知识待学习,中间写的有些错误与问题,还请大家多多赐教。

 

题目在这里可以下到,本文为初赛ring0题的题解,因为ring3那道题实在是没啥难度。题目如下:

1
2
3
4
初赛ring0题目:
DriverDemo.sys是一个驱动程序,它内置了一些限制。
1, 不能篡改该文件,尝试使驱动成功加载。
2, 该驱动程序成功加载后,突破它的限制,但不允许patch文件或内存,使它成功打印出(用dbgview可接受)调试信息"hello world!".

前期准备

驱动文件拿过来先丢到IDA里看看,很明显是一个vmp的壳,令人十分头大。说实话到这一步我就已经去搜其他人的题解了,因为我之前也没怎么涉猎过有关vmp的知识,只知道这是个强壳。但是大概瞟了一眼前面的方法似乎也没有很复杂,直接开冲。

 

image-20210712203222421

 

网络上有关驱动vmp加壳分析的博客不是很多,博客<某驱动脱壳 vmp>对于分析加了vmp壳的驱动程序所用的思路与脱一些常规壳差不多,还是去手工跳过前面的解密阶段,去找程序的找OEP,然后就可以去分析没被vmp保护的代码。我去试了试,并没成功(我还是太菜了)。

 

image-20210712204633384

 

这是DriverDemo.sys的导入表,很明显MmGetSystemRoutineAddress这个函数是多出来的,似乎有些函数调用vmp壳并不会将其隐藏起来,那在这个函数下断岂不是就可以断到解密完的地方,然后把内存dump下来再继续分析。

 

image-20210712204913496

 

上双机调试,加载驱动,断到MmGetSystemRoutineAddress函数处,跳出,来到一段看起来像是正常代码的地方。

 

image-20210712213016980

 

MmGetSystemRoutineAddress是内核里根据函数名获取函数地址的一个常用函数,可以看到获取到的函数地址保存在fffff8045ca94010处,然后后面紧接着调用了这个地址。

 

image-20210712213129121

 

跟进去发现fffff8045ca94010处存的是KdDisableDebugger的地址,这个函数是一个禁止内核调试的一个函数,常用于内核反调试,到这里差不多可以确信现在执行的代码差不多是解密后的关键代码。用Windbg把这个驱动现在的内存dump下来,拖到IDA里面看看。

 

image-20210712214146099

 

博客<利用IDA的F5来反VMP2.x的Mutation保护>建议,分析混淆后的程序最好把IDA的这个选项勾掉,以免乱创建函数,影响graph和F5,去变异代码那手动创建函数会好很多,我们这里照做。

 

image-20210712214431955

 

进去后查看字符串窗口,可以看到一些解密后的字符串信息。

 

image-20210712214530444

 

随便点一个跟过去,可以发现更多的有用的字符串。

 

image-20210713093629033

 

比如这个应该是一个设备的名字,一路查看引用向上回溯。

 

image-20210713101605285

 

最终类似于博客<某驱动脱壳 vmp>,来到了自动识别出来的DriverEntry处。(后期补充:用hzqst大表哥的这个工具也可以达到类似的效果)

 

image-20210713101914278

 

在此新建个函数,F5,简直完美,似乎我们的分析到这里感觉就差不多了。

 

image-20210713102102973

 

但是当我们随便在反编译的窗口随便点进一个函数,然后再出来,就发现DriverEntry变成了这个样子,这是发生甚么事了。

 

image-20210713102532220

 

反汇编窗口来到被截断的地方,发现这里莫名其妙多了一条分割线,把DriverEntry给切断了。

 

image-20210713102852516

 

研究了一会,发现是在sub_FFFFF8045CD26B60()的最后,出现了一个JUMPOUT,应该是这个东西把自身以及外面的DriverEntry给都截断了。

 

image-20210713103334448

 

image-20210713103951661

 

IDA反汇编加上动态调试一下,就可以知道这个sub_FFFFF8045CD2E587实际上是一个系统调用,里面左跳右跳最终会去执行RtlInitUnicodeString函数,有关vmp里面的系统调用可以参照博文<手动分析VMP加密的x64驱动导入表>。返回地址是int 3下面的那条指令,而正是这个int 3造成了截断反编译结果的JUMPOUT。所以这里我们把这个int 3改成nop,然后把所有的函数删掉再重新分析。

 

image-20210713104737819

 

可以看到反编译的结果就比较正常了,在后面还会有几次这样类似的截断,同样的方式处理就可以,剩下的工作就是把所有的系统函数名字、对应的变量类型都给标上。

第一问

image-20210713105114637

 

标完以后,其实后面就没什么难度了。回到题目,首先是让驱动正常加载,这里重点来看sub_FFFFF8045CD26A00()sub_FFFFF8045CD26B60()这两个函数。

反双机调试

image-20210713105719470

 

image-20210713110103698

 

image-20210713110039533

 

sub_FFFFF8045CD26A00()主要是反双机调试,分别设置kdDebuggerEnabledKdDebuggerNotPresent,并调用KdDisableDebugger来实现反调试。效果就是当我们调试这个驱动时,F5后想再断下就断不下了。这里我们只需要重新将kdDebuggerEnabled 置为1(执行ed kdDebuggerEnabled 1),然后跳过运行KdDisableDebugger即可。

 

image-20210713110454277

 

这里有一个问题,在我的虚拟机里运行时,不知道为什么设置KdDebuggerNotPresent为1没有效果,就算我手动修改也不行。

检查注册表项

image-20210713110907693

 

image-20210713110920338

 

image-20210713111056131

 

image-20210713112118859

1
2
3
4
5
6
typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
  ULONG TitleIndex;
  ULONG Type;
  ULONG DataLength;
  UCHAR Data[1];
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;

来到sub_FFFFF8045CD26B60()函数,在DriverEntry里,如果这个函数返回0,则驱动就会退出,所以让驱动正常加载的关键部分就是这个函数。这个函数主要逻辑就是判断某一个注册表项是否为1,如果不为1就返回0,让驱动退出。这里对于注册表操作的代码可以参照《Windows内核安全》的4.2小节,几乎是一样的。所以只需要在对应的注册表处添加一个键值为1的注册表项就可以让驱动正常运行。

 

image-20210713113150570

 

这里还有一个小问题值得说一下,当把API标完以后,会发现这几个函数的参数十分奇怪,怎么也对不上。

 

image-20210713113531186

 

研究了一下,是这个压栈的操作影响了IDA的参数识别,因为64位是通过寄存器和堆栈一块传参的。而这个push rdx应该不是传参,大概是和vmp的系统函数调用有关,所以删掉了不影响分析。

 

image-20210713113712011

 

这样就正常了。

第二问

image-20210713145125136

 

image-20210713145146119

 

image-20210713145200294

 

image-20210713145213166

 

如何使其输出hello world也就十分清晰了,就是设置一下同步事件就可以了。这部分的代码与《Windows内核安全》的4.4小节也十分类似,大家可以对照学习。具体的代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <ntddk.h>
VOID myUnloadDriver(PDRIVER_OBJECT pDriverObject)
{
    DbgPrint("unload driver\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING pPath)
{
    UNICODE_STRING EventName;
    HANDLE hEvent;
    PRKEVENT pEvent;
    DriverObject->DriverUnload = myUnloadDriver;
 
    RtlInitUnicodeString(&EventName, L"\\BaseNamedObjects\\tp2020");
    pEvent = IoCreateNotificationEvent(&EventName, &hEvent);
    KeSetEvent(pEvent, 0, 0);
 
    return STATUS_SUCCESS;
}

总结

回过头来看其实难度也不是很大,只要相关知识都掌握了,得出题解也是一眼的事。像我一样刚入门或还未入门的新手朋友们可以来试一试,锻炼一下。文中的一些附件会上传至文末。还有驱动加vmp壳感觉强度也不是很高?为什么dump下内存来字符串都是明文的,我又去自己给驱动加了vmp壳试了试,似乎dump下来字符串信息都是明文的。所以vmp加在驱动上和加载exe文件上有啥区别?感觉vmp强度应该是蛮高的才对。这方面的资料也不是很多,还请各位看官多多指教。


[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

最后于 2021-8-4 16:22 被危楼高百尺编辑 ,原因: 添加引用
上传的附件:
收藏
点赞6
打赏
分享
最新回复 (10)
雪    币: 2093
活跃值: 活跃值 (1189)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 活跃值 2021-7-16 08:53
2
0
支持下
雪    币: 365
活跃值: 活跃值 (863)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 活跃值 1 2021-7-16 09:43
3
0
感谢分享!
雪    币: 2311
活跃值: 活跃值 (787)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 活跃值 2021-7-16 09:48
4
0
666原来如此
雪    币: 2311
活跃值: 活跃值 (787)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 活跃值 2021-7-16 09:49
5
0
可能vmp在驱动的保护并不高
雪    币: 2311
活跃值: 活跃值 (787)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 活跃值 2021-7-16 09:53
6
0
楼主赶紧用这个方法看一下tx其他驱动能不能解
雪    币: 4671
活跃值: 活跃值 (582)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
如斯咩咩咩 活跃值 2021-7-16 09:53
7
0
雪    币: 312
活跃值: 活跃值 (433)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小kcode 活跃值 2021-7-23 18:10
8
0
楼主   博客<某驱动脱壳 vmp> ,这个地址是什么啊?,引用链接失效了。      
雪    币: 9133
活跃值: 活跃值 (1518)
能力值: ( LV6,RANK:87 )
在线值:
发帖
回帖
粉丝
Lixinist 活跃值 1 2021-7-23 18:20
9
0
随手写的博客,真能给翻出来啊。。。
雪    币: 2379
活跃值: 活跃值 (1397)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
危楼高百尺 活跃值 1 2021-7-24 13:07
10
0
小kcode 楼主   博客 ,这个地址是什么啊?,引用链接失效了。

没有失效呀

最后于 2021-7-24 13:08 被危楼高百尺编辑 ,原因:
雪    币: 0
活跃值: 活跃值 (108)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
初始 活跃值 2021-8-7 08:38
11
0
在.writemem时候卡住了,不知道起始地址和范围该怎么设置??
游客
登录 | 注册 方可回帖
返回