磁盘盘符隐藏并访问隐藏磁盘的文件数据

Krismile

发布日期: 2018-12-19 21:17:03 浏览量: 1305
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

背景

之前,帮一个小伙伴开发了一个程序,这个程序就是对磁盘盘符进行隐藏与显示。也就是说,我们打开资源管理器,在资源管理器中隐藏指定磁盘,不显示在界面上。而且,我们还可以使用程序对这个隐藏后的磁盘文件数据进行读写。

这个程序的实现原理,主要是删除和创建卷加载点实现来实现的。其中,我们给出两种方法来创建隐藏磁盘,分别是使用WIN32 API 函数 DefineDosDeivce 以及 SetVolumeMountPoint 来实现。现在,我就把实现过程整理成文档,分享给大家。

函数介绍

QueryDosDevice 函数

获取有关MS-DOS设备名称的信息。 该功能可以获得特定MS-DOS设备名称的当前映射。 该功能还可以获取所有现有MS-DOS设备名称的列表。

函数声明

  1. DWORD WINAPI QueryDosDevice(
  2. _In_opt_ LPCTSTR lpDeviceName,
  3. _Out_ LPTSTR lpTargetPath,
  4. _In_ DWORD ucchMax
  5. );

参数

  • lpDeviceName [in,optional]
    指定查询目标的MS-DOS设备名称字符串。设备名称不能有尾随的反斜杠;例如,使用“C:”,而不是“C:\”。此参数可以为NULL。在这种情况下,QueryDosDevice功能将将所有现有的MS-DOS设备名称的列表存储到lpTargetPath指向的缓冲区中。
  • lpTargetPath [out]
    指向将接收查询结果的缓冲区的指针。该函数用一个或多个以null结尾的字符串填充此缓冲区。最后以空值终止的字符串后跟一个额外的NULL。
    如果lpDeviceName不为NULL,则该函数将检索有关由lpDeviceName指定的特定MS-DOS设备的信息。存储在缓冲区中的第一个以null结尾的字符串是设备的当前映射。其他以null结尾的字符串表示设备的未删除的先前映射。
    如果lpDeviceName为NULL,则该函数将检索所有现有MS-DOS设备名称的列表。存储在缓冲区中的每个以null结尾的字符串都是现有MS-DOS设备的名称,例如\ Device \ HarddiskVolume1或\ Device \ Floppy0。
  • ucchMax [in]
    lpTargetPath指向的缓冲区中可以存储的最大TCHAR数。

返回值

  • 如果函数成功,则返回值是存储在lpTargetPath指向的缓冲区中的TCHAR数。
  • 如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。
  • 如果缓冲区太小,则该函数失败,最后一个错误代码为ERROR_INSUFFICIENT_BUFFER。

DefineDosDevice 函数

定义,重新定义或删除MS-DOS设备名称。

函数声明

  1. BOOL WINAPI DefineDosDevice(
  2. _In_ DWORD dwFlags,
  3. _In_ LPCTSTR lpDeviceName,
  4. _In_opt_ LPCTSTR lpTargetPath
  5. );

参数

  • dwFlags [in]
    DefineDosDevice功能的可控方面。 此参数可以是以下值中的一个或多个:
VALUE MEANING
DDD_EXACT_MATCH_ON_REMOVE 如果此值与DDD_REMOVE_DEFINITION一起指定,则该函数将使用完全匹配来确定要删除的映射。 使用此值可确保不删除未定义的内容
DDD_NO_BROADCAST_SYSTEM 不要广播WM_SETTINGCHANGE消息。 默认情况下,该消息被广播以通知shell和应用程序的更改
DDD_RAW_TARGET_PATH 使用lpTargetPath字符串。 否则,它将从MS-DOS路径转换为路径
DDD_REMOVE_DEFINITION 删除指定设备的指定定义。 要确定要删除的定义,该函数将会遍历设备的映射列表,查找与此设备关联的每个映射的前缀的lpTargetPath的匹配。 匹配的第一个映射是删除的映射,然后该函数返回。如果lpTargetPath为NULL或指向NULL字符串的指针,则该函数将删除与设备关联的第一个映射,并弹出最近推送的映射。 如果没有什么可以弹出,设备名称将被删除。如果未指定此值,则由lpTargetPath参数指向的字符串将成为此设备的新映射。
  • lpDeviceName [in]
    指向MS-DOS设备名称字符串的指针,指定功能正在定义,重新定义或删除的设备。 设备名称字符串不得有冒号作为最后一个字符,除非正在定义,重新定义或删除驱动器号。 例如,驱动器C将是字符串“C:”。 在任何情况下都不允许使用尾部反斜杠(“\”)。

  • lpTargetPath [in]
    指向将实现此设备的路径字符串的指针。 字符串是一个MS-DOS路径字符串,除非指定了DDD_RAW_TARGET_PATH标志,在这种情况下,此字符串是一个路径字符串。

返回值

  • 如果函数成功,则返回值不为零。
  • 如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

