module Main where
import Config
import Digest
import MD5
import Monitoring
import ParseArgs
import Util
import Data.Label
import Text.Printf
import Control.Monad
import Control.Applicative
import Criterion.Measurement
import System.IO
import System.Environment
import Data.Array.Accelerate ( Z(..), (:.)(..) )
import qualified Data.Array.Accelerate as A
import qualified Data.ByteString.Lazy.Char8 as L
main :: IO ()
main = do
beginMonitoring
argv <- getArgs
(conf, _cconf, files) <- parseArgs configHelp configBackend options defaults header footer argv
-- Read the plain text word lists. This creates a vector of MD5 chunks ready
-- for hashing.
--
putStr "Loading dictionary... " >> hFlush stdout
(tdict, dict) <- time $ readDict conf (get configDict conf)
let (Z :. _ :. entries) = A.arrayShape dict
putStrLn $ printf "%d words in %s" entries (secs tdict)
-- Attempt to recover one hash at a time by comparing it to entries in the
-- database. This rehashes the entire word list every time, rather than
-- pre-computing the hashes and performing a lookup. That approach, known as a
-- rainbow table, while much faster when multiple iterations of the hashing
-- function are applied, but is defeated by salting passwords. This is true
-- even if the salt is known, so long as it is unique for each password.
--
let backend = get configBackend conf
recover hash =
let abcd = readMD5 hash
idx = run1 backend (hashcat (A.use dict)) (A.fromList Z [abcd])
--
in case idx `A.indexArray` Z of
-1 -> Nothing
n -> Just (extract dict n)
recoverAll :: [L.ByteString] -> IO (Int,Int)
recoverAll =
foldM (\(i,n) h -> maybe (return (i,n+1)) (\t -> showText h t >> return (i+1,n+1)) (recover h)) (0,0)
showText hash text = do
L.putStr hash >> putStr ": " >> L.putStrLn text
-- Queue up all the message digests to process
--
digests <- concat . (map L.pack (get configStrings conf) :)
<$> mapM (\f -> L.lines `fmap` L.readFile f) files
-- Run the lookup for each unknown hash against the given wordlists.
--
putStrLn "Beginning recovery..."
(trec, (r, t)) <- time (recoverAll digests)
-- And print a summary of results
--
let percent = fromIntegral r / fromIntegral t * 100.0 :: Double
persec = (fromIntegral t * fromIntegral entries) / trec
putStrLn $ printf "\nRecovered %d/%d (%.2f %%) digests in %s, %s"
r t percent
(showFFloatSIBase (Just 2) 1000 trec "s")
(showFFloatSIBase (Just 2) 1000 persec "Hash/sec")
when (r == t) $ putStrLn "All hashes recovered (:"