2008年9月5日星期五

TrueCrypt

实现功能:
将用户指定的某个文件A 虚拟成一个分区B, 用户在分区上B操作的数据会保存到文件A上

需要解决的问题:
1、如何虚拟出一个新的设备(分区)?
2、如何将访问这个分区的操作转化为读取某个文件的操作?

2008.09.02

大概看了 TrueCrypt 源代码

1.
DriverEntry() 中
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; ++i)
{
DriverObject->MajorFunction[i] = TCDispatchQueueIRP;
}

2、TCDispatchQueueIRP 处理 IRP 代码
case IRP_MJ_FLUSH_BUFFERS:
case IRP_MJ_READ:
case IRP_MJ_WRITE:
case IRP_MJ_DEVICE_CONTROL:
if (Extension->bRootDevice)
{
if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL)
return ProcessMainDeviceControlIrp (DeviceObject, Extension, Irp);
break;
}
在处理IRP时,还需要注意区分是系统真实分区发出的IRP,还是由虚拟出来的分区发出的IRP

3、ProcessMainDeviceControlIrp() 中
case TC_IOCTL_MOUNT_VOLUME:
Irp->IoStatus.Information = sizeof (MOUNT_STRUCT);
Irp->IoStatus.Status = MountDevice (DeviceObject, mount);
DriverMutexRelease ();
判断由应用程序发出的 CTL_CODE

4、MountDevice() 中
ntStatus = TCCreateDeviceObject (DeviceObject->DriverObject, &NewDeviceObject,
mount);
if (!NT_SUCCESS (ntStatus))
{
Dump ("Mount CREATE DEVICE ERROR, ntStatus = 0x%08x\n", ntStatus);
return ntStatus;
}

5、TCCreateDeviceObject() 中
NTSTATUS
TCCreateDeviceObject (PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT * ppDeviceObject,
MOUNT_STRUCT * mount)
{
UNICODE_STRING Win32NameString, ntUnicodeString;
WCHAR dosname[32], ntname[32];
PEXTENSION Extension;
NTSTATUS ntStatus;
ULONG devChars = 0;
Dump ("TCCreateDeviceObject BEGIN\n");
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
TCGetDosNameFromNumber (dosname, mount->nDosDriveNo);
TCGetNTNameFromNumber (ntname, mount->nDosDriveNo);
RtlInitUnicodeString (&ntUnicodeString, ntname);
RtlInitUnicodeString (&Win32NameString, dosname);
devChars = FILE_DEVICE_SECURE_OPEN;
devChars |= mount->bMountReadOnly ? FILE_READ_ONLY_DEVICE : 0;
devChars |= mount->bMountRemovable ? FILE_REMOVABLE_MEDIA : 0;
Dump ("Creating device nt=%ls dos=%ls\n", ntname, dosname);
ntStatus = IoCreateDevice (
DriverObject, /* Our Driver Object */
sizeof (EXTENSION), /* Size of state information */
&ntUnicodeString, /* Device name "\Device\Name" */
FILE_DEVICE_DISK, /* Device type */
devChars, /* Device characteristics */
FALSE, /* Exclusive device */
ppDeviceObject); /* Returned ptr to Device Object */
if (!NT_SUCCESS (ntStatus))
{
Dump ("TCCreateDeviceObject NTSTATUS = 0x%08x END\n", ntStatus);
return ntStatus;/* Failed to create DeviceObject */
}
/* Initialize device object and extension. */
(*ppDeviceObject)->Flags |= DO_DIRECT_IO;
(*ppDeviceObject)->StackSize += 2; // Reduce occurrence of NO_MORE_IRP_STACK_LOCATIONS bug check caused by buggy drivers
/* Setup the device extension */
Extension = (PEXTENSION) (*ppDeviceObject)->DeviceExtension;
memset (Extension, 0, sizeof (EXTENSION));
Extension->IsVolumeDevice = TRUE;
Extension->lMagicNumber = 0xabfeacde;
Extension->nDosDriveNo = mount->nDosDriveNo;
Extension->bRemovable = mount->bMountRemovable;
KeInitializeEvent (&Extension->keCreateEvent, SynchronizationEvent, FALSE);
KeInitializeSemaphore (&Extension->RequestSemaphore, 0L, MAXLONG);
KeInitializeSpinLock (&Extension->ListSpinLock);
InitializeListHead (&Extension->ListEntry);
Dump ("TCCreateDeviceObject STATUS_SUCCESS END\n");
return STATUS_SUCCESS;
}