DeleteVolumeMountPoint 函数

删除驱动器号或安装的文件夹。

函数声明

  1. BOOL WINAPI DeleteVolumeMountPoint(
  2. _In_ LPCTSTR lpszVolumeMountPoint
  3. );

参数

  • lpszVolumeMountPoint [in]
  • 要删除的驱动器号或安装的文件夹。 需要尾随的反斜杠,例如“X:\”或“Y:\ MountX \”。

返回值

  • 如果函数成功,则返回值不为零。
  • 如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

GetVolumeNameForVolumeMountPoint 函数

检索与指定卷装入点(驱动器盘符,卷GUID路径或已装载文件夹)相关联的卷的卷GUID路径。

函数声明

  1. BOOL WINAPI GetVolumeNameForVolumeMountPoint(
  2. _In_ LPCTSTR lpszVolumeMountPoint,
  3. _Out_ LPTSTR lpszVolumeName,
  4. _In_ DWORD cchBufferLength
  5. );

参数

  • lpszVolumeMountPoint [in]
    指向包含已安装文件夹路径(例如“Y:\ MountX \”)或驱动器盘符(例如“X:\”)的字符串的指针。 字符串必须以尾部反斜杠(’\’)结尾。
  • lpszVolumeName [out]
    指向接收卷GUID路径的字符串的指针。 此路径的格式为“\?\ Volume {GUID} \”,其中GUID是用于标识卷的GUID。 如果该卷存在多个卷GUID路径,则仅返回安装管理器缓存中的第一个卷。
  • cchBufferLength [in]
    输出缓冲区的长度,在TCHAR中。 缓冲区容纳最大容量GUID路径的合理大小为50个字符。

返回值

  • 如果函数成功,则返回值不为零。
  • 如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。

SetVolumeMountPoint 函数

将卷与驱动器号或另一卷上的目录相关联。

函数声明

  1. BOOL WINAPI SetVolumeMountPoint(
  2. _In_ LPCTSTR lpszVolumeMountPoint,
  3. _In_ LPCTSTR lpszVolumeName
  4. );

参数

  • lpszVolumeMountPoint [in]
    与卷关联的用户模式路径。 这可能是驱动器号(例如“X:\”)或其他卷上的目录(例如“Y:\ MountX \”)。 字符串必须以尾部反斜杠(’\’)结尾。
  • lpszVolumeName [in]
    卷的卷GUID路径。 此字符串的格式必须为“\?\ Volume {GUID} \”,其中GUID是用于标识卷的GUID。 “\?\”关闭路径解析,并作为路径的一部分被忽略,如命名卷所述。

返回值

  • 如果函数成功,则返回值不为零。
  • 如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。
  • 如果lpszVolumeMountPoint参数包含已安装文件夹的路径,即使目录为空,GetLastError返回ERROR_DIR_NOT_EMPTY。

实现原理

在资源管理器中隐藏磁盘显示的原理就是:把磁盘对应的卷加载点删除,这样,磁盘就没有相应的驱动器号了,就不会在资源管理器中显示。对于删除卷加载点,我们可以使用 DeleteVolumeMountPoint 函数实现。

在资源管理器中还原回隐藏的磁盘,原理就是:重新创建磁盘的卷加载点,并未磁盘分配一个驱动器号。但是,在创建卷加载点之前,也就是在删除卷加载点之前,我们就要使用 GetVolumeNameForVolumeMountPoint 获取卷加载点对应的卷名。因为,当我们使用 SetVolumeMountPoint 函数的时候,需要用到卷名,为相应的卷名创建卷加载点,分配驱动器号。

创建隐藏盘符,方便我们程序访问的原理是:我们为删除卷加载点的磁盘,分配一个非字母的的驱动器号,这样,磁盘在资源管理器中是不显示的。但是,我们的程序可以通过这个非字母的盘符,正常访问盘符里的文件数据,和正常的字母盘符一样访问。在此,创建一个非字母的磁盘设备,我们可以有两种实现方式,均可以达到上述所说的效果:

  • 使用 DefineDosDevice 函数来实现,在使用 DefineDosDevice 之前,就需要获取磁盘对应的 Dos 路径,也就是说, 在删除卷加载点之前,先调用 QueryDosDevice 函数获取磁盘对应的 Dos 路径。之后,再使用 DefineDosDevice 函数将 Dos 路径对应的磁盘创建一个非字母驱动器的路径。

  • 使用 SetVolumeMountPoint 函数来实现,在使用 SetVolumeMountPoint 之前,需要通过 GetVolumeNameForVolumeMountPoint 函数来获取磁盘对应的卷名。后来,我们通过 SetVolumeMountPoint 为卷名对应的磁盘分配一个非字母驱动器号的卷加载点。

编码实现

