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

    BattlEye內核驅動檢測模塊深入分析

    VSole2022-07-18 16:33:35

    BattlEye概述

    BattlEye總共分為以下4個部分:

    BEService - 與BattlEye服務器通信的服務。

    BEDaisy - 內核驅動,執行各種內核層的檢測,并與BEClient通信。

    BEClient - 一個DLL,運行在游戲進程中,負責執行各種應用層的BE shellcode,并與內核驅動進行通信。

    BEServer - BattlEye服務器,收集上傳的信息,并判定作弊行為。

    本次分析的是BEDaisy,也就是BE內核驅動中的各種檢測。

    BattlEye內核驅動檢測模塊深入分析

    BE內核驅動中包含著很多種檢測,在發現檢測到異常情況時首先會記錄到一個內部的鏈表中,然后當BEClient對BE內核驅動發起特定長度的讀請求時,BE內核驅動會將鏈表內的數據發送給BEClient,BEClient再將其發送給BEServer。

    半個月前寫過一個簡易的繞過BE內核驅動的程序,原理就是阻斷這一過程,具體原理請看我的上一篇文章:https://bbs.pediy.com/thread-273334-1.htm

    下面的內容主要分為三個部分,第一個是上傳部分,主要是講解BEClient給BE內核驅動發送的各種檢測相關的數據;第二個是檢測部分,重點講述BE內核驅動中的各種檢測;第三個部分是對于這些檢測的總結。BE內核驅動中還包含一些其他的內容,比如數據包加解密算法、設備UID算法等,這些內容都不涉及“檢測”,因此在本文中不進行分析。

    上傳部分

    BEClient通過對BE驅動調用Write方法,也就是對應驅動的IRP_MJ_WRITE方式進行上傳。上傳的內容主要是一些黑名單特征,這些特征應該是從服務器下發的,因此可以在不重新編譯驅動的情況下,動態調整檢測的特征。

    [未知]黑名單特征(upload type 0)

    以類似數組的形式緊密排列,由于檢測數據不定長,因此每個數據包是動態長度的,依靠包頭記錄的數據包長度確定下一個數據包的位置。

     檢測分為兩類:

    給定偏移量的特征,BE在檢測時只會在特定的偏移量上進行匹配。

    沒有給定偏移量的特征,BE在檢測時會按子串匹配的方式嘗試所有位置進行匹配。

    如果report list 0中存在數據包,則會挨個檢測是否有匹配的特征,如果存在則直接原封上報異常數據。(由于并沒有人寫入report list 0,因此懷疑該檢測暫未開啟)

     report list 0 數據結構如下:

    struct AbnormalListItem {    // because nobody writes to report list 0, so some parts of the structure is unknown    BYTE Unknown[10];    BYTE Content[64];};
    

    通過IrpWrite上傳的數據包如下:

    struct UploadPatternBlackListItemType0 {    // -1 means no specified match offset, it will try every possible offset    // not -1 means a specified offset, it will just try the offset    BYTE MatchOffset;    // if the length <= 32, it will be copied to the g_PatternBlackList    BYTE PatternLength;    // length depends on PatternLength    BYTE Content[0];};
    

    g_PatternBlackList是存儲著32個PatternBlackListItem的數組,具體數量記錄在g_PatternBlackListSize中。

    struct PatternBlackListItemType0 {    // pattern in black list up to 32 bytes    BYTE Pattern[32];    // length up to 32    ULONG Length;};
    

    在檢測線程啟動時,會向g_PatternBlackList添加一個9字節長度的硬編碼的特征(看起來像是有關ROP的一些特征?不太清楚。)

    48 81 C4 80 01 00 00 5F C3
    

    對應amd64匯編

    add rsp, 180hpop rdiret
    

    回調黑名單特征(upload type 1)

    該檢測針對的是進程、線程的前置、后置回調,注冊表回調,映像加載回調,對這些函數的頭部64個字節進行特征檢測。

    struct UploadPatternBlackListItemType1or2 {    // -1 means universal pattern, this check will be applied to each callback    // not -1 means this check only works on a specific callback    BYTE FunctionType;    // -1 means no specified match offset, it will try every possible offset    // not -1 means a specified offset, it will just try the offset    BYTE MatchOffset;    // length of the pattern    BYTE PatternLength;    // length depends on PatternLength    BYTE Content[0];};
    

    系統調用黑名單特征(upload type 2)

    數據包格式同上一個特征,檢測的對象為系統調用函數的頭部64字節。

    BE驅動完整性檢測特征(upload type 3)

    上傳的內容為一個給定偏移量的字節序列,在后續步驟中(見report type 18)會使用上傳的特征對BE驅動自身的重點代碼進行檢查,檢查BE驅動是否被篡改。

    struct UploadSelfIntegrityCheck {    // if it is true, it means use stored driver memory range    // if it is false, it means use driver memory range read from driver object    BOOLEAN UseStoredDriverInfo;    // offset to the driver module    ULONG Offset;    // unknown, has an impact on the reporting policy    // if the flag is true, then normal means upload, abnormal means don't upload    // maybe use to detect some kind of attack?    BOOLEAN FlipReportPolicy;    // compare size, up to 64 bytes    ULONG CompareSize;    // content of normal data, length depends on CompareSize    BYTE Content[0];};
    

    Dxgkrnl某內部未導出函數特征(upload type 4)

    該特征用于定位Dxgkrnl某個內部的未導出函數,在后續步驟(見report type 22),BE將會Hook該函數,并對該函數的地址范圍進行檢查。

    struct UploadDxgkrnlInternalFunctionRangeCheck {    // length = upload packet length - 1    BYTE Pattern[0];    // how far is the function address from the pattern matching address    BYTE Offset;}
    

    InfinityHook檢測(upload type 5)

    該類型的數據包僅是為了觸發InfinityHook檢測(見report type 23),不傳輸數據。

    檢測部分

    該部分內容較多,總共有30多種檢測。大多數的檢測都具有標號,只有當檢測結果異常時才會記錄,并傳輸給處在應用層的BEClient,然后再由其發送給BE的服務器。所有具有標號的檢測如下,除此之外還有少量處在IRP_MJ_READ的handler中的沒有標號的檢測(例如:獲取設備UID,虛擬機檢測等)。

    1. 派遣函數完整性檢測
    2. 系統線程啟動地址檢測
    3. 進程、線程回調功能性檢測
    4. 游戲進程線程創建檢測
    5. PsLookupThreadByThreadId hook檢測
    6. [未知]
    7. 進程、線程、注冊表回調hook檢測
    8. 進程、線程、注冊表回調地址模塊范圍檢測
    9. PhysicalMemory引用檢測
    10. 系統調用完整性檢測
    11. [未知]
    12. 模塊異常指令檢測
    13. DxgCoreInterface 地址范圍檢測
    14. DxgCoreInterface hook檢測
    15. 系統線程堆棧檢測
    16. 隱藏驅動檢測
    17. [未知]
    18. 回調函數信息上報
    19. BE驅動完整性檢測
    20. 模塊IAT hook檢測
    21. gDxgkInterface 地址范圍檢測
    22. gDxgkInterface hook檢測
    23. Dxgkrnl某內部未導出函數范圍檢測(disabled)
    24. infinity hook 檢測
    25. gDxgkWin32kEngInterface 地址范圍檢測
    26. gDxgkWin32kEngInterface hook檢測
    27. PCI設備檢測
    28. HalDispatchTable 地址范圍檢測
    29. HalDispatchTable hook檢測
    30. HalPrivateDispatchTable 地址范圍檢測
    31. HalPrivateDispatchTable hook檢測
    32. FltMgrMsg對象callback模塊范圍檢測
    33. FltMgrMsg對象callback hook檢測
    34. ext_ms_win_core_win32k_full_export_l1 地址范圍檢測
    35. ...

    BE驅動完整性檢測(report type 18)

    在接收到應用程序上傳的數據后會開始檢測。BE會對自身的驅動的關鍵部位進行檢查,檢查是否被篡改。如果出現異常則會上報。 

    數據包結構:

    struct PacketSelfIntegrityCheck {    // 18 is self integrity check    BYTE PacketType;    // if it is true, it means use stored driver memory range    // if it is false, it means use driver memory range read from driver object    BOOLEAN UseStoredDriverInfo;    // offset to the driver module    ULONG Offset;    // content of checked address, 64 bytes    BYTE Content[64];};
    

    系統調用完整性檢測(report type 9)

    BE會對通過MmGetSystemRoutineAddress獲得的系統函數進行完整性檢測,會檢測此時調用MmGetSystemRoutineAddress獲得的地址與以前獲得的地址是否相同,會檢測系統函數頭部是否存在hook,如果存在hook則會追蹤連續的無條件跳轉,直到最終的hook函數,并上報該hook函數的特征上報。

    總共分為4類異常:

    • 函數指針修改
    • 函數地址不在模塊范圍內(手動映射的驅動的hook)
    • 追蹤跳轉后,函數地址不在模塊范圍內(類似上一個異常情況)
    • 存在int 3斷點,說明系統正在被調試

    除此之外,如果判定正常,仍會將信息臨時記錄在report list 2中,方便在后續過程中檢查是否存在黑名單特征。

    數據包結構:

    struct PacketSyscallIntegrityCheck {    // 9 is syscall integrity check    BYTE PacketType;    // each syscall function has an index    BYTE FuncIndex;    // -1: fine    // 0: function pointer modification    // 1: address out of module range    // 2: after jump, address out of range    // 3: int3 trap, may be under debugging    BYTE ErrorType;    // after useless jump instructions, the function body's address    PVOID Address;    // dump 64 bytes    BYTE Content[64];};
    

    系統線程啟動地址檢測(report type 1)

    會試圖通過多種手段遍歷系統線程(通過SystemProcessInformation獲得線程信息、通過枚舉TID嘗試得到線程對象),如果遍歷過程中檢測到隱藏進程/線程(找不到系統進程或系統進程的SystemProcessInformation中找不到當前線程),則會在全局變量中進行記錄。

    如果檢測到啟動地址不在加載模塊地址范圍內的系統線程(模塊地址范圍會在LoadImageNotify中以鏈表的形式記錄),則會上報異常數據。猜測是用來檢測kdmapper等工具加載的模塊。

    數據包結構:

    struct PacketSystemThreadStartAddressCheck {    // 1 is system thread start address check    BYTE PacketType;    // start address read from SYSTEM_PROCESS_INFORMATION structure    PVOID StartAddress;    // dump 64 bytes from start address    BYTE Content[64];    // thread running time    // from thread creation to now    LARGE_INTEGER RunningTime;    // CountdonwId = SystemProcessInformation->NumberOfThreads - AbnormalThreadIndex - 1    // counting thread indexes from back to front    // making the ID generic    USHORT CountdownId;    // thread create time    // between process creation and thread creation    LARGE_INTEGER CreateTime;};
    

    系統線程堆棧檢測(report type 14)

    向所有系統線程插入APC,調用RtlWalkFrameChain獲得調用者列表,依次檢查各個內核空間調用者的地址是否在模塊范圍內,是否存在黑名單中的特征,是否存在多次跳轉(>=5)、int3、nop等異常情況,如果存在則直接上報異常數據,如果判斷正常則會添加到report list 0,待進一步進行黑名單檢查。

    數據包結構:

    struct PacketSystemThreadStartAddressCheck {    // 14 is system thread stack check    BYTE PacketType;    // bad caller index in the RtlWalkFrameChain result    BYTE CallerIndex;    // bad caller's return address    PVOID Address;    // 64 bytes of caller's content    BYTE Content[64];    // notice: only 32 bits    // which thread has the bad caller    ULONG ThreadId;    // image name length    BYTE ImageNameLength;    // image name buffer    // length depends on the ImageNameLength    BYTE ImageName[0];    // low 32 bits of StartAddress, always upload    ULONG LowStartAddress;    // may be null if the StartAddress is invalid    PVOID StartAddress;    // may be null if the StartAddress is invalid    HANDLE ProcessId;    // thread running time    // from thread creation to now    LARGE_INTEGER RunningTime;    // CountdonwId = SystemProcessInformation->NumberOfThreads - AbnormalThreadIndex - 1    // counting thread indexes from back to front    // making the ID generic    USHORT CountdownId;    // thread create time    // between process creation and thread creation    LARGE_INTEGER CreateTime;    // track the E9 jumps after the return address up to 60 bytes,    // record up to 10 addresses    BYTE FollowAddressCount;    // size depends on the FollowAddressCount    PVOID FollowAddressArr[0];};
    

    進程、線程、注冊表回調檢測

    回調Hook檢測(report type 6)

    會檢測進程、線程的前置、后置回調,注冊表回調,映像加載回調,檢測是否存在一下幾種hook,最多檢測頭部64字節:

    • FF 25 XX XX XX XX: jmp [addr]
    • 48 B8 XX XX XX XX XX XX XX XX: mov rax, imm
    • FF E0: jmp rax

    (注:不會多次追蹤跳轉,只會追蹤1次,感覺設計不太合理)

    當檢測到hook時才會上報異常數據,結構如下:

    struct PacketCallbackHookCheck {    // 6 is callback hook check    BYTE PacketType;    // function type:    // 0: process callback    // 1: thread callback    // 2: register callback    // 3: image notify callback    BYTE FunctionType;    // hooked offset to the callback function begin    BYTE HookOffset;    // absolute hooked address    PVOID HookAddress;    // dump 16 bytes of callback head    BTYE CallbackHeadContent[16];    // where to jump    PVOID JumpAddress;    // content of address after the jump    BYTE HookContent[64];    // up to 260 bytes, no terminator    CHAR ModulePath[0];};
    

    回調地址模塊范圍檢測(report type 7)

    檢測回調地址是否在某個內核模塊的范圍內,如果不在任何一個模塊的地址范圍內,則上報異常。

    數據包結構:

    struct PacketCallbackRangeCheck {    // 7 is callback range check    BYTE PacketType;    // function type:    // 0: process callback    // 1: thread callback    // 2: register callback    // 3: image notify callback    BYTE FunctionType;    // address of the function    PVOID Address;    // 64 bytes content of the callback    BYTE Content[64];};
    

    回調函數信息上報(report type 17)

    所有進程、線程的前置、后置回調,注冊表回調,映像加載回調都會記錄到report list 1,待進一步檢測黑名單特征。

    數據包結構:

    struct PacketCallbackCheck {    // 17 is callback check    BYTE PacketType;    // function type:    // 0: process callback    // 1: thread callback    // 2: register callback    // 3: image notify callback    BYTE FunctionType;    // address of the callback    PVOID Address;    // 64 bytes content of the callback    BYTE Content[64];    // module path if exists, no terminator    CHAR ModulePath[0];};
    

    PhysicalMemory引用檢測(report type 8)

    檢測是否有應用程序引用"\\device\\PhysicalMemory"對象,如果存在則上報異常數據。

    檢測的邏輯如下:

    首先遍歷所有進程,使用MmUnmapViewOfSection解除掉"\\device\\PhysicalMemory"的映射,然后再查看"\\device\\PhysicalMemory" Section對象內部的ControlArea中的NumberOfUserReferences是否為0,如果非0則說明仍存在應用程序對物理內存的引用,因此判定異常,上報異常數據。(用于檢測某種手動創建的"\\device\\PhysicalMemory"對象?)

    數據包結構:

    struct PacketPhysicalMemoryReferenceCheck {    // 8 is physical memory reference check    BYTE PacketType;    // fields in struct _CONTROL_AREA    ULONG64 NumberOfSectionReferences;    ULONG64 NumberOfPfnReferences;    ULONG64 NumberOfMappedViews;    ULONG64 NumberOfUserReferences;};
    

    進程、線程回調功能性檢測(report type 2)

    首先置一個標志位為0,然后嘗試獲得游戲進程句柄,如果回調工作正常,則會將標志位置為1,否則標志位仍為0,從而達到檢測回調是否被通過某些手段摘除,無法正常工作。

    如果回調無法正常工作,則會上報一次異常數據(不會重復上報)。

    數據包結構:

    struct PacketProcessThreadCallbackFunctionalityCheck {    // 2 is process thread callback functionality check    BYTE PacketType;    // probably always true    BOOLEAN Abnormal;};
    

    派遣函數地址檢測(report type 0)

    在BE內核模塊加載時檢查所有系統模塊的派遣函數是否都是自己本模塊內的函數或者是系統模塊(ntoskrnl)的函數。

    在運行中會檢查自身的MJ_IRP_CREATE、MJ_IRP_CLOSE、MJ_IRP_READ、MJ_IRP_WRITE對應的派遣函數是否被修改,如果被修改則會上傳到異常鏈表,否則不會有額外操作。

    數據包結構:

    struct PacketDispatchFunctionIntegrityCheck {    // 0 is dispatch function integrity check    BYTE PacketType;    // driver name    // length = PacketLength - OtherFieldsLength    CHAR DriverName[0];    // major number    BYTE MajorNumber;    // hook function address    PVOID Address;    // 64 bytes of hook function    BYTE Content[64];};
    

    PsLookupThreadByThreadId hook檢測(report type 4)

    該函數在線程回調函數中被調用,該函數會檢測PsLookupThreadByThreadId是否被hook,檢測的hook類型僅是FF 25 jmp,即 jmp [addr] 類型的hook。該函數最多追蹤2次jmp,如果出現hook則會上傳到異常鏈表。

    (會對封包從1到45字節做異或0x7F的加密操作,第一個字節PacketType不進行加密,不知道為什么要這么做)

    數據包結構:

    struct PacketPsLookupThreadByThreadIdHookCheck {    // 4 is PsLookupThreadByThreadId hook check    BYTE PacketType;    // PsLookupThreadByThreadId address    PVOID FunctionAddress;    // FF 25 (4 bytes offset)    ULONG JumpOffset1;    // address after the first jump    PVOID HookFunction1;    // whether there is another jump    BOOLEAN TwoJump;    union {        // no another jump        // dump 16 bytes of the first hook function        BYTE Content1[16];        // have another jump        struct {            // record the second hook function            PVOID HookFunction2;            // dump 16 bytes of the second hook function            BYTE Content2[16];        };    };};
    

    \\FileSystem\\Filters\\FltMgrMsg對象檢測

    \\FileSystem\\Filters\\FltMgrMsg對象涉及到Filter通信,其中有過濾通信的回調函數,因此BE對其進行了檢測。

    可以參考該文章:https://www.amossys.fr/fr/ressources/blog-technique/filter-communication-ports/

    其中有3個callback會被檢測:

    1. ConnectNotifyCallback
    2. DisconnectNotifyCallback
    3. MessageNotifyCallback

    FltMgrMsg對象callback模塊范圍檢測(report type 31)

    檢測回調地址是否在某個內核模塊的范圍內,如果不在任何一個模塊的地址范圍內,則上報異常。

    數據包結構:

    struct PacketFltMgrMsgCallbackRangeCheck {    // 31 is FltMgrMsg callback range check    BYTE PacketType;    // function type:    // 0: ConnectNotifyCallback    // 1: DisconnectNotifyCallback    // 2: MessageNotifyCallback    BYTE FunctionType;    // address of the function    PVOID Address;    // 64 bytes content of the callback    BYTE Content[64];};
    

    FltMgrMsg對象callback hook檢測(report type 32)

    對3個回調函數做hook檢查,方式同回調hook檢測(report type 6),僅report type不同。

    在檢測到hook時會上報異常數據,結構如下:

    struct PacketFltMgrMsgCallbackHookCheck {    // 32 is FltMgrMsg callback hook check    BYTE PacketType;    // function type:    // 0: ConnectNotifyCallback    // 1: DisconnectNotifyCallback    // 2: MessageNotifyCallback    BYTE FunctionType;    // hooked offset to the callback function begin    BYTE HookOffset;    // absolute hooked address    PVOID HookAddress;    // dump 16 bytes of callback head    BTYE CallbackHeadContent[16];    // where to jump    PVOID JumpAddress;    // content of address after the jump    BYTE HookContent[64];    // up to 260 bytes, no terminator    CHAR ModulePath[0];};
    

    Dxgkrnl某內部未導出函數范圍檢測(report type 22)(disabled)

    首先BE會先Hook該函數,然后在Hook函數中對原始函數進行模塊范圍檢測,目前該檢測還不完善,并且在卸載驅動時也沒有對該私有鏈表進行清理,因此懷疑該檢測未開啟。如果檢測到該函數地址不在任何一個模塊內,則會上報異常數據。

    為了避免重復上報,該檢測使用report list 6記錄每個異常上報數據。

    數據包結構:

    struct PacketDxgkrnlInternalFunctionRangeCheck {    // 22 is unknown function range check    BYTE PacketType;    // address of the function    PVOID Address;    // 64 bytes content of the function    BYTE Content[64];};
    

    infinity hook 檢測(report type 23)

    首先檢測系統是否可以進行infinity hook,如果可能進行了infinity hook,則會檢測WmipLoggerContext中每一項的GetCpuClock函數地址,如果該函數地址在模塊地址范圍內,并且該地址所在節的權限為executable + non-paged(從磁盤讀取PE文件進行解析),則判定為正常,否則判定為異常,會上報異常數據。

    為了避免重復上報,該檢測使用report list 5記錄每個異常上報數據用于去重。

    數據包結構:

    struct PacketInfinityHookRangeCheck {    // 23 is infinity hook range check    BYTE PacketType;    // address of the function    PVOID Address;    // 64 bytes content of the function    BYTE Content[64];};
    

    系統模塊檢測

    遍歷內核中加載的所有模塊,對其進行檢測,但是會跳過以下幾個模塊。

    • hal.dll
    • clipsp.sys
    • CI.dll
    • tpm.sys
    • ks.sys
    • cdd.dll
    • TSDDD.dll
    • spsys.sys
    • atikmpag.sys

    在處理win32k模塊時,由于win32k模塊的內存只在csrss中進行了映射,因此需要附加到csrss后再進行檢查。

    模塊異常指令檢測(report type 11)

    由于該檢測模塊較為混亂,因此逆向分析的不是很清楚,懷疑是在尋找模塊中一些int 3、hook的指令,并將指令所在的頁面上傳到異常鏈表。

    其中對dxgkrnl.sys有特殊檢測,懷疑是在檢測gdi hook,原文鏈接:https://secret.club/2019/10/18/kernel_gdi_hook.html

    數據包結構:

    struct PacketModuleAbnormalInstructionCheck {    // 11 is module abnormal instruction check    BYTE PacketType;    // length of the module name, up to 64    BYTE ModuleNameLength;    // length depends on ModuleNameLength    CHAR ModuleName[0];    // offset in page    ULONG OffsetInPage;    // content of the page which contains the abnormal instruction, up to 0x1000 bytes    BYTE Content[0];};
    

    模塊IAT hook檢測(report type 19)

    通過解析各個模塊的內存中的PE結構,檢查是否存在某個IAT項的函數地址不在任何一個模塊范圍內,如果是則會上報異常數據。

    數據包結構:

    struct PacketModuleIATHookCheck {    // 19 is module IAT hook check    BYTE PacketType;    // module name    // no length is recorded yet !    CHAR ModuleName[0];    // function index in the IAT    ULONG FunctionIndex;    // offset of the function IAT entry to the module base    ULONG EntryOffset;    // function in the IAT entry    PVOID Function;    // content of the function    BYTE Content[64];};
    

    隱藏驅動檢測(report type 15)

    通過遍歷\\Device目錄,得到所有Device類型的對象,然后遍歷\\Driver和\\FileSystem目錄,得到所有Driver對象,對每個Device對象找到其內部存儲的Driver指針,然后逐一匹配剛才遍歷得到的Driver對象,如果沒有任何一個Driver對象與其匹配,則判定該Device對應的驅動被隱藏了,會上報異常數據。

    數據包結構:

    struct PacketHiddenDriverCheck {    // 15 is hidden driver check    BYTE PacketType;    // length of the device name    BYTE DeviceNameLength;    // name of the device whose driver is hidden    CHAR DeviceName[0];    // driver name of the hidden driver    // length = PacketLength - OtherFieldsLength    CHAR DriverName[0];};
    

    PCI設備檢測(report type 26)

    通過I/O指令遍歷PCI設備樹,尋找具有指定特征的PCI設備,懷疑是檢測DMA作弊工具。如果找到具有指定特征的PCI設備,則會上報異常數據。

    PCI設備檢測實現參考源碼:https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/blob/master/src/x86_pci.c

    UC上也有人提到過該檢測:https://www.unknowncheats.me/forum/anti-cheat-bypass/304545-detecting-dma-hardware-cheats-12.html

    (注意:在第二種上報類型中,Info中的Dev貌似被BE的開發者誤寫成了Bus,導致記錄了兩次Bus而沒有記錄Dev,笑)

    數據包結構:

    struct PacketHiddenDriverCheckType1 {    // 26 is pci device check    BYTE PacketType;    // PCI enumeration info    struct {        BYTE Bus;        BYTE Dev;        BYTE Func;    } Info;    // 4 bytes read from reg VENDOR_ID (0x0)    ULONG VendorId;    // 4 bytes read from reg PCI_CLASS (0x08)    ULONG PciClass;    // 1 byte read from reg HDRTYPE (0x0E)    BYTE HdrType;    // 4 bytes read from reg PCI_SUB_VENDOR_ID (0x2C)    ULONG SubVendorId;};
    struct PacketHiddenDriverCheckType2 {    // 26 is pci device check    BYTE PacketType;    // PCI enumeration info    struct {        BYTE Bus;        BYTE Dev;        BYTE Func;    } Info;    // 256 bytes read from reg VENDOR_ID (0x0)    BYTE VendorId[256];};
    

    Win32k函數指針表檢測

    gDxgkInterface和gDxgkWin32kEngInterface是存儲在Win32k中的兩張函數表,作用類似于SSDT,IChooseYou曾將其用于無模塊驅動的通信,https://www.unknowncheats.me/forum/anti-cheat-bypass/335585-communicating-mapped-driver-using.html,故BE對其進行檢測。

    由于win32k僅在csrss模塊的地址空間中進行了映射,因此在檢測時需要附加到csrss進程。

    gDxgkInterface 地址范圍檢測(report type 20)

    對gDxgkInterface 表中的絕大部分函數進行地址范圍檢測(跳過前兩個函數),檢測其地址是否在win32k模塊范圍內。

    數據包結構:

    struct PacketWin32kRangeCheckType1 {    // 20 is win32k gDxgkInterface range check    BYTE PacketType;    // function index in the gDxgkInterface table    ULONG Index;    // function address    PVOID Function;    // 64 bytes of the function    BYTE Content[64];};
    

    gDxgkInterface hook檢測(report type 21)

    對上述函數進行hook檢測,檢測方式同回調Hook檢測(report type 6),僅report type不同。FunctionType值為函數在表中的下標。

    gDxgkWin32kEngInterface 地址范圍檢測(report type 24)

    對gDxgkWin32kEngInterface表中的所有函數進行地址范圍檢測,檢測其地址是否在win32k模塊范圍內。

    數據包結構:

    struct PacketWin32kRangeCheckType2 {    // 20 is win32k gDxgkWin32kEngInterface range check    BYTE PacketType;    // function index in the gDxgkWin32kEngInterface table    ULONG Index;    // function address    PVOID Function;    // 64 bytes of the function    BYTE Content[64];};
    

    gDxgkWin32kEngInterface hook檢測(report type 25)

    對上述函數進行hook檢測,檢測方式同回調Hook檢測(report type 6),僅report type不同。FunctionType值為函數在表中的下標。

    ext_ms_win_core_win32k_full_export_l1 地址范圍檢測(report type 33)

    該表未導出,因此BE通過特征碼定位的方式獲得該表,通過BRUSHOBJ_hGetColorTransform函數進行定位,在該函數中搜索如下特征碼,addr1即為ext_ms_win_core_win32k_full_export_l1:

    mov rax, [addr1]test rax, raxje addr2call qword ptr [addr3]
    

    對該表中的函數逐個檢測地址,查看其是否在win32k和win32kfull模塊的范圍內,如果不在則會上報異常數據。

    數據包結構:

    struct PacketWin32kRangeCheckType3 {    // 33 is win32k ext_ms_win_core_win32k_full_export_l1 range check    BYTE PacketType;    // function index in the ext_ms_win_core_win32k_full_export_l1 table    ULONG Index;    // function address    PVOID Function;    // 64 bytes of the function    BYTE Content[64];};
    

    Dxgkrnl 函數指針表檢測

    DxgCoreInterface是Dxgkrnl模塊中的一張函數表。可能曾被用作無模塊通信/繪制,或者僅是預防性檢查。

    DxgCoreInterface 地址范圍檢測(report type 12)

    對DxgCoreInterface表中的所有函數進行地址范圍檢測,檢測其地址是否在win32k模塊范圍內。

    數據包結構:

    struct PacketDxgkrnlRangeCheck {    // 12 is Dxgkrnl DxgCoreInterface range check    BYTE PacketType;    // function index in the DxgCoreInterface table    ULONG Index;    // function address    PVOID Function;    // 64 bytes of the function    BYTE Content[64];};
    

    DxgCoreInterface hook檢測(report type 13)

    對上述函數進行hook檢測,檢測方式同回調Hook檢測(report type 6),僅report type不同。

    HAL 函數指針表檢測

    這是https://www.unknowncheats.me/forum/anti-cheat-bypass/335585-communicating-mapped-driver-using.html這篇文章中提到的另一種通信方式,具體的實現方式是hook HalDispatchTable中的函數,因此BE對該表進行檢測。除此之外,BE還發現HalPrivateDispatchTable也可以被hook,因此又額外加入了對該表的檢測。

    HalDispatchTable 地址范圍檢測(report type 27)

    對HalDispatchTable 表中的所有函數進行地址范圍檢測,檢測其地址是否在ntoskrnl、hal等系統模塊范圍內。

    數據包結構:

    struct PacketHalDispatchTableRangeCheck {    // 27 is HalDispatchTable range check    BYTE PacketType;    // function index in the HalDispatchTable table    ULONG Index;    // function address    PVOID Function;    // 64 bytes of the function    BYTE Content[64];};
    

    HalDispatchTable hook檢測(report type 28)

    對上述函數進行hook檢測,檢測方式同回調Hook檢測(report type 6),僅report type不同。FunctionType值為函數在表中的下標。

    HalPrivateDispatchTable 地址范圍檢測(report type 29)

    對HalPrivateDispatchTable 表中的所有函數進行地址范圍檢測,檢測其地址是否在ntoskrnl、hal等系統模塊范圍內。

    數據包結構:

    struct PacketHalPrivateDispatchTableRangeCheck {    // 29 is HalPrivateDispatchTable range check    BYTE PacketType;    // function index in the HalPrivateDispatchTable table    ULONG Index;    // function address    PVOID Function;    // 64 bytes of the function    BYTE Content[64];};
    

    HalPrivateDispatchTable hook檢測(report type 30)

    對上述函數進行hook檢測,檢測方式同回調Hook檢測(report type 6),僅report type不同。FunctionType值為函數在表中的下標。

    派遣函數 hook檢測(report type 5)

    在BE加載時,會對系統內的所有模塊進行掃描,對每個驅動的每個派遣函數進行掃描,檢測是否存在hook。

    只會檢測頭部64個字節以內的hook(僅以下兩種形式),并且只會跟蹤一次跳轉,不會跟蹤多次跳轉。

    • jmp [addr]
    • mov rax, imm
    • jmp rax

    數據包結構:

    struct PacketDispatchFunctionHookCheck {    // 5 is dispatch function hook check    BYTE PacketType;    // major number    BYTE MajorNumber;    // offset of the hook instructions to the function begin    BYTE HookOffset;    // address of the hook instructions    PVOID HookAddress;    // 16 bytes of the hook instructions    BYTE HookInstructions[16];    // hook function    PVOID HookFunction;    // 64 bytes of the hook function    BYTE Content[64];    // driver name read from the driver object (DriverObject->DriverName)    CHAR DriverName[0];};
    

    驅動句柄打開失敗(report type 10)

    嘗試打開\\Driver,\\FileSystem目錄下的Driver對象,如果通過ObOpenObjectByName打開失敗,則會上報異常數據。

    數據包結構:

    struct PacketOpenDriverObjectFailedCheck {    // 10 is open driver object failed check    BYTE PacketType;    // eg:\\Driver\\xxx or \\FileSystem\\xxx    CHAR DriverName[0];    // ObOpenObjectByName status    NTSTATUS Status;};
    

    游戲進程線程創建檢測(report type 3)

    通過線程創建回調監視游戲內創建線程的操作,如果線程啟動地址不在任何一個游戲模塊內,則判定為異常,上報異常數據。猜測該檢測主要用來檢測DLL注入。

    數據包結構:

    struct PacketGameThreadCreateCheck {    // 3 is thread create check    BYTE PacketType;    // start address of the thread being created    PVOID StartAddress;};
    

    總結

    1、在所有hook檢測中只檢測了頭部的64字節,因此中部hook或者尾部hook通常可以更好的繞過檢測,并且不要使用過于常規的hook無條件跳轉(jmp [addr] / mov rax, imm jmp rax),請盡情發揮你的想象

    2、BE內核驅動會維護內部的進程、驅動、模塊等鏈表,因此如果使用簡單的斷鏈是沒有用的,并且如果隱藏的不好,出現了數據的不一致性,“隱藏”這一行為也會被當做異常數據上報

    3、由于win32k、dxgkrnl等驅動可以用于無模塊通信、繪制等用途,并且不受Patch Guard管控,因此BE對其進行了額外的完整性檢查

    4、通過kdmapper等工具加載的驅動是重點關注對象,BE內核驅動會檢查各種函數是否是無模塊地址、并且系統線程的起始地址、堆棧也會被檢查

    相關工作

    1、BattlEye去虛擬化內核模塊

    https://www.unknowncheats.me/forum/anti-cheat-bypass/489381-bedaisy-sys-devirtualized.html

    這個帖子給出了一個使用VTIL脫掉VMP殼的BE內核模塊,本次逆向工作就是在這個帖子的基礎之上完成的。

    2、NoVmp

    https://github.com/can1357/NoVmp

    使用VTIL作為內核,實現了給VMP3脫殼。(但是用在最新版的BE驅動上會崩潰)

    3、BE內核驅動逆向

    https://github.com/dllcrt0/bedaisy-reversal

    這個人也做了個開源的BE內核驅動的逆向,但是細節稍有些粗糙,并且不全。

    4、BE shellcode

    https://github.com/weak1337/BE-Shellcode

    這個人做了對BE應用層的一些shellcode的分析,質量很高。

    其他

    附件是逆向后的文件,感興趣的可以看一看這些檢測具體是怎么實現的。如果發現我哪里分析的有問題,歡迎指出錯誤。

    offset函數callback
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    假如想在x86平臺運行arm程序,稱arm為source ISA, 而x86為target ISA, 在虛擬化的角度來說arm就是Guest, x86為Host。這種問題被稱為Code-Discovery Problem。每個體系結構對應的helper函數在target/xxx/helper.h頭文件中定義。
    前言1.漏洞描述在win32k!xxxMNEndMenuState函數中,函數會調用MNFreePopup函數釋放tagPOPUPMENU對象,但是函數釋放對象以后,沒有清空指針。而在彈出窗口過程中,用戶可以劫持相應的處理函數來實現兩次調用xxxMNEndMenuState函數,因為雙重釋放導致BSOD的產生。通過內存布局,可以偽裝tagPOPUPMENU對象在釋放的內存空間中,通過解引用修改窗口對象的關鍵的標志位,可以通過SendMessage函數讓窗口在內核態執行指定的處理函數實現提權操作。
    大廠基本為了程序的安全,會使用大量內聯SVC去調用系統函數,以此來保護程序的安全。如何實現SVC指令的IO重定向,成為最大的問題。內核態是當Linux需要處理文件,或者進行中斷IO等操作的時候就會進入內核態。當arm系列cpu發現svc指令的時候,就會陷入中斷,簡稱0x80中斷。
    簡介實驗環境是Win7 X86系統。曾經在這篇文章中常見的幾種DLL注入技術說過,通過修改注冊表的內容可以實現AppInit_DLLs注入。那么本文的實驗是通過CmRegisterCallback來實現對注冊表修改的監控以此來阻止修改。并通過對CmRegisterCallback的逆向分析來實現對監控函數的刪除。
    前言筆者于五月份時遇到幾個經控制流平坦化的樣本,由于之前沒有接觸過這方面知識,未深入分析。七月初看到一篇
    本系列將以官網資料為基礎主要通過動態跟蹤來解析DynamoRIO的源代碼。因為如果不結合實例只是將各函數的作用寫出來,實在無法很好的說明問題,我們將以代碼覆蓋工具drcov為例,分析DynamoRIO的執行流程。
    BEServer - BattlEye服務器,收集上傳的信息,并判定作弊行為。本次分析的是BEDaisy,也就是BE內核驅動中的各種檢測。上傳的內容主要是一些黑名單特征,這些特征應該是從服務器下發的,因此可以在不重新編譯驅動的情況下,動態調整檢測的特征。沒有給定偏移量的特征,BE在檢測時會按子串匹配的方式嘗試所有位置進行匹配。
    源碼分析1、LLVM編譯器簡介LLVM 命名最早源自于底層虛擬機的縮寫,由于命名帶來的混亂,LLVM就是該項目的全稱。LLVM 核心庫提供了與編譯器相關的支持,可以作為多種語言編譯器的后臺來使用。自那時以來,已經成長為LLVM的主干項目,由不同的子項目組成,其中許多是正在生產中使用的各種 商業和開源的項目,以及被廣泛用于學術研究。
    PHP disable_functions disable_functions是php.ini中的一個設置選項。相當一個黑名單,可以用來設置PHP環境禁止使用某些函數,通常是網站管理員為了安全起見,用來禁用某些危險的命令執行函數等。
    disable_functions是php.ini中的一個設置選項。相當一個黑名單,可以用來設置PHP環境禁止使用某些函數,通常是網站管理員為了安全起見,用來禁用某些危險的命令執行函數等。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类