操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    在一次排查系統的驅動的目錄system32/driver目錄文件時,看到一個名字為ProcLaunchMon.sys的驅動

    數字簽名微軟官方的

    該驅動帶入的時間是2019?年?12?月?7?日,??17:08:33,文件描述是Time Travel Debugging Process Launch Monitor大致明白了就是一個調試工具實事記錄進程活動的驅動。微軟的這個官方頁面有講述了什么是Time Travel Debugging,
    https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview

    本篇的文章的目的是主要研究這個驅動到底做了什么?
    接下來就祭出逆向工具ida
    微軟對這個模塊是有部分符號文件的,左側的Function windows可以看到他是用c++寫的驅動有一些類名顯示

    主要的類名就是ProcessLaunchMonitorClient 、ProcessLaunchMonitorDevice、LegacyDevice
    然后再看看導入表有哪些函數,函數并不多主要就兩類: 進程函數和事件函數
    PsSetCreateProcessNotifyRoutine
    ZwCreateEvent
    PsSuspendProcess
    PsResumeProcess
    ZwDuplicateObject
    等等

    分析完表面這些明顯的地方,下面就從驅動的入口點分析
    最開始的時候會構建一個device 類
    LegacyDevice::LegacyDevice

    LegacyDevice::LegacyDevice類的大小是0x38,因為驅動,所以一般用c++寫驅動的時候都會用動態內存分配全局結構,ProcessLaunchMonitorDevice::`vftable 是該類的基類,在驅動的虛表rdata節里可以看到該基類的定義

    前面第一個是構造與析構函數,后面四個是IO 例程函數DispatchRoutine、DispatchCreate、DispatchClose、DispatchBufferedIoctl

    內存構造結束了,就直接調用LegacyDevice::LegacyDevice的構造函數

    首先會構造設備名字的字符串

    然后創建設備和設備連接

    注意該驅動使用的是
    WdmlibIoCreateDeviceSecure來創建設備,該函數創建的設備是管理權限才能打開設備。

    最后構造函數里會填充MajorFunction結構體,用StaticDispatchRoutine函數覆蓋。

    memset64(a2->MajorFunction, (unsigned __int64)LegacyDevice::StaticDispatchRoutine, 0x1Cui64);

    自此這個構造類結束。

    構造了這個全局類LegacyDevice::LegacyDevice,后接下來就是調用注冊進程回調通知

    回調函數是ProcessCreationNotifyRoutine

    到這里入口函數就結束,其實過程很簡單,但是精華卻在例程函數里。

    要控制利用驅動,首先我們必須CreateFile一個驅動,這是會進入驅動的IRP_MJ_CREATE例程,接下來看上面的提到的StaticDispatchRoutine函數

    這個函數很簡單,可能很多人一下子看不懂,從設備信息中獲得DeviceExtension結構然后call v4 + 8的函數,這到底是什么呢,
    秘密在剛才那個構造函數里。
    在之前的LegacyDevice::LegacyDevice的構造函數里有這么句代碼

    (_QWORD )(*DeviceObject)->DeviceExtension=this;

    就是把LegacyDevice::LegacyDevice這個全局類的this指針賦值給DeviceObject)->DeviceExtension的結構中,現在明白了這個DeviceExtension里的值是什么了把,對他就是LegacyDevice::LegacyDevice的地址值,那么

    ((void (fastcall **)(int64, _QWORD ))((_QWORD )v4 + 8i64))(v4, v7);

    的代碼的意思就是調用LegacyDevice::LegacyDevice的基類的第二個函數DispatchRoutine

    繼續分析進入DispatchRoutine 函數

    IRP_MJ_CREATE 會進入第一個函數

    ProcessLaunchMonitorDevice::DispatchCreate(this, ((struct _FILE_OBJECT *)iocode + 6));

    ProcessLaunchMonitorDevice::DispatchCreate函數里會為每一個Client創建該設備的對象生成一個ProcessLaunchMonitorClient::ProcessLaunchMonitorClient(v6, v7, &v15),該類的內存大小為0x70.

    這個類的構造函數一個最主要的功能就是生成一個進程間通訊的Event,賦值給了
    v3=(PVOID
    )((char )this + 56);的便宜的位置。
    最后會把這個Process
    賦值給了

    ProcessLaunchMonitorDevice這個全局類的v10=((_QWORD )this + 6);的位置的LIST_ENTRY的鏈表里。以及當前設備的文件對象的FsContext的上下文里。

    打開了設備之后,我們就要通過IRP_MJ_DEVICE_CONTROL IO控制碼是14的之類的去給驅動發IO命令,那么就會經過驅動的

    然后再進入ProcessLaunchMonitorDevice::DispatchBufferedIoctl函數

    首先該函數會先從之前的我們講的文件對象的FsContext結構中取出之前創建的ProcessLaunchMonitorClient的指針。

    接下來會看到里面有一個IO code

    Case : 0x224040 恢復進程
    Case 0x224044 關閉 ProcessLaunchMonitorClient 并且清楚恢復所有被懸掛的進程
    Case 0x2240048 獲取之前ProcessLaunchMonitorClient里創建的進程間通訊的事件。
    等等

    這些主要的IRP例程的函數大致的邏輯就分析完畢,那該驅動的進程回調函數有什么用呢?這個問題很好,下面就來分析回調通知里的邏輯

    當一個進程啟動后就會進入該驅動回調通知

    然后就會進入

    ProcessLaunchMonitorDevice::HandleProcessCreatedOrDestroyed(gLegacyDevice::gLegacyDevice, Create !=0, v4, v6);

    這個函數是主要的處理邏輯
    然后就會進入

    進入這個函數SuspendResumeProcessById(a4, v13)后就會把進程懸掛起來

    可以看到

    If(a2)
    {
    v5=PsSuspendProcess(Object);
    }

    如果a2這個參數是1的話,直接就懸掛了,然后外面給的參數就是1,那就是只要驅動功能起來了,就直接懸掛了新起來的進程(這個驅動太霸道了), 如果你不發之前我們看到那個IO: 0x224040的控制碼的話,它就一直被懸掛,起不來了,或者這個驅動被關閉也能自動恢復所有被懸掛的進程。
    其他一些小的附加的結構體的處理不在具體分析,以上就是該驅動的最主要的功能,當然分析完畢就是驗證我們的分析結果。

    主要代碼如下:(具體的iocode 我模糊處理了,避免被惡意亂用)

    int main()
    {
    
    TCHAR szDriverKey[MAX_PATH]={ 0 };
    GetSystemDirectory(szDriverKey,MAX_PATH);
    StringCchCat(szDriverKey, MAX_PATH, _T("\\dirver\\ProcLaunchMon.sys"));
    
    if (TRUE)
    {
        //install driver
        LoadDriver(szDriverKey);
    }
    BOOL CheckServiceOk=FALSE;
    
    if (SUCCEEDED(StringCchPrintf(
        szDriverKey,
        MAX_PATH,
        _T("\\\\.\\%s"),
        _T("com_microsoft_xxxxx_ProcLaunchMon")))) //
    
    
    {
        HANDLE hObjectDrv=CreateFile(
            szDriverKey,
            GENERIC_READ |
            GENERIC_WRITE,
            0,
            0,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            0);
    
        if (hObjectDrv !=INVALID_HANDLE_VALUE)
        {
    
            DWORD dwProcessId=-1;
            BYTE OutBuffer[4002]={ 0 };
            ULONG BytesReturned=0;
    
            DWORD sendrequest=0xxabsdd;
            if (DeviceIoControl(
                hObjectDrv,
                sendrequest,//,
                &dwProcessId,
                4,
                &dwProcessId,
                4,
                &BytesReturned, 0))
            {
                ULONG64 Request=0;
                sendrequest=0xa2XXXX;
                ULONG64 KeyHandle=0;
                if (DeviceIoControl(
                    hObjectDrv,
                    sendrequest,//
                    &Request,
                    8,
                    &KeyHandle,
                    8,
                    &BytesReturned, 0))
                {
                    //Get the kernel process event and wait for the kernel setting event
    
                    if (WaitForSingleObject((HANDLE)KeyHandle, INFINITE)==WAIT_OBJECT_0)
                    {
                        //                         int nI=0;
                        //                         nI++;
                    }
    
                    while (TRUE)
                    {
                        sendrequest=0x125a3X;
    
                        typedef struct _GetPidBuffer
                        {
                            LARGE_INTEGER Pid;
                            ULONG32 Other;
                        }GetPidBuffer;
                        GetPidBuffer ProcessPid={0};
                        if (DeviceIoControl(
                            hObjectDrv,
                            sendrequest,
                            &Request,
                            8,
                            &ProcessPid,
                            12,
                            &BytesReturned,
                            0))
                        {
                            if (BytesReturned && ProcessPid.Pid.LowPart > 0)
                            {
    
                                printf("curent run pid:%d", ProcessPid.Pid.HighPart);
                                //If the process is not recovered, it will cause denial of service attack. For example,
                                //if it is security software, it will cause security software failure
                                sendrequest=0x224040;
    
                                if (DeviceIoControl(
                                    hObjectDrv,
                                    sendrequest,
                                    &ProcessPid.Pid.HighPart,
                                    8,
                                    &ProcessPid.Pid.HighPart,
                                    8,
                                    &BytesReturned, 0))
                                {
                                }
                                Sleep(100);
                                BytesReturned=0;
                            }
                            else
                            {
                                if (WaitForSingleObject((HANDLE)KeyHandle, INFINITE)==WAIT_OBJECT_0)
                                {
    
                                }
                            }
                        }        
                    }
    
                }
            }
    
            CloseHandle(hObjectDrv);
            hObjectDrv=NULL;
        }
    }
    return 0;
    }

    1. 加載驅動成功

    2. 打開設備成功

    3. 開啟功能

    后記: 這個曾經上報給微軟的msrc,對方承認是個有待改進的問題的驅動,但是并非是個漏洞,至今已經過去了四個月了,可以到了公布的時間了,如果一個惡意進程已經攻擊進入一個系統后,,并且已經有了管理員權限,他就可以利用這個驅動去控制安全軟件的啟動,甚至失效,這也是很危險的驅動。

    IO

    IO (Input/Output,輸入/輸出)即數據的讀取(接收)或寫入(發送)操作,通常用戶進程中的一個完整IO分為兩階段:用戶進程空間<-->內核空間、內核空間<-->設備空間(磁盤、網絡等)。IO有內存IO、網絡IO和磁盤IO三種,通常我們說的IO指的是后兩者。

    LINUX中進程無法直接操作I/O設備,其必須通過系統調用請求kernel來協助完成I/O動作;內核會為每個I/O設備維護一個緩沖區。

    對于一個輸入操作來說,進程IO系統調用后,內核會先看緩沖區中有沒有相應的緩存數據,沒有的話再到設備中讀取,因為設備IO一般速度較慢,需要等待;內核緩沖區有數據則直接復制到進程空間。

    所以,對于一個網絡輸入操作通常包括兩個不同階段:

    (1)等待網絡數據到達網卡→讀取到內核緩沖區,數據準備好;

    (2)從內核緩沖區復制數據到進程空間。


    關鍵概念理解

    • 同步:發起一個調用,得到結果才返回。
    • 異步:調用發起后,調用直接返回;調用方主動詢問被調用方獲取結果,或被調用方通過回調函數。
    • 阻塞:調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之后才會返回。
    • 非阻塞:調用指在不能立刻得到結果之前,該調用不會阻塞當前線程。

    同步才有阻塞和非阻塞之分;
    阻塞與非阻塞關乎如何對待事情產生的結果(阻塞:不等到想要的結果我就不走了)

    明確進程狀態

    理解進程的狀態轉換

    • 就緒狀態 -> 運行狀態:處于就緒狀態的進程被調度后,獲得CPU資源(分派CPU時間片),于是進程由就緒狀態轉換為運行狀態。
    • 運行狀態 -> 就緒狀態:處于運行狀態的進程在時間片用完后,不得不讓出CPU,從而進程由運行狀態轉換為就緒狀態。此外,在可剝奪的操作系統中,當有更高優先級的進程就 、 緒時,調度程度將正執行的進程轉換為就緒狀態,讓更高優先級的進程執行。
    • 運行狀態 -> 阻塞狀態:當進程請求某一資源(如外設)的使用和分配或等待某一事件的發生(如I/O操作的完成)時,它就從運行狀態轉換為阻塞狀態。進程以系統調用的形式請求操作系統提供服務,這是一種特殊的、由運行用戶態程序調用操作系統內核過程的形式。
    • 阻塞狀態 -> 就緒狀態:當進程等待的事件到來時,如I/O操作結束或中斷結束時,中斷處理程序必須把相應進程的狀態由阻塞狀態轉換為就緒狀態。

    從操作系統層面執行應用程序理解 IO 模型

    阻塞IO模型:

    • 簡介:進程會一直阻塞,直到數據拷貝完成應用程序調用一個IO函數,導致應用程序阻塞,等待數據準備好。 如果數據沒有準備好,一直等待….數據準備好了,從內核拷貝到用戶空間,IO函數返回成功指示。 我們 第一次接觸到的網絡編程都是從 listen()、send()、recv()等接口開始的。使用這些接口可以很方便的構建服務器 /客戶機的模型。
    • 阻塞I/O模型圖:在調用recv()/recvfrom()函數時,發生在內核中等待數據和復制數據的過程。

    當調用recv()函數時,系統首先查是否有準備好的數據。如果數據沒有準備好,那么系統就處于等待狀態。當數據準備好后,將數據從系統緩沖區復制到用戶空間,然后該函數返回。在套接應用程序中,當調用recv()函數時,未必用戶空間就已經存在數據,那么此時recv()函數就會處于等待狀態。
    阻塞模式給網絡編程帶來了一個很大的問題,如在調用 send()的同時,線程將被阻塞,在此期間,線程將無法執行任何運算或響應任何的網絡請求。這給多客戶機、多業務邏輯的網絡編程帶來了挑戰。這時,我們可能會選擇多線程的方式來解決這個問題。
    應對多客戶機的網絡應用,最簡單的解決方式是在服務器端使用多線程(或多進程)。多線程(或多進程)的目的是讓每個連接都擁有獨立的線程(或進程),這樣任何一個連接的阻塞都不會影響其他的連接。
    具體使用多進程還是多線程,并沒有一個特定的模式。傳統意義上,進程的開銷要遠遠大于線程,所以,如果需要同時為較多的客戶機提供服務,則不推薦使用多進程;如果單個服務執行體需要消耗較多的 CPU 資源,譬如需要進行大規模或長時間的數據運算或文件訪問,則進程較為安全。

    非阻塞IO模型

    • 簡介:非阻塞IO通過進程反復調用IO函數(多次系統調用,并馬上返回);在數據拷貝的過程中,進程是阻塞的; 我們把一個SOCKET接口設置為非阻塞就是告訴內核,當所請求的I/O操作無法完成時,不要將進程睡眠,而是返回一個錯誤。這樣我們的I/O操作函數將不斷的測試數據是否已經準備好,如果沒有準備好,繼續測試,直到數據準備好為止。在這個不斷測試的過程中,會大量的占用CPU的時間。

    IO復用模型:

    • 簡介:IO multiplexing就是我們說的select,poll,epoll,有些地方也稱這種IO方式為event driven IO。select/epoll的好處就在于單個process就可以同時處理多個網絡連接的 IO。它的基本原理就是select,poll,epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有數據到達了,就通知用戶進程。

    當用戶進程調用了select,那么整個進程會被block,而同時,kernel會“監視”所有select負責的socket,當任何一個socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操作,將數據從kernel拷貝到用戶進程。
    所以,I/O 多路復用的特點是通過一種機制一個進程能同時等待多個文件描述符,而這些文件描述符(套接字描述符)其中的任意一個進入讀就緒狀態,select()函數就可以返回。

    異步IO模型

    • 簡介:用戶進程發起read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到一個asynchronous read之后,首先它會立刻返回,所以不會對用戶進程產生任何block。然后,kernel會等待數據準備完成,然后將數據拷貝到用戶內存,當這一切都完成之后,kernel會給用戶進程發送一個signal,告訴它read操作完成了。


    區別IO多路復用中的select poll epoll

    select

    int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); select 函數監視的文件描述符分3類,分別是writefds、readfds、和exceptfds。調用后select函數會阻塞,直到有描述符就緒(有數據 可讀、可寫、或者有except),或者超時(timeout指定等待時間,如果立即返回設為null即可),函數返回。當select函數返回后,可以 通過遍歷fdset,來找到就緒的描述符

    poll

    int poll (struct pollfd *fds, unsigned int nfds, int timeout); 不同與select使用三個位圖來表示三個fdset的方式,poll使用一個 pollfd的指針實現。pollfd并沒有最大數量限制(但是數量過大后性能也是會下降)。 和select函數一樣,poll返回后,需要輪詢pollfd來獲取就緒的描述符。

    epoll

    epoll是通過事件的就緒通知方式,調用epoll_create創建實例,調用epoll_ctl添加或刪除監控的文件描述符,調用epoll_wait阻塞住,直到有就緒的文件描述符,通過epoll_event參數返回就緒狀態的文件描述符和事件。

    epoll操作過程需要三個接口,分別如下: int epoll_create(int size);//創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大 生成一個 epoll 專用的文件描述符,其實是申請一個內核空間,用來存放想關注的 socket fd 上是否發生以及發生了什么事件。

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    控制某個 epoll 文件描述符上的事件:注冊、修改、刪除。其中參數 epfd 是 epoll_create() 創建 epoll 專用的文件描述符。

    int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    等待 I/O 事件的發生;返回發生事件數。參數說明:
    epfd: 由 epoll_create() 生成的 Epoll 專用的文件描述符;
    epoll_event: 用于回傳代處理事件的數組;
    maxevents: 每次能處理的事件數;
    timeout: 等待 I/O 事件發生的超時值;

    區別總結

    (1)select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。而epoll其實也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。
    (2)select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,epoll 通過 mmap 把內核空間和用戶空間映射到同一塊內存,省去了拷貝的操作。

    應用舉例

    • Tornado:
      • 使用單線程的方式,避免線程切換的性能開銷,同時避免在使用一些函數接口時出現線程不安全的情況
      • 支持異步非阻塞網絡IO模型,避免主進程阻塞等待。

    tornado 的 IOLoop 模塊 是異步機制的核心,它包含了一系列已經打開的文件描述符和每個描述符的處理器 (handlers)。這些 handlers 就是對 select, poll , epoll等的封裝。(所以本質上說是 IO 復用)

    • Django
      沒有用異步,通過使用多進程的WSGI server(比如uWSGI)來實現并發,這也是WSGI普遍的做法。

    Linux后端服務器開發要學關于IO的哪些知識點

    網絡IO是網絡通信的血管,數據是血液。血液的流動是不能離開血管的。

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有