虚拟出一个分区的关键应该在 (5) 中,等明天再试。

2008.09.03

在昨天的基础上, 尝试将创建虚拟设备的部份代码抽出来
代码如下:

1#ifdef __cplusplus
2extern "C"
3{
4#endif
5#include
6#ifdef __cplusplus
7}
8#endif
9#ifdef NT4_DRIVER
10#define DRIVER_STR WIDE
11#else
12#define DRIVER_STR
13#endif
14#define NT_MOUNT_PREFIX DRIVER_STR("\\Device\\TrueCryptVolume")
15PDEVICE_OBJECT gSFilterDriverObject = NULL;
16/**//* This structure is allocated for non-root devices! WARNING: bRootDevice
17 must be the first member of the structure! */
18typedef struct EXTENSION
19{
20} EXTENSION, *PEXTENSION;
21typedef struct _DEVICE_EXTENSION
22{
23PDEVICE_OBJECT fdo;
24PDEVICE_OBJECT NextStackDevice;
25UNICODE_STRING ifSymLinkName;
26}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
27extern "C" NTSTATUS AddDevice(IN PDRIVER_OBJECT DriverObject,
28IN PDEVICE_OBJECT PhysicalDeviceObject)
29{
30
31 NTSTATUS status;
32 PDEVICE_OBJECT fdo;
33
34 status = IoCreateDevice(
35 DriverObject,
36 sizeof(DEVICE_EXTENSION),
37 NULL,
38 FILE_DEVICE_DISK,
39 0,
40 FALSE,
41 &fdo);
42
43 if( !NT_SUCCESS(status))
44 return status;
45
46 PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
47 dx->fdo = fdo;
48
49 dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
50 fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
51 fdo->Flags &= ~DO_DEVICE_INITIALIZING;
52
53 return STATUS_SUCCESS;
54}
55extern "C" void
56TCGetNTNameFromNumber (LPWSTR ntname, int nDriveNo)
57{
58 WCHAR tmp[3] =
59 {0, ':', 0};
60 int j = nDriveNo + (WCHAR) 'A';
61 tmp[0] = (short) j;
62 wcscpy (ntname, (LPWSTR) NT_MOUNT_PREFIX);
63 wcsncat (ntname, tmp, 1);
64}
65extern "C" NTSTATUS
66DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
67{
68 NTSTATUS ntStatus;
69 WCHAR dosname[32], ntname[32];
70 ULONG devChars = 0;
71 // 定义一个 Unicode 字符串
72 UNICODE_STRING ntUnicodeString;
73 //RtlInitUnicodeString(&nameString, L"\\FileSystem\\Filters\\SFilter");
74 DriverObject->DriverExtension->AddDevice = AddDevice;
75 KdPrint(("SFilter!DriverEntry\n"));
76
77 TCGetNTNameFromNumber (ntname, 3);
78 RtlInitUnicodeString (&ntUnicodeString, ntname);
79 KdPrint(("SFilter!DriverEntry: Create Driver \"%wZ\"\n", &ntUnicodeString));
80 devChars = FILE_DEVICE_SECURE_OPEN;
81
82 PDEVICE_OBJECT pDeviceObject = NULL;
83
84 // 创建虚拟设备
85 ntStatus = IoCreateDevice (
86 DriverObject, /**//* Our Driver Object */
87 sizeof (EXTENSION), /**//* Size of state information */
88 &ntUnicodeString, /**//* Device name "\Device\Name" */
89 FILE_DEVICE_DISK, /**//* Device type */
90 devChars, /**//* Device characteristics */
91 FALSE, /**//* Exclusive device */
92 &pDeviceObject); /**//* Returned ptr to Device Object */
93
94 if ( !NT_SUCCESS( ntStatus ) )
95 {
96 KdPrint(("SFilter!DriverEntry: Error Creating Control Device Object \"%wZ\", status=%08x\n", &ntUnicodeString, ntStatus));
97 }
98 else
99 {
100 KdPrint(("SFilter!DriverEntry: Success Creating Control Device Object \"%wZ\", status=%08x\n", &ntUnicodeString, ntStatus));
101 }
102
103 return ntStatus;
104}
105


