;*************************************************************************** ;* REBOOT.ASM ;* ;* Contains routines used to support a local reboot in the Sys ;* VM. To do this, we interact with USER and the Reboot VxD. ;* ;* This functionality is only present in the 386 KERNEL. ;* ;* The KRebootInit function is called just after USER is loaded. ;* ;* The Reboot VxD calls the local reboot proc to terminate an app. ;* The local reboot proc may fail and cause the VxD to trace until ;* it is able to kill the task. See comments in the local reboot ;* proc for more details. ;* ;* This code must be located in KERNEL due to the internal KERNEL ;* information it must access. ;* ;* Created by JonT starting 12 July 1991 ;* ;*************************************************************************** TITLE REBOOT - Local Reboot Routines .xlist include kernel.inc include tdb.inc include protect.inc include newexe.inc .list .386p MAX_TRACE EQU 5000h HookInt1 MACRO mov ax,0204h ;;Tell VxD to hook/unhook Int 1 mov cx,cs ;;Point to ISR mov dx,codeOFFSET TraceOut movzx edx,dx call [lpReboot] ;;Call the VxD API ENDM UnhookInt1 MACRO mov ax,0204h ;;Tell VxD to hook/unhook Int 1 xor cx,cx ;;Pass a zero in CX to unhook call [lpReboot] ;;Call the VxD API ENDM DataBegin externD lpReboot externW curTDB externW hExeHead externW hUser externB Kernel_Flags externB fTaskSwitchCalled externW wMyOpenFileReent externB OutBuf externW pGlobalHeap EVEN globalW wLastSeg, 0 globalW wTraceCount, 0 lpOldInt1 DD 0 szUser DB 'USER' DataEnd externFP Int21Handler externFP GetPrivateProfileInt IFDEF FE_SB externFP FarMyIsDBCSLeadByte ENDIF ;** Note that this goes in the fixed code segment sBegin CODE assumes CS,CODE ; KRebootInit ; Initializes the KERNEL Local Reboot functionality and talks with ; the Reboot VxD. cProc KRebootInit, , cBegin SetKernelDS ;** Get the reboot device entry point if it exists xor di,di ;Get a NULL pointer mov es,di ; in case there's no reboot device mov bx,0009h mov ax,1684h ;Get device entry point int 2fh mov ax,es or ax,di jz SHORT RI_NoRebootDev mov WORD PTR lpReboot[0],di mov WORD PTR lpReboot[2],es ;** Set the reboot device call back mov ax, 0201h ;Reboot VxD #201: Set callback addr mov di,cs mov es,di lea di,KRebootProc lea si, fTaskSwitchCalled ;DS:SI points to task switch flag call [lpReboot] RI_NoRebootDev: cEnd ; LocalRebootProc ; ; Called by the Reboot VxD to cause the current Windows app to be ; terminated. cProc KRebootProc, cBegin nogen ;** Save all the registers so we can restart if necessary pusha ;16 bytes push ds ;2 bytes push es ;2 bytes SetKernelDS mov bp,sp ;Point with BP ;** If the KERNEL debugger is installed, DON'T trace! test Kernel_Flags[2],KF2_SYMDEB ;Debugger installed IF KDEBUG jz SHORT RP_CheckSeg ;No debugger, try to trace Trace_Out <'LocalReboot: Debugger installed, nuking app without tracing'> jmp SHORT RP_NukeIt ;Just try to nuke it here! ELSE jnz SHORT RP_NukeIt ;Just try to nuke it here! ENDIF ;** Get the code segment we were executing in RP_CheckSeg: mov ax,[bp + 22] ;Get CS from stack (IRET frame) ;** See if the owner of the code segment was loaded before USER cCall IsBootSeg ;Returns TRUE if owned by boot mod or ax,ax ;Ok to nuke? jnz RP_TraceOut ;No, must trace out RP_NukeIt: ;** First, we need to get the filename of the .EXE we're about to ;** nuke. We do this by getting the name out of the module ;** database. Module databases are not page locked and also ;** have the fully qualified pathname. So, we copy just the ;** module name into a buffer in our pagelocked data segment cld push ds pop es mov di, dataOFFSET OutBuf ;Point to start of buffer mov ds, curTDB ;Get the current TDB UnSetKernelDS mov ds, ds:[TDB_pModule] ;Point to the module database mov si, WORD PTR ds:[ne_crc + 2] ;Points to length byte of module EXE mov cl, [si] ;Get length byte xor ch, ch sub cl, 8 ;8 bytes of garbage add si, 8 mov bx, si ;In case we don't find a slash (bad) RP_SlashLoop: lodsb ;Get this char IFDEF FE_SB call FarMyIsDBCSLeadByte jc SHORT RP_NoDBCSChar lodsb dec cx jmp SHORT RP_NoSlash ;It is, can't be a slash RP_NoDBCSChar: ENDIF cmp al, '\' ;Is this a slash? je SHORT RP_Slash ;Yes cmp al, '/' jne SHORT RP_NoSlash RP_Slash: mov bx, si ;BX points after last slash RP_NoSlash: loop RP_SlashLoop mov cx, si ;Compute count of characters in 8.3 name sub cx, bx mov si, bx ;Point to 8.3 name rep movsb ;Copy the string into OutBuf xor al, al ;Zero byte stosb ;** Call the VxD to put up the app name push es ;ES points to kernel DS pop ds ReSetKernelDS mov di, dataOFFSET OutBuf ;Point to module name with ES:DI mov ax, 203h ;Display message through VxD call [lpReboot] or ax, ax ;If non-zero, we nuke it jz SHORT RP_NoNuke IF KDEBUG krDebugOut DEB_WARN, <'LocalReboot: Trying to nuke @ES:DI'> ENDIF ;** Clean out some static info mov wMyOpenFileReent, 0 ;Clear reentrant flag for MyOpenFile ;** Call USER's signal proc for the task mov es,curTDB ;Get the current TDB cmp WORD PTR es:[TDB_USignalProc] + 2,0 ;USER signal proc? jz SHORT @F ;No mov bx,0666h ;Death knell mov di,-1 cCall es:[TDB_USignalProc], @@: ;** Nuke the app. Does not return mov ax,4c00h DOSCALL ;** We're somewhere in a boot module. Try to trace out by telling ;** VxD to do another instruction RP_TraceOut: mov ax,[bp + 22] ;Get CS from stack (IRET frame) IF KDEBUG krDebugOut DEB_WARN, <'LocalReboot: Tracing out of boot module %AX2'> ENDIF mov wLastSeg,ax ;Save for next time mov wTraceCount,0 ;Clear the trace count HookInt1 ;** Set the trace flag or WORD PTR [bp + 24],0100h jmp SHORT RP_SetFlag ;** Force the trap flag clear on the no nuke case RP_NoNuke: and WORD PTR [bp + 24],NOT 0100h RP_SetFlag: pop es pop ds popa STIRET ;VxD calls as an interrupt cEnd nogen ; TraceOut ; ; This routine continues to trace until it traces out of a boot module ; or until the trace count is up. If it needs to nuke the app, ; it jumps to RP_NukeIt which depends only on DS being set. This ; call returns via an IRET since it is called as an INT 1 handler. cProc TraceOut, cBegin nogen ;** Save all the registers so we can restart if necessary pusha ;16 bytes push ds ;2 bytes push es ;2 bytes SetKernelDS mov bp,sp ;Point with BP ;** We keep tracing forever if the heap is locked push es mov es, pGlobalHeap ;Point to GlobalInfo structure cmp es:[gi_lrulock], 0 ;Is the global heap busy pop es jne SHORT TO_10 ;Force the trace out ;** See if the CS is the same as last time inc wTraceCount ;Bump the count cmp wTraceCount,MAX_TRACE ;Too many instructions? IF KDEBUG jb SHORT TO_10 ;Count not exceeded mov cx,MAX_TRACE ;Get trace count Trace_Out <'LocalReboot: Trace count (#CXh instructions) exceeded'> jmp SHORT TO_Reboot ELSE jae SHORT TO_Reboot ;Yes, too many, nuke it ENDIF TO_10: mov ax,[bp + 22] ;Get the CS from the IRET frame cmp ax,wLastSeg ;Same as last time? je SHORT TO_StillBad ;Yes, don't bother looking up mov wLastSeg,ax ;Save as last executed segment mov bx,cs ;Get our CS cmp ax,bx ;Our segment? je SHORT TO_StillBad ;Yes, can't nuke here! cCall IsBootSeg ;Returns TRUE if owned by boot mod or ax,ax ;Ok to nuke? jnz SHORT TO_StillBad ;No, must continue tracing IF KDEBUG mov cx,wTraceCount ;Get trace count Trace_Out <'LocalReboot: Traced out after #CXh instructions'> ENDIF ;** Unhook the interrupt handler and kill the app now TO_Reboot: UnhookInt1 jmp RP_NukeIt ;Try to nuke the app ;** Restore the registers and restart TO_StillBad: or WORD PTR [bp + 24],0100h ;Set the trace flag pop es pop ds popa iret ;VxD calls as an interrupt cEnd nogen ; IsBootSeg ; ; Tries to find a code segment somewhere in the initial segments ; loaded before USER. The CS value is passed in AX. Returns ; TRUE iff the CS was found in a boot module. cProc IsBootSeg, cBegin nogen SetKernelDS mov dx,ax ;Put CS in DX for call mov es,hExeHead ;Get the first module on chain IBS_Loop: cCall IsModuleOwner ;See if we can find the owner or ax,ax ;Found? jnz SHORT IBS_End ;Yes, return TRUE mov ax,es ;Get the module handle cmp ax,hUser ;If we just tried USER, we're done je SHORT IBS_NotFound ;Not found mov es,es:[6] ;Nope, get next module to try jmp IBS_Loop IBS_NotFound: xor ax,ax ;Return FALSE IBS_End: cEnd ; IsModuleOwner ; ; Checks in the EXE header to see if the given segment is in this ; module. ; DX is the segment, ES points to the module database ; Returns TRUE/FALSE in AX. Doesn't trash DX. cProc IsModuleOwner, cBegin nogen xor ax,ax ;Get a FALSE just in case mov cx,es:[ne_cseg] ;Get max number of segments jcxz SHORT IMO_End ;No segments mov di,es:[ne_segtab] ;Point to the segment table IMO_SegLoop: cmp dx,es:[di].ns_handle ;Is this the correct segment entry? jz SHORT IMO_FoundIt ;Yes, get out add di,SIZE new_seg1 ;Bump to next entry loop IMO_SegLoop ;Loop back to check next entry jmp SHORT IMO_End ;Didn't find it IMO_FoundIt: mov ax,1 ;Return that we found it here IMO_End: cEnd sEnd END