gpt4 book ai didi

c++ - 发送具有动态长度的 IOKit 命令

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:50:43 31 4
gpt4 key购买 nike

我正在使用 IOKit 框架通过用户空间客户端的 IOConnectCallMethod 和驱动程序端的 IOExternalMethodDispatch 与我的驱动程序通信。

到目前为止,我能够发送固定长度的命令,现在我希望发送不同大小的字符数组(即完整路径)。

但是,似乎驱动程序和客户端命令长度是耦合的,这意味着驱动程序中 IOExternalMethodDispatchcheckStructureInputSize 必须等于 inputStructCnt 来自IOConnectCallMethod 在客户端。

这是两边的结构内容:

司机:

struct IOExternalMethodDispatch
{
IOExternalMethodAction function;
uint32_t checkScalarInputCount;
uint32_t checkStructureInputSize;
uint32_t checkScalarOutputCount;
uint32_t checkStructureOutputSize;
};

客户:

kern_return_t IOConnectCallMethod(
mach_port_t connection, // In
uint32_t selector, // In
const uint64_t *input, // In
uint32_t inputCnt, // In
const void *inputStruct, // In
size_t inputStructCnt, // In
uint64_t *output, // Out
uint32_t *outputCnt, // In/Out
void *outputStruct, // Out
size_t *outputStructCnt) // In/Out

这是我使用不同大小命令的失败尝试:

std::vector<char> rawData; //vector of chars

// filling the vector with filePath ...

kr = IOConnectCallMethod(_connection, kCommandIndex , 0, 0, rawData.data(), rawData.size(), 0, 0, 0, 0);

从驱动程序命令处理程序端,我正在调用 IOUserClient::ExternalMethodIOExternalMethodArguments *argumentsIOExternalMethodDispatch *dispatch 但是这需要我从客户端传递的动态数据的确切长度。

除非我将调度函数设置为它应该期望的确切数据长度,否则这是行不通的。

知道如何解决这个问题,或者在这种情况下我应该使用不同的 API 吗?

最佳答案

正如您已经发现的那样,接受可变长度“结构”输入和输出的答案是指定特殊的 kIOUCVariableStructureSize IOExternalMethodDispatch 中输入或输出结构大小的值.

这将允许方法分派(dispatch)成功并调出您的方法实现。然而,一个令人讨厌的陷阱是结构输入和输出不一定通过 structureInput 提供。和 structureOutput IOExternalMethodArguments 中的指针字段结构体。在结构定义 (IOKit/IOUserClient.h) 中,注意:

struct IOExternalMethodArguments
{


const void * structureInput;
uint32_t structureInputSize;

IOMemoryDescriptor * structureInputDescriptor;



void * structureOutput;
uint32_t structureOutputSize;

IOMemoryDescriptor * structureOutputDescriptor;


};

根据实际大小,内存区域可能被 structureInput 引用 structureInputDescriptor (和 structureOutput structureOutputDescriptor )——交叉点通常为 8192 字节,或 2 个内存页。任何较小的都会作为指针进入,任何较大的都会被内存描述符引用。不过不要指望特定的交叉点,这是一个实现细节,原则上可能会发生变化。

如何处理这种情况取决于您需要对输入或输出数据做什么。不过,通常情况下,你会想直接在你的 kext 中读取它——所以如果它作为内存描述符出现,你需要先将它映射到内核任务的地址空间。像这样:

static IOReturn my_external_method_impl(OSObject* target, void* reference, IOExternalMethodArguments* arguments)
{
IOMemoryMap* map = nullptr;
const void* input;
size_t input_size;
if (arguments->structureInputDescriptor != nullptr)
{
map = arguments->structureInputDescriptor->createMappingInTask(kernel_task, 0, kIOMapAnywhere | kIOMapReadOnly);
if (map == nullptr)
{
// insert error handling here
return …;
}
input = reinterpret_cast<const void*>(map->getAddress());
input_size = map->getLength();
}
else
{
input = arguments->structureInput;
input_size = arguments->structureInputSize;
}

// …
// do stuff with input here
// …

OSSafeReleaseNULL(map); // make sure we unmap on all function return paths!
return …;
}

输出描述符可以类似地处理,除了没有kIOMapReadOnly当然是选项!

注意:轻微的安全风险:

在内核中解释用户数据通常是一项对安全敏感的任务。直到最近,结构输入机制特别脆弱——因为输入结构是从用户空间到内核空间的内存映射,另一个用户空间线程仍然可以在内核读取内存时修改它。您需要非常小心地编写内核代码,以避免将漏洞引入恶意用户客户端。例如,在映射内存中对用户空间提供的值进行边界检查,然后在假设它仍在有效范围内的情况下重新读取它是错误的。

避免这种情况最直接的方法是复制一次内存,然后只使用复制的数据版本。要采用这种方法,您甚至不需要对描述符进行内存映射:您可以使用 readBytes()成员函数。不过,对于大量数据,您可能不想为了提高效率而这样做。

最近(在 10.12.x 周期中)Apple 更改了 structureInputDescriptor所以它是用 kIOMemoryMapCopyOnWrite 创建的选项。 (据我所知,这是专门为此目的创建的。)这样做的结果是,如果用户空间修改了内存范围,它不会修改内核映射,而是透明地创建它写入的页面的拷贝。依赖于此假设您的用户系统已完全修补。 即使在完全打补丁的系统上,structureOutputDescriptor遇到同样的问题,所以从内核的角度来看,将其视为只写。 永远不要读回你在那里写的任何数据。(写时复制映射对输出结构没有意义。)

关于c++ - 发送具有动态长度的 IOKit 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45432476/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com