macOS內(nèi)核拓展與用戶態(tài)進(jìn)程的通信實現(xiàn)(二)

前面已經(jīng)講解了基于KernControl API的通信實現(xiàn)了嚎,該實現(xiàn)相對簡單舌镶,但有一些缺點。內(nèi)核向應(yīng)用層傳輸消息需調(diào)用ctl_enqueuedata接口,該接口實際將數(shù)據(jù)緩存至緩沖區(qū)期犬,當(dāng)需要瞬時大量傳輸消息時,緩沖區(qū)容量有限,將丟棄后來的數(shù)據(jù)丹墨。如果傳輸?shù)臄?shù)據(jù)有優(yōu)先級,則需對ctl_enqueuedata接口進(jìn)行二次封裝嬉愧,避免高優(yōu)先級數(shù)據(jù)丟失贩挣。

IOKit Fundamentals 框架提供更加全面且方便的內(nèi)核驅(qū)動API。IOService是大多數(shù)內(nèi)核驅(qū)動的基類没酣,提供驅(qū)動實例化相關(guān)的各項服務(wù)王财。IOUserClient是與用戶態(tài)應(yīng)用程序間通信的基類,通過繼承并實現(xiàn)該類裕便,可與客戶端應(yīng)用程序建立通信機(jī)制绒净。IOSharedDataQueue是非常便于使用的內(nèi)核與用戶態(tài)進(jìn)程進(jìn)行數(shù)據(jù)交換的通用隊列,用戶可自行設(shè)置隊列大小偿衰」医基于IOKit中的IOService、IOUserClient下翎、IOSharedDataQueue可方便的實現(xiàn)內(nèi)核與用戶進(jìn)程的通信和數(shù)據(jù)傳輸缤言。下面結(jié)合代碼進(jìn)行簡單實現(xiàn),更多代碼細(xì)節(jié)和工程配置可參考NuwaStone項目漏设。

IOService內(nèi)核編程

由于本次編寫的為內(nèi)核拓展墨闲,僅需要IOService進(jìn)行驅(qū)動的加載與卸載管理,這里的代碼實現(xiàn)很簡單郑口,僅需重寫start鸳碧、stop方法盾鳞。如有加載卸載時的自定義操作可在函數(shù)中實現(xiàn)。

DriverService.hpp

class DriverService : public IOService {
    OSDeclareDefaultStructors(DriverService);

public:
    // Called by the kernel when the kext is loaded
    bool start(IOService *provider) override;

    // Called by the kernel when the kext is unloaded
    void stop(IOService *provider) override;
    
private:
    void clearInstances();
};

IOUserClient內(nèi)核編程

編寫繼承于IOUserClient的類后需重寫如下方法瞻离,相關(guān)源文件實現(xiàn)請參考開源項目腾仅。registerNotificationPort和clientMemoryForType用于數(shù)據(jù)交換隊列的配置,externalMethod配置對外函數(shù)調(diào)用接口套利,對外接口的函數(shù)原型如callYourMethod定義推励。

DriverClient.hpp

class DriverClient : public IOUserClient {
    OSDeclareDefaultStructors(DriverClient);
    
public:
    // Called as part of IOServiceOpen in clients.
    bool initWithTask(task_t owningTask, void *securityID, UInt32 type) override;

    // Called after initWithTask as part of IOServiceOpen.
    bool start(IOService *provider) override;

    // Called when this class is stopping.
    void stop(IOService *provider) override;

    // Called when a client manually disconnects (via IOServiceClose).
    IOReturn clientClose(void) override;

    // Called when a client dies.
    IOReturn clientDied(void) override;

    // Called during termination.
    bool didTerminate(IOService *provider, IOOptionBits options, bool *defer) override;

    // Called in clients with IOConnectSetNotificationPort. 用于數(shù)據(jù)傳輸
    IOReturn registerNotificationPort(mach_port_t port, UInt32 type, UInt32 refCon) override;

    // Called in clients with IOConnectMapMemory. 用于數(shù)據(jù)傳輸
    IOReturn clientMemoryForType(UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory) override;

    // Called in clients with IOConnectCallScalarMethod. 設(shè)置對外通信調(diào)用接口
    IOReturn externalMethod(UInt32 selector, IOExternalMethodArguments *arguments, IOExternalMethodDispatch *dispatch, OSObject *target, void *reference) override;

    // 自定義對外調(diào)用方法
    static IOReturn callYourMethod(OSObject *target, void *reference, IOExternalMethodArguments *arguments);
};

IOKit客戶端編程

連接內(nèi)核拓展前需先進(jìn)行加載,加載調(diào)用KextManagerLoadKextWithIdentifier或KextManagerLoadKextWithURL即可肉迫。內(nèi)核拓展需在plist中配置IOService及IOUserClient類名验辞,在拓展啟動后可通過類名進(jìn)行查找匹配。首先需查找注冊了指定類名的內(nèi)核驅(qū)動喊衫,代碼如下:

