您的位置:首页 > 运维架构

rootkit直接访问硬件之一 ----修改IOPL

2008-03-30 22:50 155 查看
今天我们一起来探索下如何实现ring3程序直接访问硬件。这部分内容跟windows保护模式中的I/O保护部分有直接的关系。

我们来看看Windows系统I/O访问保护的方法:

80386采用 I/O特权级IPOL 和 I/O许可位图 的方法来控制输入/输出,实现输入/输出保护.

I/O许可位图位于 任务状态段TSS中。 I/O特权级IPOL就是EFLAGS寄存器中IOPL位。

采用的保护规则 是:

(1)若CPL<=IOPL,则直接转步骤(8);

(2)取得I/O位图开始偏移;

(3)计算I/O地址对应位所在字节在I/O许可位图内的偏移;

(4)计算位偏移以形成屏蔽码值,即计算I/O地址对应位在字节中的第几位;

(5)把字节偏移加上位图开始偏移,再加1,所得值与TSS界限比较,若越界,则产生出错码为0的通用保护故障;

(6)若不越界,则从位图中读对应字节及下一个字节;

(7)把读出的两个字节与屏蔽码进行与运算,若结果不为0表示检查未通过,则产生出错码为0的通用保护故障;

(8)进行I/O访问。

其中windows中IOPL值是为0,从第一条规则我们可以了解到为什么ring0下可以直接访问I/O了,因为ring0中CPL = 0 ,完全满足第一条。而ring3下CPL = 3 ,第一条不能满足。

我们知道每个任务使用各自的EFLAGS值和拥有自己的TSS,所以每个任务可以有不同的IOPL。所以想在ring3下访问I/O,最简单的方法当然是修改当前进程IOPL的值为3了。

根据前面了解到 IPOL就是EFLAGS寄存器中IOPL位,所以我们尝试下修改eflags寄存器看看。如图:

 我们写段代码看看:

#include "stdio.h"

int main(int argc, char* argv[])

{

  _asm

  {

    pushfd

    pop eax   

    or eax,0x3000  //设置iopl = 3

    push eax

    popfd

    pushfd   //看看修改后的效果

    pop eax  //eflags寄存器的值保存到eax中

  }

  return 0;

}

通过上面代码,我们发现不能直接修改eflags中iopl的值。呵呵,继续想办法吧。

每个Windows进程都有一个相对应的执行体进程(EPROCESS),EPROCESS不仅包括了进程的许多属性,还包扩了许多指向其他数据结构的指针,其中包含了大量有用的信息. 

为了找出思路,我们还是先看看进程eprocess吧。

lkd> dt _eprocess

