簡體   English   中英

發送動態長度的IOKit命令

[英]sending IOKit command with dynamic length

我使用的是由於IOKit框架用我的司機溝通IOConnectCallMethod從用戶空間客戶端和IOExternalMethodDispatch駕駛員側。

到目前為止,我能夠發送固定長度的命令,現在我希望發送一個不同大小的字符數組(即完整路徑)。

但是,似乎驅動程序和客戶端命令長度是耦合的,這意味着驅動程序中checkStructureInputSizeIOExternalMethodDispatch必須等於客戶端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 *argumentsIOExternalMethodDispatch *dispatch調用IOUserClient::ExternalMethod ,但這需要我從客戶端傳遞的動態數據的確切長度。

除非我使用它應該期望的確切數據長度設置調度函數,否則這不起作用。

知道如何解決這個問題,或者在這種情況下我應該使用不同的API嗎?

正如我們已經發現,用於接受可變長度“結構”輸入和輸出的答案是指定特殊kIOUCVariableStructureSize在輸入或輸出結構尺寸值IOExternalMethodDispatch

這將允許方法調度成功並調用您的方法實現。 然而,一個令人討厭的缺陷是結構輸入和輸出不一定通過IOExternalMethodArguments結構中的structureInputstructureOutput指針字段提供。 在結構定義(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重視kIOUCVariableStructureSizeIoExternalMethodDispatch並傳遞到驅動程序正確的命令。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM