1. 程式人生 > >雙進程守護?內核對象?單實例....?抱歉會進程掛起...抱歉我還有HOOK....

雙進程守護?內核對象?單實例....?抱歉會進程掛起...抱歉我還有HOOK....

oid get 進行 訪問 雙刃劍 con tps 不為 詳細

那時候總會問自己,這次寫的驅動,用Windbg調試過嗎? 寫SsdtHook,手動找過嗎?寫ObjectHook知道對象結構嗎?用FS寄存器獲取信息?為什麽能獲取那麽多的信息?,_kpcr與他有什麽關系?
要從那次學習雙進程守護說起,也算小半年以前的事情了。不懂互斥體、不懂事件、信號量等機制,你就說用互斥體、事件寫雙進程守護?
科普:
當年自學操作系統的時候不懂,慢慢的也就懂了。穿孔器、紙卡帶的年代只有程序,為了解決人與CPU的交互效率低下,單批道處理器當年就出現了。但是仍然滿足不了需求,這時候多批道處理也就成了時間產物(從晶體管到小規模集成電路到3D晶體管技術)。進程(PCB進程控制塊),也是為了解決多批道處理下程序不可控,結果不可復用(資源共享帶來的雙刃劍)問題才真正的被運用起來。然而分時系統、實時系統精準程度要求,高並發的需求等,線程也才真正的立足於系統,成為最小的調度執行單位。
而原子操作、臨界區、互斥體這些東西都要基於多線程來說,線程同步、異步同步。也就有了消費者與生產者,哲學家等同步問題。簡單理解為保證數據不混亂、有層次有邏輯的去相互配合執行任務。有緊急任務(高優先級)可以搶占處理機,而且具有公平性,每個進程都有機會被運行;有較大的吞吐量,所有要有合理的調度算法。

原子操作:
簡單來說,保證利用某一資源時候,當前資源不被其他CPU搶占使用。
缺點:只能解決某一個變量,比如是一個變量數據做簡單運算(有相對的原子操作API)。
臨界區:
基於原子操作的缺點,臨界區概念慢慢形成。臨界區可以保護一段代碼指令。
由InitializeCriticalSection(..)初始化一個臨界區,誰初始化的這個臨界區就屬於誰,有擁有者的概念。擁有者無限調用EnterCriticalSection()則不會被阻塞,其他的則會被阻塞在外,直到DeleteCriricalSection()銷毀 ,臨界區由InitializeCriticalSection ------> LeaveCriticalSection形成了保護。

互斥體:
臨界區啥缺點?他不是內核對象?什麽是內核對象,我們可以把進程、線程、文件IO、互斥體、信號量、事件、線程池、訪問令牌、計時器等都叫做內核對象,可以參考《windows核心編程》一書。
既然是內核對象?當然可以跨進程、臨界區是無法做到這一點,互斥體也有類似於臨界區擁有則的概念,重要的是有兩種狀態:1、激發態 2、非激發態。來判斷當前互斥體是否被使用,而且如果互斥體內部進程或者線程崩潰,那麽互斥體空間將自動釋放且為激發態,但是他只能被擁者去則釋放,不可以被別的線程釋放
創建一個互斥體CreateMutex(),一般互斥體用於寫單實例進程,因為互斥體(參數3)是系統全局唯一,可以判斷當前系統是否已存在該進程,如果存在則不再打開或則創建。
OpenMutexW()打開一個互斥體
ReleaseMutex釋放存在的互斥體。

利用互斥體實現單一進程檢測源碼如下:


#include <stdio.h>
#include <iostream>
#include <Windows.h>

using std::cout; 
using std::endl;

BOOL IsMutex()
{
    HANDLE hMutex = NULL;

    hMutex = CreateMutex(NULL, FALSE, L"TEXT");

    if (hMutex)
    {

        if (ERROR_ALREADY_EXISTS == GetLastError())
        {

            ReleaseMutex(hMutex);

            CloseHandle(hMutex);

            return TRUE;
        }

    }

    return FALSE;
}

int main(void)
{

    if(IsMutex())
        cout << "系統已存在TEXT互斥體" << endl;
    else
        cout << "第一次創建互斥體成功" << endl;

    system("pause");

    return 0;
}

信號量:
信號量當前信號數不為0,則代表為激發態。註意調用WaitForSingleObject()的時候,就會把信號數-1,也就是說如果信號數不為0,那麽使用該函數信號數-1,相當於又上了一把鎖,記得調用函數ReleaseSemaphore()恢復(信號數+1)。任何一個線程都可以進行釋放(互斥體成對出現),意味著多個線程可保護同一段代碼或者指令。
事件:
這是一個相對民主的內核對象,進程同步中用的也比較多。他可以設置等待函數對於此事件對象有沒有後遺癥。而且可以手動設置激發態或者非激發態,自主性非常強,很靈活。
1、CreateEventW()用來創建一個事件對象
2、OPenEventA()打開一個事件對象
3、SetEvent()設置為激發態
4、ReSetEvent()設置為非激發態
5、PulseEvent()手動設置激發態
6、CloseHandle()內核對象當引用計數為0,系統管理銷毀。
這些函數具體參數可以msdn查看或者百度看詳細信息,介紹那麽多下面也要貼上一段雙進程守護代碼。
雙進程守護程序一:

int main(void)
{
    // 創建事件對象
    HANDLE hEvent = CreateEvent(NULL, FALSE, TRUE, L"守護One.exe");

    while (TRUE)
    {
        HANDLE hEventTow = OpenEvent(NULL, FALSE, L"守護Two.exe");

        // 如果不存在則創建
        if (!hEventTow)
        {
            CreateProcess(L"守護Two.exe", NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &s_Si, &s_Pi);

            WaitForSingleObject(s_Pi.hProcess, INFINITE);

            CloseHandle(s_Pi.hThread);

            CloseHandle(s_Pi.hProcess);

        }
        else
            CloseHandle(hEventTow);
    }

    system("pause");

    return 0;
}

雙進程守護程序二:

STARTUPINFO s_Si = {};

PROCESS_INFORMATION s_Pi = {};

// 創建事件對象
HANDLE hEvent = CreateEvent(NULL, FALSE, TRUE, L"守護Two.exe");

while (TRUE)
{
    HANDLE hEventTow = OpenEvent(NULL, FALSE, L"守護One.exe");

    // 如果不存在則創建
    if (!hEventTow)
    {
        CreateProcess(L"守護One.exe", NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &s_Si, &s_Pi);

        WaitForSingleObject(s_Pi.hProcess, INFINITE);

        CloseHandle(s_Pi.hThread);

        CloseHandle(s_Pi.hProcess);

    }
    else
        CloseHandle(hEventTow);
}

system("pause");

return 0;

雙進程守護缺點很多,假如我掛起其中一個進程(不被響應),另一個進程則可關閉。
我們還有HOOK來保護自己,下次用一個簡單的mfc來聊一聊,fs寄存器_kpcr,還有一些Hook.
技術分享圖片

雙進程守護?內核對象?單實例....?抱歉會進程掛起...抱歉我還有HOOK....