/*
 *	Switch ring3 application to ring0 in Windows95/98
 *
 *	author :  Ewald Snel, 1999
 */

int main()
{
    unsigned int _cr3;

    RING0ON();
    __asm {
        mov     eax , -1
        xor     eax , eax
        mov     eax , cr3
    }
    RING0OFF();
    __asm {
        mov     [_cr3] , eax
    }
    printf( "cr3=%08x\n", _cr3 );
}


/*
  CALL at ring0

  RETURNS to ring3 if 'true' stacked
*/
__declspec (naked) RING0OFF()
{
    __asm
    {
        // decrease ring0 'counter'
            cmp         dword ptr [esp][4] , 0
            jne         _back_to_ring3

        // pop 'counter', remain at ring0
            ret         4

_back_to_ring3:
        // disable interrupts and tasking
            cli

            push        eax
            push        esi

        // find gdt base address
            sub         esp , 8
            sgdt        fword ptr [esp]
            mov         esi , [esp][2]
            add         esp , 8

        // find current ldt's descriptor
            sldt        ax
            and         eax , 0xfff8
            add         esi , eax

        // get ldt base address
            mov         al , [esi][4]
            mov         ah , [esi][7]
            shl         eax , 16
            mov         ax , [esi][2]
            mov         esi , eax

        // set dpl of 'cs' descriptor to 3
            mov         ax , cs
            and         eax , 0fff8h
            or          byte ptr [esi][eax][5] , 60h

            pop         esi
            pop         eax

        // 'pushpop' eip return address
            pop         dword ptr [esp]

        // enable interrupts and tasking
            sti

        // return to ring3
            retf
    }
}


/*
  CALL at ring3 or ring0

  RETURNS always at ring0
*/
__declspec (naked) RING0ON()
{
    __asm
    {
        // check to see if we're already at ring0
            push        eax
            mov         ax , cs
            test        al , 3
            pop         eax
            jnz         _enable_ring0

        // 'push' 'false' count
            push        dword ptr [esp]
            mov         dword ptr [esp][4] , 0

        // return with count pushed on the stack
            ret


        // set exclusive access (NO TASKING ALLOWED!!)
_enable_ring0:
            cli

            push        eax
            push        esi

        // find gdt base address
            sub         esp , 8
            sgdt        fword ptr [esp]
            mov         esi , [esp][2]
            add         esp , 8

        // find current ldt's descriptor
            sldt        ax
            and         eax , 0xfff8
            add         esi , eax

        // get ldt base address
            mov         al , [esi][4]
            mov         ah , [esi][7]
            shl         eax , 16
            mov         ax , [esi][2]
            mov         esi , eax

        // set dpl of 'cs' descriptor to 0
            mov         ax , cs
            and         eax , 0fff8h
            and         byte ptr [esi][eax][5] , 9fh

        // save descriptor 000c
            push        dword ptr [esi][0ch]
            push        dword ptr [esi][8]

        // overwrite with call gate
            mov         eax , offset _enter_ring0
            mov         dword ptr [esi][8] , eax
            mov         dword ptr [esi][0ch] , eax
            mov         ax , cs
            and         al , 0fch
            mov         [esi][0ah] , ax
            mov         word ptr [esi][0ch] , 0ec07h    // cg386, dpl=3, dc=7

        // call into ring0
            push        000ch
            push        eax
            call        fword ptr [esp]
            //.
            //.


        // ring0 entry point
_enter_ring0:
        // restore descriptor 000c
            mov         eax , [esp][16]
            mov         [esi][8] , eax
            mov         eax , [esp][20]
            mov         [esi][0ch] , eax

        // rebuild ring0 and ring3 stacks
            mov         esi , [esp][24]
            mov         eax , [esp][32]
            mov         [esp][24] , eax
            mov         eax , [esp][4]
            mov         [esp][32] , eax
            mov         eax , [esp][28]
            mov         dword ptr [esp][28] , 1
            add         esp , 24
            add         dword ptr [esp][12] , 28

        // enable interrupts and tasking
            sti

        // continue execution at ring0
            ret
    }
}

