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

问题描述

我试图了解有关DriverKit和内存管理的更多信息,我读了以下问题:

How to allocate memory in a DriverKit system extension and map it to another process?

我想了解如何使用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

在我的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;
}

这是我不知道如何在DExt中复制的内容,也是我认为我真的不了解CreateMapping的基础的地方。

还是我以前无法做到的事情?

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

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

请注意,在另一个测试应用中,我已经成功使用了IOConnectMapMemory64() / copyClientMemoryForType(),但是我想专门学习CreateMapping()。

(我希望我对此问题进行了很多编辑……对于StackOverflow来说还很新)

解决方法

还是我以前无法做到的事情?

简而言之,不是。

您正在尝试映射任意用户进程内存,客户端应用程序未使用IOKit将其明确标记为可用于驱动程序。这与Apple关于安全性,安保性和沙箱的想法不符,因此DriverKit中没有这种东西。

显然,kexts功能强大,因此在以前,这是可能的,实际上,我本人在运送驱动程序时就使用了该技术,然后在将所述kexts移植到DriverKit时遇到了麻烦。

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

  • 通过传递> = 4097字节的缓冲区作为IOConnectCall…Method()的结构输入或输出参数,以便它们作为IOMemoryDescriptor到达驱动程序。 请注意,这些可以长期保留在驱动程序中,但是至少对于输入结构,由于使用了写时复制映射,因此用户空间方面的更新不会反映在驱动程序方面。em>因此,应仅将它们用于按预期方向发送数据。
  • 通过用户流程,使用IOMemoryDescriptor / IOConnectMapMemory64()将现有CopyClientMemoryForType()映射到其空间中。

这确实意味着您不能使用所使用的间接数据结构。您将必须使用“打包”结构,或在持久共享缓冲区中建立索引。

“打包”结构是指包含标头结构(例如clientData)的缓冲区,其后在连续存储器中紧跟着其他数据(例如buffer),并通过偏移量引用到此连续存储器中。整个连续的内存块都可以作为输入结构传递。

我已向Apple提出反馈,要求建立一种更强大的机制来在用户客户端和敏捷之间交换数据;我不知道是否会实施,但如果这样的设施有用,我建议您也这样做。 (通过示例说明您想要使用的内容)我们报告的越多,发生的可能性就越大。 ({IOMemoryDescriptor::CreateSubMemoryDescriptor()是在我提出请求后添加的;我不会声称自己是第一个这样做的人,或者苹果并没有计划在我的建议之前添加它,但是它们 积极改进DriverKit API。)

对问题之前的原始答案进行了更具体的编辑:

(保留,因为它大体上解释了如何处理外部方法的缓冲区参数,这可能对将来的读者有所帮助。)

您的问题有点含糊,但是让我看看我是否可以算出您在kext中所做的,与您在dext中所做的:

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

DriverKit扩展在功能上有更多限制,但是外部方法参数的到达方式几乎完全相同。

  • 小结构通过IOUserClientMethodArguments'structureInput字段到达,该字段指向OSData object。您可以通过getBytesNoCopy() / getLength()方法访问内容。
  • 如果您需要IOMemoryDescriptor中的这些数据来进行I / O,我知道的唯一方法是使用IOBufferMemoryDescriptorIOUSBHostDevice::CreateIOBuffer()创建IOBufferMemoryDescriptor::Create并然后将数据从OSData对象复制到缓冲区中。
  • 大型缓冲区已经通过IOMemoryDescriptor再次引用。您可以将此传递给I / O函数,或使用CreateMapping()
  • 映射到驱动程序的地址空间。
,
namespace
{
    /*
    **********************************************************************************
    **          create a memory descriptor and map its address
    **********************************************************************************
    */
    IOReturn arcmsr_userclient_create_memory_descriptor_and_map_address(const void* address,size_t length,IOMemoryDescriptor** memory_descriptor) 
    {
        IOBufferMemoryDescriptor *buffer_memory_descriptor = nullptr;
        uint64_t buffer_address;
        uint64_t len;

    #if ARCMSR_DEBUG_IO_USER_CLIENT
        arcmsr_debug_print("ArcMSRUserClient: *******************************************************\n");
        arcmsr_debug_print("ArcMSRUserClient: ** IOUserClient IOMemoryDescriptor create_with_bytes   \n");
        arcmsr_debug_print("ArcMSRUserClient: *******************************************************\n");
    #endif
        if (!address || !memory_descriptor) 
        {
            return kIOReturnBadArgument;
        }
        if (IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut,length,&buffer_memory_descriptor) != kIOReturnSuccess) 
        {
            if (buffer_memory_descriptor) 
            {
                OSSafeReleaseNULL(buffer_memory_descriptor);
            }
            return kIOReturnError;
        }
        if (buffer_memory_descriptor->Map(0,&buffer_address,&len) != kIOReturnSuccess) 
        {
            if (buffer_memory_descriptor) 
            {
                OSSafeReleaseNULL(buffer_memory_descriptor);
            }
            return kIOReturnError;
        }
        if (length != len) 
        {
            if (buffer_memory_descriptor) 
            {
                OSSafeReleaseNULL(buffer_memory_descriptor);
            }
            return kIOReturnNoMemory;
        }
        memcpy(reinterpret_cast<void*>(buffer_address),address,length);
        *memory_descriptor = buffer_memory_descriptor;
        return kIOReturnSuccess;
    }
}  /* namespace */

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...