Skip to content

Instantly share code, notes, and snippets.

@anonymous1184
Last active December 24, 2023 14:46
Show Gist options
  • Save anonymous1184/07e30be81ee0cd2e8c9fd75c7806c8a0 to your computer and use it in GitHub Desktop.
Save anonymous1184/07e30be81ee0cd2e8c9fd75c7806c8a0 to your computer and use it in GitHub Desktop.
Quick File Hashing

; Version: 2023.04.20.1
; Usages and examples: https://redd.it/m0kzdy
; Configuration ↓
wide := true ; Display the full hashes
; Enables/disabled algorithms
use := {}
use.MD2 := false
use.MD4 := false
use.MD5 := true
use.SHA1 := true
use.SHA256 := true
use.SHA384 := false
use.SHA512 := false
useLower := true ; Hashes in lowercase
; Configuration ↑
;
; Do not edit beyond this point
;
; Some speed-related settings
ListLines Off
SetBatchLines -1
Process Priority,, High
/* A user reported that for some (unknown) reason in one of his drives, the file
being sent to be hashed threw the "No file was selected" message if the filename
contained spaces, this fixes the issue even when is not supposed to happen.
*/
if (A_Args.Count() > 1) {
file := ""
for _, arg in A_Args
file .= arg " "
A_Args[1] := Trim(file)
}
; Verify if a file (not a directory) was sent
if (FileExist(A_Args[1]) ~= "^$|D") {
MsgBox 0x40010, Error, No file was selected.
ExitApp 1
}
/* To avoid issues with spaces the shell works with files in the old 8.3 format,
this expands it to a full long path.
*/
loop Files, % A_Args[1]
A_Args[1] := A_LoopFileLongPath
; Super-globals to avoid declaring global usage
global RadioGroup := "", RefValue, CurrentHash := 0, AlgData := [], AlgDataCount
/* The map of the algorithms it's created in the index zero given that AHK uses
1-based arrays. It has the pertinent data for the GUI to display and stored.
*/
AlgData[0] := []
AlgData[0].Push({ name: "md2", active: false, len: -1, hash: ""})
AlgData[0].Push({ name: "md4", active: false, len: -1, hash: ""})
AlgData[0].Push({ name: "md5", active: false, len: 32, hash: ""})
AlgData[0].Push({ name: "sha1", active: false, len: 40, hash: ""})
AlgData[0].Push({ name: "sha256", active: false, len: 64, hash: ""})
AlgData[0].Push({ name: "sha384", active: false, len: 96, hash: ""})
AlgData[0].Push({ name: "sha512", active: false, len: 128, hash: ""})
/* Loop through the selected algorithms, copying the objects inside the 0-index
to a consecutive index, is always a good measure to delete what's not needed.
*/
for _, active in use {
if (active)
AlgData.Push(AlgData[0, A_Index]) ; Copying an object from [0]
}
AlgData.Delete(0) ; Delete the map to recover memory.
AlgDataCount := AlgData.Count()
init := false
clipHash := ParseClipboard() ; Checking the clipboard for a hash-like string.
; New GUI, fixed-width font.
Gui New, AlwaysOnTop ToolWindow
Gui Font, q5 s10, Consolas
/* In order to work with Radios as a group, only the first one created needs to
have the a variable assigned, all subsequent radios created belong to the group.
This is the variable for the radios, it will be used on the first iteration of -
the `for` loop and it will be cleared (at the end of the first iteration).
*/
v := " vRadioGroup"
for i, alg in AlgData {
/* It uses the properties in the map. `alg.active` is used to check the
radio and provide visual feedback of which hash is in display.
*/
isChecked := (alg.active ? " Checked" : "")
Gui Add, Radio, % "gRadioChanged h20" isChecked v, % alg.name ":"
v := ""
}
Gui Add, Text, h20, Reference:
/* The `newSection` option allows the Gui stacking elements automatically within
another column, same as the variable in the Radio Group it will be used once and
it will be cleared on the first iteration.
*/
newSection := " ys"
for i, alg in AlgData {
/* The variable names of the Edits matches their algorithm counterpart,
which is consecutive in order to be able to identify the algorithm when
updating the hash / showing the tooltip.
For them to have a consistent width are filled with spaces, to later get
the last Edit (will be the longest) and update the width of all others.
*/
val := Format("{: " (wide ? alg.len : 21) "}", "")
Gui Add, Edit, % "ReadOnly -Tabstop h20 vEdit" A_Index newSection, % val
newSection := ""
}
Gui Add, Edit, Uppercase h20 vRefValue gValidate
/* In wide mode, use the next-to-last size as it will be the biggest and update
all of the Edit boxes with the same size.
*/
if (wide) {
GuiControlGet largest, Pos, % "Edit" AlgDataCount
loop % AlgDataCount + 1
GuiControl Move, % "Edit" A_Index, % "w" largestW + 2
}
/* We don't put the reference value when creating the Edit even if something was
found on the Clipboard because it will alter the sizes, we do now:
*/
GuiControl Text, % "Edit" AlgDataCount + 1, % clipHash
; Display the GUI
Gui Show,, % A_Args[1]
if (init) ; Init might hold an array if a hash is found in the Clipboard.
GetHash(init*)
if (!wide) ; When clipping the hashes enable a tooltip with the full hash.
OnMessage(0x200, "MouseMove")
Process Priority,, Normal
return ; End of auto-execute
; Grab the hash from the specified algorithm
GetHash(Alg, Field) {
global wide, useLower
/* To put the gui in "stand by": clear the text in the fields, place a -
"Working" label where appropriate, disable the radio buttons to avoid --
weird behaviors and disable the reference field.
*/
loop % AlgDataCount {
txt := A_Index = Field ? "Working..." : ""
GuiControl Text, % "Edit" A_Index, % txt
GuiControl Disable, % "Button" A_Index
}
ControlFocus Static1, A
GuiControl +ReadOnly, % "Edit" AlgDataCount + 1
if (Alg["hash"] = "") { ; Only hash if we haven't
hash := Hash_File(A_Args[1], Alg["name"])
Alg["hash"] := Format("{:" (useLower ? "L" : "U") "}", hash)
}
; Set the hash value
ControlGet CurrentHash, Hwnd,, % "Edit" Field, % A_Args[1]
hash := wide ? Alg["hash"] : SubStr(Alg["hash"], 1, 8) " ... "
. SubStr(Alg["hash"], -7)
GuiControl Text, % "Edit" Field, % hash
Validate() ; Validate the hash / update the color of the reference Edit
loop % AlgDataCount ; Enable the radios
GuiControl Enable, % "Button" A_Index
; Enable the reference field.
GuiControl -ReadOnly, % "Edit" AlgDataCount + 1
ControlFocus % "Button" Field, A
}
; Display a ToolTip when hover the current hash.
MouseMove(_wParam, _lParam, _Message, hWnd) {
if (hWnd != CurrentHash) {
ToolTip
} else {
GuiControlGet varName, Name, % hWnd
ToolTip % AlgData[SubStr(varName, 5), "hash"]
}
}
/* Check the clipboard contents for hash-like data, if found, trigger at init a
hashing based on the characteristics of the data found.
*/
ParseClipboard() {
global init
txt := Trim(Clipboard, "`t`n`r ")
if (txt = "")
return
if txt is not xdigit
return
lengths := ""
for _, alg in AlgData
lengths .= alg.len ","
lengths := RTrim(lengths, ",")
len := StrLen(txt)
if len not in % lengths
return
for i, alg in AlgData {
if (alg.len = len) {
init := [alg, A_Index]
AlgData[i, "active"] := true
return Format("{:U}", txt)
}
}
}
RadioChanged() { ; Every time a radio changes trigger the proper hashing.
Gui Submit, NoHide
for _, alg in AlgData {
if (RadioGroup = A_Index)
GetHash(alg, RadioGroup)
}
}
Validate() { ; Compare the reference value and change the color of the edit.
Gui Submit, NoHide
for _, alg in AlgData {
if (RadioGroup = A_Index) {
Gui Font, % "c" (alg.hash = RefValue ? "Green" : "Red")
GuiControl Font, % "Edit" AlgDataCount + 1
return
}
}
}
; Gui Labels
GuiClose:
GuiEscape:
ExitApp
return
; Standard directives
#NoEnv
#NoTrayIcon
#SingleInstance Ignore
/*
`Hash_File()` is compatible with `LC_CalcFileHash()` from libcrypt.ahk.
If you are already using libcrypt.ahk, you can remove the code below and use the
`LC_CalcFileHash()` instead.
*/
Hash_File(Path, AlgId) {
out := "ERROR"
, PROV_RSA_AES := 24
, CRYPT_VERIFYCONTEXT := 0xF0000000
;@ahk-neko-ignore-fn 7 line
, MD2 := 0x00008001 ; CALG_MD2
, MD4 := 0x00008002 ; CALG_MD4
, MD5 := 0x00008003 ; CALG_MD5
, SHA1 := 0x00008004 ; CALG_SHA1
, SHA256 := 0x0000800C ; CALG_SHA_256
, SHA384 := 0x0000800D ; CALG_SHA_384
, SHA512 := 0x0000800E ; CALG_SHA_512
, map := StrSplit("0123456789abcdef")
try {
if !(oFile := FileOpen(Path, 0x700))
throw
oFile.Seek(0)
if (!DllCall("advapi32\CryptAcquireContext", "Ptr*", hProv := 0, "Ptr", 0, "Ptr", 0, "UInt", PROV_RSA_AES, "UInt", CRYPT_VERIFYCONTEXT))
throw
if (!DllCall("advapi32\CryptCreateHash", "Ptr", hProv, "UInt", %AlgId%, "UInt", 0, "UInt", 0, "Ptr*", hHash := 0))
throw
VarSetCapacity(pbData, 0x4000000, 0) ; 64 Mb
while (!oFile.AtEOF) {
cbData := oFile.RawRead(&pbData, 0x4000000)
if (!DllCall("advapi32\CryptHashData", "Ptr", hHash, "Ptr", &pbData, "UInt", cbData, "UInt", 0))
throw
}
if (!DllCall("advapi32\CryptGetHashParam", "Ptr", hHash, "UInt", 2, "Ptr", 0, "UInt*", HashLen := 0, "UInt", 0))
throw
VarSetCapacity(Hash, HashLen)
if (!DllCall("advapi32\CryptGetHashParam", "Ptr", hHash, "UInt", 2, "Ptr", &Hash, "UInt*", HashLen, "UInt", 0))
throw
out := ""
loop % HashLen {
val := NumGet(Hash, A_Index - 1, "UChar")
out .= map[(val >> 0x4) + 1] map[(val & 0xF) + 1]
}
}
if (hHash)
DllCall("advapi32\CryptDestroyHash", "Ptr", hHash)
if (hProv)
DllCall("advapi32\CryPtReleaseContext", "Ptr", hProv, "UInt", 0)
return out
}
MIT License
Copyright (c) 2020 anonymous1184
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@Gewerd-Strauss
Copy link

Hya my friend,

if possible, can this get a license, if you don't mind? Same reason as for Ini.ahk (which, please update).

Thank you,
~Gw

@anonymous1184
Copy link
Author

Done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment