-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathandroid_pattern_cracker.py
More file actions
executable file
·185 lines (149 loc) · 5.82 KB
/
Copy pathandroid_pattern_cracker.py
File metadata and controls
executable file
·185 lines (149 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python3
"""
Android Pattern Cracker
Android lock patterns map to a 3x3 grid of dots (0-8):
0 1 2
3 4 5
6 7 8
Each pattern is a sequence of dots (length 4-9). The pattern is hashed
with SHA1 to produce the lock screen credential stored on-device.
Modes:
crack - Given a SHA1 hash (or file containing one), find the pattern.
generate - Print all valid patterns and their SHA1 hashes.
"""
import sys
import itertools
import argparse
import hashlib
# Dot positions as hex bytes (0x00-0x08) — used for SHA1 hashing.
DOTS_HEX = [chr(i) for i in range(9)]
# Dot positions as human-readable strings ("0"-"8") — used for display.
DOTS_ASCII = [str(i) for i in range(9)]
def read_pattern_hash(filepath):
"""
Read a binary pattern file and return its content as a hex string.
Args:
filepath: Path to a raw binary pattern file (e.g. gesture_0.key).
Returns:
Hex string representation of the file's bytes.
"""
with open(filepath, "rb") as f:
return f.read().hex()
def format_result(pattern_str, sha1_hex):
"""
Format a pattern and its hash into a display string.
Args:
pattern_str: Readable pattern (e.g. "01478").
sha1_hex: SHA1 hash of the pattern.
Returns:
Formatted string "pattern : hash".
"""
return f"{pattern_str} : {sha1_hex}"
def _dots_to_str(hex_tuple):
"""
Convert a tuple of hex dot characters back to a readable pattern string.
Args:
hex_tuple: Tuple of single-char hex strings (e.g. ("\x00", "\x01")).
Returns:
Readable pattern string (e.g. "01").
"""
return "".join(str(DOTS_HEX.index(dot)) for dot in hex_tuple)
def crack_pattern(pattern_hex_str, length_range, write_file, verbose):
"""
Brute-force all dot permutations to find a pattern matching a SHA1 hash.
Iterates over every permutation of dots for each size in length_range,
hashes each permutation, and compares against the target hash.
Args:
pattern_hex_str: Target SHA1 hash to match against.
length_range: Range of pattern lengths to search (e.g. range(4, 10)).
write_file: File path to write the result, or None to skip writing.
verbose: If True, print every permutation attempted.
Returns:
The matching pattern string (e.g. "01478"), or None if not found.
"""
for size in length_range:
print(f"[+] Searching patterns of size = {size}")
for perm_hex in itertools.permutations(DOTS_HEX, size):
hex_str = "".join(perm_hex)
sha1_hex = hashlib.sha1(hex_str.encode("latin-1")).hexdigest()
if sha1_hex == pattern_hex_str:
pattern_str = _dots_to_str(perm_hex)
print(f"[!] ###### Successfully cracked! Pattern = {pattern_str} ######")
if write_file:
with open(write_file, "w") as f:
f.write(f"{pattern_str},{pattern_hex_str}")
print("[!] Written to file.")
return pattern_str
if verbose:
print(format_result(_dots_to_str(perm_hex), sha1_hex))
print(f"[+] Finished searching size = {size}")
return None
def generate_patterns(length_range):
"""
Generate and print all valid pattern permutations with their SHA1 hashes.
Args:
length_range: Range of pattern lengths to generate (e.g. range(4, 10)).
"""
for size in length_range:
print(f"[+] Generating patterns of size = {size}")
for perm_hex in itertools.permutations(DOTS_HEX, size):
hex_str = "".join(perm_hex)
sha1_hex = hashlib.sha1(hex_str.encode("latin-1")).hexdigest()
print(format_result(_dots_to_str(perm_hex), sha1_hex))
print(f"[+] Generated patterns of size = {size}")
def parse_args():
"""
Parse and validate command-line arguments.
Returns:
argparse.Namespace with mode, length, file, hash, write, verbose.
"""
parser = argparse.ArgumentParser(
description="Crack or generate Android lock patterns."
)
parser.add_argument(
"--mode", required=True, choices=["crack", "generate"],
help="crack: find a pattern from its hash; generate: print all patterns"
)
parser.add_argument(
"--length", type=int, default=0, choices=range(4, 10),
help="Pattern length (default: try 4-9)"
)
parser.add_argument("--file", help="File containing the pattern hash (crack mode)")
parser.add_argument("--hash", help="SHA1 hash to crack (crack mode)")
parser.add_argument(
"--write", default="output",
help="Output file (default: 'output')"
)
parser.add_argument("--verbose", action="store_true", help="Print every permutation")
return parser.parse_args()
def main():
"""
Entry point: parse arguments and dispatch to generate or crack mode.
"""
args = parse_args()
# Determine length range to iterate over.
if args.length == 0:
length_range = range(4, 10)
else:
length_range = range(args.length, args.length + 1)
if args.mode == "generate":
generate_patterns(length_range)
return
# Crack mode: resolve the target hash from --file or --hash.
if args.file and args.hash:
print("[!] Error: use --file or --hash, not both.")
sys.exit(1)
if args.file:
pattern_hex_str = read_pattern_hash(args.file)
print(f"[+] Pattern from file: {pattern_hex_str}")
elif args.hash:
pattern_hex_str = args.hash
print(f"[+] Pattern hash: {args.hash}")
else:
print("[!] Error: --file or --hash required in crack mode.")
sys.exit(1)
result = crack_pattern(pattern_hex_str, length_range, args.write, args.verbose)
if result is None:
print("[!] No matching pattern found.")
if __name__ == "__main__":
main()