日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

r0下的進程保護

 

前言

進程保護是眾多AV或者病毒都要所具備的基礎功能,本文就0環下通過SSDT來對進程進行保護進行探究,SSDT也不是什么新技術,但作為學習,老的技術我們同樣需要掌握。

什么是SSDT

SSDT 的全稱是System Services Descriptor Table,系統服務描述符表。

首先要明確的是他是一張表,通過windbg查看這張表。

dd  KeServiceDescriptorTable
r0下的進程保護

 

這個表就是一個把 Ring3 的 Win32 API 和 Ring0 的內核 API 聯系起來。

當我們在r 3調用一個API時,實際上調用的是一個接口,這里拿ReadProcessMemory舉例。

ReadProcessMemory函數在kernel32.dll中導出,通過斷點可以找到對應的反匯編代碼。在匯編代碼中,可以看到ReadProcessMemory調用了ntdll.dll中的ZwReadVirtualMemory函數。

r0下的進程保護

 

在ZwReadVirtualMemory函數開始的地方下斷點。

bp ZwReadVirtualMemory

實際上功能代碼也沒有在ZwReadVirtualMemory函數中實現,只是拿著一個索引號并跳轉到一個地址。

r0下的進程保護

 

這個索引號實際上就是SSDT表中的索引號,回到windbg,我們現在拿到索引號0xBA去SSDT表中找。

kd> dd  KeServiceDescriptorTable
80553fa0  80502b8c 00000000 0000011c 80503000
80553fb0  00000000 00000000 00000000 00000000
80553fc0  00000000 00000000 00000000 00000000
80553fd0  00000000 00000000 00000000 00000000
80553fe0  00002710 bf80c0b6 00000000 00000000
80553ff0  f8b67a80 f82e7b60 821bfa90 806e2f40
80554000  00000000 00000000 22bc349b 00000001
80554010  afa8a15b 01d7eb4f 00000000 00000000
kd> dd 80502b8c + 0xba*4
80502e74  805aa712 805c99e0 8060ea76 8060c43c
80502e84  8056f0d2 8063ab56 8061aca8 8061d332
80502e94  8059b804 8059c7cc 8059c1d4 8059baee
80502ea4  805bf456 80598d62 8059908e 805bf264
80502eb4  806064b6 8051ee82 8061cc3e 805cbd40
80502ec4  805cbc22 8061cd3a 8061ce20 8061cf48
80502ed4  8059a07c 8060db50 8060db50 805c892a
80502ee4  8063d80e 8060be28 80607fb8 8060882a
kd> u 805aa712
r0下的進程保護

 

可以看到在0環調用的是NtReadVirtualMemory,這實際上才是真正實現功能的地方。而SSDT將r 3和r 0聯系到一起。

r0下的進程保護

 

SSDT結構

在 NT 4.0 以上的 windows 操作系統中,默認就存在兩個系統服務描述表,這兩個調度表對應了兩類不同的系統服務,

這兩個調度表為:KeServiceDescriptorTable 和 KeServiceDescriptorTableShadow,

其中 KeServiceDescriptorTable 主要是處理來自 Ring3 層 Kernel32.dll 中的系統調用,

而 KeServiceDescriptorTableShadow 則主要處理來自 User32.dll 和 GDI32.dll 中的系統調用,

并且 KeServiceDescriptorTable 在 ntoskrnl.exe(Windows 操作系統內核文件,包括內核和執行體層)是導出的,

而 KeServiceDescriptorTableShadow 則是沒有被 Windows 操作系統所導出,

而關于 SSDT 的全部內容則都是通過 KeServiceDescriptorTable 來完成的。

r0下的進程保護

 

SSDT表的結構通過結構體表示為如下:

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    KSYSTEM_SERVICE_TABLE   ntoskrnl;  // ntoskrnl.exe 的服務函數
    KSYSTEM_SERVICE_TABLE   win32k;    // win32k.sys 的服務函數(GDI32.dll/User32.dll 的內核支持)
    KSYSTEM_SERVICE_TABLE   notUsed1;
    KSYSTEM_SERVICE_TABLE   notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;

其中每一項又是一個結構體:KSYSTEM_SERVICE_TABLE。通過結構體表示為如下:

