module Main where
import MD5
import Digest
import Config
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
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 * 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 (:"