win_kernel_exp_1

环境配置

首先将目标机器调成debug模式:

1
2
3
4
5
C:\Windows\system32>bcdedit /copy {current} /d "Kernel Debugging On"
The entry was successfully copied to {自动生成的id}.

C:\Windows\system32>bcdedit /debug {自动生成的id} on
The operation completed successfully.

用COM pipe串行端口配置双机调试。

在目标机器上配置好HEVD驱动后有个坑,windbg里的HEVD的符号表必须配置C:\projects\hevd\build\driver\vulnerable\x86\HEVD\HEVD.pdb,按照习惯配置SRV*path*不行。

此时就可以看到HEVD驱动的symbol载入了:

1
2
3
4
5
6
7
8
9
10
11
12
0: kd> x /D HEVD!a*
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

9659e4f0 HEVD!AllocateFakeObjectNonPagedPoolNx (void)
9659e11c HEVD!AllocateFakeObjectNonPagedPool (void)
9659e236 HEVD!AllocateUaFObjectNonPagedPool (void)
9659e734 HEVD!AllocateUaFObjectNonPagedPoolNxIoctlHandler (void)
9659cbce HEVD!ArbitraryWriteIoctlHandler (void)
9659e216 HEVD!AllocateFakeObjectNonPagedPoolIoctlHandler (void)
9659e5ee HEVD!AllocateFakeObjectNonPagedPoolNxIoctlHandler (void)
9659e60e HEVD!AllocateUaFObjectNonPagedPoolNx (void)
9659e35a HEVD!AllocateUaFObjectNonPagedPoolIoctlHandler (void)

内核常用的两个函数

和设备驱动交互的句柄通过createFileA获得:

1
2
3
4
5
6
7
8
9
HANDLE CreateFileA(
[in] LPCSTR lpFileName, // 指向一个以 NULL 结尾的字符串,表示要创建或打开的目标文件名。可以是绝对路径或相对路径。
[in] DWORD dwDesiredAccess, // 指定对文件的访问权限。例如 GENERIC_READ(只读)、GENERIC_WRITE(写入)等。
[in] DWORD dwShareMode, // 指定文件的共享模式。例如 FILE_SHARE_READ(允许其他进程读取)、FILE_SHARE_WRITE(允许写入)等。
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 指向 SECURITY_ATTRIBUTES 结构的指针,指定安全描述符和句柄继承性。如果为 NULL,则使用默认安全属性。
[in] DWORD dwCreationDisposition, // 指定文件存在或不存在时的操作。例如 CREATE_NEW(创建新文件)、OPEN_EXISTING(打开现有文件)等。
[in] DWORD dwFlagsAndAttributes, // 指定文件属性和标志。例如 FILE_ATTRIBUTE_NORMAL(普通文件)、FILE_FLAG_OVERLAPPED(异步 I/O)等。
[in, optional] HANDLE hTemplateFile // 模板文件的句柄,用于复制模板文件的属性。如果为 NULL,则不使用模板文件。
);

获得句柄后使用deviceIoControl函数获得设备的输入和输出控制(IOCTL)接口:

1
2
3
4
5
6
7
8
9
10
BOOL DeviceIoControl(
[in] HANDLE hDevice, // 要操作的设备句柄。通常通过 CreateFile 或类似函数获取。
[in] DWORD dwIoControlCode, // 指定要执行的设备 I/O 控制代码(IOCTL)。该代码定义了具体的操作类型。
[in, optional] LPVOID lpInBuffer, // 指向输入缓冲区的指针,包含传递给设备的数据。如果操作不需要输入数据,则可以为 NULL。
[in] DWORD nInBufferSize, // 输入缓冲区的大小(以字节为单位)。如果 lpInBuffer 为 NULL,则此值应为 0。
[out, optional] LPVOID lpOutBuffer, // 指向输出缓冲区的指针,用于接收设备返回的数据。如果操作不返回数据,则可以为 NULL。
[in] DWORD nOutBufferSize, // 输出缓冲区的大小(以字节为单位)。如果 lpOutBuffer 为 NULL,则此值应为 0。
[out, optional] LPDWORD lpBytesReturned, // 指向一个 DWORD 变量的指针,用于接收实际返回的字节数。如果调用者不关心返回的字节数,则可以为 NULL。
[in, out, optional] LPOVERLAPPED lpOverlapped // 指向 OVERLAPPED 结构的指针,用于异步操作。如果设备句柄是以同步方式打开的,则此参数被忽略,可为 NULL。
);

