HEXDUMP.BAT version 2.1 using CERTUTIL

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
dbenham
Expert
Posts: 2289
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

HEXDUMP.BAT version 2.1 using CERTUTIL

#1 Post by dbenham » 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
  • 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
Cons compared to the old version
  • 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.
Full documentation is embedded within the script. Use HEXDUMP /? from the command line to get the help.

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);
}

dbenham
Expert
Posts: 2289
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: HEXDUMP.BAT version 2.1 using CERTUTIL

#2 Post by dbenham » 05 Sep 2018 08:16

I've updated the original post to version 2.1

The only change is some minor redesign of the exit process to ensure that temp files are "always" deleted, even if there is an error.


Dave Benham

Post Reply