HEXDUMP.BAT version 2.1 using CERTUTIL
Posted: 04 Sep 2018 13:46
Over 7 years ago I wrote a pure batch HEXDUMP.BAT script (and function) that could read a binary file and write out a nicely formatted hex dump of the content. That script relied on a clever FC technique to read the data. It works great, but is very slow.
Now that we have a bunch of options for CERTUTIL -encodeHex formatting, I decided to completely redesign HEXDUMP to use CERTUTIL. The new HEXDUMP.BAT doesn't do anything that CERTUTIL can't already do, it just makes it much easier to use.
Pros compared to the old FC based version
HEXDUMP.BAT
Now that we have a bunch of options for CERTUTIL -encodeHex formatting, I decided to completely redesign HEXDUMP to use CERTUTIL. The new HEXDUMP.BAT doesn't do anything that CERTUTIL can't already do, it just makes it much easier to use.
Pros compared to the old FC based version
- Much faster
- Ability to read stdin, so it can be used with pipes and redirection
- Ability to control line terminator in output: \r\n, \n, or none
- Ability to write Unicode output
- Simpler to control the output format
- No longer pure batch - I use a bit of JScript to read binary piped or redirected input.
- No longer able to dump a portion of a file, but this was rarely used before because large files were so slow.
- Not quite as much flexibility in the output format, but all the important features are still there.
HEXDUMP.BAT
Code: Select all
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:::
:::HEXDUMP [/Option [Value]]...
:::
::: Writes the content of stdin as hexadecimal to stdout, with 16 bytes per line,
::: using the following format:
:::
::: OOOOOOO XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX AAAAAAAAAAAAAAAA
:::
::: where:
:::
::: 0000000 = the hexadecimal offset within the input
::: XX = the hexadecimal value of a byte
::: AAAAAAAAAAAAAAAA = the bytes as ASCII (control codes and non-ASCII as .)
:::
::: Output is encoded as ASCII, with each line terminated by CarriageReturn
::: LineFeed.
:::
::: The behavior can be modified by appending any combination of the following
::: options:
:::
::: /I InFile - Input from InFile instead of stdin
::: /O OutFile - Output to OutFile instead of stdout: - overwrites InFile
::: /NA - No ASCII
::: /NO - No Offsets
::: /R - Raw hexadecimal on a single line, with no space between bytes
::: /LF - LineFeed as line terminator instead of CarriageReturn LineFeed
::: /NL - No Line terminators, all output on one line without line terminator
::: /U - Unicode encoded output with BOM (UTF-16)
::: /V - Write version info to stdout
::: /? - Write this help to stdout
:::
:::HEXDUMP.BAT version 2.1 was written by Dave Benham
:::and is maintained at https://www.dostips.com/forum/viewtopic.php?f=3&t=8816
@echo off
setlocal disableDelayedExpansion
set "tempRoot="
:: Define options
set "/options= /I:"" /LF: /NA: /NL: /NO: /O:"" /R: /U: /V: /?: "
:: Set default option values
for %%O in (%/options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
set "/?="
:GetOptions
if not "%~1"=="" (
set "/test=%~1"
setlocal enableDelayedExpansion
if "!/test:~0,1!" neq "/" call :exitErr "Invalid argument" 1
set "/test=!/options:*%~1:=! "
if "!/test!"=="!/options! " (
endlocal
call :exitErr "Invalid option %~1" 1
) else if "!/test:~0,1!"==" " (
endlocal
set "%~1=1"
) else (
endlocal
set "%~1=%~2"
shift /1
)
shift /1
goto :GetOptions
)
if defined /? (
for /f "delims=: tokens=*" %%A in ('findstr "^:::" "%~f0"') do @echo(%%A
exit /b 0
)
if defined /V (
for /f "delims=: tokens=*" %%A in ('findstr /bic:":::HEXDUMP.BAT version" "%~f0"') do echo(%%A
exit /b 0
)
if "%/O%"=="-" if not defined /I call :exitErr "Cannot write to stdin" 1
set /a "type= 11 - 0%/NA% - 0%/NO%*2"
if %type%==9 set "type=5"
if defined /R set "type=12"
set /a "type|= (0%/LF%*0x80000000 | 0%/NL%*0x40000000)"
if %type% lss 0 cmd /c exit /b %type%
if %type% lss 0 set "type=0x%=exitCode%"
set "unicode="
if defined /U set "unicode=-UnicodeText"
if defined /I set "in=%/I%" & goto :getOutput
call :getTempRoot
set "in=%tempRoot%.stdin"
cscript //nologo //E:JScript "%~f0" "%in%"
:getOutput
if "%/O%" equ "-" set "out=%~1" & goto :go
if defined /O set "out=%/O%" & goto :go
if not defined tempRoot call :getTempRoot
set "out=%tempRoot%.hexdump"
:go
(certutil %unicode% -f -encodehex "%in%" "%out%" %type% || echo ERROR: HexDump failed during CertUtil processing) | findstr "ERROR" >&2 && call :exit 1
if not defined /O (findstr "^" "%out%") else echo Hexdump successfully written to "%out%".
call :exit 0
:getTempRoot
set "tempRoot="
if defined temp for %%T in ("%temp%") do set "tempRoot=%%~fT\"
if not defined tempRoot if defined tmp for %%T in ("%tmp%") do set "tempRoot=%%~fT\"
set "tempRoot=%tempRoot%%~nx0.%time::=_%.%random%"
if exist "%tempRoot%.lock" goto :getTempRoot
2>nul (break >"%tempRoot%.lock") || goto :getTempRoot
exit /b
:exitErr
>&2 echo ERROR: %~1.
shift
:: Fall through to :exit
:exit
if defined tempRoot del "%tempRoot%.*"
(goto) 2>nul & exit /b %1
************* JScript portion - read stdin and write to file defined by arg0 **********/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var out = fso.OpenTextFile(WScript.Arguments(0),2,true);
var chr;
while( !WScript.StdIn.AtEndOfStream ) {
chr=WScript.StdIn.Read(1000000);
out.Write(chr);
}