Bad news about use of FINDSTR to output the remainder after the last substitution - it is unreliable because FINDSTR cannot process lines greater than 8191 bytes long when the input is piped or redirected.
I've got a pair of patchBin scripts based on CERTUTIL that work very well, and are reasonably fast. They rely on two little known facts that were documented on DosTips:
1) CERTUTIL /ENCODEHEX has an
optional format argument that can eliminate newlines from the output, and can output the hex as one continuous stream without spaces, or as hex pairs delimited by exactly one space.
2) In the absence of newlines,
SET /P will read exactly 1023 bytes, or the remainder of the file, whichever is smaller.
By processing 1023 input bytes at a time (via 2 or 3 SET /P statements) the following scripts need far fewer reads and writes, so the performance is significantly improved over Aacini's method that includes the address and the ASCII in the hex output and has only 16 input bytes per line.
My scripts would theoretically have a max input file size of 2 GB due to SET /A limits, but CERTUTIL /EncodeHex limits the output to 102,400,000 bytes.
This first script has no space delimiters in the hex output, and is limited to input of length 51,200,000 bytes. It will not work on XP because the chosen hex output format is not supported on XP.
Code: Select all
:: patchBin InFile OutFile|- "Offset:HexValue[ Offset:HexValue]..."
:: Max InFile length = 51,200,000 bytes
@echo off
setlocal enableDelayedExpansion
set /a "end=%~z1+1023"
set "out=%~2"
if !out!==- set "out=%~1"
set "changes=%~3"
certutil /f /encodehex %1 "%out%.hex" 0x4000000C >nul
call :modifyHex <"%out%.hex" >"%out%.new.hex"
certutil /f /decodehex "%out%.new.hex" "%out%" >nul
del "%out%.hex" "%out%.new.hex"
exit /b
:modifyHex
for /f "tokens=1*" %%A in ("!changes!") do (
set "changes=%%B"
for /f "delims=: tokens=1,2" %%C in ("%%A") do (
set /a "offset=%%C"
set "value=%%D"
)
)
for /l %%N in (1023 1023 %end%) do (
set "ln="
set /p "ln="
set "ln2="
set /p "ln2="
set "ln=!ln!!ln2!"
if !offset! lss %%N call :modify %%N
echo !ln!
)
exit /b
:modify
set /a "offset=(offset-%1+1023)*2, next=offset+2"
set "ln=!ln:~0,%offset%!!value!!ln:~%next%!"
set /a "offset=0x7FFFFFFF"
for /f "tokens=1*" %%A in ("!changes!") do (
set "changes=%%B"
for /f "delims=: tokens=1,2" %%C in ("%%A") do (
set /a "offset=%%C"
set "value=%%D"
)
)
if %offset% lss %1 goto :modify
exit /b 0
This second form works on XP, but uses space delimiters, so the max input file length is only 34,133,333 bytes
Code: Select all
:: patchBin InFile OutFile|- "Offset:HexValue[ Offset:HexValue]..."
:: Max InFile length = 34,133,333 bytes
@echo off
setlocal enableDelayedExpansion
set /a "end=%~z1+1023"
set "out=%~2"
if !out!==- set "out=%~1"
set "changes=%~3"
certutil /f /encodehex %1 "%out%.hex" 0x40000004 >nul
call :modifyHex <"%out%.hex" >"%out%.new.hex"
certutil /f /decodehex "%out%.new.hex" "%out%" >nul
del "%out%.hex" "%out%.new.hex"
exit /b
:modifyHex
for /f "tokens=1*" %%A in ("!changes!") do (
set "changes=%%B"
for /f "delims=: tokens=1,2" %%C in ("%%A") do (
set /a "offset=%%C"
set "value=%%D"
)
)
for /l %%N in (1023 1023 %end%) do (
set "ln="
set /p "ln="
set "ln2="
set /p "ln2="
set "ln3="
set /p "ln3="
set "ln=!ln!!ln2!!ln3!"
if !offset! lss %%N call :modify %%N
echo !ln!
)
exit /b
:modify
set /a "offset=(offset-%1+1023)*3, next=offset+3"
set "ln=!ln:~0,%offset%!!value! !ln:~%next%!"
set /a "offset=0x7FFFFFFF"
for /f "tokens=1*" %%A in ("!changes!") do (
set "changes=%%B"
for /f "delims=: tokens=1,2" %%C in ("%%A") do (
set /a "offset=%%C"
set "value=%%D"
)
)
if %offset% lss %1 goto :modify
exit /b 0
You can use either of the above with the following to patch cmd.exe to substitute SHOW for the ECHO command on Windows 10. Based on Aacini's post, the required offsets are not constant across versions and/or languages.
Code: Select all
C:\test>patchBin "%comspec%" "MyCmd.exe" "0x30a10:53 0x30a12:48 0x30a14:4F 0x30a16:57"
C:\test>myCmd /c show OK
OK
C:\test>
If you wanted to overwrite the original cmd.exe (Please don't
), then you would simply substitute a dash for "MyCmd.exe"
Dave Benham