【问题标题】:How should "NewUserClient" be implemented“NewUserClient”应该如何实现
【发布时间】:2020-05-01 15:34:06
【问题描述】:

我正在尝试与应用程序中的 dext 进行交互。我能够使用IOServiceOpen 找到服务,并且我接到了对我的dext 的NewUserClient 的调用(我可以看到传递的type 参数正在日志中输出)。在此之后,我有点失落。在这里阅读NewUserClient 我可以看到应该使用Create 来创建一个新的服务对象。

讨论部分herepropertiesKey 字典中的键描述了新服务。

这个字典应该放在系统扩展的plist文件中作为顶级条目,还是应该用IOKitPersonalities中的键放置字典?

我可以将IOServiceDEXTEntitlements 键保留为空值,以便不对连接到系统扩展的应用程序的权利施加任何限制吗?

我的 plist 看起来像这样(MyUserClientProperties 键/字典在两个地方)。

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">  
<plist version="1.0">  
<dict>  
  <key>CFBundleDevelopmentRegion</key>  
  <string>$(DEVELOPMENT_LANGUAGE)</string>  
  <key>CFBundleExecutable</key>  
  <string>$(EXECUTABLE_NAME)</string>  
  <key>CFBundleIdentifier</key>  
  <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>  
  <key>CFBundleInfoDictionaryVersion</key>  
  <string>6.0</string>  
  <key>CFBundleName</key>  
  <string>$(PRODUCT_NAME)</string>  
  <key>CFBundlePackageType</key>  
  <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>  
  <key>CFBundleShortVersionString</key>  
  <string>1.0</string>  
  <key>CFBundleVersion</key>  
  <string>1</string>  
  <key>MyUserClientProperties</key>  
       <dict>  
           <key>IOClass</key>  
           <string>MyUserClient</string>  
           <key>IOUserClass</key>  
           <string>MyUserUSBInterfaceDriver</string>  
           <key>IOServiceDEXTEntitlements</key>  
           <string></string>  
       </dict>  
  <key>IOKitPersonalities</key>  
  <dict>  
  <key>example_device</key>  
  <dict>  
      <key>MyUserClientProperties</key>  
           <dict>  
               <key>IOClass</key>  
               <string>MyUserClient</string>  
               <key>IOUserClass</key>  
               <string>MyUserUSBInterfaceDriver</string>  
               <key>IOServiceDEXTEntitlements</key>  
               <string></string>  
           </dict>  
  <key>CFBundleIdentifier</key>  
  <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>  
  <key>IOClass</key>  
  <string>IOUserService</string>  
  <key>IOProviderClass</key>  
  <string>IOUSBHostInterface</string>  
  <key>IOUserClass</key>  
  <string>MyUserUSBInterfaceDriver</string>  
  <key>IOUserServerName</key>  
  <string>sc.example.MyUserUSBInterfaceDriver</string>  
      <key>bConfigurationValue</key>  
      <integer>0x1</integer>  
      <key>bInterfaceNumber</key>  
      <integer>0x0</integer>  
      <key>idVendor</key>  
      <integer>0x123</integer>  
      <key>idProduct</key>  
      <integer>0x08</integer>  
  </dict>  
  </dict>  
    <key>OSBundleUsageDescription</key>  
    <string>Example user space USB driver</string>  
</dict>  
</plist>  

我是否需要将SUPERDISPATCH 作为最后一个参数传递给Create

来自“OSX 和 iOS 内核编程”第 5 章第 81 页:

I/O Kit 设计的独创性在于用户客户端对象本身就是一个驱动程序对象:IOUserClient 类继承自 IOService,并且与任何其他 IOService 实例一样,每个用户客户端都有一个提供者类,对于用户客户端,是应用程序控制的驱动程序实例。

虽然上述内容可能只对 kext (?) 正确,但我认为对于 dext,事情的工作方式相同,

来自Create 文档:使用 kIOUserClassKey 键指定您希望系统实例化的自定义 IOService 子类的名称。

为什么需要实例化另一个IOService 类?这门课的目的是什么?它是继承自IOUserClient 的我的班级的提供者吗?如果是这样,我怎样才能使我的驱动程序实例(实现NewUserClient 的驱动程序)成为提供程序?

来自Create 文档:使用kIOClassKey 指定自定义IOUserClient 子类的名称以返回给您的服务的客户端。

将创建并分配给Create 的第三个参数的类的类型是什么?如果是这样,那是我应该分配IOUserClient*指针的那个,它被传递给NewUserClient吗?

kern_return_t IMPL(MyUserUSBInterfaceDriver, NewUserClient) {  
  os_log(OS_LOG_DEFAULT, "%{public}d:", type);  

  IOPropertyName propertiesKey = "MyUserClientProperties";  

  IOService* client;  

  auto ret = Create(this, propertiesKey, &client, SUPERDISPATCH);  
  // Need to do more things here...  
  return ret;  
}  

无论我尝试什么,我总是得到一个断言,但我看不出是什么原因造成的。

