<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>

    通過對PsSetCreateProcessNotifyRoutineEx的逆向分析得出的結果來實現反進程監控

    VSole2021-11-13 16:21:53

    前言

    關于如何使用PsSetCreateProcessNotifyRoutineEx來實現進程監控,請看這篇文章:

    通過PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine實現進程與線程監控(https://bbs.pediy.com/thread-269312.htm)

    這篇主要是講解通過逆向PsSetCreateProcessNotifyRoutineEx來查看這個API如何實現的進程監控并以此來實現反進程監控。

    這次的實驗是在Win7 x86系統上進行,所以如果是其他版本的Windows系統,里面的具體實現會有不同。

    逆向分析PsSetCreateProcessNotifyRoutine

    首先看下PsSetCreateProcessNotifyRoutine的反匯編結果。

    PAGE:005947DE ; NTSTATUS __stdcall PsSetCreateProcessNotifyRoutine(PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine, BOOLEAN Remove)PAGE:005947DE                 public PsSetCreateProcessNotifyRoutinePAGE:005947DE PsSetCreateProcessNotifyRoutine proc nearPAGE:005947DE                                         ; CODE XREF: sub_6FC83F+2B↓pPAGE:005947DE                                         ; sub_798D02+22↓pPAGE:005947DEPAGE:005947DE NotifyRoutine   = dword ptr  8PAGE:005947DE Remove          = byte ptr  0ChPAGE:005947DEPAGE:005947DE                 mov     edi, ediPAGE:005947E0                 push    ebpPAGE:005947E1                 mov     ebp, espPAGE:005947E3                 push    0               ; 將0壓入棧中PAGE:005947E5                 push    dword ptr [ebp+Remove] ; 將Remove壓入棧中PAGE:005947E8                 push    [ebp+NotifyRoutine] ; 將Routine壓入棧中PAGE:005947EB                 call    PspSetCreateProcessNotifyRoutinePAGE:005947F0                 pop     ebpPAGE:005947F1                 retn    8PAGE:005947F1 PsSetCreateProcessNotifyRoutine endp
    

    可以看到PsSetCreateProcessNotifyRoutine就干了一件事,就是調用PspSetCreateProcessNotifyRoutine。在對它的調用時候,壓入的兩個參數除了NotifyRoutine和Remove以外,還壓入了一個0。事實上PsSetCreateProcessNotifyRoutine和PsSetCreateProcessNotifyRoutineEx的唯一區別就是,在壓入第三個參數的時候,PsSetCreateProcessNotifyRoutine壓入的是0,而PsSetCreateProcessNotifyRoutineEx壓入的是1。

    接著跟進PspSetCreateProcessNotifyRoutine。

    PspSetCreateProcessNotifyRoutine proc nearPAGE:005947F9                                         ; CODE XREF: PsSetCreateProcessNotifyRoutineEx+D↑pPAGE:005947F9                                         ; PsSetCreateProcessNotifyRoutine+D↑pPAGE:005947F9PAGE:005947F9 arg_Routine     = dword ptr  8PAGE:005947F9 arg_Remove      = dword ptr  0ChPAGE:005947F9 arg_Zero        = byte ptr  10hPAGE:005947F9PAGE:005947F9                 mov     edi, ediPAGE:005947FB                 push    ebpPAGE:005947FC                 mov     ebp, espPAGE:005947FE                 cmp     byte ptr [ebp+arg_Remove], 0 ; 判斷Remove是否為0PAGE:00594802                 push    ebxPAGE:00594803                 push    esiPAGE:00594804                 push    ediPAGE:00594805                 jz      loc_594905      ; 為0則跳轉,當要增加回調的時候,會傳入FALSE,所以這里會跳轉
    

    函數首先判斷壓入的Remove參數是否為0,是的話則跳轉。由于在增加回調的時候,Remove參數會使用FALSE,所以這個時候這個跳轉會成立。接著跟進loc_594905看看。

    PAGE:00594905 loc_594905:                             ; CODE XREF: PspSetCreateProcessNotifyRoutine+C↑jPAGE:00594905                 cmp     [ebp+arg_Zero], 0 ; 判斷第三個參數是否為0PAGE:00594909                 jz      short loc_59491E PAGE:0059490B                 push    [ebp+arg_Routine]PAGE:0059490E                 call    sub_56F869PAGE:00594913                 test    eax, eaxPAGE:00594915                 jnz     short loc_59491E PAGE:00594917                 mov     eax, STATUS_ACCESS_DENIEDPAGE:0059491C                 jmp     short loc_59496B
    

    在loc_594905中,函數會判斷傳進的第三個參數是否為0。上面提到過,這個參數其實是區別你調用的是PsSetCreateProcessNotifyRoutine還是PsSetCreateProcessNotifyRoutineEx。如果是后者,這個參數就是1,前者這個參數是0。而兩者的區別只是后者會調用sub_56F869。但由于這個函數的作用對今天的主題而言沒什么意義,所以這里不分析,直接接著看loc_59491E的內容。

    PAGE:0059491E loc_59491E:                             ; CODE XREF: PspSetCreateProcessNotifyRoutine+110↑jPAGE:0059491E                                         ; PspSetCreateProcessNotifyRoutine+11C↑jPAGE:0059491E                 xor     eax, eax        ; 將eax清0PAGE:00594920                 cmp     [ebp+arg_Zero], al ; 將傳入的第三個參數與al做比較PAGE:00594923                 setnz   al              ; 將zf位取反然后賦值給alPAGE:00594926                 push    eax             ; 將eax入棧PAGE:00594927                 push    [ebp+arg_Routine] ; 將要設置的回調函數地址入棧PAGE:0059492A                 call    AllocateAssignPAGE:0059492F                 mov     ebx, eax        ; 將分配的內存的地址賦給ebxPAGE:00594931                 test    ebx, ebx        ; 判斷ebx是否為0PAGE:00594931                                         ; 為0則為eax賦值內存不夠的返回值并跳到函數結束PAGE:00594933                 jnz     short loc_59493CPAGE:00594935                 mov     eax, STATUS_INSUFFICIENT_RESOURCESPAGE:0059493A                 jmp     short loc_59496B
    

    函數將要設置的回調函數的地址作為第一個參數,根據調用是否帶有Ex的PsSetCreateProcessNotifyRoutine來傳入第二個參數。

    這里主要是調用AllocateAssign這個函數,當然這個函數名是我重命名以后的函數名。因為這個函數就是為保存回調函數地址申請一塊空間,如果申請到了那么會跳轉繼續執行,如果沒申請到就會設置返回值為沒有足夠的資源并且跳到函數結束執行,接下來看看AllocateAssign的反匯編結果。

    PAGE:005946D7 AllocateAssign  proc near               ; CODE XREF: PsSetLoadImageNotifyRoutine+E↑pPAGE:005946D7                                         ; DbgkLkmdRegisterCallback+53↑p ...PAGE:005946D7PAGE:005946D7 arg_NotifyRoutine= dword ptr  8PAGE:005946D7 arg_Zero        = dword ptr  0ChPAGE:005946D7PAGE:005946D7                 mov     edi, ediPAGE:005946D9                 push    ebpPAGE:005946DA                 mov     ebp, espPAGE:005946DC                 push    'brbC'          ; TagPAGE:005946E1                 push    0Ch             ; NumberOfBytesPAGE:005946E3                 push    PagedPool       ; PoolTypePAGE:005946E5                 call    ExAllocatePoolWithTagPAGE:005946EA                 test    eax, eaxPAGE:005946EC                 jz      short loc_5946FDPAGE:005946EE                 mov     ecx, [ebp+arg_NotifyRoutine]PAGE:005946F1                 and     dword ptr [eax], 0 ; 分配到的內存低4位清0PAGE:005946F4                 mov     [eax+4], ecx    ; 分配到的內存的中間4位賦值為函數地址PAGE:005946F7                 mov     ecx, [ebp+arg_Zero]PAGE:005946FA                 mov     [eax+8], ecx    ; 分配到的內存的最高4位賦值為傳入的參數,此時為0PAGE:005946FDPAGE:005946FD loc_5946FD:                             ; CODE XREF: AllocateAssign+15↑jPAGE:005946FD                 pop     ebpPAGE:005946FE                 retn    8PAGE:005946FE AllocateAssign  endp
    

    可以看到這個函數中,首先是申請0xC大小的內存空間,然后將低4為清0,中間4位賦值為要設置的回調函數的地址,最高4位根據傳入的參數來設置。

    由此我們可以知道,在PsSetCreateProcessNotifyRoutine函數中,系統會分配0xC大小的內存,而內存的中間4位保存的就是要設置的回調函數的地址。根據調用的是PsSetCreateProcessNotifyRoutine還是PsSetCreateProcessNotifyRoutineEx,系統會設置最高的4位的值,如果是前者會設置位0,后者設置位1。

    接著看內存看內存分配并且賦值成功以后,會執行的代碼,也就是loc_59493C的代碼。

    PAGE:0059493C loc_59493C:                             ; CODE XREF: PspSetCreateProcessNotifyRoutine+13A↑jPAGE:0059493C                 mov     esi, offset ProcessFuncArray ; 將進程數組地址賦給esiPAGE:00594941                 xor     edi, edi        ; 為edi清0PAGE:00594943PAGE:00594943 loc_594943:                             ; CODE XREF: PspSetCreateProcessNotifyRoutine+165↓jPAGE:00594943                 push    0               ; 將0壓入棧中PAGE:00594945                 mov     ecx, ebx        ; 分配到的內存的地址賦值給ecxPAGE:00594947                 mov     eax, esi        ; 數組地址賦值給eaxPAGE:00594949                 call    SetArrayPAGE:0059494E                 test    al, alPAGE:00594950                 jnz     short loc_594972 ; 函數返回值不為0,說明設置成功PAGE:00594952                 add     edi, 4          ; 將edi加4PAGE:00594955                 add     esi, 4          ; 數組的地址加4,也就是獲得下一個元素的地址PAGE:00594958                 cmp     edi, 100h       ; 判斷是否小于0x100,小于的話。使用新地址來調用上面的函數繼續設置PAGE:0059495E                 jb      short loc_594943 ; 這里可以得出結論,這個數組一共0x100 % 4 = 0x40PAGE:0059495E                                         ; 也就是說,最多可以設置64個進程創建的回調函數PAGE:00594960                 push    ebx             ; BufferPAGE:00594961                 call    FreeAllocatePAGE:00594966                 mov     eax, STATUS_INVALID_PARAMETER ; 如果執行到這里說明賦值失敗PAGE:00594966                                         ; 接下來就釋放內存,返回值設為失敗PAGE:0059496BPAGE:0059496B loc_59496B:                             ; CODE XREF: PspSetCreateProcessNotifyRoutine+A8↑jPAGE:0059496B                                         ; PspSetCreateProcessNotifyRoutine+10A↑j ...PAGE:0059496B                 pop     ediPAGE:0059496C                 pop     esiPAGE:0059496D                 pop     ebxPAGE:0059496E                 pop     ebpPAGE:0059496F                 retn    0Ch
    

    可以看到,函數首先將一個數組地址賦給esi,然后將0壓入棧中并且調用SetArray這個函數,在調用這個函數之前還把esi的地址,也就是這個數組的地址賦給了eax,把ebx的值賦給ecx,這個ebx的值就是在上面調用完AllocateAssign函數以后得到的分配和賦值的內存地址。因為在調用AllocateAssign函數后的返回值eax賦值給了ebx。

    調用完SetArray函數以后,會判斷返回值是否為0,不為0那就是調用成功,也就是回調函數設置成功,就會跳轉到函數調用成功的退出代碼執行。如果為0說明并沒有設置完成,隨后會對edi和esi進行加4的操作并判斷edi是否小于0x100,如果小于就跳上去繼續執行SetArray函數。由于此時esi指向的是ProcessFuncArray數組的地址,所以加4就是指向數組中下一個元素,edi就是為了避免數組溢出而設置,根據值的大小可以猜測這個數組一共0x40個大小。

    分析到這里就可以得出結論,PsSetCreateProcessNotifyRoutine函數設置進程監控的回調函數的辦法就是首先申請一塊內存來保存要設置的回調函數的地址,然后將這個申請到的內存的地址賦到ProcessFuncArray數組中的某個元素。

    那么它是怎么判斷要賦值到哪個元素呢,就要繼續跟進SetArray函數。

    PAGE:00594706 SetArray        proc near               ; CODE XREF: IoRegisterPriorityCallback+5B↑pPAGE:00594706                                         ; IoUnregisterPriorityCallback+4C↑p ...PAGE:00594706PAGE:00594706 var_8           = dword ptr -8PAGE:00594706 var_4           = dword ptr -4PAGE:00594706 arg_Zero        = dword ptr  8PAGE:00594706PAGE:00594706                 mov     edi, ediPAGE:00594708                 push    ebpPAGE:00594709                 mov     ebp, espPAGE:0059470B                 push    ecx             ; 此時ecx是分配的地址,eax是數組的地址PAGE:0059470C                 push    ecxPAGE:0059470D                 push    ebxPAGE:0059470E                 push    esiPAGE:0059470F                 push    ediPAGE:00594710                 mov     esi, eax        ; 將數組地址賦給esiPAGE:00594712                 test    ecx, ecx        ; 判斷分配地址是否為0PAGE:00594714                 jz      short loc_594726 PAGE:00594716                 push    8PAGE:00594718                 pop     edxPAGE:00594719                 call    ExAcquireRundownProtectionEx ; 為了讓調用者安全訪問對象,這里不是重點,過PAGE:0059471E                 test    al, alPAGE:00594720                 jz      loc_5947D0      ; 返回為0就代表函數運行失敗,會退出函數PAGE:00594726PAGE:00594726 loc_594726:                             ; CODE XREF: SetArray+E↑jPAGE:00594726                 mov     ebx, [esi]      ; 將數組中的內容取出賦給ebxPAGE:00594728                 mov     eax, ebx        ; 將內容賦給eaxPAGE:0059472A                 jmp     short loc_594749
    

    函數將數組中的對應的內容賦給eax以后跳轉到loc_594759執行,接著看loc_594749處的代碼。

    PAGE:00594749 loc_594749:                             ; CODE XREF: SetArray+24↑jPAGE:00594749                 xor     eax, [ebp+arg_Zero] ; 將得到的值與0進行異或,與7做比較,小于7則跳轉.PAGE:0059474C                 cmp     eax, 7          ; 所以這兩條合起來看的話,就是除了低3位,其他都為0,否則就跳轉PAGE:0059474F                 jbe     short loc_59472C ; 可以得出結論,數組中元素高29位用來判斷是否還未使用。PAGE:0059474F                                         ; 如果為0,說明還未使用
    

    在這里程序將得到的數組中對應的元素內容和傳入的參數進行異或,然后判斷是否小于7,如果小于7就跳轉到對數組進行賦值的代碼進行執行。而0x7對應的二進制是高29位是0,低3位是1,在跟進得到的數組內容要和0進行異或可以知道,這里是判斷得到的數組中的內容高29位是不是0。如果高29位中有1個是1,那么異或以后得到的值就會大于7。

    接著看loc_59472C中的代碼,也就是對數組進行賦值的代碼。

    PAGE:0059472C loc_59472C:                             ; CODE XREF: SetArray+49↓jPAGE:0059472C                 test    ecx, ecx        ; ecx是申請的地址,判斷是否為0,為0則跳轉PAGE:0059472E                 jz      short loc_594737PAGE:00594730                 mov     eax, ecx    ; 將ecx賦給eaxPAGE:00594732                 or      eax, 7      ; 將低三位置1,這里要注意,在將申請到的內存地址復制到數組之前,會將地址的低3位置1PAGE:00594735                 jmp     short loc_594739 PAGE:00594737 ; ---------------------------------------------------------------------------PAGE:00594737PAGE:00594737 loc_594737:                             ; CODE XREF: SetArray+28↑jPAGE:00594737                 xor     eax, eax        ; 如果ecx是0,那就會跳轉到這里執行,也就是eax會被變成0PAGE:00594739PAGE:00594739 loc_594739:                             ; CODE XREF: SetArray+2F↑jPAGE:00594739                 mov     edx, eax        ; 將或運算得到的值賦給edxPAGE:0059473B                 mov     edi, esi        ; 取出數組對應元素地址給ediPAGE:0059473D                 mov     eax, ebx        ; ebx保存的是數組中的內容,賦給eaxPAGE:0059473F                 lock cmpxchg [edi], edx ; edi是數組地址,而eax是數組元素,所以eax==[edi]PAGE:0059473F                                         ; 那么此時,edx,也就是計算以后的地址會賦值給[edi],也就是賦值到數組中PAGE:00594743                 cmp     eax, ebx        ; 由于上面的賦值操作,這里會成立跳轉PAGE:00594745                 jz      short loc_594751 PAGE:00594747                 mov     ebx, eax
    

    這里的ecx的值就是前面用來保存回調函數地址所調用的AllocateAssign函數得到的分配的內存地址。因為上面申請到內存以后,將內存地址先賦給eax在賦給ecx,隨后的代碼中都沒對ecx進行改變。

    將這個地址賦給eax并且與7異或,那就是將低3位置1,接著就跳轉到loc_594739處執行。執行的時候,這個esi就是要賦值的數組的對應元素的地址,因為在進入SetArray函數前,將數組的對應元素的地址賦給了esi后就沒在改變了。而根據cmpxchg的功能可以知道,這里就是將AllocateAssign分配到的內存地址和7或運算以后的地址復制到數組中,隨后跳轉到loc_594751執行。那么繼續看loc_594751執行的代碼。

    PAGE:00594751 loc_594751:                             ; CODE XREF: SetArray+3F↑jPAGE:00594751                 mov     edi, ebx        ; ebx中是數組中原來的內容,將他賦值給ediPAGE:00594753                 and     edi, 0FFFFFFF8h ; 將低3位清0PAGE:00594756                 cmp     edi, [ebp+arg_Zero] ; 判斷是否為0,不為0則跳轉退出函數,函數執行失敗,返回值為0PAGE:00594759                 jnz     short loc_5947C4 ; 這里就是判斷原來的高29位是不是0,不是的話,函數執行失敗PAGE:0059475B                 test    edi, edi         ;判斷edi是否位0PAGE:0059475D                 jz      short loc_5947C0 ; 為0則跳轉,函數執行成功,返回值eax=1PAGE:0059475F                 mov     esi, large fs:124h ; 接下來的內容和APC相關,這里不討論PAGE:00594766                 dec     word ptr [esi+86h]PAGE:0059476D                 xchg    eax, [ebp+var_4]PAGE:00594770                 mov     ecx, dword_53C7D8PAGE:00594776                 and     ecx, 1PAGE:00594779                 xchg    eax, [ebp+var_8]PAGE:0059477C                 test    ecx, ecxPAGE:0059477E                 jz      short loc_594794PAGE:00594780                 mov     ecx, offset dword_53C7D8PAGE:00594785                 call    ExfAcquirePushLockExclusivePAGE:0059478A                 mov     ecx, offset dword_53C7D8PAGE:0059478F                 call    ExfReleasePushLockExclusive
    

    在這里,程序首先將原來數組中保存的內容的低三位清0,接著判斷得到的數據是否為0。也就是判斷原來數組中的內容的高29位是不是0,如果是0說明上面的賦值成功了,程序就會跳轉到函數執行成功的地方,也就是loc_5947C0處繼續執行,在loc_5947C0,程序會將eax賦值為1,也就是說返回值是1。如果高29位不是0,那上面的賦值就會失敗,程序就會跳轉到loc_5947C4的地址繼續執行,在這個地址中,程序會將eax賦值為0,也就是說返回值是0。

    綜上,可以得出以下的結論:

    • 程序會申請一塊0xC大小的內存,內存中低4為是0,中間4為是要設置的回調函數的地址,高4為根據是否是Ex函數來設置為是0還是1。
    • 從整型數組ProcessArray中按順序依次查看對應的數組的元素的位置是不是可以用來保存分配的內存地址。
    • 而這個位置能不能存儲這個地址,取決于這個位置中保存的內容的高29位是不是0,如果是0就用來保存申請到的地址。

    反進程監控的實現

    根據上面的分析,可以得出結論,設置這些回調函數的時候,操作系統首先會申請一個0xC大小的內存,然后將內存地址的中間4位設置為回調函數的地址。隨后就會在ArrayProcess數組中存放這個分配的內存地址,當要注意這個內存地址的低3位在復制到數組之前會被置1。

    那也就是說當有進程的狀態發生變化的時候,操作系統就會去ProcessArray數組中遍歷,如果發現對應的元素的高29位不是0就把內容取出并且將低3位置0(因為在復制到數組之中去的時候,分配的內存的低3位會被置為1)。置0以后得到的地址在+4的地方保存的就是回調函數的地址,系統就會根據這個地址在調用相應的回調函數。

    所以要進行反進程監控,只需要找到這個數組,并且將這個數組中的回調函數的地址取出然后通過PsSetCreateProcessNotifyRoutine函數刪除這個回調函數就可以了。

    要找到這個數組首先需要找到PspSetCreateProcessNotifyRoutine函數地址,因為在這個函數中才有對這個數組進行使用,才可以找到數組地址。但因為這個函數并沒有導出,所以需要通過PsSetCreateProcessNotifyRoutine函數來找到它,因為這個函數是導出函數,可以直接獲取到地址。

    在PsSetCreateProcessNotifyRoutine中對PspSetCreateProcessNotifyRoutine的調用如下:

    可以看到是調用地址處偏移為9的地方就是PspSetCreateProcessNotifyRoutine函數的地址,所以可以將0xE8作為特征碼來查找函數地址。

    得到PspSetCreateProcessNotifyRoutine函數地址以后,可以在對esi賦值為函數地址的地方找到ProcessFuncArray數組的地址如下:

    所以得到PspSetCreateProcessNotifyRoutine函數地址以后,可以根據0xEB,0x2F和0xBE這三個特征碼來定位進程數組的地址(當然這樣有點太少,可以弄的多一點,這里就偷了個懶)。

    根據以上內容就可以寫出獲取ProcessFuncArray地址的代碼如下:

    PUINT32 SearchProcessArray(){    UNICODE_STRING uStrFuncName;    PUCHAR pPsSetCreateProessNotifyRoutine = NULL;    PUCHAR pPspCreateProcessNotifyRoutine = NULL;    PUINT32 pProcessArray = NULL;     //獲取PsSetCreateProcessNotifyRoutine函數地址    RtlInitUnicodeString(&uStrFuncName, L"PsSetCreateProcessNotifyRoutine");    pPsSetCreateProessNotifyRoutine = (PUCHAR)MmGetSystemRoutineAddress(&uStrFuncName);     //獲取PspCreateProcessNotifyRoutine函數的地址,由于程序執行到最后的ret語句會使用0xC2,所以這里用它作為結束    while (pPsSetCreateProessNotifyRoutine && *pPsSetCreateProessNotifyRoutine != 0xC2)       {        //根據0xE8來獲取函數地址        if (*pPsSetCreateProessNotifyRoutine == 0xE8)        {            pPspCreateProcessNotifyRoutine = pPsSetCreateProessNotifyRoutine + 5 + *(PUINT32)(pPsSetCreateProessNotifyRoutine + 1);            break;        }        pPsSetCreateProessNotifyRoutine++;    }     //獲取回調函數數組的地址,由于程序執行到最后的ret語句會使用0xC2,所以這里用它作為結束    while (pPspCreateProcessNotifyRoutine && *pPspCreateProcessNotifyRoutine != 0xC2)    {        //根據特征碼來獲取數組的地址        if (*pPspCreateProcessNotifyRoutine == 0xEB &&            *(pPspCreateProcessNotifyRoutine + 1) == 0x2F &&            *(pPspCreateProcessNotifyRoutine + 2) == 0xBE)        {            pProcessArray = (PUINT32)*(PUINT32)(pPspCreateProcessNotifyRoutine + 3);            break;        }        pPspCreateProcessNotifyRoutine++;    }     return pProcessArray;}
    

    獲取到函數地址以后,只需要遍歷數組并查看相應的高29位是不是0,如果不是0說明里面保存了地址,取出這個地址將低3位置0以后在加4就得到了保存回調函數地址的地址,將這個回調函數地址取出就可以調用PsSetCreateProcessNotifyRoutine函數來進行回調函數的刪除,代碼如下:

    NTSTATUS status = STATUS_SUCCESS;PUINT32 pProcessArray = NULL, pFuncAddr = NULL;UINT32 i = 0;  pProcessArray = SearchProcessArray();if (pProcessArray != NULL){    for (i = 0; i < 0x40; i++)    {        if (pProcessArray[i] & ~0x7)        {            pFuncAddr = (PUINT32)(pProcessArray[i] & ~0x7 + 4);            status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)*pFuncAddr, TRUE);            if (NT_SUCCESS(status))            {                DbgPrint("成功刪除進程的回調函數, 函數地址0x%X\r", *pFuncAddr);            }        }    }}else DbgPrint("沒有找到進程數組\r");
    

    運行結果

    首先是開啟進程監控以后,可以看到所有進程的創建都被監控到了,而且demo.exe進程的創建也被攔截了。

    當運行程序卸載掉回調函數以后,在創建進程就沒有被監控到,并且demo.exe進程也可以成功創建。

    函數調用回調函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    當線程從等待狀態蘇醒后,會自動檢測自己得APC隊列中是否存在APC過程。所以只需要將目標進程的線程的APC隊列里面添加APC過程,當然為了提高命中率可以向進程的所有線程中添加APC過程。然后促使線程從休眠中恢復就可以實現APC注入。往線程APC隊列添加APC,系統會產生一個軟中斷。第二個參數表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT訪問權限。第三個參數表示傳遞給執行函數的參數。如果直接傳入shellcode不設置第三個函數,可以直接執行shellcode。
    在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數
    全局鉤子注入在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數
    Dll注入
    2021-11-08 14:57:41
    最近太忙啦XDM,又在做一些列的分析復現工作量有點大,更新要慢一點了。一致,也不會覆蓋其他的進程信息。
    RCE系統交互條件與受限環境下的利用
    動態函數PHP中支持一個功能叫 variable function ,變量函數的意思。//最終是system;當一個變量后邊帶括號,那他就被視作一個函數。編譯器會解析出變量的值,然后會去找當前是否存在名為“system()”的函數并執行它。這里就不給實例了,很多免殺案例中都用到了這個特性。也是被瘋狂查殺的特征。回調函數回調函數,簡單來說就是一個函數不是由我直接調用,而是通過另一個函數去調用它。
    Win32k組件最初的設計和編寫是完全建立的用戶層上的,但是微軟在 Windows NT 4.0 的改變中將 Win32k.sys 作為改變的一部分而引入,用以提升圖形繪制性能并減少 Windows 應用程序的內存需求。窗口管理器(User)和圖形設備接口(GDI)在極大程度上被移出客戶端/服務端運行時子系統(CSRSS)并被落實在它自身的一個內核模塊中。
    OPC UA協議是工業控制領域中的一種十分流行的通訊協議。漏洞分析該漏洞存在于OPC UA .NET Standard Server Stack代碼庫中。根據官方漏洞公告,遠程攻擊者可通過發送惡意的請求來耗盡服務器所有可用內存。同時設置該channel的各種消息處理回調函數。并調用Attach函數將該TcpServerChannel與Client socket進行關聯。圖 12、DoReadComplete讀取Messsage消息過程OnMessageReceive函數最終是通過HandleInComingMessage來具體處客戶端請求消息內容。
    本系列將以官網資料為基礎主要通過動態跟蹤來解析DynamoRIO的源代碼。因為如果不結合實例只是將各函數的作用寫出來,實在無法很好的說明問題,我們將以代碼覆蓋工具drcov為例,分析DynamoRIO的執行流程。
    在cgiHandler函數中,將用戶的HTTP請求參數作為環境變量,通過諸如LD_PRELOAD即可劫持進程的動態鏈接庫,實現遠程代碼執行。代碼首先拼接出用戶請求的cgi完整路徑并賦予cgiPath,然后檢查此文件是否存在以及是否為可執行文件。隨后代碼將cgiPath、envp、stdIn與stdOut作為參數傳入launchCgi函數中。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类