Skip to content

Commit f798e7e

Browse files
author
mynex
committed
Add interactive wordlist generator tool
1 parent 711b1a5 commit f798e7e

1 file changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
"""
2+
PyCrunch - Interactive Wordlist Generator
3+
Inspired by Crunch
4+
5+
Author: @atlasilim
6+
Educational use only.
7+
"""
8+
9+
import argparse
10+
import itertools
11+
import sys
12+
import os
13+
import time
14+
import math
15+
import signal
16+
17+
VERSION = "1.0.0"
18+
BUFFER_SIZE = 10 * 1024 * 1024
19+
DEFAULT_CHARSET = "abcdefghijklmnopqrstuvwxyz"
20+
21+
CRUNCH_PATTERNS = {
22+
'@': "abcdefghijklmnopqrstuvwxyz",
23+
',': "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
24+
'%': "0123456789",
25+
'^': "!@#$%^&*()-_+=~`[]{}|\\:;\"'<>,.?/"
26+
}
27+
28+
def signal_handler(sig, frame):
29+
print("\n\n[!] Process cancelled by user.")
30+
sys.exit(0)
31+
32+
def calculate_size(min_len, max_len, charset_len, joiner="", limit=None):
33+
total_bytes = 0
34+
newline_len = len(os.linesep) if not joiner else len(joiner)
35+
words_counted = 0
36+
37+
for length in range(min_len, max_len + 1):
38+
num_combinations = charset_len ** length
39+
40+
if limit:
41+
remaining_limit = limit - words_counted
42+
current_count = min(num_combinations, remaining_limit)
43+
else:
44+
current_count = num_combinations
45+
46+
line_size = length + newline_len
47+
total_bytes += current_count * line_size
48+
words_counted += current_count
49+
50+
if limit and words_counted >= limit:
51+
break
52+
53+
return total_bytes
54+
55+
def format_size(size_bytes):
56+
if size_bytes == 0: return "0 B"
57+
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
58+
i = int(math.floor(math.log(size_bytes, 1024)))
59+
p = math.pow(1024, i)
60+
s = round(size_bytes / p, 2)
61+
return f"{s} {size_name[i]}"
62+
63+
def generate_wordlist(min_len, max_len, charset, output_file, limit=None):
64+
start_time = time.time()
65+
charset_list = list(charset)
66+
charset_len = len(charset_list)
67+
68+
if charset_len == 0:
69+
print("[!] Error: Charset cannot be empty.")
70+
sys.exit(1)
71+
72+
print(f"[*] Analyzing...")
73+
total_size = calculate_size(min_len, max_len, charset_len, limit=limit)
74+
formatted_size = format_size(total_size)
75+
76+
print(f"--- PyCrunch v{VERSION} ---")
77+
print(f"[*] Charset : {charset} (Length: {charset_len})")
78+
print(f"[*] Length : {min_len} - {max_len}")
79+
if limit:
80+
print(f"[*] Word Limit : {limit}")
81+
print(f"[*] Est. Size : {formatted_size}")
82+
if output_file:
83+
print(f"[*] Output File : {output_file}")
84+
else:
85+
print(f"[*] Output : STDOUT")
86+
87+
if output_file:
88+
try:
89+
f = open(output_file, 'w', encoding='utf-8', buffering=BUFFER_SIZE)
90+
except OSError as e:
91+
print(f"[!] File opening error: {e}")
92+
sys.exit(1)
93+
else:
94+
f = sys.stdout
95+
96+
total_combinations = sum(charset_len ** l for l in range(min_len, max_len + 1))
97+
if limit:
98+
total_to_generate = min(total_combinations, limit)
99+
else:
100+
total_to_generate = total_combinations
101+
102+
counter = 0
103+
last_update = time.time()
104+
105+
try:
106+
for length in range(min_len, max_len + 1):
107+
if limit and counter >= limit:
108+
break
109+
110+
for p in itertools.product(charset_list, repeat=length):
111+
word = "".join(p) + "\n"
112+
f.write(word)
113+
counter += 1
114+
115+
if limit and counter >= limit:
116+
break
117+
118+
if output_file and time.time() - last_update > 0.5:
119+
percent = (counter / total_to_generate) * 100
120+
elapsed = time.time() - start_time
121+
speed = counter / elapsed if elapsed > 0 else 0
122+
remaining = (total_to_generate - counter) / speed if speed > 0 else 0
123+
124+
sys.stdout.write(f"\r[%] Progress: {percent:.2f}% | Speed: {int(speed)} w/s | ETA: {int(remaining)}s ")
125+
sys.stdout.flush()
126+
last_update = time.time()
127+
128+
except KeyboardInterrupt:
129+
print("\n[!] Operation interrupted.")
130+
finally:
131+
if output_file and f:
132+
f.close()
133+
print(f"\n[*] Finished! File: {output_file}")
134+
print(f"[*] Total Words: {counter}")
135+
136+
def get_char_list_from_pattern_char(char):
137+
if char in CRUNCH_PATTERNS:
138+
return list(CRUNCH_PATTERNS[char])
139+
return [char]
140+
141+
def generate_with_pattern(pattern, output_file, limit=None):
142+
start_time = time.time()
143+
generators = []
144+
145+
for char in pattern:
146+
generators.append(get_char_list_from_pattern_char(char))
147+
148+
total_combinations = 1
149+
for gen in generators:
150+
total_combinations *= len(gen)
151+
152+
if limit:
153+
total_to_generate = min(total_combinations, limit)
154+
else:
155+
total_to_generate = total_combinations
156+
157+
line_len = len(pattern) + len(os.linesep)
158+
total_size = total_to_generate * line_len
159+
formatted_size = format_size(total_size)
160+
161+
print(f"[*] Pattern : {pattern}")
162+
if limit:
163+
print(f"[*] Word Limit : {limit}")
164+
print(f"[*] Est. Size : {formatted_size}")
165+
166+
if output_file:
167+
try:
168+
f = open(output_file, 'w', encoding='utf-8', buffering=BUFFER_SIZE)
169+
except OSError as e:
170+
print(f"[!] File opening error: {e}")
171+
sys.exit(1)
172+
else:
173+
f = sys.stdout
174+
175+
counter = 0
176+
last_update = time.time()
177+
178+
try:
179+
for p in itertools.product(*generators):
180+
word = "".join(p) + "\n"
181+
f.write(word)
182+
counter += 1
183+
184+
if limit and counter >= limit:
185+
break
186+
187+
if output_file and time.time() - last_update > 0.5:
188+
percent = (counter / total_to_generate) * 100
189+
elapsed = time.time() - start_time
190+
speed = counter / elapsed if elapsed > 0 else 0
191+
remaining = (total_to_generate - counter) / speed if speed > 0 else 0
192+
193+
sys.stdout.write(f"\r[%] Progress: {percent:.2f}% | Speed: {int(speed)}/s | ETA: {int(remaining)}s ")
194+
sys.stdout.flush()
195+
last_update = time.time()
196+
197+
except KeyboardInterrupt:
198+
print("\n[!] Operation interrupted.")
199+
finally:
200+
if output_file and f:
201+
f.close()
202+
print(f"\n[*] Finished! File: {output_file}")
203+
print(f"[*] Total: {counter}")
204+
205+
def main():
206+
signal.signal(signal.SIGINT, signal_handler)
207+
208+
parser = argparse.ArgumentParser(
209+
description=f"PyCrunch v{VERSION} - Professional Wordlist Generator",
210+
formatter_class=argparse.RawTextHelpFormatter,
211+
epilog="Examples:\n"
212+
" python pycrunch.py 4 4 abcdef -o list.txt\n"
213+
" python pycrunch.py 6 8 -o numbers.txt -l 5000\n"
214+
" python pycrunch.py -t p@ss%% -o pattern.txt\n"
215+
" @ = a-z\n"
216+
" , = A-Z\n"
217+
" % = 0-9\n"
218+
" ^ = symbols"
219+
)
220+
221+
parser.add_argument("min", type=int, nargs='?', help="Min length (required if no pattern)")
222+
parser.add_argument("max", type=int, nargs='?', help="Max length (required if no pattern)")
223+
parser.add_argument("chars", type=str, nargs='?', default=DEFAULT_CHARSET, help="Characters to use")
224+
parser.add_argument("-o", "--output", help="Output file")
225+
parser.add_argument("-t", "--pattern", help="Pattern use")
226+
parser.add_argument("-l", "--limit", type=int, help="Max words to generate")
227+
228+
args = parser.parse_args()
229+
230+
if args.pattern:
231+
generate_with_pattern(args.pattern, args.output, args.limit)
232+
sys.exit(0)
233+
234+
if args.min is None or args.max is None:
235+
parser.print_help()
236+
print("\n[!] Error: Min and max values are required if pattern is not used.")
237+
sys.exit(1)
238+
239+
if args.min > args.max:
240+
print("[!] Error: Min length cannot be greater than max.")
241+
sys.exit(1)
242+
243+
generate_wordlist(args.min, args.max, args.chars, args.output, args.limit)
244+
245+
if __name__ == "__main__":
246+
main()

0 commit comments

Comments
 (0)