简单调试一下DriverEntry

重启机器后break在HEVD的DriverEntry函数:

1
2
3
4
5
6
7
8
9
10
11
12
kd> bu HEVD!DriverEntry
kd>
kd> lm m H*
start end module name
82e44000 82e7a000 hal (deferred)
8ba00000 8ba08000 hwpolicy (deferred)
kd> g
KDTARGET: Refreshing KD connection
Breakpoint 0 hit
Breakpoint 1 hit
HEVD!DriverEntry:
96c7f000 55 push ebp

看一下IoCreateSymbolicLink的调用,call它的地址的最后1.5个字节是0xB4

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
3: kd> bu 96c7f0b4
3: kd> u 96c7f0b4
HEVD!DriverEntry+0xb4 [c:\projects\hevd\driver\hevd\hacksysextremevulnerabledriver.c @ 147]:
96c7f0b4 ff151090c396 call dword ptr [HEVD!_imp__IoCreateSymbolicLink (96c39010)]
96c7f0ba 8b350490c396 mov esi,dword ptr [HEVD!_imp__DbgPrintEx (96c39004)]
96c7f0c0 8bf8 mov edi,eax
96c7f0c2 6812f2c796 push offset HEVD! ?? ::PBOPGDP::`string' (96c7f212)
96c7f0c7 68aef3c796 push offset HEVD! ?? ::PBOPGDP::`string' (96c7f3ae)
96c7f0cc 6a03 push 3
96c7f0ce 6a4d push 4Dh
96c7f0d0 ffd6 call esi
3: kd> g
Breakpoint 3 hit
HEVD!DriverEntry+0xb4:
96c7f0b4 ff151090c396 call dword ptr [HEVD!_imp__IoCreateSymbolicLink (96c39010)]
3: kd> r
eax=8d7be9bc ebx=86e0a030 ecx=00000000 edx=85645240 esi=00000000 edi=86e0a0d8
eip=96c7f0b4 esp=8d7be9a0 ebp=8d7be9c8 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206
HEVD!DriverEntry+0xb4:
96c7f0b4 ff151090c396 call dword ptr [HEVD!_imp__IoCreateSymbolicLink (96c39010)] ds:0023:96c39010={nt!IoCreateSymbolicLink (82beab70)}
3: kd> dd esp L1
8d7be9a0 8d7be9bc
3: kd> dS 8d7be9bc
96c7f182 "\DosDevices\HackSysExtremeVulner"
96c7f1c2 "ableDriver"

IoCreateSymbolicLink函数调用:

1
2
3
4
5
6
7
8
9
10
11
NTSTATUS IoCreateSymbolicLink(
[in] PUNICODE_STRING SymbolicLinkName, // 指向一个 UNICODE_STRING 结构,表示要创建的符号链接名称。
// 符号链接是一个用户模式下的路径,用于映射到内核模式下的设备对象。
// 例如:SymbolicLinkName 可以是 "\\DosDevices\\MyDevice"。
// 这个路径通常是用户模式程序用来访问设备的接口。

[in] PUNICODE_STRING DeviceName // 指向一个 UNICODE_STRING 结构,表示目标设备的名称。
// 设备名称是内核模式下设备对象的路径,通常以 "\Device\" 开头。
// 例如:DeviceName 可以是 "\\Device\\MyDriverDevice"。
// 符号链接将指向这个设备名称,从而允许用户模式程序通过符号链接访问设备。
);

可以看到这个函数的第一个参数即DosDeviceName(用户模式下的符号链接名称)是\DosDevices\HackSysExtremeVulnerableDriver

