岁月联盟 - 技术社区 - BBS.SYUE.COM's Archiver

猪猪 发表于 2007-2-23 14:04

任意用户模式下执行 ring 0 代码

文章作者:sinister@whitecell.org

  众所周知在非 Admin 用户模式下,是不允许加载驱动执行 RING 0 代码的。本文提供了一种方法,通过修改系统 GDT,IDT 来添加自己的 CALLGATE 和INTGATE 这样便在系统中设置了一个后门。我们就可以利用这个后门在任意用户模式下执行 ring 0 代码了。为了保证我们添加的 CALLGATE 和 INTGATE 永久性。可以在第一次安装时利用 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 。


[b]驱动程序:[/b]

[code]/*****************************************************************
文件名     : 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;
}[/code]

[b]应用程序:[/b]

[code]#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]


}[/code]

[code]  二、通过添加中断门实现

添加中断门没有什么需要解决的问题。直接在 RING 3 利用 int x
即可切换。想想系统调用 INT 2E 就很容易理解了。[/code]

[code]/*****************************************************************
文件名     : 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;
}[/code]

关于我们:

WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。
WSS 主页:http://www.whitecell.org/

页: [1]

Powered by Discuz! Archiver 7.0.0  © 2001-2009 Comsenz Inc.