[英]sending IOKit command with dynamic length
我使用的是由於IOKit框架用我的司機溝通IOConnectCallMethod
從用戶空間客戶端和IOExternalMethodDispatch
駕駛員側。
到目前為止,我能夠發送固定長度的命令,現在我希望發送一個不同大小的字符數組(即完整路徑)。
但是,似乎驅動程序和客戶端命令長度是耦合的,這意味着驅動程序中checkStructureInputSize
的IOExternalMethodDispatch
必須等於客戶端IOConnectCallMethod
中的inputStructCnt
。
以下是雙方的結構內容:
驅動程序:
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);
從驅動程序命令處理程序端,我使用IOExternalMethodArguments *arguments
和IOExternalMethodDispatch *dispatch
調用IOUserClient::ExternalMethod
,但這需要我從客戶端傳遞的動態數據的確切長度。
除非我使用它應該期望的確切數據長度設置調度函數,否則這不起作用。
知道如何解決這個問題,或者在這種情況下我應該使用不同的API嗎?
正如我們已經發現,用於接受可變長度“結構”輸入和輸出的答案是指定特殊kIOUCVariableStructureSize
在輸入或輸出結構尺寸值IOExternalMethodDispatch
。
這將允許方法調度成功並調用您的方法實現。 然而,一個令人討厭的缺陷是結構輸入和輸出不一定通過IOExternalMethodArguments
結構中的structureInput
和structureOutput
指針字段提供。 在結構定義(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
也會遇到同樣的問題,因此從內核的角度來看它只能寫入。 永遠不要回讀你在那里寫的任何數據。 (寫時復制映射對輸出結構沒有意義。)
在再次閱讀相關手冊后,我找到了相關段落:
checkScalarInputCount,checkStructureInputSize,checkScalarOutputCount和checkStructureOutputSize字段允許在將參數列表傳遞給目標對象之前對其進行完整性檢查。 標量計數應設置為目標方法期望讀取或寫入的標量(64位)值的數量。 結構大小應設置為目標方法期望讀取或寫入的任何結構的大小。 對於任一結構大小字段,如果在編譯時無法確定結構的大小,請指定kIOUCVariableStructureSize而不是實際大小。
所以,我不得不以避免大小驗證做的,就是設置現場checkStructureInputSize
重視kIOUCVariableStructureSize
在IoExternalMethodDispatch
並傳遞到驅動程序正確的命令。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.