Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

555 lines
16 KiB

TITLE r2xfer.asm
.386P
OPTION SEGMENT:USE16
.MODEL LARGE
COMMENT $
This file contains code and data that handles the case where a R3 code
contains a call to a R2 entry point.
In OS/2, only the selector part (call gate) of the call destination is
meaningful.
The OS/2 SS replaces the original call with a call to a jump table
entry in the R2XFER code segment (which is the second code segment of
DOSCALLS.DLL).
The R2XFER code segment contains code that emulates the stack changing
when transferring protection rings. The size of this code is limited to a
predefined size since it is followed by other data structures which are
set at run time by the loader. The limit is currently set to 0x200
(which can be changed anytime later if it is found that 0x200 is not
sufficient to handle the ring transfer emulation code).
The code generated by this file is followed in memory by an array of Ring 2
info which is prepared by the OS/2 SS loader.
The array is variable length and each entry contains the following
information:
struct _R2CallInfo {
UCHAR R2CallNearInst; // Opcode of the call relative near inst (0xE8)
USHORT R2CommonEntry; // This field will contain the relative offset to 0H
UCHAR R2BytesToCopy; // Total size of proc parameters to copy between stacks
USHORT R2EntryPointOff; // Offset of entry address of R2 routine
USHORT R2EntryPointSel; // Selector of entry address of R2 routine
} R2CallInfo;
When a ring 3 routine calls a ring 2 routine (which the OS/2 SS loader
changes to a call to the appropriate R2CallInfo entry, which does another
call to the CommonR2Xfer routine) the R3 stack looks upon entry to
CommonR2Xfer:
param1
param2
param3
return-addr-r3 CS
IP
return-addr-r2 IP <- SP
We want to start the R2 code with an R2 stack in the following format:
<- TOS
<16 bytes free> These free bytes are a local work-around for the PMNTPrint()
accesses to the stack.
Old SS
Old SP
0 These 2 zeroes are used to catch (by trap) any access
0 of the ring 2 code to the ring 3 stack
param1
param2
param3
return-addr-r3 CS
IP <- SS:SP
This file also defines a data segment that is used to hold information of the
OS/2 SS ring 2 stacks. This data segment is the 3'rd segment of DOSCALLS.DLL
The data is organized as an array with each entry representing the ring 2
stack of the appropriate thread.
Each entry in the array has the following format:
struct _R2Stacks {
USHORT R2StackSize;
SEL R2StackSel;
} R2Stacks[256];
Entry 0 is not used since there is no thread 0.
OS/2 supports 255 threads so this array contains enough entries.
An entry of the array is initialized when the CommonR2Xfer routine
is called by an OS/2 thread for the first time, with same ThreadID as
the index of the enrty.
OS/2 allocates 512 bytes as the initial size of the R2 stack and so
do we.
$
R2Stacks STRUCT 1
R2InitSP WORD 512 ; 0:512 indicates that no R2 stack exists
R2StackSel WORD 0
R2Stacks ENDS
R2CallInfo STRUCT 1
R2bytesToCopy BYTE ?
R2EntryPoint DWORD ?
R2CallInfo ENDS
EXTERN DosAllocSeg:FAR
.CODE
PUBLIC CommonR2Xfer
CommonR2Xfer PROC FAR
; Push all registered involved in the emulation. The order of the push
; was selected to allow easy exchange of stacks.
push BP
mov BP,SP
push AX
push ES
push DI
push SI
push BX
push CX
push DX
; The stack of the calling process is now:
;
; param1 SP+26 BP+12
; param2 SP+24 BP+10
; param3 SP+22 BP+8
; return-addr-r3 CS SP+20 BP+6
; IP SP+18 BP+4
; return-addr-r2 IP SP+16 BP+2
; BP SP+14 BP
; AX SP+12 BP-2
; ES SP+10 BP-4
; DI SP+8 BP-6
; SI SP+6 BP-8
; BX SP+4 BP-10
; CX SP+2 BP-12
; DX SP BP-14
mov BX,FS:6 ; Get current thread ID. FS points to the LINFOSEG
shl BX,2 ;
add BX,2 ; BX points now to SEL part of the R2 TOS (Top Of Stack)
mov AX,SEG R2StacksInfo
mov ES,AX ;
mov AX,ES:[BX] ; AX contains the SEL of the R2 TOS
or AX,AX ; Verify that the thread has a R2 stack
jnz R2CheckIfCalledFromR2; If yes, skip creation of R2 Stack
;
; Create R2 Stack
;
push 512 ; Allocate stack with size of 512 bytes (same as OS/2)
push ES ; ES:BX contain the address of the Stack entry
push BX ;
push 0 ; Flags for SEG_NONSHARED
call DosAllocSeg ; Allocate a R2 stack segment
or AX,AX ; Check if the allocation succeeded
jz R2StackExists ; Yes.
pop DX ; No. What to do? Restore all registers and return...
pop CX ;
pop BX ;
pop SI ;
pop DI ;
pop ES ;
pop AX ;
pop BP ;
add SP,2 ; Ignore the return to the R2CallInfo entry
retf ; Just return...
;
; Check if we call a ring 2 function from within ring 2.
; If yes, no need to change stacks.
; AX contains the ring 2 stack selector
;
R2CheckIfCalledFromR2:
mov CX,SS ; Get current Stack Segment
cmp CX,AX ; Compare with the R2 SS
jne R2StackExists ; Not the same. Change stacks.
mov SI,[BP+2] ; SI contains a pointer to the R2BytesToCopy field
; in the call table entry
mov EAX,CS:[SI].R2CallInfo.R2EntryPoint ; EAX contains the Address of the R2 destination
pop DX ; Restore registers.
pop CX ;
pop BX ;
pop SI ;
pop DI ;
pop ES ;
push word ptr [BP] ; The old BP is going to be overwritten...
mov [BP],EAX ; Old BP and return-addr-r2 are overwritten.
pop BP ; Retsore Old BP from Stack
pop AX ;
retf ; return to the R2 code
;
; A R2 stack exists or was created
;
R2StackExists:
;
; The calls to DosXXX preserve all registers except AX,
; so BX was not changed and we can use it
;
les DI,ES:[BX-2] ; ES:DI points to the top of the R2 stack
sub DI,4+4+16 ; allocate place for old SS:SP, 2 zeroes and 16 free bytes
mov ES:[DI+6],SS ; Save old SS
mov BX,BP ;
add BX,4 ; 8-4 (the 4 in the R3 stack is used to move there
; the return address to the function in R3 that
; called the R2 routine).
mov SI,[BP+2] ; SI contains a pointer to the R2BytesToCopy field
; in the call table entry
mov EDX,CS:[SI].R2CallInfo.R2EntryPoint ; EDX contains the Address of the R2 destination
movzx CX,CS:[SI].R2CallInfo.R2BytesToCopy ; CX contains the # of bytes to copy
jcxz no_params
mov AX,CX ; save a copy of the number of parameters
add BX,CX ; Assume as if the R3 params were removed
mov ES:[DI+4],BX ; Save old sp
mov word ptr ES:[DI],0 ; Clear the R3 access trap catch bytes
mov word ptr ES:[DI+2],0 ;
sub DI,CX ; DI points to lowest address for parameters of
; the R2 stack
lea SI,[BP+8] ; source of the copy in the R3 stack
cld ;
shr CX,1
rep movs word ptr ES:[DI],SS:[SI] ; copy all the arguments
sub DI,AX ; DI now points to the begining of the lowest R2
; arguments
jmp short params_copied
no_params:
xor AX,AX ; Remember number of parameters
mov ES:[DI+4],BX ; Save old sp (no need to add CX)
mov word ptr ES:[DI],0 ; Clear the R3 access trap catch bytes
mov word ptr ES:[DI+2],0 ;
;
; The R2 stack looks now like this:
;
; <16 free bytes>
; Old SS
; Old SP
; 0
; 0
; param1
; param2
; param3 <- ES:DI
;
params_copied:
sub DI,16 ; DI points at the lowest parameter on the R2 stack
mov ES:[DI+14],CS ; save return address from R2 procedure to this code
mov ES:[DI+12], OFFSET R2CodeRet
mov ES:[DI+8],EDX ; put on the R2 stack the address of the R2 procedure
;
; The R2 stack looks now like this:
;
; <16 free bytes>
; Old SS
; Old SP
; 0
; 0
; param1
; param2
; param3
; CS of this code DI+14
; Offset of R2CodeRet DI+12
; ring-2-entry CS DI+10
; IP DI+8
; free DI+6
; free DI+4
; free DI+2
; free <- ES:DI
;
;
; copy the R3 return address over the first parameter pushed onto the R3 stack
;
mov EDX,[BP+4] ; old CS:IP
mov BX,BP ;
add BX,AX ; Add # of parameter bytes
mov SS:[BX+4],EDX ; save return address over first parameters
;
; The R3 stacks looks now like this:
;
; return-addr-r3 CS SP+26 BP+12
; return-addr-r3 IP SP+24 BP+10
; param3 SP+22 BP+8
; return-addr-r3 CS SP+20 BP+6
; IP SP+18 BP+4
; return-addr-r2 IP SP+16 BP+2
; BP SP+14 BP
; AX SP+12 BP-2
; ES SP+10 BP-4
; DI SP+8 BP-6
; SI SP+6 BP-8
; BX SP+4 BP-10
; CX SP+2 BP-12
; DX SP BP-14
;
pop DX ;
pop CX ;
pop BX ;
pop SI ;
mov EAX,[BP-2] ; EAX contains original BP:AX
mov ES:[DI+4],EAX ;
mov EAX,[BP-6] ; EAX contains original ES:DI
mov ES:[DI],EAX ;
;
; The R2 stack looks now like this:
;
; <16 free bytes>
; Old SS
; Old SP
; 0
; 0
; param1
; param2
; param3
; free
; free
; ring-2-entry CS
; IP
; original BP
; original AX
; original ES
; original DI <- ES:DI
;
mov SP,DI ; exchange the stack to the R2 stack
mov AX,ES ;
mov SS,AX ;
pop DI ; Restore all registers
pop ES ;
pop AX ;
pop BP ;
;
; The R2 stack looks now like this:
;
; <16 free bytes>
; Old SS
; Old SP
; 0
; 0
; param1
; param2
; param3
; CS of this code
; Offset of R2CodeRet from beginning of segment
; ring-2-entry CS
; IP <- SS:ESP
;
retf ; call the R2 actual code
;
; Here we return from the R2 code
;
R2CodeRet:
push BP
mov BP,SP
push AX
push ES
push DI
mov DI,FS:6 ; Index of thread
shl DI,2
mov AX, SEG R2StacksInfo
mov ES,AX
les DI,ES:[DI] ; ES:DI point to Top of R2 stack
les DI,ES:[DI-(4+16)] ; ES:DI contain the current R3 stack pointer
sub DI,8 ; DI points below return address to R3
mov EAX,[BP-2] ; move using EAX old BP:AX from R2 stack to R3 stack
mov ES:[DI+4],EAX ;
mov EAX,[BP-6] ; move using EAX ES:DI from R2 stack to R3 stack
mov ES:[DI],EAX ;
mov [BP],ES ;
mov [BP-2],DI ;
lss SP,[BP-2] ;
pop DI ;
pop ES ;
pop AX ;
pop BP ;
retf
IF ($-CommonR2Xfer) GT 200H
;
; The size of the code segment exceeds 200H
; The ldr assumes that it is not greater than 200H
; If you have reached here and increased the value above 200H
; you have to modify also the OS/2 SS file ldr\ldrinit.c
;
.ERR SizeOfSegmentExceeds200H
ENDIF
CommonR2Xfer ENDP
;
; Next statement is used to make the size of the segment exactly 64K
; It causes the linker to issue warning L4020:
;
; segment: code segment size exceeds 64K-36
;
; Ignore the warning!
;
ORG 0FFFFH ; This statement causes the linker to create
; a segment virtual size of 64K
.DATA
R2StacksInfo R2Stacks 256 DUP (<>)
.CODE THUNK16
PUBLIC DOSCALLBACK
DOSCALLBACK PROC FAR
; The ring 2 stack upon entry:
;
; Target Callee (segment)
; Target Callee (offset)
; Ret Address (segment)
; Ret Address (offset) <- SP
;
;
; Move to ring 3 and change the stack to ring 3 stack
;
; Preserve reDIsters accross the call
push BP
mov BP,SP
push AX
push ES
push DI
push DS
push SI
; R2 stack is now:
;
; Target Callee (segment) BP+8
; Target Callee (offset) BP+6
; Ret Address (segment) BP+4
; Ret Address (offset) BP+2
; old BP <- BP
; AX
; ES
; DI
; DS
; SI <- SP
mov DI,FS:6 ; Get current thread ID. FS points to the LINFOSEG
shl DI,2 ; DI points now to SEL:OFF of the R2 TOS (Top Of Stack)
mov AX,SEG R2StacksInfo
mov ES,AX ;
lds SI,ES:[DI] ; DS:SI contain the R2 TOS
mov AX,SS
cmp AX,DS:[SI-18] ; Check if we are on the R3 ring
jne doscallback_R2
; The current ring is R3
pop SI
pop DS
pop DI
pop ES
pop AX
call dword ptr [BP+6]
pop BP
retf 4
doscallback_R2:
push SI ; Save OS2 TOS
lds SI,DS:[SI-20] ; DS:SI contain the R3 current stack
mov ES:[DI],BP ; Set a new ring 2 TOS for ring 3 -> ring 2
add word ptr ES:[DI],4 ; transitions.
;
; set a return address to the DosRetForward() API
;
sub SI,10 ; Allocate space on the ring 3 stack
mov AX, SEG DosRetForward
mov DS:[SI+8],AX
mov AX,OFFSET DosRetForward
mov DS:[SI+6],AX
mov AX,[BP+8] ; Target Callee (segment)
mov DS:[SI+4],AX ;
mov AX,[BP+6] ; Target Callee (offset)
mov DS:[SI+2],AX ;
mov AX,[BP] ; old BP
mov DS:[SI],AX ;
;
; move the R2 return address to a new location on the R2 stack
; so that we can return back to the R2 function that called DosCallBack()
;
mov AX,[BP+4] ; Ret address (segment)
mov [BP+8],AX ;
mov AX,[BP+2] ; Ret address (offset)
mov [BP+6],AX ;
;
; Save old R2 TOS in the R2 stacks data structure
;
pop word ptr [BP+4]
;
; save ring 3 SS:SP for task switch (done by the LSS instruction below)
;
mov [BP],SI
mov [BP+2],DS
pop SI ; Restore saved reDIster
pop DS
pop DI
pop ES
pop AX
lss SP,[BP] ; switch stacks
pop BP
retf ; perform call to user routine in ring 3
DOSCALLBACK ENDP
DOSRETFORWARD PROC FAR
sub SP,2 ; allocate space on the ring 3 stack
push BP
mov BP,SP
push AX
push ES
push DI
push DS
push SI
mov DI,FS:6 ; Get current thread ID. FS points to the LINFOSEG
shl DI,2 ; DI points now to SEL:OFF of the R2 TOS (Top Of Stack)
mov AX,SEG R2StacksInfo
mov ES,AX ;
lds SI,ES:[DI] ; DS:SI contain the R2 TOS
mov AX,DS:[SI] ; ring 2 prev TOS
mov ES:[DI],AX
mov AX,[BP] ; Get old BP and save it on the ring 2 stack
mov DS:[SI],AX
;
; save ring 2 SS:SP for task switch (done by the LSS instruction below)
;
mov [BP+2],DS
mov [BP],SI
pop SI
pop DS
pop DI
pop ES
pop AX
lss SP,[BP]
pop BP
retf ; return to the ring 2 routine that called DosCallBack()
DOSRETFORWARD ENDP
END