如何使用 IOMemoryDe​​scriptor::CreateMapping 在 DriverKit 中映射内存?

Sco*_*aun 2 memory macos iokit macos-system-extension driverkit

我正在尝试了解有关 DriverKit 和内存管理的更多信息,并且我读到了这个问题:

如何在 DriverKit 系统扩展中分配内存并将其映射到另一个进程?

我想了解如何使用IOMemoryDescriptor::CreateMapping.

我写了一个小应用程序来测试我所做的(非常简化的代码):

uint8_t * buffer = new uint8_t[256];
for (int i = 0 ; i < 256 ; i++)
   buffer[i] = 0xC6;

clientData in, out;
in.nbytes = 256;
in.pbuffer = buffer;
size_t sout = sizeof(out);

IOConnectCallStructMethod(connection, selector,&in,sizeof(in),&out,&sout);

// out.pbuffer now has new values in it
Run Code Online (Sandbox Code Playgroud)

在我的 Kext 用户客户端类中,我正在做(我正在简化):

IOReturn UserClient::MyExtFunction(clientData * in, clientData * out, IOByteCount inSize, IOByteCount * outSize)
{
   MyFunction(in->nBytes, in->pbuffer);//this will change the content of pbuffer

   *out = *in; 
}


IOReturn UserClient::MyFunction(SInt32 nBytesToRead,void* pUserBuffer,SInt32* nBytesRead)
{
    PrepareBuffer(nBytesToRead,&pBuffer);
    ...
    (call function that will fill pBuffer)
}


IOReturn UserClient::PrepareBuffer(UInt32 nBytes,void** pBuffer);
{
   IOMemoryDescriptor * desc = IOMemoryDescriptor::withAddressRange((mach_vm_address_t)*pBuffer,nBytes,direction, owner task);

   desc->prepare();
   IOMemoryMap * map = desc->map();
   *pBuffer = (void*)map->getVirtualAddress();

   return kIOReturnSuccess;
}
Run Code Online (Sandbox Code Playgroud)

这是我不知道如何在 DExt 中重现的地方,我认为我真的不理解CreateMapping.

或者我以前做不到的事?

在我的驱动程序中,这是我不知道如何使用的地方CreateMapping,IOMemoryMap因此可以将该缓冲区映射到内存位置并使用不同的值进行更新。

我可以创建一个IOBufferMemoryDescriptor,但如何将它绑定到我的应用程序的缓冲区?我也不明白 的各种选项CreateMapping。

请注意,在另一个测试应用程序中我已成功使用IOConnectMapMemory64()/CopyClientMemoryForType()但我想专门了解 CreateMapping()。

(我希望没关系,我多次编辑了这个问题......对 StackOverflow 来说还是个新手)

pmd*_*mdj 5

\n

或者我以前做不到的事?

\n
\n

简而言之,不。

\n

您正在尝试映射任意用户进程内存,客户端应用程序未使用 IOKit 显式地将其标记为可供驱动程序使用。这不符合苹果关于安全、保密和沙箱的想法,因此这种东西在 DriverKit 中是不可用的。

\n

显然,kext 是万能的,所以这以前是可能的,事实上,我自己在传送驱动程序时使用了该技术,然后在将所述 kext 移植到 DriverKit 时遇到了麻烦。

\n

据我所知,直接访问客户端进程内存的唯一方法是:

\n
    \n
  • 通过将缓冲区 >= 4097 字节作为结构输入或输出参数传递给IOConnectCall\xe2\x80\xa6Method()s,以便它们作为 s 到达IOMemoryDescriptor驱动程序中。请注意,这些可以长期保留在驱动程序中,但至少对于输入结构而言,用户空间端的更新不会反映在驱动程序端,因为使用了写时复制映射。因此它们应该纯粹用于沿预期方向发送数据。
  • \n
  • 用户进程使用/将现有映射IOMemoryDescriptor到其空间。IOConnectMapMemory64()CopyClientMemoryForType()
  • \n
\n

这确实意味着您不能使用像您正在使用的那样的间接数据结构。您必须使用“打包”结构或持久共享缓冲区的索引。

\n

我所说的“打包”结构是指包含标头结构(例如 your )的缓冲区,clientData该标头结构在连续内存中后面跟着进一步的数据(例如 your buffer),通过偏移量引用到该连续内存中。整个连续内存块可以作为输入结构传递。

\n

我已向 Apple 提交反馈,要求提供更强大的机制来在用户客户端和 dext 之间交换数据;我不知道它是否会实施,但如果这样的设施有用,我建议您也这样做。(用例子解释你想用它做什么)我们报告的人越多,它发生的可能性就越大。(IOMemoryDescriptor::CreateSubMemoryDescriptor()是在我提出请求后添加的;我不会声称我是第一个这样做的人,也不会声称 Apple 不打算在我的建议之前添加它,但他们正在积极改进 DriverKit API。 )

\n

问题之前的原始答案被编辑得更加具体:

\n

(保留是因为它一般性地解释了如何处理外部方法的缓冲区参数,这可能对未来的读者有帮助。)

\n

你的问题有点模糊,但让我看看是否可以弄清楚你在 kext 中做了什么,以及你在 dext 中做了什么:

\n
    \n
  • 您正在IOConnectCallStructMethod(connection, selector, buffer, 256, NULL, NULL);您的应用程序中调用。这意味着buffer作为“结构输入”参数传递给外部方法。
  • \n
  • 因为缓冲区的长度为 256 字节,小于或等于sizeof(io_struct_inband_t),所以缓冲区的内容会带内发送到内核- 换句话说,它在调用时被复制IOConnectCallStructMethod()。
  • \n
  • 这意味着在 kext 的外部方法调度函数中,结构输入是通过传入结构中的structureInput/structureInputSize字段传递的IOExternalMethodArguments。structureInput是内核上下文中的指针,可以直接取消引用。该指针仅在方法分派执行期间有效,并且一旦方法同步返回就无法使用。
  • \n
  • 如果需要将缓冲区用于设备 I/O,则可能需要将其包装在IOMemoryDescriptor. 做到这一点的一种方法确实是通过IOMemoryDescriptor::CreateMapping().
  • \n
  • 如果缓冲区为 4097 字节或更大,它将通过 传递structureInputDescriptor IOMemoryDescriptor,它可以直接传递到设备 I/O,也可以进行内存映射以在内核中取消引用。该内存描述符直接引用用户进程的内存。
  • \n
\n

DriverKit 扩展的功能受到相当大的限制,但外部方法参数的到达方式几乎完全相同。

\n
    \n
  • IOUserClientMethodArguments小结构通过\'字段到达structureInput,该字段指向一个OSDataobject。getBytesNoCopy()您可以通过/方法访问内容getLength()。
  • \n
  • 如果您在前向 I/O 中需要此数据IOMemoryDescriptor,我知道的唯一方法是IOBufferMemoryDescriptor使用IOUSBHostDevice::CreateIOBuffer()或创建一个对象IOBufferMemoryDescriptor::Create,然后将数据从OSData对象复制到缓冲区中。
  • \n
  • 大缓冲区再次已通过IOMemoryDescriptor. 您可以将其传递给 I/O 函数,或者使用以下命令将其映射到驱动程序的地址空间CreateMapping()
  • \n
\n