ntdll!_EPROCESS

   +0x000 Pcb              : _KPROCESS

   +0x06c ProcessLock      : _EX_PUSH_LOCK

   +0x070 CreateTime       : _LARGE_INTEGER

   +0x078 ExitTime         : _LARGE_INTEGER

   +0x080 RundownProtect   : _EX_RUNDOWN_REF

   +0x084 UniqueProcessId  : Ptr32 Void

   +0x088 ActiveProcessLinks : _LIST_ENTRY

   +0x090 QuotaUsage       : [3] Uint4B

   +0x09c QuotaPeak        : [3] Uint4B

   +0x0a8 CommitCharge     : Uint4B

   +0x0ac PeakVirtualSize  : Uint4B

   +0x0b0 VirtualSize      : Uint4B

   +0x0b4 SessionProcessLinks : _LIST_ENTRY

   +0x0bc DebugPort        : Ptr32 Void

   +0x0c0 ExceptionPort    : Ptr32 Void

   +0x0c4 ObjectTable      : Ptr32 _HANDLE_TABLE

   +0x0c8 Token            : _EX_FAST_REF

   +0x0cc WorkingSetLock   : _FAST_MUTEX

   +0x0ec WorkingSetPage   : Uint4B

   +0x0f0 AddressCreationLock : _FAST_MUTEX

   +0x110 HyperSpaceLock   : Uint4B

   +0x114 ForkInProgress   : Ptr32 _ETHREAD

   +0x118 HardwareTrigger  : Uint4B

   +0x11c VadRoot          : Ptr32 Void

   +0x120 VadHint          : Ptr32 Void

   +0x124 CloneRoot        : Ptr32 Void

   +0x128 NumberOfPrivatePages : Uint4B

   +0x12c NumberOfLockedPages : Uint4B

   +0x130 Win32Process     : Ptr32 Void

   +0x134 Job              : Ptr32 _EJOB

   +0x138 SectionObject    : Ptr32 Void

   +0x13c SectionBaseAddress : Ptr32 Void

   +0x140 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK

   +0x144 WorkingSetWatch  : Ptr32 _PAGEFAULT_HISTORY

   +0x148 Win32WindowStation : Ptr32 Void

   +0x14c InheritedFromUniqueProcessId : Ptr32 Void

   +0x150 LdtInformation   : Ptr32 Void

   +0x154 VadFreeHint      : Ptr32 Void

   +0x158 VdmObjects       : Ptr32 Void

   +0x15c DeviceMap        : Ptr32 Void

   +0x160 PhysicalVadList  : _LIST_ENTRY

   +0x168 PageDirectoryPte : _HARDWARE_PTE_X86

   +0x168 Filler           : Uint8B

   +0x170 Session          : Ptr32 Void

   +0x174 ImageFileName    : [16] UChar

   +0x184 JobLinks         : _LIST_ENTRY

   +0x18c LockedPagesList  : Ptr32 Void

   +0x190 ThreadListHead   : _LIST_ENTRY

   +0x198 SecurityPort     : Ptr32 Void

   +0x19c PaeTop           : Ptr32 Void

   +0x1a0 ActiveThreads    : Uint4B

   +0x1a4 GrantedAccess    : Uint4B

   +0x1a8 DefaultHardErrorProcessing : Uint4B

   +0x1ac LastThreadExitStatus : Int4B

   +0x1b0 Peb              : Ptr32 _PEB

   +0x1b4 PrefetchTrace    : _EX_FAST_REF

   +0x1b8 ReadOperationCount : _LARGE_INTEGER

   +0x1c0 WriteOperationCount : _LARGE_INTEGER

   +0x1c8 OtherOperationCount : _LARGE_INTEGER

   +0x1d0 ReadTransferCount : _LARGE_INTEGER

   +0x1d8 WriteTransferCount : _LARGE_INTEGER

   +0x1e0 OtherTransferCount : _LARGE_INTEGER

   +0x1e8 CommitChargeLimit : Uint4B

   +0x1ec CommitChargePeak : Uint4B

   +0x1f0 AweInfo          : Ptr32 Void

   +0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO

   +0x1f8 Vm               : _MMSUPPORT

   +0x238 LastFaultCount   : Uint4B

   +0x23c ModifiedPageCount : Uint4B

   +0x240 NumberOfVads     : Uint4B

   +0x244 JobStatus        : Uint4B

   +0x248 Flags            : Uint4B

   +0x248 CreateReported   : Pos 0, 1 Bit

   +0x248 NoDebugInherit   : Pos 1, 1 Bit

   +0x248 ProcessExiting   : Pos 2, 1 Bit

   +0x248 ProcessDelete    : Pos 3, 1 Bit

   +0x248 Wow64SplitPages  : Pos 4, 1 Bit

   +0x248 VmDeleted        : Pos 5, 1 Bit

   +0x248 OutswapEnabled   : Pos 6, 1 Bit

   +0x248 Outswapped       : Pos 7, 1 Bit

   +0x248 ForkFailed       : Pos 8, 1 Bit

   +0x248 HasPhysicalVad   : Pos 9, 1 Bit

   +0x248 AddressSpaceInitialized : Pos 10, 2 Bits

   +0x248 SetTimerResolution : Pos 12, 1 Bit

   +0x248 BreakOnTermination : Pos 13, 1 Bit

   +0x248 SessionCreationUnderway : Pos 14, 1 Bit

   +0x248 WriteWatch       : Pos 15, 1 Bit

   +0x248 ProcessInSession : Pos 16, 1 Bit

   +0x248 OverrideAddressSpace : Pos 17, 1 Bit

   +0x248 HasAddressSpace  : Pos 18, 1 Bit

   +0x248 LaunchPrefetched : Pos 19, 1 Bit

   +0x248 InjectInpageErrors : Pos 20, 1 Bit

   +0x248 VmTopDown        : Pos 21, 1 Bit

   +0x248 Unused3          : Pos 22, 1 Bit

   +0x248 Unused4          : Pos 23, 1 Bit

   +0x248 VdmAllowed       : Pos 24, 1 Bit

   +0x248 Unused           : Pos 25, 5 Bits

   +0x248 Unused1          : Pos 30, 1 Bit

   +0x248 Unused2          : Pos 31, 1 Bit

   +0x24c ExitStatus       : Int4B

   +0x250 NextPageColor    : Uint2B

   +0x252 SubSystemMinorVersion : UChar

   +0x253 SubSystemMajorVersion : UChar

   +0x252 SubSystemVersion : Uint2B

   +0x254 PriorityClass    : UChar

   +0x255 WorkingSetAcquiredUnsafe : UChar

   +0x258 Cookie           : Uint4B

大致上看了下,没有iopl相关的项,我们继续看看里面的子项内容。

看看  +0x000 Pcb  : _KPROCESS这个结构体。

lkd> dt _kprocess

ntdll!_KPROCESS

   +0x000 Header           : _DISPATCHER_HEADER

   +0x010 ProfileListHead  : _LIST_ENTRY

   +0x018 DirectoryTableBase : [2] Uint4B

   +0x020 LdtDescriptor    : _KGDTENTRY

   +0x028 Int21Descriptor  : _KIDTENTRY

   +0x030 IopmOffset       : Uint2B

    +0x032 Iopl             : UChar

   +0x033 Unused           : UChar

   +0x034 ActiveProcessors : Uint4B

   +0x038 KernelTime       : Uint4B

   +0x03c UserTime         : Uint4B

   +0x040 ReadyListHead    : _LIST_ENTRY

   +0x048 SwapListEntry    : _SINGLE_LIST_ENTRY

   +0x04c VdmTrapcHandler  : Ptr32 Void

   +0x050 ThreadListHead   : _LIST_ENTRY

   +0x058 ProcessLock      : Uint4B

   +0x05c Affinity         : Uint4B

   +0x060 StackCount       : Uint2B

   +0x062 BasePriority     : Char

   +0x063 ThreadQuantum    : Char

   +0x064 AutoAlignment    : UChar

   +0x065 State            : UChar

   +0x066 ThreadSeed       : UChar

   +0x067 DisableBoost     : UChar

   +0x068 PowerState       : UChar

   +0x069 DisableQuantum   : UChar

   +0x06a IdealNode        : UChar

   +0x06b Flags            : _KEXECUTE_OPTIONS

   +0x06b ExecuteOptions   : UChar

在这里,我们找到了 Iopl和IopmOffset 这两项。 其中 IopmOffset指 该进程 I/O许可位图在 tss区域的偏移 ,也就是TSS中的 IoMapBase值。而 Iopl 是一个布尔值,该值决定了进程中线程切换时, EFLAGS寄存器中IOPL位是设置为0还是为3。 如果进程 Iopl值为TRUE,

则 EFLAGS寄存器中IOPL值为3,如果进程 Iopl值为FALSE,则 EFLAGS寄存器中IOPL值为0.

到这里我们更进了一步。呵呵,离成功不远了。

接下来我们发现在ntdll.dll中有一个导出函数NtSetInformationProcess。

我们可以在ring3下直接调用NtSetInformationProcess函数的ProcessUserModeIOPL功能将进程中 Iopl值为TRUE。但是有个前提,需要当前用户具有SeTcbPrivilege这个权限才行,并且当前用户必须是administrator.

SeTcbPrivilege 表示当前用户的操作代表了系统的操作。并且这个权限只有system有,Administrator都没有这个权限。因此,我们需要首先得到这个权限,并且将这个权限使能。然后再调用 NtSetInformationProcess的ProcessUserModeIOPL功能。思路很清晰了,这个我们在ring3下就能实现。贴代码:

#include <string.h>

#include <windows.h>

#include <tchar.h>

#include <ntsecapi.h>

#include <process.h>

BOOL EnablePrivilege(PTCHAR Privilege)

{

    BOOL rc = FALSE;

    HANDLE hToken;

    LUID luid;

    TOKEN_PRIVILEGES tokenPrivilege;

  DWORD dwProcID = GetCurrentProcessId();

  HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcID);

    //

    //  Open the current process' token.

    //

    rc = OpenProcessToken(

            GetCurrentProcess(),

            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,

            &hToken);

    if (rc)

    {

        rc = LookupPrivilegeValue(NULL, Privilege, &luid);

        if (rc)

        {

            tokenPrivilege.PrivilegeCount = 1;

            tokenPrivilege.Privileges[0].Luid = luid;

            tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            //

            //  Assign the given privilege.

            //

            rc = AdjustTokenPrivileges(

                    hToken, 

                    FALSE, 

                    &tokenPrivilege, 

                    sizeof(tokenPrivilege), 

                    NULL, 

                    NULL);

        }

    }

    if (hToken)

    {

        CloseHandle(hToken);

    }

   

  if(hProc)

    CloseHandle(hProc);

    return rc;

}

BOOL EnableProcPrivilege()

