;A patcher with search capability, the patch becomes usefull for more versions! ;Multiple string patching added! ;Multiple file patching added! ;TO-DO: - comments (almost finished) ; - finish interface / readible output (almost finished) ; - for each file add an array of approved filesizes ; - print also filesizes if they don't match ; - possibility for correcting date/attributes ; - (longterm) CRC-32 correcting possibility ;Copyright (c) 1998,1999 Anarchriz. All rights reserved. ;currently version 28.01.1999 .model tiny .stack 200h NUMBEROFFILES equ 2 ; <-- STEPSIZE equ 32768 MAXSTRINGSIZE equ 32 MAXSTRINGS equ 32 MAXFILES equ 8 LOCATIONFILESIZE equ 6 .data ;Messages beginMsg db 'The Patcher version 28.01.1999 - copyright (c) 1998,1999 Anarchriz',10,13,'$' patchMsg db 'Test patch Message, by Anarchriz ;)',10,13,'$' patchingMsg db 'Patching file: $' filesizeMsg db ' The expected filesize and the current filesize do not match, continue?',10,13 db ' y)es, a)lways or n)o : $' offsetMsg db ' Expected offset ######## and found ######## for string ##, continue ?',10,13 db ' y)es, a)lways or n)o : $' newline db 10,13,'$' succesMsg db 'Patching was succesfull!',10,13,'$' ;Error Messages noFilesToPatchErrorMsg db 'ERROR: no files to patch',10,13,'$' filenameTolongMsg db 'ERROR: given filename to long',10,13,'$' fileNotFoundMsg db 'ERROR: did not found the file to patch',10,13,'$' readErrorMsg db 'ERROR: error on reading file',10,13,'$' moveErrorMsg db 'ERROR: error on moving file pointer',10,13,'$' writeErrorMsg db 'ERROR: error on writing file',10,13,'$' errorOnOpeningMsg db 'ERROR: error on opening the file',10,13,'$' nothingToDoErrorMsg db 'ERROR: no string(s) to search & patch',10,13,'$' stringNotFoundMsg db 'String(s) to search&patch not found or multiple occurrences.',10,13,'$' closeErrorMsg db 'ERROR: error on closing file',10,13,'$' noSuccesMsg db 'Patching was not succesfull!',10,13,'$' ;Variables patchFiles db 'blabla.txt',0 ;Filename, ends with an 0x0 !! dd 12345h ;Expected filesize dw 2 ;number of strings to patch db 3 ;begin first patch&search string db 'abc' db 'def' dd 2ch db 4 ;begin second patch&search string db 31h, 32h, 33h, 34h db 32h, 33h, 34h, 31h dd 84h db 'blabla2.txt',0 ;FILE 2 ! dd 12345h dw 2 db 6 db 'qwertz' db 'zullez' dd 46 db 6 db 'qwerty' db 'rtyuio' dd 0cbah offsetsStrings dw MAXSTRINGS+1 dup (?) firstChars db MAXSTRINGS dup (?) foundOffsets dd MAXSTRINGS dup (0ffffffffh) tempOffset dd (?) patchFilesOffset dw (?) beginStringsOffset dw (?) numberOfStrings dw (?) continueAlways db 0 ;flag to check if user wants to continue always fileNumber db 0 fileHandle dw 0 bufferEdge db MAXSTRINGSIZE-1 dup (0) asciiHex db '0123456789ABCDEF' dta db 64 dup (?) readBuffer db STEPSIZE dup (?) .code .386 start: ; INIT mov ax, @data mov ds, ax push ds pop es ;es=ds xor ebx, ebx ;* set DiskTransferArea for function findfile * mov ah, 1ah mov dx, offset dta int 21h ;* print start messages * mov dx, offset beginMsg call PrintMsg mov dx, offset patchMsg call PrintMsg ;* check if we have something to do * mov ax, NUMBEROFFILES cmp ax, 0 jbe noFilesToPatchError ;fatal error ;* set base offset for first file * mov [patchFilesOffset], offset patchFiles ; INIT end BigPatchLoop: ;* check if filename is to long * mov al, 0 cld mov di, [patchFilesOffset] mov cx, 256 repne scasb jz EOFfilename jmp filenameTolong ;fatal error ;* get the number of strings to search&patch for this file ; and the offset of the beginning of the strings set * EOFfilename: add di, 4 mov ax, [di] mov [numberOfStrings], ax inc di inc di mov [beginStringsOffset], di ;* check if we have something to do * mov cx, [numberOfStrings] ;NUMBEROFSTRINGS cmp cx, 0 je nothingToDoError ;fatal error (chosen) ;* print which file we are currently patching * mov dx, offset patchingMsg call PrintMsg mov byte ptr[di-7], '$' ;make 0 terminated string printable mov dx, [patchFilesOffset] call PrintMsg mov byte ptr[di-7], 0 ;restore string mov dx, offset newline call PrintMsg ;* get & check the size of the file to patch * mov dx, [patchFilesOffset] call FindFile cmp ax, 0ffffh je fileNotFound ;fatal error cmp [continueAlways], 1 je equalFilesize ;no question needed mov eax, dword ptr[dta+1ah] ;filesize in DTA mov di, [beginStringsOffset] cmp eax, dword ptr[di-LOCATIONFILESIZE] ;filesize in patchFiles je equalFilesize mov ax, offset filesizeMsg call AskYesNo cmp al,'y' je equalFilesize cmp al,'a' jne forcedExit mov [continueAlways], 1 equalFilesize: ;* open file to patch * mov dx, [patchFilesOffset] call OpenFile cmp ax, 0ffffh je errorOnOpening ;fatal error mov [fileHandle], ax ;* init the strings to search for * call InitStrings ;* search the offsets of the strings * mov si, offset fileHandle mov [tempOffset], 0 call SearchString cmp ax, 0 jne doubleOrNotFoundError ;fatal error ;note: there is no check for if the offsets+strings overlap cmp [continueAlways], 1 je equalOffsets ;* check if the offsets match the expected ones * call CheckOffsets cmp ax, 0ffffh je forcedExit equalOffsets: ;* move and write new strings to file * mov si, offset fileHandle call WriteOffsets cmp ax,0ffffh je writeError ;fatal error cmp ax,0fffeh je moveError ;fatal error mov si, offset fileHandle call CloseFile cmp ax, 0ffffh je closeError ;fatal error ;* now preparing to patch next file (resetting foundOffsets) if needed * inc [fileNumber] cmp [fileNumber], NUMBEROFFILES je AllDone xor ebx, ebx mov bx, [numberOfStrings] mov ax, [offsetsStrings+2*ebx] mov [patchFilesOffset], ax initFoundOffsetsLoop: dec ebx mov [foundOffsets+4*ebx], 0ffffffffh cmp ebx, 0 jne initFoundOffsetsLoop jmp BigPatchLoop AllDone: ;successfull! mov dx, offset succesMsg call PrintMsg mov al, 0 jmp exit ;************************** ;* Exit * ;************************** exit: cmp al, 0 je exitNow mov dx, offset nosuccesMsg call PrintMsg exitNow: mov ah, 4ch int 21h closeExit: push ax mov si, offset fileHandle call CloseFile cmp ax, 0ffffh pop ax je closeError jmp exit forcedExit: mov al,11 jmp closeExit ;************************** ;* Error handlers * ;************************** noFilesToPatchError: ;should never happen mov dx, offset noFilesToPatchErrorMsg call PrintMsg mov al, 1 jmp exit filenameTolong: ;should never happen mov dx, offset filenameTolongMsg call PrintMsg mov al, 2 jmp exit nothingToDoError: ;should never happen mov dx, offset nothingToDoErrorMsg call PrintMsg mov al, 5 jmp exit fileNotFound: mov dx, offset fileNotFoundMsg call PrintMsg mov al, 3 jmp exit errorOnOpening: ;happens when file is read-only mov dx, offset errorOnOpeningMsg call PrintMsg mov al, 4 jmp exit doubleOrNotFoundError: mov dx, offset stringNotFoundMsg call PrintMsg mov al, 6 jmp closeExit readError: ;bad sectors? mov dx, offset readErrorMsg call PrintMsg mov al, 7 jmp closeExit moveError: ;should never happen mov dx, offset moveErrorMsg call PrintMsg mov al, 8 jmp closeExit writeError: ;bad sectors? mov dx, offset writeErrorMsg call PrintMsg mov al, 9 jmp closeExit closeError: ;removed floppy? mov dx, offset closeErrorMsg call PrintMsg mov al, 10 jmp exit ;************************** ;* String routines * ;************************** ;Parameters: foundOffsets ; offsetsStrings ;array with offsets of the strings to search&patch ;returns: ax=0 ok. ; ax=ffff not ok, user said no CheckOffsets proc push ebx push dx push di xor ecx, ecx mov cx, [numberOfStrings] ;NUMBEROFSTRINGS xor ebx, ebx checkOffsetLoop: mov eax, [foundOffsets+4*ebx];found offset of the string mov di, [offsetsStrings+2*ebx+2] cmp eax, dword ptr[di-4] ;expected offset of the string jne askOffset checkOffsetLoop2: inc ebx cmp ebx, ecx jne checkOffsetLoop jmp checkOffsetOk askOffset: ;* print question with offsetnumbers * push cx mov dx, offset offsetMsg + 35 mov cl, 8 ; eax=foundOffset call AsciiOffset mov dx, offset offsetMsg + 55 mov al, bl mov cl, 2 call AsciiOffset mov dx, offset offsetMsg + 16 mov cl, 8 mov eax, dword ptr[di-4] ;=expected offset call AsciiOffset pop cx mov ax, offset offsetMsg call AskYesNo ;ask to continue if offsets are not equal cmp al,'y' je checkOffsetLoop2 cmp al,'a' jne checkOffsetError mov [continueAlways], 1 ;never ask anymore checkOffsetOk: xor ax, ax checkOffsetReturn: pop di pop dx pop ebx ret checkOffsetError: mov ax, 0ffffh jmp checkOffsetReturn CheckOffsets endp InitStrings proc push ax push bx push cx push dx push di push si mov bx, [beginStringsOffset] ;begin offset of strings tabel mov di, 0 mov si, 0 mov cx, [numberOfStrings] ;NUMBEROFSTRINGS initStringsLoop: cmp cx, 0 je initStringsReturn mov [offsetsStrings+di], bx xor ax, ax mov al, [bx] shl ax, 1 inc bx mov dl, [bx] mov [firstChars+si], dl add ax, 4 add bx, ax inc di inc di inc si dec cx jmp initStringsLoop initStringsReturn: mov [offsetsStrings+di], bx ;offset after all other offsets pop si pop di pop dx pop cx pop bx pop ax ret InitStrings endp ;Parameters: ds:si pointer to file handle ;returns: ax=ffff no strings found ; ax=fffe double strings found ; ax=0 all ok. SearchString proc push ebx push cx push dx push di ;* first STEPSIZE bytes and init this search * readLoop: mov dx, offset readBuffer call ReadFile cmp ax, 0ffffh je readError cmp ax, 0 je noMoreStrings mov bx, dx sub bx, (MAXSTRINGSIZE-1) ; point to MAXSTRINGSIZE-1 bytes before readbuffer (bufferEdge) initSearch: ;di: points to the first character of each string ;ax: is bytes read (functions as counter) ;bx: is pointer to buffer where we are searching in mov di, 0 searchLoop: mov cl, [firstChars+di] cmp byte ptr[bx], cl je checkString searchLoop2: inc di cmp di, [numberOfStrings] ;NUMBEROFSTRINGS jne searchLoop searchLoop3: inc bx dec ax cmp ax, 0 jne initSearch ;* copy MAXSTRINGSIZE-1 bytes from end readBuffer to bufferEdge * push si mov di, offset bufferEdge mov si, offset readBuffer + STEPSIZE - (MAXSTRINGSIZE-1) mov cx, MAXSTRINGSIZE - 1 cld rep movsb pop si add [tempOffset], STEPSIZE jmp readLoop checkString: ;* first character matched, now check the rest of the string * push cx push si push di mov si, bx inc si shl di, 1 mov di, [offsetsStrings+di] xor cx, cx mov cl, [di] dec cx ;cx=stringsize-1 inc di ;strings beginnen 1 later + 1 char inc di cld repz cmpsb ;compare ds:si with es:di for cx bytes pop di pop si pop cx je foundString jmp searchLoop2 ;no match, continue checking first character of ither strings foundString: ;* now found a string, store offset if not already found * push ebx push di sub bx, offset readBuffer movsx ebx, bx add ebx, [tempOffset] shl di, 2 cmp [foundOffsets+di], 0ffffffffh jne doubleStrings mov [foundOffsets+di], ebx pop di pop ebx jmp searchLoop3 ;continue searching strings noMoreStrings: ;* end of search, check if a string is not found * xor ebx, ebx mov bx, [numberOfStrings] ;NUMBEROFSTRINGS noMoreStringsLoop: dec ebx cmp [foundOffsets+4*ebx],0ffffffffh je stringsNotFound cmp ebx, 0 jne noMoreStringsLoop xor ax, ax searchStringReturn: pop di pop dx pop cx pop ebx ret stringsNotFound: mov ax,0ffffh jmp searchStringReturn doubleStrings: pop di pop ebx mov ax,0fffeh jmp searchStringReturn SearchString endp ;Parameters: ds:si pointer to file handle ; foundOffsets ; offsetsStrings ;returns: ax=ffff error while writing ; ax=fffe error while moving file pointer ; ax=0 all ok. I hope ;) WriteOffsets proc push ebx push dx push di xor ebx, ebx writeOffsetsLoop: mov eax, [foundOffsets+4*ebx] ;si pointer filehandle call MoveFile cmp eax, 0ffffffffh je writeOffsetsMoveError mov di, [offsetsStrings+2*ebx] xor dx, dx mov dl, [di] mov ax, dx ;ax holds stringsize inc dx add dx, di ;dx now holds the offset of new string ;si=offset fileHandle call WriteFile cmp ax, 0ffffh je writeOffsetsWriteError inc ebx cmp bx, [numberOfStrings] ; I suppose ebx < 65536 jne writeOffsetsLoop xor ax, ax writeOffsetsReturn: pop di pop dx pop ebx ret writeOffsetsMoveError: mov ax, 0fffeh jmp writeOffsetsReturn writeOffsetsWriteError: mov ax, 0ffffh jmp writeOffsetsReturn WriteOffsets endp ;************************** ;*** File routines *** ;************************** ;eax=offset ; ds:si=pointer filehandle ;return eax=0ffffffffh on error MoveFile proc push bx push cx push dx mov dx, ax shr eax, 16 mov cx, ax mov ax, 4200h mov bx, [si] int 21h jc moveFileError moveFileReturn: pop dx pop cx pop bx ret moveFileError: mov eax, 0ffffffffh jmp moveFileReturn MoveFile endp ;ds:dx=pointer filename ;return: ax<0ffffh=handle.;ax=true=open error. OpenFile proc mov ax, 3d02h int 21h jc open_error ret open_error: mov ax, 0ffffh ret OpenFile endp ;ds:dx=pointer readbuffer ; ds:si=pointer filehandle ;return ax=ax=read bytes;ax=0ffffh=read error. ReadFile proc push bx push cx mov ah, 3fh mov bx, [si] mov cx, STEPSIZE int 21h jc read_error ReadFile_return: pop cx pop bx ret read_error: mov ax, 0ffffh jmp ReadFile_return ReadFile endp ;ds:dx=pointer (write)readbuffer ; ds:si=pointer filehandle ; ax=bytes to write ;return ax=written bytes;ax=0ffffh=write error. WriteFile proc push bx push cx mov cx, ax mov ah, 40h mov bx, [si] int 21h jc write_error WriteFile_return: pop cx pop bx ret write_error: mov ax, 0ffffh jmp WriteFile_return WriteFile endp ;ds:si=pointer filehandle CloseFile proc push bx mov ah, 3eh mov bx, [si] cmp bx, 0 je doNotClose int 21h jc close_error mov word ptr[si], 0 doNotClose: xor ax,ax CloseFile_return: pop bx ret close_error: mov ax, 0ffffh jmp CloseFile_return CloseFile endp ;ds:dx=point file spec ;return: ax=ffffh -> file not found else ax=n/a FindFile proc push cx xor cx, cx mov ah, 4eh int 21h jc not_found FindFile_return: pop cx ret not_found: mov ax, 0ffffh jmp FindFile_return FindFile endp ;**************************** ;* Miscellaneous routines * ;**************************** PrintMsg proc push ax mov ah,9 int 21h pop ax ret PrintMsg endp ;ax=offset question ;returns: al=key in ascii AskYesNo proc push dx mov dx, ax call PrintMsg xor ax, ax int 16h cmp al, 61h jae askYesNoLowercase add al, 34 askYesNoLowercase: mov dl, al mov ah, 2 int 21h mov dx, offset newline call PrintMsg pop dx ret AskYesNo endp ;parameters: eax offset to convert ; cl length in characters ; ds:dx pointer to writebuffer (at least 8 bytes) ;returns: nothing AsciiOffset proc push eax push bx push cx push si mov si, dx mov ch, 0 add si, cx asciiOffsetLoop: mov bx, ax and bx, 0000000000001111b mov bl, [asciiHex+bx] mov [si], bl shr eax, 4 dec si dec cl jnz asciiOffsetLoop pop si pop cx pop bx pop eax ret AsciiOffset endp ;Alert! This routine does NOT work! It gives divide by zero error! ;Parameters: eax filesize to convert ; ds:dx pointer to writebuffer (at least 10 bytes) DecimalFilesize proc push eax push ebx push ecx push edx push di mov ecx, 1000000000 ;10^9 mov di, dx decimalFilesizeLoop: div ecx ;eax=eax/ecx ; add al, 30h mov [di], al mov ebx, edx mov eax, ecx mov ecx, 10 div ecx mov ecx, eax mov eax, ebx inc di cmp ecx, 0 jne decimalFilesizeLoop pop di pop edx pop ecx pop ebx pop eax ret DecimalFilesize endp end start