使用 windgb 调试得到

SFilter!DriverEntry
SFilter!DriverEntry: Create Driver "?"
SFilter!DriverEntry: Error Creating Control Device Object "?", status=c000003b
问题:
设备创建失败, 可能是由 ntUnicodeString 非法,
但为何 ntUnicodeString 会变成 ?
经试验, 将 KdPrint 的 \"%wZ\", 改为 %s

KdPrint(("SFilter!DriverEntry\n"));
TCGetNTNameFromNumber (ntname, 3);

KdPrint(("SFilter!DriverEntry: Create Driver ntname = \"%s\"\n", &ntname));

RtlInitUnicodeString (&ntUnicodeString, ntname);
KdPrint(("SFilter!DriverEntry: Create Driver ntUnicodeString = \"%s\"\n", &ntUnicodeString));
--------------------------------------------------
输出结果
SFilter!DriverEntry: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntUnicodeString = "$"
SFilter!DriverEntry: Error Creating Control Device Object "?", status=c000003b

经过 RtlInitUnicodeString (&ntUnicodeString, ntname); 后
ntUnicodeString 变成了 "?" ?
于是查 MSDN RtlInitUnicodeString 相关的信息, 用法似乎没有错
修改了一下程序
extern "C" void
TCGetNTNameFromNumber (LPWSTR ntname, int nDriveNo)
{
WCHAR tmp[3] = {0, ':', 0};
int j = nDriveNo + (WCHAR) 'A';
tmp[0] = (short) j;
wcscpy (ntname, (LPWSTR) NT_MOUNT_PREFIX);

KdPrint(("TCGetNTNameFromNumber: Create Driver ntname = \"%d\"\n", nDriveNo));
KdPrint(("TCGetNTNameFromNumber: Create Driver ntname = \"%s\"\n", ntname));

wcsncat (ntname, tmp, 1);

KdPrint(("TCGetNTNameFromNumber: Create Driver ntname = \"%s\"\n", ntname));
}

extern "C" NTSTATUS
DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS ntStatus;
WCHAR dosname[32], ntname[32];
ULONG devChars = 0;

// 定义一个 Unicode 字符串
UNICODE_STRING ntUnicodeString;
//RtlInitUnicodeString(&nameString, L"\\FileSystem\\Filters\\SFilter");

DriverObject->DriverExtension->AddDevice = AddDevice;

KdPrint(("SFilter!DriverEntry\n"));


TCGetNTNameFromNumber (ntname, 3);

KdPrint(("SFilter!DriverEntry: Create Driver ntname = \"%s\"\n", &ntname));

RtlInitUnicodeString (&ntUnicodeString, ntname);

KdPrint(("SFilter!DriverEntry: Create Driver ntUnicodeString = \"%s\"\n", &ntUnicodeString));




输出结果如下:
SFilter!DriverEntry
TCGetNTNameFromNumber: Create Driver ntname = "3"
TCGetNTNameFromNumber: Create Driver ntname = "\Device\TrueCryptVolume"
TCGetNTNameFromNumber: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntUnicodeString = "H"
SFilter!DriverEntry: Error Creating Control Device Object "?", status=c000003b

接着,蓝屏……

表示很怀疑,把 tmp 的值也打印出来

KdPrint(("TCGetNTNameFromNumber: Create Driver tmp = \"%s\"\n", tmp));
wcsncat (ntname, tmp, 1);
----------------------------------------------------------------------------------------
结果显示
SFilter!DriverEntry
TCGetNTNameFromNumber: Create Driver ntname = "3"
TCGetNTNameFromNumber: Create Driver ntname = "\Device\TrueCryptVolume"
TCGetNTNameFromNumber: Create Driver tmp = "D"
TCGetNTNameFromNumber: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntUnicodeString = "H"
SFilter!DriverEntry: Error Creating Control Device Object "?", status=c000003b

---
根据代码的执行结果:
tmp 的值应该是 D: 才对!!!!! 奇怪!!!