typedef struct _KSYSTEM_SERVICE_TABLE
{
    PULONG  ServiceTableBase;          // SSDT (System Service Dispatch Table)的基地址
    PULONG  ServiceCounterTableBase;   // 用于 checked builds, 包含 SSDT 中每個服務被調用的次數
    ULONG   NumberOfService;           // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小
    ULONG   ParamTableBase;            // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;

通過看圖形化界面可以更加直觀,下圖是ntoskrnl.exe和win32k.sys的服務函數結構。

r0下的進程保護

 

HOOK SSDT

有了上面的知識儲備,理解SSDT HOOK就很容易了。

當3環程序執行后,操作系統拿著索引去SSDT表中找對應的0環程序,這時我們就可以在SSDT表中做點手腳,將某一個api函數的指針改成我們自己函數的指針,這樣執行的將會是我們自己的代碼。

首先需要定義我們自己的函數

ULONG g_Pid = 568;
NTSTATUS NTAPI MyOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId)
{
    NTSTATUS status;
    status = STATUS_SUCCESS;
    //當此進程為要保護的進程時
    if (ClientId->UniqueProcess == (HANDLE)g_Pid)
    {
        //設為拒絕訪問
        DesiredAccess = 0;
    }
    return NtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}

g_Pid定義為全局的,我們想保護哪個進程就將該進程的pid賦值給g_Pid。

比如這里就保護Dbgview.exe。

r0下的進程保護

 

這里函數準備好以后,就要將該函數的指針覆蓋原來NtOpenProcess的指針。但是需要注意的是:我們自己改自己的代碼是不用管權限的,改別人的代碼很有可能這塊內存是只讀的,并不可寫。

那么本質上就是SSDT對應的物理頁是只讀的,這里有兩種辦法,我們都知道物理頁的內存R/W位的屬性是由PDE和PTE相與而來的,那么我們就可以改變SSDT對應的PDE和PTE的R/W屬性,將物理頁設置為可讀可寫的。通過CR4寄存器判斷是2-9-9-12分頁還是10-10-12分頁。

if(RCR4 & 0x00000020)
{//說明是2-9-9-12分頁
    KdPrint(("2-9-9-12分頁 %pn",RCR4));
    KdPrint(("PTE1 %pn",*(Dword*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));
    *(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02; 
    KdPrint(("PTE1 %pn",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));
}
else
{//說明是10-10-12分頁
    KdPrint(("10-10-12分頁n"));
    KdPrint(("PTE1 %pn",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));
    *(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
    KdPrint(("PTE2 %pn",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));
}

還有一種方式就是通過Cr0寄存器。CR0寄存器的第16位叫做保護屬性位,控制著頁的讀或寫屬性。

r0下的進程保護

 

WP為1 時, 不能修改只讀的內存頁 , WP為0 時, 可以修改只讀的內存頁。那么我們就可以將WP位置為0,暫時關閉可讀屬性。

VOID PageProtectOn()
{
    __try
    {
        _asm
        {
            mov eax, cr0
            or eax, 10000h
            mov cr0, eax
            sti
        }
    }
    __except (1)
    {
        DbgPrint("PageProtectOn執行失敗!");
    }
}
VOID PageProtectOff()
{
    __try
    {
        _asm
        {
            cli
            mov eax, cr0
            and eax, not 10000h //and eax,0FFFEFFFFh
            mov cr0, eax
        }
    }
    __except (1)
    {
        DbgPrint("PageProtectOff執行失敗!");
    }
}

可以修改SSDT表后,就要寫函數來修改NtOpenProcess指針,也就是我們的HOOK函數。

NTSTATUS _hook()
{
    NTSTATUS status;
    status = STATUS_SUCCESS;
    PageProtectOff();

    PoldAddress = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a];
    KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a] = (ULONG)MyOpenProcess;

    PageProtectOn();
    return status;
}

在修改SSDT表前先關閉物理頁保護,修改完后要開啟物理頁保護,保證其他任務能夠順利完成。這里的索引可以通過ida或者debug工具去看。

r0下的進程保護

 

然后就是卸載鉤子,用于驅動卸載的時候使用。

NTSTATUS _unhook()
{
    NTSTATUS status;
    status = STATUS_SUCCESS;
    PageProtectOff();
    KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0x7a] = PoldAddress;
    PageProtectOn();
    return status;
}

最后是入口和卸載函數

VOID DriverUnload(PDRIVER_OBJECT driver)
{
    _unhook();
    DbgPrint("卸載了。。。。。n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
    _hook();
    DbgPrint("跑起來了。。。n");

    driver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

最后編譯,加載驅動,當我們嘗試用任務管理器殺死Dbgview時會被拒絕。

r0下的進程保護

 

如果通過taskkill同樣不行。

r0下的進程保護

 

后記

在SSDT上寫鉤子,在0環是最低級的方式,可以看到編寫代碼十分簡單,但是也是非常容易被檢測的,比如我們通過PChunter這樣的內核工具去看一下。

r0下的進程保護

 

可以看到NtOpenProcess赫然在列。實際上SSDT已作為基礎需要被了解。

本文由SD原創發布
轉載,請參考轉載聲明,注明出處: https://www.anquanke.com/post/id/262577
安全客 - 有思想的安全新媒體

分享到:
標簽:進程
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定