删除卷加载点,隐藏盘符

  1. // 隐藏磁卷加载点, 实现磁盘隐藏
  2. BOOL HideValume(char *pszDriver)
  3. {
  4. BOOL bRet = ::DeleteVolumeMountPoint(pszDriver);
  5. if (FALSE == bRet)
  6. {
  7. ShowError("DeleteVolumeMountPoint");
  8. return FALSE;
  9. }
  10. return TRUE;
  11. }

获取磁盘对应的卷名

  1. // 获取磁盘对应的卷名
  2. ::GetVolumeNameForVolumeMountPoint("E:\\", szVolumeName, MAX_PATH);

设置卷加载点,显示磁盘

  1. // 显示卷加载点, 恢复磁盘显示
  2. BOOL ShowValume(char *pszDriver, char *pszVolumeName)
  3. {
  4. /* 注意在使用SetVolumeMountPoint的时候,挂载点目录必须存在,而且必须为空目录,否则程序会运行失败 */
  5. while (ERROR_DIR_NOT_EMPTY == ::SetVolumeMountPoint(pszDriver, pszVolumeName))
  6. {
  7. // 更改加载盘符
  8. pszDriver[0]++;
  9. }
  10. return TRUE;
  11. }

使用 DefineDosDevice 创建隐藏磁盘

  1. // 创建隐藏盘符
  2. BOOL CreateHideVolume(char *lpszDosPath)
  3. {
  4. // 创建隐藏盘符,对于非字母盘符,在"我的电脑"里是不可见的,只有程序可以访问
  5. if (::DefineDosDevice(DDD_RAW_TARGET_PATH, MY_HIDEN_DRIVER, lpszDosPath))
  6. {
  7. return TRUE;
  8. }
  9. return FALSE;
  10. }

删除隐藏磁盘路径

  1. // 删除 1:
  2. DeleteHideVolume(szDosPath);
  3. // 删除 2:
  4. HideValume("2:\\");

程序测试

我们在 main 函数中,调用上述封装好的函数进行测试。首先,我们先获取将要隐藏磁盘对应的卷名以及 Dos 路径。然后,我们开始删除卷加载点,实现磁盘的隐藏。接着,我们使用 DefineDosDeivce 的方法创建一个非字母驱动器路径 1:,并拷贝 520.exe 文件到非字符驱动器路径的根目录下 1:\ ,测试非字母路径能否正常访问。然后,我们使用 SetVolumeMountPoint 创建一个非字母的驱动器 2:\,并拷贝 520.exe 文件到非字符驱动器路径的根目录下 2:\ ,测试非字母路径能否正常访问。最后,我们便删除上述两种方法创建的非字母驱动器号路径,并恢复正确的磁盘路径,显示磁盘。

  1. int _tmain(int argc, _TCHAR* argv[])
  2. {
  3. char szVolumeName[MAX_PATH] = { 0 };
  4. char szDosPath[MAX_PATH] = { 0 };
  5. // 获取磁盘对应的卷名
  6. ::GetVolumeNameForVolumeMountPoint("E:\\", szVolumeName, MAX_PATH);
  7. // 获取磁盘路径对应的Dos路径
  8. ::QueryDosDevice("E:", szDosPath, MAX_PATH);
  9. // 删除卷加载点来实现磁盘隐藏
  10. HideValume("E:\\");
  11. system("pause");
  12. // 使用 DefineDosDevice 创建一个非字母驱动器号的磁盘路径 1:
  13. CreateHideVolume(szDosPath);
  14. system("pause");
  15. // 复制文件到隐藏磁盘
  16. if (FALSE == ::CopyFile("520.exe", "1:\\520__111111.exe", FALSE))
  17. {
  18. printf("copy file error[%d].\n", ::GetLastError());
  19. }
  20. printf("copy file ok.\n");
  21. system("pause");
  22. // 使用 SetVolumeMountPoint 创建一个非字母驱动器号的磁盘路径 2:
  23. ShowValume("2:\\", szVolumeName);
  24. system("pause");
  25. // 复制文件到隐藏磁盘
  26. if (FALSE == ::CopyFile("520.exe", "2:\\520_22222222.exe", FALSE))
  27. {
  28. printf("copy file error[%d].\n", ::GetLastError());
  29. }
  30. printf("copy file ok.\n");
  31. system("pause");
  32. // 删除 1:
  33. DeleteHideVolume(szDosPath);
  34. // 删除 2:
  35. HideValume("2:\\");
  36. // 恢复正确磁盘路径
  37. ShowValume("E:\\", szVolumeName);
  38. system("pause");
  39. return 0;
  40. }

我们以管理员权限运行程序,测试结果正确:

总结

要注意的是,程序是需要管理员或者管理员以上权限才可以正常执行。同时,也需要理解上述的两种方法实现的对隐藏磁盘数据文件的读写。理解 DefineDosDevice 和 SetVolumeMountPoint 函数的参数含义以及具体的使用方法。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download HideVolume_Test.7z ( 144.60kb, 6次下载 )

发送私信

真正的强者不是要压倒一切,而是不被一切压倒

16
文章数
12
评论数
最近文章
eject