Code: Select all
C:\test>certutil -hashfile test.bat md5
MD5 hash of file test.bat:
89 68 0c 14 1d 7a 1f 00 a2 43 79 c6 b1 ac fd b9
CertUtil: -hashfile command completed successfully.
There were a few minor issues to overcome:
- CERTUTIL delimits hex pairs with spaces, whereas md5sum etc. uses a continuous stream of hex digits.
- CERTUTIL gives an error if the file is empty (size 0). I had to hardcode the various hashes for zero length strings.
- I needed a way to capture relative paths when using the /S option. I opted to use FORFILES, which is very convenient, but slow.
- I needed a way to capture stdin to a temp file, without any modification. I initially used FINDSTR, but that appends \r\n if the last line does not end with \n. So I converted to using JScript, thus HASHSUM is no longer pure batch.
When preparing a manifest, it defaults to SHA256, but you can change the algorithm with the /A option.
When checking a manifest with the /C option, you have three options for specifying the algorithm, with higher precedence at the top.
- Explicitly specify the algorithm with the /A option
- Derive the algorithm from the extension of the manifest
- Automatically detect the algorithm based on the length of the first hash in the manifest
Examples of usage follow the code in this post.
HASHSUM.BAT version 1.8
Code: Select all
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
@goto :Batch
::::
::::HASHSUM.BAT history
::::
:::: v1.8 2021-01-19 - Improved detection of current code page as per work at
:::: https://www.dostips.com/forum/viewtopic.php?f=3&t=8533
:::: v1.7 2021-01-19 - Added /U option to support Unicode in file names
:::: v1.6 2019-02-26 - Modify /F and /FR to support non-ASCII characters
:::: v1.5 2018-02-18 - Added /H, /F, /FR and /NH options.
:::: v1.4 2016-12-26 - Convert /A value to upper case because some Windows
:::: versions are case sensitive. Also improve JScript file
:::: read performance by reading 1000000 bytes instead of 1.
:::: v1.3 2016-12-17 - Bug fixes: Eliminate unwanted \r\n from temp file by
:::: reading stdin with JScript instead of FINDSTR.
:::: Fix help to ignore history.
:::: v1.2 2016-12-07 - Bug fixes: Exclude FORFILES directories and
:::: correct setlocal/endlocal management in :getOptions
:::: v1.1 2016-12-06 - New /V option, and minor bug fixes.
:::: v1.0 2016-12-05 - Original release
:::
:::HASHSUM [/Option [Value]]... [File]...
:::
::: Print or check file hashes using any of the following standard
::: hash algorithms: MD5, SHA1, SHA256, SHA384, or SHA512.
:::
::: HASHSUM always does a binary read - \r\n is never converted to \n.
:::
::: In the absence of /C, HASHSUM computes the hash for each File, and writes
::: a manifest of the results. Each line of output consists of the hash value,
::: followed by a space and an asterisk, followed by the File name. The default
::: hash alogrithm is sha256. File may include wildcards, but must not contain
::: any path information.
:::
::: If File is not given, then read from standard input and write the hash
::: value only, without the trailing space, asterisk, or file name.
:::
::: Options:
:::
::: /? - Prints this help information to standard output.
:::
::: /?? - Prints paged help using MORE.
:::
::: /V - Prints the HASHSUM.BAT version.
:::
::: /H - Prints the HASHSUM.BAT history.
:::
::: /U - Unicode mode: Experimental setting that attempts to support
::: Unicode in filenames. Please report any problems to the DosTips
::: forum at https://www.dostips.com/forum/viewtopic.php?f=3&t=7592
:::
::: /A Algorithm
:::
::: Specifies one of the following hash algorithms:
::: MD5, SHA1, SHA256, SHA384, SHA512
:::
::: /P RootPath
:::
::: Specifies the root path for operations.
::: The default is the current directory.
:::
::: /S - Recurse into all Subdirectories. The relative path from the root
::: is included in the file name output.
::: This option is ignored if used with /C.
:::
::: /I - Include the RootPath in the file name output.
::: This option is ignored if used with /C.
:::
::: /T - Writes a space before each file name, rather than an
::: asterisk. However, files are still read in binary mode.
::: This option is ignored if used with /C.
:::
::: /C - Read hash values and file names from File (the manifest), and verify
::: that local files match. File may include path information with /C.
:::
::: If File is not given, then read hash and file names from standard
::: input. Each line of input must have a hash, followed by two spaces,
::: or a space and an asterisk, followed by a file name.
:::
::: If /A is not specified, then the algorithm is determined by the
::: File extension. If the extension is not a valid algorithm, then
::: the algorithm is derived based on the length of the first hash
::: within File.
:::
::: Returns ERRORLEVEL 1 if any manifest File is not found or is invalid,
::: or if any local file is missing or does not match the hash value in
::: the manifest. If all files are found and match, then returns 0.
:::
::: /F FileName
:::
::: When using /C, only check lines within the manifest that contain the
::: string FileName. The search ignores case.
:::
::: /FR FileRegEx
:::
::: When using /C, only check lines within the manifest that match the
::: FINDSTR regular expression FileRegEx. The search ignores case.
:::
::: /NH - (No Headers) Suppresses listing of manifest name(s) when using /C.
:::
::: /NE - (No Errors) Suppresses error messages when using /C.
:::
::: /NM - (No Matches) Suppresses listing of matching files when using /C.
:::
::: /NS - (No Summary) Suppresses summary information when using /C.
:::
::: /Q - (Quiet) Suppresses all output when using /C.
:::
:::HASHSUM.BAT version 1.8 was written by Dave Benham
:::maintained at https://www.dostips.com/forum/viewtopic.php?f=3&t=7592
============= :Batch portion ===========
@echo off
setlocal disableDelayedExpansion
:: Define options
set "options= /A:"" /C: /I: /P:"" /S: /T: /?: /??: /NH: /NE: /NM: /NS: /Q: /V: /H: /U: /F:"" /FR:"" "
:: Set default option values
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
set "/?="
set "/??="
:getOptions
if not "%~1"=="" (
set "test=%~1"
setlocal enableDelayedExpansion
if "!test:~0,1!" neq "/" endlocal & goto :endOptions
set "test=!options:*%~1:=! "
if "!test!"=="!options! " (
endlocal
>&2 echo Invalid option %~1
exit /b 1
) else if "!test:~0,1!"==" " (
endlocal
set "%~1=1"
) else (
endlocal
set "%~1=%~2"
shift /1
)
shift /1
goto :getOptions
)
:endOptions
:: Display paged help
if defined /?? (
(for /f "delims=: tokens=*" %%A in ('findstr "^:::[^:] ^:::$" "%~f0"') do @echo(%%A)|more /e
exit /b 0
) 2>nul
:: Display help
if defined /? (
for /f "delims=: tokens=*" %%A in ('findstr "^:::[^:] ^:::$" "%~f0"') do echo(%%A
exit /b 0
)
:: Display version
if defined /V (
for /f "delims=: tokens=*" %%A in ('findstr /ric:"^:::hashsum.bat version" "%~f0"') do echo(%%A
exit /b 0
)
:: Display history
if defined /H (
for /f "delims=: tokens=*" %%A in ('findstr "^::::" "%~f0"') do echo(%%A
exit /b 0
)
:: Unicode filename support
set "find=find"
if defined /U (
for /f "tokens=*" %%A in ('chcp') do for %%B in (%%A) do set "chcp=%%~nB"
set "find=findstr /l"
>nul chcp 65001
)
:: If no file specified, then read stdin and write to a temp file
set "tempFile="
if "%~1" equ "" set "tempFile=%~nx0.%time::=_%.%random%.tmp"
if defined tempFile cscript //nologo //E:JScript "%~f0" "%temp%\%tempFile%"
if defined /P cd /d "%/P%" || exit /b 1
if defined /C goto :check
:generate
if defined tempFile cd /d "%temp%"
if not defined /A set "/A=sha256"
if defined /S set "/S=/s"
if defined /T (set "/T= ") else set "/T=*"
call :defineEmpty
if not defined /P goto :generateLoop
if not defined /I goto :generateLoop
if "%/P:~-1%" equ "\" (set "/I=%/P:\=/%") else set "/I=%/P:\=/%/"
set "rtn=0"
:generateLoop
(
for /f "delims=" %%F in (
'forfiles %/s% /m "%tempFile%%~1" /c "cmd /c if @isdir==FALSE echo @relpath" 2^>nul'
) do for /f "delims=" %%A in (
'certutil.exe -hashfile %%F %/A% ^| %find% /v ":" ^|^| if %%~zF gtr 0 (echo X^) else echo %empty%'
) do (
set "file=%%~F"
set "hash=%%A"
setlocal enableDelayedExpansion
set "file=!file:~2!"
if defined tempFile (
if !hash! equ X (
set "rtn=1"
echo ERROR
) else echo !hash: =!
) else (
if !hash! equ X (
set "rtn=1"
echo ERROR: !/I!!file!
) else echo !hash: =! !/T!!/I!!file:\=/!
)
endlocal
)
) || (
set "rtn=1"
echo MISSING: %/T%%1
)
shift /1
if "%~1" neq "" goto :generateLoop
if defined tempFile del "%tempFile%"
if defined /U >nul chcp %chcp%
exit /b %rtn%
:check
if defined /Q for %%V in (/NE /NM /NS /NH) do set "%%V=1"
if defined /F if defined /FR (
>&2 echo ERROR: /F and /FR cannot be combined
exit /b 1
)
set "searchTemp="
if defined /F (
set "searchTemp=%temp%\%~nx0.%time::=_%.%random%.search.tmp"
setlocal enableDelayedExpansion
(echo(!/F!) > "!%searchTemp!"
endlocal
set "file=" & set "freg=rem" & set "norm=rem"
) else if defined /FR (
set "searchTemp=%temp%\%~nx0.%time::=_%.%random%.search.tmp"
setlocal enableDelayedExpansion
(echo(!/FR!) > "!%searchTemp!"
endlocal
set "file=rem" & set "freg=" & set "norm=rem"
) else (
set "file=rem" & set "freg=rem" & set "norm="
)
set /a manifestCnt=missingManifestCnt=invalidCnt=missingCnt=failCnt=okCnt=0
:checkLoop
set "alogorithm=%/A%"
if defined tempFile set "tempFile=%temp%\%tempFile%"
for %%F in ("%tempFile%%~1") do call :checkFile "%%~F"
if defined tempFile del "%tempFile%"
shift /1
if "%~1" neq "" goto :checkLoop
if defined searchTemp del "%searchTemp%"
if not defined /NS (
echo ========== SUMMARY ==========
echo Total manifests = %manifestCnt%
echo Matched files = %okCnt%
echo(
if %missingManifestCnt% gtr 0 echo Missing manifests = %missingManifestCnt%
if %invalidCnt% gtr 0 echo Invalid manifests = %invalidCnt%
if %missingCnt% gtr 0 echo Missing files = %missingCnt%
if %failCnt% gtr 0 echo Failed files = %failCnt%
)
if defined /U >nul chcp %chcp%
set /a "1/(missingManifestCnt+invalidCnt+missingCnt+failCnt)" 2>nul && (
echo(
exit /b 1
)
exit /b 0
:checkFile
set /a manifestCnt+=1
if not defined /NH if defined tempfile (echo ---------- ^<stdin^> ----------) else echo ---------- %1 ----------
if not defined algorithm set "/A="
if not defined /A echo *.md5*.sha1*.sha256*.sha384*.sha512*|%find% /i "*%~x1*" >nul && for /f "delims=." %%A in ("%~x1") do set "/A=%%A"
findstr /virc:"^[0123456789abcdef][0123456789abcdef]* [ *][^ *?|<>]" %1 >nul 2>nul && (
if not defined /NE if defined tempFile (echo *INVALID: ^<stdin^>) else echo *INVALID: %1
set /a invalidCnt+=1
exit /b
)
(
%norm% for /f "usebackq tokens=1* delims=* " %%A in (%1) do (
%file% for /f "tokens=1* delims=* " %%A in ('type %1 ^| findstr /ilg:"%searchTemp%"') do (
%freg% for /f "tokens=1* delims=* " %%A in ('type %1 ^| findstr /irg:"%searchTemp%"') do (
set "hash0=%%A"
set "fileName=%%B"
if defined /A (call :defineEmpty) else call :determineFormat
setlocal enableDelayedExpansion
set "fileName=!fileName:/=\!"
for /f "tokens=1* delims=" %%C in (
'certutil.exe -hashfile "!fileName!" !/A! ^| %find% /v ":" ^|^| if exist "!fileName!" (echo !empty!^) else echo X'
) do set "hash=%%C"
if /i "!hash0!" equ "!hash: =!" (
if not defined /NM echo OK: !fileName!
endlocal
set /a okCnt+=1
) else if !hash! equ X (
if not defined /NE echo *MISSING: !fileName!
endlocal
set /a missingCnt+=1
) else (
if not defined /NE echo *FAILED: !fileName!
endlocal
set /a failCnt+=1
)
)
) 2>nul || if not defined /F if not defined /FR (
if not defined /NE echo *MISSING: %1
set /a missingManifestCnt+=1
)
exit /b
:determineFormat
if "%hash0:~127%" neq "" (
set "/A=SHA512"
) else if "%hash0:~95%" neq "" (
set "/A=SHA384"
) else if "%hash0:~63%" neq "" (
set "/A=SHA256"
) else if "%hash0:~39%" neq "" (
set "/A=SHA1"
) else set "/A=MD5"
:defineEmpty
if /i "%/A%"=="md5" (
set "empty=d41d8cd98f00b204e9800998ecf8427e"
set "/A=MD5"
) else if /i "%/A%"=="sha1" (
set "empty=da39a3ee5e6b4b0d3255bfef95601890afd80709"
set "/A=SHA1"
) else if /i "%/A%"=="sha256" (
set "empty=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
set "/A=SHA256"
) else if /i "%/A%"=="sha384" (
set "empty=38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"
set "/A=SHA384"
) else if /i "%/A%"=="sha512" (
set "empty=cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
set "/A=SHA512"
) else (
echo ERROR: Invalid /A algorithm>&2
(goto) 2>nul&exit /b 1
)
exit /b
************* JScript portion **********/
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);
}
Code: Select all
C:\test>hashsum /s /a md5 *.exe
8d443f2e93a3f0b67f442e4f1d5a4d6d *md5.exe
eb574b236133e60c989c6f472f07827b *md5sum.exe
9f036e40755daaa5a5fc141d3cbfbb99 *nGetPid.exe
36622f75d59bd1988bfd18a259ae82ef *Aacini Tools/Ascii.exe
f26e6cdc8f8bba5df1634bb631f6306f *Aacini Tools/Colorshow.exe
512b675126f22da6d50916b98222c5d7 *Aacini Tools/Cursorpos.exe
bda35b908b81f7dfad35beafd1582279 *Aacini Tools/Cursorsize.exe
4cf48fe5dca5638d0ddfd3429faf6abf *Aacini Tools/Flushinputbuffer.exe
d81a9a79847aa075bebb8ec054bd13e7 *Aacini Tools/Getkey.exe
b269ecf2bed68cef8d623c877c3b93d6 *Aacini Tools/Show.exe
ddbece33177224a8a2f44600cc71e76b *Aacini Tools/Stddate.exe
0d929b261d0981d3c46c04418af2ec6d *Aacini Tools/Stdtime.exe
34df06129a094d773c14092eb151a8e0 *Aacini Tools/Strlen.exe
c0a7f18cb075d39ab2d0978dd7a261dc *Aacini Tools/Textcolor.exe
3ff1f054f90a708e0fdeb8b4c81db24e *Aacini Tools/Window.exe
538d34e6b28951cb412761e634805f5b *Aacini Tools/bin/Hexchar.exe
dfc42151c290ae5bbbc448d211405489 *ansisys/AnsiSys.exe
6795af42d625abe4bd529798aba07bfd *bf/BFI.exe
5f02fcfe6262dfb852b5ce904f14e20d *bf/**censored**.exe
0e93af2d97030b34334b8f3200f5ca44 *calc/ColorChar.exe
512b675126f22da6d50916b98222c5d7 *calc/CursorPos.exe
82d93fd60f4686c82034f8a70dc0b269 *calc/GetInput.exe
59f74ed847f7b7fac82b809896b12f67 *choice/choice.exe
50c52220cd00b642ff4e5b85abd28557 *curl/curl.exe
33900b6c02c16fcf630b468a36156496 *fonts/hbFontEdit/FONTEDIT.EXE
c6e160e565a0b5764cf70ed258b9ffd9 *fonts/MittensKnight/FontEdit.exe
a89f984fbb6820cd505971f5f6d0ca89 *fonts/RasterFontEditor/RasterFontEditor.exe
e49097bb9659520ae8cb7cdddab1f5fd *pixelfnt_dev/PIXELFNT.EXE
69b8044bb90d89705289df7e10a5152a *SendMessage/SendMessage.exe
Code: Select all
C:\test>hashsum /c exe_files.md5
---------- "exe_files.md5" ----------
OK: md5.exe
OK: md5sum.exe
OK: nGetPid.exe
OK: Aacini Tools\Ascii.exe
OK: Aacini Tools\Colorshow.exe
OK: Aacini Tools\Cursorpos.exe
OK: Aacini Tools\Cursorsize.exe
OK: Aacini Tools\Flushinputbuffer.exe
OK: Aacini Tools\Getkey.exe
OK: Aacini Tools\Show.exe
OK: Aacini Tools\Stddate.exe
OK: Aacini Tools\Stdtime.exe
OK: Aacini Tools\Strlen.exe
OK: Aacini Tools\Textcolor.exe
OK: Aacini Tools\Window.exe
OK: Aacini Tools\bin\Hexchar.exe
OK: ansisys\AnsiSys.exe
OK: bf\BFI.exe
OK: bf\**censored**.exe
OK: calc\ColorChar.exe
OK: calc\CursorPos.exe
OK: calc\GetInput.exe
OK: choice\choice.exe
OK: curl\curl.exe
OK: fonts\hbFontEdit\FONTEDIT.EXE
OK: fonts\MittensKnight\FontEdit.exe
OK: fonts\RasterFontEditor\RasterFontEditor.exe
OK: pixelfnt_dev\PIXELFNT.EXE
OK: SendMessage\SendMessage.exe
========== SUMMARY ==========
Total manifests = 1
Matched files = 29
Code: Select all
C:\test>echo 9f036e40755daaa5a5fc141d3cbfbb99 *nGetPid.exe | hashsum /c
---------- <stdin> ----------
OK: nGetPid.exe
========== SUMMARY ==========
Total manifests = 1
Matched files = 1
Code: Select all
C:\test>echo Hello world|hashsum /a sha1
f0e19ccba8cd50a4288c368eeb655c524fedcf5f
Dave Benham