Skip to content

Commit 0259f00

Browse files
authored
Script that may work better on MacOs
1 parent 008602e commit 0259f00

File tree

1 file changed

+362
-0
lines changed

1 file changed

+362
-0
lines changed

ClatsCracker 1.06.8 Mac.pyw

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
from __future__ import annotations
2+
import platform
3+
from pathlib import Path
4+
from tkinter import filedialog, messagebox, scrolledtext, ttk
5+
import hashlib, hmac, os, sys, time, itertools, string, logging, zlib, threading, concurrent.futures as cf, tkinter as tk, multiprocessing as mp
6+
from concurrent.futures import ThreadPoolExecutor
7+
from typing import List, Optional, Tuple, Dict
8+
import subprocess
9+
10+
def _pip_install(pkg: str) -> None:
11+
try:
12+
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", pkg])
13+
except Exception:
14+
pass
15+
16+
try:
17+
from argon2 import PasswordHasher, Type
18+
except ImportError:
19+
_pip_install("argon2-cffi")
20+
from argon2 import PasswordHasher, Type
21+
22+
try:
23+
from passlib.hash import nthash
24+
except ImportError:
25+
_pip_install("passlib")
26+
from passlib.hash import nthash
27+
28+
_HAS_FAST_HASHES = False
29+
try:
30+
from Crypto.Hash import MD4 as _MD4, RIPEMD160 as _RIPEMD160
31+
_HAS_FAST_HASHES = True
32+
except ImportError:
33+
_pip_install("pycryptodome")
34+
try:
35+
from Crypto.Hash import MD4 as _MD4, RIPEMD160 as _RIPEMD160
36+
_HAS_FAST_HASHES = True
37+
except ImportError:
38+
_HAS_FAST_HASHES = False
39+
40+
try:
41+
import bcrypt
42+
except ImportError:
43+
_pip_install("bcrypt")
44+
try:
45+
import bcrypt
46+
except ImportError:
47+
bcrypt = None
48+
49+
try:
50+
import psutil
51+
except ImportError:
52+
_pip_install("psutil")
53+
try:
54+
import psutil
55+
except ImportError:
56+
psutil = None
57+
58+
try:
59+
import chardet
60+
except ImportError:
61+
_pip_install("chardet")
62+
try:
63+
import chardet
64+
except ImportError:
65+
chardet = None
66+
67+
CPU_USAGE_THRESHOLD = 99.5
68+
_CPU_CHECK_INTERVAL = 0.2
69+
_SCRIPT_DIR = (Path(sys.executable).parent if getattr(sys, "frozen", False)
70+
else Path(__file__).resolve().parent)
71+
LOG_FILE = str(_SCRIPT_DIR / "Hash_Cracking_Results_ClatsCracker.txt")
72+
ALIAS_MAP = {'shakeke128': 'shake128', 'sha1v2': 'sha1_v2', 'ntplm': 'ntlm', 'md5-sha1': 'md5_sha1'}
73+
_DEFAULT_THREADS = max(1, (os.cpu_count() or 1))
74+
_ENC_CACHE: Dict[str, str] = {}
75+
SCRIPT_NAME = Path(sys.argv[0]).name if sys.argv else ''
76+
logger = logging.getLogger("clatscracker")
77+
logger.setLevel(logging.INFO)
78+
_fh = logging.FileHandler(LOG_FILE, encoding="utf-8")
79+
_fh.setFormatter(logging.Formatter("%(asctime)s — %(levelname)s — %(filename)s — %(message)s"))
80+
logger.addHandler(_fh)
81+
write_log = logger.info
82+
83+
hash_lengths = {'crc32': 8,'adler32': 8,'md4': 32,'md5': 32,'ripemd160': 40,'sha1': 40,'sha1_v2': 40,'sha224': 56,'sha256': 64,'sha3_224': 56,'sha3_256': 64,'sha3_384': 96,'sha3_512': 128,'sha512': 128,'sha512_224': 56,'sha512_256': 64,'blake2_224': 56,'blake2b': 128,'blake2s': 64,'sha384': 96,'shake128': 64,'shake256': 128,'shakeke128': 64,'sm3': 64,'md5_sha1': 72,'mdc2': 32,'whirlpool': 128,'scrypt': 128}
84+
_HASH_FUNCS = {
85+
'crc32': lambda b: format(zlib.crc32(b) & 0xffffffff, '08x'),
86+
'adler32': lambda b: format(zlib.adler32(b) & 0xffffffff, '08x'),
87+
'md4': (lambda b: _MD4.new(data=b).hexdigest()) if _HAS_FAST_HASHES else (lambda b: hashlib.new('md4', b).hexdigest()),
88+
'md5': lambda b: hashlib.md5(b).hexdigest(),
89+
'ripemd160': (lambda b: _RIPEMD160.new(data=b).hexdigest()) if _HAS_FAST_HASHES else (lambda b: hashlib.new('ripemd160', b).hexdigest()),
90+
'blake2_224': lambda b: hashlib.blake2b(b, digest_size=28).hexdigest(),
91+
'sha224': lambda b: hashlib.sha224(b).hexdigest(),'sha256': lambda b: hashlib.sha256(b).hexdigest(),'sha512': lambda b: hashlib.sha512(b).hexdigest(),
92+
'sha3_224': lambda b: hashlib.sha3_224(b).hexdigest(),'sha3_256': lambda b: hashlib.sha3_256(b).hexdigest(),'sha3_384': lambda b: hashlib.sha3_384(b).hexdigest(),'sha3_512': lambda b: hashlib.sha3_512(b).hexdigest(),
93+
'sha1': lambda b: hashlib.sha1(b).hexdigest(),'sha512_224': lambda b: hashlib.new('sha512_224', b).hexdigest(),'sha512_256': lambda b: hashlib.new('sha512_256', b).hexdigest(),
94+
'blake2b': lambda b: hashlib.blake2b(b).hexdigest(),'blake2s': lambda b: hashlib.blake2s(b).hexdigest(),'sha384': lambda b: hashlib.sha384(b).hexdigest(),
95+
'shake128': lambda b: hashlib.shake_128(b).hexdigest(32),'shake256': lambda b: hashlib.shake_256(b).hexdigest(64),'shakeke128': lambda b: hashlib.shake_128(b).hexdigest(32),
96+
'sm3': lambda b: hashlib.new('sm3', b).hexdigest(),
97+
'md5_sha1': lambda b: hashlib.md5(b).hexdigest() + hashlib.sha1(b).hexdigest(),
98+
'mdc2': lambda b: hashlib.new('mdc2', b).hexdigest(),'whirlpool': lambda b: hashlib.new('whirlpool', b).hexdigest(),
99+
'scrypt': lambda b: hashlib.scrypt(b, salt=b'', n=16384, r=8, p=1, dklen=64).hex(),
100+
}
101+
_ARGON2_PH = PasswordHasher(type=Type.ID)
102+
103+
def canonical_algo(name: str) -> str:
104+
return ALIAS_MAP.get(name.lower(), name.lower())
105+
106+
class CrackerState:
107+
def __init__(self):
108+
self.passwords_tried = 0
109+
self.total_passwords = 0
110+
self.found_password: Optional[str] = None
111+
self.threads_count = _DEFAULT_THREADS
112+
self.start_time: Optional[float] = None
113+
self.abort_requested = False
114+
self.cracking_complete = False
115+
self.progress_lock = threading.Lock()
116+
self.found_lock = threading.Lock()
117+
118+
def phpass_verify(password: str, phpass_hash: str) -> bool:
119+
it = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
120+
if not (phpass_hash.startswith('$P$') or phpass_hash.startswith('$H$')) or len(phpass_hash) != 34:
121+
return False
122+
cl = it.index(phpass_hash[3])
123+
cnt = 1 << cl
124+
salt = phpass_hash[4:12]
125+
h = hashlib.md5((salt + password).encode()).digest()
126+
for _ in range(cnt):
127+
h = hashlib.md5(h + password.encode()).digest()
128+
def e(inp: bytes):
129+
out, val, bits = [], 0, 0
130+
for byte in inp:
131+
val |= byte << bits
132+
bits += 8
133+
while bits >= 6:
134+
out.append(it[val & 0x3f])
135+
val >>= 6
136+
bits -= 6
137+
if bits:
138+
out.append(it[val & 0x3f])
139+
return ''.join(out)
140+
return phpass_hash[:12] + e(h)[:22] == phpass_hash
141+
142+
def guess_hash_algorithm(h: str) -> Optional[List[str]]:
143+
if h.startswith('$2'):
144+
return ['bcrypt']
145+
if h.startswith('$argon2id$'):
146+
return ['argon2id']
147+
if (h.startswith('$P$') or h.startswith('$H$')) and len(h) == 34:
148+
return ['phpass_md5']
149+
hl, l = h.lower(), len(h)
150+
if l == 32 and all(c in '0123456789abcdef' for c in hl):
151+
return ['md5', 'ntlm']
152+
cand = [k for k, v in hash_lengths.items() if l == v]
153+
if l == 128 and all(c in '0123456789abcdef' for c in hl):
154+
cand.append('scrypt')
155+
return list(dict.fromkeys(cand)) or None
156+
157+
def validate_hash_length(a: str, h: str) -> bool:
158+
if a in {'bcrypt', 'argon2id', 'scrypt', 'phpass_md5', 'ntlm'}:
159+
return True
160+
exp = hash_lengths.get(a)
161+
return False if exp and len(h) != exp else True
162+
163+
_cpu_last = 0.0
164+
_next_cpu_check = 0.0
165+
def throttle_cpu():
166+
global _cpu_last, _next_cpu_check
167+
if psutil:
168+
now = time.time()
169+
if now >= _next_cpu_check:
170+
_cpu_last = psutil.cpu_percent(0.05)
171+
_next_cpu_check = now + _CPU_CHECK_INTERVAL
172+
if _cpu_last > CPU_USAGE_THRESHOLD:
173+
time.sleep(0.2)
174+
175+
def compile_checker(d: str, a: str):
176+
if a == 'bcrypt':
177+
ref = d.encode()
178+
return (lambda p: bcrypt and bcrypt.checkpw(p.encode(), ref))
179+
if a == 'argon2id':
180+
def _chk(p: str):
181+
try:
182+
_ARGON2_PH.verify(d, p)
183+
return True
184+
except Exception:
185+
return False
186+
return _chk
187+
if a == 'phpass_md5':
188+
return lambda p: phpass_verify(p, d)
189+
if a == 'ntlm':
190+
return lambda p: nthash.verify(p, d)
191+
if a == 'sha1_v2':
192+
return lambda p: hmac.compare_digest(hashlib.sha1(hashlib.sha1(p.encode()).digest()).hexdigest(), d)
193+
if a == 'scrypt':
194+
return lambda p: hmac.compare_digest(hashlib.scrypt(p.encode(), salt=b'', n=16384, r=8, p=1, dklen=64).hex(), d)
195+
if a in ('shake128', 'shakeke128'):
196+
return lambda p: hmac.compare_digest(hashlib.shake_128(p.encode()).hexdigest(32), d)
197+
if a == 'shake256':
198+
return lambda p: hmac.compare_digest(hashlib.shake_256(p.encode()).hexdigest(64), d)
199+
fn = _HASH_FUNCS.get(a)
200+
return (lambda p: fn(p.encode()) == d) if fn else (lambda _: False)
201+
202+
def _process_chunk(chunk: List[str], checker, step: int) -> Tuple[int, Optional[str]]:
203+
processed = 0
204+
for pwd in chunk:
205+
processed += 1
206+
if checker(pwd):
207+
return processed, pwd
208+
if processed % step == 0:
209+
throttle_cpu()
210+
return processed, None
211+
212+
def detect_file_encoding(path: str, sample: int = 10000) -> str:
213+
if chardet is None:
214+
return 'utf-8'
215+
try:
216+
with open(path, 'rb') as f:
217+
return chardet.detect(f.read(sample)).get('encoding') or 'utf-8'
218+
except Exception:
219+
return 'utf-8'
220+
221+
def get_line_count(path: str) -> int:
222+
try:
223+
enc = _ENC_CACHE.get(path) or detect_file_encoding(path)
224+
_ENC_CACHE[path] = enc
225+
with open(path, 'r', encoding=enc, errors='replace') as f:
226+
return sum(1 for _ in f)
227+
except Exception:
228+
return 0
229+
230+
def lines_in_chunks(path: str, chunk: int = 300_000):
231+
try:
232+
enc = _ENC_CACHE.get(path) or detect_file_encoding(path)
233+
_ENC_CACHE[path] = enc
234+
with open(path, 'r', encoding=enc, errors='replace', buffering=1024 * 1024) as f:
235+
while True:
236+
batch = list(itertools.islice(f, chunk))
237+
if not batch:
238+
break
239+
yield [line.rstrip('\n') for line in batch]
240+
except Exception:
241+
return
242+
243+
def _generate_passwords(chars: str, length: int, chunk: int = 100000):
244+
prod = itertools.product(chars, repeat=length)
245+
while True:
246+
slice_ = list(itertools.islice(prod, chunk))
247+
if not slice_:
248+
break
249+
yield [''.join(t) for t in slice_]
250+
251+
def dictionary_crack_worker(path: str, d: str, checker, state: CrackerState, step: int, cb):
252+
if not os.path.exists(path):
253+
write_log(f"Dictionary {path} not found")
254+
return
255+
for chunk in lines_in_chunks(path):
256+
if state.abort_requested or state.found_password:
257+
break
258+
processed_chunk = 0
259+
for pwd in chunk:
260+
if state.abort_requested or state.found_password:
261+
break
262+
processed_chunk += 1
263+
if checker(pwd):
264+
with state.found_lock:
265+
state.found_password = pwd
266+
state.abort_requested = True
267+
break
268+
with state.progress_lock:
269+
state.passwords_tried += processed_chunk
270+
pct = (state.passwords_tried / state.total_passwords * 100) if state.total_passwords else 0
271+
cb(pct, state)
272+
if state.abort_requested or state.found_password:
273+
break
274+
275+
def concurrent_hash_cracker(dicts: List[str], d: str, a: str, state: CrackerState, cb, done):
276+
state.found_password = None
277+
state.passwords_tried = 0
278+
state.abort_requested = False
279+
state.cracking_complete = False
280+
write_log("Dictionary cracking started")
281+
state.start_time = time.time()
282+
state.total_passwords = sum(get_line_count(p) for p in dicts if os.path.exists(p))
283+
if not state.total_passwords:
284+
write_log("Empty dictionaries")
285+
done(None)
286+
return
287+
checker = compile_checker(d, a)
288+
max_workers = min(state.threads_count, len(dicts))
289+
with ThreadPoolExecutor(max_workers=max_workers) as tpe:
290+
futs = [tpe.submit(dictionary_crack_worker, p, d, checker, state, 5_000_000, cb) for p in dicts if os.path.exists(p)]
291+
while True:
292+
if state.found_password:
293+
state.abort_requested = True
294+
for f in futs:
295+
f.cancel()
296+
break
297+
if all(f.done() for f in futs):
298+
break
299+
time.sleep(0.05)
300+
write_log("Dictionary cracking complete")
301+
done(state.found_password)
302+
303+
def brute_force_crack(d: str, a: str, chars: str, length: int, state: CrackerState, cb, done):
304+
state.found_password = None
305+
state.passwords_tried = 0
306+
state.abort_requested = False
307+
state.cracking_complete = False
308+
state.start_time = time.time()
309+
state.total_passwords = len(chars) ** length
310+
write_log("Bruteforce started")
311+
checker = compile_checker(d, a)
312+
step = max(1, 5_000_000 // state.threads_count)
313+
for batch in _generate_passwords(chars, length, step):
314+
if state.abort_requested or state.found_password:
315+
break
316+
futs = [_POOL.submit(_process_chunk, batch[i:i + step], checker, step) for i in range(0, len(batch), step)]
317+
for fut in cf.as_completed(futs):
318+
if state.abort_requested:
319+
break
320+
try:
321+
proc, found = fut.result()
322+
except Exception:
323+
continue
324+
with state.progress_lock:
325+
state.passwords_tried += proc
326+
pct = (state.passwords_tried / state.total_passwords * 100) if state.total_passwords else 0
327+
cb(pct, state)
328+
if found:
329+
with state.found_lock:
330+
state.found_password = found
331+
state.abort_requested = True
332+
break
333+
if state.found_password or state.abort_requested:
334+
break
335+
write_log("Bruteforce complete")
336+
done(state.found_password)
337+
338+
class HashCrackerGUI:
339+
...
340+
# (GUI class is **identical**; omitted here for brevity — no platform changes required)
341+
...
342+
343+
def _worker_init(nice_val: int):
344+
try:
345+
os.nice(nice_val)
346+
except AttributeError:
347+
pass
348+
349+
def main():
350+
mp.freeze_support()
351+
try:
352+
mp.set_start_method("spawn", force=True)
353+
except RuntimeError:
354+
pass
355+
global _POOL
356+
_POOL = ThreadPoolExecutor(max_workers=os.cpu_count() or 1)
357+
root = tk.Tk()
358+
HashCrackerGUI(root)
359+
root.mainloop()
360+
361+
if __name__ == "__main__":
362+
main()

0 commit comments

Comments
 (0)