;; lfnload.asm -- lfnsrv loader ;; Copyright (C) 1999-2000 Andrew Crabtree, Wojciech Galazka ;; ;; Original code written by Andrew Crabtree, 1997-1999 ;; ;; Code od strcpy procedure based on the code from ;; The UCR Standard Library for Assembly Language Programmers ;; Written By Randall Hyde and others ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software Foundation, ;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ;; ;; This is the 16 bit DOS TSR portion of the Long File Name API on Windows NT ;; It will load the NT dll, hook Int 0x21, and then terminate and stay ;; resident Then it snoops int 21. Any LFN calls will be handled here, and ;; redirected to the DLL. Some calls need special processing in ;; both the DLL and here. Some non LFN calls are also processed ;; ;; ";;" marks comments ;; ";;;;" meens code that has beem found unneccessary ;; ;; The code is not optimized, but it still fits well below 3 kB so who cares? ;; If you have any ideas how the 600 bytes buffer space can be used for ;; drop me a line ;; JUMPS ;; BOP Macros from Microsoft NT DDK Include File ISVBOP.H ;; See msdn.microsoft.com for a copy of NT 4.0 DDK VDDRegister macro db 0c4h,0c4h,058h,000h endm VDDDispatch macro db 0c4h,0c4h,058h,002h endm VDDUnregister macro db 0c4h,0c4h,058h,001h endm DISPLAY_STRING EQU 9h print macro text mov ah, DISPLAY_STRING mov dx, offset text int 21h endm dispatchh macro what, where cmp ah, what je where endm dispatch macro what, where cmp ax, what je where endm call_vdd macro .386 shl eax, 16 .8086 mov ax, [dllHandle] VDDDispatch endm RESET_DRIVE EQU 0Dh SET_DEFAULT_DRIVE EQU 0Eh GET_CURRENT_DRIVE EQU 19h SET_INTERRUPT_VECTOR EQU 25h GO_STAY_TSR EQU 31h GET_TRUE_VERSION_NUMBER EQU 3306h GET_INTERRUPT_VECTOR EQU 35h GET_DISK_FREE_SPACE EQU 36h SET_CURRENT_DIRECTORY EQU 3Bh W95_GET_COMPRESSED_FILE_SIZE EQU 4302h W95_EXT_LENGTH_FILENAME_OPERATIONS EQU 43FFh GET_CURRENT_DIRECTORY EQU 47h FREE_ENVIRONM_MEMORY EQU 49h TERMINATE_PROGRAM EQU 4Ch GET_SYSVARS_TABLE EQU 52h GET_CURRENT_PSP EQU 62h CREATE_OPEN_FILE EQU 6C00h GET_LAST_ACCESS_DATE EQU 5704h SET_LAST_ACCESS_DATE EQU 5705h GET_CREATION_DATE EQU 5706h SET_CREATION_DATE EQU 5707h LONG_RESET_DRIVE EQU 710Dh LONG_CREATE_DIRECTORY EQU 7139h LONG_REMOVE_DIRECTORY EQU 713Ah LONG_SET_CURRENT_DIRECTORY EQU 713Bh LONG_DELETE_FILE EQU 7141h LONG_GET_SET_FILE_ATTR EQU 7143h LONG_GET_CURRENT_DIRECTORY EQU 7147h LONG_FIND_FIRST EQU 714Eh LONG_FIND_NEXT EQU 714Fh LONG_RENAME_FILE EQU 7156h LONG_TRUENAME EQU 7160h LONG_CREATE_FILE EQU 716Ch LONG_GET_VOLUME_INFORMATION EQU 71A0h LONG_FIND_CLOSE EQU 71A1h LONG_FIND_NEXT_EXT EQU 71A2h LONG_RESERVED_1 EQU 71A3h LONG_RESERVED_2 EQU 71A4h LONG_RESERVED_3 EQU 71A5h LONG_GET_FILE_INFORMATION EQU 71A6h LONG_CONVERT_TIME EQU 71A7h LONG_GENERATE_SHORT_NAME EQU 71A8h LONG_SERVER_CREATE_FILE EQU 71A9h LONG_HANDLE_SUBST EQU 71AAh LFNSRV_API EQU 7171h LFNSRV_GET_LOADER_INFO EQU 0001h LFNSRV_GET_VERSION_INFO EQU 0001h LFNSRV_SET_SFT_INFO EQU 0003h LOADER_PRODUCTBUILD EQU 70 LOADER_MAGIC_VALUE EQU 4321h MAX_SIZE equ 260 seg1 segment byte 'code' assume cs:seg1, ds:seg1, es:seg1, ss:seg1 ORG 100h start proc far jmp start1 dllHandle dw 0 ; return value of Register Module, used by Dispatch oldint21h dd ? assume cs:seg1, ds:nothing, es:nothing, ss:nothing ;; ;; strcpy- Copies string pointed at by es:di to string pointed at by dx:si. ;; ;; inputs: ;; es:di- Zero-terminated source string. ;; dx:si- Buffer for destination string. ;; ;; outputs: es:di- Points at destination string. ;; es,ds,si,di,ax destroyed ;; ;; Note: The destination buffer must be large enough to hold the string and ;; zero terminating byte. ;; strcpy proc near push cx pushf cld xor al, al mov cx, 0ffffh push di repne scasb pop di neg cx mov ax, es mov ds, ax mov es, dx xchg si, di dec cx shr cx, 1 jnc CpyWrd lodsb stosb CpyWrd: rep movsw ; DidByte: popf pop cx ret strcpy endp interruptHandler proc far ;; EAX is not saved pushf dispatchh SET_DEFAULT_DRIVE , SetDefaultDrive dispatchh GET_DISK_FREE_SPACE , lfnfuncGeneric jb orginal dispatchh SET_CURRENT_DIRECTORY , SetCurDir dispatch W95_GET_COMPRESSED_FILE_SIZE , lfnfuncGeneric dispatch W95_EXT_LENGTH_FILENAME_OPERATIONS , lfnfuncGeneric dispatch GET_LAST_ACCESS_DATE , lfnfuncGetDate dispatch SET_LAST_ACCESS_DATE , lfnfuncSetDate dispatch GET_CREATION_DATE , lfnfuncGetDate dispatch SET_CREATION_DATE , lfnfuncSetDate dispatch LONG_RESET_DRIVE , lfnfuncResetDrive dispatch LONG_CREATE_DIRECTORY , lfnfuncGeneric dispatch LONG_REMOVE_DIRECTORY , lfnfuncGeneric dispatch LONG_SET_CURRENT_DIRECTORY , lfnfuncSetCurDir dispatch LONG_DELETE_FILE , lfnfuncGeneric dispatch LONG_GET_SET_FILE_ATTR , lfnfuncGeneric dispatch LONG_GET_CURRENT_DIRECTORY , lfnfuncGetCutDir dispatch LONG_FIND_FIRST , lfnfuncGeneric dispatch LONG_FIND_NEXT , lfnfuncGeneric dispatch LONG_RENAME_FILE , lfnfuncGeneric dispatch LONG_TRUENAME , lfnfuncGeneric dispatch LONG_CREATE_FILE , lfnfuncCreateFile dispatch LONG_GET_VOLUME_INFORMATION , lfnfuncGeneric dispatch LONG_FIND_CLOSE , lfnfuncGeneric dispatch LONG_FIND_NEXT_EXT , lfnfuncGeneric dispatch LONG_RESERVED_1 , lfnfuncGeneric dispatch LONG_RESERVED_2 , lfnfuncGeneric dispatch LONG_RESERVED_3 , lfnfuncGeneric dispatch LONG_GET_FILE_INFORMATION , lfnfuncGerFileInformation dispatch LONG_CONVERT_TIME , lfnfuncGeneric dispatch LONG_GENERATE_SHORT_NAME , lfnfuncGeneric dispatch LONG_SERVER_CREATE_FILE , lfnfuncCreateFile dispatch LONG_HANDLE_SUBST , lfnfuncGeneric dispatch LFNSRV_API , lfnSrvApi orginal: popf jmp [oldint21h] ; jump to DOS interrupt dispatch handler lfnfuncGeneric: popf call_vdd ret 2 lfnfuncGetDate: lfnfuncGerFileInformation: lfnfuncSetDate: ;; on call to vdd ;; BX = file handle ;; ES:DI = JFT ;; ;; additionally on GET_DATE calls ;; ECX = PDP ;; additionally on GET_FILE_INFO call ;; ECX = PDP ;; DS:DX = buffer ;; additionally on SET_DATE calls ;; right part of ECX = PDP ;; left part of ECX (==CX) = time ;; DX = date ;; popf cmp ax, SET_LAST_ACCESS_DATE je dont_zero_cx cmp ax, SET_CREATION_DATE je dont_zero_cx xor cx, cx dont_zero_cx: push ax push bx mov ah, GET_CURRENT_PSP pushf call [oldint21h] ;;BX=PSP segment mov ax, cx mov cx, bx mov es, bx mov di, es:[34h] mov es, es:[36h] ;;ES:DI = JFT .386 shl ecx, 16 .8086 mov cx, ax ;;ECX = PSPseg:Time pop bx ;;BX = file handle pop ax ;;function code call_vdd ret 2 lfnfuncResetDrive: popf mov ah, RESET_DRIVE pushf call [oldint21h] ; clc ret 2 SetCurDir: lfnfuncSetCurDir: popf ;; ds:dx points to the pathname push es push si push di push ds push dx push ax mov di, dx mov ax, ds mov es, ax mov ax, cs mov dx, ax mov si, offset set_dir_buffer call strcpy ;;dx:si <= es:di pop ax pop dx pop ds call_vdd jc bad_1 mov AH, SET_CURRENT_DIRECTORY pushf call [oldint21h] ; bad_1: push ds push dx push ax mov ax, cs mov es, ax mov di, offset set_dir_buffer mov si, dx mov dx ,ds call strcpy ;;dx:si <= es:di pop ax pop dx pop ds pop di pop si pop es ret 2 lfnfuncGetCutDir: popf mov ah, GET_CURRENT_DIRECTORY PUSHF call [oldint21h] jc bad or dl, dl jnz skip_drive mov ah, GET_CURRENT_DRIVE pushf call [oldint21h] jc bad mov dl, al add dl, 64d ;;40h skip_drive: mov ax, LONG_GET_CURRENT_DIRECTORY call_vdd ret 2 lfnfuncCreateFile: ;; WORD nMode = getBX(); ;; WORD nAttrs = getCX(); ;; WORD nAction = getDX(); ;; WORD nAlias = getDI(); ;; ULONG Address = MAKEADDRESS(getDS(), getSI()); popf push es push si push di push ds push dx push ax mov di, si mov ax, ds mov es, ax mov ax, cs mov dx, ax mov si, offset create_file_buffer call strcpy ;;dx:si <= es:di pop ax pop dx pop ds pop di pop si call_vdd jc bad_2 or ax, ax jz nofileoperation_2 ;;;; mov ax, CREATE_OPEN_FILE pushf call [oldint21h] ; bad_2: nofileoperation_2: push si push di push ds push dx push ax mov ax, cs mov es, ax mov di, offset create_file_buffer mov dx ,ds call strcpy ;;dx:si <= es:di pop ax pop dx pop ds pop di pop si pop es ret 2 SetDefaultDrive: ;; Sets default drive ;; ;; first call original dos function ;; then get default directory ;; the act as if you call SetDirectory for that drive ;; popf pushf call [oldint21h] ;set default drive cmp dl, al ja no_change push dx ;0=A, 1=B, 2=C push ax push ds push si mov ax, cs mov ds, ax mov si, offset set_dir_buffer mov al, dl add al, 'A' mov byte ptr ds:[si ], al mov byte ptr ds:[si+1], ':' mov byte ptr ds:[si+2], '\' mov byte ptr ds:[si+3], 0 add si, 3 add dl, 1 ;1=A, 2=B mov ah, GET_CURRENT_DIRECTORY pushf call [oldint21h] ;get default directory mov dx, offset set_dir_buffer mov ax, LONG_SET_CURRENT_DIRECTORY pushf call_vdd popf pop si pop ds pop ax pop dx no_change: ret 2 lfnSrvApi: cmp BX, LFNSRV_GET_LOADER_INFO je get_loader_info cmp BX, LFNSRV_SET_SFT_INFO je lfnfuncGeneric jmp orginal get_loader_info: popf ;; AX = handle ;; BX = buildinfo ;; CX = magic value ;; ds:ds old int 21h entry mov ax, [dllHandle] mov bx, LOADER_PRODUCTBUILD mov cx, LOADER_MAGIC_VALUE lds dx, DWORD PTR [oldint21h] bad: nofileoperation: ret 2 interruptHandler endp buffer db 2*264d dup(?) set_dir_buffer equ buffer create_file_buffer equ buffer + 264d resident_end: message1 db 'Long filename API services for Windows NT, built ',??DATE,' ',??TIME,13,10 db 'Copyright (C) 1997-2000 Wojciech Galazka, Andrew Crabtree',13,10 db 'Released under the terms of the GNU General Public License',13,10 db 'The software comes with NEITHER IMPLIED NOR EXPRESSED WARRANTY',13,10 db 'to the extent permitted by law',13,10,'$' error0 DB 'ERROR: MS Windows NT 4.0 is required to run this program, terminating ...',13,10,'$' error1 db 'ERROR: Module not found, terminating ...',13,10,'$'; error2 db 'ERROR: Internal error 1, terminating ...',13,10,'$'; ;Dispatch routine not found, error3 db 'ERROR: Internal error 2, terminating ...',13,10,'$'; ;Initialization routine not found, error4 db 'ERROR: Internal error 3, terminating ...',13,10,'$'; ;Out of memory, wrongversion db 'ERROR: Internal error 4, terminating ...',13,10,'$'; ;DLL & COM file version mismatch, errorUnknown db 'ERROR: Internal error 5, terminating ...',13,10,'$'; ;Unknown error, registered db 'SUCCESS: Long filename API services installed',13,10,'$'; unregister db 'SUCCESS: Long filename API services uninstalled.',13,10,'$'; startmsg db 'Searching module ... ','$' dllName db 'lfnsrv.dll',0; db 13,10,'$' dllDispatch db 'LFNDispatch',0; db 13,10,'$' dllInitialize db 'LFNRegisterInit',0; db 13,10,'$' assume cs:seg1, ds:seg1, es:seg1, ss:seg1 start1: ; CS=DS=ES push cs pop ds print message1 ; check presence of Windows NT MOV AX, GET_TRUE_VERSION_NUMBER INT 21H CMP BX, 3205H JE check_lfn_presence print error0 jmp Terminate check_lfn_presence: print startmsg print dllName ;check if lfnsrv already running mov ax, LFNSRV_API mov bx, LFNSRV_GET_LOADER_INFO int 21h cmp cx, LOADER_MAGIC_VALUE ;lfn services not running jne install_lfn ;; restore interrupt mov [dllHandle], ax mov ah, SET_INTERRUPT_VECTOR mov al, 21h ; DOS Handler ;; ds:dx points to the original int 21 routine int 21h mov ax, [dllHandle] VDDUnregister push cs pop ds print unregister jmp Terminate install_lfn: stc ;; register Our DLL - From VDD guide for Application Based Intercepts mov ax, 0 mov si, offset dllName ;dll name mov di, offset dllInitialize ;init routine mov bx, offset dllDispatch ;dispatch routine VDDRegister jnc registerOK registerError: cmp ax, 1 je Lerror1 cmp ax, 2 je Lerror2 cmp ax, 3 je Lerror3 cmp ax, 4 je Lerror4 print errorUnknown jmp Terminate Lerror1: print error1 jmp Terminate Lerror2: print dllDispatch print error2 jmp Terminate Lerror3: print dllInitialize print error3 jmp Terminate Lerror4: print error4 Terminate: mov ah, TERMINATE_PROGRAM xor al, al ; Return Code int 21h registerOK: mov [dllHandle], ax ;check for version mov ax, LFNSRV_API mov bx, LFNSRV_GET_VERSION_INFO call_vdd cmp bx, LOADER_PRODUCTBUILD je installIRQ mov ax, [dllHandle] VDDUnregister print wrongversion jmp terminate installIRQ: ;; but first set info about SFT ;; get SFT mov ah, GET_SYSVARS_TABLE int 21h ;;ES:BX points to "SysVars" table ;;;; assume es:nothing mov di, es:[bx+04h] mov es, es:[bx+06h] ;;now SFT in ES:DI mov ax, LFNSRV_API mov bx, LFNSRV_SET_SFT_INFO call_vdd print registered mov ah, GET_INTERRUPT_VECTOR mov al, 21h int 21h mov WORD PTR [oldint21h],bx mov WORD PTR [oldint21h+2], es mov ah, SET_INTERRUPT_VECTOR mov al, 21h ;;;; push cs ;;;; pop ds mov dx, offset interruptHandler; int 21h ;; Terminate and stay resident now mov es, cs:[2Ch] mov ah, FREE_ENVIRONM_MEMORY int 21h mov ah, GO_STAY_TSR ; TSR xor al, al ; Return Code mov dx, offset resident_end+15 mov cl, 4 shr dx, cl int 21h start endp seg1 ends end start