1503 lines
40 KiB
NASM
1503 lines
40 KiB
NASM
.xlist
|
|
include kernel.inc
|
|
include gpfix.inc
|
|
include pdb.inc
|
|
include tdb.inc
|
|
include newexe.inc
|
|
include protect.inc
|
|
.list
|
|
|
|
.386
|
|
|
|
ERROR_NO_MORE_ITEMS EQU 259
|
|
|
|
;-----------------------------------------------------------------------
|
|
;-----------------------------------------------------------------------
|
|
; App patching defines
|
|
|
|
|
|
externFP RegCloseKey32
|
|
externFP RegOpenKey32
|
|
externFP RegEnumKey32
|
|
externFP RegEnumValue32
|
|
|
|
externFP FarGetCachedFileHandle
|
|
externFP GlobalRealloc
|
|
externFP GlobalSize
|
|
externFP Far_genter
|
|
externFP Far_gleave
|
|
externFP Far_htoa0
|
|
externFP Far_pdref
|
|
externFP FreeSelector
|
|
|
|
; MultiThreading version of change PDB
|
|
;externFP GetCurPDB
|
|
;externFP SetCurPDB
|
|
;externFP TopPDB
|
|
; MultiThreading version of change PDB
|
|
|
|
externW topPDB
|
|
externW Win_PDB
|
|
externW gdtdsc
|
|
|
|
REG_BINARY equ 3
|
|
|
|
HKEY_LOCAL_MACHINE equ 80000002h
|
|
HKEY_LOCAL_MACHINE_HI equ 8000h
|
|
HKEY_LOCAL_MACHINE_LO equ 0002h
|
|
|
|
; Under HKEY_LOCAL_MACHINE
|
|
REGSTR_PATH_APPPATCH equ <"System\CurrentControlSet\Control\Session Manager\AppPatches">
|
|
; There is one value for each modification to a given segment.
|
|
; The values for a segment are stored under the key
|
|
; REGSTR_PATH_APPPATCH\<AppName>\<AppVersion>\<signature>
|
|
; where <signature> lists bytes in the module to match.
|
|
|
|
|
|
BY_HANDLE_FILE_INFORMATION STRUC
|
|
bhfi_dwFileAttributes DD ?
|
|
bhfi_ftCreationTime DD 2 dup (?)
|
|
bhfi_ftLastAccessTime DD 2 dup (?)
|
|
bhfi_ftLastWriteTime DD 2 dup (?)
|
|
bhfi_dwVolumeSerialNumber DD ?
|
|
bhfi_nFileSizeHigh DD ?
|
|
bhfi_nFileSizeLow DD ?
|
|
bhfi_nNumberOfLinks DD ?
|
|
bhfi_nFileIndexHigh DD ?
|
|
bhfi_nFileIndexLow DD ?
|
|
BY_HANDLE_FILE_INFORMATION ENDS
|
|
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Signature definitions
|
|
;
|
|
AP_SIG STRUC
|
|
sig_format DB ? ; formatType for the struc
|
|
; rest of the data depends on the value in the sig_format field
|
|
AP_SIG ENDS
|
|
|
|
; Supported values for the sig_format field
|
|
AP_SIG_FORMAT_HEXE equ 01h ; match hExe bytes, 1B offset
|
|
AP_SIG_FORMAT_HEXE2 equ 02h ; match hExe bytes, 2B offset
|
|
AP_SIG_FORMAT_FILE2 equ 03h ; match file bytes, 2B offset
|
|
AP_SIG_FORMAT_FILE3 equ 04h ; match file bytes, 3B offset
|
|
AP_SIG_FORMAT_FILE4 equ 05h ; match file bytes, 4B offset
|
|
AP_SIG_FORMAT_FILESIZE2 equ 06h ; match file size, 2B size
|
|
AP_SIG_FORMAT_FILESIZE3 equ 07h ; match file size, 3B size
|
|
AP_SIG_FORMAT_FILESIZE4 equ 08h ; match file size, 4B size
|
|
AP_SIG_FORMAT_META equ 0ffh ; contains other signatures
|
|
|
|
;AP_SIG_FORMAT_HEXE
|
|
AP_SIG_HEXE STRUC
|
|
es_format DB ? ; AP_SIG_FORMAT_HEXE
|
|
; There can be multiple signature strings, packed one after the other.
|
|
; All must match to give a match.
|
|
; cb==0 signals the end.
|
|
es_cb DB ? ; number of bytes to compare
|
|
es_offset DB ? ; offset to begin signature compare
|
|
es_abSignature DB ? ; cb DUP (?) ; signature bytes
|
|
AP_SIG_HEXE ENDS
|
|
|
|
;AP_SIG_FORMAT_HEXE2
|
|
AP_SIG_HEXE2 STRUC
|
|
es2_format DB ? ; AP_SIG_FORMAT_HEXE2
|
|
; There can be multiple signature strings, packed one after the other.
|
|
; All must match to give a match.
|
|
; cb==0 signals the end.
|
|
es2_cb DB ? ; number of bytes to compare
|
|
es2_offset DW ? ; offset to begin signature compare
|
|
es2_abSignature DB ? ; cb DUP (?) ; signature bytes
|
|
AP_SIG_HEXE2 ENDS
|
|
|
|
;AP_SIG_FORMAT_FILE2
|
|
AP_SIG_FILE2 STRUC
|
|
fs2_format DB ? ; AP_SIG_FORMAT_FILE2
|
|
; There can be multiple signature strings, packed one after the other.
|
|
; All must match to give a match.
|
|
; cb==0 signals the end.
|
|
fs2_cb DB ? ; number of bytes to compare
|
|
fs2_offset DB 2 DUP (?) ; offset to begin signature compare
|
|
fs2_abSignature DB ? ; cb DUP (?) ; bytes to match
|
|
AP_SIG_FILE2 ENDS
|
|
|
|
;AP_SIG_FORMAT_FILE3
|
|
AP_SIG_FILE3 STRUC
|
|
fs3_format DB ? ; AP_SIG_FORMAT_FILE3
|
|
; There can be multiple signature strings, packed one after the other.
|
|
; All must match to give a match.
|
|
; cb==0 signals the end.
|
|
fs3_cb DB ? ; number of bytes to compare
|
|
fs3_offset DB 3 DUP (?) ; offset to begin signature compare
|
|
fs3_abSignature DB ? ; cb DUP (?) ; bytes to match
|
|
AP_SIG_FILE3 ENDS
|
|
|
|
;AP_SIG_FORMAT_FILE4
|
|
AP_SIG_FILE4 STRUC
|
|
fs4_format DB ? ; AP_SIG_FORMAT_FILE4
|
|
; There can be multiple signature strings, packed one after the other.
|
|
; All must match to give a match.
|
|
; cb==0 signals the end.
|
|
fs4_cb DB ? ; number of bytes to compare
|
|
fs4_offset DB 4 DUP (?) ; offset to begin signature compare
|
|
fs4_abSignature DB ? ; cb DUP (?) ; bytes to match
|
|
AP_SIG_FILE4 ENDS
|
|
|
|
;AP_SIG_FORMAT_FILESIZE
|
|
AP_SIG_FILESIZE STRUC
|
|
fss_format DB ? ; AP_SIG_FORMAT_FILESIZE[2,3,4]
|
|
fss_cbFile DB ? ; file size in bytes
|
|
AP_SIG_FILESIZE ENDS
|
|
|
|
;AP_SIG_FORMAT_FILESIZE2
|
|
AP_SIG_FILESIZE2 STRUC
|
|
fss2_format DB ? ; AP_SIG_FORMAT_FILESIZE2
|
|
fss2_cbFile DB 2 DUP (?) ; file size in bytes
|
|
AP_SIG_FILESIZE2 ENDS
|
|
|
|
;AP_SIG_FORMAT_FILESIZE3
|
|
AP_SIG_FILESIZE3 STRUC
|
|
fss3_format DB ? ; AP_SIG_FORMAT_FILESIZE3
|
|
fss3_cbFile DB 3 DUP (?) ; file size in bytes
|
|
AP_SIG_FILESIZE3 ENDS
|
|
|
|
;AP_SIG_FORMAT_FILESIZE4
|
|
AP_SIG_FILESIZE4 STRUC
|
|
fss4_format DB ? ; AP_SIG_FORMAT_FILESIZE4
|
|
fss4_cbFile DB 4 DUP (?) ; file size in bytes
|
|
AP_SIG_FILESIZE4 ENDS
|
|
|
|
;AP_SIG_FORMAT_META
|
|
AP_SIG_META STRUC
|
|
ms_format DB ? ; AP_SIG_FORMAT_META
|
|
; There can be multiple sub-signatures, packed one after the other.
|
|
; All must match to give a match.
|
|
; cb==0 signals the end.
|
|
ms_cb DB ? ; number of bytes in the sub-signature
|
|
ms_abSubSignature DB ? ; the sub-signature
|
|
AP_SIG_META ENDS
|
|
|
|
;-----------------------------------------------------------------------
|
|
; Patch definitions
|
|
;
|
|
AP_COMMON STRUC
|
|
ap_format DB ? ; formatType for the struc
|
|
ap_cbSize DB ? ; number of bytes in the whole struct
|
|
; rest of the data depends on the value in the ap_format field
|
|
AP_COMMON ENDS
|
|
|
|
; Supported values for the ap_format field:
|
|
AP_FORMAT_REPLACE equ 1
|
|
AP_FORMAT_ADD equ 2
|
|
|
|
;AP_FORMAT_REPLACE
|
|
AP_REPLACE STRUC
|
|
apr_format DB ? ; AP_FORMAT_REPLACE
|
|
apr_cbSize DB ? ; number of bytes in the whole struct
|
|
apr_offset DW ? ; offset within the segment
|
|
apr_cb DB ? ; number of bytes to replace
|
|
apr_abOld DB ? ; cb DUP (?) ; old bytes
|
|
apr_abNew DB ? ; cb DUP (?) ; new bytes
|
|
AP_REPLACE ENDS
|
|
|
|
;AP_FORMAT_ADD
|
|
AP_ADD STRUC
|
|
apa_format DB ? ; AP_FORMAT_ADD
|
|
apa_cbSize DB ? ; number of bytes in the whole struct
|
|
apa_offset DW ? ; offset within the segment
|
|
apa_cb DB ? ; number of bytes to replace
|
|
apa_abNew DB ? ; cb DUP (?) ; new bytes
|
|
AP_ADD ENDS
|
|
|
|
|
|
|
|
DataBegin
|
|
|
|
szREGSTR_PATH_APPPATCH db REGSTR_PATH_APPPATCH, "\", 0
|
|
|
|
; app-patching cache
|
|
globalW hExePatchAppCache 0
|
|
globalD hkeyPatchAppCache 0
|
|
|
|
|
|
DataEnd
|
|
|
|
|
|
|
|
sBegin NRESCODE
|
|
assumes CS,NRESCODE
|
|
|
|
; GetPatchAppCache
|
|
;
|
|
; Gets an hkeyReg from the PatchApp cache if hExe in cache.
|
|
;
|
|
; Arguments:
|
|
; hExe
|
|
;
|
|
; Returns:
|
|
; dx:ax -1 if cache miss
|
|
; registry key associated with hExe if cache hit
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;HKEY
|
|
cProc GetPatchAppCache, <PUBLIC, NEAR>, <ds>
|
|
parmW hExe
|
|
cBegin
|
|
SetKernelDSNRes
|
|
|
|
; Set up dx==ax==-1 (indicates cache miss)
|
|
sub ax, ax
|
|
dec ax
|
|
mov dx, ax
|
|
|
|
mov cx, hExe
|
|
cmp cx, hExePatchAppCache
|
|
jne short gpac_exeunt
|
|
|
|
mov dx, hkeyPatchAppCache.hi
|
|
mov ax, hkeyPatchAppCache.lo
|
|
krDebugOut DEB_WARN,"GetPatchAppCache: hit (#cx, #dx#ax)"
|
|
gpac_exeunt:
|
|
UnSetKernelDS
|
|
cEnd
|
|
|
|
|
|
; AddPatchAppCache
|
|
;
|
|
; Adds an (hExe,hkeyReg) pair to the PatchApp cache.
|
|
;
|
|
; Arguments:
|
|
; hExe
|
|
; hkeyReg
|
|
;
|
|
; Returns:
|
|
;
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;VOID
|
|
cProc AddPatchAppCache, <PUBLIC, NEAR>, <ds>
|
|
parmW hExe
|
|
parmD hkeyReg
|
|
cBegin
|
|
SetKernelDSNRes
|
|
|
|
mov ax, hExe
|
|
mov hExePatchAppCache, ax
|
|
|
|
mov edx, hkeyReg
|
|
mov hkeyPatchAppCache, edx
|
|
if KDEBUG
|
|
push cx
|
|
mov cx, hkeyPatchAppCache.hi
|
|
krDebugOut DEB_WARN,"AddPatchAppCache: (#ax, #cx#dx)"
|
|
pop cx
|
|
endif
|
|
|
|
UnSetKernelDS
|
|
cEnd
|
|
|
|
|
|
; FlushPatchAppCache
|
|
;
|
|
; Flushes an hExe from the PatchApp cache.
|
|
; If the cache has a reg key for the hExe, closes the key.
|
|
;
|
|
; Arguments:
|
|
; hExe
|
|
;
|
|
; Returns:
|
|
;
|
|
; Remarks:
|
|
; called by DelModule
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;VOID
|
|
cProc FlushPatchAppCache, <PUBLIC, NEAR>, <>
|
|
parmW hExe
|
|
cBegin
|
|
; CheckKernelDS
|
|
ReSetKernelDS
|
|
|
|
mov ax, hExe
|
|
krDebugOut DEB_TRACE,"FlushPatchAppCache: (hExe #ax)"
|
|
cmp ax, hExePatchAppCache
|
|
jne short fpac_exeunt
|
|
|
|
sub eax, eax
|
|
cmp eax, hkeyPatchAppCache
|
|
je short fpac_after_close_key
|
|
cCall RegCloseKey32, <hkeyPatchAppCache>
|
|
if KDEBUG
|
|
mov cx, hExe
|
|
mov bx, hkeyPatchAppCache.lo
|
|
mov dx, hkeyPatchAppCache.hi
|
|
krDebugOut DEB_WARN,"FlushPatchAppCache: flushing (hExe #cx) (hkey #dx#bx)"
|
|
endif
|
|
|
|
fpac_after_close_key:
|
|
mov hExePatchAppCache, ax
|
|
if KDEBUG
|
|
mov hkeyPatchAppCache, eax ; a little extra for debug
|
|
endif
|
|
|
|
fpac_exeunt:
|
|
UnSetKernelDS
|
|
cEnd
|
|
|
|
|
|
; PatchGetFileSize
|
|
;
|
|
; Get the size of a file, given the DOS file handle.
|
|
;
|
|
; Arguments:
|
|
; dfh - DOS file handle
|
|
;
|
|
; Returns:
|
|
; DWORD file size
|
|
;
|
|
; Remarks:
|
|
; Since these patches are only for old (<4.0) apps, only support
|
|
; DWORD file size.
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;DWORD
|
|
cProc PatchGetFileSize, <PUBLIC, NEAR>, <ds,si,di>
|
|
parmW dfh
|
|
|
|
localV FileInformation,<SIZE BY_HANDLE_FILE_INFORMATION>
|
|
localW HiPosition
|
|
localW LoPosition
|
|
|
|
localW SavePDB
|
|
|
|
cBegin
|
|
; MultiThreading version of change PDB
|
|
; cCall GetCurPDB
|
|
; mov SavePDB, ax
|
|
; SetKernelDSNRes
|
|
; cCall SetCurPDB,<topPDB> ; kernel's PSP
|
|
; UnSetKernelDS
|
|
; MultiThreading version of change PDB
|
|
SetKernelDSNRes ; ds is a scratch reg -> no need restore
|
|
mov ax, topPDB
|
|
xchg Win_PDB, ax ; Switch to Kernel's PDB,
|
|
UnSetKernelDS
|
|
mov SavePDB, ax ; saving current PDB
|
|
|
|
mov bx, dfh
|
|
smov ds, ss
|
|
lea dx, FileInformation
|
|
stc ; Real-mode drivers don't set CY on error
|
|
mov ax, 71a6h
|
|
int 21h
|
|
jc short gfs_try_offsets
|
|
|
|
mov dx, FileInformation.bhfi_nFileSizeLow.hi
|
|
mov ax, FileInformation.bhfi_nFileSizeLow.lo
|
|
jmp short gfs_exeunt
|
|
|
|
gfs_try_offsets:
|
|
; Real-mode drivers don't support 71a6, so we do it the hard way.
|
|
; Move from current by 0 to get the current postion
|
|
mov bx, dfh
|
|
sub cx, cx
|
|
mov dx, cx
|
|
mov ax, 4201h
|
|
int 21h
|
|
jc short gfs_fail
|
|
|
|
; Save current position
|
|
mov HiPosition, dx
|
|
mov LoPosition, ax
|
|
|
|
; Get file size by moving from end by 0
|
|
sub cx, cx
|
|
mov dx, cx
|
|
mov ax, 4202h
|
|
int 21h
|
|
jc short gfs_fail
|
|
|
|
push dx
|
|
push ax
|
|
|
|
; Restore current position
|
|
mov cx, HiPosition
|
|
mov dx, LoPosition
|
|
mov ax, 4200h
|
|
int 21h
|
|
; Don't check for error, since we can't recover any more anyway...
|
|
|
|
pop ax
|
|
pop dx
|
|
jmp short gfs_exeunt
|
|
|
|
gfs_fail:
|
|
sub ax, ax
|
|
mov dx, ax
|
|
gfs_exeunt:
|
|
; MultiThreading version of change PDB
|
|
push ax
|
|
mov ax, SavePDB
|
|
SetKernelDSNRes ; ds restored on proc exit
|
|
mov Win_PDB, ax
|
|
UnSetKernelDS
|
|
; cCall SetCurPDB,<SavePDB> ; preserves dx
|
|
pop ax
|
|
; MultiThreading version of change PDB
|
|
|
|
cEnd
|
|
|
|
|
|
; CompareFileBytes
|
|
;
|
|
; Compares a sequence of bytes with those at a given offset in a file.
|
|
;
|
|
; Arguments:
|
|
; dfh
|
|
; lpBytes
|
|
; cb
|
|
; dwFileOffset
|
|
;
|
|
; Returns:
|
|
; WORD, zero iff match
|
|
;
|
|
; Remarks:
|
|
; The caller is responsible for preserving the offset in the file.
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;WORD
|
|
cProc CompareFileBytes, <PUBLIC, NEAR>, <si,di,ds>
|
|
parmW dfh
|
|
parmD lpBytes
|
|
parmW cb
|
|
parmD dwFileOffset
|
|
cBegin
|
|
; Seek to dwFileOffset.
|
|
mov bx, dfh
|
|
mov cx, dwFileOffset.hi
|
|
mov dx, dwFileOffset.lo
|
|
mov ax, 4200h
|
|
int 21h
|
|
jc short cfb_fail
|
|
|
|
if KDEBUG
|
|
; The high byte of cb _must_ be 0.
|
|
mov cx, cb
|
|
cmp ch, 0
|
|
je short @F
|
|
krDebugOut DEB_FERROR,"CompareFileBytes: cb (#cx) > 0ffh"
|
|
@@:
|
|
endif
|
|
mov byte ptr [cb].1, 0 ; force cb < 100h
|
|
|
|
; Read from file.
|
|
mov cx, cb
|
|
smov ds, ss
|
|
sub sp, cx
|
|
mov dx, sp ; ds:dx = read buffer
|
|
mov ah, 3fh
|
|
int 21h
|
|
jc short cps_fail_restore_stack
|
|
|
|
; Make sure we filled the buffer.
|
|
cmp ax, cx
|
|
jne short cps_fail_restore_stack
|
|
|
|
; Compare the bytes.
|
|
les di, lpBytes ; es:di = signature bytes
|
|
smov ds, ss
|
|
mov si, sp ; ds:si = read buffer
|
|
rep cmpsb
|
|
jne short cps_fail_restore_stack
|
|
|
|
sub ax, ax
|
|
add sp, cb
|
|
jmp short cfb_exit
|
|
|
|
cps_fail_restore_stack:
|
|
add sp, cb
|
|
cfb_fail:
|
|
or al, 1
|
|
cfb_exit:
|
|
cEnd
|
|
|
|
|
|
; ComparePatchSignature
|
|
;
|
|
; Tests a patch signature against an hExe.
|
|
;
|
|
; Arguments:
|
|
; hExe
|
|
; lpPatchSignature
|
|
;
|
|
; Returns:
|
|
; WORD, zero iff match
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;WORD
|
|
cProc ComparePatchSignature, <PUBLIC, NEAR>, <si,di,ds>
|
|
parmW hExe
|
|
parmD lpPatchSignature
|
|
|
|
localW SavePDB
|
|
localW dfh
|
|
localW cbNonZero
|
|
localD dwFileOffset
|
|
cBegin
|
|
beg_fault_trap cps_fault
|
|
mov es, hExe
|
|
lds si, lpPatchSignature
|
|
|
|
cld
|
|
.errnz AP_SIG.sig_format
|
|
lodsb
|
|
;---------------------------------------------------
|
|
cmp al, AP_SIG_FORMAT_META
|
|
jne short cps_maybe_hexe
|
|
|
|
.errnz (AP_SIG_HEXE.ms_cb - AP_SIG_HEXE.ms_format) - 1
|
|
cps_loop_meta:
|
|
lodsb
|
|
mov ah, 0
|
|
mov cx, ax ; cx = size of sub-signature
|
|
jcxz cps_meta_exeunt ; end of list. must be match.
|
|
|
|
mov di, si
|
|
add di, cx ; ds:di = next sub-signature
|
|
cCall ComparePatchSignature,<hExe,ds,si>
|
|
test ax, ax
|
|
jnz cps_fail
|
|
; Got a match. Try the next one.
|
|
mov si, di ; ds:si = next sub-signature
|
|
jmp cps_loop_meta
|
|
|
|
cps_meta_exeunt:
|
|
jmp cps_exeunt
|
|
|
|
;---------------------------------------------------
|
|
; AP_SIG_FORMAT_HEXE
|
|
; AP_SIG_FORMAT_HEXE2
|
|
cps_maybe_hexe:
|
|
.errnz (AP_SIG_FORMAT_HEXE2-AP_SIG_FORMAT_HEXE)-1
|
|
cmp al, AP_SIG_FORMAT_HEXE
|
|
jb short cps_maybe_filesize
|
|
cmp al, AP_SIG_FORMAT_HEXE2
|
|
ja short cps_maybe_filesize
|
|
|
|
; Compute number of bytes in offset after first byte.
|
|
mov dl, al
|
|
sub dl, AP_SIG_FORMAT_HEXE
|
|
|
|
.errnz (AP_SIG_HEXE.es_cb - AP_SIG_HEXE.es_format) - 1
|
|
mov ah, 0
|
|
cps_hexe_loop:
|
|
lodsb
|
|
mov cx, ax ; cx = size of signature block
|
|
jcxz cps_hexe_match ; end of list. must be match.
|
|
|
|
; Set up bx with the offset in the hExe
|
|
sub bx, bx ; bx = default offset (0)
|
|
lodsb
|
|
mov bl, al ; bl = low byte of offset
|
|
test dl, dl ; more bytes?
|
|
jz short @F
|
|
lodsb
|
|
mov bh, al ; bh = high byte of offset
|
|
@@:
|
|
mov di, bx ; es:di points to bytes in hExe
|
|
rep cmpsb
|
|
jne cps_fail
|
|
jmp cps_hexe_loop
|
|
|
|
cps_hexe_match:
|
|
; ax already 0
|
|
jmp cps_exeunt
|
|
|
|
;---------------------------------------------------
|
|
; AP_SIG_FORMAT_FILESIZE2
|
|
; AP_SIG_FORMAT_FILESIZE3
|
|
; AP_SIG_FORMAT_FILESIZE4
|
|
cps_maybe_filesize:
|
|
.errnz (AP_SIG_FORMAT_FILESIZE3-AP_SIG_FORMAT_FILESIZE2)-1
|
|
.errnz (AP_SIG_FORMAT_FILESIZE4-AP_SIG_FORMAT_FILESIZE3)-1
|
|
cmp al, AP_SIG_FORMAT_FILESIZE2
|
|
jb short cps_maybe_file
|
|
cmp al, AP_SIG_FORMAT_FILESIZE4
|
|
ja short cps_maybe_file
|
|
|
|
; Compute number of non-zero bytes in file size high word
|
|
mov cl, al
|
|
sub cl, AP_SIG_FORMAT_FILESIZE2
|
|
push cx
|
|
|
|
; MultiThreading version of change PDB
|
|
; cCall GetCurPDB
|
|
; mov SavePDB, ax
|
|
; SetKernelDSNRes es
|
|
; cCall SetCurPDB,<topPDB> ; kernel's PSP
|
|
; UnSetKernelDS es
|
|
; MultiThreading version of change PDB
|
|
push ds
|
|
SetKernelDSNRes
|
|
mov ax, topPDB
|
|
xchg Win_PDB, ax ; Switch to Kernel's PDB,
|
|
UnSetKernelDS
|
|
pop ds
|
|
mov SavePDB, ax ; saving current PDB
|
|
|
|
; Since these patches are only for old (<4.0) apps, only support
|
|
; DWORD file size.
|
|
or ax, -1
|
|
cCall FarGetCachedFileHandle,<hExe,ax,ax>
|
|
cmp ax, -1
|
|
je short cps_filesize_fh_cache_miss
|
|
|
|
cCall PatchGetFileSize,<ax>
|
|
pop cx ; cl = non-zero bytes in file size high word
|
|
|
|
; Low word of file size must match.
|
|
; [si] = low byte of signature file size
|
|
cmp ax, [si]
|
|
jne short cps_filesize_fail
|
|
|
|
mov ch, 0
|
|
sub bx, bx ; bx = default high word of file size (0)
|
|
jcxz cps_filesize_compare_high
|
|
|
|
; [si] = low word of signature file size
|
|
inc si
|
|
inc si
|
|
; [si] = low byte of high word of signature file size (if exists)
|
|
; bx = 0
|
|
; cx = [1|2], non-zero bytes in file size high word
|
|
lodsb
|
|
mov bl, al
|
|
dec cl
|
|
jcxz cps_filesize_compare_high
|
|
lodsb
|
|
mov bh, al
|
|
cps_filesize_compare_high:
|
|
cmp bx, dx
|
|
jne short cps_filesize_fail
|
|
|
|
cps_filesize_match:
|
|
; File size matches.
|
|
; MultiThreading version of change PDB
|
|
; cCall SetCurPDB,<SavePDB>
|
|
; MultiThreading version of change PDB
|
|
mov ax, SavePDB
|
|
push ds
|
|
SetKernelDSNRes
|
|
mov Win_PDB, ax
|
|
UnSetKernelDS
|
|
pop ds
|
|
|
|
sub ax, ax
|
|
jmp cps_exeunt
|
|
|
|
cps_filesize_fh_cache_miss:
|
|
krDebugOut DEB_ERROR,"ComparePatchSignature: filesize fh cache miss"
|
|
|
|
cps_filesize_fail:
|
|
; MultiThreading version of change PDB
|
|
; cCall SetCurPDB,<SavePDB>
|
|
; MultiThreading version of change PDB
|
|
mov ax, SavePDB
|
|
push ds
|
|
SetKernelDSNRes
|
|
mov Win_PDB, ax
|
|
UnSetKernelDS
|
|
pop ds
|
|
|
|
jmp cps_fail
|
|
|
|
;---------------------------------------------------
|
|
; AP_SIG_FORMAT_FILE2
|
|
; AP_SIG_FORMAT_FILE3
|
|
; AP_SIG_FORMAT_FILE4
|
|
cps_maybe_file:
|
|
.errnz (AP_SIG_FORMAT_FILE3-AP_SIG_FORMAT_FILE2)-1
|
|
.errnz (AP_SIG_FORMAT_FILE4-AP_SIG_FORMAT_FILE3)-1
|
|
cmp al, AP_SIG_FORMAT_FILE2
|
|
jb cps_bad_format
|
|
cmp al, AP_SIG_FORMAT_FILE4
|
|
ja cps_bad_format
|
|
|
|
; Compute number of non-zero bytes in file offset high word
|
|
mov cl, al
|
|
sub cl, AP_SIG_FORMAT_FILE2
|
|
mov ch, 0
|
|
mov cbNonZero, cx
|
|
|
|
; MultiThreading version of change PDB
|
|
; cCall GetCurPDB
|
|
; mov SavePDB, ax
|
|
; SetKernelDSNRes es
|
|
; cCall SetCurPDB,<topPDB> ; kernel's PSP
|
|
; UnSetKernelDS es
|
|
; MultiThreading version of change PDB
|
|
push ds
|
|
SetKernelDSNRes
|
|
mov ax, topPDB
|
|
xchg Win_PDB, ax ; Switch to Kernel's PDB,
|
|
UnSetKernelDS
|
|
pop ds
|
|
mov SavePDB, ax ; saving current PDB
|
|
|
|
mov es, hExe
|
|
|
|
or ax, -1
|
|
push es
|
|
cCall FarGetCachedFileHandle,<es,ax,ax>
|
|
pop es
|
|
cmp ax, -1
|
|
je short cps_file_fh_cache_miss
|
|
|
|
mov bx, ax ; bx = dos file handle
|
|
mov dfh, ax
|
|
sub cx, cx
|
|
mov dx, cx
|
|
mov ax, 4201h
|
|
int 21h
|
|
jc cps_fail
|
|
|
|
mov dwFileOffset.hi, dx
|
|
mov dwFileOffset.lo, ax
|
|
|
|
cps_file_loop:
|
|
lodsb
|
|
mov ah, 0
|
|
mov cx, ax ; cx = size of signature block
|
|
jcxz cps_file_match ; end of list. must be match.
|
|
|
|
mov di, cx ; di = number of bytes to match
|
|
|
|
; Get the file offset into dx:bx
|
|
; First get the low word.
|
|
lodsw
|
|
mov bx, ax ; bx = low word of file offset
|
|
|
|
; Get one or both bytes of the high word.
|
|
sub dx, dx ; dx = default high word of file offset (0)
|
|
mov cx, cbNonZero
|
|
jcxz cps_file_compare
|
|
; [si] = low byte of high word of file offset
|
|
; dx = 0
|
|
; cx = [1|2], non-zero bytes in file offset high word
|
|
lodsb
|
|
mov dl, al
|
|
dec cl
|
|
jcxz cps_file_compare
|
|
lodsb
|
|
mov dh, al
|
|
|
|
cps_file_compare:
|
|
; ds:[si] = bytes to match in file
|
|
; di = byte count
|
|
; dx:bx = offset in file
|
|
cCall CompareFileBytes,<dfh,ds,si,di,dx,bx>
|
|
test ax, ax
|
|
jnz short cps_file_fail
|
|
|
|
add si, di ; ds:si = next signature block
|
|
jmp cps_file_loop
|
|
|
|
cps_file_fh_cache_miss:
|
|
krDebugOut DEB_ERROR,"ComparePatchSignature: file fh cache miss"
|
|
|
|
cps_file_fail:
|
|
or al, 1
|
|
jmp short cps_file_exit
|
|
|
|
cps_file_match:
|
|
sub ax, ax
|
|
|
|
cps_file_exit:
|
|
mov si, ax ; si = return value
|
|
|
|
; Restore file position
|
|
mov bx, dfh ; bx = dos file handle
|
|
mov cx, dwFileOffset.hi
|
|
mov dx, dwFileOffset.lo
|
|
mov ax, 4200h
|
|
int 21h
|
|
; Don't check error, since we can't do anything anyway.
|
|
if KDEBUG
|
|
jnc short @F
|
|
krDebugOut DEB_ERROR,"ComparePatchSignature: failure restoring file position"
|
|
@@:
|
|
endif
|
|
; MultiThreading version of change PDB
|
|
; cCall SetCurPDB,<SavePDB>
|
|
; MultiThreading version of change PDB
|
|
mov ax, SavePDB
|
|
push ds
|
|
SetKernelDSNRes
|
|
mov Win_PDB, ax
|
|
UnSetKernelDS
|
|
pop ds
|
|
|
|
mov ax, si
|
|
jmp short cps_exeunt
|
|
|
|
;---------------------------------------------------
|
|
end_fault_trap
|
|
cps_fault:
|
|
fault_fix_stack
|
|
krDebugOut DEB_ERROR,"ComparePatchSignature: trapped fault"
|
|
|
|
cps_bad_format:
|
|
krDebugOut DEB_ERROR,"ComparePatchSignature: invalid format"
|
|
cps_fail:
|
|
or al, 1 ; ensure we return "no_match"
|
|
cps_exeunt:
|
|
cEnd
|
|
|
|
|
|
; GetPatchAppRegKey
|
|
;
|
|
; Determines if we patch an app at load time, gets reg key if so.
|
|
;
|
|
; Arguments:
|
|
; hExe
|
|
;
|
|
; Returns:
|
|
; HKEY, non-zero iff we segment-patch info in the registry for this app.
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS, ES
|
|
|
|
assumes ds,nothing
|
|
assumes es,nothing
|
|
|
|
;HKEY
|
|
cProc GetPatchAppRegKey, <PUBLIC, FAR>, <si,di,ds,es>
|
|
parmW hExe
|
|
|
|
szKey_size = 200
|
|
abPatchSignature_size = 100
|
|
|
|
localD hkeyModule
|
|
localD hkeySignature
|
|
localD dwIndex
|
|
localV szKey,szKey_size ; BUGBUG need a better size
|
|
localV abPatchSignature,abPatchSignature_size ; BUGBUG need a better size
|
|
|
|
cBegin
|
|
; We must refuse to patch system modules.
|
|
; Should be ok because of version check (in caller).
|
|
if KDEBUG
|
|
mov es, hExe
|
|
cmp es:[ne_expver], 400h ; Hacks don't apply to 4.0 or above
|
|
jb short @F
|
|
; Should never get here since the caller should have already checked.
|
|
krDebugOut DEB_ERROR,"GetPatchAppRegKey: (#es) version later than 4.0"
|
|
@@:
|
|
endif
|
|
cCall GetPatchAppCache,<hExe>
|
|
; ffff:ffff means a cache miss
|
|
or cx, -1
|
|
cmp cx, dx
|
|
jne gpark_exeunt
|
|
cmp cx, ax
|
|
jne gpark_exeunt
|
|
|
|
; Not in the cache. Get it from the registry and add to the cache.
|
|
|
|
; Copy the subkey prefix to the buffer.
|
|
lea di, szKey
|
|
smov es, ss
|
|
SetKernelDSNRes
|
|
lea si, szREGSTR_PATH_APPPATCH
|
|
mov cx, szKey_size
|
|
@@:
|
|
lodsb
|
|
stosb
|
|
test al, al
|
|
loopnz @B
|
|
if KDEBUG
|
|
jz short @F
|
|
krDebugOut DEB_ERROR,"GetPatchAppRegKey: len(szREGSTR_PATH_APPPATCH) > szKey_size"
|
|
@@:
|
|
endif
|
|
dec di
|
|
UnSetKernelDS
|
|
|
|
; Append the module's base name (pascal format).
|
|
mov ds, hExe
|
|
mov si, ds:[ne_restab]
|
|
lodsb
|
|
if KDEBUG
|
|
mov ah, 0
|
|
cmp cx, ax
|
|
ja short @F
|
|
krDebugOut DEB_ERROR,"GetPatchAppRegKey: len(reg path) > szKey_size"
|
|
@@:
|
|
endif
|
|
movzx cx, al
|
|
rep movsb
|
|
sub al, al
|
|
stosb ; NULL terminator
|
|
|
|
; Get the key for this module.
|
|
mov hkeySignature, 0 ; In case we fail.
|
|
mov eax, HKEY_LOCAL_MACHINE
|
|
lea si, szKey
|
|
lea di, hkeyModule
|
|
ccall RegOpenKey32, <eax, ss, si, ss, di>
|
|
or ax,dx
|
|
jnz gpark_fail2
|
|
|
|
or dwIndex, -1 ; == mov dwIndex,-1 (but smaller)
|
|
gpark_loop:
|
|
inc dwIndex
|
|
lea si, szKey
|
|
ccall RegEnumKey32, <hkeyModule, dwIndex, ss, si, 0, szKey_size>
|
|
or ax, dx
|
|
jnz short gpark_loop_done
|
|
|
|
; First, convert string to binary.
|
|
; Reuse szKey since we don't need path any more (we have hKeyModule).
|
|
lea si, abPatchSignature
|
|
lea di, szKey
|
|
krDebugOut DEB_WARN,"GetPatchAppRegKey: checking signature (@ss:di)"
|
|
cCall ConvertPatchStringToBinary,<ss,si,abPatchSignature_size,ss,di>
|
|
; Skip a badly formatted patch signature.
|
|
test ax, ax
|
|
if KDEBUG
|
|
jnz short @F
|
|
lea cx, szKey
|
|
krDebugOut DEB_ERROR,"PatchAppSeg: bad patch signature in registry, @ss:cx"
|
|
@@:
|
|
endif
|
|
jz gpark_loop
|
|
|
|
cCall ComparePatchSignature,<hExe,ss,si>
|
|
test ax, ax
|
|
jne gpark_loop
|
|
|
|
if KDEBUG
|
|
mov ax, hExe
|
|
krDebugOut DEB_WARN,"GetPatchAppRegKey: (#ax) sig matches (@ss:di)"
|
|
endif
|
|
; We have a match. Get the corresponding key.
|
|
lea si, szKey
|
|
lea di, hkeySignature
|
|
ccall RegOpenKey32, <hkeyModule, ss, si, ss, di>
|
|
or ax,dx
|
|
jz short gpark_add
|
|
|
|
krDebugOut DEB_ERROR,"GetPatchAppRegKey: RegOpenKey failed, #dx#ax"
|
|
jmp short gpark_fail
|
|
|
|
gpark_loop_done:
|
|
|
|
if KDEBUG
|
|
test dx, dx
|
|
jnz short @F
|
|
cmp ax, ERROR_NO_MORE_ITEMS
|
|
je short gpark_after_loop_done_err
|
|
@@:
|
|
krDebugOut DEB_ERROR,"GetPatchAppRegKey: unexpected error #dx#ax"
|
|
gpark_after_loop_done_err:
|
|
endif
|
|
|
|
gpark_fail:
|
|
; Mark that there are no patches in the registry.
|
|
mov hkeySignature, 0
|
|
gpark_add:
|
|
; Close the reg key for the path to the end of the module name
|
|
cCall RegCloseKey32, <hkeyModule>
|
|
|
|
gpark_fail2:
|
|
; Add hkeySignature to the cache and set up return regs.
|
|
cCall AddPatchAppCache,<hExe, hkeySignature>
|
|
|
|
mov ax, hkeySignature.lo
|
|
mov dx, hkeySignature.hi
|
|
gpark_exeunt:
|
|
cEnd
|
|
|
|
; ConvertPatchStringToBinary
|
|
;
|
|
; Convert a string of [0-9,A-F] with nibble-swapped bytes to binary.
|
|
;
|
|
; Arguments:
|
|
; lpBinary - output buffer
|
|
; cbBinary - size of output buffer in bytes
|
|
; lpString - input NULL-terminated string
|
|
;
|
|
; Returns:
|
|
; Boolean, TRUE iff we convert the entire string.
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS, ES
|
|
|
|
assumes ds, nothing
|
|
assumes es, nothing
|
|
|
|
;BOOL
|
|
cProc ConvertPatchStringToBinary, <PUBLIC, NEAR>,<di,si,ds>
|
|
parmD lpBinary
|
|
parmW cbBinary
|
|
parmD lpString
|
|
cBegin
|
|
krDebugOut DEB_WARN,"ConvertPatchStringToBinary: enter"
|
|
mov cx, cbBinary
|
|
jcxz cpstb_bad
|
|
|
|
les di, lpBinary
|
|
lds si, lpString
|
|
sub ah, ah
|
|
|
|
cpstb_loop:
|
|
lodsb
|
|
cmp al, 0
|
|
je short cpstb_maybe_good
|
|
cmp al, ' '
|
|
je cpstb_loop
|
|
cmp al, ','
|
|
je cpstb_loop
|
|
cmp al, '0'
|
|
jb short cpstb_bad
|
|
cmp al, '9'
|
|
ja short cpstb_maybe_lower
|
|
|
|
; digit
|
|
sub al, '0'
|
|
jmp short cpstb_have_nibble
|
|
|
|
cpstb_maybe_lower:
|
|
or al, 20h ; map upper-case to lower-case
|
|
cmp al, 'f'
|
|
ja short cpstb_bad
|
|
cmp al, 'a'
|
|
jb short cpstb_bad
|
|
|
|
; lower-case
|
|
sub al, 'a'-10
|
|
|
|
cpstb_have_nibble:
|
|
cmp ah, 0
|
|
jne short cpstb_store_byte
|
|
mov ah, al
|
|
or ah, 80h
|
|
jmp cpstb_loop
|
|
|
|
cpstb_store_byte:
|
|
shl ah, 4
|
|
or al, ah
|
|
stosb
|
|
sub ah, ah
|
|
loop cpstb_loop
|
|
cpstb_bad:
|
|
krDebugOut DEB_ERROR, "ConvertPatchStringToBinary: bad char #al, or bad buffer size"
|
|
sub ax, ax
|
|
jmp short cpstb_end
|
|
cpstb_maybe_good:
|
|
cmp ah, 0 ; odd-length input string ?
|
|
jne cpstb_bad
|
|
cpstb_good:
|
|
or al, 1
|
|
cpstb_end:
|
|
cEnd
|
|
|
|
|
|
; PatchAppSegWorker
|
|
;
|
|
; Do the work of patching an app segment.
|
|
;
|
|
; Arguments:
|
|
; wSeg - the segment we are about to patch
|
|
; wPartySeg - a data aliase of the segment we are about to patch
|
|
; cbOriginalSeg - the value returned by GlobalSize(wSeg)
|
|
; lpcbCurrentSeg - ptr to the current size of the segment
|
|
; lpBinaryPatch - the APPPATCH struct to apply to wSeg
|
|
;
|
|
; Returns:
|
|
; VOID
|
|
;
|
|
; Registers Preserved:
|
|
; DI, SI, DS
|
|
|
|
assumes ds, nothing
|
|
assumes es, nothing
|
|
|
|
;VOID
|
|
cProc PatchAppSegWorker, <PUBLIC, NEAR>,<si,di,ds>
|
|
parmW wSeg
|
|
parmW wPartySeg
|
|
parmW cbOriginalSeg
|
|
parmD lpcbCurrentSeg
|
|
parmD lpBinaryPatch
|
|
|
|
localW wAddPartySeg
|
|
|
|
cBegin
|
|
lds si, lpBinaryPatch
|
|
mov es, wPartySeg
|
|
krDebugOut DEB_WARN,"PatchAppSegWorker: applying patch to party seg #es"
|
|
|
|
mov al, [si].AP_COMMON.ap_format
|
|
cmp al, AP_FORMAT_REPLACE
|
|
jne short pasw_maybe_add
|
|
|
|
; Replace some code in a segment.
|
|
krDebugOut DEB_WARN,"PatchAppSegWorker: type==replace"
|
|
|
|
; Check size
|
|
mov ch, 0
|
|
mov cl, [si].AP_REPLACE.apr_cb
|
|
mov al, (AP_REPLACE.apr_abOld - AP_REPLACE.apr_format)
|
|
add al, cl
|
|
add al, cl
|
|
cmp al, [si].AP_REPLACE.apr_cbSize
|
|
|
|
if KDEBUG
|
|
je short @F
|
|
mov ah, ch
|
|
mov dh, ch
|
|
mov dl, [si].AP_REPLACE.apr_cbSize
|
|
krDebugOut DEB_ERROR,"PatchAppSegWorker: actual size (#ax) != apa_cbSize (#dx)"
|
|
@@:
|
|
endif
|
|
jne pasw_end
|
|
mov di, [si].AP_REPLACE.apr_offset
|
|
add di, cx
|
|
cmp di, cbOriginalSeg
|
|
ja pasw_replace_offset_too_large
|
|
|
|
sub di, cx
|
|
add si, (AP_REPLACE.apr_abOld - AP_REPLACE.apr_format)
|
|
repe cmpsb ; compare old bytes to hExe bytes
|
|
jne pasw_repl_no_match
|
|
|
|
mov si, lpBinaryPatch.lo
|
|
mov ch, 0
|
|
mov cl, [si].AP_REPLACE.apr_cb
|
|
add si, (AP_REPLACE.apr_abOld - AP_REPLACE.apr_format)
|
|
add si, cx ; skip over the old bytes
|
|
sub di, cx ; rewind to the patch area start
|
|
rep movsb ; replace the bytes
|
|
jmp pasw_end
|
|
pasw_maybe_add:
|
|
cmp al, AP_FORMAT_ADD
|
|
jne pasw_bad_format
|
|
|
|
; Add some code to the segment.
|
|
krDebugOut DEB_WARN,"PatchAppSegWorker: type==add"
|
|
|
|
; Check size
|
|
mov ch, 0
|
|
mov cl, [si].AP_ADD.apr_cb
|
|
mov al, (AP_ADD.apa_abNew - AP_ADD.apa_format)
|
|
add al, cl
|
|
cmp al, [si].AP_ADD.apa_cbSize
|
|
if KDEBUG
|
|
je short @F
|
|
mov ah, ch
|
|
mov dh, ch
|
|
mov dl, [si].AP_ADD.apa_cbSize
|
|
krDebugOut DEB_ERROR,"PatchAppSegWorker: actual size (#ax) != apa_cbSize (#dx)"
|
|
@@:
|
|
endif
|
|
jne pasw_end
|
|
|
|
; Make sure the add is beyond the original segment.
|
|
mov di, [si].AP_ADD.apa_offset
|
|
cmp di, cbOriginalSeg
|
|
jb short pasw_offset_too_small
|
|
|
|
; Grow the segment if necessary.
|
|
mov ah, 0
|
|
mov al, [si].AP_ADD.apa_cb
|
|
add di, ax
|
|
|
|
; See if the segment is already big enough.
|
|
les bx, lpcbCurrentSeg
|
|
cmp di, es:[bx]
|
|
jbe short pasw_do_add
|
|
|
|
; Segment too small. Grow it.
|
|
cCall GlobalRealloc,<wSeg,0,di,0>
|
|
; Make sure we got the same sel back.
|
|
mov cx, wSeg
|
|
and al, not 1
|
|
and cl, not 1
|
|
cmp ax, cx
|
|
jne short pasw_repl_realloc_failed
|
|
|
|
; Save the new size of the segment.
|
|
les bx, lpcbCurrentSeg
|
|
mov es:[bx], di
|
|
|
|
pasw_do_add:
|
|
mov ch, 0
|
|
mov cl, [si].AP_ADD.apa_cb
|
|
|
|
;Since wSeg may have grown, create a new party seg.
|
|
mov bx, wSeg
|
|
mov ax, 000Ah ;DPMI, Create Code Segment Alias
|
|
int 031h
|
|
mov es, ax
|
|
|
|
sub di, cx
|
|
add si, (AP_ADD.apa_abNew - AP_ADD.apa_format)
|
|
rep movsb ; add the bytes
|
|
cCall FreeSelector, <es>
|
|
|
|
if KDEBUG
|
|
jmp short pasw_end
|
|
endif
|
|
|
|
pasw_bad_format:
|
|
if KDEBUG
|
|
sub ah, ah
|
|
krDebugOut DEB_ERROR,"PatchAppSegWorker: unknown format #ax"
|
|
jmp short pasw_end
|
|
endif
|
|
pasw_repl_realloc_failed:
|
|
if KDEBUG
|
|
mov ax, wSeg
|
|
krDebugOut DEB_ERROR,"PatchAppSegWorker: realloc failed on seg #ax"
|
|
jmp short pasw_end
|
|
endif
|
|
pasw_repl_no_match:
|
|
if KDEBUG
|
|
krDebugOut DEB_WARN,"PatchAppSegWorker: replace failed in seg #es"
|
|
jmp short pasw_end
|
|
endif
|
|
pasw_offset_too_small:
|
|
if KDEBUG
|
|
mov cx, cbOriginalSeg
|
|
krDebugOut DEB_ERROR,"PatchAppSegWorker: add offset (#di) < size (#cx)"
|
|
jmp short pasw_end
|
|
endif
|
|
pasw_replace_offset_too_large:
|
|
if KDEBUG
|
|
mov cx, cbOriginalSeg
|
|
krDebugOut DEB_ERROR,"PatchAppSegWorker: replace offset (#di) > size (#cx)"
|
|
endif
|
|
pasw_end:
|
|
|
|
cEnd
|
|
|
|
|
|
; PatchAppSeg
|
|
;
|
|
; Apply any patches for the given segment.
|
|
;
|
|
; Arguments:
|
|
; hkeyPatchApp - reg key containing patches for this app
|
|
; wSegNo - number of the segment in the module
|
|
; wSeg - selector of the segment
|
|
;
|
|
; Returns:
|
|
; BOOL - ax!=0 iff one or more patches applied
|
|
;
|
|
; Registers Preserved:
|
|
; CX, DI, SI, DS, ES
|
|
|
|
assumes ds, nothing
|
|
assumes es, nothing
|
|
|
|
;BOOL
|
|
cProc PatchAppSeg, <PUBLIC, FAR>,<cx,si,di,ds,es>
|
|
parmD hkeyPatchApp
|
|
parmW wSegNo
|
|
parmW wSeg
|
|
|
|
szKey_size = 5
|
|
szValString_size = 32
|
|
abValData_size = 100
|
|
abBinaryPatch_size = 100
|
|
|
|
localD hkey
|
|
localV szKey,szKey_size ; BUGBUG need a better size
|
|
localV szValString,szValString_size ; BUGBUG need a better size
|
|
localV abValData,abValData_size ; BUGBUG need a better size
|
|
localV abBinaryPatch,abBinaryPatch_size ; BUGBUG need a better size
|
|
localD cbValString
|
|
localD cbValData
|
|
localD dwType
|
|
localD dwIndex
|
|
localW cbOriginalSeg
|
|
localW cbCurrentSeg
|
|
localW wPartySeg
|
|
|
|
cBegin
|
|
if KDEBUG
|
|
mov ax, wSegNo
|
|
mov bx, wSeg
|
|
krDebugOut DEB_WARN,"PatchAppSeg: enter, (wSegNo #ax) (wSeg #bx)"
|
|
endif
|
|
push wSeg
|
|
call GlobalSize
|
|
mov cbOriginalSeg,ax
|
|
mov cbCurrentSeg,ax
|
|
|
|
; Segment number is the subkey.
|
|
|
|
lea si, szKey
|
|
cCall Far_htoa0, <ss, si, wSegNo>
|
|
mov bx, ax
|
|
mov byte ptr ss:[bx], 0 ; NULL terminator
|
|
|
|
; Get the key for this module/seg pair.
|
|
lea si, szKey
|
|
lea di, hKey
|
|
cCall RegOpenKey32, <hkeyPatchApp, ss, si, ss, di>
|
|
or ax,dx
|
|
jnz pas_no_patches
|
|
|
|
; Turn off the code bit for the seg to make it writeable.
|
|
; NB - Bail if this is a data segment.
|
|
|
|
|
|
|
|
mov bx, seg gdtdsc
|
|
mov ds, bx
|
|
assume ds:nothing
|
|
mov ds, ds:gdtdsc
|
|
mov bx, wSeg
|
|
and bl, not 7
|
|
test byte ptr ds:[bx+5], DSC_CODE_BIT
|
|
jz pas_no_patches ; bail if data seg
|
|
|
|
mov bx, wSeg
|
|
mov ax, 000Ah ;DPMI, Create Code Segment Alias
|
|
int 031h
|
|
mov wPartySeg, ax
|
|
|
|
; Mark this code segment not discardable so we don't have to deal
|
|
; with patching it again.
|
|
call Far_genter
|
|
mov dx, wSeg
|
|
call Far_pdref
|
|
; ds:esi = arena record
|
|
and ds:[esi].pga_flags, not (GA_DISCARDABLE or GA_DISCCODE)
|
|
call Far_gleave
|
|
|
|
or dwIndex, -1
|
|
pas_loop:
|
|
sub ecx, ecx
|
|
inc dwIndex
|
|
|
|
push dword ptr hkey
|
|
push dword ptr dwIndex
|
|
mov cbValString, szValString_size
|
|
mov cbValData, abValData_size
|
|
push ss
|
|
lea ax, szValString
|
|
push ax
|
|
push ss
|
|
lea ax, cbValString
|
|
push ax
|
|
push ecx
|
|
push ss
|
|
lea ax, dwType
|
|
push ax
|
|
push ss
|
|
lea ax, abValData
|
|
push ax
|
|
push ss
|
|
lea ax, cbValData
|
|
push ax
|
|
cCall RegEnumValue32
|
|
or ax, dx
|
|
jnz pas_loop_done
|
|
if KDEBUG
|
|
lea bx, szValString
|
|
krDebugOut DEB_WARN,"PatchAppSeg: found patch @ss:bx"
|
|
endif
|
|
|
|
cmp dwType, REG_BINARY
|
|
jne short pas_bad_type
|
|
|
|
lea bx, abValData ; ss:bx points to patch
|
|
movzx ecx, ss:[bx].AP_COMMON.ap_cbSize
|
|
cmp cbValData, ecx
|
|
if KDEBUG
|
|
je short @F
|
|
push bx
|
|
mov eax, cbValData
|
|
mov edx, eax
|
|
ror edx, 16
|
|
mov ebx, ecx
|
|
ror ebx, 16
|
|
krDebugOut DEB_ERROR,"PatchAppSeg: actual size (#dx:#ax) != ap_cbSize (#bx:#cx)"
|
|
pop bx
|
|
@@:
|
|
endif
|
|
jne pas_loop
|
|
|
|
pas_apply_patch:
|
|
lea ax, cbCurrentSeg ; ss:ax points to curr seg size
|
|
; Now apply the patch.
|
|
|
|
cCall PatchAppSegWorker,<wSeg,wPartySeg,cbOriginalSeg,ss,ax,ss,bx>
|
|
jmp pas_loop
|
|
|
|
pas_bad_type:
|
|
if KDEBUG
|
|
mov eax, dwType
|
|
mov edx, eax
|
|
ror edx, 16
|
|
krDebugOut DEB_WARN,"PatchAppSeg: unimplemented type #dx:#ax"
|
|
endif
|
|
jmp pas_loop
|
|
|
|
pas_no_patches:
|
|
sub ax, ax
|
|
jmp short pas_end
|
|
|
|
pas_loop_done:
|
|
if KDEBUG
|
|
test dx, dx
|
|
jnz short @F
|
|
cmp ax, ERROR_NO_MORE_ITEMS
|
|
je short pas_cleanup
|
|
@@:
|
|
krDebugOut DEB_WARN,"PatchAppSeg: unexpected error #dx#ax"
|
|
endif
|
|
pas_cleanup:
|
|
cCall FreeSelector, <wPartySeg>
|
|
|
|
cCall RegCloseKey32, <hkey>
|
|
or al, 1 ; ax!=0 marks patch found in reg
|
|
pas_end:
|
|
cEnd
|
|
|
|
sEnd NRESCODE
|
|
|
|
end
|