Windows 设备上的 Apple USB 下置过滤器如何帮助控制设备配置
如果您曾经将 iPhone、iPad 或 iPod 连接到 Windows PC,您可能已经注意到该设备显示为不同类型的设备,具体取决于您使用它做什么。例如,如果您正在为 iPhone 充电,它可能会显示为“USB 复合设备”,但如果您正在与 iTunes 同步音乐,它可能会显示为“Apple Mobile Device USB 驱动程序”。你有没有想过这是如何工作的?事实证明,苹果在Windows机器上有一个USB下过滤器,可以帮助他们控制操作系统使用哪些USB配置。
这篇博文将分为两部分:首先,我们将探讨 Apple 的 USB 下层过滤器的工作原理、它的作用以及无论是否安装了 Apple 软件,它如何提供不同的体验,其次,我们将调查为什么当设备的 WPD 属性指示设备正在使用协议媒体传输协议 (MTP) 时,iPhone 的开箱即用文件操作如此有限。我们将深入研究 Windows 便携式设备 (WPD)、USB 描述符和用户模式驱动程序框架 (UMDF) 等主题。WPD_DEVICE_PROTOCOL
备注:这篇文章的研究是使用Windows 11 22H2,AppleLowerFilter版本486.0.0.0和iPhone SE第二代进行的。
赋予动机
这篇博文背后的动机是一种好奇心,这种好奇心源于将iPhone连接到Windows机器时提供的有限选项。令人惊讶的是,查看iPhone的WPD属性,WPD设备协议被定义为,这很奇怪,因为接口描述符定义的类是Image。这种不匹配以及在不使用Apple软件的情况下与设备交互的有限选项困扰着我,因此我决定进行调查以尝试阐明这些观察结果。MTP: 15.20
初始化苹果的 USB 下部过滤器
Apple 设备将自己呈现为具有多个接口的复合设备,以确保正确识别其设备并加载所有必要的驱动程序。这是因为 Apple 设备通常具有多个接口,这些接口提供不同的功能,例如音频、视频和控制。当我们将Apple设备插入Windows机器时,总线适配器会识别该设备并将其硬件ID和兼容ID提供给操作系统。这些 ID 用于根据 ID 的匹配质量在驱动程序存储区中搜索最佳驱动程序。若要使总线驱动程序将此设备视为复合设备,必须满足某些要求。如果无法满足这些要求,操作系统将不会自动加载 USB 复合设备类驱动程序 (usbccgp)。在这种情况下,我们需要提供一个 INF 来加载通用父驱动程序,对于苹果来说,它是文
件 AppleUSB.inf
对于iPhone,未满足的要求是设备具有多种配置()。bNumConfigurations == 4
这个单一的INF文件包含不同设备(例如AppleUSB,AppleUsbHomePod和AppleUsbWatch)的各种设置配置。对于iOS设备,硬件ID将完全匹配,因此操作系统将应用AppleUSB设置配置,这将复制并在设备特定的注册表项下添加以下值:AppleLowerFilter.sys
[AppleUSB_CCGPDriverInstall_AddReg.HW]
HKR,,"OriginalConfigurationValue",0x00010001,2
HKR,,"UsbccgpCapabilities",0x00010001,0x10 ; Selective suspend for USB devices attached to the USB Composite Parent Driver
HKR,,FriendlyName,,%iPhone.AppleUSB.DeviceDesc% ;"Apple Mobile Device USB Composite Device"
HKR,,LowerFilters,0x00010000,AppleLowerFilter
该值可在设备的硬件注册表项中为 Usbccgp.sys 驱动程序设置。它确定应将复合设备的哪个配置用作默认配置。首次插入复合设备时,系统会读取原始配置值并加载指定的配置。这对于具有多个配置的复合设备非常有用,其中一种配置可能首选为默认配置。更多信息。OriginalConfigurationValue
安装驱动程序包后,Microsoft将详细介绍以下步骤 - Windows 如何安装设备:步骤 3 - 设备将重新启动。- 重新启动后,PnP 管理器识别设备的功能驱动程序和任何可选的筛选器驱动程序,构建设备堆栈 - 在我们的例子中,FDO 是 Usbccgp,LowerFiDO 是 AppleLowerFilter - 并通过为尚未加载的任何所需驱动程序调用 DriverEntry 例程来启动设备。然后为每个驱动程序调用 AddDevice 例程,从较低筛选器的驱动程序开始,然后是函数驱动程序。如果需要,将分配资源,PnP 管理器会向设备的驱动程序发送IRP_MN_START_DEVICE
深入了解苹果的USB下部过滤器
在上一节中介绍了 AppleLowerFilter 枚举和安装背后的理论之后,我们现在将仔细研究驱动程序的工作原理以及它在启用 Windows 机器上的 Apple 设备功能方面所扮演的角色。
作为 WDF 驱动程序,PnP 调用驱动程序条目的第一步是初始化框架并绑定 WDF 版本(在本例中为 WDF 1.15)。完成此操作后,框架将调用我们的 DriverEntry 函数,在 AppleLowerFilter 的情况下,驱动程序条目将简单地创建一个驱动程序对象,并在 .AppleLowerFilter 的 AddDevice 例程将执行以下操作:WDF_DRIVER_CONFIG
通过调用 WdfFdoInitSetFilter 将自身标识为 FiDO。
为事件注册 PnP 和电源管理回调:
EvtDevicePrepareHardware
EvtDeviceReleaseHardware
EvtDeviceD0Entry
EvtDeviceD0Exit
为 IRP 设置两个 IRP 预处理回调:
IRP_MJ_PNP
IRP_MJ_INTERNAL_DEVICE_CONTROL
最后,使用名为“上下文类型信息”创建 DO。FILTER_EXTENSION (sizeof == 0x50)
在上电序列期间,我们感兴趣的下一步是准备上电硬件,这意味着调用过滤器注册的 EvtDevicePrepareHardware 回调。这可能是AppleLowerFilter中最有趣的一步,所以让我们仔细看看它。
回调的第一步是检索 USB 描述符,这是通过我调用的函数完成的。此函数用于检索 USB 设备的 USB 设备描述符。这是通过为 URB(USB 请求块)分配内存并使用类型来完成的,该类型是从 USB 设备检索描述符的请求。请求的描述符是 ,它提供有关 USB 设备的信息,例如其供应商和产品 ID、设备类和协议。该函数同步提交 URB 以检索描述符。GetUsbDeviceDescriptorURB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICEUSB_DEVICE_DESCRIPTOR_TYPE
对于苹果正在使用usbdlib的大多数USB相关操作,这有点令人惊讶,因为这是一个WDF驱动程序,他们本可以使用wdfusb标头,我个人认为这会简化事情🙂。
然后,驱动程序将 存储到上下文中,并继续调用我认为此驱动程序的主要函数:。在描述内部结构之前,让我们看一下此函数的简单伪代码:bNumConfigurationsFILTER_EXTENSIONGetPreferredConfig
int PreferredConfig;
if (FilterCtx->bNumConfigurations == 1) {
PreferredConfig = 1;
} else {
if( !NT_SUCCESS( GetDeviceConfigUrb(WdfDevice, &PreferredConfig) ) {
PreferredConfig = 3;
}
}
FilterCtx->DeviceConfig = PreferredConfig;
if( !QueryAppleSoftwarePresent() ){
PreferredConfig = 1;
}
if ( PreferredConfig >= FilterCtx->bNumConfigurations) {
PreferredConfig = FilterCtx->bNumConfigurations;
}
if ( PreferredConfig >= 5 ) {
return 5;
}
return PreferredConfig;
此功能将首先检查设备的配置数量是否为 1。如果是,则首选配置设置为 1。否则,代码将发送 URB 以检索首选配置。如果 URB 请求失败,则首选配置设置为 3。然后,代码在 FilterCtx 结构中设置字段。然后,它会检查函数是否返回 false(表示未安装 Apple 软件),如果是,则将首选配置设置为 1。然后,代码检查首选配置是否大于或等于设备描述符中指定的配置数。如果是,则首选配置设置为最大配置数。最后,如果首选配置大于或等于 5,则代码返回 5。DeviceConfigQueryAppleSoftwarePresent
此函数中有几个关键点,我们将更深入地研究。首先突出的是函数,此函数将为 URB 分配内存,设置 URB 以发出特定于供应商的控制请求,然后将 URB 同步提交到 USB 驱动程序堆栈。发出的特定供应商请求是请求类型为 69 的控制传输和包含 <> 字节数据的传输缓冲区。GetDeviceConfigUrb
Urb->UrbHeader.Function = URB_FUNCTION_VENDOR_DEVICE;
Urb->UrbHeader.Length = sizeof(_URB_CONTROL_VENDOR_OR_CLASS_REQUEST);
Urb->UrbControlVendorClassRequest.TransferBufferLength = 1;
Urb->UrbControlVendorClassRequest.TransferBuffer = preferredConfig;
Urb->UrbControlVendorClassRequest.Request = 69; // Curious number choice by the Apple guys :D
Urb->UrbControlVendorClassRequest.Value = 0;
Urb->UrbControlVendorClassRequest.Index = 0;
Urb->UrbControlVendorClassRequest.TransferFlags = USBD_TRANSFER_DIRECTION_OUT | USBD_SHORT_TRANSFER_OK;
Status = SubmitUrbSync(wdfDevice, UsbdHandle, Urb);
选择首选配置的下一个关键点是函数,这个函数起着重要作用,因为它的返回值将决定我们是否总是被限制在一个首选配置上。此函数将执行以下操作:QueryAppleSoftwarePresent
// Check if we have already found the Apple Mobile Device Service
if (gAppleMobileServiceFound) {
return 1;
}
// pseudocode just for simplicity
auto EventString{"\BaseNamedObjects\com.apple.MobileDeviceProcess.QueryMuxReady"};
// Try to open the MuxReady event
if ( NT_SUCCESS( ZwOpenEvent(&eventHandle, STANDARD_RIGHTS_REQUIRED, &EventString) ) ) {
ZwClose(eventHandle)
return 1;
}
// I believe this is old behavior. At least with the latest version of iTunes from the
// Microsoft Store, these keys are not created.
auto serviceFound = QueryRegistry(
L"\Registry\Machine\System\CurrentControlSet\Services\Apple Mobile Device Service",
L"ImagePath");
auto appFound = QueryRegistry(
L"\Registry\Machine\System\CurrentControlSet\Services\Apple Mobile Device App",
0);
if (serviceFound || appFound) {
// Apple Mobile Device Service and app were found, so set the global flag
gAppleMobileServiceFound = true;
return 1;
}
// Apple NT event, Apple Mobile Device Service and app not found, so return 0
return 0;
回到函数,这个值起着很大的作用,因为这个函数返回的数字将用于覆盖设备的注册表项。GetPreferredConfigOriginalConfigurationValue
注意:返回的值将减去 1,因为 描述的注册表值对应于 USB 定义的配置索引,由配置描述符 () 的成员指示,而不是由设备配置描述符中报告的 bConfigurationNum 值指示。GetPreferredConfigOriginalConfigurationValuebConfigurationValueUSB_CONFIGURATION_DESCRIPTOR
我们刚刚看到的是,为什么即使您的INF将值写入 如果您将iPhone插入未安装iTunes的PC,您将在注册表中看到以下内容:2OriginalConfigurationValue
在注册表中设置 后,EvtDevicePrepareHardware 函数将调用 WdfUsbTargetDeviceCreate 来创建 USB 目标设备对象。USB 目标设备对象表示基础 USB 设备,并为驱动程序提供与设备通信的方法。OriginalConfigurationValue
要定期检查首选配置,该函数会设置 .将定期调用计时器的回调函数,以检查首选配置是否已更改。如果首选配置已更改,则该函数将同步调用 WdfUsbTargetDeviceCyclePortSync,以便意外删除并重新枚举设备,以便使用新配置加载。WDFTIMER
计时器设置了一个句点,因此框架不会调用计时器。另一方面,筛选器将从计时器的回调函数和 D5Entry 回调中以 0 秒(相对于当前系统时间)的 DueTime 进行调用。0WdfTimerStart
现在,让我们查看这两个 IRP 预处理回调,以全面了解驱动程序工作流。IRP 的预处理允许驱动程序在将 IRP 发送到默认处理程序或堆栈中的另一个驱动程序之前修改或重定向 IRP。让我们首先看一下内部设备控制请求的处理程序。此函数将检查 IRP 是否IOCTL_INTERNAL_USB_SUBMIT_URB请求以选择 USB 配置。如果是,该函数将获取设备的句柄,转发 IRP,然后检索具有类 Image 的 USB 接口的管道句柄。中断、批量输入和批量输出的管道句柄将存储在设备上下文中。
现在,让我们看一下 PnP IRP 预处理的处理程序。在这种情况下,句柄将处理两种情况:-IRP_MN_QUERY_DEVICE_RELATIONS - IRP_MN_QUERY_ID。QueryID IRP 的情况非常简单,该函数将检查 - 请记住,此值是通过供应商特定的 URB 获得的 - 是否设置为 1,如果是,该函数会将字符串附加到返回的信息和请求。FilterCtx->DeviceConfig&RESTORE_MODEBusQueryHardwareIDsBusQueryDeviceID。
另一方面,QueryDeviceRelation 更有趣一些。首先,此处理程序仅在某些计时器未运行(稍后会详细介绍)并且计算机上安装了Apple软件时才执行。它只会处理总线关系 IRP,它将同步转发请求并检查状态是否成功。如果返回任何信息,它将在返回的设备对象列表中查找其 CompatibleId 包含 的设备。如果找到,它会取消引用此 DO,然后将其从列表中删除并更新设备计数 – 这将使即使 usbccgp 为 WPD 设备创建了 PDO,PnP 也不会看到 DO,因为返回的列表不会有它。很快我们将看到较低的过滤器如何处理这个问题。USBClass_06
如果找到类 6 的设备,则该函数将根据 DbgPrints 设置另一个 WDF 计时器,我们将其称为 PtpTimer,它在 5 秒后触发。触发时,回调将在 deviceContext 中设置一个标志,以便 QueryDeviceRelations 处理程序不再处理请求,将检查 iTunes 是否存在,如果存在,它将向 USB 设备发送以下一组 PTP/MTP 操作请求数据包。
打开会话 - 操作代码:0x1002
供应商扩展 - 操作代码:0x9008
关闭会话 - 操作代码:0x1003
下面的 USB 数据包捕获说明了这些操作的执行,请注意它们在该端口上捕获大约 5 秒后是如何发生的。
我已经尽一切努力获取有关操作0x9008的更多信息,但似乎没有任何关于Apple设备的信息。我能得到的最好的是ChatGPT说“PTP / MTP数据包中的操作命令0x9008通常对应于”Apple设备信息“命令”。不幸的是,我要求提供证明这一点的文档/报价,聊天给我的每个链接要么无效,要么不可用/已弃用的Apple文档。给定名称“Apple Device Info”,我认为它类似于 PTP/MTP 命令“GetDeviceInfo”,但我在机器命令上尝试的每个测试似乎0x9008都没有数据阶段,所以我最好的猜测是要么不是“设备信息”命令,要么 Apple 设备不再响应该命令。但是,如果有人对此命令有更多信息,请联系,我很想听到更多关于它🙂的信息!
作为参考,来自ChatGPT的引用/链接:
最后,在发送 PTP/MTP 请求后,PtpTimer 将使用关系类型进行调用,这将触发新的 IRP 查询设备关系,但由于此时计时器已执行,因此处理程序不会从“设备”列表中删除 WPD 设备。因此,这次PnP管理器我们将实际看到WPD设备的PDO,并开始为其构建堆栈。下图显示了通过将我们自己的 LowerFilter 添加到堆栈并仅跟踪 IRP 由 AppleLowerFilter 处理的前置和后捕获的此行为。IoInvalidateDeviceRelationsBusRelation
我不知道苹果为什么要这样做,我的猜测是带有操作代码0x9008的 PTP 数据包以某种方式通知设备主机上存在 iTunes 或这些行周围的内容。我没有注意到在安装或不安装iTunes的WPD设备上有任何不同的行为,除了WPD设备需要5秒钟才能实际显示。从设备的 LowerFilter 列表中删除 AppleLowerFilter 似乎不会对 WPD 设备的行为产生任何重大影响。再说一次,如果有人知道更多关于苹果为什么要这样做,我很想听听!
这几乎就是AppleLowerFilter的行为方式,可以看出它主要在设备初始化期间工作,除此之外,检查活动配置的计时器将每5秒在后台运行一次,以查看端口是否必须重新枚举。
PTP还是MTP,这是个问题
在本节中,我们将重点介绍为什么iPhone没有像我们对使用MTP协议的设备所期望的那样在其存储上提供一整套操作。我们还将调查 USB 接口类/子类与属性不匹配的原因。为了回答这些问题,我们将了解如何创建 WPD 设备、如何“装载”存储以及如何设置 WPD 属性。WPD_DEVICE_PROTOCOL
首先让我们看看使用 PTP 和 iPhone 连接的 Android 设备之间的 WPD 设备协议属性的差异:
鉴于iPhone中的WPD协议属性,我们希望有一组更丰富的选项来与设备进行交互。我们可以通过查看设备的接口描述符来快速回答为什么iPhone表现为PTP设备。请参阅以下 iPhone 和小米在 PTP 和 MTP 模式下的描述符 – iPhone 有多种配置,但无论我们选择哪一种,创建 WPD PDO 的接口都将始终包含具有 Class 6 和子类 1 的接口。
iPhone Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x00 (Interface 0)
bAlternateSetting : 0x00
bNumEndpoints : 0x03 (3 Endpoints)
bInterfaceClass : 0x06 (Image)
bInterfaceSubClass : 0x01 (Still Imaging device)
bInterfaceProtocol : 0x01
iInterface : 0x0E (String Descriptor 14)
Language 0x0409 : "PTP"
Xiaomi MTP Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x00 (Interface 0)
bAlternateSetting : 0x00
bNumEndpoints : 0x03 (3 Endpoints)
bInterfaceClass : 0xFF (Vendor Specific)
bInterfaceSubClass : 0xFF
bInterfaceProtocol : 0x00
iInterface : 0x05 (String Descriptor 5)
Language 0x0409 : "MTP"
Xiaomi PTP Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x00 (Interface 0)
bAlternateSetting : 0x00
bNumEndpoints : 0x03 (3 Endpoints)
bInterfaceClass : 0x06 (Image)
bInterfaceSubClass : 0x01 (Still Imaging device)
bInterfaceProtocol : 0x01
iInterface : 0x00 (No String Descriptor)
尽管这回答了这个大问题,但仍有一些细节,例如为什么iPhone不允许在其中创建或复制任何内容,而另一方面,即使使用PTP的Xioami也允许创建对象,因此作为一个喜欢深入了解事物的人,仅仅瞥一眼界面描述符不足以满足我的好奇心。
由于此描述符将生成 兼容ID ,寻找与此 ID 匹配的 INF,我们找到 .在此 INF 中,我们可以获取 WPD 设备的 UMDF 部分的以下组件:USBClass_06&SubClass_01&Prot_01wpdmtp.inf
WpdMtp.dll:MTP 核心协议组件
WpdMtpUS.dll:MTP 驱动程序的 USB 扫描传输层
WpdMtpDr.dll:Windows 便携式设备媒体传输协议驱动程序
只是为了完成,作为内核方面的一部分,INF 将添加为下过滤器,反射器作为函数驱动程序。WinUSB.sysWUDFRd.sys
从上面提到的三个二进制文件中,主要的WPD MTP驱动程序将在 中运行,因此我们将从那里开始。这是一个 UMDFv1 驱动程序,它将强烈基于 COM 并以 C++ 编写,从好的方面来说,我们有 WpdWudfSampleDriver,这将使我们几乎没有逆转要做 - 令我惊讶的是,该驱动程序尚未更新为使用 UMDFv2,因为 UMDFv1 几乎已被弃用并且几乎没有对新功能的支持, 我想如果没有坏,不要修复它😅.WpdMtpDrWUDFHost
如上所示,我们的入口点将是 OnDeviceAdd 例程。在此函数中,将创建对象,这将我们带到 CDevice::OnPrepareHardware 例程,其中 WpdBaseDriver 通过调用 WpdBaseDriver::Initialize 进行初始化。不幸的是,这是示例代码的部分,并且将开始有所不同。示例代码没有要与之通信的真实设备,但在我们的例子中,我们有。这就是进来的地方,充当设备和真实设备之间的粘合剂。MTP 核心库包含表示真实设备的类。在 WpdBaseDriver 初始化期间,将加载 MTP 核心库,并打开与设备的会话,如以下简化代码片段所示。CDeviceWpdMtpDrWpdMtp.dllWpdMtpDrCMtpDevice
// No error handling
HANDLE hEvent{};
CMtpDevice * MtpCore{};
MTP_RESPONSECODE mtpRes = OK; // 0x2001
CoCreateInstance(&CLSID_WindowsMtp, NULL, CLSCTX_INPROC_SERVER, &IID_IMtp2, (LPVOID *)&MtpCore);
//
// wpdEvent represents a class that implements IMtpEventCallback, to get notifications from MTP events
//
MtpCore->Open(pszPortName, wpdEvents, NULL);
MtpCore->InitAsyncCancelEventHandle(hEvent);
CWpdDriverBase->m_mtpCore = MtpCore // Let's assume this will be wrapped in a ComPtr
MtpCore->OpenSession( 1, &mtpRes );
if( mtpRes == SessionAlreadyOpened || mtpRes == InvalidTransactionId ) { // 0x201E & 0x2004 respectively
MtpCore->ResetDevice( &mtpRes );
MtpCore->OpenSession( 1, &mtpRes );
}
MtpCore->Close();
加载 MTP 核心模块后,将触发初始化例程以检索 MTP 设备信息数据集。这是发送到设备的初始 MTP 请求之一,并且在其返回时填充 DeviceInfo 结构。值得注意的是,该结构包含关键信息,例如型号、制造商和各种 MTP 版本标识符,如 MTPforUSB-IFv5.1 规范的第 1.1.1 节所述。此类信息在以后设置 WPD 属性时起着至关重要的作用。
MTP 核心发送请求并将响应解析为结构,而 则利用缓存系统存储指向 返回的类的 COM 指针。此方法可防止频繁地向设备重新发出 PTP/MTP 请求,从而优化 I/O 操作。请注意,此缓存机制超出了本文的范围。CDeviceInfoWpdMtpDrWpdMtp
以下堆栈显示了首次调用此函数的时间:
0:008> k
# Child-SP RetAddr Call Site
00 000000b5`6027f3d8 00007ffb`db67dc66 wpdmtp!CMtpDevice::GetDeviceInfo
01 000000b5`6027f3e0 00007ffb`db6850fd wpdmtpdr!CMtpWrapper::GetDeviceInfo+0x10a
02 000000b5`6027f450 00007ffb`db65f058 wpdmtpdr!CMtpWrapper::IsDevicePropertySupported+0xf5
03 000000b5`6027f4f0 00007ffb`db65f3c6 wpdmtpdr!WpdBaseDriver::Initialize+0x58
04 000000b5`6027f570 00007ffb`db6494a9 wpdmtpdr!WpdBaseDriver::Initialize+0xbe
05 000000b5`6027f5a0 00007ffb`db64b9f6 wpdmtpdr!CDevice::InitializeBaseDriver+0x71d
06 000000b5`6027f660 00007ffb`db64acf9 wpdmtpdr!CDevice::_PrepareHardwareThread+0x4e
07 000000b5`6027f7b0 00007ffc`1f3626bd wpdmtpdr!CDevice::PrepareHardwareThread+0x9
08 000000b5`6027f7e0 00007ffc`2034a9f8 KERNEL32!BaseThreadInitThunk+0x1d
09 000000b5`6027f810 00000000`00000000 ntdll!RtlUserThreadStart+0x28
注意:我不会详细介绍WPD协议的工作原理,因为这本身就是一篇文章。但是,主要是有两个 IOCTL(用于只读和读写命令),它们将 WPD 有效负载打包在里面。在 UM 中,WPD 应用程序将生成通常使用 WPD API 的 WPD 命令,该命令将序列化此 WPD 命令并将其打包到 IOCTL 请求中,这将到达驱动程序,驱动程序将反序列化命令并采取相应的操作。有关 WPD 驱动程序如何处理此问题的示例,请参阅:CQueue::P rocessWpdMessage。
设备准备好接收 I/O 操作后,操作系统将尝试检索 WPD 设备属性,此信息位于对象 ID 中(此对象 ID 是预定义的,始终表示设备对象)。此请求将到达 WPD 驱动程序,该驱动程序将使用 .对于这种情况,这是设置值的方式:DEVICECDeviceInfoWPD_DEVICE_PROTOCOL
// No error handling
ULONG extId;
CString protocol;
USHORT extVersion;
IMtpDeviceInfo deviceInfo;
IPortableDeviceValues portableDeviceValues;
if( property == WPD_DEVICE_PROTOCOL) {
deviceInfo->GetVendorExtId( &extId );
deviceInfo->GetVendorExtVersion( &extVersion );
if( extId == MICROSOFT_VENDOR_EXT_ID ) { // MICROSOFT_VENDOR_EXT_ID == 6
protocol.Format(_T("MTP: %.2f"), extVersion / 100.0 );
} else {
protocol.Format(_T("PTP: %.2f"), extVersion / 100.0 );
}
portableDeviceValues->SetStringValue( &WPD_DEVICE_PROTOCOL, protocol);
}
所以现在如果我们看一下iPhone返回的DeviceInfo数据集,我们可以查看并最终回答为什么设置为。如果有人好奇,MS 将其定义为其 WMDRM 协议的一部分。这是MTP响应程序需要在DeviceInfo数据集中设置的值之一,以告诉MTP发起方它支持AAVT,令人惊讶的是,iPhone只添加此必需值,而不是其他值。有关详细信息,请参阅:MTP 供应商扩展标识消息和媒体传输协议 MS 增强规范VendorExtIdVendorExtVersionWPD_DEVICE_PROTOCOLMTP 15.20MICROSOFT_VENDOR_EXT_ID
WPD 设备属性中的一个快速详细信息是,有一个名为的属性仅用于表示目的。但既然我们在这里,我认为快速讨论它很有趣。此属性将在函数上检索,该函数将使用 SetupAPI 获取 CompatibleId,并使用大致如下的算法确定设备类型:WPD_DEVICE_TYPECDevicePropContext::GetDeviceType
如果为 ,则使用 从 ID 的“XXX”部分提取设备类型。MS_SUBCOMP_XXXwcstol
如果为 ,请尝试获取 PTP/MPT 属性“感知设备类型 (0xD407)”(如果未找到),请将“设备类型”设置为 WPD_DEVICE_TYPE_GENERIC。MS_COMP_MTP
如果两者都不是,请将“设备类型”设置为WPD_DEVICE_TYPE_MEDIA_TYPE。CLASS_06&SUBCLASS_01&PROT_01URN:MICROSOFT-COM:DEVICE:MTP:1
获取 .如果 VendorExtId 不是 6,请将 DeviceType 设置为 WPD_DEVICE_TYPE_CAMERA。否则,如果未找到,请尝试获取“感知设备类型”,将“设备类型”设置为WPD_DEVICE_TYPE_GENERIC,如果找到,请将“设备类型”设置为设备返回的值。DeviceInfo
对于iPhone,我们将到达步骤4,并且由于VendorExtId等于6并且不存在属性“感知设备类型”,因此DeviceType将设置为WPD_DEVICE_TYPE_GENERIC。
现在我们了解了属性的设置方式,让我们探讨一下为什么 Apple 设备的文件操作如此有限。对于任何熟悉 PTP/MTP 协议的人来说,他们可能已经知道这一点,但无论协议 PTP 或 MTP,设备中的每个存储对象(以 开头表示)都有自己的属性。同样,当设备上的 I/O 操作启动时,OS 使用两个关键操作从存储对象中检索信息:(检索 ) 的列表和(定义存储对象的行为方式)。我们将重点介绍后者,因为它返回一个包含以下三个关键字段的 StorageInfo 数据集(有关详细信息,请参阅 MTPforUSB-IFv5.2 规范的第 2.1.1 节)。WPD_DEVICE_PROTOCOLStorageIDssGetStorageIDs (0x1004)StorageIDsGetStorageInfo (0x1005)
存储类型
文件系统类型
访问能力
当 WPD 驱动程序首次尝试获取设备的存储信息时,请求将通过 MTP 核心模块。此模块将 PTP/MTP 操作请求发送到设备,并将生成的 StorageInfo 数据集返回到驱动程序。
0:008> k
# Child-SP RetAddr Call Site
00 000000b5`6027de48 00007ffb`db68387f wpdmtp!CMtpDevice::GetStorageInfo
01 000000b5`6027de50 00007ffb`db695d05 wpdmtpdr!CMtpWrapper::GetStorageInfo+0x117
02 000000b5`6027def0 00007ffb`db6cc52f wpdmtpdr!CStoragePropContext::CacheStorageUniqueId+0xed
03 000000b5`6027df40 00007ffb`db6cb7e9 wpdmtpdr!CHierarchyHandler::QueryStorages+0x1bb
04 000000b5`6027dfe0 00007ffb`db6c9c7e wpdmtpdr!CHierarchyHandler::QueryDeviceChildren+0x65
05 000000b5`6027e0c0 00007ffb`db68f589 wpdmtpdr!CHierarchyHandler::GetChildren+0x2ae
06 000000b5`6027e140 00007ffb`db672608 wpdmtpdr!CEnumContext::BeginFunctionalObjectsEnumeration+0xa9
07 000000b5`6027e230 00007ffb`db66f5a7 wpdmtpdr!WpdCapabilities::OnGetFunctionalObjects+0x118
08 000000b5`6027e2f0 00007ffb`db65e497 wpdmtpdr!WpdCapabilities::DispatchWpdMessage+0x18f
09 000000b5`6027e3e0 00007ffb`db65036d wpdmtpdr!WpdBaseDriver::DispatchWpdMessage+0x5e7
0a 000000b5`6027e6a0 00007ffb`db64f82d wpdmtpdr!CSessionQueue::ProcessWpdMessage+0x411
0b 000000b5`6027e720 00007ffc`14858eba wpdmtpdr!CSessionQueue::OnDeviceIoControl+0x22d
因此,如果我们查看iPhone如何响应此请求,我们将能够根据我们上面提到的三个字段确定存储对象的行为方式。
从上图中,我们得到以下信息:- 存储类型 == 固定 RAM,这是移动设备的标准配置。- 文件系统类型 == DCF,DCF 代表相机 FS 的设计规则,你们中的许多人会从著名的根目录中认出这一点。DCF 标准定义了在目录和文件上设置只读属性的选项。- 访问能力==只读,不删除对象,这是棺材上的钉子。这将定义对存储对象的访问限制,操作系统将遵守这些限制。例如,这将影响iPhone内上下文菜单中显示的选项。DCIM
所以在这里我们有它,这就是为什么iPhone上的文件选项如此有限的原因。仅供比较,下图显示了使用 PTP 插入小米设备时的 StorageInfo 数据集
事实证明,这就是为什么即使使用 PTP 协议连接,我也能够在小米设备上创建对象的原因。但是,值得注意的是,小米似乎对其MTP响应器有问题。无论在设备上选择了 PTP 还是 MTP,都会返回相同的数据集以响应请求,至少在 Redmi Note 8 型号上是这样。GetStorageInfo
这样,我们已经解决了本节前面提出的问题,从而更清楚地了解了 Apple 设备的行为方式的原因,以及如何为设备配置 WPD 属性。
苹果软件对苹果设备堆栈的影响
在结束这篇文章之前,让我们简要讨论一下在主机上安装 iTunes 时会发生什么,以及它如何启用诸如将文件复制到设备/从设备复制文件等操作。如前所述,由于存储对象中的限制,WPD API 将仅在 iPhone 上提供有限的操作子集。但是,安装 iTunes 时,它会添加一个不同的层,以便更全面地访问设备。
正如我们在AppleLowerFilter中看到的那样,一旦安装了iTunes,这将允许设备选择不同的USB配置描述符。如果没有iTunes,我们仅限于配置1,另一方面,默认情况下安装iTunes后,所选配置将为3。让我们快速浏览一下这两种配置及其接口:
------------------ Configuration Descriptor -------------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x02 (Configuration Descriptor)
wTotalLength : 0x0027 (39 bytes)
bNumInterfaces : 0x01 (1 Interface)
bConfigurationValue : 0x01 (Configuration 1)
iConfiguration : 0x05 (String Descriptor 5)
Language 0x0409 : "PTP"
bmAttributes : 0xC0
D7: Reserved, set 1 : 0x01
D6: Self Powered : 0x01 (yes)
D5: Remote Wakeup : 0x00 (no)
D4..0: Reserved, set 0 : 0x00
MaxPower : 0xFA (500 mA)
---------------- Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x00 (Interface 0)
bAlternateSetting : 0x00
bNumEndpoints : 0x03 (3 Endpoints)
bInterfaceClass : 0x06 (Image)
bInterfaceSubClass : 0x01 (Still Imaging device)
bInterfaceProtocol : 0x01
iInterface : 0x0E (String Descriptor 14)
Language 0x0409 : "PTP"
// Endpoint descriptors -> Bulk IN, Bulk OUT & Interrupt IN
------------------ Configuration Descriptor -------------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x02 (Configuration Descriptor)
wTotalLength : 0x003E (62 bytes)
bNumInterfaces : 0x02 (2 Interfaces)
bConfigurationValue : 0x03 (Configuration 3)
iConfiguration : 0x07 (String Descriptor 7)
Language 0x0409 : "PTP + Apple Mobile Device"
bmAttributes : 0xC0
D7: Reserved, set 1 : 0x01
D6: Self Powered : 0x01 (yes)
D5: Remote Wakeup : 0x00 (no)
D4..0: Reserved, set 0 : 0x00
MaxPower : 0xFA (500 mA)
---------------- Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x00 (Interface 0)
bAlternateSetting : 0x00
bNumEndpoints : 0x03 (3 Endpoints)
bInterfaceClass : 0x06 (Image)
bInterfaceSubClass : 0x01 (Still Imaging device)
bInterfaceProtocol : 0x01
iInterface : 0x0E (String Descriptor 14)
Language 0x0409 : "PTP"
// Endpoint descriptors -> Bulk IN, Bulk OUT & Interrupt IN
---------------- Interface Descriptor -----------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x04 (Interface Descriptor)
bInterfaceNumber : 0x01 (Interface 1)
bAlternateSetting : 0x00
bNumEndpoints : 0x02 (2 Endpoints)
bInterfaceClass : 0xFF (Vendor Specific)
bInterfaceSubClass : 0xFE
bInterfaceProtocol : 0x02
iInterface : 0x17 (String Descriptor 23)
Language 0x0409 : "Apple USB Multiplexor"
// Endpoint descriptors -> Bulk IN & Bulk OUT
选择配置 3,将使 usbccgp 生成设备 ID(01 提取自 bInterfaceNumber)。这些设备 ID 实际上是在 中定义的,它定义了以下文件的副本:USBVID_xxxx&PID_yyyy&MI_01appleusb.inf
[AppleUsbMux_Install]
Include=winusb.inf
Needs=WINUSB.NT
CopyFiles=AppleUsbFilter_CopyFiles
CopyFiles=AppleKmdfFilter_CopyFiles
FeatureScore=0x7F
[AppleUsbFilter_CopyFiles]
AppleUsbFilter.dll
[AppleKmdfFilter_CopyFiles]
AppleKmdfFilter.sys
这两个驱动程序将成为苹果称为“苹果移动设备USB设备”的设备的一部分,该设备使用专有协议而不是MTP或PTP与iPhone通信。Yoy 可以通过查看令人敬畏的 libimobiledevice 的源代码来了解有关此协议的更多信息。一旦驱动程序安装并运行,iTunes本身就会使用标准WPD API调用和自定义Apple特定命令的组合与iPhone进行通信。这允许iTunes提供将文件复制到/从设备复制文件,管理应用程序和备份以及更新设备固件等功能。
下图提供了 iPhone 的整个设备堆栈的简化概述,包括安装 iTunes 和创建 AppleUsbMux 设备的方案。
结论
在这篇文章中,我们探讨了Apple的USB下过滤器如何在Windows机器上工作,以及它的作用是提供不同的体验,具体取决于是否安装了Apple软件。我们还深入研究了 Windows 便携设备 (WPD) 和用户模式驱动程序框架 (UMDF) 等主题,以更好地了解 Apple 设备堆栈的内部工作原理。我们介绍了如何初始化和设置 WPD 设备,这有助于我们了解为什么 WPD 设备协议属性与 Apple 设备中的接口描述符定义的类不匹配。我们还研究了如何设置 WPD 设备的存储对象,以及这如何在我们必须在不使用第三方软件的情况下使用 iPhone 进行操作的限制中发挥作用。最后,我们简要讨论了安装iTunes如何在Apple移动设备堆栈中发挥作用,以及这如何使iTunes能够正确管理设备内容。
苹果希望保护某些信息并限制与iPhone存储交互的开箱即用选项是可以理解的,但是如果有一个更加混合的解决方案,用户可以在某些限制内拥有更大的灵活性,那就太好了。虽然iTunes为管理iPhone内容提供了强大的解决方案,但有时安装第三方软件可能不是一种选择。但是,我认识到,随着iTunes作为Microsoft商店应用程序的最近发布,此限制可能会减少。总的来说,虽然我理解苹果限制访问某些信息的方法,但更多的灵活性是受欢迎的。也许Mac是更好地与Apple设备😄集成的解决方案。
原文始发于微信公众号(安全狗的自我修养):windows中的苹果USB过滤驱动与iPhone-WPD工作原理
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论