3   com.apple.DriverKit            0x0000000102f2b24b __assert_rtn + 102  
4   com.apple.DriverKit            0x0000000102f2c20a IOService::Create_Impl(IOService*, char const*, IOService**) (.cold.2) + 35  
5   com.apple.DriverKit            0x0000000102f1766b IOService::Create_Impl(IOService*, char const*, IOService**) + 91  
6   com.apple.DriverKit            0x0000000102f2668f IOService::Create_Invoke(IORPC, OSMetaClassBase*, int (*)(OSMetaClassBase*, IOService*, char const*, IOService**)) + 135  
7   com.apple.DriverKit            0x0000000102f276d7 IOService::Create(IOService*, char const*, IOService**, int (*)(OSMetaClassBase*, IORPC)) + 267  
8   sc.example.MyUserUSBInterfaceDriver 0x0000000102ee0c89 MyUserUSBInterfaceDriver::NewUserClient_Impl(unsigned int, IOUserClient**) + 313 (MyUserUSBInterfaceDriver.cpp:155)

【问题讨论】:

    标签: c++ macos-catalina iokit macos-system-extension driverkit


    【解决方案1】:

    尽管关于 DriverKit 的 WWDC 演示试图假装如此,但 DriverKit 的世界观与内核的截然不同,您需要了解一些实现细节,因为抽象非常容易泄漏。

    您可能已经发现,在 DriverKit 驱动程序中看起来像 IOService 对象实际上是内核(和用户空间)的 I/O 注册表视图中的 IOUserService 对象。通过 DriverKit 的 IPC 机制弥补了差距。

    为了创建新的用户客户端,您需要一个(内核)IOUserClient 子类的实例,该实例由您的(dext)IOUserClient 子类支持。这个内核类实际上是IOUserUserClient。 (是的,真的。)正如您所发现的,文档并不清楚您如何处理这个问题。我发现查看源代码方面的可用内容很有帮助 - 调用 NewUserClient is implemented in the IOUserServer::serviceNewUserClient() function here 的内核方面。

    您会立即注意到的一件事是,如果缺少 IOServiceDEXTEntitlements 属性,这不会阻止代码成功:

                prop = userUC->copyProperty(gIOServiceDEXTEntitlementsKey);
                ok = checkEntitlements(entitlements, prop, NULL, NULL);
    

    checkEntitlements:

        if (!prop) {
            return true;
        }
    

    这是一个好消息,因为这意味着我们暂时不需要担心它,可以简单地把它放在一边。

    接下来,propertiesKey 引用了提供者IOUserService 内核对象上的一个属性。您无法从 dext 的代码中设置这些属性,因此提供它们的唯一方法是从 IOKit 匹配个性字典中。

    你可以随意命名这个属性,但是:

    • 它的值必须是字典。
    • 它必须包含 "IOClass" 键值对,指定要实例化为字符串的 kernel 类 - 在您的情况下为 "IOUserUserClient"
    • 它必须包含"IOUserClass" 键值对。这指定要实例化的 dext 类,再次作为字符串。在你的情况下,这看起来像 MyUserClient

    把它放在一起:

      <key>IOKitPersonalities</key>  
      <dict>
        <key>example_device</key>  
        <dict>  
          <key>MyUserClientProperties</key>
          <dict>
            <key>IOUserClass</key>
            <string>MyUserClient</string>
            <key>IOClass</key>
            <string>IOUserUserClient</string>
          </dict>
          <key>CFBundleIdentifier</key>
          <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
          …
        </dict>
      </dict>
      …
    

    然后,从您的 NewUserClient 函数调用:

        IOService* client = nullptr;
        kern_return_t ret = this->Create(this, "MyUserClient", &client);
    

    我认为这里不需要SUPERDISPATCH,因为您可能不会在您的类中覆盖Create 方法,所以无论如何您的超级实现都会被继承。

    然后进行错误检查、任何其他可能需要的初始化、准备等,最后:

        *userClient = client;
        return kIOReturnSuccess;
    

    【讨论】:

    • 太棒了。断言现在消失了,我可以看到对 MyUserClient::init()MyUserClient::Start_Impl(IOService *) 的调用正在记录中。但是我从IOServiceOpen 得到了一个不成功的返回码(-536870174)。此外,当我分离设备时,dext 进程不会终止。如果我不调用IOServiceOpen,则终止 dext 进程。
    • @tuple_cat 错误代码0xE00002E2kIOReturnNotPermitted。您可能需要将 com.apple.developer.driverkit.userclient-access 权利添加到您的用户空间进程(dext 捆绑 ID 字符串数组)。
    • 我正在从应用程序调用IOServiceOpen,该应用程序使用OSSystemExtensionManager 激活dext。我将com.apple.developer.driverkit.userclient-access 权利添加到生成.dext 捆绑包的目标中。还尝试了调用IOServiceOpen 的应用程序的权利,但我仍然得到kIOReturnNotPermitted
    • @tuple_cat 这个后续好像超出了 SO cmets 的范围,能不能开个新问题,最好包括所有应用的权利和相关代码?我一定会看看(我会查看所有标记为iokitdriverkit 的问题)
    • 是的,好主意。在此处创建新问题:stackoverflow.com/questions/61613403/…
    猜你喜欢
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 2021-03-24
    • 2011-11-27
    • 2011-10-29
    • 2020-04-27
    相关资源
    最近更新 更多