_OPENING OS/2'S BACKDOOR_ by Andrew Schulman [LISTING ONE] ; DEVHLP.ASM ; to produce DEVHLP.SYS ("OS/2 Device Driver in a Can") ; Andrew Schulman, 32 Andrew St., Cambridge MA 02139 ; with revisions by Art Rothstein, Morgan Labs, San Francisco, CA ; DEF file: ; LIBRARY DEVHLP ; DESCRIPTION 'DEVHLP.SYS (c) Andrew Schulman 1990' ; PROTMODE ; masm devhlp; && link devhlp,devhlp.sys,,,devhlp.def ; put in OS/2 config.sys: ; device=devhlp.sys ; access with DosOpen ("DEVHLPXX"), DosDevIOCTL (category 128, func 60h) .286p ; the only specific DevHlp that DEVHLP.SYS knows about VerifyAccess equ 27h ioctlpkt struc db 13 dup (?) ; header cat db ? ; category fun db ? ; function param dd ? ; param area dataptr dd ? ; data area ioctlpkt ends regs struc regs_ax dw ? regs_bx dw ? regs_cx dw ? regs_dx dw ? regs_si dw ? regs_di dw ? regs_ds dw ? regs_es dw ? regs_flags dw ? regs ends regs_size equ size regs dgroup group _DATA _DATA segment word public 'DATA' header dd -1 dw 8880h dw Strat dw 0 db 'DEVHLPXX' db 8 dup (0) DevHlp dd 0 dispch dw Init ; 0 -- Init dw 12 dup (Error) ; 1..12 -- not supported dw DevOp ; 13 -- DevOpen dw DevOp ; 14 -- DevClose dw Error ; 15 -- not supported dw GenIOCtl ; 16 -- DevIOCtl dw 10 dup (Error) ; 17..26 -- not supported enddata dw 0 _DATA ends _TEXT segment word public 'CODE' assume cs:_TEXT, ds:DGROUP, es:NOTHING Strat proc far mov di, es:[bx+2] and di, 0ffh cmp di, 26 ; max # of commands jle Strat1 call Error jmp short Strat2 Strat1: add di, di call word ptr [di+dispch] Strat2: mov word ptr es:[bx+3], ax ; set request header status ret Strat endp ; used by DevOpen and DevClose DevOp proc near mov ax, 0100h ret DevOp endp GenIOCtl proc near push es push bx cmp es:[bx].cat, 128 jne bad cmp es:[bx].fun, 60h jne bad call Do_DevHlp jc bad mov ax, 0100h ; no error jmp short done bad: mov ax, 8101h ; error done: pop bx pop es ret GenIOCtl endp Do_DevHlp proc near ; verify user's access: ; VerifyAccess will shut down user's app in the event of error mov ax, word ptr es:[bx+17] ; selector of parameter block mov di, word ptr es:[bx+15] ; offset mov cx, regs_size ; length to be read mov dx, VerifyAccess ; read call DevHlp jnc ok1 ret ok1: mov ax, word ptr es:[bx+21] ; selector of data buffer mov di, word ptr es:[bx+19] ; offset mov cx, regs_size ; length to be written mov dx, (1 SHL 8) + VerifyAccess ; read/write call DevHlp jnc ok2 ret ok2: push ds ; see if we should verify ds lds di, es:[bx].param mov ax, [di].regs_ds pop ds test ax, ax ; need to verify? je nods ; skip if no xor di, di ; verify seg:0 for read, 1 byte mov cx, 1 ; length mov dx, VerifyAccess ; read=0 call DevHlp jc fini ; if carry flag set nods: push ds ; see if we should verify es lds di, es:[bx].param mov ax, [di].regs_es pop ds test ax, ax ; need to verify? je noes ; skip if no xor di, di ; verify seg:0 for read, 1 byte mov cx, 1 ; length mov dx, VerifyAccess ; read=0 call DevHlp jc fini ; if carry flag set noes: push ds ; going to be bashed! push es push bx ; save DevHlp address on stack so we can change ds push word ptr DevHlp+2 push word ptr DevHlp ; get the parameters for DevHlp from regs lds di, es:[bx].param mov ax, [di].regs_ax mov bx, [di].regs_bx mov cx, [di].regs_cx mov dx, [di].regs_dx mov si, [di].regs_si mov es, [di].regs_es push [di].regs_ds mov di, [di].regs_di pop ds ; here it is, the whole point of this exercise! mov bp, sp call dword ptr [bp] pop bp ; pull DevHlp address off stack pop bp ; without changing carry flag jc fini ; save ES:BX to put in out-regs: destroys DX mov bp, es mov dx, bx ; get back old DS, ES:BX pop bx pop es pop ds ; save FLAGS, SI, DS on stack pushf push si push ds ; set up regs to return to the app lds si, es:[bx].dataptr mov [si].regs_ax, ax pop [si].regs_ds pop [si].regs_si pop [si].regs_flags mov [si].regs_cx, cx mov [si].regs_bx, dx mov [si].regs_es, bp mov [si].regs_di, di clc fini: ret Do_DevHlp endp Error proc near mov ax, 8103h ret Error endp Init proc near mov ax, es:[bx+14] mov word ptr DevHlp, ax mov ax, es:[bx+16] mov word ptr DevHlp+2, ax mov word ptr es:[bx+14], offset _TEXT:Init ; end of code mov word ptr es:[bx+16], offset DGROUP:enddata mov ax, 0100h ret Init endp _TEXT ends end [LISTING TWO] /* DRVRLIST.C list the device drivers in OS/2 Art Rothstein, 1990 we assume the first driver in the chain is NUL and is in the global data segment, and that the second driver (CON) is the same segment. cl -AL drvrlist.c (four-byte data pointers required for memchr) */ #define INCL_DOSDEVICES #include #include #include #include #include "devhlp.h" USHORT devhlp ; SEL MakeSel( SEL selValue) { extern USHORT devhlp ; REGS regs ; USHORT ret ; regs.dx = DevHlp_VirtToPhys ; // function requested regs.ds = selValue ; // selector regs.es = 0 ; // avoid trap regs.si = 0 ; // offset ret = DosDevIOCtl( ®s, ®s, 0x60, 128, devhlp) ; if ( ret != 0 || regs.flags.carry != 0) return 0 ; // physical address in ax:bx regs.cx = 0 ; // limit 65,535 regs.dx = MAKEUSHORT( DevHlp_PhysToUVirt, UVirt_ReadWrite); regs.es = 0 ; // avoid trap ret = DosDevIOCtl( ®s, ®s, 0x60, 128, devhlp) ; if ( ret != 0 || regs.flags.carry != 0) // if error return 0 ; return regs.es ; // return the selector } BOOL ReleaseSel( SEL selValue) { extern USHORT devhlp ; REGS regs ; USHORT ret ; regs.ax = selValue ; // selector to free regs.dx = MAKEUSHORT( DevHlp_PhysToUVirt, UVirt_Release); regs.ds = 0 ; // safety regs.es = 0 ; ret = DosDevIOCtl( ®s, ®s, 0x60, 128, devhlp) ; if ( ret != 0 || regs.flags.carry != 0) // if error return FALSE ; return TRUE ; // successful return } void main( void) { USHORT usOffsetDriver , usBytesLeft; // in search for NUL device PCH pchGlobal ; // pointer to system global data static CHAR szDriverName[] = "DEVHLPXX" // device helper driver , szNullDriver[] = "NUL "; // first driver in system typedef struct _DDHEADER { // device driver header struct _DDHEADER * pddNext ; // chain to next driver USHORT fsAttribute ; // driver attributes USHORT usStrategyEntryOffset ; USHORT usIDCEntryOffset ; // inter device communication CHAR chName[ 8] ; // name for character devices USHORT usIDCEntrySegmentProt ; USHORT usIDCDataSegmentProt ; USHORT usIDCEntrySegmentReal ; USHORT usIDCDataSegmentReal ; } DDHEADER ; typedef DDHEADER * PDDHEADER ; PDDHEADER pddCurrent // current DCB , pddNext; // next DCB SEL selDriver ; // selector of DCB // open the DEVHLP device if ((devhlp = open(szDriverName, 0)) == -1) { puts( "Can't find DEVHLP.SYS") ; exit( 1) ; } // locate the first driver selDriver = 0x50 ; // global data segment usOffsetDriver = 0 ; usBytesLeft = 32000 ; // should be large enough pchGlobal = MAKEP( MakeSel( selDriver), usOffsetDriver) ; do { PCH pchMatch ; pchMatch = memchr( pchGlobal + 1, 'N', usBytesLeft); //look for first char if ( pchMatch == NULL) { // if no match ReleaseSel( SELECTOROF( pchGlobal)) ; // release the selector puts( "NUL driver not found") ; // and give up exit( 1) ; } // if no match // partial match usBytesLeft -= pchMatch - pchGlobal ; // reduce residual count pchGlobal = pchMatch ; // point to start of match } while ( memcmp( pchGlobal // break out if name matches , szNullDriver // exactly , sizeof szNullDriver - 1) != 0); // run the chain printf( " Address Name\n") ; // column headings for ( usOffsetDriver = OFFSETOF( pchGlobal) - 0x0a // back up to DCB start , pddCurrent = ( PDDHEADER) ( pchGlobal - 0x0a) , selDriver = SELECTOROF( pddCurrent->pddNext) // selector of next DCB ; ; ) { printf( "%4X:%04X ", selDriver, usOffsetDriver); if ( ( pddCurrent->fsAttribute & 0x8000) == 0) // if block driver printf( "Block device, %d logical units\n" , pddCurrent->chName[ 0]); // number of units else // if character driver printf( "%-8.8s\n", pddCurrent->chName); selDriver = SELECTOROF( pddCurrent->pddNext) ; // point to next DCB usOffsetDriver = OFFSETOF( pddCurrent->pddNext) ; if ( usOffsetDriver == 0xffff) // if end of chain break ; // we are done pddNext = MAKEP( MakeSel( selDriver), usOffsetDriver) ; ReleaseSel( SELECTOROF( pddCurrent)) ; // free previous DCB pddCurrent = pddNext ; // age the pointer } // loop once for each device driver // release the last selector ReleaseSel( SELECTOROF( pddCurrent)) ; exit( 0) ; } [LISTING THREE] /* DEVHLP.H -- for use with DosDevIOCtl and DEVHLP.SYS */ #define DevHlp_SchedClockAddr 0x00 #define DevHlp_DevDone 0x01 #define DevHlp_Yield 0x02 #define DevHlp_TCYield 0x03 #define DevHlp_Block 0x04 #define DevHlp_Run 0x05 #define DevHlp_SemRequest 0x06 #define DevHlp_SemClear 0x07 #define DevHlp_SemHandle 0x08 #define DevHlp_PushReqPacket 0x09 #define DevHlp_PullReqPacket 0x0A #define DevHlp_PullParticular 0x0B #define DevHlp_SortReqPacket 0x0C #define DevHlp_AllocReqPacket 0x0D #define DevHlp_FreeReqPacket 0x0E #define DevHlp_QueueInit 0x0F #define DevHlp_QueueFlush 0x10 #define DevHlp_QueueWrite 0x11 #define DevHlp_QueueRead 0x12 #define DevHlp_Lock 0x13 #define DevHlp_Unlock 0x14 #define DevHlp_PhysToVirt 0x15 #define DevHlp_VirtToPhys 0x16 #define DevHlp_PhysToUVirt 0x17 #define DevHlp_AllocPhys 0x18 #define DevHlp_FreePhys 0x19 #define DevHlp_SetROMVector 0x1A #define DevHlp_SetIRQ 0x1B #define DevHlp_UnSetIRQ 0x1C #define DevHlp_SetTimer 0x1D #define DevHlp_ResetTimer 0x1E #define DevHlp_MonitorCreate 0x1F #define DevHlp_Register 0x20 #define DevHlp_DeRegister 0x21 #define DevHlp_MonWrite 0x22 #define DevHlp_MonFlush 0x23 #define DevHlp_GetDosVar 0x24 #define DevHlp_SendEvent 0x25 #define DevHlp_ROMCritSection 0x26 #define DevHlp_VerifyAccess 0x27 #define DevHlp_SysTrace 0x28 #define DevHlp_AttachDD 0x2A #define DevHlp_AllocGDTSelector 0x2D #define DevHlp_PhysToGDTSelector 0x2E #define DevHlp_RealToProt 0x2F #define DevHlp_ProtToReal 0x30 #define DevHlp_EOI 0x31 #define DevHlp_UnPhysToVirt 0x32 #define DevHlp_TickCount 0x33 #define DevHlp_GetLIDEntry 0x34 #define DevHlp_FreeLIDEntry 0x35 #define DevHlp_ABIOSCall 0x36 #define DevHlp_ABIOSCommonEntry 0x37 #define DevHlp_RegisterStackUsage 0x38 #define UVirt_Exec 0 #define UVirt_ReadWrite 1 #define UVirt_Release 2 #pragma pack(1) typedef struct { unsigned int carry : 1; unsigned int : 1; unsigned int parity : 1; unsigned int : 1; unsigned int aux : 1; unsigned int : 1; unsigned int zero : 1; unsigned int sign : 1; unsigned int trap : 1; unsigned int int_en : 1; unsigned int direction : 1; unsigned int overflow : 1; unsigned int iopl : 2; unsigned int nest_task : 1; unsigned int : 1; } FLAGS; typedef struct { USHORT ax,bx,cx,dx,si,di,ds,es; FLAGS flags; } REGS; [Figure 1º Invoking the PhysToUVirt DevHlp] MOV AX, address_high ; top of 32-bit physical absolute address MOV BX, address_low ; bottom of 32-bit physical absolute address MOV CX, length ; count of bytes to map (0=64k) MOV DH, request_type ; 0=code, 1=data, 2=cancel MOV DL, DevHlp_PhysToUVirt ; 17h CALL DWORD PTR [DevHlp] JNC ok ; carry set=error, clear=ok ; AX contains error code ok: ; ES:BX contains virtual address [Figure 2º Parameters for calling DosDevIOCtl()] USHORT DosDevIOCtl(pvData, pvParms, usFunction, usCategory, hDevice) PVOID pvData; /* far pointer to data packet <- driver */ PVOID pvParms; /* far pointer to parameter packet -> driver */ USHORT usFunction; /* two-byte device function */ USHORT usCategory; /* two-byte device category */ HFILE hDevice; /* two-byte device handle */ [Figure 3º The DEVHLP parameter/data packet] typedef struct { USHORT ax, bx, cx, dx, si, di, ds, es, flags; } REGS; [Figure 4º PhysToUVirt() in C] #define DevHlp_PhysToUVirt 0x17 typedef enum { UVirt_Exec=0, UVirt_ReadWrite, UVirt_Release } UVIRT_TYPE; // turn physical address into virtual address void far *PhysToUVirt(ULONG addr, USHORT size, UVIRT_TYPE type) { REGS r; USHORT sel, ret=1; HFILE devhlp; r.ax = HIUSHORT(addr); r.bx = LOUSHORT(addr); r.cx = size; r.si = r.di = r.ds = r.es = 0; // not used r.dx = MAKEUSHORT(DevHlp_PhysToUVirt, type); if ((devhlp = open("DEVHLPXX", 0)) != -1) { ret = DosDevIOCtl(&r, &r, 0x60, 128, devhlp); close(devhlp); } // if DosDevIOCtl failed OR if DevHlp set carry flag... if (ret || (r.flags & 1)) return NULL; else return MAKEP(r.es, r.bx); }