查看原文
其他

某鹅安全竞赛20年初赛ring0题解

危楼高百尺 看雪学苑 2022-07-01
本文为看雪论坛精华文章
看雪论坛作者ID:危楼高百尺

最近打算把某鹅安全竞赛做一遍,来练练手。做到20年的时候发现20年的题解不是很多,仅有的几个写的也不是很详细,于是就打算来写一个详细点(新手向)的,供和我一样刚入门的朋友们参考。

其实对于vmp壳我还有许多的知识待学习,中间如有些错误或者问题,还请大家多多赐教。
 
题目在这里可以下到,本文为初赛ring0题的题解,因为ring3那道题实在是没啥难度。题目如下:
初赛ring0题目:DriverDemo.sys是一个驱动程序,它内置了一些限制。1, 不能篡改该文件,尝试使驱动成功加载。2, 该驱动程序成功加载后,突破它的限制,但不允许patch文件或内存,使它成功打印出(用dbgview可接受)调试信息"hello world!".



1


前期准备


驱动文件拿过来先丢到IDA里看看,很明显是一个vmp的壳,令人十分头大。说实话到这一步我就已经去搜其他人的题解了。

因为我之前也没怎么涉猎过有关vmp的知识,只知道这是个强壳,但是大概瞟了一眼前面的方法似乎也没有很复杂,直接开冲。
 
 
网络上有关驱动vmp加壳分析的博客不是很多,博客《某驱动脱壳 vmp》对于分析加了vmp壳的驱动程序所用的思路与脱一些常规壳差不多。

还是去手工跳过前面的解密阶段,去找程序的找OEP,然后就可以去分析没被vmp保护的代码。我去试了试,并没成功。


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

 

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


MmGetSystemRoutineAddress是内核里根据函数名获取函数地址的一个常用函数,可以看到获取到的函数地址保存在fffff8045ca94010处,然后后面紧接着调用了这个地址。
 
跟进去发现fffff8045ca94010处存的是KdDisableDebugger的地址,这个函数是一个禁止内核调试的一个函数,常用于内核反调试。

到这里差不多可以确信现在执行的代码差不多是解密后的关键代码。用Windbg把这个驱动现在的内存dump下来,拖到IDA里面看看。


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

 
 
进去后查看字符串窗口,可以看到一些解密后的字符串信息。
 
 
随便点一个跟过去,可以发现更多的有用的字符串。

比如这个应该是一个设备的名字,一路查看引用向上回溯。
最终类似于博客《某驱动脱壳 vmp》,来到了自动识别出来的DriverEntry处。

在此新建个函数,F5,简直完美,似乎我们的分析到这里感觉就差不多了。 
 
但是当我们随便在反编译的窗口随便点进一个函数,然后再出来,就发现DriverEntry变成了这个样子,这是发生甚么事了。
 
 
反汇编窗口来到被截断的地方,发现这里莫名其妙多了一条分割线,把DriverEntry给切断了。
 
 
研究了一会,发现是在sub_FFFFF8045CD26B60()的最后,出现了一个JUMPOUT,应该是这个东西把自身以及外面的DriverEntry给都截断了。
 
 
IDA反汇编加上动态调试一下,就可以知道这个sub_FFFFF8045CD2E587实际上是一个系统调用,里面左跳右跳最终会去执行RtlInitUnicodeString函数,有关vmp里面的系统调用可以参照博文<手动分析VMP加密的x64驱动导入表>

返回地址是int 3下面的那条指令,而正是这个int 3造成了截断反编译结果的JUMPOUT。所以这里我们把这个int 3改成nop,然后把所有的函数删掉再重新分析。


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



2


第一问

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

反双机调试


 
 
 
sub_FFFFF8045CD26A00()主要是反双机调试,分别设置kdDebuggerEnabled和KdDebuggerNotPresent,并调用KdDisableDebugger来实现反调试。

效果就是当我们调试这个驱动时,F5后想再断下就断不下了。这里我们只需要重新将kdDebuggerEnabled 置为1(执行ed kdDebuggerEnabled 1),然后跳过运行KdDisableDebugger即可。
 
 
这里有一个问题,在我的虚拟机里运行时,不知道为什么设置KdDebuggerNotPresent为1没有效果,就算我手动修改也不行。


检查注册表项


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的注册表项就可以让驱动正常运行。
 
 
这里还有一个小问题值得说一下,当把API标完以后,会发现这几个函数的参数十分奇怪,怎么也对不上。

 

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


3


第二问
 
如何使其输出hello world也就十分清晰了,就是设置一下同步事件就可以了。这部分的代码与《Windows内核安全》的4.4小节也十分类似,大家可以对照学习。具体的代码如下。
#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;}



4


总结

回过头来看其实难度也不是很大,只要相关知识都掌握了,得出题解也是一眼的事。像我一样刚入门或还未入门的新手朋友们可以来试一试,锻炼一下。

文中的一些附件会上传至文末。还有驱动加vmp壳感觉强度也不是很高?为什么dump下内存来字符串都是明文的,我又去自己给驱动加了vmp壳试了试,似乎dump下来字符串信息都是明文的。

所以vmp加在驱动上和加载exe文件上有啥区别?感觉vmp强度应该是蛮高的才对。这方面的资料也不是很多,还请各位看官多多指教。


 


看雪ID:危楼高百尺

https://bbs.pediy.com/user-home-926584.htm

*本文由看雪论坛 危楼高百尺 原创,转载请注明来自看雪社区




# 往期推荐

1.某知笔记服务端docker镜像授权分析

2. angr学习(三)一道自己模仿着出的简单题和angr-ctf符号化输入相关题目

3. 记一次Word宏Downloader样本分析

4.XX之NTDLL随机化“逆向”(XP系统)

5.Frida分析违法应用Native层算法

6. 关于一次在pwnable.kr中input题目的经历(python3)



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存