diff --git a/README.md b/README.md index e24fb94..6962033 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,8 @@ # Android Pattern Lock Decoder -Pattern unlock on Android is entered by joining at least four points on a 3x3 matrix. Each -point can only be used once and the maximum number of points is nine. Android, internally -stores the pattern as a byte sequence. Each point is mapped to its index, where 0 represents -the top left point and 8 the bottom right one on the matrix. Thus the pattern is similar -to a PIN Lock with a minimum of four and and a maximum of nine digits but which only use -nine distinctive digits (0 to 8). Because points cannot be repeated in the pattern and -certain combinations are not possible (e.g. directly connecting 1 to 9), the number of -variations in an unlock pattern is considerably lower compared to those of a nine-digit PIN. +Pattern unlock on Android is entered by joining at least four points on a 3x3 matrix. Each point can only be used once and the maximum number of points is nine. Android, internally stores the pattern as a byte sequence. + +Each point is mapped to its index, where 0 represents the top left point and 8 the bottom right one on the matrix. Thus the pattern is similar to a PIN Lock with a minimum of four and and a maximum of nine digits but which only use nine distinctive digits (0 to 8). Because points cannot be repeated in the pattern and certain combinations are not possible (e.g. directly connecting 1 to 9), the number of variations in an unlock pattern is considerably lower compared to those of a nine-digit PIN. The Sha-1 hash of a pattern lock is stored in ``` @@ -17,23 +12,34 @@ The Sha-1 hash of a pattern lock is stored in ``` as an **unsalted** SHA-1 value. -Each pattern is always hashed to the same value because a random salt value isn't used -when calculating the hash. This makes it relatively easy to generate a precomputed table -of all possible patterns and their respective hashes. This allows for **instant** recovery -of the pattern once the `gesture.key` file is retrieved from the device. However the file, -is owned by the system user and its default permissions are set to `0600` so on production -devices recovery is not possible without rooting the device. +Each pattern is always hashed to the same value because a random salt value isn't used when calculating the hash. This makes it relatively easy to generate a precomputed table of all possible patterns and their respective hashes. This allows for **instant** recovery of the pattern once the `gesture.key` file is retrieved from the device. However the file, +is owned by the system user and its default permissions are set to `0600` so on production devices recovery is not possible without rooting the device. ## Usage +```bash +Usage: python3 androidpatterndecode.py -g -d . + +This program is used to recover android's pattern password. + +Options: + -h, --help show this help message and exit + -g GESTURE_FILE, --gesture=GESTURE_FILE + Path to your gesture.key file in your local system + -d DICTIONARY_FILE, --dictionary=DICTIONARY_FILE + Path to your dictionary file containing sha1 hashes in + your local system +``` + +## Example + Example below uses the following pattern lock: -![Pattern-Lock](https://github.com/cvarta/droid-pattern-decoder/blob/master/res/img/device-2015-02-18-234613.png) +![Pattern-Lock](./res/img/pattern.png) -Copy the gesture.key file from `/data/system/gesture.key` to `/sdcard/` and pull the file -from the device using `adb pull /sdcard/gesture.key .` +Copy the gesture.key file from `/data/system/gesture.key` to `/sdcard/` and pull the file from the device using `adb pull /sdcard/gesture.key .` -To view the content use the od dump tool (Example file in [res/gesture.key](../master/res/gesture.key)) +To view the content use the `od` dump tool or any hex editor like `ghex or HxD or xxd` (Example file in [res/gesture.key](./res/gesture.key)) ``` $ od -t x1 < res/gesture.key @@ -42,10 +48,9 @@ $ od -t x1 < res/gesture.key 0000024 ``` -To lookup the pattern use the supplied python script with the provided hash table: - -``` -$ ./src/androidpatterndecode.py -g ./res/gesture.key -d ./res/androidpatternsha1.txt +To retrieve the pattern from the `gesture.key` file, use the [androidpatterndecode.py](./androidpatterndecode.py) a python script by providing a hash table: - 3214789 +```bash +$ python3 androidpatterndecode.py -g res/gesture.key -d res/androidpatternsha1.txt +[+] Pattern retrieved from gesture.key file is: 3214789 ``` diff --git a/src/androidpatterndecode.py b/androidpatterndecode.py old mode 100755 new mode 100644 similarity index 54% rename from src/androidpatterndecode.py rename to androidpatterndecode.py index 4a6433a..7bff6c0 --- a/src/androidpatterndecode.py +++ b/androidpatterndecode.py @@ -1,57 +1,80 @@ -#!/usr/bin/python +#!/usr/bin/env python3 + __author__ = 'Chris Yereaztian' + """ This script recovers the pattern combination from Android's pattern lock. To acquire the gesture.key file you need a rooted device. """ + from optparse import OptionParser +import binascii +def b2s(a): + """ + Converts bytes to str + """ + return "".join(list(map(chr, a))) def read_gesture(gesture_file_loc=""): - """Reads gesture.key file and converts bytes to string""" + """ + Reads gesture.key file and converts bytes to string + """ try: gesture_file = open(gesture_file_loc, "rb") sha1bytes = [] for i in range(0, 20): sha1hash = gesture_file.read(1) - sha1bytes.append(sha1hash.encode('hex')) - i += 1 + sha1bytes.append(binascii.hexlify(sha1hash)) + # i += 1 sha1hash = "" for i in range(0, 20): - sha1hash += sha1bytes[i] - return sha1hash + sha1hash += b2s(sha1bytes[i]) + return(sha1hash) except IOError: - print "[-] Gesture file cannot be opened. File not present or permission denied" + print("[-] Gesture file cannot be opened. File not present or permission denied") exit(1) def match_pattern(dictionary="", sha1hash=""): - """Looks up hash in provided dictionary""" + """ + Looks up hash in provided dictionary + """ + sha1hash = str.upper(sha1hash) dictionary_file = open(dictionary, "r") lines = dictionary_file.readlines() + for line in lines: if line.__contains__(sha1hash): index = line.index(";", 0, 10) pattern = line[0:index] - print "" - print pattern.center(80) + print("[+] Pattern retrieved from gesture.key file is: " + pattern) + # print(pattern.center(80)) def parse_opt(): - #Create a option parser - parser = OptionParser("%prog [options].\n\rThis program is used to recover android's pattern passowrd.") - #Add options + # Create a option parser + + parser = OptionParser("python3 %prog -g -d .\n\rThis program is used to recover android's pattern password.") + + # Add options + parser.add_option("-g", "--gesture", type="string", dest="gesture_file", help="Path to your gesture.key file in your local system") - parser.add_option("-d", "--dictionary", type="string", dest="dictionary_file", help="Path to your dictionary file in your local system") - #Parse options + parser.add_option("-d", "--dictionary", type="string", dest="dictionary_file", help="Path to your dictionary file containing sha1 hashes in your local system") + + # Parse options + options, arguments = parser.parse_args() - #Verify all options are used + + # Verify all options are used + if options.gesture_file and options.dictionary_file: - #Recover the pattern from the hash + # Recover the pattern from the hash sha1hash = read_gesture(options.gesture_file) match_pattern(options.dictionary_file, sha1hash) + else: - #Show help screen in case of missing arguments + # Show help screen in case of missing arguments parser.print_help() exit(1) diff --git a/res/img/device-2015-02-18-234613.png b/res/img/device-2015-02-18-234613.png deleted file mode 100644 index cf37166..0000000 Binary files a/res/img/device-2015-02-18-234613.png and /dev/null differ diff --git a/res/img/pattern.png b/res/img/pattern.png new file mode 100644 index 0000000..e273e6f Binary files /dev/null and b/res/img/pattern.png differ