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

    系統調用(R3API調用過程詳解)

    VSole2021-10-20 14:18:04

    WindowsAPI

    • API(Application Programming Interface),我們調用時只需提供正確的參數以及接收返回值就可以判斷API執行是否成功或者通過GetLastError獲得錯誤原因.
    • 大部分API在R3都是處理各種校驗,真正執行功能都是在R0(并不是所有的API都是在R0處理).
    • 系統中幾個核心DLL(Kernel32.dll,User32.dll,GDI32.dll,Ntdll.dll(大多數API通過此DLL進入內核)).
    • 通過API ReadProcessMemory / OpenProcess 分析函數從R3進入R0過程,進入R0如何處理原有寄存器數據,傳遞參數,找到對應內核函數并調用,以及從R0返回R3過程.
    • 前置知識點(匯編,C,Win32,段頁機制,段描述符,中斷門,).
    • 涉及知識點(_KUSER_SHARED_DATA,_KTRAP_FRAME,_KPCR,_KPRCB,_KTHREAD,KiFastSystemCall→KiFastCallEntry,KiIntSystemCall→KiSystemService,SSDT)下文詳解.
    • 代碼示例(重寫R3API,SSDTHOOK,內核重載).

    R3API調用分析

    代碼示例:

     復制代碼 隱藏代碼
    #include 
    #include 
    int main()
    {
      //隨便選擇一個進程
      HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 2252);
      //0x400000大部分情況下為ImageBase
      DWORD dwData = 0;
      ReadProcessMemory(hProcess, (PVOID)0x400000, &dwData, 4, NULL);
      return 0;
    }
    

    1).將編譯好的文件拖入DBG / OD 分析(定位MAIN函數找到API調用位置)

    2).OpenProcess執行流程分析

    OpenProcess執行流程:進程模塊內CALLAPI(OpenProcess) -> kernel32.dll(OpenProcess) -> kernelBase.dll(OpenProcess) -> ntdll.dll(ZwOpenProcess) -> ntdll.dll中執行會進入R0后文詳解.

    3).ReadProcessMemory執行流程分析

    ReadProcessMemory執行流程:進程模塊內CALLAPI(ReadProcessMemory) -> kernel32.dll(ReadProcessMemory) -> kernelBase.dll(ReadProcessMemory) -> ntdll.dll(ZwReadVirtualMemory) -> ntdll.dll中執行會進入R0后文詳解.

    R3API功能實現分析

    1).ReadProcessMemory分析(R3功能實現分析)

    1).通過IDA導入KernelBase.dll,查詢ReadProcessMemory函數,如下圖:

    分析得出ReadProcessMemory函數并未做任何處理而是調用ntdll.dll中NtReadVirtualMemory

    2).通過IDA導入Ntdll.dll,查詢NtReadVirtualMemory函數,如下圖:

    后文詳解此處...

    2).OpenProcess分析(R3功能實現分析)

    1).通過IDA導入KernelBase.dll,查詢OpenProcess函數,如下圖:

    分析得出OpenProcess函數并未做任何功能實現,而是在原有參數基礎上填充內核需要結構體信息后調用NtOpenProcess

    2).通過IDA導入Ntdll.dll,查詢NtOpenProcess函數,如下圖:

    這兩個函數最終都執行到ntdll.dll中并且除了eax值不相同其余都一樣.

    edx = 7FFE0300h

    call [edx]

    這里只需要分析edx指向地址7FFE0300h中的值即可.

    這里我們需要了解一個結構體_KUSER_SHARED_DATA

    _KUSER_SHARED_DATA

    1)._KUSER_SHARED_DATA

    • 用戶層和內核層分別定義了一個_KUSER_SHARED_DATA結構體,用于在用戶層和內核層共享數據,其大小為4KB(測試環境Win7 x86 這塊結構系統默認用了0x5ff,意味著結構體 + 0x600 ~ 0xFFF可以構建自己的共享數據).
    • 頁的知識可以知道共享數據是用戶層和內核層_KUSER_SHARED_DATA結構體對應線性地址指向同一個物理頁,但在用戶層中這塊內存是只讀的,內核層中是可讀可寫的.
    • 用戶層和內核層使用固定的地址映射_KUSER_SHARED_DATA結構體,地址如下表所示:

    內核起始地址內核結束地址用戶起始地址用戶結束地址

    x860xFFDF00000xFFDF0FFF0x7FFE00000x7FFE0FFFx640xFFFFF78000000000|0xFFFFF78000000FFF0x7FFE00000x7FFE0FFF

    • _KUSER_SHARED_DATA共享論證.

    測試環境Win7 x86

    1).Windbg輸入指令 !process 0 0 找一個進程附加

    2).Windbg輸入指令 .process /i xxxxxxxx

    此時Windbg處于Dbgview進程空間中.

    3).Windbg輸入指令 !pte 用戶層以及內核層_KUSER_SHARED_DATA結構體對應線性地址

    4).修改用戶層結構數據查看內核層對應數據

    2)._KUSER_SHARED_DATA.SystemCall

     復制代碼 隱藏代碼
    (Windbg輸入指令 dt _KUSER_SHARED_DATA)
    nt!_KUSER_SHARED_DATA
       +0x300 SystemCall       : Uint4B //系統調用
       +0x304 SystemCallReturn : Uint4B //調用返回
    

    R3API如果通過 MOV EDX, 7FFE0300h; CALL DWORD PTR [edx];方式進R0,實際上相當于調用_KUSER_SHARED_DATA.SystemCall中的存儲的值.

    _KUSER_SHARED_DATA.SystemCall中存儲的值決定了函數通過什么方式進R0.(操作系統通過檢查當前CPU是否支持快速調用來填充這個值,支持函數地址為KiFastSystemCall快速調用,不支持函數地址為KiIntSystemCall中斷調用).

    CPU是否支持快速調用?

    當EAX = 1 執行CPUID指令 如果EDX第11位(SEP) = 1 說明支持快速調用,否則為中斷調用,即_KUSER_SHARED_DATA.SystemCall中存儲的值.

    至此已經了解到R3進入R0兩種方式,接下來分析中斷調用,快速調用如何進入R0.

    代碼示例:

     復制代碼 隱藏代碼
    #include 
    #include 
    int main()
    {
      DWORD dwEAX = 0;
      DWORD dwECX = 0;
      DWORD dwEDX = 0;
      __asm
      {
        xor eax, eax
        mov eax, 1
        CPUID
        mov dwEAX, eax
        mov dwECX, eax
        mov dwEDX, edx
      }
      printf("EAX 0x%08x ",dwEAX);
      printf("ECX 0x%08x ",dwECX);
      printf("EDX 0x%08x ",dwEDX);
      printf("EDX 11(BIT) [%d] ", (dwEDX & 0x800) >> 11);
      system("pause");
      return 0;
    }
    

    intel白皮書介紹如下:

    系統調用

    1).中斷調用KiIntSystemCall

    固定中斷號為: 2Eh 通過解析如下圖:

    2).快速調用KiFastSystemCall

    如果CPU支持sysenter(快速調用)指令,操作系統會提前將CS/ESP/EIP的值存儲在MSR寄存器中,sysenter指令執行時,CPU會將MSR寄存器中的值寫入相關寄存器(沒有查詢內存過程速度更快).

    MSR地址IA32_SYSENTER_CS174HIA32_SYSENTER_ESP175HIA32_SYSENTER_EIP176H

    intel白皮書對SYSENTER介紹如下:

    代碼示例(特權指令需要在R0下運行)

     復制代碼 隱藏代碼
    #include <ntifs.h>
    NTSTATUS DriverUnload(PDRIVER_OBJECT pDriver)
    {
      DbgPrint("Driver Exit \r");
    }
    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
    {
      DbgPrint("Driver Load \r");
      pDriver->DriverUnload = DriverUnload;
      DbgBreakPoint();
      ULONGLONG uData = 0;
      __asm
      {
        mov ecx, 0x174          //相當于msr寄存器index
        rdmsr
        mov dword ptr [uData], eax    //eax存儲數據低32位
        mov dword ptr [uData + 4],edx  //edx存儲數據高32位
      }
      DbgPrint("MSR[174] -> [0x%llx] \r", uData);
      return STATUS_SUCCESS;
    }
    

    3).中斷調用快速調用區別如下

    快速調用中斷調用

    R3執行APIKiFastSystemCallKiIntSystemCall

    8bd4        mov    edx,esp //三環棧頂 系統調用號在EAX

    0f34        sysenter8d542408      lea    edx,[esp+8] //參數指針 系統調用號在EAX

    cd2e          int    2Eh

    c3              ret提權方式(段的機制R3進入R0相當于CPL發生改變 )如果CPU支持sysenter指令,操作系統會提前將CS/ESP/EIP的值存儲在MSR寄存器中,sysenter指令執行時,CPU會將MSR寄存器中的值寫入相關寄存器(沒有查詢內存過程速度更快).將特權級切換到R0,如果EFLAG.VM被置位,則清除該標志位.int    2Eh對應段描述符為83e4ee00-00083fee中斷門描述符,其中加載代碼段選擇子為0x0008 對應段描述符為00cf9(1001)b00-0000ffff DPL = 0執行成功后CPL = 0.且因權限切換會向堆棧壓入SS,ESP,EFLAG,CS,EIP.提權方式(段的機制R3進入R0相當于CPL發生改變 )查詢MSR寄存器.CS = rdmsr 174.SS = CS + 8(數值上).ESP = rdmsr 175.EIP = rdmsr 176.ESP,SS由TSS提供.CS由中斷門描述符中低4字節高16位提供.EIP由中斷門描述符中高4字節高16位與低4字節低16位組成.進入R0執行APIKiFastCallEntry(Windbg輸入 rdmsr 176獲取)KiSystemService(Windbg輸入!IDT 2E獲取)

    至此已經知道R3API在ntdll.dll中進入R0的兩種方法.

    在分析對應內核函數前需要了解兩個結構體_KTRAP_FRAME,_KPCR.

    _KTRAP_FRAME

    Windbg輸入dt _KTRAP_FRAME指令:

    _KTRAP_FRAME結構如下:

     復制代碼 隱藏代碼
    nt!_KTRAP_FRAME //類似于R3 -> CONTEXT
       +0x000 DbgEbp           : Uint4B
       +0x004 DbgEip           : Uint4B
       +0x008 DbgArgMark       : Uint4B
       +0x00c DbgArgPointer    : Uint4B
       +0x010 TempSegCs        : Uint2B
       +0x012 Logging          : UChar
       +0x013 Reserved         : UChar
       +0x014 TempEsp          : Uint4B
       +0x018 Dr0              : Uint4B
       +0x01c Dr1              : Uint4B
       +0x020 Dr2              : Uint4B
       +0x024 Dr3              : Uint4B
       +0x028 Dr6              : Uint4B
       +0x02c Dr7              : Uint4B
       +0x030 SegGs            : Uint4B
       +0x034 SegEs            : Uint4B
       +0x038 SegDs            : Uint4B
       +0x03c Edx              : Uint4B
       +0x040 Ecx              : Uint4B
       +0x044 Eax              : Uint4B
       +0x048 PreviousPreviousMode : Uint4B
       +0x04c ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
       +0x050 SegFs            : Uint4B
       +0x054 Edi              : Uint4B
       +0x058 Esi              : Uint4B
       +0x05c Ebx              : Uint4B
       +0x060 Ebp              : Uint4B
       +0x064 ErrCode          : Uint4B //如果是發生錯誤導致其他中斷觸發時,這里會有ErrCode,中斷調用進內核函數KiSystemService這里push 0.
       +0x068 Eip              : Uint4B
       +0x06c SegCs            : Uint4B
       +0x070 EFlags           : Uint4B
       +0x074 HardwareEsp      : Uint4B
       +0x078 HardwareSegSs    : Uint4B
       +0x07c V86Es            : Uint4B //0x07c ~ 0x088位置為虛擬8086模式下使用,函數進入R0時棧頂默認指向_KTRAP_FRAME.V86Es
       +0x080 V86Ds            : Uint4B
       +0x084 V86Fs            : Uint4B
       +0x088 V86Gs            : Uint4B
    

    0x07c ~ 0x088位置為虛擬8086模式下使用.

    中斷調用進入R0時棧頂(ESP由TSS提供(每個線程進入R0時ESP都由TSS.ESP0提供,以及TSS里存儲的ESP0一直是當前線程進入R0時對應ESP0,線程切換時會更新TSS里存儲的ESP0))默認指向_KTRAP_FRAME.V86Es,中斷門執行權限發生切換時會向堆棧壓入SS,ESP,EFLAG,CS,RETADDR(EIP),由此得知當執行函數KiIntSystemCall進入R0函數KiSystemService時此時ESP指向_KTRAP_FRAME.Eip.(下文分析).

    快速調用進入R0時堆棧是由MSR[175]提供的,KiFastCallEntry函數執行時首先會修改FS指向_KPCR結構,通過_KPCR -> _TSS定位到當前線程ESP0,并切換新的堆棧.此時ESP指向_KTRAP_FRAME.V86Ds.(下文分析).

    _KPCR

    一個核一個_KPCR(Processor Control Region CPU控制塊)記錄當前CPU核對應各種狀態以及上下文環境.

     復制代碼 隱藏代碼
    查看CPU數量
    kd> dd KeNumberProcessors               
    83fb796c  00000001                 //一個核心           
    查看KPCR 
    kd> dd KiProcessorBlock          //幾個核對應幾個KPCR 
    83fb78c0  83f78d20 00000000      //減去120(kpcr的大小)                
    kd> dt _KPCR 83f78d20-120        //就是kpcr的地址   
     復制代碼 隱藏代碼
    nt!_KPCR
       +0x000 NtTib            : _NT_TIB
           nt!_NT_TIB
           +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
           +0x004 StackBase        : Ptr32 Void
           +0x008 StackLimit       : Ptr32 Void
           +0x00c SubSystemTib     : Ptr32 Void
           +0x010 FiberData        : Ptr32 Void
           +0x010 Version          : Uint4B
           +0x014 ArbitraryUserPointer : Ptr32 Void
           +0x018 Self             : Ptr32 _NT_TIB   //結構體指針 指向自己
       +0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
       +0x004 Used_StackBase   : Ptr32 Void
       +0x008 Spare2           : Ptr32 Void
       +0x00c TssCopy          : Ptr32 Void
       +0x010 ContextSwitches  : Uint4B
       +0x014 SetMemberCopy    : Uint4B
       +0x018 Used_Self        : Ptr32 Void
       +0x01c SelfPcr          : Ptr32 _KPCR
       +0x020 Prcb             : Ptr32 _KPRCB
       +0x024 Irql             : UChar
       +0x028 IRR              : Uint4B
       +0x02c IrrActive        : Uint4B
       +0x030 IDR              : Uint4B
       +0x034 KdVersionBlock   : Ptr32 Void   
       +0x038 IDT              : Ptr32 _KIDTENTRY
       +0x03c GDT              : Ptr32 _KGDTENTRY
       +0x040 TSS              : Ptr32 _KTSS
       +0x044 MajorVersion     : Uint2B
       +0x046 MinorVersion     : Uint2B
       +0x048 SetMember        : Uint4B
       +0x04c StallScaleFactor : Uint4B
       +0x050 SpareUnused      : UChar
       +0x051 Number           : UChar
       +0x052 Spare0           : UChar
       +0x053 SecondLevelCacheAssociativity : UChar
       +0x054 VdmAlert         : Uint4B
       +0x058 KernelReserved   : [14] Uint4B
       +0x090 SecondLevelCacheSize : Uint4B
       +0x094 HalReserved      : [16] Uint4B
       +0x0d4 InterruptMode    : Uint4B
       +0x0d8 Spare1           : UChar
       +0x0dc KernelReserved2  : [17] Uint4B
       +0x120 PrcbData         : _KPRCB
    

    KiSystemService(函數分析)

    KiIntSystemCall(R3) -> KiSystemService(R0)

    設置環境

    KiSystemService設置環境后跳轉KiFastCallEntry(詳情見下文)

    KiFastCallEntry(函數分析)

    KiFastSystemCall(R3) -> KiFastCallEntry(R0)

    設置環境

    尋找內核函數地址,拷貝參數

    此時需要了解一個結構SystemServiceTable

    結構如下:

     復制代碼 隱藏代碼
    定位SystemServiceTable
    _KTHREAD -> ServiceTable 
    系統服務表有兩張:
    1.ntoskrnl.exe導出的常用系統服務.
    2.Win32k.sys導出的與圖形顯示和用戶界面相關的系統服務(只有GDI相關線程訪問對應系統服務表才會有值).
    系統服務表結構如下:
    typedef struct _KSERVICE_TABLE_DESCRIPTOR
    {
      KSYSTEM_SERVICE_TABLE ntoskrnl;    // 內核函數
      KSYSTEM_SERVICE_TABLE win32k;      // win32k.sys 函數
      KSYSTEM_SERVICE_TABLE unUsed1;     // 未使用
      KSYSTEM_SERVICE_TABLE unUsed2;     // 未使用
    } KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;
    typedef struct _KSYSTEM_SERVICE_TABLE
    {
      PULONG ServiceTableBase;       // 函數地址表基址
      PULONG ServiceCounterTableBase;// 函數被調用的次數
      ULONG NumberOfService;         // 函數個數
      PULONG ParamTableBase;         // 函數參數表基址
    } KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;
    ServiceTable指向函數地址表每個成員大小為4字節,存儲函數地址.
    ServiceLimit存儲函數地址表的成員個數.
    ArgumentTable 函數參數表每個成員大小為1字節,存儲函數參數個數(存儲值 / 4 = 參數個數).
    在快速調用和中斷調用R3函數執行時,EAX存儲了系統服務號.
    通過第12位確定是哪張表.
    通過低12位確定在函數地址表中的索引值以及函數參數表的索引值.
    

    System Services Descriptor Table系統服務描述符表,為導出結構KeServiceDescriptorTable(代碼中只需聲明即可直接使用).

    查找ReadProcessMemory(測試環境系統服務號為115h)對應內核函數地址以及參數

    Windbg查看SSDT

    dd KeServiceDescriptorTable

    繼續函數分析

    至此完成了初始化內核環境以及參數拷貝,函數調用.

    函數返回

    涉及到APC,此部分會更新到APC處,大致流程為執行完畢后首先判斷當前IRQL等級(不為0跳轉處理藍屏),然后判斷是否為虛擬8086模式,接著判斷有沒有APC需要處理等.最后通過iretd返回.

    涉及到的結構體如下圖:

    Win7 x86系統調用全過程...

    重寫R3API

    • WindowsAPI分析中可以判斷出大部分API在R3都未做真正功能實現,只是完成一些內核所需結構數據填充,數據校驗等等.
    • 重寫R3API,需對接快速調用/中短調用堆棧要求,以及對應內核函數所需數據就可以實現(可避免惡意掛鉤,R3層的API監控等).

    1).快速調用方式重寫

    通過WindowsAPI分析,可以得知除了R3API業務實現內還需要注意EDX進入內核前指向棧頂,EAX存儲系統服務號.

     復制代碼 隱藏代碼
    #include 
    #include 
    BOOL MyReadMemory(
        HANDLE  hProcess,
        LPCVOID lpBaseAddress,
        LPVOID  lpBuffer,
        SIZE_T  nSize,
        SIZE_T* lpNumberOfBytesRead
    )
    {
        BOOL bRet = 0;
        __asm
        {
            //Kernelbase.dll -> ReadProcessMemory 
            lea eax, nSize
            push eax
            push nSize
            push lpBuffer
            push lpBaseAddress
            push hProcess
            //ntdll.dll -> NtReadVirtualMemory 
            //模擬call 棧頂-4
            sub esp, 4
            //系統服務號
            mov eax, 0x115
            //KiFastSystemCall 
            //模擬call 堆棧保存返回地址
            PUSH RETADDR 
            //快速調用
            mov edx, esp
            //sysenter對應硬編碼
            _emit 0x0F;
            _emit 0x34;
        RETADDR:
            add esp, 0x18
            mov bRet, eax
        }
        return bRet;
    }
    int main()
    {
        DWORD dwData = 0;
        HANDLE handle = 0;
        handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 2252);
        MyReadMemory(handle, (LPVOID)0x400000, &dwData, 4, NULL);
        printf("dwData [0x%08x] ", dwData);
        system("pause");
        return 0;
    }
    

    2).中斷調用方式重寫

     復制代碼 隱藏代碼
    #include 
    #include 
    BOOL MyReadMemory(
        HANDLE  hProcess,
        LPCVOID lpBaseAddress,
        LPVOID  lpBuffer,
        SIZE_T  nSize,
        SIZE_T* lpNumberOfBytesRead
    )
    {
        BOOL bRet = 0;
        __asm
        {
            //系統服務號
            mov eax, 0x115
            //首參數指針
            lea edx, hProcess
            //中斷調用固定號
            int 0x2E
        }
        return bRet;
    }
    int main()
    {
        DWORD dwData = 0;
        HANDLE handle = 0;
        handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 2252);
        MyReadMemory(handle, (LPVOID)0x400000, &dwData, 4, NULL);
        printf("dwData [0x%08x] ", dwData);
        system("pause");
        return 0;
    }
    

    3).動態重寫

    • 上述兩種方式中系統服務號都是寫死的不方便項目中使用,下述代碼演示動態獲取系統服務號并調用函數.
     復制代碼 隱藏代碼
    #include 
    #include 
    #include  
    typedef NTSTATUS(WINAPI* ZwOpenProcessProc)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, CLIENT_ID* ClientId);
    int main()
    {
        //獲取ZwOpenProcess函數地址
        HMODULE hModule = LoadLibraryA("ntdll.dll");
        PUCHAR pFunAddr = (PUCHAR)GetProcAddress(hModule, "ZwOpenProcess");
        printf("Funaddr -> [0x%08x] \r", pFunAddr);
        //獲取ZwOpenProcess函數長度
        ULONG uSize = 0;
        for (int i = 0; i < 100; i++)
        {
            //C2 == ret 
            if (pFunAddr[i] == 0xc2)
            {
                uSize = i + 2;
                break;
            }
        }
        printf("FunLength -> [0x%08x] \r", uSize);
        //函數指針申請內存
        ZwOpenProcessProc func = (ZwOpenProcessProc)VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        //拷貝默認函數數據
        memcpy(func, pFunAddr, uSize);
        //分析得知需要打開進程需要填充CLIENT_ID.UniqueProcess
        CLIENT_ID Client = { 0 };
        Client.UniqueProcess = (HANDLE)2252;
        //分析得知需要填充POBJECT_ATTRIBUTES.Length
        OBJECT_ATTRIBUTES Obj_Attr = { 0 };
        Obj_Attr.Length = sizeof(OBJECT_ATTRIBUTES);
        HANDLE hProcess = NULL;
        NTSTATUS ntstatus = func(&hProcess, PROCESS_ALL_ACCESS, &Obj_Attr, &Client);
        printf("Ret -> [%x]  hProcess -> [%x] \r", ntstatus, hProcess);
        system("Pause");
        return 0;
    }
    

    SSDT_HOOK

    測試環境Win7 x86

    • 重新加載一份按照PE格式拉伸后的內核文件到內存(避免當前內核已經被掛鉤).
    • 通過導出表獲取HOOK函數系統服務號.
    • 利用導出KeServiceDescriptorTable結構定位系統服務表實現替換函數(類似IAT_HOOK).
     復制代碼 隱藏代碼
    #include 
    #include 
    #include 
    //獲取系統目錄
    PWCHAR GetSystemFullPath();
    //內核文件按照PE拉伸后格式映射到內存
    PUCHAR FileMaping(PWCHAR SystemPath);
    //釋放文件映射
    VOID UnFileMaping(PVOID mapBase);
    //通過函數名查找導出函數
    ULONG64 GetFuntionAddressByExportTableName(PUCHAR ImageBuffer, PUCHAR FunctionName);
    //導出未文檔化函數
    NTSTATUS MmCreateSection(
            __deref_out PVOID* SectionObject,
            __in ACCESS_MASK DesiredAccess,
            __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
            __in PLARGE_INTEGER InputMaximumSize,
            __in ULONG SectionPageProtection,
            __in ULONG AllocationAttributes,
            __in_opt HANDLE FileHandle,
            __in_opt PFILE_OBJECT FileObject
    );
    // 系統服務表
    typedef struct _KSYSTEM_SERVICE_TABLE
    {
            PULONG ServiceTableBase;                        // 函數地址表(SSDT)
            PULONG ServiceCounterTableBase;                // SSDT 函數被調用的次數
            ULONG NumberOfService;                                // 函數個數
            PULONG ParamTableBase;                                // 函數參數表(SSPT)
    } KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;
    typedef struct _KSERVICE_TABLE_DESCRIPTOR
    {
            KSYSTEM_SERVICE_TABLE ntoskrnl;                // 內核函數
            KSYSTEM_SERVICE_TABLE win32k;                // win32k.sys 函數
            KSYSTEM_SERVICE_TABLE unUsed1;
            KSYSTEM_SERVICE_TABLE unUsed2;
    } KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;
    extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
    PUCHAR G_MapNtdll = NULL;
    //拷貝SSDT表
    BOOLEAN SSDT_Init();
    //釋放SSDT表
    VOID SSDT_Destroy();
    //獲取函數系統服務號
    ULONG SSDT_GetFunIndex(PUCHAR szFunctionName);
    //SSDTHOOK
    ULONG_PTR SSDT_Hook(PUCHAR szFunctionName, ULONG_PTR FunctionAddr);
    //關閉寫保護以及中斷
    ULONG wpOff()
    {
            ULONG cr0 = __readcr0();
            _disable();
            __writecr0(cr0 & (~0x10000));
            return cr0;
    }
    //恢復CR0默認數據
    VOID wpOn(ULONG value)
    {
            __writecr0(value);
            _enable();
    }
    //恢復HOOK時用到
    ULONG G_OldFunAddr = NULL;
    //函數指針
    typedef NTSTATUS(NTAPI* OpenProcessProc)(_Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId);
    //替換函數
    NTSTATUS NTAPI MyOpenProcess(_Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId)
    {
            PUCHAR pEprocess = (PUCHAR)IoGetCurrentProcess();
            DbgPrint("進程ID: [%d] 調用OpenProcess \r", *(PULONG)(pEprocess + 0xb4));
            //TODO:
            //獲取參數,監控,修改返回值....
            return ((OpenProcessProc)G_OldFunAddr)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
    }
    NTSTATUS DriverUnload(PDRIVER_OBJECT pDriver)
    {
            DbgPrint("Driver Exit \r");
            //恢復鉤子
            if (G_OldFunAddr)
            {
                    SSDT_Hook("ZwOpenProcess", G_OldFunAddr);
            }
            //釋放后延遲避免有進程還在執行我們函數釋放導致藍屏
            SSDT_Destroy();
            //延時
            LARGE_INTEGER inTime = { 0 };
            inTime.QuadPart = -10000 * 3000;
            KeDelayExecutionThread(KernelMode, FALSE, &inTime);
    }
    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
    {
            DbgPrint("Driver Load \r");
            pDriver->DriverUnload = DriverUnload;
            if (SSDT_Init())
            {
                    G_OldFunAddr = SSDT_Hook("ZwOpenProcess", MyOpenProcess);
            }
            return STATUS_SUCCESS;
    }
    PWCHAR GetSystemFullPath()
    {
            //申請路徑緩沖區
            PWCHAR SystemPath = ExAllocatePool(PagedPool, PAGE_SIZE);
            if (!SystemPath)
            {
                    return NULL;
            }
            memset(SystemPath, 0, PAGE_SIZE);
            //初始化路徑
            RtlStringCbPrintfW(SystemPath, PAGE_SIZE, L"\\??\\%s\\System32\tdll.dll", SharedUserData->NtSystemRoot);
            DbgPrint("SystemPath -> [%ws] \r", SystemPath);
            return SystemPath;
    }
    PUCHAR FileMaping(PWCHAR SystemPath)
    {
            //Initialize UnicodeString
            UNICODE_STRING FileName = { 0 };
            RtlInitUnicodeString(&FileName, SystemPath);
            //Initialize ObjectAttribute
            OBJECT_ATTRIBUTES objectFile = { 0 };
            InitializeObjectAttributes(&objectFile, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
            //CreateFile
            HANDLE hFile = NULL;
            IO_STACK_LOCATION iostacklocation = { 0 };
            NTSTATUS ntstatus = ZwCreateFile(&hFile, GENERIC_READ, &objectFile, &iostacklocation, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL);
            if (!NT_SUCCESS(ntstatus))
            {
                    DbgPrint("FileMaping ZwCreateFile Filed \r");
                    return NULL;
            }
            //Create Section
            OBJECT_ATTRIBUTES objectSection = { 0 };
            InitializeObjectAttributes(&objectSection, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
            PVOID pSection = NULL;
            LARGE_INTEGER InputMaximumSize = { 0 };
            ntstatus = MmCreateSection(&pSection, SECTION_ALL_ACCESS, &objectSection, &InputMaximumSize, PAGE_EXECUTE_READWRITE, 0x1000000, hFile, NULL);
            if (!NT_SUCCESS(ntstatus))
            {
                    DbgPrint("FileMaping MmCreateSection Filed \r");
                    ZwClose(hFile);
                    return NULL;
            }
            PVOID pMapBase = NULL;
            SIZE_T ViewSize = 0;
            ntstatus = MmMapViewInSystemSpace(pSection, &pMapBase, &ViewSize);
            ObDereferenceObject(pSection);
            ZwClose(hFile);
            if (NT_SUCCESS(ntstatus))
            {
                    return pMapBase;
            }
            return NULL;
    }
    VOID UnFileMaping(PVOID pImage)
    {
            if (pImage)
            {
                    MmUnmapViewInSystemSpace(pImage);
            }
    }
    ULONG64 GetFuntionAddressByExportTableName(PUCHAR ImageBuffer, PUCHAR FunctionName)
    {
            //Headers
            PIMAGE_DOS_HEADER    pDos = (PIMAGE_DOS_HEADER)ImageBuffer;
            if (*(PUSHORT)pDos != IMAGE_DOS_SIGNATURE)
            {
                    DbgPrint("Not PeFile \r");
                    return NULL;
            }
            PIMAGE_NT_HEADERS    pNts = (PIMAGE_NT_HEADERS)(ImageBuffer + pDos->e_lfanew);
            if (*(PULONG)pNts != IMAGE_NT_SIGNATURE)
            {
                    DbgPrint("Not PeFile \r");
                    return NULL;
            }
            PIMAGE_FILE_HEADER      pFil = (PIMAGE_FILE_HEADER)((ULONG)pNts + 0x4);
            PIMAGE_OPTIONAL_HEADER  pOpt = (PIMAGE_OPTIONAL_HEADER)((ULONG)pFil + IMAGE_SIZEOF_FILE_HEADER);
            PIMAGE_EXPORT_DIRECTORY pExp = (PIMAGE_EXPORT_DIRECTORY)(ImageBuffer + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
            //遍歷導出表
            ULONG64 FunctionAddr = NULL;
            for (int i = 0; i < pExp->NumberOfNames; i++)
            {
                    PULONG  pAddressOfFuntion        = ImageBuffer + pExp->AddressOfFunctions;
                    PULONG  pAddressOfNames                = ImageBuffer + pExp->AddressOfNames;
                    PUSHORT pAddressOfOrd                = ImageBuffer + pExp->AddressOfNameOrdinals;
                    PUCHAR  CurrentFunctionName = ImageBuffer + pAddressOfNames[i];
                    ULONG uIndex = -1;
                    if (strcmp(CurrentFunctionName, FunctionName) == 0)
                    {
                            uIndex = pAddressOfOrd[i];
                    }
                    if (uIndex != -1)
                    {
                            FunctionAddr = ImageBuffer + pAddressOfFuntion[uIndex];
                            break;
                    }
            }
            if (FunctionAddr)
            {
                    DbgPrint("FindFunctionAddress FunName[%s] Addr[%p] \r", FunctionName, FunctionAddr);
            }
            else
            {
                    DbgPrint("FindFunctionAddress Error FunName[%s] \r", FunctionName);
            }
            return FunctionAddr;
    }
    BOOLEAN SSDT_Init()
    {
            if (G_MapNtdll)
            {
                    return TRUE;
            }
            PWCHAR szPath = GetSystemFullPath();
            if (szPath == NULL)
            {
                    return FALSE;
            }
            G_MapNtdll = FileMaping(szPath);
            if (G_MapNtdll == NULL)
            {
                    ExFreePool(szPath);
                    return FALSE;
            }
            ExFreePool(szPath);
            return TRUE;
    }
    VOID SSDT_Destroy()
    {
            if (G_MapNtdll)
            {
                    UnFileMaping(G_MapNtdll);
                    G_MapNtdll = NULL;
            }
    }
    ULONG SSDT_GetFunIndex(PUCHAR szFunctionName)
    {
            //獲取函數地址
            PUCHAR pFunAddr = (PUCHAR)GetFuntionAddressByExportTableName(G_MapNtdll, szFunctionName);
            if (pFunAddr == NULL)
            {
                    return -1;
            }
            //獲取函數系統服務號
            return *(PULONG)(pFunAddr + 1);
    }
    ULONG_PTR SSDT_Hook(PUCHAR szFunctionName, ULONG_PTR FunctionAddr)
    {
            ULONG uIndex = SSDT_GetFunIndex(szFunctionName);
            if (uIndex == -1)
            {
                    return NULL;
            }
            //備份舊的地址
            ULONG OldFuncAddr = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[uIndex];
            //替換SSDT表中函數
            ULONG cr0 = wpOff();
            KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[uIndex] = FunctionAddr;
            wpOn(cr0);
            return OldFuncAddr;
    }
    
    函數調用系統調用
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    例如,木馬病毒、勒索程序、系統漏洞等安全威脅給人們帶來了巨大的經濟損失,引發隱私泄露等安全問題。HMM-CBOW行為模型是王焱濟在 2019 年提出的一種改進型序列行為模型。圖 3 HMM-CBOW 行為模型HMM-CBOW 行為模型主要對軟件運行過程中的系統調用序列、參數及返回值等進行細粒度的刻畫,將序列進一步劃分為方便存儲、處理與查找的若干不同長度的短序列。
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    API(Application Programming Interface),我們調用時只需提供正確的參數以及接收返回值就可以判斷API執行是否成功或者通過GetLastError獲得錯誤原因.
    RCE系統交互條件與受限環境下的利用
    威努特專家小組就某單位遭受勒索病毒攻擊開展應急處置工作。
    在學習漏洞的時候,按照0Day2書中第24章第1節的內容進行學習的,這章本來是遠程拒絕服務的漏洞(CVE-2009-3103),但是當我在網上搜索這個漏洞的EXP時,意外的發現了Srv2.sys模塊中的另一個漏洞(CVE-2009-2532),而這個漏洞竟然可以實現遠程任意代碼執行,誒,這我就不困了,然后順手兩個漏洞一起分析了,把Srv2.sys模塊對數據包的接收處理過程逆向了一遍,了解了其中的漏
    32位,調用了很多Win32 api,本次計時器的破解突破口就在這。這些操作本質是通過外設向操作系統發送了一些消息,操作系統通過窗口句柄把消息發送給對應窗口過程函數進行處理進而把處理結果通過窗口再次呈現給用戶。將txt文件還原到文件夾中,程序在43FCE8順序執行。patch文件以后,關閉網絡連接發現仍然可以正常啟動程序,至此成功繞過程序需要聯網的要求。timekey破解開發者給出的限制是Timekey.txt里面的內容需要定期更新,大概是要每一個月更新一次。
    前言本文主要著眼于glibc下的一些漏洞及利用技巧和IO調用鏈,由淺入深,分為 “基礎堆利用漏洞及基本IO攻擊” 與 “高版本glibc下的利用” 兩部分來進行講解,前者主要包括了一些glibc相關的基礎知識,以及低版本glibc下常見的漏洞利用方式,后者主要涉及到一些較新的glibc下的IO調用鏈。
    VMPWN的入門系列-2
    2023-08-03 09:29:42
    解釋器是一種計算機程序,用于解釋和執行源代碼。與編譯器不同,解釋器不會將源代碼轉換為機器語言,而是直接執行源代碼。即,這個程序接收一定的解釋器語言,然后按照一定的規則對其進行解析,完成相應的功能,從本質上來看依然是一個虛擬機。總的來說,如果輸入字符數小于0x10,string類的大概成員應該如下struct?
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类