Skip to content

Instantly share code, notes, and snippets.

@534n0
Forked from anonymous1184/HashFile.ahk
Created March 16, 2021 15:59
Show Gist options
  • Select an option

  • Save 534n0/41e6267f97b68f30e4a080f5bd04fd15 to your computer and use it in GitHub Desktop.

Select an option

Save 534n0/41e6267f97b68f30e4a080f5bd04fd15 to your computer and use it in GitHub Desktop.
Quick File Hashing
; Description: https://redd.it/m0kzdy
; Switch to display the full hashes
global wide := 0
; Enables/disabled algorithms
use := { MD2: 0
, MD4: 0
, MD5: 1
, SHA1: 1
, SHA256: 1
, SHA384: 0
, SHA512: 0 }
; Don't edit beyond this point
; Some speed-related settings
ListLines Off
SetBatchLines -1
Process Priority,, H
; Verify if a file (not a directory) was sent
attrib := FileExist(A_Args[1])
if (!attrib || InStr(attrib, "D"))
{
MsgBox % 0x10|0x40000, Error, No file was selected.
If ErrorLevel
ExitApp 1
}
/* To avoid issues with spaces the shell works wit files
in the old 8.3 format, this expands it to a full path.
*/
loop files, % A_Args[1]
A_Args[1] := A_LoopFileLongPath
; "Super-global" variables
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 object arrays. It has pertinent data for the GUI to display and stored.
*/
algData[0] := [{ name:"md2" , checked:"", len:-1 , alg_id:0x8001, hash:"" }
, { name:"md4" , checked:"", len:-1 , alg_id:0x8002, hash:"" }
, { name:"md5" , checked:"", len:32 , alg_id:0x8003, hash:"" }
, { name:"sha1" , checked:"", len:40 , alg_id:0x8004, hash:"" }
, { name:"sha256", checked:"", len:64 , alg_id:0x800c, hash:"" }
, { name:"sha384", checked:"", len:96 , alg_id:0x800d, hash:"" }
, { name:"sha512", checked:"", len:128, alg_id:0x800e, 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 i,active in use
if active
algData.Push(algData[0][A_Index]) ; Copying an object from inside [0]
algData.Delete(0) ; Deleting the map
algDataCount := algData.Count()
init := false
clipHash := parseClipboard() ; Checking the clipboard for a hash
; New GUI, small and fixed font
Gui New, +AlwaysOnTop +ToolWindow
Gui Font, s10, Consolas
/* In order to work with Radios as a group, only the first one created needs to
have the a variable designated, 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 line).
*/
v := " vRadioGroup"
for i,alg in algData
/* It uses the properties in the map, `alg.checked` is populated in only one of
objects in the array if when scanning the clipboard a hash matching the length
defined in `alg.len`.
*/
Gui Add, Radio, % "gRadioChanged h20" v alg.checked, % alg.name ":" (v:="")
Gui Add, Text, h20, Reference:
/* The `ys` options let's the automatic GUI placement to start stacking elements in
another column, same as the variable in the Radio Group it will be used once and it
will be cleared on the first iteration.
*/
ys := " ys"
for i,alg in algData
/* The variable names of the Edits matches their class whish is a consecutive -
number, that in order to be able to identify each Edit corresponding it algorithm
when updating the hash and showing the tooltip.
For them to have a consistent width are filled with spaces for later grab the last
Edit that will be the longest and update the width of all others.
*/
Gui Add, Edit, % "ReadOnly -Tabstop h20 vEdit" A_Index ys
, % Format("{: " (wide ? alg.len : 21) "}", "") (ys:="")
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 find something in the
CLipboard because it will alter the sizes.
*/
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.
hashFile(init*)
if !wide ; When clipping the hashes enable a mouse over with a tooltip of the full hash.
OnMessage(0x200, "mouseMove")
return
hashFile(alg, field)
{
/* Clean the GUI elements, place a "Working" label where appropriate, disable the radio
buttons to avoid weird behaviors and disable the reference field.
*/
loop % algDataCount
{
GuiControl Text, % "Edit" A_Index
, % A_Index = field ? "Working..." : ""
GuiControl Disable, % "Button" A_Index
}
ControlFocus Static1, A
GuiControl +ReadOnly, % "Edit" algDataCount + 1
;~ Sleep 500
if !alg["hash"] ; Only hash if we haven't
alg["hash"] := hash_file(A_Args[1], alg["alg_id"], 0x10000)
; 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 and 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, msg, hWnd)
{
if (hWnd = currentHash)
{
GuiControlGet varName, Name, % hWnd
ToolTip % algData[SubStr(varName, 5)]["hash"]
}
else ToolTip
}
/* Check the clipboard contents for hash-like data, if found trigger at init
a hashing based on the characteristics of the data found.
*/
parseClipboard()
{
if !txt := Trim(Clipboard, " `n`r`t")
return
if txt is xdigit
{
lengths := ""
for i,alg in algData
lengths .= alg.len ","
lengths := RTrim(lengths, ",")
len := StrLen(txt)
if len in % lengths
for i,alg in algData
if (alg.len = len)
{
global init
init := [alg, A_Index]
algData[i]["checked"] := " Checked"
return Format("{:U}", txt)
}
}
}
radioChanged() ; Every time a radio changes trigger the proper hashing.
{
Gui Submit, NoHide
for i,alg in algData
if (radioGroup = A_Index)
hashFile(alg, radioGroup)
}
validate() ; Compare the reference value and change the color of the edit.
{
Gui Submit, NoHide
for i,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
; Standard directives
#NoEnv
#NoTrayIcon
#KeyHistory 0
#SingleInstance ignore
/**
* hash_file() is compatible with LC_CalcFileHash() from libcrypt.ahk
* If already using libcrypt, you can replace it and dump code below.
*
* or...
*
* hash_file(p*)
* {
* return LC_CalcFileHash(p*)
* }
*/
hash_file(fileName, algoId, continuos = 0, ByRef hash = 0, ByRef hashLength = 0)
{
file := FileOpen(fileName, 0x0)
if (!file || file.length > 0X7FFFFFFF)
return
if !continuos
{
VarSetCapacity(data, file.length, 0)
file.rawRead(&data, file.length)
return hash_addr(&data, file.length, algoId, hash, hashLength)
}
hashLength := 0
while (file.pos < file.length)
{
readLength := file.length > continuos ? continuos : file.length - file.pos
VarSetCapacity(data, hashLength + readLength, 0)
DllCall("Ntdll\RtlMoveMemory", "Ptr", &data, "Ptr", &hash, "Ptr", hashLength)
file.rawRead(&data + hashLength, readLength)
h := hash_addr(&data, hashLength + readLength, algoId, hash, hashLength)
}
return h
}
hash_addr(addr, length, algoId, ByRef hash := 0, ByRef hashLength := 0)
{
map := StrSplit("0123456789ABCDEF"), out := hProv := hHash := hKey := ""
if DllCall("Advapi32\CryptAcquireContext", "Ptr*",hProv, "Ptr",0, "Ptr",0, "UInt",0x18, "UInt",0xF0000000)
{
if DllCall("Advapi32\CryptCreateHash", "Ptr",hProv, "UInt",algoId, "UInt",0, "UInt",0, "Ptr*",hHash)
if DllCall("Advapi32\CryptHashData", "Ptr",hHash, "Ptr",addr, "UInt",length, "UInt",0)
if DllCall("Advapi32\CryptGetHashParam", "Ptr",hHash, "UInt",2, "Ptr",0, "UInt*",hashLength, "UInt",0)
{
VarSetCapacity(hash, hashLength, 0)
if DllCall("Advapi32\CryptGetHashParam", "Ptr",hHash, "UInt",2, "Ptr",&hash, "UInt*",hashLength, "UInt",0)
loop, % hashLength
v := NumGet(hash, A_Index - 1, "UChar")
, out .= map[(v >> 0x4) + 1] map[(v & 0xF) + 1]
DllCall("Advapi32\CryptDestroyHash", "Ptr",hHash)
}
DllCall("Advapi32\CryptReleaseContext", "Ptr",hProv, "UInt",0)
}
return out
}
; Description: https://redd.it/m0kzdy
; Switch to display the full hashes
global wide := 0
; Enables/disabled algorithms
use := { MD2: 0
, MD4: 0
, MD5: 1
, SHA1: 1
, SHA256: 1
, SHA384: 0
, SHA512: 0 }
; Don't edit beyond this point
; Some speed-related settings
ListLines Off
SetBatchLines -1
Process Priority,, H
; Verify if a file (not a directory) was sent
attrib := FileExist(A_Args[1])
if (!attrib || InStr(attrib, "D"))
{
MsgBox % 0x10|0x40000, Error, No file was selected.
If ErrorLevel
ExitApp 1
}
/* To avoid issues with spaces the shell works wit files
in the old 8.3 format, this expands it to a full path.
*/
loop files, % A_Args[1]
A_Args[1] := A_LoopFileLongPath
; "Super-global" variables
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 object arrays. It has pertinent data for the GUI to display and stored.
*/
algData[0] := [{ name:"md2" , checked:"", len:-1 , alg_id:0x8001, hash:"" }
, { name:"md4" , checked:"", len:-1 , alg_id:0x8002, hash:"" }
, { name:"md5" , checked:"", len:32 , alg_id:0x8003, hash:"" }
, { name:"sha1" , checked:"", len:40 , alg_id:0x8004, hash:"" }
, { name:"sha256", checked:"", len:64 , alg_id:0x800c, hash:"" }
, { name:"sha384", checked:"", len:96 , alg_id:0x800d, hash:"" }
, { name:"sha512", checked:"", len:128, alg_id:0x800e, 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 i,active in use
if active
algData.Push(algData[0][A_Index]) ; Copying an object from inside [0]
algData.Delete(0) ; Deleting the map
algDataCount := algData.Count()
init := false
clipHash := parseClipboard() ; Checking the clipboard for a hash
; New GUI, small and fixed font
Gui New, +AlwaysOnTop +ToolWindow
Gui Font, s10, Consolas
/* In order to work with Radios as a group, only the first one created needs to
have the a variable designated, 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 line).
*/
v := " vRadioGroup"
for i,alg in algData
/* It uses the properties in the map, `alg.checked` is populated in only one of
objects in the array if when scanning the clipboard a hash matching the length
defined in `alg.len`.
*/
Gui Add, Radio, % "gRadioChanged h20" v alg.checked, % alg.name ":" (v:="")
Gui Add, Text, h20, Reference:
/* The `ys` options let's the automatic GUI placement to start stacking elements in
another column, same as the variable in the Radio Group it will be used once and it
will be cleared on the first iteration.
*/
ys := " ys"
for i,alg in algData
/* The variable names of the Edits matches their class whish is a consecutive -
number, that in order to be able to identify each Edit corresponding it algorithm
when updating the hash and showing the tooltip.
For them to have a consistent width are filled with spaces for later grab the last
Edit that will be the longest and update the width of all others.
*/
Gui Add, Edit, % "ReadOnly -Tabstop h20 vEdit" A_Index ys
, % Format("{: " (wide ? alg.len : 21) "}", "") (ys:="")
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 find something in the
CLipboard because it will alter the sizes.
*/
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.
hashFile(init*)
if !wide ; When clipping the hashes enable a mouse over with a tooltip of the full hash.
OnMessage(0x200, "mouseMove")
return
hashFile(alg, field)
{
/* Clean the GUI elements, place a "Working" label where appropriate, disable the radio
buttons to avoid weird behaviors and disable the reference field.
*/
loop % algDataCount
{
GuiControl Text, % "Edit" A_Index
, % A_Index = field ? "Working..." : ""
GuiControl Disable, % "Button" A_Index
}
ControlFocus Static1, A
GuiControl +ReadOnly, % "Edit" algDataCount + 1
;~ Sleep 500
if !alg["hash"] ; Only hash if we haven't
alg["hash"] := hash_file(A_Args[1], alg["alg_id"], 0x10000)
; 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 and 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, msg, hWnd)
{
if (hWnd = currentHash)
{
GuiControlGet varName, Name, % hWnd
ToolTip % algData[SubStr(varName, 5)]["hash"]
}
else ToolTip
}
/* Check the clipboard contents for hash-like data, if found trigger at init
a hashing based on the characteristics of the data found.
*/
parseClipboard()
{
if !txt := Trim(Clipboard, " `n`r`t")
return
if txt is xdigit
{
lengths := ""
for i,alg in algData
lengths .= alg.len ","
lengths := RTrim(lengths, ",")
len := StrLen(txt)
if len in % lengths
for i,alg in algData
if (alg.len = len)
{
global init
init := [alg, A_Index]
algData[i]["checked"] := " Checked"
return Format("{:U}", txt)
}
}
}
radioChanged() ; Every time a radio changes trigger the proper hashing.
{
Gui Submit, NoHide
for i,alg in algData
if (radioGroup = A_Index)
hashFile(alg, radioGroup)
}
validate() ; Compare the reference value and change the color of the edit.
{
Gui Submit, NoHide
for i,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
; Standard directives
#NoEnv
#NoTrayIcon
#KeyHistory 0
#SingleInstance ignore
/**
* hash_file() is compatible with LC_CalcFileHash() from libcrypt.ahk
* If already using libcrypt, you can replace it and dump code below.
*
* or...
*
* hash_file(p*)
* {
* return LC_CalcFileHash(p*)
* }
*/
hash_file(fileName, algoId, continuos = 0, ByRef hash = 0, ByRef hashLength = 0)
{
file := FileOpen(fileName, 0x0)
if (!file || file.length > 0X7FFFFFFF)
return
if !continuos
{
VarSetCapacity(data, file.length, 0)
file.rawRead(&data, file.length)
return hash_addr(&data, file.length, algoId, hash, hashLength)
}
hashLength := 0
while (file.pos < file.length)
{
readLength := file.length > continuos ? continuos : file.length - file.pos
VarSetCapacity(data, hashLength + readLength, 0)
DllCall("Ntdll\RtlMoveMemory", "Ptr", &data, "Ptr", &hash, "Ptr", hashLength)
file.rawRead(&data + hashLength, readLength)
h := hash_addr(&data, hashLength + readLength, algoId, hash, hashLength)
}
return h
}
hash_addr(addr, length, algoId, ByRef hash := 0, ByRef hashLength := 0)
{
map := StrSplit("0123456789ABCDEF"), out := hProv := hHash := hKey := ""
if DllCall("Advapi32\CryptAcquireContext", "Ptr*",hProv, "Ptr",0, "Ptr",0, "UInt",0x18, "UInt",0xF0000000)
{
if DllCall("Advapi32\CryptCreateHash", "Ptr",hProv, "UInt",algoId, "UInt",0, "UInt",0, "Ptr*",hHash)
if DllCall("Advapi32\CryptHashData", "Ptr",hHash, "Ptr",addr, "UInt",length, "UInt",0)
if DllCall("Advapi32\CryptGetHashParam", "Ptr",hHash, "UInt",2, "Ptr",0, "UInt*",hashLength, "UInt",0)
{
VarSetCapacity(hash, hashLength, 0)
if DllCall("Advapi32\CryptGetHashParam", "Ptr",hHash, "UInt",2, "Ptr",&hash, "UInt*",hashLength, "UInt",0)
loop, % hashLength
v := NumGet(hash, A_Index - 1, "UChar")
, out .= map[(v >> 0x4) + 1] map[(v & 0xF) + 1]
DllCall("Advapi32\CryptDestroyHash", "Ptr",hHash)
}
DllCall("Advapi32\CryptReleaseContext", "Ptr",hProv, "UInt",0)
}
return out
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment