As you know, Windows NT/2000 strictly divides the system into kernel mode and user mode to achieve its reliability. In the i386 system, the system corresponds to the ring0 and ring3 CPU levels respectively. In ring0, You can execute privileged commands and have access to any I/O device. To switch from user to core, that is, from ring 3 to ring 0, you must use a CPU-based door mechanism, such as door interruption and door invocation. Windows NT/2000 provides such a mechanism for the user-mode Execution System Service (RING 0 routine), that is, the int 2EH interrupt service of the system service, and performs strict parameter checks, only services provided by Windows NT/2000 can be strictly executed. If you want to execute the Ring 0 Code provided by the user (the code running with the Ring 0 permission ), the general method seems to only write the device driver. This article describes how to execute ring0 code in user mode without any driver.
In Windows NT/2000, the device driver is transferred to the kernel area (commonly located on address 0x80000000). The ring 0 permission is granted by gdt Item 8 with DPL 0, that is, when CS is 8. This article constructs a callgate pointing to our code in the system to implement ring0 code. Based on this idea, to achieve this goal, we mainly construct our own callgate. Callgate is specified by the global table named Global Descriptor Table (gdt) in the system. The gdt address can be obtained by the i386 command sgdt (sgdt is not a privileged command and can be executed by a general ring 3 program ). Gdt addresses are stored in the processor control region structure of Windows NT/2000 (see Windows NT/2000 environment switch again). Callgate in gdt is in the following format:
Typedef struct
{
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;
Gdt is located in the kernel area. Generally, a user-State program cannot have direct access to this memory area. Fortunately, Windows NT/2000 provides a section kernel object named physicalmemory located in the/device path. As the name suggests, this section object can be used to operate the physical memory. Use objdir.exe to analyze this object as follows:
C:/ntddk/bin> objdir/D/Device
Physicalmemory
Section
DACL-
Ace [0]-grant-0xf001f-nt authority/System
Inherit:
Access: 0x001f and (D rctl wown wdacl)
Ace [1]-grant-0x2000d-builtin/administrators
Inherit:
Access: 0x000d and (rctl)
From the DACL Ace of the dump object, we can see that by default, only the System user has the read and write permissions on the object, that is, the physical memory has the read and write capabilities, while the Administrator only has the read permission, normal users have no permissions at all. However, if you have the Administrator permission, you can use the getsecurityinfo, setentriesinacl, and setsecurityinfo APIs to modify the Ace of this object. This is why the code I provide requires administrator. The implementation code is as follows:
Void setphyscialmemorysectioncanbewrited (handle hsection)
{
PACl pdacl = NULL;
PACl pnewdacl = NULL;
Psecurity_descriptor PSD = NULL;
DWORD dwres;
Explicit_access EA;
If (dwres = getsecurityinfo (hsection, se_kernel_object, dacl_security_information,
Null, null, & pdacl, null, & PSD )! = Error_success)
{
Printf ("getsecurityinfo error % u/N", dwres );
Goto cleanup;
}
Zeromemory (& EA, sizeof (explicit_access ));
EA. grfaccesspermissions = section_map_write;
EA. grfaccessmode = grant_access;
EA. grfinheritance = no_inheritance;
EA. Trustee. trusteeform = trustee_is_name;
EA. Trustee. trusteetype = trustee_is_user;
EA. Trustee. ptstrname = "CURRENT_USER ";
If (dwres = setentriesinacl (1, & EA, pdacl, & pnewdacl )! = Error_success)
{
Printf ("setentriesinacl % u/N", dwres );
Goto cleanup;
}
If (dwres = setsecurityinfo (hsection, se_kernel_object, dacl_security_information, null, null, pnewdacl, null )! = Error_success)
{
Printf ("setsecurityinfo % u/N", dwres );
Goto cleanup;
}
Cleanup:
If (PSD)
Localfree (PSD );
If (pnewdacl)
Localfree (PSD );
}
This Code adds the following Ace for the given handle object:
Physicalmemory
Section
DACL-
Ace [0]-grant-0x2-webcrazy/Administrator
Inherit:
Access: 0x0002 // section_map_write
In this way, with the Administrator permission, we have the ability to read and write physical memory. But to modify the gdt table to implement the Ring 0 code. We will face another challenge because the gdt address obtained by the sgdt command is a virtual address (linear address ), we can modify the gdt table through the/device/physicalmemory object only after knowing the physical address of the gdt table. This involves converting a linear address into a physical address. Let's take a look at how Windows NT/2000 implements this:
Kd> u nt! Mmgetphysicaladdress L 30
Ntoskrnl! Mmgetphysicaladdress:
801374e0 56 push ESI
801374e1 8b742408 mov ESI, [esp + 0x8]
801374e5 33d2 XOR edX, EDX
801374e7 81fe00000080 cmp esi, 0x80000000
801374ed 722c JB ntoskrnl! Mmgetphysicaladdress + 0x2b (8013751b)
801374ef 81fe000000a0 cmp esi, 0xa0000000
801374f5 7324 JNB ntoskrnl! Mmgetphysicaladdress + 0x2b (8013751b)
801374f7 39153ce71780 CMP [ntoskrnl! Mmkseg2frame (8017e73c)], EDX
801374fd 741c JZ ntoskrnl! Mmgetphysicaladdress + 0x2b (8013751b)
801374ff 8bc6 mov eax, ESI
80137501 c1e80c SHR eax, 0xc
80137504 25ffff0100 and eax, 0x1ffff
80137509 6a0c push 0xc
8013750b 59 pop ECx
8013750c e8d3a7fcff call ntoskrnl! _ Allshl (80101ce4)
80137511 81e6ff0f0000 and ESI, 0 xfff
80137517 03c6 add eax, ESI
80137519 eb17 JMP ntoskrnl! Mmgetphysicaladdress + 0x57 (80137532)
8013751b 8bc6 mov eax, ESI
8013751d c1e80a SHR eax, 0xa
80137520 25fcff3f00 and eax, 0x3ffffc
80137525 2d00000040 sub eax, 0x40000000
8013752a 8b00 mov eax, [eax]
8013752c a801 test Al, 0x1
8013752e 7506 jnz ntoskrnl! Mmgetphysicaladdress + 0x44 (80137536)
80137530 33c0 XOR eax, eax
80137532 5E pop ESI
80137533 c20400 RET 0x4
From this assembly code, we can see that if the linear address is within the range of 0x80000000 and 0xa0000000, it is a simple shift operation (between the 801374ff-80137519 command) and no page table is queried. I think Microsoft's arrangement is definitely out of execution efficiency. This also gives us a glimmer of light, because gdt tables are generally located in this region in Windows NT/2000 (I don't know if this is the case for Windows NT/2000 with the/3 GB switch ).
After such analysis, we can modify the gdt table only through the user State program. But adding a callgate is not something I can introduce. I have a look at this intel manual. The specific implementation code is as follows:
Typedef struct GDTR {
Short limit;
Short baselow;
Short basehigh;
} Gdtr_t, * pgdtr_t;
Ulong minimmgetphysicaladdress (ulong virtualaddress)
{
If (virtualaddress <0x80000000 | virtualaddress> = 0xa0000000)
Return 0;
Return virtualaddress & 0x1ffff000;
}
Bool execring0proc (ulong entry, ulong seglen)
{
Gdtr_t gdt;
_ ASM sgdt gdt;
Ulong mapaddr = minimmgetphysicaladdress (gdt. basehigh <16u | gdt. baselow );
If (! Mapaddr) return 0;
Handle hsection = NULL;
Ntstatus status;
Object_attributes objectattributes;
Unicode_string objname;
Callgate_descriptor * CG;
Status = STATUS_SUCCESS;
Rtlinitunicodestring (& objname, l "// device // physicalmemory ");
Initializeobjectattributes (& objectattributes,
& Objname,
Obj_case_insensitive | obj_kernel_handle,
Null,
(Psecurity_descriptor) null );
Status = zwopensection (& hsection, section_map_read | section_map_write, & objectattributes );
If (status = STATUS_ACCESS_DENIED ){
Status = zwopensection (& hsection, read_control | write_dac, & objectattributes );
Setphyscialmemorysectioncanbewrited (hsection );
Zwclose (hsection );
Status = zwopensection (& hsection, section_map_write | section_map_write, & objectattributes );
}
If (status! = STATUS_SUCCESS)
{
Printf ("error open physicalmemory section object, status: % 08x/N", status );
Return 0;
}
Pvoid baseaddress;
Baseaddress = mapviewoffile (hsection,
File_map_read | file_map_write,
0,
Mapaddr, // low part
(Gdt. Limit + 1 ));
If (! Baseaddress)
{
Printf ("error mapviewoffile :");
Printwin32error (getlasterror ());
Return 0;
}
Bool setcg = false;
For (CG = (callgate_descriptor *) (ulong) baseaddress + (gdt. Limit & 0xfff8); (ulong) CG> (ulong) baseaddress; CG --)
If (Cg-> type = 0 ){
CG-> offset_0_15 = loword (entry );
CG-> selector = 8;
CG-> param_count = 0;
CG-> some_bits = 0;
CG-> type = 0xc; // 386 call gate
CG-> app_system = 0; // a system Descriptor
CG-> DPL = 3; // ring 3 code can call
CG-> present = 1;
CG-> offset_16_31 = hiword (entry );
Setcg = true;
Break;
}
If (! Setcg ){
Zwclose (hsection );
Return 0;
}
Short farcall [3];
Farcall [2] = (short) (ulong) CG-(ulong) baseaddress) | 3; // ring 3 callgate;
If (! Virtuallock (pvoid) entry, seglen ))
{
Printf ("error virtuallock :");
Printwin32error (getlasterror ());
Return 0;
}
Setthreadpriority (getcurrentthread (), thread_priority_time_critical );
Sleep (0 );
_ ASM call fword PTR [farcall]
Setthreadpriority (getcurrentthread (), thread_priority_normal );
Virtualunlock (pvoid) entry, seglen );
// Clear callgate
* (Ulong *) CG = 0;
* (Ulong *) CG + 1) = 0;
Zwclose (hsection );
Return true;
}
The Code provided demonstrates the operation on the control register and I/O Ports. In Windows 9x, CIH virus is compromised by Ring 0 permissions. However, Windows NT/2000 is not Windows 9x after all, and she already has many security auditing mechanisms, the Code provided in this article also requires administrator permissions. However, if the system has certain vulnerabilities, such as buffer overflow, it is still possible to obtain such permissions, therefore, I am not responsible for the methods provided in this Article. All discussions are just a technology enthusiast discussing technology. Thank you!
References:
1. Intel Corp <intel architecture software developer's manual, Volume 3>
Bytes -----------------------------------------------------------------------------------
Article from: http://www.geocities.jp/webcrazyjp/ntring0.htm
Author's website: webcrazy (http://webcrazy.yeah.net /)