-
-
Save 534n0/41e6267f97b68f30e4a080f5bd04fd15 to your computer and use it in GitHub Desktop.
Quick File Hashing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ; 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 | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ; 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