I have a directory of files, a HashControlFile.txt
which contains the SHA256 sum calculated from the files in the directory, and a need to iterate through these files in a way that calculates and formats the output from the CertUtil
utility in Windows and checks between the directory files and the HashControlFile and provides a framework for performing additional action whether a match was found or not between them.
The HashControlFile.txt
only contains SHA256sum entries, a new one each line, such as:
181210f8f9c779c26da1d9b2075bde0127302ee0e3fca38c9a83f5b1dd8e5d3b
for a file who's contents are simply 123
.
I have two elements of this puzzle already. To loop through the directory any produce a SHA256sum the below produces such in a way that formats it to prevent hex pairings that can occur between different versions of the CertUtil.exe
on Windows:
for %%i in (\\FileDirectory\*) do (
@For /F "Delims=" %%G In ('%SystemRoot%\System32\certutil.exe -HashFile "%%i" SHA256 2^>NUL ^| %SystemRoot%\System32\find.exe /V ":"') Do @Set "hash=%%G" & SetLocal EnableDelayedExpansion & For %%H In ("!hash: =!") Do @EndLocal & Echo(%%~H) >> \\OtherDirectory\HashControlFile.txt
)
Additionally, to loop through the directory and compare the between the files and HashControlFile I have the below piece:
FOR /f "delims=" %%b IN (\\FileDirectory\*) DO (
FOR /f %%y IN (####Produce FORMATTED SHA256SUM HERE####) do (
findstr /x "%%y" \\OtherDirectory\HashControlFile.txt > NUL
IF ERRORLEVEL 1 (
ECHO "%%b" NOT found
) ELSE (
ECHO "%%b" found
)
)
)
My difficulty has been in using the above element within the second element to loop through using formatted CertUtil output. Any help would be appreciated
The SHA256 hash lookup for each file in a directory in a file containing only SHA256 hash values can be done with the following batch file:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Directory=\\ServerName\ShareName\FilesDirectory"
set "HashFile=\\ServerName\ShareName\OtherDirectory\HashControlFile.txt"
if not exist "%HashFile%" echo ERROR: Missing file: "%HashFile%"& goto EndBatch
for %%G in ("%Directory%\*") do if /I not "%%G" == "%HashFile%" if not "%%G" == "%~f0" (
set "FileName=%%~nxG"
for /F "delims=" %%H In ('%SystemRoot%\System32\certutil.exe -HashFile "%%G" SHA256 2^>NUL ^| %SystemRoot%\System32\find.exe /V ":"') do (
setlocal EnableDelayedExpansion
set "HashValue=%%H"
%SystemRoot%\System32\findstr.exe /L /X /C:"!HashValue: =!" "!HashFile!" >nul
if not errorlevel 1 (
echo FOUND: "!FileName!"
) else (
echo MISSING: "!FileName!"
)
endlocal
)
)
:EndBatch
endlocal
The environment variable Directory
is defined with the directory containing the files of which SHA256 hash values should be determined and looked up in the file with the hash values.
The environment variable HashFile
is defined with the fully qualified file name of the ASCII/ANSI encoded text file containing all the SHA256 hash values.
Then is checked the existence of the file with the hash values. An error message is output and nothing else is done if the specified hash list file does not exist at all. See also single line with multiple commands using Windows batch file for an explanation of the unconditional command operator &
which is used in the command line with the first IF condition.
The most outer FOR loop processes all non-hidden files in specified directory.
The second IF condition with a case-insensitive string comparison is for ignoring the file with the hash values if this file is in the same directory as the files to process which is not the case with the paths specified in the code above, but is in general possible.
The third IF condition with a case-sensitive string comparison is for ignoring the batch file if the currently processed batch file is in the directory of the files to process.
The name of the file with file extension but without path of the current file is assigned to the environment variable FileName
. Delayed variable expansion is disabled on execution of this command line and the next command line resulting in entire batch file working also for files with one or more exclamation mark in fully qualified file name.
The inner FOR loop runs in background one more command process with %ComSpec% /c
and the command line specified within '
appended as additional arguments.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>NUL
and |
. The redirection operators >
and |
must be escaped with caret character ^
on FOR command line to be interpreted as literal characters when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.
The SHA256 hash value of the file is assigned to the loop variable H
which is in format:
18 12 10 f8 f9 c7 79 c2 6d a1 d9 b2 07 5b de 01 27 30 2e e0 e3 fc a3 8c 9a 83 f5 b1 dd 8e 5d 3b
The spaces must be removed to get the SHA256 hash value to look up in the file with all the hash values.
Delayed variable expansion is enabled now before assigning the hash value with the spaces to an environment variable with name HashValue
.
Next is executed FINDSTR to search in the file with all the hash values literally and case-sensitive for a line which matches completely with the current hash value with all spaces removed using a delayed expanded variable reference with substitution of every space character by an empty string, i.e. removal of all spaces from the hash value. There is of no interest of the found line(s) if the hash value is found at all in the hash list file which is the reason for redirecting the output of FINDSTR to device NUL to suppress the output of the found line(s). There is only of interest if the hash value is found or not found by FINDSTR in the hash list file.
FINDSTR exits with exit code 1
if there is no match and with exit code 0
if there is at least one match. The exit code is assigned by cmd.exe
to its internal dynamic variable ERRORLEVEL
. This exit code is evaluated for being less than 1 which means 0 to output the FOUND:
or the MISSING:
message with the current file name.
Then the previous execution environment is restored with an again disabled delayed variable expansion. Read this answer for details about the commands SETLOCAL and ENDLOCAL as there is much more done on each iteration of the inner FOR loop in the background by cmd.exe
than just changing the status of delayed variable expansion.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
certutil /?
or certutil -?
echo /?
endlocal /?
find /?
findstr /?
for /?
goto /?
if /?
set /?
setlocal /?