{

  BOOL rc = TRUE;

  LSA_HANDLE PolicyHandle;

  PSID Sid=0;

  DWORD cbSid=0;

  LPTSTR ReferencedDomainName=0;

  DWORD cbReferencedDomainName=0;

  SID_NAME_USE peUse;

  PUNICODE_STRING UserRights=0; //UnicodeString Pointer to PRIVILEGE

  ULONG Count=0; //

  HANDLE token=0;

  PTOKEN_PRIVILEGES TokenInformation=0;

  BOOL owned=0;

  OSVERSIONINFO osv;

  char username[30];

  DWORD cb = 30;

  LSA_OBJECT_ATTRIBUTES ObjectAttributes;

  ZeroMemory(&ObjectAttributes,sizeof(ObjectAttributes));

  ZeroMemory(&osv,sizeof(osv));

  osv.dwOSVersionInfoSize = sizeof(osv);

  //

  //判断当前系统是否为nt及以上

  //

  GetVersionEx(&osv);

  if(!(osv.dwPlatformId & VER_PLATFORM_WIN32_NT))

  {

       rc = FALSE;

  }

  //

  // 判断当前用户是否为administrator

  //

  GetUserName(username,&cb);

  if(stricmp(username,"administrator"))

  {

        rc = FALSE;

  }

  //

  //First open LSA policy database

  //the call returns a NTSTATUS. NTSTATUS 0 means everything is OK.

  //

  if (LsaOpenPolicy(

          0,

          &ObjectAttributes,

          GENERIC_EXECUTE|GENERIC_READ|GENERIC_WRITE,

          &PolicyHandle

          ))

  {

    rc = FALSE;

  }

  Sid=new char[500];

  ReferencedDomainName=new CHAR[100];

  cbSid=500;

  cbReferencedDomainName=100;

  //

  //Show Administrator SID

  //

  if (!LookupAccountName(

              0,

              "Administrator",

              Sid,

              &cbSid,

              ReferencedDomainName,

              &cbReferencedDomainName,

              &peUse

              ))

  {

      rc =  FALSE;

  }

  //

  //Add SeTchPrivilege to Administrator if not owned yet!

  //

  UserRights=new LSA_UNICODE_STRING;

  UserRights->Buffer=L"SeTcbPrivilege";

  UserRights->MaximumLength=28;

  UserRights->Length=28;

  if (LsaAddAccountRights(

              PolicyHandle,

              Sid,

              UserRights,

              1

              ))

  {

    rc = FALSE;

  }

  if(Sid)

    delete Sid;

  if(ReferencedDomainName)

    delete ReferencedDomainName;

  if (UserRights) 

    delete UserRights;

  if (TokenInformation)

    delete TokenInformation;

  if (token)

    CloseHandle(token);

  if (PolicyHandle) 

    LsaClose(PolicyHandle);

  rc = EnablePrivilege(SE_TCB_NAME);

  return rc;

}

BOOL EnableUserModeHardwareIO()

{

    typedef ULONG (__stdcall* pfn_ZwSetInformationProcess)(

                              HANDLE, 

                              ULONG, 

                              PVOID, 

                              ULONG);

    BOOL rc = FALSE;

    HMODULE hNtDll = NULL;

    ULONG IOPL = 1;

    INT ProcessUserModeIOPL = 16;

    pfn_ZwSetInformationProcess ZwSetInformationProcess;

    DWORD dwProcessID = GetCurrentProcessId();

    HANDLE   hProc= OpenProcess(   PROCESS_ALL_ACCESS,   TRUE,dwProcessID); 

    hNtDll = GetModuleHandle("ntdll.dll");

    if (hNtDll)

    {

        ZwSetInformationProcess = (pfn_ZwSetInformationProcess) 

                                  GetProcAddress(hNtDll, "ZwSetInformationProcess");

        if (ZwSetInformationProcess)

        {

            //

            //  Enable SeTcbPrivilege

            //

            rc = EnableProcPrivilege();

            if (rc)

            {

                //

                //  Grant user mode hardware IO access.

                //                 

                rc = ZwSetInformationProcess(

                        hProc, 

                        ProcessUserModeIOPL, 

                        &IOPL, 

                        sizeof(IOPL));           

                //

                //  An NTSTATUS is returned, so zero is success.

                //

                if (!rc)

                {

                    rc = TRUE;

                }

            }

        }

    }

    CloseHandle(hProc);

    return rc;

}

int __cdecl main(int argc, char* argv[])

{

    if (EnableUserModeHardwareIO())

    {

        //

        //  直接给键盘发一个指令,让机器重启

        //

        __asm mov dx, 0x64

        __asm mov al, 0xFE

        __asm out dx, al

    }

    return 0;

}

接下来,想要修改进程中iopl值,我们还可以通过写驱动的方式解决,我们只要写一个ring3程序,加载这个驱动,并将自身进程的id传递给驱动程序,就可以在驱动中修改该进程ipol的值。也写一个demo附上。

最后,当然还可以这么做,直接在驱动中执行I/O操作,ring3加载这个驱动,所有硬件操作直接让驱动来完成。这个还是留给大家自己来完成吧。

技术成就梦想

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