问题,
1、KdPrint 在打印 WCHAR[] 与 UNICODE_STRING 时的处理
2、WCHAR[] 与 UNICODE_STRING 的连接又该如何?(如果没有成功编译 TrueCrypt,所以无法跟踪到它里面的值的变化)
--------------------------------------------------------
于是先把创建设备的代码去掉,先把 ntUnicodeString 正确显示出来


#ifdef __cplusplus
extern "C"
{
#endif
#include
#ifdef __cplusplus
}
#endif
#ifdef NT4_DRIVER
#define DRIVER_STR WIDE
#else
#define DRIVER_STR
#endif
#define NT_MOUNT_PREFIX DRIVER_STR("\\Device\\TrueCryptVolume")
PDEVICE_OBJECT gSFilterDriverObject = NULL;
/**//* This structure is allocated for non-root devices! WARNING: bRootDevice
must be the first member of the structure! */
typedef struct EXTENSION
{
} EXTENSION, *PEXTENSION;
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT fdo;
PDEVICE_OBJECT NextStackDevice;
UNICODE_STRING ifSymLinkName;
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
extern "C" NTSTATUS AddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{

NTSTATUS status;
PDEVICE_OBJECT fdo;

status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_DISK,
0,
FALSE,
&fdo);

if( !NT_SUCCESS(status))
return status;

PDEVICE_EXTENSION dx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
dx->fdo = fdo;

dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;

return STATUS_SUCCESS;
}
extern "C" void
TCGetNTNameFromNumber (LPWSTR ntname, int nDriveNo)
{
WCHAR tmp[3] = {0, ':', 0};
int j = nDriveNo + (WCHAR) 'A';
tmp[0] = (short) j;
wcscpy (ntname, (LPWSTR) NT_MOUNT_PREFIX);

KdPrint(("TCGetNTNameFromNumber: Create Driver ntname = \"%d\"\n", nDriveNo));
KdPrint(("TCGetNTNameFromNumber: Create Driver ntname = \"%s\"\n", ntname));

KdPrint(("TCGetNTNameFromNumber: Create Driver tmp = \"%s\"\n", tmp));
wcsncat (ntname, tmp, 1);

KdPrint(("TCGetNTNameFromNumber: Create Driver ntname = \"%s\"\n", ntname));
}
extern "C" NTSTATUS
DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
WCHAR dosname[32], ntname[32];
ULONG devChars = 0;
// 定义一个 Unicode 字符串
UNICODE_STRING ntUnicodeString;
//RtlInitUnicodeString(&nameString, L"\\FileSystem\\Filters\\SFilter");
DriverObject->DriverExtension->AddDevice = AddDevice;
KdPrint(("SFilter!DriverEntry\n"));

TCGetNTNameFromNumber (ntname, 3);

KdPrint(("SFilter!DriverEntry: Create Driver ntname = \"%s\"\n", &ntname));

RtlInitUnicodeString (&ntUnicodeString, ntname);
KdPrint(("SFilter!DriverEntry: Create Driver ntUnicodeString = \"%s\"\n", &ntUnicodeString));
/**//*
devChars = FILE_DEVICE_SECURE_OPEN;

PDEVICE_OBJECT pDeviceObject = NULL;

// 创建虚拟设备
ntStatus = IoCreateDevice (
DriverObject, /* Our Driver Object * /
sizeof (EXTENSION), /* Size of state information * /
&ntUnicodeString, /* Device name "\Device\Name" * /
FILE_DEVICE_DISK, /* Device type * /
devChars, /* Device characteristics * /
FALSE, /* Exclusive device * /
&pDeviceObject); /* Returned ptr to Device Object * /

if ( !NT_SUCCESS( ntStatus ) )
{
KdPrint(("SFilter!DriverEntry: Error Creating Control Device Object \"%wZ\", status=%08x\n", &ntUnicodeString, ntStatus));
}
else
{
KdPrint(("SFilter!DriverEntry: Success Creating Control Device Object \"%wZ\", status=%08x\n", &ntUnicodeString, ntStatus));
}*/

return ntStatus;
}

---
代码改成了这样后, 调试依然会蓝屏。


SFilter!DriverEntry
TCGetNTNameFromNumber: Create Driver ntname = "3"
TCGetNTNameFromNumber: Create Driver ntname = "\Device\TrueCryptVolume"
TCGetNTNameFromNumber: Create Driver tmp = "D"
TCGetNTNameFromNumber: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntname = "\Device\TrueCryptVolume"
SFilter!DriverEntry: Create Driver ntUnicodeString = "H"

