0x01 写在前面
决赛果然是被大佬教做人.png
ring0好像需要大量的动调还有断链操作emmmmmm
然鹅窝的双机调试还没搞好。。。。
先去把 Linux Kernel的东西搞好再回来深究吧(望天)
Writeup中所示内容均为比赛期间临场学习得到的成果,没有分析出的部分在大佬写了相关内容后将会以友链的方式更新到文章里~
0x02 ring3 程序分析
题目描述
winmine.exe是一个扫雷游戏程序,winmine.dmp是该程序的一份进程dump, 在这份dump中,有一个DLL作弊程序。
1, 请找到该作弊程序,给出模块名(1分)
2, 并分析它所包含的4个作弊功能,给出实现作弊功能的函数的偏移,并说明其作弊功能是什么(4分)
题目分析
定位并提取dump中的内存映像
使用工具:WinDbg Preview 1.0.2001.02001
、CFF Explorer 2012
- 首先使用
WinDbg Preview
加载程序,使用lm
命令列出所有加载的模块列表 -
通过
.writemem C:\Users\error404\Desktop\CheatTools.dll 0x6e220000 (0x6e266000-0x1)
命令将其中的模块存成文件。 -
接下来需要做一下这个文件的修复,因为我们这样提取必然已经破坏了文件内部的段首部信息。
因此我们需要将
Raw Address
和Virtual Address
校准。
IDA分析该程序
使用工具:IDA Pro For Mac v7.0
、Resource Hacker v5.1.7
- 首先,我们查看字符串列表。
我们发现了重要的头文件标识,这里出现了
MFC
文件常用的头文件,于是怀疑程序中极有可能存在资源文件,于是使用Resource Hacker
查看其内部资源文件。 -
可以发现,程序给出了主窗体代码。
于是我们可以确认四个作弊模块分别是:
- 暂停时间
- 遇雷不报
- 一键游戏
- 地雷分布(透视)
逐作弊模块分析——暂停时间
- 我们在初赛中其实已经发现在扫雷程序中,
.text:01002FF5 inc dword_100579C
处的代码是计时器递增逻辑,于是我们只需要查找这个程序中哪里进行了类似修改即可,这里我们选择直接查找这个立即数。 -
通过反汇编,我们确认了这个函数就是负责暂停计时的函数。
逐作弊模块分析——遇雷不报
-
我们在初赛中其实已经发现在扫雷程序中,
0x1003591
处的代码是遇雷不报逻辑,于是我们只需要查找这个程序中哪里进行了类似修改即可,这里我们选择直接查找这个立即数。 -
通过反汇编,我们确认了这个函数就是负责忽略踩雷逻辑的函数。
此处会将源程序中
push 0
这个压参操作转换为了jmp short loc_10035B0
。
逐作弊模块分析——一键游戏
-
这里我们首先要确定,一键游戏这个功能一定是要调用扫雷中的点击操作的,即,用户点击格子后的处理程序,这个函数在扫雷程序中的
sub_1003512
。可以看到函数中有明显的取坐标的操作
v2 = &byte_1005340[32 * a2 + a1];
,于是v2
就是我们”点击”的格子。之后也有对于是否踩雷的判断
-
于是直接查找
0x1003512
是否在作弊模块中产生了调用。于是成功定位。
逐作弊模块分析——地雷分布(透视)
-
对于透视功能,可以在字符串窗口发现一个
行:%d,列:%d
-
于是推测此函数就是透视函数。
题目总结
-
作弊程序模块名为:
CheatTools.dll
-
四个作弊功能如下:
- 暂停时间
偏移:0x1660
-
遇雷不报
偏移:0x16D0
-
一键游戏
偏移:0x1F00
-
地雷分布(透视)
偏移:0x1AC0
- 暂停时间
0x03 ring0 程序分析 —— Part1
题目描述
(1) 成功加载Driver.drv至驱动模块链表(0.5分)
(2) 自编写驱动加载程序,使用非服务方式加载Driver.drv驱动,加载后需要保证驱动路径为C:\Driver.drv(0.5分)
(3) 分析驱动接口,给出每个功能的调用控制码,输入,输出数据的结构(0.5分)
(4) 分析驱动接口,分析每个接口的作用(0.5分)
(5) 调用驱动接口,使Driver.drv在驱动模块中断链隐藏(只允许在应用层调用Driver.drv接口实现)(2分)
(6) 从Flag.fg中分析出Flag,此flag用于解密Part2.7z(1分)
题目分析
以服务方式加载驱动
- 为了加载该驱动程序,需要手动编写一个INF文件。
[Version] Signature = "$CHICAGO$" Provider = ERROR404 DriverVer = 8/21/2002,3.0.0.3 Class = DisplayCodec ClassGUID = {E6ABB47D-8339-4c60-BE92-E9045FF5A33D} [DestinationDirs] DefaultDestDir = 12 DriverDemo.DriverFiles = 12 [SourceDisksNames] 1 = "DriverDemo",Disk1,, [SourceDisksFiles] DriverDemo.sys = 1,objfre\i386, [DefaultInstall] OptionDesc = %DriverDemoServiceDesc% CopyFiles = DriverDemo.DriverFiles AddReg = DriverDemo.AddRegistry [DefaultInstall.Services] AddService = %DriverDemoServiceName%,,DriverDemo.Service [DefaultUninstall] DelFiles = DriverDemo.DriverFiles DelReg = DriverDemo.DelRegistry [DefaultUninstall.Services] DelService = DriverDemo,0x200 [DriverDemo.Service] DisplayName = %DriverDemoServiceName% Description = %DriverDemoServiceDesc% ServiceBinary = %12%\DriverDemo.drv ServiceType = 1 StartType = 3 ErrorControl = 1 LoadOrderGroup = "DriverDemo" [DriverDemo.DriverFiles] DriverDemo.drv [DriverDemo.AddRegistry] HKLM,%DriverDemoRegistry%,%DriverDemoKey%,0x00010001,1 [DriverDemo.DelRegistry] HKLM,%DriverDemoRegistry%,%DriverDemoKey% ;; ;; String Section ;; [Strings] DriverDemoServiceDesc = "DriverDemo.Inf" DriverDemoServiceName = "DriverDemo" DriverDemoRegistry = "SOFTWARE\AppDataLow\Tencent\{61B942F7-A946-4585-B624-B2C0228FFEBC}" DriverDemoKey = "key" Disk1 = "DriverDemo Source Media"
- 然后在Windows中右击安装此inf文件即可,通过查阅系统日志,发现此驱动程序已被成功加载。
-
接下来尝试通过
sc start DriverDemo
启动,发现失败于是选择静态分析文件。
-
从发现在内核入口的主函数处最终返回的是一个错误码!
于是我们对其进行
Patch
操作 -
然后加载并运行
非服务方式加载驱动
-
经过查阅相关资料,此处选择使用
ZwSetSystemInformation
方式加载驱动。 -
关键点有两个,一个是要求加载后驱动的路径恒为
C:/Driver_test.drv
,另一个就是成功加载驱动,这里我们选择选择使用MoveFile
这个API
函数来完成移动驱动的目的。 -
最终加载代码如下:
#include <Windows.h> #include <stdio.h> #include <atlconv.h> #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) const INT SystemLoadAndCallImage = 38; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWCH Buffer; } UNICODE_STRING, * PUNICODE_STRING; typedef struct _SYSTEM_LOAD_AND_CALL_IMAGE { UNICODE_STRING ModuleName; } SYSTEM_LOAD_AND_CALL_IMAGE, * PSYSTEM_LOAD_AND_CALL_IMAGE; typedef void(*RTLINITUNICODESTRING)( PUNICODE_STRING DestinationString, PCWSTR SourceString ); typedef NTSTATUS(__stdcall* ZwSetSystemInformation)( IN DWORD SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength ); int main(int argc, char* argv) { SYSTEM_LOAD_AND_CALL_IMAGE Images; RTLINITUNICODESTRING RtlInitUnicodeString; ZwSetSystemInformation fZwSetSystemInformation; char szDrvFullPath[256] = "C:\\Users\\error404\\Desktop\\Driver_test.drv"; if (!(RtlInitUnicodeString = (RTLINITUNICODESTRING)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "RtlInitUnicodeString"))) { printf("GetProcAddrss error:%d\n", GetLastError()); exit(-1); } if (!(fZwSetSystemInformation = (ZwSetSystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwSetSystemInformation"))) { printf("GetProcAddrss error:%d\n", GetLastError()); exit(-1); } MoveFileA(szDrvFullPath, "C:\\Driver.drv"); USES_CONVERSION; PWCHAR path = A2W("\\??\\C:\\Driver.drv"); RtlInitUnicodeString(&(Images.ModuleName), path); if (!NT_SUCCESS(fZwSetSystemInformation(SystemLoadAndCallImage, &Images, sizeof(SYSTEM_LOAD_AND_CALL_IMAGE)))) { printf("drive %s was loaded....", szDrvFullPath); } else { printf("dirve load error..."); } getchar(); return 1; }
分析驱动接口
-
首先,驱动和用户层交互的时候必然调用的是
I/O
相关的操作内容,那么首先定位到sub_140001ED0
。 -
然后我们进行函数跟进。
发现了四个功能模块函数,那么我们逐个分析。
-
第一个功能模块位于
sub_1400023F0
。可以看出调用控制码为
0x400000010
,输入结构为一个函数指针和一个QWORD型的变量 -
第二个功能模块位于
sub_1400022A0
。可以看出调用控制码为
0x800000008
,输入结构为一个数值, 8和0x1E8A0C。 -
第三个功能模块位于
sub_140002310
。没有给出调用控制码,输入结构为一个数值, 8和0x237BB3。
-
第四个功能模块位于
sub_140002380
。没有给出调用控制码,输入结构为一个数值, 8和0x237BB3。
-
由于没有明确的思路做后续分析,于是不再分析具体功能。推测功能模块已经被做了混淆和花指令处理,功能应该是和Windows内存隐藏技术相关。
-
Flag.fg可以判断出是一个shellcode:
但是分析flag字符串失败,因此没有开启Part2的题目。
附一个大佬的分析博客:https://bbs.pediy.com/thread-258803.htm
0 条评论