驱动结构体如下:

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
typedef struct _DRIVER_OBJECT {
CSHORT Type;
// 类型字段,用于标识该结构体的类型。通常由系统设置,表示这是一个驱动对象。

CSHORT Size;
// 结构体的大小(以字节为单位)。系统使用此字段来验证结构体的完整性。

PDEVICE_OBJECT DeviceObject;
// 指向设备对象链表的第一个设备对象(DEVICE_OBJECT)。一个驱动程序可以管理多个设备对象。
// 如果没有设备对象,则此字段为 NULL。

ULONG Flags;
// 驱动对象的标志位,用于指示驱动的行为或状态。例如:
// - DO_DEVICE_INITIALIZING:设备正在初始化。
// - DO_BUFFERED_IO:驱动使用缓冲 I/O。
// - DO_DIRECT_IO:驱动使用直接 I/O。

PVOID DriverStart;
// 指向驱动程序代码在内存中的起始地址。通常由加载器设置。

ULONG DriverSize;
// 驱动程序代码的大小(以字节为单位)。

PVOID DriverSection;
// 指向描述驱动程序模块的内存段信息。通常由系统维护,用于调试或卸载。

PDRIVER_EXTENSION DriverExtension;
// 指向驱动扩展结构(DRIVER_EXTENSION),包含额外的驱动信息(如服务键路径)。

UNICODE_STRING DriverName;
// 驱动程序的名称(Unicode 字符串)。通常是以 "\Driver\<DriverName>" 的形式表示。

PUNICODE_STRING HardwareDatabase;
// 指向硬件数据库的 Unicode 字符串(通常是注册表路径)。
// 用于存储与硬件相关的配置信息。

PFAST_IO_DISPATCH FastIoDispatch;
// 指向快速 I/O 分发表(FAST_IO_DISPATCH)。快速 I/O 是一种优化路径,
// 用于处理简单的文件操作,而无需生成 IRP。

PDRIVER_INITIALIZE DriverInit;
// 指向驱动初始化函数(即 DriverEntry 函数)。这是驱动程序的入口点。

PDRIVER_STARTIO DriverStartIo;
// 指向驱动的 StartIo 函数。用于启动排队的 I/O 请求。
// 如果驱动不支持队列 I/O,则此字段为 NULL。

PDRIVER_UNLOAD DriverUnload;
// 指向驱动卸载函数。当驱动被卸载时,系统会调用此函数。
// 如果驱动不支持卸载,则此字段为 NULL。

PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
// 主要功能分发表(Major Function Dispatch Table),是一个函数指针数组。
// 每个元素对应一个 IRP 主功能代码(IRP_MJ_*),如 IRP_MJ_CREATE、IRP_MJ_READ 等。
// 当系统需要处理特定类型的 IRP 时,会调用相应的回调函数。
} DRIVER_OBJECT, *PDRIVER_OBJECT;

HEVD会将IRP功能设置为IRP_MJ_DEVICE_CONTROL,即处理IOCTL(I/O控制)请求,并将其指向IrpDeviceIoCtlHandler函数:

1
2
3
4
5
6
7
8
9
10
11
12
// 使用 memset32 将 DriverObject->MajorFunction 数组的前 0x1C (28) 个元素设置为 IrpNotImplementedHandler 函数指针。
// 这意味着所有未明确指定处理函数的 IRP 主功能代码将使用默认的 IrpNotImplementedHandler 处理函数。
// 这样可以确保驱动程序在接收到未实现的功能请求时不会崩溃,并返回一个适当的错误码。
memset32(DriverObject->MajorFunction, IrpNotImplementedHandler, 0x1Cu);
// 设置 MajorFunction 数组中索引为 0xE (14) 的元素为 IrpDeviceIoCtlHandler 函数指针。
// 这对应于 IRP_MJ_DEVICE_CONTROL 主功能代码,用于处理设备控制(IOCTL)请求。
// 当用户模式应用程序通过 DeviceIoControl API 发送 IOCTL 请求时,系统会调用此处理函数。
DriverObject->MajorFunction[0xE] = IrpDeviceIoCtlHandler;
// 设置 MajorFunction 数组中索引为 2 的元素为 IrpCreateCloseHandler 函数指针。
// 这对应于 IRP_MJ_CLOSE 主功能代码,当用户模式应用程序关闭设备句柄时生成。
// 此处理函数通常用于释放资源、清理状态等操作。
DriverObject->MajorFunction[2] = IrpCreateCloseHandler;

x86栈溢出