func startProvider() -> Bool {
        guard let service = IOServiceMatching("your service name") else {
            return false
        }
        
        Logger(.Info, "Wait for kext to be connected.")
        waitForDriver(matchingDict: service)
        return true
    }

service存放匹配成功的驅(qū)動字典跌造,然后需要創(chuàng)建通信端口和處理隊列進(jìn)行處理連接請求。處理連接請求時所持有的IOService對象需注意釋放族购。調(diào)用IOServiceOpen接口即可建立連接壳贪,后面的IOConnectCallScalarMethod表示調(diào)用驅(qū)動對外接口進(jìn)行連接測試。函數(shù)返回前需將用于連接請求處理的端口釋放寝杖。通過IOConnectCallScalarMethod或IOConnectCallStructMethod可調(diào)用驅(qū)動對外接口违施,其中ScalarMethod僅可傳輸有限數(shù)量的常量,StructMethod則可傳輸自定義結(jié)構(gòu)體類型瑟幕,相關(guān)驅(qū)動配置可參照NuwaStone項目磕蒲。

func processConnectionRequest(iterator: io_iterator_t) {
        repeat {
            // 持有的對象需進(jìn)行釋放
            let nextService = IOIteratorNext(iterator)
            guard nextService != 0 else {
                break
            }
            
            // 建立與內(nèi)核驅(qū)動的連接
            var result = IOServiceOpen(nextService, mach_task_self_, 0, &connection)
            if result != kIOReturnSuccess {
                Logger(.Error, "Failed to open kext service [\(String.init(format: "0x%x", result))].")
                IOObjectRelease(nextService)
                break
            }
            
            // 調(diào)用驅(qū)動方法測試連接
            result = IOConnectCallScalarMethod(connection, kNuwaUserClientOpen.rawValue, nil, 0, nil, nil)
            if result != kIOReturnSuccess {
                Logger(.Error, "An error occurred while opening the connection [\(result)].")
                IOObjectRelease(nextService)
                break
            }
            
            IOObjectRelease(nextService)
            IONotificationPortDestroy(notificationPort)
            isConnected = true
            Logger(.Info, "Connected with kext successfully.")
        } while true
    }
    
func waitForDriver(matchingDict: CFDictionary) {
        var iterator: io_iterator_t = 0
        let selfPointer = Unmanaged.passUnretained(self).toOpaque()
        let notificationQueue = DispatchQueue(label: "your queue name")
        
        let appearedCallback: IOServiceMatchingCallback = { refcon, iterator in
            let selfPtr = Unmanaged<YourClassName>.fromOpaque(refcon!).takeUnretainedValue()
            selfPtr.processConnectionRequest(iterator: iterator)
        }
        
        notificationPort = IONotificationPortCreate(kIOMasterPortDefault)
        IONotificationPortSetDispatchQueue(notificationPort, notificationQueue)
        IOServiceAddMatchingNotification(notificationPort, kIOMatchedNotification, matchingDict, appearedCallback, selfPointer, &iterator)
        processConnectionRequest(iterator: iterator)
    }

我們通過調(diào)用IOServiceOpen存放io_connect_t類型的對象建立了與內(nèi)核驅(qū)動的連接颤殴,相應(yīng)的笑跛,斷開連接時需調(diào)用IOServiceClose接口。

func stopProvider() -> Bool {
        let result = IOServiceClose(connection)
        if result != KERN_SUCCESS {
            Logger(.Error, "Failed to close IOService [\(String.init(format: "0x%x", result))].")
            return false
        }
        
        connection = IO_OBJECT_NULL
        isConnected = false
        return true
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鹿霸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秆乳,老刑警劉巖懦鼠,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屹堰,居然都是意外死亡肛冶,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進(jìn)店門扯键,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睦袖,“玉大人,你說我怎么就攤上這事荣刑∠隗希” “怎么了伦乔?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長董习。 經(jīng)常有香客問我烈和,道長,這世上最難降的妖魔是什么皿淋? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任招刹,我火速辦了婚禮,結(jié)果婚禮上窝趣,老公的妹妹穿的比我還像新娘疯暑。我一直安慰自己,他們只是感情好哑舒,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布缰儿。 她就那樣靜靜地躺著,像睡著了一般散址。 火紅的嫁衣襯著肌膚如雪乖阵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天预麸,我揣著相機(jī)與錄音瞪浸,去河邊找鬼。 笑死吏祸,一個胖子當(dāng)著我的面吹牛对蒲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贡翘,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼蹈矮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鸣驱?” 一聲冷哼從身側(cè)響起泛鸟,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎踊东,沒想到半個月后北滥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡闸翅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年再芋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坚冀。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡济赎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情司训,我是刑警寧澤华蜒,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站豁遭,受9級特大地震影響叭喜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蓖谢,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一捂蕴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闪幽,春花似錦啥辨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至腕够,卻和暖如春级乍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帚湘。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工玫荣, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人大诸。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓捅厂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親资柔。 傳聞我的和親對象是個殘疾皇子焙贷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內(nèi)容