*** Fatal System Error: 0x0000007e
(0xC0000005,0x46656D61,0xF9EA2770,0xF9EA246C)

Break instruction exception - code 80000003 (first chance)

A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.

A fatal system error has occurred.



需要回滚代码。明天再继续。





2008.09.04

今天打算先看下应用层与驱动通信的方法
然后再看下由 Bill Gates 说的(做file disk建议看filedisk的源代码,在http://www.acc.umu.se/~bosse。)
在开始工作之前,先感谢一下 BillGates ,虽然他已经退休了,但居然光临我的BLOG,

读 filedisk 的代码前,想先使用一下它
看了 Readme,安装 filedisk 的步骤(它居然连 .inf 都省了,直接用.reg 晕!!!! )
1、把 filedisk.sys 拷贝到 %systemroot%\system32\drivers\
2、filedisk.reg 里面设置的驱动加载方面默认是{自动}的,所以不用改了
产生问题1:
有了这个 .reg 就相当于 .inf? .reg 安装驱动跟 .inf 安装驱动方法的区别?
-- 未解决
3、运行 filedisk.reg 然后重启
4、重启后, 在cmd 窗体执行命令
filedisk /mount 0 c:\test.img 8M e:
5、此时,系统多出一个 e 盘,双击提示未格式化,格式化完 E 盘后,就可以像普通磁盘一样操作了

产生问题1: 看了下设备管理器并没有多出一个设备,那么E盘怎么出来的?
用 WinObj.exe 也无所发现 (先迷茫一下先!!!)

接下来,试下编译 filedisk sys 的源程序
build filedisk 没出现问题,生成了 check 的 filedisk.sys

然后,试下编译 filedisk exe 的程序
在 exe 目录下 nmake /f makefile ,却出现错误提示
Microsoft (R) Program Maintenance Utility Version 6.00.9782.0
Copyright (C) Microsoft Corp 1988-1998. All rights reserved.

makefile(7) : fatal error U1052: file '\makefile.def' not found
Stop.

google了一下,找到方法
据说 exe 同样用DDK的环境来编译
在 filedisk 的 exe 目录 build -cz

果然,编译通过了生成了 filedisk.exe

产生问题1:
filedisk 的 exe 为何不能用 nmake 来编译?
产生问题2:
ddk 也可以用来编译 exe 程序?
产生问题3:
如果用 vc 建一个 Win32 Console Application 工程又能否编译?
-- 试了一下,果然是可以滴!!

把自己编译的 filedisk.sys 和 filedisk.exe 放到虚拟机试下是否正常先.
因为刚刚在前面已经设置了驱动是自动加载的,怕覆盖 filedisk.sys 后,系统还是会用回原来的 filedisk.sys
于是重启一下系统先~!

产生很奇怪的问题1:
用自己编译的 exe && sys 运后,可以正常虚拟一个分区出来,但 win 提示不能格式化分区
-- 用原先的 exe 再试一下, 试过后, win 仍然提示不能格式化分区
于是用回 原先的 sys 再试一下,
突然间怀疑,是不是 映象文件的问题,因为我用的是
filedisk /mount 0 c:\test.txt 8M e:
后缀名是 .txt
改成 filedisk /mount 0 c:\test.img 8M e: 后 win 提示格式化分区成功,于是

产生很奇怪的问题2:
filedisk 的映象文件的后缀名如果为 .txt ,会导致 win 不能格式化虚拟出来的分区?
按道理应该跟映象文件的后缀名无关才是,估计要研究下里面的代码才清楚了!!!
-- 未解决

另外还产生很奇怪的问题3:
我把映象文件的大小设置 1K, win会提示“无法完成格式化”,难道 win 需要空间存放文件分配表?
试了一下,映象文件的大小在 3M 左右,win才可以格式化成功,具体值无尝试
另一方面,如果 windows 的分区在格式化时,它应该会建一个{文件分配表}之类的东西,
而这个{文件分配表}的大小应该跟分区的大小有关 (暂无找到相关的资料,未解决)
-- 未解决

大概看了下 filedisk.c 的代码, 里面用的是
CreateFile + DeviceIoControl 的方法 跟 驱动通信
里面并没有采用动态加载驱动
问题1:
如何动态加载驱动?
之前在网上看到的资料应该有“SM”之类的API可以动态加载驱动,
这个有时间再研究.

一个上午的时间就这样被花光了!!!!!




问题1:
运行 TrueCrypt\Driver\BuildDriver.cmd ,不能编译驱动,提示 "BuildDriver.cmd: error: Cannot copy target."
没有时间看 ReadMe. 未解决

问题2:
将 TrueCrypt.sln 用工具转换为 TrueCrypt.dsw ,打开后无论编译哪一个工程,都会提示很多错误信息
未解决

问题3:
IoCreateDevice 最后一个参数 Returned ptr to Device Object, 是否需要为其分配空间
-- Answered by bill Bill Gates : Device Object对象由系统分配,系统会在这个对象reference count为0的时候自动销毁。

安装 filedisk 后
产生问题1: 看了下设备管理器并没有多出一个设备,那么E盘怎么出来的?
用 WinObj.exe 也无所发现 (先迷茫一下先!!!)

产生问题1:
filedisk 的 exe 为何不能用 nmake 来编译?
产生问题2:
ddk 也可以用来编译 exe 程序?
产生问题3:
如果用 vc 建一个 Win32 Console Application 工程又能否编译?
-- 试了一下,果然是可以滴!!

产生很奇怪的问题1:
用自己编译的 exe && sys 运后,可以正常虚拟一个分区出来,但 win 提示不能格式化分区
-- 用原先的 exe 再试一下, 试过后, win 仍然提示不能格式化分区
于是用回 原先的 sys 再试一下,
突然间怀疑,是不是 映象文件的问题,因为我用的是
filedisk /mount 0 c:\test.txt 8M e:
后缀名是 .txt
改成 filedisk /mount 0 c:\test.img 8M e: 后 win 提示格式化分区成功,于是

产生很奇怪的问题2:
filedisk 的映象文件的后缀名如果为 .txt ,会导致 win 不能格式化虚拟出来的分区?
按道理应该跟映象文件的后缀名无关才是,估计要研究下里面的代码才清楚了!!!
-- 未解决

另外还产生很奇怪的问题3:
我把映象文件的大小设置 1K, win会提示“无法完成格式化”,难道 win 需要空间存放文件分配表?
试了一下,映象文件的大小在 3M 左右,win才可以格式化成功,具体值无尝试
另一方面,如果 windows 的分区在格式化时,它应该会建一个{文件分配表}之类的东西,
而这个{文件分配表}的大小应该跟分区的大小有关 (暂无找到相关的资料,未解决)

此篇是(关于 TrueCrypt)的续集, 在看此篇前先看上篇,不然会很危险

从 DriverEntry 看起

NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
// 读取注册时使用的变量
UNICODE_STRING parameter_path;
RTL_QUERY_REGISTRY_TABLE query_table[2];
ULONG n_devices;

NTSTATUS status;
UNICODE_STRING device_dir_name;
OBJECT_ATTRIBUTES object_attributes;
ULONG n;
USHORT n_created_devices;
// #define PARAMETER_KEY L"\\Parameters"
parameter_path.Length = 0;
parameter_path.MaximumLength = RegistryPath->Length + sizeof(PARAMETER_KEY);
parameter_path.Buffer = (PWSTR) ExAllocatePool(PagedPool, parameter_path.MaximumLength);
if (parameter_path.Buffer == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyUnicodeString(¶meter_path, RegistryPath);
RtlAppendUnicodeToString(¶meter_path, PARAMETER_KEY);
RtlZeroMemory(&query_table[0], sizeof(query_table));
query_table[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
query_table[0].Name = NUMBEROFDEVICES_VALUE; // #define NUMBEROFDEVICES_VALUE L"NumberOfDevices"
query_table[0].EntryContext = &n_devices;
status = RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE,
parameter_path.Buffer,
&query_table[0],
NULL,
NULL
);
ExFreePool(parameter_path.Buffer);
if (!NT_SUCCESS(status))
{
// 如果读取注册表失败, 则默认为 4个
// #define DEFAULT_NUMBEROFDEVICES 4
KdPrint(("FileDisk: Query registry failed, using default values.\n"));
n_devices = DEFAULT_NUMBEROFDEVICES;
}
// 下面的代码完成在设备目录中创建对应的目录
/*
* #define DEVICE_BASE_NAME _T("\\FileDisk")
* #define DEVICE_DIR_NAME _T("\\Device") DEVICE_BASE_NAME
* #define DEVICE_NAME_PREFIX DEVICE_DIR_NAME DEVICE_BASE_NAME
*/
RtlInitUnicodeString(&device_dir_name, DEVICE_DIR_NAME);
InitializeObjectAttributes(
&object_attributes,
&device_dir_name,
OBJ_PERMANENT,
NULL,
NULL
);
status = ZwCreateDirectoryObject(
&dir_handle,
DIRECTORY_ALL_ACCESS,
&object_attributes
);
if (!NT_SUCCESS(status))
{
return status;
}
// 将上面创建的设备目录属性设置为 临时
ZwMakeTemporaryObject(dir_handle);
for (n = 0, n_created_devices = 0; n < n_devices; n++)
{
status = FileDiskCreateDevice(DriverObject, n, FILE_DEVICE_DISK);
if (NT_SUCCESS(status))
{
n_created_devices++;
}
}
for (n = 0; n < n_devices; n++)
{
status = FileDiskCreateDevice(DriverObject, n, FILE_DEVICE_CD_ROM);
if (NT_SUCCESS(status))
{
n_created_devices++;
}
}
if (n_created_devices == 0)
{
ZwClose(dir_handle);
return status;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = FileDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FileDiskCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = FileDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = FileDiskReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FileDiskDeviceControl;
DriverObject->DriverUnload = FileDiskUnload;
return STATUS_SUCCESS;
}


DriverEntry() 做的大概东西就是:
1、读取注册表的
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\FileDisk\Parameters\
下面的 NumberOfDevices 这个值
2、如果指定的设备目录(\\FileDisk\\Device)不存在, 创建对应的设备目录
3、然后调用 IoCreateDevice() 在创建的设备目录下创建设备
4、每创建一个设备都同时创建一个线程,具体用来做什么,正在摸索中.........

问题1: RTL_QUERY_REGISTRY_TABLE query_table[2]; // 这里为何要定义为 2?
-- 未解决,先看下在应用层读取注册表是如何做的先!

问题2: 将上面创建的设备目录属性设置为 临时 ZwMakeTemporaryObject(dir_handle);
-- 未解决,设置属性为临时的作用是? 重启系统时这个设备目录会不存在? 如果不设置这个属性,重启系统后还存在?

问题3: 为何在 DriverEntry() 里就用 IoCreateDevice() 创建设备?不是等应用层运行用 mount 后才创建设备的吗?
-- 未解决,先郁闷一下!!!!

问题4: 关于[设备目录]的疑问,[设备目录]是个什么东东?……
-- 未解决,在N久前,好像看过这样一篇文章,讲的是 Windows 下面所有内核对象都是采用对象管理 之类的
有空再找下这方面的详细资料!!! 再郁闷一下!!!

问题5、FileDiskCreateDevice() 函数中在调用了 IoCreateDevice() 创建设备后
设置了 device_object 的属性, 这些语句的作用是什么?
device_object->Flags |= DO_DIRECT_IO;
device_extension = (PDEVICE_EXTENSION) device_object->DeviceExtension;

device_extension->media_in_device = FALSE;

if (DeviceType == FILE_DEVICE_CD_ROM)
{
device_object->Characteristics |= FILE_READ_ONLY_DEVICE;
device_extension->read_only = TRUE;
}


另外奇怪的是, 为何设置 device_object 的属性后,不需要调 update() 之类的函数,
重新设置的属性在设置完后就生效????

问题6、FileDisk.sys 在 DriverEntry() 就创建了四个设备,四条内核线程,导致 mount 最多就 4 个设备,
为何不做成可以动态加载N个设备?(因为我见过其它类似于 TrueCrypt 的工具,是可以动态Mount N个分区的)

接下来,应该先了解下 驱动与应用层的通信方式是怎样的先!Google 一下

首先应该了解下在 Windows 下面,应用层(Ring3)跟内核(Ring0)的通信是如何进行的。
先不管内核(Ring0),先把 Ring3 弄明白再说
Google 到了下面一段代码

/**//* The code of interest is in the subroutine GetDriveGeometry. The
code in main shows how to interpret the results of the IOCTL call. */

#include
#include
#include

BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)
{
HANDLE hDevice; // handle to the drive to be examined
BOOL bResult; // results flag
DWORD junk; // discard results

hDevice = CreateFile("\\\\.\\PhysicalDrive0", // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
return (FALSE);
}

bResult = DeviceIoControl(hDevice, // device to be queried
IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform
NULL, 0, // no input buffer
pdg, sizeof(*pdg), // output buffer
&junk, // # bytes returned
(LPOVERLAPPED) NULL); // synchronous I/O

CloseHandle(hDevice);

return (bResult);
}

int main(int argc, char *argv[])
{
DISK_GEOMETRY pdg; // disk drive geometry structure
BOOL bResult; // generic results flag
ULONGLONG DiskSize; // size of the drive, in bytes

bResult = GetDriveGeometry (&pdg);

if (bResult)
{
printf("Cylinders = %I64d\n", pdg.Cylinders);
printf("Tracks per cylinder = %ld\n", (ULONG) pdg.TracksPerCylinder);
printf("Sectors per track = %ld\n", (ULONG) pdg.SectorsPerTrack);
printf("Bytes per sector = %ld\n", (ULONG) pdg.BytesPerSector);

DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder *
(ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector;
printf("Disk size = %I64d (Bytes) = %I64d (Mb)\n", DiskSize,
DiskSize / (1024 * 1024));
}
else
{
printf("GetDriveGeometry failed. Error %ld.\n", GetLastError());
}

return ((int)bResult);
}




上述的程序,用 CreateFile 打开 “\\\\.\\PhysicalDrive0” 这个设备,
据说 PhysicalDrive0 这个设备就是“第一块物理硬盘”了,至少是不是一定就是引导盘,不太清楚

问题1:PhysicalDrive0 是不是一定就是引导盘??

然后通过 DeviceIoControl() 与 设备交互
最后 CloseHandle()

打开设备就像打开文件一样,如果把 DeviceIoControl() 换成 WriteFile 呢, 哈哈哈,,试一下先



BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)
{
HANDLE hDevice; // handle to the drive to be examined
BOOL bResult; // results flag
DWORD junk; // discard results

hDevice = CreateFile("\\\\.\\PhysicalDrive0", // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes

if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
return (FALSE);
}

DWORD dwRet = 0 ;

if( WriteFile(hDevice, "abc", 3, &dwRet, NULL) == FALSE)
{
printf("Error WriteFile: LastErrorCode = %d \n", ::GetLastError());
return FALSE;
};

CloseHandle(hDevice);

return (bResult);
}


为了安全起见,在虚拟机下运行,运行结果如下:
Error WriteFile: LastErrorCode = 5

查了 ErrorLookUp
5 的错误是 Access is denied.
哈哈~~~如果 WriteFile 成功不就见鬼了!!!

从安全的角度来看,NT以上的系统好像是不能直接对硬盘的绝对扇区操作的,至于 WriteFile PhysicalDrive0 失败的具体原因,不太清楚。

问题2: WriteFile PhysicalDrive0 失败的具体原因? (ReadFile没试,理论上应当也会失败)

总结一下:
1、关于 CreateFile 时所使用的设备名
一些标准设备的设备名,微软已经定义好了,
比如 PhysicalDrive0 这些,如果是自己的驱动所创建的设备,当然名字可以任由自己取

在 CreateFile 打开设备时,设备的名字通常为 \\.\DeviceName (在C++中的字符串则表示为
\\\\.\\DeviceName)

在驱动.sys 当然需要做一些东西,才可以让应用层通过这个设备名来访问(具体见下集分解)


2、DeviceIoControl() 这个函数是与驱动层通信的关键

一个操作码,一个输入缓冲区,一个输出缓冲区
具体做什么,可以当然需要驱动与应用程序之间事先商量好的,
微软同样定义了一些标准设备的操作码,在 winioctl.h 文件中


今天好累, 去睡觉了~~~~~


-- 未解决

没有评论: