From 80b9f4950e48b4fa131aef7b9a9c3929aed8bfec Mon Sep 17 00:00:00 2001 From: bandrel Date: Sat, 27 Jan 2018 12:21:00 -0500 Subject: [PATCH] replaced submodules --- .gitmodules | 12 - PACK/ChangeLog | 8 + PACK/LICENSE | 27 + PACK/README | 677 ++ PACK/enchant/__init__.py | 907 ++ PACK/enchant/_enchant.py | 366 + PACK/enchant/checker/CmdLineChecker.py | 203 + PACK/enchant/checker/GtkSpellCheckerDialog.py | 304 + PACK/enchant/checker/__init__.py | 379 + PACK/enchant/checker/tests.py | 246 + PACK/enchant/checker/wxSpellCheckerDialog.py | 272 + PACK/enchant/errors.py | 57 + PACK/enchant/lib/enchant/README.txt | 4 + PACK/enchant/pypwl.py | 285 + PACK/enchant/share/enchant/README.txt | 4 + PACK/enchant/share/enchant/ispell/README.txt | 0 PACK/enchant/share/enchant/myspell/README.txt | 3 + PACK/enchant/tests.py | 616 ++ PACK/enchant/tokenize/__init__.py | 536 ++ PACK/enchant/tokenize/en.py | 172 + PACK/enchant/tokenize/tests.py | 326 + PACK/enchant/utils.py | 354 + PACK/maskgen.py | 312 + PACK/policygen.py | 243 + PACK/rulegen.py | 1193 +++ PACK/statsgen.py | 302 + hashcat-utils/CHANGES | 9 + hashcat-utils/LICENSE | 22 + hashcat-utils/README.md | 28 + hashcat-utils/bin/cleanup-rules.app | Bin 0 -> 13072 bytes hashcat-utils/bin/cleanup-rules.bin | Bin 0 -> 10440 bytes hashcat-utils/bin/cleanup-rules.exe | Bin 0 -> 18432 bytes hashcat-utils/bin/combinator.app | Bin 0 -> 13272 bytes hashcat-utils/bin/combinator.bin | Bin 0 -> 10488 bytes hashcat-utils/bin/combinator.exe | Bin 0 -> 18944 bytes hashcat-utils/bin/combinator3.app | Bin 0 -> 13272 bytes hashcat-utils/bin/combinator3.bin | Bin 0 -> 10488 bytes hashcat-utils/bin/combinator3.exe | Bin 0 -> 20480 bytes hashcat-utils/bin/combipow.app | Bin 0 -> 13240 bytes hashcat-utils/bin/combipow.bin | Bin 0 -> 10496 bytes hashcat-utils/bin/combipow.exe | Bin 0 -> 19456 bytes hashcat-utils/bin/cutb.app | Bin 0 -> 8944 bytes hashcat-utils/bin/cutb.bin | Bin 0 -> 6336 bytes hashcat-utils/bin/cutb.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/expander.app | Bin 0 -> 8952 bytes hashcat-utils/bin/expander.bin | Bin 0 -> 10432 bytes hashcat-utils/bin/expander.exe | Bin 0 -> 17920 bytes hashcat-utils/bin/gate.app | Bin 0 -> 8976 bytes hashcat-utils/bin/gate.bin | Bin 0 -> 6344 bytes hashcat-utils/bin/gate.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/generate-rules.app | Bin 0 -> 13104 bytes hashcat-utils/bin/generate-rules.bin | Bin 0 -> 10448 bytes hashcat-utils/bin/generate-rules.exe | Bin 0 -> 18944 bytes hashcat-utils/bin/hcstatgen.app | Bin 0 -> 9144 bytes hashcat-utils/bin/hcstatgen.bin | Bin 0 -> 10480 bytes hashcat-utils/bin/hcstatgen.exe | Bin 0 -> 18432 bytes hashcat-utils/bin/keyspace.app | Bin 0 -> 17392 bytes hashcat-utils/bin/keyspace.bin | Bin 0 -> 18696 bytes hashcat-utils/bin/keyspace.exe | Bin 0 -> 50176 bytes hashcat-utils/bin/len.app | Bin 0 -> 8976 bytes hashcat-utils/bin/len.bin | Bin 0 -> 6344 bytes hashcat-utils/bin/len.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/mli2.app | Bin 0 -> 9080 bytes hashcat-utils/bin/mli2.bin | Bin 0 -> 10464 bytes hashcat-utils/bin/mli2.exe | Bin 0 -> 17920 bytes hashcat-utils/bin/morph.app | Bin 0 -> 13296 bytes hashcat-utils/bin/morph.bin | Bin 0 -> 10512 bytes hashcat-utils/bin/morph.exe | Bin 0 -> 19456 bytes hashcat-utils/bin/permute.app | Bin 0 -> 8920 bytes hashcat-utils/bin/permute.bin | Bin 0 -> 6328 bytes hashcat-utils/bin/permute.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/permute_exist.app | Bin 0 -> 8976 bytes hashcat-utils/bin/permute_exist.bin | Bin 0 -> 10432 bytes hashcat-utils/bin/permute_exist.exe | Bin 0 -> 17920 bytes hashcat-utils/bin/prepare.app | Bin 0 -> 8952 bytes hashcat-utils/bin/prepare.bin | Bin 0 -> 6328 bytes hashcat-utils/bin/prepare.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/req-exclude.app | Bin 0 -> 8976 bytes hashcat-utils/bin/req-exclude.bin | Bin 0 -> 10440 bytes hashcat-utils/bin/req-exclude.exe | Bin 0 -> 18432 bytes hashcat-utils/bin/req-include.app | Bin 0 -> 8976 bytes hashcat-utils/bin/req-include.bin | Bin 0 -> 10440 bytes hashcat-utils/bin/req-include.exe | Bin 0 -> 18432 bytes hashcat-utils/bin/rli.app | Bin 0 -> 13528 bytes hashcat-utils/bin/rli.bin | Bin 0 -> 10552 bytes hashcat-utils/bin/rli.exe | Bin 0 -> 19456 bytes hashcat-utils/bin/rli2.app | Bin 0 -> 9080 bytes hashcat-utils/bin/rli2.bin | Bin 0 -> 10464 bytes hashcat-utils/bin/rli2.exe | Bin 0 -> 17920 bytes hashcat-utils/bin/seprule.pl | 71 + hashcat-utils/bin/splitlen.app | Bin 0 -> 9112 bytes hashcat-utils/bin/splitlen.bin | Bin 0 -> 10472 bytes hashcat-utils/bin/splitlen.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/strip-bsn.app | Bin 0 -> 8976 bytes hashcat-utils/bin/strip-bsn.bin | Bin 0 -> 6336 bytes hashcat-utils/bin/strip-bsn.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/strip-bsr.app | Bin 0 -> 8976 bytes hashcat-utils/bin/strip-bsr.bin | Bin 0 -> 6336 bytes hashcat-utils/bin/strip-bsr.exe | Bin 0 -> 17408 bytes hashcat-utils/bin/tmesis.pl | 59 + hashcat-utils/bin/topmorph.pl | 71 + princeprocessor/CHANGES | 55 + princeprocessor/LICENSE | 33 + princeprocessor/README.md | 38 + princeprocessor/pp.save | 1 + princeprocessor/pp64.app | Bin 0 -> 26040 bytes princeprocessor/pp64.bin | Bin 0 -> 22848 bytes princeprocessor/pp64.exe | Bin 0 -> 56832 bytes princeprocessor/rules/prince_generated.rule | 8461 +++++++++++++++++ princeprocessor/rules/prince_optimized.rule | 1255 +++ 110 files changed, 17899 insertions(+), 12 deletions(-) delete mode 100644 .gitmodules create mode 100644 PACK/ChangeLog create mode 100644 PACK/LICENSE create mode 100644 PACK/README create mode 100644 PACK/enchant/__init__.py create mode 100644 PACK/enchant/_enchant.py create mode 100644 PACK/enchant/checker/CmdLineChecker.py create mode 100644 PACK/enchant/checker/GtkSpellCheckerDialog.py create mode 100644 PACK/enchant/checker/__init__.py create mode 100644 PACK/enchant/checker/tests.py create mode 100644 PACK/enchant/checker/wxSpellCheckerDialog.py create mode 100644 PACK/enchant/errors.py create mode 100644 PACK/enchant/lib/enchant/README.txt create mode 100644 PACK/enchant/pypwl.py create mode 100644 PACK/enchant/share/enchant/README.txt create mode 100644 PACK/enchant/share/enchant/ispell/README.txt create mode 100644 PACK/enchant/share/enchant/myspell/README.txt create mode 100644 PACK/enchant/tests.py create mode 100644 PACK/enchant/tokenize/__init__.py create mode 100644 PACK/enchant/tokenize/en.py create mode 100644 PACK/enchant/tokenize/tests.py create mode 100644 PACK/enchant/utils.py create mode 100755 PACK/maskgen.py create mode 100755 PACK/policygen.py create mode 100755 PACK/rulegen.py create mode 100755 PACK/statsgen.py create mode 100644 hashcat-utils/CHANGES create mode 100644 hashcat-utils/LICENSE create mode 100644 hashcat-utils/README.md create mode 100755 hashcat-utils/bin/cleanup-rules.app create mode 100755 hashcat-utils/bin/cleanup-rules.bin create mode 100755 hashcat-utils/bin/cleanup-rules.exe create mode 100755 hashcat-utils/bin/combinator.app create mode 100755 hashcat-utils/bin/combinator.bin create mode 100755 hashcat-utils/bin/combinator.exe create mode 100755 hashcat-utils/bin/combinator3.app create mode 100755 hashcat-utils/bin/combinator3.bin create mode 100755 hashcat-utils/bin/combinator3.exe create mode 100755 hashcat-utils/bin/combipow.app create mode 100755 hashcat-utils/bin/combipow.bin create mode 100755 hashcat-utils/bin/combipow.exe create mode 100755 hashcat-utils/bin/cutb.app create mode 100755 hashcat-utils/bin/cutb.bin create mode 100755 hashcat-utils/bin/cutb.exe create mode 100755 hashcat-utils/bin/expander.app create mode 100755 hashcat-utils/bin/expander.bin create mode 100755 hashcat-utils/bin/expander.exe create mode 100755 hashcat-utils/bin/gate.app create mode 100755 hashcat-utils/bin/gate.bin create mode 100755 hashcat-utils/bin/gate.exe create mode 100755 hashcat-utils/bin/generate-rules.app create mode 100755 hashcat-utils/bin/generate-rules.bin create mode 100755 hashcat-utils/bin/generate-rules.exe create mode 100755 hashcat-utils/bin/hcstatgen.app create mode 100755 hashcat-utils/bin/hcstatgen.bin create mode 100755 hashcat-utils/bin/hcstatgen.exe create mode 100755 hashcat-utils/bin/keyspace.app create mode 100755 hashcat-utils/bin/keyspace.bin create mode 100755 hashcat-utils/bin/keyspace.exe create mode 100755 hashcat-utils/bin/len.app create mode 100755 hashcat-utils/bin/len.bin create mode 100755 hashcat-utils/bin/len.exe create mode 100755 hashcat-utils/bin/mli2.app create mode 100755 hashcat-utils/bin/mli2.bin create mode 100755 hashcat-utils/bin/mli2.exe create mode 100755 hashcat-utils/bin/morph.app create mode 100755 hashcat-utils/bin/morph.bin create mode 100755 hashcat-utils/bin/morph.exe create mode 100755 hashcat-utils/bin/permute.app create mode 100755 hashcat-utils/bin/permute.bin create mode 100755 hashcat-utils/bin/permute.exe create mode 100755 hashcat-utils/bin/permute_exist.app create mode 100755 hashcat-utils/bin/permute_exist.bin create mode 100755 hashcat-utils/bin/permute_exist.exe create mode 100755 hashcat-utils/bin/prepare.app create mode 100755 hashcat-utils/bin/prepare.bin create mode 100755 hashcat-utils/bin/prepare.exe create mode 100755 hashcat-utils/bin/req-exclude.app create mode 100755 hashcat-utils/bin/req-exclude.bin create mode 100755 hashcat-utils/bin/req-exclude.exe create mode 100755 hashcat-utils/bin/req-include.app create mode 100755 hashcat-utils/bin/req-include.bin create mode 100755 hashcat-utils/bin/req-include.exe create mode 100755 hashcat-utils/bin/rli.app create mode 100755 hashcat-utils/bin/rli.bin create mode 100755 hashcat-utils/bin/rli.exe create mode 100755 hashcat-utils/bin/rli2.app create mode 100755 hashcat-utils/bin/rli2.bin create mode 100755 hashcat-utils/bin/rli2.exe create mode 100755 hashcat-utils/bin/seprule.pl create mode 100755 hashcat-utils/bin/splitlen.app create mode 100755 hashcat-utils/bin/splitlen.bin create mode 100755 hashcat-utils/bin/splitlen.exe create mode 100755 hashcat-utils/bin/strip-bsn.app create mode 100755 hashcat-utils/bin/strip-bsn.bin create mode 100755 hashcat-utils/bin/strip-bsn.exe create mode 100755 hashcat-utils/bin/strip-bsr.app create mode 100755 hashcat-utils/bin/strip-bsr.bin create mode 100755 hashcat-utils/bin/strip-bsr.exe create mode 100755 hashcat-utils/bin/tmesis.pl create mode 100755 hashcat-utils/bin/topmorph.pl create mode 100644 princeprocessor/CHANGES create mode 100644 princeprocessor/LICENSE create mode 100644 princeprocessor/README.md create mode 100644 princeprocessor/pp.save create mode 100755 princeprocessor/pp64.app create mode 100755 princeprocessor/pp64.bin create mode 100644 princeprocessor/pp64.exe create mode 100644 princeprocessor/rules/prince_generated.rule create mode 100644 princeprocessor/rules/prince_optimized.rule diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e4e6a57..0000000 --- a/.gitmodules +++ /dev/null @@ -1,12 +0,0 @@ -[submodule "PACK"] - path = PACK - url = https://github.com/iphelix/pack.git -[submodule "hashcat-utils"] - path = hashcat-utils - url = https://github.com/hashcat/hashcat-utils.git -[submodule "princeprocessor"] - path = princeprocessor - url = https://github.com/hashcat/princeprocessor.git -[submodule "pack"] - path = pack - url = https://github.com/iphelix/pack.git diff --git a/PACK/ChangeLog b/PACK/ChangeLog new file mode 100644 index 0000000..43db990 --- /dev/null +++ b/PACK/ChangeLog @@ -0,0 +1,8 @@ +PACK 0.0.4 August 8, 2013 +------------------------- + +* Rewrote maskgen to generate optimized masks in addition to custom parameters. +* Rewrote policygen to generate non-compliant masks. +* Rewrote rulegen to use multiple processes for significant speed improvements on multi-core systems. +* Renamed dictstat to statsgen and removed all regular expressions for performance enhancements. +* Rewrote statsgen to attempt policy detection. \ No newline at end of file diff --git a/PACK/LICENSE b/PACK/LICENSE new file mode 100644 index 0000000..1b563eb --- /dev/null +++ b/PACK/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013, Peter Kacherginsky +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of the 'The Sprawl' nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PACK/README b/PACK/README new file mode 100644 index 0000000..304fc69 --- /dev/null +++ b/PACK/README @@ -0,0 +1,677 @@ +Password Analysis and Cracking Kit by Peter Kacherginsky (iphelix) +================================================================== + +PACK (Password Analysis and Cracking Toolkit) is a collection of utilities developed to aid in analysis of password lists in order to enhance password cracking through pattern detection of masks, rules, character-sets and other password characteristics. The toolkit generates valid input files for Hashcat family of password crackers. + +NOTE: The toolkit itself is not able to crack passwords, but instead designed to make operation of password crackers more efficient. + +Selecting passwords lists for analysis +====================================== + +Before we can begin using the toolkit we must establish a selection criteria of password lists. Since we are looking to analyze the way people create their passwords, we must obtain as large of a sample of leaked passwords as possible. One such excellent list is based on RockYou.com compromise. This list both provides large and diverse enough collection that provides a good results for common passwords used by similar sites (e.g. social networking). The analysis obtained from this list may not work for organizations with specific password policies. As such, selecting sample input should be as close to your target as possible. In addition, try to avoid obtaining lists based on already cracked passwords as it will generate statistics bias of rules and masks used by individual(s) cracking the list and not actual users. + +StatsGen +======================================= + +The most basic analysis that you can perform is simply obtaining most common length, character-set and other characteristics of passwords in the provided list. In the example below, we will use 'rockyou.txt' containing approximately 14 million passwords. Launch `statsgen.py` with the following command line: + + $ python statsgen.py rockyou.txt + +Below is the output from the above command: + + _ + StatsGen #.#.# | | + _ __ __ _ ___| | _ + | '_ \ / _` |/ __| |/ / + | |_) | (_| | (__| < + | .__/ \__,_|\___|_|\_\ + | | + |_| iphelix@thesprawl.org + + + [*] Analyzing passwords in [rockyou.txt] + [+] Analyzing 100% (14344390/14344390) of passwords + NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords + + [*] Length: + [+] 8: 20% (2966037) + [+] 7: 17% (2506271) + [+] 9: 15% (2191039) + [+] 10: 14% (2013695) + [+] 6: 13% (1947798) + ... + + [*] Character-set: + [+] loweralphanum: 42% (6074867) + [+] loweralpha: 25% (3726129) + [+] numeric: 16% (2346744) + [+] loweralphaspecialnum: 02% (426353) + [+] upperalphanum: 02% (407431) + ... + + [*] Password complexity: + [+] digit: min(0) max(255) + [+] lower: min(0) max(255) + [+] upper: min(0) max(187) + [+] special: min(0) max(255) + + [*] Simple Masks: + [+] stringdigit: 37% (5339556) + [+] string: 28% (4115314) + [+] digit: 16% (2346744) + [+] digitstring: 04% (663951) + [+] othermask: 04% (576324) + ... + + [*] Advanced Masks: + [+] ?l?l?l?l?l?l?l?l: 04% (687991) + [+] ?l?l?l?l?l?l: 04% (601152) + [+] ?l?l?l?l?l?l?l: 04% (585013) + [+] ?l?l?l?l?l?l?l?l?l: 03% (516830) + [+] ?d?d?d?d?d?d?d: 03% (487429) + ... + + +NOTE: You can reduce the number of outliers displayed by including the --hiderare flag which will not show any items with occurrence of less than 1%. + +Here is what we can immediately learn from the above list: + + * Most of the passwords have length 6 to 10 characters. + * The majority of passwords have loweralphanumeric character-set. + * There is no obvious minimum or maximum password complexity. + * Analyzed passwords tend to follow a simple masks "string followed by digits". + +The last section, "Advanced Masks", contains most frequently occurring masks using the Hashcat format. Individual symbols can be interpreted as follows: + + ?l - a single lowercase character + ?u - a single uppercase character + ?d - a single digit + ?s - a single special character + +For example, the very first mask, "?l?l?l?l?l?l?l?l", will match all of the lowercase alpha passwords. Given the sample size you will be able to crack approximately 4% of passwords. However, after generating the initial output, you may be interested in using filters to narrow down on password data. + +Using filters +------------- + +Let's see how RockYou users tend to select their passwords using the "stringdigit" simple mask (a string followed by numbers): + + $ python statsgen.py ../PACK-0.0.3/archive/rockyou.txt --simplemask stringdigit -q --hiderare + + [*] Analyzing passwords in [rockyou.txt] + [+] Analyzing 37% (5339556/14344390) of passwords + NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords + + [*] Length: + [+] 8: 23% (1267260) + [+] 7: 18% (981432) + [+] 9: 17% (939971) + [+] 10: 14% (750938) + [+] 6: 11% (618983) + [+] 11: 05% (294869) + [+] 12: 03% (175875) + [+] 13: 01% (103047) + [+] 14: 01% (65958) + + [*] Character-set: + [+] loweralphanum: 88% (4720184) + [+] upperalphanum: 06% (325941) + [+] mixedalphanum: 05% (293431) + + [*] Password complexity: + [+] digit: min(1) max(252) + [+] lower: min(0) max(46) + [+] upper: min(0) max(31) + [+] special: min(0) max(0) + + [*] Simple Masks: + [+] stringdigit: 100% (5339556) + + [*] Advanced Masks: + [+] ?l?l?l?l?l?l?d?d: 07% (420318) + [+] ?l?l?l?l?l?d?d: 05% (292306) + [+] ?l?l?l?l?l?l?l?d?d: 05% (273624) + [+] ?l?l?l?l?d?d?d?d: 04% (235360) + [+] ?l?l?l?l?d?d: 04% (215074) + ... + +The very top of the output specifies what percentage of total passwords was analyzed. In this case, by cracking only passwords matching the "stringdigit" mask it is only possible to recover about 37% of the total set just as was displayed in the original output. Next, it appears that only 11% of this password type use anything other than lowercase. So it would be smart to concentrate on only lowercase strings matching this mask. At last, in the "Advanced Mask" section we can see that the majority of "stringdigit" passwords consist of a string with two or four digits following it. With the information gained from the above output, we can begin creating a mental image of target users' password generation patterns. + +There are a few other filters available for password length, mask, and character sets: + +**Length:** --minlength and/or --maxlength + +**Simple Mask:** --simplemask [numeric, loweralpha, upperalpha, mixedalpha, loweralphanum, etc.] + +**Character sets:** --charset [digit, string, stringdigit, digitstring, digitstringdigit, etc.] + +NOTE: More than one filter of the same class can be specified as a comma-separated list: + + --simplemask="stringdigit,digitstring" + +Saving advanced masks +--------------------- + +While the "Advanced Mask" section only displays patterns matching greater than 1% of all passwords, you can obtain and save a full list of password masks matching a given dictionary by using the following command: + + $ python statsgen.py rockyou.txt -o rockyou.masks + +All of the password masks and their frequencies will be saved into the specified file in the CSV format. Naturally, you can provide filters to only generate masks file matching specified parameters. The output file can be used as an input to MaskGen tool covered in the next section. + +MaskGen +================== + +MaskGen allows you to craft pattern-based mask attacks for input into Hashcat family of password crackers. The tool uses output produced by statsgen above with the '-o' flag in order to produce the most optimal mask attack sorted by mask complexity, mask occurrence or ratio of the two (optimal index). + +Let's run MaskGen with only StatGen's output as an argument: + + $ python maskgen.py rockyou.masks + + _ + MaskGen #.#.# | | + _ __ __ _ ___| | _ + | '_ \ / _` |/ __| |/ / + | |_) | (_| | (__| < + | .__/ \__,_|\___|_|\_\ + | | + |_| iphelix@thesprawl.org + + + [*] Analyzing masks in [rockyou.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [optindex]. + [*] Finished generating masks: + Masks generated: 146578 + Masks coverage: 100% (14344390/14344390) + Masks runtime: >1 year + +There are several pieces of information that you should observe: + + * Default cracking speed used for calculations is 1,000,000,000 keys/sec + * Default sorting mode is [optindex] equivalent to --optindex flag. + * 146,578 unique masks were generated which have 100% coverage + * Total runtime of all generated masks is more than 1 year. + +Specifying target time +---------------------- + +Since you are usually limited in time to perform and craft attacks, maskgen allows you to specify how much time you have to perform mask attacks and will generate the most optimal collection of masks based on the sorting mode. Let's play a bit with different sorting modes and target times: + + $ python maskgen.py rockyou.masks --targettime 600 --optindex -q + [*] Analyzing masks in [rockyou.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [optindex]. + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 779 + Masks coverage: 56% (8116195/14344390) + Masks runtime: 0:11:36 + + + $ python maskgen.py rockyou.masks --targettime 600 --complexity -q + [*] Analyzing masks in [rockyou.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [complexity]. + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 5163 + Masks coverage: 31% (4572346/14344390) + Masks runtime: 0:10:01 + + + $ python maskgen.py rockyou.masks --targettime 600 --occurrence -q + [*] Analyzing masks in [rockyou.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [occurrence]. + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 4 + Masks coverage: 16% (2390986/14344390) + Masks runtime: 1:34:05 + +All of the above runs have target time of 600 seconds (or 10 minutes) with different sorting modes. Based on our experiments, masks generated using OptIndex sorting mode can crack 56% of RockYou passwords in about 10 minutes. At the same time masks generated using Occurrence sorting mode not only have pretty weak coverage of only 16%, but also exceeded specified target time by more than an hour. + +NOTE: Masks sorted by complexity can be very effective when attacking policy based lists. + +Let's see some of the masks generated by maskgen in optindex mode using the --showmasks flag: + + $ python maskgen.py rockyou.masks --targettime 43200 --optindex -q --showmasks + [*] Analyzing masks in [rockyou.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [optindex]. + [L:] Mask: [ Occ: ] [ Time: ] + ... + [ 7] ?l?d?s?l?l?d?d [6 ] [ 0:00:00] + [ 8] ?s?l?l?l?l?l?l?s [3480 ] [ 0:05:36] + [ 9] ?l?l?l?l?d?d?d?d?s [1553 ] [ 0:02:30] + [ 8] ?d?l?d?d?d?l?l?l [47 ] [ 0:00:04] + [ 8] ?d?l?l?d?l?d?d?l [47 ] [ 0:00:04] + [ 8] ?d?l?l?d?d?l?d?l [47 ] [ 0:00:04] + [ 8] ?d?l?d?l?d?d?l?l [47 ] [ 0:00:04] + [ 8] ?d?d?l?l?d?l?d?l [47 ] [ 0:00:04] + [ 8] ?d?l?d?d?l?l?l?l [122 ] [ 0:00:11] + [ 8] ?u?u?d?u?d?d?d?d [18 ] [ 0:00:01] + [ 6] ?d?s?s?s?s?s [4 ] [ 0:00:00] + [10] ?l?l?l?l?l?l?l?l?d?d [213109 ] [ 5:48:02] + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 3970 + Masks coverage: 74% (10620959/14344390) + Masks runtime: 16:10:38 + +Displayed masks follow a pretty intuitive format: + + + [ 9] ?l?l?l?l?d?d?d?d?s [1553 ] [ 0:02:30] + \ \ \ \ + \ \_ generated mask \ \_ mask runtime + \ \ + \_ mask length \_ mask occurrence + + +In the above sample you can see some of the logic that goes into mask generation. For example, while '?s?l?l?l?l?l?l?s' mask has one of the longest runtimes in the sample (5 minutes), it still has higher priority because of its relatively higher occurrence to '?l?l?l?l?d?d?d?d?s'. At the same time, while '?l?d?s?l?l?d?d' has pretty low coverage it still gets a higher priority than other masks because as only a six character mask it executes very quickly. + +Specifying mask filters +----------------------- + +You can further optimize your generated mask attacks by using filters. For example, you may have sufficiently powerful hardware where you can simple bruteforce all of the passwords up to 8 characters. In this case, you can generate masks only greater than 8 characters using the --minlength flag as follows: + + $ python maskgen.py rockyou.masks --targettime 43200 --optindex -q --minlength 8 + [*] Analyzing masks in [rockyou.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [optindex]. + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 585 + Masks coverage: 41% (5905182/14344390) + Masks runtime: 15:50:36 + +Naturally the generated mask coverage was reduced, but these filters become useful when preparing a collection of masks when attacking password lists other than the one used to generate them. + +The list below shows additional filters you can use: + + Individual Mask Filter Options: + --minlength=8 Minimum password length + --maxlength=8 Maximum password length + --mintime=3600 Minimum mask runtime (seconds) + --maxtime=3600 Maximum mask runtime (seconds) + --mincomplexity=1 Minimum complexity + --maxcomplexity=100 + Maximum complexity + --minoccurrence=1 Minimum occurrence + --maxoccurrence=100 + Maximum occurrence + +Occurrrence and complexity flags can be particularly powerful to fine-tune generated masks using different sorting modes. + +Saving generated masks +---------------------- + +Once you are satisfied with the above generated masks, you can save them using the -o flag: + + $ python maskgen.py rockyou.masks --targettime 43200 --optindex -q -o rockyou.hcmask + [*] Analyzing masks in [rockyou.masks] + [*] Saving generated masks to [rockyou.hcmask] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Sorting masks by their [optindex]. + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 3970 + Masks coverage: 74% (10620959/14344390) + Masks runtime: 16:10:38 + +This will produce 'rockyou.hcmask' file which can be directly used by Hashcat suite of tools or as part of a custom script that loops through them. + +Checking mask coverage +---------------------- + +It is often useful to see how well generated masks perform against already cracked lists. Maskgen can compare a collection of masks against others to see how well they would perform if masks from one password list would be attempted against another. Let's compare how well masks generated from RockYou list will perform against another compromised list such as Gawker: + + $ python statsgen.py ../PACK-0.0.3/archive/gawker.dic -o gawker.masks + + $ python maskgen.py gawker.masks --checkmasksfile rockyou.hcmask -q + [*] Analyzing masks in [gawker.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Checking coverage of masks in [rockyou.hcmask] + [*] Finished matching masks: + Masks matched: 1775 + Masks coverage: 96% (1048889/1084394) + Masks runtime: 16:25:44 + +Using the '--checkmasksfile' parameter we attempted to run masks inside 'rockyou.hcmask' file generated earlier against masks from a sample leaked list 'gawker.masks'. This results in a good 96% coverage where 1775 of the 3970 total generated RockYou-based masks matched masks in Gawker list. + +It is also possible to see the coverage of one or more masks by specifying them directly on the command-line as follows: + + $ python maskgen.py gawker.masks --checkmasks="?u?l?l?l?l?l?d,?l?l?l?l?l?d?d" -q + [*] Analyzing masks in [gawker.masks] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Checking coverage of the these masks [?u?l?l?l?l?l?d, ?l?l?l?l?l?d?d] + [*] Finished matching masks: + Masks matched: 2 + Masks coverage: 1% (18144/1084394) + Masks runtime: 0:00:04 + +Both of the specified masks matched with only 1% coverage. + +Specifying speed +---------------- + +Depending on your exact hardware specs and target hash you may want to increase or decrease keys/sec speed used during calculations using the '--pps' parameter: + + $ python maskgen.py rockyou.masks --targettime 43200 --pps 50000000 -q + [*] Analyzing masks in [rockyou.masks] + [*] Using 50,000,000 keys/sec for calculations. + [*] Sorting masks by their [optindex]. + [!] Target time exceeded. + [*] Finished generating masks: + Masks generated: 1192 + Masks coverage: 61% (8754548/14344390) + Masks runtime: 12:17:31 + +Using the '--pps' parameter to match you actual performance makes target time more meaningful. + +PolicyGen +========= + +A lot of the mask and dictionary attacks will fail in the corporate environment with minimum password complexity requirements. Instead of resorting to a pure bruteforcing attack, we can leverage known or guessed password complexity rules to avoid trying password candidates that are not compliant with the policy or inversely only audit for noncompliant passwords. Using PolicyGen, you will be able to generate a collection of masks following the password complexity in order to significantly reduce the cracking time. + +Below is a sample session where we generate all valid password masks for an environment requiring at least one digit, one upper, and one special characters. + + $ python policygen.py --minlength 8 --maxlength 8 --minlower 1 --minupper 1 --mindigit 1 --minspecial 1 -o complexity.hcmask + _ + PolicyGen #.#.# | | + _ __ __ _ ___| | _ + | '_ \ / _` |/ __| |/ / + | |_) | (_| | (__| < + | .__/ \__,_|\___|_|\_\ + | | + |_| iphelix@thesprawl.org + + + [*] Saving generated masks to [complexity.hcmask] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Password policy: + Pass Lengths: min:8 max:8 + Min strength: l:1 u:1 d:1 s:1 + Max strength: l:None u:None d:None s:None + [*] Generating [compliant] masks. + [*] Generating 8 character password masks. + [*] Total Masks: 65536 Time: 76 days, 18:50:04 + [*] Policy Masks: 40824 Time: 35 days, 0:33:09 + +From the above output you can see that we have generated 40824 masks matching the specified complexity that will take about 35 days to run at the speed of 1,000,000,000 keys/sec. + +In case you are simply performing a password audit and tasked to discover only non-compliant passwords you can specify '--noncompliant' flag to invert generated masks: + + $ python policygen.py --minlength 8 --maxlength 8 --minlower 1 --minupper 1 --mindigit 1 --minspecial 1 -o noncompliant.hcmask -q --noncompliant + [*] Saving generated masks to [noncompliant.hcmask] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Password policy: + Pass Lengths: min:8 max:8 + Min strength: l:1 u:1 d:1 s:1 + Max strength: l:None u:None d:None s:None + [*] Generating [non-compliant] masks. + [*] Generating 8 character password masks. + [*] Total Masks: 65536 Time: 76 days, 18:50:04 + [*] Policy Masks: 24712 Time: 41 days, 18:16:55 + +Let's see some of the non-compliant masks generated above using the '--showmasks' flag: + + $ python policygen.py --minlength 8 --maxlength 8 --minlower 1 --minupper 1 --mindigit 1 --minspecial 1 -o noncompliant.hcmask -q --noncompliant --showmasks + [*] Saving generated masks to [noncompliant.hcmask] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Password policy: + Pass Lengths: min:8 max:8 + Min strength: l:1 u:1 d:1 s:1 + Max strength: l:None u:None d:None s:None + [*] Generating [non-compliant] masks. + [*] Generating 8 character password masks. + [ 8] ?d?d?d?d?d?d?d?d [l: 0 u: 0 d: 8 s: 0] [ 0:00:00] + [ 8] ?d?d?d?d?d?d?d?l [l: 1 u: 0 d: 7 s: 0] [ 0:00:00] + [ 8] ?d?d?d?d?d?d?d?u [l: 0 u: 1 d: 7 s: 0] [ 0:00:00] + [ 8] ?d?d?d?d?d?d?d?s [l: 0 u: 0 d: 7 s: 1] [ 0:00:00] + ... + [ 8] ?s?s?s?s?s?s?s?d [l: 0 u: 0 d: 1 s: 7] [ 0:07:06] + [ 8] ?s?s?s?s?s?s?s?l [l: 1 u: 0 d: 0 s: 7] [ 0:18:28] + [ 8] ?s?s?s?s?s?s?s?u [l: 0 u: 1 d: 0 s: 7] [ 0:18:28] + [ 8] ?s?s?s?s?s?s?s?s [l: 0 u: 0 d: 0 s: 8] [ 0:23:26] + [*] Total Masks: 65536 Time: 76 days, 18:50:04 + [*] Policy Masks: 24712 Time: 41 days, 18:16:55 + +As you can see all of the masks have at least one missing password complexity requirement. Interestingly with fewer generated masks it takes longer to attack because of long running masks like '?s?s?s?s?s?s?s?s'. + +Specifying maximum complexity +----------------------------- + +It is also possible to specify maximum password complexity using --maxlower, --maxupper, --maxdigit and --maxspecial flags in order to fine-tune you attack. For example, below is a sample site which enforces password policy but does not allow any special characters: + + $ python policygen.py --minlength 8 --maxlength 8 --minlower 1 --minupper 1 --mindigit 1 --maxspecial 0 -o maxcomplexity.hcmask -q + [*] Saving generated masks to [maxcomplexity.hcmask] + [*] Using 1,000,000,000 keys/sec for calculations. + [*] Password policy: + Pass Lengths: min:8 max:8 + Min strength: l:1 u:1 d:1 s:None + Max strength: l:None u:None d:None s:0 + [*] Generating [compliant] masks. + [*] Generating 8 character password masks. + [*] Total Masks: 65536 Time: 76 days, 18:50:04 + [*] Policy Masks: 5796 Time: 1 day, 20:20:55 + +Rules Analysis +================== + +`rulegen.py` implements password analysis and rule generation for the Hashcat password cracker as described in the [Automatic Password Rule Analysis and Generation](http://thesprawl.org/research/automatic-password-rule-analysis-generation/) paper. Please review this document for detailed discussion on the theory of rule analysis and generation. + +Reversing source words and word mangling rules from already cracked passwords can be very effective in performing attacks against still encrypted hashes. By continuously recycling/expanding generated rules and words you may be able to crack a greater number of passwords. + +Prerequisites +----------------- +There are several prerequisites for the effective use of `rulegen.py`. The tool utilizes Enchant spell-checking library to interface with a number of spell-checking engines such as Aspell, MySpell, etc. You must install these tools prior to use. It is also critical to install dictionaries for whatever spell-checking engine you end up using (alternatively it is possible to use a custom wordlist). At last, I have bundled PyEnchant for convenience which should interface directly with Enchant's shared libraries; however, should there be any issues, simply remove the bundled 'enchant' directory and install PyEnchant for your distribution. + +For additional details on specific Hashcat rule syntax see [Hashcat Rule Based Attack](http://hashcat.net/wiki/doku.php?id=rule_based_attack). + +Analyzing a Single Password +------------------------------- + +The most basic use of `rulegen.py` involves analysis of a single password to automatically detect rules. Let's detect rules and potential source word used to generate a sample password `P@55w0rd123`: + + $ python rulegen.py --verbose --password P@55w0rd123 + _ + RuleGen #.#.# | | + _ __ __ _ ___| | _ + | '_ \ / _` |/ __| |/ / + | |_) | (_| | (__| < + | .__/ \__,_|\___|_|\_\ + | | + |_| iphelix@thesprawl.org + + + [*] Using Enchant 'aspell' module. For best results please install + 'aspell' module language dictionaries. + [*] Analyzing password: P@55w0rd123 + [-] Pas sword => {edit distance suboptimal: 8 (7)} => P@55w0rd123 + [+] Password => sa@ ss5 so0 $1 $2 $3 => P@55w0rd123 + [+] Passwords => sa@ ss5 so0 o81 $2 $3 => P@55w0rd123 + [+] Passwords => sa@ ss5 so0 i81 o92 $3 => P@55w0rd123 + [+] Passwords => sa@ ss5 so0 i81 i92 oA3 => P@55w0rd123 + [+] Password's => sa@ ss5 so0 o81 o92 $3 => P@55w0rd123 + [+] Password's => sa@ ss5 so0 o81 i92 oA3 => P@55w0rd123 + [+] Password's => sa@ ss5 so0 i81 o92 oA3 => P@55w0rd123 + +There are several flags that we have used for this example: + + * --password - specifies a single password to analyze. + * --verbose - prints out verbose information such as generated rules and performance statistics. + +Processing password files is covered in a section below; however, let's first discuss some of the available fine tuning options using a single password as an example. + +Spell-checking provider +--------------------------- + +Notice that we are using the `aspell` Enchant module for source word detection. The exact spell-checking engine can be changed using the `--provider` flag as follows: + + $ python rulegen.py --verbose --provider myspell --password P@55w0rd123 -q + [*] Using Enchant 'myspell' module. For best results please install + 'myspell' module language dictionaries. + ... + + +NOTE: Provider engine priority can be specified using a comma-separated list (e.g. --provider aspell,myspell). + +Forcing source word +----------------------- + +The use of the source word detection engine can be completely disabled by specifying a source word with the `--word` flag: + + $ python rulegen.py -q --verbose --word word --password P@55w0rd123 + [*] Analyzing password: P@55w0rd123 + [+] word => ^5 ^5 ^@ ^P so0 $1 $2 $3 => P@55w0rd123 + +By specifying different source words you can have a lot of fun experimenting with the rule generation engine. + +Defining Custom Dictionary +------------------------------ + +Inevitably you will come across a point where generating rules using the standard spelling-engine wordlist is no longer sufficient. You can specify a custom wordlist using the `--wordlist` flag. This is particularly useful when reusing source words from a previous analysis session: + + $ python rulegen.py -q --verbose --wordlist rockyou.txt --password 1pa55w0rd1 + [*] Using Enchant 'Personal Wordlist' module. For best results please install + 'Personal Wordlist' module language dictionaries. + [*] Analyzing password: 1pa55w0rd1 + [+] password => ^1 ss5 so0 $1 => 1pa55w0rd1 + +Custom wordlist can be particularly useful when using not normally found words such as slang as well as using already cracked passwords. + +Generating Suboptimal Rules and Words +----------------------------------------- + +While `rulegen.py` attempts to generate and record only the best source words and passwords, there may be cases when you are interested in more results. Use `--morewords` and `--morerules` flags to generate words and rules which may exceed optimal edit distance: + + $ python rulegen.py -q --verbose --password '$m0n3y$' --morerules --morewords + [*] Using Enchant 'aspell' module. For best results please install + 'aspell' module language dictionaries. + [*] Analyzing password: $m0n3y$ + [+] money => ^$ so0 se3 $$ => $m0n3y$ + [+] moneys => ^$ so0 se3 o6$ => $m0n3y$ + [+] mingy => ^$ si0 sg3 $$ => $m0n3y$ + [+] many => ^$ sa0 i43 $$ => $m0n3y$ + [+] Mooney => sM$ o1m so0 se3 $$ => $m0n3y$ + +It is possible to further expand generated words using `--maxworddist` and `--maxwords` flags. Similarly, you can produce more rules using `--maxrulelen` and `--maxrules` flags. + +Disabling Advanced Engines +------------------------------ + +`rulegen.py` includes a number of advanced engines to generate better quality words and rules. It is possible to disable them to observe the difference (or if they are causing issues) using `--simplewords` and `--simplerules` flags. Let's observe how both source words and rules change with these flags on: + + $ python rulegen.py -q --verbose --password '$m0n3y$' --simplewords --simplerules + [*] Using Enchant 'aspell' module. For best results please install + 'aspell' module language dictionaries. + [*] Analyzing password: $m0n3y$ + [-] Meany => {edit distance suboptimal: 5 (4)} => $m0n3y$ + [+] many => i0$ o20 i43 i6$ => $m0n3y$ + [+] mingy => i0$ o20 o43 i6$ => $m0n3y$ + [+] money => i0$ o20 o43 i6$ => $m0n3y$ + [+] mangy => i0$ o20 o43 i6$ => $m0n3y$ + [+] manky => i0$ o20 o43 i6$ => $m0n3y$ + +Notice the quality of generated words and rules was reduced significantly with words like 'manky' having less relationship to the actual source word 'money'. At the same time, generated rules were reduced to simple insertions, deletions and replacements. + +Processing password lists +----------------------------- + +Now that you have mastered all of the different flags and switches, we can attempt to generate words and rules for a collection of passwords. Let's generate a text file `korelogic.txt` containing the following fairly complex test passwords: + + &~defcon + '#(4)\ + August19681 + '&a123456 + 10-D'Ann + ~|Bailey + Krist0f3r + f@cebOOK + Nuclear$( + zxcvbn2010! + 13Hark's + NjB3qqm + Sydney93? + antalya%] + Annl05de + ;-Fluffy + +Now let's observe `rulegen.py` analysis by simply specifying the password file as the first argument: + + $ python rulegen.py korelogic.txt -q + [*] Using Enchant 'aspell' module. For best results please install + 'aspell' module language dictionaries. + [*] Analyzing passwords file: korelogic.txt: + [*] Press Ctrl-C to end execution and generate statistical analysis. + [*] Saving rules to analysis.rule + [*] Saving words to analysis.word + [*] Finished processing 16 passwords in 1.00 seconds at the rate of 15.94 p/sec + [*] Generating statistics for [analysis] rules and words. + [-] Skipped 0 all numeric passwords (0.00%) + [-] Skipped 2 passwords with less than 25% alpha characters (12.50%) + [-] Skipped 0 passwords with non ascii characters (0.00%) + + [*] Top 10 rules + [+] ^3 ^1 o4r - 3 (2.00%) + [+] i61 i79 i86 i98 oA1 - 2 (1.00%) + [+] ^- ^0 ^1 i4' o5A - 2 (1.00%) + [+] sS1 i13 T2 - 1 (0.00%) + [+] i61 se9 i86 i98 oA1 - 1 (0.00%) + [+] o61 i79 i86 i98 oA1 - 1 (0.00%) + [+] ^- ^0 ^1 so' i5A - 1 (0.00%) + [+] D3 si0 i55 $e - 1 (0.00%) + [+] i61 i79 se6 i98 oA1 - 1 (0.00%) + [+] i3a o5y o6a i7% o8] - 1 (0.00%) + + [*] Top 10 words + [+] Analyze - 1 (0.00%) + [+] defcon - 1 (0.00%) + [+] Kristen - 1 (0.00%) + [+] Bailey - 1 (0.00%) + [+] Augusts - 1 (0.00%) + [+] Annelid - 1 (0.00%) + [+] Hack's - 1 (0.00%) + [+] antlers - 1 (0.00%) + [+] antelope - 1 (0.00%) + [+] xxxv - 1 (0.00%) + +Using all default settings we were able to produce several high quality rules. The application displays some basic Top 10 rules and words statistics. All of the generated rules and words are saved using basename 'analysis' by default: + +* analysis.word - unsorted and ununiqued source words +* analysis-sorted.word - occurrence sorted and unique source words +* analysis.rule - unsorted and ununiqued rules +* analysis-sorted.rule - occurrence sorted and unique rules + +Notice that several passwords such as '#(4)\ and '&a123456 were skipped because they do not have sufficient characteristics to be processed. Other than alpha character count, the program will skip all numeric passwords and passwords containing non-ASCII characters. The latter is due to a bug in the Enchant engine which I hope to fix in the future thus allowing word processing of many languages. + +Specifying output basename +------------------------------ + +As previously mentioned `rulegen.py` saves output files using the 'analysis' basename by default. You can change file basename with the `--basename` or `-b` flag as follows: + + $ python rulegen.py korelogic.txt -q -b korelogic + [*] Using Enchant 'aspell' module. For best results please install + 'aspell' module language dictionaries. + [*] Analyzing passwords file: korelogic.txt: + [*] Press Ctrl-C to end execution and generate statistical analysis. + [*] Saving rules to korelogic.rule + [*] Saving words to korelogic.word + + +Debugging rules +-------------------- + +There may be situations where you run into issues generating rules for the Hashcat password cracker. `rulegen.py` includes the `--hashcat` flag to validate generated words and rules using hashcat itself running in --stdout mode. In order for this mode to work correctly, you must download the latest version of hashcat-cli and edit the `HASHCAT_PATH` variable in the source. For example, at the time of this writing I have placed the hashcat-0.## folder in the PACK directory and defined `HASHCAT_PATH` as 'hashcat-0.##/'. + +You can also observe the inner workings of the rule generation engine with the `--debug` flag. Don't worry about messages of certain rule failings, this is the result of the halting problem solver trying to find an optimal and valid solution. + +Conclusion +============== + +While this guide introduces a number of methods to analyze passwords, reverse rules and generate masks, there are a number of other tricks that are waiting for you to discover. I would be excited if you told me about some unusual use or suggestions for any of the covered tools. + +Happy Cracking! + + -Peter \ No newline at end of file diff --git a/PACK/enchant/__init__.py b/PACK/enchant/__init__.py new file mode 100644 index 0000000..46f7487 --- /dev/null +++ b/PACK/enchant/__init__.py @@ -0,0 +1,907 @@ +# pyenchant +# +# Copyright (C) 2004-2011, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPsE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" +enchant: Access to the enchant spellchecking library +===================================================== + +This module provides several classes for performing spell checking +via the Enchant spellchecking library. For more details on Enchant, +visit the project website: + + http://www.abisource.com/enchant/ + +Spellchecking is performed using 'Dict' objects, which represent +a language dictionary. Their use is best demonstrated by a quick +example:: + + >>> import enchant + >>> d = enchant.Dict("en_US") # create dictionary for US English + >>> d.check("enchant") + True + >>> d.check("enchnt") + False + >>> d.suggest("enchnt") + ['enchant', 'enchants', 'enchanter', 'penchant', 'incant', 'enchain', 'enchanted'] + +Languages are identified by standard string tags such as "en" (English) +and "fr" (French). Specific language dialects can be specified by +including an additional code - for example, "en_AU" refers to Australian +English. The later form is preferred as it is more widely supported. + +To check whether a dictionary exists for a given language, the function +'dict_exists' is available. Dictionaries may also be created using the +function 'request_dict'. + +A finer degree of control over the dictionaries and how they are created +can be obtained using one or more 'Broker' objects. These objects are +responsible for locating dictionaries for a specific language. + +In Python 2.x, unicode strings are supported transparently in the +standard manner - if a unicode string is given as an argument, the +result will be a unicode string. Note that Enchant works in UTF-8 +internally, so passing an ASCII string to a dictionary for a language +requiring Unicode may result in UTF-8 strings being returned. + +In Python 3.x unicode strings are expected throughout. Bytestrings +should not be passed into any functions. + +Errors that occur in this module are reported by raising subclasses +of 'Error'. + +""" +_DOC_ERRORS = ['enchnt', 'enchnt', 'fr'] + +# Make version info available +__ver_major__ = 1 +__ver_minor__ = 6 +__ver_patch__ = 6 +__ver_sub__ = "" +__version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__, + __ver_patch__, __ver_sub__) + +import os + +try: + from enchant import _enchant as _e +except ImportError: + if not os.environ.get("PYENCHANT_IGNORE_MISSING_LIB", False): + raise + _e = None + +from enchant.errors import * +from enchant.utils import EnchantStr, get_default_language +from enchant.pypwl import PyPWL + +# Due to the unfortunate name collision between the enchant "tokenize" module +# and the stdlib "tokenize" module, certain values of sys.path can cause +# the former to override the latter and break the "warnings" module. +# This hacks around it by making a dumming "warnings" module. +try: + import warnings +except ImportError: + class warnings(object): + def warn(self, *args, **kwds): + pass + + + warnings = warnings() + + +class ProviderDesc(object): + """Simple class describing an Enchant provider. + + Each provider has the following information associated with it: + + * name: Internal provider name (e.g. "aspell") + * desc: Human-readable description (e.g. "Aspell Provider") + * file: Location of the library containing the provider + + """ + _DOC_ERRORS = ["desc"] + + def __init__(self, name, desc, file): + self.name = name + self.desc = desc + self.file = file + + def __str__(self): + return "" % self.desc + + def __repr__(self): + return str(self) + + def __eq__(self, pd): + """Equality operator on ProviderDesc objects.""" + return (self.name == pd.name and \ + self.desc == pd.desc and \ + self.file == pd.file) + + def __hash__(self): + """Hash operator on ProviderDesc objects.""" + return hash(self.name + self.desc + self.file) + + +class _EnchantObject(object): + """Base class for enchant objects. + + This class implements some general functionality for interfacing with + the '_enchant' C-library in a consistent way. All public objects + from the 'enchant' module are subclasses of this class. + + All enchant objects have an attribute '_this' which contains the + pointer to the underlying C-library object. The method '_check_this' + can be called to ensure that this point is not None, raising an + exception if it is. + """ + + def __init__(self): + """_EnchantObject constructor.""" + self._this = None + # To be importable when enchant C lib is missing, we need + # to create a dummy default broker. + if _e is not None: + self._init_this() + + def _check_this(self, msg=None): + """Check that self._this is set to a pointer, rather than None.""" + if msg is None: + msg = "%s unusable: the underlying C-library object has been freed." + msg = msg % (self.__class__.__name__,) + if self._this is None: + raise Error(msg) + + def _init_this(self): + """Initialise the underlying C-library object pointer.""" + raise NotImplementedError + + def _raise_error(self, default="Unspecified Error", eclass=Error): + """Raise an exception based on available error messages. + + This method causes an Error to be raised. Subclasses should + override it to retrieve an error indication from the underlying + API if possible. If such a message cannot be retrieved, the + argument value is used. The class of the exception + can be specified using the argument + """ + raise eclass(default) + + _raise_error._DOC_ERRORS = ["eclass"] + + def __getstate__(self): + """Customize pickling of PyEnchant objects. + + Since it's not safe for multiple objects to share the same C-library + object, we make sure it's unset when pickling. + """ + state = self.__dict__.copy() + state["_this"] = None + return state + + def __setstate__(self, state): + self.__dict__.update(state) + self._init_this() + + +class Broker(_EnchantObject): + """Broker object for the Enchant spellchecker. + + Broker objects are responsible for locating and managing dictionaries. + Unless custom functionality is required, there is no need to use Broker + objects directly. The 'enchant' module provides a default broker object + so that 'Dict' objects can be created directly. + + The most important methods of this class include: + + * dict_exists: check existence of a specific language dictionary + * request_dict: obtain a dictionary for specific language + * set_ordering: specify which dictionaries to try for for a + given language. + + """ + + def __init__(self): + """Broker object constructor. + + This method is the constructor for the 'Broker' object. No + arguments are required. + """ + _EnchantObject.__init__(self) + + def _init_this(self): + self._this = _e.broker_init() + if not self._this: + raise Error("Could not initialise an enchant broker.") + + def __del__(self): + """Broker object destructor.""" + if _e is not None: + self._free() + + def _raise_error(self, default="Unspecified Error", eclass=Error): + """Overrides _EnchantObject._raise_error to check broker errors.""" + err = _e.broker_get_error(self._this) + if err == "" or err is None: + raise eclass(default) + raise eclass(err) + + def _free(self): + """Free system resource associated with a Broker object. + + This method can be called to free the underlying system resources + associated with a Broker object. It is called automatically when + the object is garbage collected. If called explicitly, the + Broker and any associated Dict objects must no longer be used. + """ + if self._this is not None: + _e.broker_free(self._this) + self._this = None + + def request_dict(self, tag=None): + """Request a Dict object for the language specified by . + + This method constructs and returns a Dict object for the + requested language. 'tag' should be a string of the appropriate + form for specifying a language, such as "fr" (French) or "en_AU" + (Australian English). The existence of a specific language can + be tested using the 'dict_exists' method. + + If is not given or is None, an attempt is made to determine + the current language in use. If this cannot be determined, Error + is raised. + + NOTE: this method is functionally equivalent to calling the Dict() + constructor and passing in the argument. + + """ + return Dict(tag, self) + + request_dict._DOC_ERRORS = ["fr"] + + def _request_dict_data(self, tag): + """Request raw C pointer data for a dictionary. + + This method call passes on the call to the C library, and does + some internal bookkeeping. + """ + self._check_this() + tag = EnchantStr(tag) + new_dict = _e.broker_request_dict(self._this, tag.encode()) + if new_dict is None: + eStr = "Dictionary for language '%s' could not be found" + self._raise_error(eStr % (tag,), DictNotFoundError) + return new_dict + + def request_pwl_dict(self, pwl): + """Request a Dict object for a personal word list. + + This method behaves as 'request_dict' but rather than returning + a dictionary for a specific language, it returns a dictionary + referencing a personal word list. A personal word list is a file + of custom dictionary entries, one word per line. + """ + self._check_this() + pwl = EnchantStr(pwl) + new_dict = _e.broker_request_pwl_dict(self._this, pwl.encode()) + if new_dict is None: + eStr = "Personal Word List file '%s' could not be loaded" + self._raise_error(eStr % (pwl,)) + d = Dict(False) + d._switch_this(new_dict, self) + return d + + def _free_dict(self, dict): + """Free memory associated with a dictionary. + + This method frees system resources associated with a Dict object. + It is equivalent to calling the object's 'free' method. Once this + method has been called on a dictionary, it must not be used again. + """ + self._check_this() + _e.broker_free_dict(self._this, dict._this) + dict._this = None + dict._broker = None + + def dict_exists(self, tag): + """Check availability of a dictionary. + + This method checks whether there is a dictionary available for + the language specified by 'tag'. It returns True if a dictionary + is available, and False otherwise. + """ + self._check_this() + tag = EnchantStr(tag) + val = _e.broker_dict_exists(self._this, tag.encode()) + return bool(val) + + def set_ordering(self, tag, ordering): + """Set dictionary preferences for a language. + + The Enchant library supports the use of multiple dictionary programs + and multiple languages. This method specifies which dictionaries + the broker should prefer when dealing with a given language. 'tag' + must be an appropriate language specification and 'ordering' is a + string listing the dictionaries in order of preference. For example + a valid ordering might be "aspell,myspell,ispell". + The value of 'tag' can also be set to "*" to set a default ordering + for all languages for which one has not been set explicitly. + """ + self._check_this() + tag = EnchantStr(tag) + ordering = EnchantStr(ordering) + _e.broker_set_ordering(self._this, tag.encode(), ordering.encode()) + + def describe(self): + """Return list of provider descriptions. + + This method returns a list of descriptions of each of the + dictionary providers available. Each entry in the list is a + ProviderDesc object. + """ + self._check_this() + self.__describe_result = [] + _e.broker_describe(self._this, self.__describe_callback) + return [ProviderDesc(*r) for r in self.__describe_result] + + def __describe_callback(self, name, desc, file): + """Collector callback for dictionary description. + + This method is used as a callback into the _enchant function + 'enchant_broker_describe'. It collects the given arguments in + a tuple and appends them to the list '__describe_result'. + """ + s = EnchantStr("") + name = s.decode(name) + desc = s.decode(desc) + file = s.decode(file) + self.__describe_result.append((name, desc, file)) + + def list_dicts(self): + """Return list of available dictionaries. + + This method returns a list of dictionaries available to the + broker. Each entry in the list is a two-tuple of the form: + + (tag,provider) + + where is the language lag for the dictionary and + is a ProviderDesc object describing the provider + through which that dictionary can be obtained. + """ + self._check_this() + self.__list_dicts_result = [] + _e.broker_list_dicts(self._this, self.__list_dicts_callback) + return [(r[0], ProviderDesc(*r[1])) for r in self.__list_dicts_result] + + def __list_dicts_callback(self, tag, name, desc, file): + """Collector callback for listing dictionaries. + + This method is used as a callback into the _enchant function + 'enchant_broker_list_dicts'. It collects the given arguments into + an appropriate tuple and appends them to '__list_dicts_result'. + """ + s = EnchantStr("") + tag = s.decode(tag) + name = s.decode(name) + desc = s.decode(desc) + file = s.decode(file) + self.__list_dicts_result.append((tag, (name, desc, file))) + + def list_languages(self): + """List languages for which dictionaries are available. + + This function returns a list of language tags for which a + dictionary is available. + """ + langs = [] + for (tag, prov) in self.list_dicts(): + if tag not in langs: + langs.append(tag) + return langs + + def __describe_dict(self, dict_data): + """Get the description tuple for a dict data object. + must be a C-library pointer to an enchant dictionary. + The return value is a tuple of the form: + (,,,) + """ + # Define local callback function + cb_result = [] + + def cb_func(tag, name, desc, file): + s = EnchantStr("") + tag = s.decode(tag) + name = s.decode(name) + desc = s.decode(desc) + file = s.decode(file) + cb_result.append((tag, name, desc, file)) + + # Actually call the describer function + _e.dict_describe(dict_data, cb_func) + return cb_result[0] + + __describe_dict._DOC_ERRORS = ["desc"] + + def get_param(self, name): + """Get the value of a named parameter on this broker. + + Parameters are used to provide runtime information to individual + provider backends. See the method 'set_param' for more details. + """ + name = EnchantStr(name) + return name.decode(_e.broker_get_param(self._this, name.encode())) + + get_param._DOC_ERRORS = ["param"] + + def set_param(self, name, value): + """Set the value of a named parameter on this broker. + + Parameters are used to provide runtime information to individual + provider backends. For example, the myspell provider will search + any directories given in the "enchant.myspell.dictionary.path" + parameter when looking for its dictionary files. + """ + name = EnchantStr(name) + value = EnchantStr(value) + _e.broker_set_param(self._this, name.encode(), value.encode()) + + +class Dict(_EnchantObject): + """Dictionary object for the Enchant spellchecker. + + Dictionary objects are responsible for checking the spelling of words + and suggesting possible corrections. Each dictionary is owned by a + Broker object, but unless a new Broker has explicitly been created + then this will be the 'enchant' module default Broker and is of little + interest. + + The important methods of this class include: + + * check(): check whether a word id spelled correctly + * suggest(): suggest correct spellings for a word + * add(): add a word to the user's personal dictionary + * remove(): add a word to the user's personal exclude list + * add_to_session(): add a word to the current spellcheck session + * store_replacement(): indicate a replacement for a given word + + Information about the dictionary is available using the following + attributes: + + * tag: the language tag of the dictionary + * provider: a ProviderDesc object for the dictionary provider + + """ + + def __init__(self, tag=None, broker=None): + """Dict object constructor. + + A dictionary belongs to a specific language, identified by the + string . If the tag is not given or is None, an attempt to + determine the language currently in use is made using the 'locale' + module. If the current language cannot be determined, Error is raised. + + If is instead given the value of False, a 'dead' Dict object + is created without any reference to a language. This is typically + only useful within PyEnchant itself. Any other non-string value + for raises Error. + + Each dictionary must also have an associated Broker object which + obtains the dictionary information from the underlying system. This + may be specified using . If not given, the default broker + is used. + """ + # Initialise misc object attributes to None + self.provider = None + # If no tag was given, use the default language + if tag is None: + tag = get_default_language() + if tag is None: + err = "No tag specified and default language could not " + err = err + "be determined." + raise Error(err) + self.tag = tag + # If no broker was given, use the default broker + if broker is None: + broker = _broker + self._broker = broker + # Now let the superclass initialise the C-library object + _EnchantObject.__init__(self) + + def _init_this(self): + # Create dead object if False was given. + # Otherwise, use the broker to get C-library pointer data. + self._this = None + if self.tag: + this = self._broker._request_dict_data(self.tag) + self._switch_this(this, self._broker) + + def __del__(self): + """Dict object destructor.""" + # Calling free() might fail if python is shutting down + try: + self._free() + except AttributeError: + pass + + def _switch_this(self, this, broker): + """Switch the underlying C-library pointer for this object. + + As all useful state for a Dict is stored by the underlying C-library + pointer, it is very convenient to allow this to be switched at + run-time. Pass a new dict data object into this method to affect + the necessary changes. The creating Broker object (at the Python + level) must also be provided. + + This should *never* *ever* be used by application code. It's + a convenience for developers only, replacing the clunkier + parameter to __init__ from earlier versions. + """ + # Free old dict data + Dict._free(self) + # Hook in the new stuff + self._this = this + self._broker = broker + # Update object properties + desc = self.__describe(check_this=False) + self.tag = desc[0] + self.provider = ProviderDesc(*desc[1:]) + + _switch_this._DOC_ERRORS = ["init"] + + def _check_this(self, msg=None): + """Extend _EnchantObject._check_this() to check Broker validity. + + It is possible for the managing Broker object to be freed without + freeing the Dict. Thus validity checking must take into account + self._broker._this as well as self._this. + """ + if self._broker is None or self._broker._this is None: + self._this = None + _EnchantObject._check_this(self, msg) + + def _raise_error(self, default="Unspecified Error", eclass=Error): + """Overrides _EnchantObject._raise_error to check dict errors.""" + err = _e.dict_get_error(self._this) + if err == "" or err is None: + raise eclass(default) + raise eclass(err) + + def _free(self): + """Free the system resources associated with a Dict object. + + This method frees underlying system resources for a Dict object. + Once it has been called, the Dict object must no longer be used. + It is called automatically when the object is garbage collected. + """ + if self._broker is not None and self._this is not None: + self._broker._free_dict(self) + + def check(self, word): + """Check spelling of a word. + + This method takes a word in the dictionary language and returns + True if it is correctly spelled, and false otherwise. + """ + self._check_this() + word = EnchantStr(word) + val = _e.dict_check(self._this, word.encode()) + if val == 0: + return True + if val > 0: + return False + self._raise_error() + + def suggest(self, word): + """Suggest possible spellings for a word. + + This method tries to guess the correct spelling for a given + word, returning the possibilities in a list. + """ + self._check_this() + word = EnchantStr(word) + suggs = _e.dict_suggest(self._this, word.encode()) + return [word.decode(w) for w in suggs] + + def add(self, word): + """Add a word to the user's personal word list.""" + self._check_this() + word = EnchantStr(word) + _e.dict_add(self._this, word.encode()) + + def remove(self, word): + """Add a word to the user's personal exclude list.""" + self._check_this() + word = EnchantStr(word) + _e.dict_remove(self._this, word.encode()) + + def add_to_pwl(self, word): + """Add a word to the user's personal word list.""" + warnings.warn("Dict.add_to_pwl is deprecated, please use Dict.add", + category=DeprecationWarning, stacklevel=2) + self._check_this() + word = EnchantStr(word) + _e.dict_add_to_pwl(self._this, word.encode()) + + def add_to_session(self, word): + """Add a word to the session personal list.""" + self._check_this() + word = EnchantStr(word) + _e.dict_add_to_session(self._this, word.encode()) + + def remove_from_session(self, word): + """Add a word to the session exclude list.""" + self._check_this() + word = EnchantStr(word) + _e.dict_remove_from_session(self._this, word.encode()) + + def is_added(self, word): + """Check whether a word is in the personal word list.""" + self._check_this() + word = EnchantStr(word) + return _e.dict_is_added(self._this, word.encode()) + + def is_removed(self, word): + """Check whether a word is in the personal exclude list.""" + self._check_this() + word = EnchantStr(word) + return _e.dict_is_removed(self._this, word.encode()) + + def is_in_session(self, word): + """Check whether a word is in the session list.""" + warnings.warn("Dict.is_in_session is deprecated, " \ + "please use Dict.is_added", + category=DeprecationWarning, stacklevel=2) + self._check_this() + word = EnchantStr(word) + return _e.dict_is_in_session(self._this, word.encode()) + + def store_replacement(self, mis, cor): + """Store a replacement spelling for a miss-spelled word. + + This method makes a suggestion to the spellchecking engine that the + miss-spelled word is in fact correctly spelled as . Such + a suggestion will typically mean that appears early in the + list of suggested spellings offered for later instances of . + """ + if not mis: + raise ValueError("can't store replacement for an empty string") + if not cor: + raise ValueError("can't store empty string as a replacement") + self._check_this() + mis = EnchantStr(mis) + cor = EnchantStr(cor) + _e.dict_store_replacement(self._this, mis.encode(), cor.encode()) + + store_replacement._DOC_ERRORS = ["mis", "mis"] + + def __describe(self, check_this=True): + """Return a tuple describing the dictionary. + + This method returns a four-element tuple describing the underlying + spellchecker system providing the dictionary. It will contain the + following strings: + + * language tag + * name of dictionary provider + * description of dictionary provider + * dictionary file + + Direct use of this method is not recommended - instead, access this + information through the 'tag' and 'provider' attributes. + """ + if check_this: + self._check_this() + _e.dict_describe(self._this, self.__describe_callback) + return self.__describe_result + + def __describe_callback(self, tag, name, desc, file): + """Collector callback for dictionary description. + + This method is used as a callback into the _enchant function + 'enchant_dict_describe'. It collects the given arguments in + a tuple and stores them in the attribute '__describe_result'. + """ + s = EnchantStr("") + tag = s.decode(tag) + name = s.decode(name) + desc = s.decode(desc) + file = s.decode(file) + self.__describe_result = (tag, name, desc, file) + + +class DictWithPWL(Dict): + """Dictionary with separately-managed personal word list. + + NOTE: As of version 1.4.0, enchant manages a per-user pwl and + exclude list. This class is now only needed if you want + to explicitly maintain a separate word list in addition to + the default one. + + This class behaves as the standard Dict class, but also manages a + personal word list stored in a separate file. The file must be + specified at creation time by the 'pwl' argument to the constructor. + Words added to the dictionary are automatically appended to the pwl file. + + A personal exclude list can also be managed, by passing another filename + to the constructor in the optional 'pel' argument. If this is not given, + requests to exclude words are ignored. + + If either 'pwl' or 'pel' are None, an in-memory word list is used. + This will prevent calls to add() and remove() from affecting the user's + default word lists. + + The Dict object managing the PWL is available as the 'pwl' attribute. + The Dict object managing the PEL is available as the 'pel' attribute. + + To create a DictWithPWL from the user's default language, use None + as the 'tag' argument. + """ + _DOC_ERRORS = ["pel", "pel", "PEL", "pel"] + + def __init__(self, tag, pwl=None, pel=None, broker=None): + """DictWithPWL constructor. + + The argument 'pwl', if not None, names a file containing the + personal word list. If this file does not exist, it is created + with default permissions. + + The argument 'pel', if not None, names a file containing the personal + exclude list. If this file does not exist, it is created with + default permissions. + """ + Dict.__init__(self, tag, broker) + if pwl is not None: + if not os.path.exists(pwl): + f = open(pwl, "wt") + f.close() + del f + self.pwl = self._broker.request_pwl_dict(pwl) + else: + self.pwl = PyPWL() + if pel is not None: + if not os.path.exists(pel): + f = open(pel, "wt") + f.close() + del f + self.pel = self._broker.request_pwl_dict(pel) + else: + self.pel = PyPWL() + + def _check_this(self, msg=None): + """Extend Dict._check_this() to check PWL validity.""" + if self.pwl is None: + self._free() + if self.pel is None: + self._free() + Dict._check_this(self, msg) + self.pwl._check_this(msg) + self.pel._check_this(msg) + + def _free(self): + """Extend Dict._free() to free the PWL as well.""" + if self.pwl is not None: + self.pwl._free() + self.pwl = None + if self.pel is not None: + self.pel._free() + self.pel = None + Dict._free(self) + + def check(self, word): + """Check spelling of a word. + + This method takes a word in the dictionary language and returns + True if it is correctly spelled, and false otherwise. It checks + both the dictionary and the personal word list. + """ + if self.pel.check(word): + return False + if self.pwl.check(word): + return True + if Dict.check(self, word): + return True + return False + + def suggest(self, word): + """Suggest possible spellings for a word. + + This method tries to guess the correct spelling for a given + word, returning the possibilities in a list. + """ + suggs = Dict.suggest(self, word) + suggs.extend([w for w in self.pwl.suggest(word) if w not in suggs]) + for i in range(len(suggs) - 1, -1, -1): + if self.pel.check(suggs[i]): + del suggs[i] + return suggs + + def add(self, word): + """Add a word to the associated personal word list. + + This method adds the given word to the personal word list, and + automatically saves the list to disk. + """ + self._check_this() + self.pwl.add(word) + self.pel.remove(word) + + def remove(self, word): + """Add a word to the associated exclude list.""" + self._check_this() + self.pwl.remove(word) + self.pel.add(word) + + def add_to_pwl(self, word): + """Add a word to the associated personal word list. + + This method adds the given word to the personal word list, and + automatically saves the list to disk. + """ + self._check_this() + self.pwl.add_to_pwl(word) + self.pel.remove(word) + + def is_added(self, word): + """Check whether a word is in the personal word list.""" + self._check_this() + return self.pwl.is_added(word) + + def is_removed(self, word): + """Check whether a word is in the personal exclude list.""" + self._check_this() + return self.pel.is_added(word) + + +## Create a module-level default broker object, and make its important +## methods available at the module level. +_broker = Broker() +request_dict = _broker.request_dict +request_pwl_dict = _broker.request_pwl_dict +dict_exists = _broker.dict_exists +list_dicts = _broker.list_dicts +list_languages = _broker.list_languages +get_param = _broker.get_param +set_param = _broker.set_param + + +# Expose the "get_version" function. +def get_enchant_version(): + """Get the version string for the underlying enchant library.""" + return _e.get_version() + + +# Run unit tests when called from comand-line +if __name__ == "__main__": + import sys + import enchant.tests + + res = enchant.tests.runtestsuite() + if len(res.errors) > 0 or len(res.failures) > 0: + sys.exit(1) + sys.exit(0) diff --git a/PACK/enchant/_enchant.py b/PACK/enchant/_enchant.py new file mode 100644 index 0000000..3871bbd --- /dev/null +++ b/PACK/enchant/_enchant.py @@ -0,0 +1,366 @@ +# pyenchant +# +# Copyright (C) 2004-2008, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPsE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + + enchant._enchant: ctypes-based wrapper for enchant C library + + This module implements the low-level interface to the underlying + C library for enchant. The interface is based on ctypes and tries + to do as little as possible while making the higher-level components + easier to write. + + The following conveniences are provided that differ from the underlying + C API: + + * the "enchant" prefix has been removed from all functions, since + python has a proper module system + * callback functions do not take a user_data argument, since + python has proper closures that can manage this internally + * string lengths are not passed into functions such as dict_check, + since python strings know how long they are + +""" + +import sys, os, os.path +from ctypes import * +from ctypes.util import find_library + +from enchant import utils +from enchant.errors import * +from enchant.utils import unicode + +# Locate and load the enchant dll. +# We've got several options based on the host platform. + +e = None + + +def _e_path_possibilities(): + """Generator yielding possible locations of the enchant library.""" + yield os.environ.get("PYENCHANT_LIBRARY_PATH") + yield find_library("enchant") + yield find_library("libenchant") + yield find_library("libenchant-1") + if sys.platform == 'darwin': + # enchant lib installed by macports + yield "/opt/local/lib/libenchant.dylib" + + +# On win32 we ship a bundled version of the enchant DLLs. +# Use them if they're present. +if sys.platform == "win32": + e_path = None + try: + e_path = utils.get_resource_filename("libenchant.dll") + except (Error, ImportError): + try: + e_path = utils.get_resource_filename("libenchant-1.dll") + except (Error, ImportError): + pass + if e_path is not None: + # We need to use LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH so + # that we don't accidentally suck in other versions of e.g. glib. + if not isinstance(e_path, unicode): + e_path = unicode(e_path, sys.getfilesystemencoding()) + LoadLibraryEx = windll.kernel32.LoadLibraryExW + LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 + e_handle = LoadLibraryEx(e_path, None, LOAD_WITH_ALTERED_SEARCH_PATH) + if not e_handle: + raise WinError() + e = CDLL(e_path, handle=e_handle) + +# On darwin we ship a bundled version of the enchant DLLs. +# Use them if they're present. +if e is None and sys.platform == "darwin": + try: + e_path = utils.get_resource_filename("lib/libenchant.1.dylib") + except (Error, ImportError): + pass + else: + # Enchant doesn't natively support relocatable binaries on OSX. + # We fake it by patching the enchant source to expose a char**, which + # we can write the runtime path into ourelves. + e = CDLL(e_path) + try: + e_dir = os.path.dirname(os.path.dirname(e_path)) + prefix_dir = POINTER(c_char_p).in_dll(e, "enchant_prefix_dir_p") + prefix_dir.contents = c_char_p(e_dir) + except AttributeError: + e = None + +# Not found yet, search various standard system locations. +if e is None: + for e_path in _e_path_possibilities(): + if e_path is not None: + try: + e = cdll.LoadLibrary(e_path) + except OSError: + pass + else: + break + +# No usable enchant install was found :-( +if e is None: + raise ImportError("enchant C library not found") + + +# Define various callback function types + +def CALLBACK(restype, *argtypes): + """Factory for generating callback function prototypes. + + This is factored into a factory so I can easily change the definition + for experimentation or debugging. + """ + return CFUNCTYPE(restype, *argtypes) + + +t_broker_desc_func = CALLBACK(None, c_char_p, c_char_p, c_char_p, c_void_p) +t_dict_desc_func = CALLBACK(None, c_char_p, c_char_p, c_char_p, c_char_p, c_void_p) + +# Simple typedefs for readability + +t_broker = c_void_p +t_dict = c_void_p + +# Now we can define the types of each function we are going to use + +broker_init = e.enchant_broker_init +broker_init.argtypes = [] +broker_init.restype = t_broker + +broker_free = e.enchant_broker_free +broker_free.argtypes = [t_broker] +broker_free.restype = None + +broker_request_dict = e.enchant_broker_request_dict +broker_request_dict.argtypes = [t_broker, c_char_p] +broker_request_dict.restype = t_dict + +broker_request_pwl_dict = e.enchant_broker_request_pwl_dict +broker_request_pwl_dict.argtypes = [t_broker, c_char_p] +broker_request_pwl_dict.restype = t_dict + +broker_free_dict = e.enchant_broker_free_dict +broker_free_dict.argtypes = [t_broker, t_dict] +broker_free_dict.restype = None + +broker_dict_exists = e.enchant_broker_dict_exists +broker_dict_exists.argtypes = [t_broker, c_char_p] +broker_free_dict.restype = c_int + +broker_set_ordering = e.enchant_broker_set_ordering +broker_set_ordering.argtypes = [t_broker, c_char_p, c_char_p] +broker_set_ordering.restype = None + +broker_get_error = e.enchant_broker_get_error +broker_get_error.argtypes = [t_broker] +broker_get_error.restype = c_char_p + +broker_describe1 = e.enchant_broker_describe +broker_describe1.argtypes = [t_broker, t_broker_desc_func, c_void_p] +broker_describe1.restype = None + + +def broker_describe(broker, cbfunc): + def cbfunc1(*args): + cbfunc(*args[:-1]) + + broker_describe1(broker, t_broker_desc_func(cbfunc1), None) + + +broker_list_dicts1 = e.enchant_broker_list_dicts +broker_list_dicts1.argtypes = [t_broker, t_dict_desc_func, c_void_p] +broker_list_dicts1.restype = None + + +def broker_list_dicts(broker, cbfunc): + def cbfunc1(*args): + cbfunc(*args[:-1]) + + broker_list_dicts1(broker, t_dict_desc_func(cbfunc1), None) + + +try: + broker_get_param = e.enchant_broker_get_param +except AttributeError: + # Make the lookup error occur at runtime + def broker_get_param(broker, param_name): + return e.enchant_broker_get_param(param_name) +else: + broker_get_param.argtypes = [t_broker, c_char_p] + broker_get_param.restype = c_char_p + +try: + broker_set_param = e.enchant_broker_set_param +except AttributeError: + # Make the lookup error occur at runtime + def broker_set_param(broker, param_name): + return e.enchant_broker_set_param(param_name) +else: + broker_set_param.argtypes = [t_broker, c_char_p, c_char_p] + broker_set_param.restype = None + +try: + get_version = e.enchant_get_version +except AttributeError: + # Make the lookup error occur at runtime + def get_version(): + return e.enchant_get_version() +else: + get_version.argtypes = [] + get_version.restype = c_char_p + +dict_check1 = e.enchant_dict_check +dict_check1.argtypes = [t_dict, c_char_p, c_size_t] +dict_check1.restype = c_int + + +def dict_check(dict, word): + return dict_check1(dict, word, len(word)) + + +dict_suggest1 = e.enchant_dict_suggest +dict_suggest1.argtypes = [t_dict, c_char_p, c_size_t, POINTER(c_size_t)] +dict_suggest1.restype = POINTER(c_char_p) + + +def dict_suggest(dict, word): + numSuggsP = pointer(c_size_t(0)) + suggs_c = dict_suggest1(dict, word, len(word), numSuggsP) + suggs = [] + n = 0 + while n < numSuggsP.contents.value: + suggs.append(suggs_c[n]) + n = n + 1 + if numSuggsP.contents.value > 0: + dict_free_string_list(dict, suggs_c) + return suggs + + +dict_add1 = e.enchant_dict_add +dict_add1.argtypes = [t_dict, c_char_p, c_size_t] +dict_add1.restype = None + + +def dict_add(dict, word): + return dict_add1(dict, word, len(word)) + + +dict_add_to_pwl1 = e.enchant_dict_add +dict_add_to_pwl1.argtypes = [t_dict, c_char_p, c_size_t] +dict_add_to_pwl1.restype = None + + +def dict_add_to_pwl(dict, word): + return dict_add_to_pwl1(dict, word, len(word)) + + +dict_add_to_session1 = e.enchant_dict_add_to_session +dict_add_to_session1.argtypes = [t_dict, c_char_p, c_size_t] +dict_add_to_session1.restype = None + + +def dict_add_to_session(dict, word): + return dict_add_to_session1(dict, word, len(word)) + + +dict_remove1 = e.enchant_dict_remove +dict_remove1.argtypes = [t_dict, c_char_p, c_size_t] +dict_remove1.restype = None + + +def dict_remove(dict, word): + return dict_remove1(dict, word, len(word)) + + +dict_remove_from_session1 = e.enchant_dict_remove_from_session +dict_remove_from_session1.argtypes = [t_dict, c_char_p, c_size_t] +dict_remove_from_session1.restype = c_int + + +def dict_remove_from_session(dict, word): + return dict_remove_from_session1(dict, word, len(word)) + + +dict_is_added1 = e.enchant_dict_is_added +dict_is_added1.argtypes = [t_dict, c_char_p, c_size_t] +dict_is_added1.restype = c_int + + +def dict_is_added(dict, word): + return dict_is_added1(dict, word, len(word)) + + +dict_is_removed1 = e.enchant_dict_is_removed +dict_is_removed1.argtypes = [t_dict, c_char_p, c_size_t] +dict_is_removed1.restype = c_int + + +def dict_is_removed(dict, word): + return dict_is_removed1(dict, word, len(word)) + + +dict_is_in_session1 = e.enchant_dict_is_in_session +dict_is_in_session1.argtypes = [t_dict, c_char_p, c_size_t] +dict_is_in_session1.restype = c_int + + +def dict_is_in_session(dict, word): + return dict_is_in_session1(dict, word, len(word)) + + +dict_store_replacement1 = e.enchant_dict_store_replacement +dict_store_replacement1.argtypes = [t_dict, c_char_p, c_size_t, c_char_p, c_size_t] +dict_store_replacement1.restype = None + + +def dict_store_replacement(dict, mis, cor): + return dict_store_replacement1(dict, mis, len(mis), cor, len(cor)) + + +dict_free_string_list = e.enchant_dict_free_string_list +dict_free_string_list.argtypes = [t_dict, POINTER(c_char_p)] +dict_free_string_list.restype = None + +dict_get_error = e.enchant_dict_get_error +dict_get_error.argtypes = [t_dict] +dict_get_error.restype = c_char_p + +dict_describe1 = e.enchant_dict_describe +dict_describe1.argtypes = [t_dict, t_dict_desc_func, c_void_p] +dict_describe1.restype = None + + +def dict_describe(dict, cbfunc): + def cbfunc1(tag, name, desc, file, data): + cbfunc(tag, name, desc, file) + + dict_describe1(dict, t_dict_desc_func(cbfunc1), None) diff --git a/PACK/enchant/checker/CmdLineChecker.py b/PACK/enchant/checker/CmdLineChecker.py new file mode 100644 index 0000000..fd3be0b --- /dev/null +++ b/PACK/enchant/checker/CmdLineChecker.py @@ -0,0 +1,203 @@ +# pyenchant +# +# Copyright (C) 2004-2008, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + + enchant.checker.CmdLineChecker: Command-Line spell checker + + This module provides the class CmdLineChecker, which interactively + spellchecks a piece of text by interacting with the user on the + command line. It can also be run as a script to spellcheck a file. + +""" + +import sys + +from enchant.checker import SpellChecker +from enchant.utils import printf + + +class CmdLineChecker: + """A simple command-line spell checker. + + This class implements a simple command-line spell checker. It must + be given a SpellChecker instance to operate on, and interacts with + the user by printing instructions on stdout and reading commands from + stdin. + """ + _DOC_ERRORS = ["stdout", "stdin"] + + def __init__(self): + self._stop = False + self._checker = None + + def set_checker(self, chkr): + self._checker = chkr + + def get_checker(self, chkr): + return self._checker + + def run(self): + """Run the spellchecking loop.""" + self._stop = False + for err in self._checker: + self.error = err + printf(["ERROR:", err.word]) + printf(["HOW ABOUT:", err.suggest()]) + status = self.read_command() + while not status and not self._stop: + status = self.read_command() + if self._stop: + break + printf(["DONE"]) + + def print_help(self): + printf(["0..N: replace with the numbered suggestion"]) + printf(["R0..rN: always replace with the numbered suggestion"]) + printf(["i: ignore this word"]) + printf(["I: always ignore this word"]) + printf(["a: add word to personal dictionary"]) + printf(["e: edit the word"]) + printf(["q: quit checking"]) + printf(["h: print this help message"]) + printf(["----------------------------------------------------"]) + printf(["HOW ABOUT:", self.error.suggest()]) + + def read_command(self): + cmd = raw_input(">> ") + cmd = cmd.strip() + + if cmd.isdigit(): + repl = int(cmd) + suggs = self.error.suggest() + if repl >= len(suggs): + printf(["No suggestion number", repl]) + return False + printf(["Replacing '%s' with '%s'" % (self.error.word, suggs[repl])]) + self.error.replace(suggs[repl]) + return True + + if cmd[0] == "R": + if not cmd[1:].isdigit(): + printf(["Badly formatted command (try 'help')"]) + return False + repl = int(cmd[1:]) + suggs = self.error.suggest() + if repl >= len(suggs): + printf(["No suggestion number", repl]) + return False + self.error.replace_always(suggs[repl]) + return True + + if cmd == "i": + return True + + if cmd == "I": + self.error.ignore_always() + return True + + if cmd == "a": + self.error.add() + return True + + if cmd == "e": + repl = raw_input("New Word: ") + self.error.replace(repl.strip()) + return True + + if cmd == "q": + self._stop = True + return True + + if "help".startswith(cmd.lower()): + self.print_help() + return False + + printf(["Badly formatted command (try 'help')"]) + return False + + def run_on_file(self, infile, outfile=None, enc=None): + """Run spellchecking on the named file. + This method can be used to run the spellchecker over the named file. + If is not given, the corrected contents replace the contents + of . If is given, the corrected contents will be + written to that file. Use "-" to have the contents written to stdout. + If is given, it specifies the encoding used to read the + file's contents into a unicode string. The output will be written + in the same encoding. + """ + inStr = "".join(file(infile, "r").readlines()) + if enc is not None: + inStr = inStr.decode(enc) + self._checker.set_text(inStr) + self.run() + outStr = self._checker.get_text() + if enc is not None: + outStr = outStr.encode(enc) + if outfile is None: + outF = file(infile, "w") + elif outfile == "-": + outF = sys.stdout + else: + outF = file(outfile, "w") + outF.write(outStr) + outF.close() + + run_on_file._DOC_ERRORS = ["outfile", "infile", "outfile", "stdout"] + + +def _run_as_script(): + """Run the command-line spellchecker as a script. + This function allows the spellchecker to be invoked from the command-line + to check spelling in a file. + """ + # Check necessary command-line options + from optparse import OptionParser + op = OptionParser() + op.add_option("-o", "--output", dest="outfile", metavar="FILE", + help="write changes into FILE") + op.add_option("-l", "--lang", dest="lang", metavar="TAG", default="en_US", + help="use language idenfified by TAG") + op.add_option("-e", "--encoding", dest="enc", metavar="ENC", + help="file is unicode with encoding ENC") + (opts, args) = op.parse_args() + # Sanity check + if len(args) < 1: + raise ValueError("Must name a file to check") + if len(args) > 1: + raise ValueError("Can only check a single file") + # Create and run the checker + chkr = SpellChecker(opts.lang) + cmdln = CmdLineChecker() + cmdln.set_checker(chkr) + cmdln.run_on_file(args[0], opts.outfile, opts.enc) + + +if __name__ == "__main__": + _run_as_script() diff --git a/PACK/enchant/checker/GtkSpellCheckerDialog.py b/PACK/enchant/checker/GtkSpellCheckerDialog.py new file mode 100644 index 0000000..0d8f69a --- /dev/null +++ b/PACK/enchant/checker/GtkSpellCheckerDialog.py @@ -0,0 +1,304 @@ +# GtkSpellCheckerDialog for pyenchant +# +# Copyright (C) 2004-2005, Fredrik Corneliusson +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# + +import gtk +import gobject + +from enchant.utils import printf, unicode + +# columns +COLUMN_SUGGESTION = 0 + + +def create_list_view(col_label, ): + # create list widget + list_ = gtk.ListStore(str) + list_view = gtk.TreeView(model=list_) + + list_view.set_rules_hint(True) + list_view.get_selection().set_mode(gtk.SELECTION_SINGLE) + # Add Colums + renderer = gtk.CellRendererText() + renderer.set_data("column", COLUMN_SUGGESTION) + column = gtk.TreeViewColumn(col_label, renderer, text=COLUMN_SUGGESTION) + list_view.append_column(column) + return list_view + + +class GtkSpellCheckerDialog(gtk.Window): + def __init__(self, *args, **kwargs): + gtk.Window.__init__(self, *args, **kwargs) + self.set_title('Spell check') + self.set_default_size(350, 200) + + self._checker = None + self._numContext = 40 + + self.errors = None + + # create accel group + accel_group = gtk.AccelGroup() + self.add_accel_group(accel_group) + + # list of widgets to disable if there's no spell error left + self._conditional_widgets = [] + conditional = self._conditional_widgets.append + + # layout + mainbox = gtk.VBox(spacing=5) + hbox = gtk.HBox(spacing=5) + self.add(mainbox) + mainbox.pack_start(hbox, padding=5) + + box1 = gtk.VBox(spacing=5) + hbox.pack_start(box1, padding=5) + conditional(box1) + + # unreconized word + text_view_lable = gtk.Label('Unreconized word') + text_view_lable.set_justify(gtk.JUSTIFY_LEFT) + box1.pack_start(text_view_lable, False, False) + + text_view = gtk.TextView() + text_view.set_wrap_mode(gtk.WRAP_WORD) + text_view.set_editable(False) + text_view.set_cursor_visible(False) + self.error_text = text_view.get_buffer() + text_buffer = text_view.get_buffer() + text_buffer.create_tag("fg_black", foreground="black") + text_buffer.create_tag("fg_red", foreground="red") + + box1.pack_start(text_view) + + # Change to + change_to_box = gtk.HBox() + box1.pack_start(change_to_box, False, False) + + change_to_label = gtk.Label('Change to:') + self.replace_text = gtk.Entry() + text_view_lable.set_justify(gtk.JUSTIFY_LEFT) + change_to_box.pack_start(change_to_label, False, False) + change_to_box.pack_start(self.replace_text) + + # scrolled window + sw = gtk.ScrolledWindow() + sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + box1.pack_start(sw) + + self.suggestion_list_view = create_list_view('Suggestions') + self.suggestion_list_view.connect("button_press_event", self._onButtonPress) + self.suggestion_list_view.connect("cursor-changed", self._onSuggestionChanged) + sw.add(self.suggestion_list_view) + + # ---Buttons---#000000#FFFFFF---------------------------------------------------- + button_box = gtk.VButtonBox() + hbox.pack_start(button_box, False, False) + + # Ignore + button = gtk.Button("Ignore") + button.connect("clicked", self._onIgnore) + button.add_accelerator("activate", accel_group, + gtk.keysyms.Return, 0, gtk.ACCEL_VISIBLE) + button_box.pack_start(button) + conditional(button) + + # Ignore all + button = gtk.Button("Ignore All") + button.connect("clicked", self._onIgnoreAll) + button_box.pack_start(button) + conditional(button) + + # Replace + button = gtk.Button("Replace") + button.connect("clicked", self._onReplace) + button_box.pack_start(button) + conditional(button) + + # Replace all + button = gtk.Button("Replace All") + button.connect("clicked", self._onReplaceAll) + button_box.pack_start(button) + conditional(button) + + # Recheck button + button = gtk.Button("_Add") + button.connect("clicked", self._onAdd) + + button_box.pack_start(button) + conditional(button) + + # Close button + button = gtk.Button(stock=gtk.STOCK_CLOSE) + button.connect("clicked", self._onClose) + button.add_accelerator("activate", accel_group, + gtk.keysyms.Escape, 0, gtk.ACCEL_VISIBLE) + button_box.pack_end(button) + + # dictionary label + self._dict_lable = gtk.Label('') + mainbox.pack_start(self._dict_lable, False, False, padding=5) + + mainbox.show_all() + + def _onIgnore(self, w, *args): + printf(["ignore"]) + self._advance() + + def _onIgnoreAll(self, w, *args): + printf(["ignore all"]) + self._checker.ignore_always() + self._advance() + + def _onReplace(self, *args): + printf(["Replace"]) + repl = self._getRepl() + self._checker.replace(repl) + self._advance() + + def _onReplaceAll(self, *args): + printf(["Replace all"]) + repl = self._getRepl() + self._checker.replace_always(repl) + self._advance() + + def _onAdd(self, *args): + """Callback for the "add" button.""" + self._checker.add() + self._advance() + + def _onClose(self, w, *args): + self.emit('delete_event', gtk.gdk.Event(gtk.gdk.BUTTON_PRESS)) + return True + + def _onButtonPress(self, widget, event): + if event.type == gtk.gdk._2BUTTON_PRESS: + printf(["Double click!"]) + self._onReplace() + + def _onSuggestionChanged(self, widget, *args): + selection = self.suggestion_list_view.get_selection() + model, iter = selection.get_selected() + if iter: + suggestion = model.get_value(iter, COLUMN_SUGGESTION) + self.replace_text.set_text(suggestion) + + def _getRepl(self): + """Get the chosen replacement string.""" + repl = self.replace_text.get_text() + repl = self._checker.coerce_string(repl) + return repl + + def _fillSuggestionList(self, suggestions): + model = self.suggestion_list_view.get_model() + model.clear() + for suggestion in suggestions: + value = unicode("%s" % (suggestion,)) + model.append([value, ]) + + def setSpellChecker(self, checker): + assert checker, 'checker cant be None' + self._checker = checker + self._dict_lable.set_text('Dictionary:%s' % (checker.dict.tag,)) + + def getSpellChecker(self, checker): + return self._checker + + def updateUI(self): + self._advance() + + def _disableButtons(self): + for w in self._conditional_widgets: + w.set_sensitive(False) + + def _enableButtons(self): + for w in self._conditional_widgets: + w.set_sensitive(True) + + def _advance(self): + """Advance to the next error. + This method advances the SpellChecker to the next error, if + any. It then displays the error and some surrounding context, + and well as listing the suggested replacements. + """ + # Disable interaction if no checker + if self._checker is None: + self._disableButtons() + self.emit('check-done') + return + + # Advance to next error, disable if not available + try: + self._checker.next() + except StopIteration: + self._disableButtons() + self.error_text.set_text("") + self._fillSuggestionList([]) + self.replace_text.set_text("") + return + self._enableButtons() + + # Display error context with erroneous word in red + self.error_text.set_text('') + iter = self.error_text.get_iter_at_offset(0) + append = self.error_text.insert_with_tags_by_name + + lContext = self._checker.leading_context(self._numContext) + tContext = self._checker.trailing_context(self._numContext) + append(iter, lContext, 'fg_black') + append(iter, self._checker.word, 'fg_red') + append(iter, tContext, 'fg_black') + + # Display suggestions in the replacements list + suggs = self._checker.suggest() + self._fillSuggestionList(suggs) + if suggs: + self.replace_text.set_text(suggs[0]) + else: + self.replace_text.set_text("") + + +def _test(): + from enchant.checker import SpellChecker + text = "This is sme text with a fw speling errors in it. Here are a fw more to tst it ut." + printf(["BEFORE:", text]) + chk_dlg = GtkSpellCheckerDialog() + chk_dlg.show() + chk_dlg.connect('delete_event', gtk.main_quit) + + chkr = SpellChecker("en_US", text) + + chk_dlg.setSpellChecker(chkr) + chk_dlg.updateUI() + gtk.main() + + +if __name__ == "__main__": + _test() diff --git a/PACK/enchant/checker/__init__.py b/PACK/enchant/checker/__init__.py new file mode 100644 index 0000000..304c0aa --- /dev/null +++ b/PACK/enchant/checker/__init__.py @@ -0,0 +1,379 @@ +# pyenchant +# +# Copyright (C) 2004-2008, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + +enchant.checker: High-level spellchecking functionality +======================================================== + +This package is designed to host higher-level spellchecking functionality +than is available in the base enchant package. It should make writing +applications that follow common usage idioms significantly easier. + +The most useful class is SpellChecker, which implements a spellchecking +loop over a block of text. It is capable of modifying the text in-place +if given an array of characters to work with. + +This package also contains several interfaces to the SpellChecker class, +such as a wxPython GUI dialog and a command-line interface. + +""" + +import array +import warnings + +import enchant +from enchant.errors import * +from enchant.tokenize import get_tokenizer +from enchant.utils import bytes, unicode, basestring, next +from enchant.utils import get_default_language + + +class SpellChecker: + """Class implementing stateful spellchecking behaviour. + + This class is designed to implement a spell-checking loop over + a block of text, correcting/ignoring/replacing words as required. + This loop is implemented using an iterator paradigm so it can be + embedded inside other loops of control. + + The SpellChecker object is stateful, and the appropriate methods + must be called to alter its state and affect the progress of + the spell checking session. At any point during the checking + session, the attribute 'word' will hold the current erroneously + spelled word under consideration. The action to take on this word + is determined by calling methods such as 'replace', 'replace_always' + and 'ignore_always'. Once this is done, calling 'next' advances + to the next misspelled word. + + As a quick (and rather silly) example, the following code replaces + each misspelled word with the string "SPAM": + + >>> text = "This is sme text with a fw speling errors in it." + >>> chkr = SpellChecker("en_US",text) + >>> for err in chkr: + ... err.replace("SPAM") + ... + >>> chkr.get_text() + 'This is SPAM text with a SPAM SPAM errors in it.' + >>> + + Internally, the SpellChecker always works with arrays of (possibly + unicode) character elements. This allows the in-place modification + of the string as it is checked, and is the closest thing Python has + to a mutable string. The text can be set as any of a normal string, + unicode string, character array or unicode character array. The + 'get_text' method will return the modified array object if an + array is used, or a new string object if a string it used. + + Words input to the SpellChecker may be either plain strings or + unicode objects. They will be converted to the same type as the + text being checked, using python's default encoding/decoding + settings. + + If using an array of characters with this object and the + array is modified outside of the spellchecking loop, use the + 'set_offset' method to reposition the internal loop pointer + to make sure it doesn't skip any words. + + """ + _DOC_ERRORS = ["sme", "fw", "speling", "chkr", "chkr", "chkr"] + + def __init__(self, lang=None, text=None, tokenize=None, chunkers=None, filters=None): + """Constructor for the SpellChecker class. + + SpellChecker objects can be created in two ways, depending on + the nature of the first argument. If it is a string, it + specifies a language tag from which a dictionary is created. + Otherwise, it must be an enchant Dict object to be used. + + Optional keyword arguments are: + + * text: to set the text to be checked at creation time + * tokenize: a custom tokenization function to use + * chunkers: a list of chunkers to apply during tokenization + * filters: a list of filters to apply during tokenization + + If is not given and the first argument is a Dict, + its 'tag' attribute must be a language tag so that a tokenization + function can be created automatically. If this attribute is missing + the user's default language will be used. + """ + if lang is None: + lang = get_default_language() + if isinstance(lang, basestring): + dict = enchant.Dict(lang) + else: + dict = lang + try: + lang = dict.tag + except AttributeError: + lang = get_default_language() + if lang is None: + raise DefaultLanguageNotFoundError + self.lang = lang + self.dict = dict + if tokenize is None: + try: + tokenize = get_tokenizer(lang, chunkers, filters) + except TokenizerNotFoundError: + # Fall back to default tokenization if no match for 'lang' + tokenize = get_tokenizer(None, chunkers, filters) + self._tokenize = tokenize + + self.word = None + self.wordpos = None + self._ignore_words = {} + self._replace_words = {} + # Default to the empty string as the text to be checked + self._text = array.array('u') + self._use_tostring = False + self._tokens = iter([]) + + if text is not None: + self.set_text(text) + + def __iter__(self): + """Each SpellChecker object is its own iterator""" + return self + + def set_text(self, text): + """Set the text to be spell-checked. + + This method must be called, or the 'text' argument supplied + to the constructor, before calling the 'next()' method. + """ + # Convert to an array object if necessary + if isinstance(text, basestring): + if type(text) is unicode: + self._text = array.array('u', text) + else: + self._text = array.array('c', text) + self._use_tostring = True + else: + self._text = text + self._use_tostring = False + self._tokens = self._tokenize(self._text) + + def get_text(self): + """Return the spell-checked text.""" + if self._use_tostring: + return self._array_to_string(self._text) + return self._text + + def _array_to_string(self, text): + """Format an internal array as a standard string.""" + if text.typecode == 'u': + return text.tounicode() + return text.tostring() + + def wants_unicode(self): + """Check whether the checker wants unicode strings. + + This method will return True if the checker wants unicode strings + as input, False if it wants normal strings. It's important to + provide the correct type of string to the checker. + """ + if self._text.typecode == 'u': + return True + return False + + def coerce_string(self, text, enc=None): + """Coerce string into the required type. + + This method can be used to automatically ensure that strings + are of the correct type required by this checker - either unicode + or standard. If there is a mismatch, conversion is done using + python's default encoding unless another encoding is specified. + """ + if self.wants_unicode(): + if not isinstance(text, unicode): + if enc is None: + return text.decode() + else: + return text.decode(enc) + return text + if not isinstance(text, bytes): + if enc is None: + return text.encode() + else: + return text.encode(enc) + return text + + def __next__(self): + return self.next() + + def next(self): + """Process text up to the next spelling error. + + This method is designed to support the iterator protocol. + Each time it is called, it will advance the 'word' attribute + to the next spelling error in the text. When no more errors + are found, it will raise StopIteration. + + The method will always return self, so that it can be used + sensibly in common idioms such as: + + for err in checker: + err.do_something() + + """ + # Find the next spelling error. + # The uncaught StopIteration from next(self._tokens) + # will provide the StopIteration for this method + while True: + (word, pos) = next(self._tokens) + # decode back to a regular string + word = self._array_to_string(word) + if self.dict.check(word): + continue + if word in self._ignore_words: + continue + self.word = word + self.wordpos = pos + if word in self._replace_words: + self.replace(self._replace_words[word]) + continue + break + return self + + def replace(self, repl): + """Replace the current erroneous word with the given string.""" + repl = self.coerce_string(repl) + aRepl = array.array(self._text.typecode, repl) + if repl: + self.dict.store_replacement(self.word, repl) + self._text[self.wordpos:self.wordpos + len(self.word)] = aRepl + incr = len(repl) - len(self.word) + self._tokens.set_offset(self._tokens.offset + incr, replaced=True) + + def replace_always(self, word, repl=None): + """Always replace given word with given replacement. + + If a single argument is given, this is used to replace the + current erroneous word. If two arguments are given, that + combination is added to the list for future use. + """ + if repl is None: + repl = word + word = self.word + repl = self.coerce_string(repl) + word = self.coerce_string(word) + self._replace_words[word] = repl + if self.word == word: + self.replace(repl) + + def ignore_always(self, word=None): + """Add given word to list of words to ignore. + + If no word is given, the current erroneous word is added. + """ + if word is None: + word = self.word + word = self.coerce_string(word) + if word not in self._ignore_words: + self._ignore_words[word] = True + + def add_to_personal(self, word=None): + """Add given word to the personal word list. + + If no word is given, the current erroneous word is added. + """ + warnings.warn("SpellChecker.add_to_personal is deprecated, " \ + "please use SpellChecker.add", + category=DeprecationWarning, stacklevel=2) + self.add(word) + + def add(self, word=None): + """Add given word to the personal word list. + + If no word is given, the current erroneous word is added. + """ + if word is None: + word = self.word + self.dict.add(word) + + def suggest(self, word=None): + """Return suggested spellings for the given word. + + If no word is given, the current erroneous word is used. + """ + if word is None: + word = self.word + suggs = self.dict.suggest(word) + return suggs + + def check(self, word): + """Check correctness of the given word.""" + return self.dict.check(word) + + def set_offset(self, off, whence=0): + """Set the offset of the tokenization routine. + + For more details on the purpose of the tokenization offset, + see the documentation of the 'enchant.tokenize' module. + The optional argument whence indicates the method by + which to change the offset: + * 0 (the default) treats as an increment + * 1 treats as a distance from the start + * 2 treats as a distance from the end + """ + if whence == 0: + self._tokens.set_offset(self._tokens.offset + off) + elif whence == 1: + assert (off > 0) + self._tokens.set_offset(off) + elif whence == 2: + assert (off > 0) + self._tokens.set_offset(len(self._text) - 1 - off) + else: + raise ValueError("Invalid value for whence: %s" % (whence,)) + + def leading_context(self, chars): + """Get characters of leading context. + + This method returns up to characters of leading + context - the text that occurs in the string immediately + before the current erroneous word. + """ + start = max(self.wordpos - chars, 0) + context = self._text[start:self.wordpos] + return self._array_to_string(context) + + def trailing_context(self, chars): + """Get characters of trailing context. + + This method returns up to characters of trailing + context - the text that occurs in the string immediately + after the current erroneous word. + """ + start = self.wordpos + len(self.word) + end = min(start + chars, len(self._text)) + context = self._text[start:end] + return self._array_to_string(context) diff --git a/PACK/enchant/checker/tests.py b/PACK/enchant/checker/tests.py new file mode 100644 index 0000000..e99b552 --- /dev/null +++ b/PACK/enchant/checker/tests.py @@ -0,0 +1,246 @@ +# pyenchant +# +# Copyright (C) 2004-2009, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + + enchant.checker.tests: Unittests for enchant SpellChecker class + +""" + +import unittest + +import enchant +import enchant.tokenize +from enchant.utils import * +from enchant.errors import * +from enchant.checker import * + + +class TestChecker(unittest.TestCase): + """TestCases for checking behaviour of SpellChecker class.""" + + def test_basic(self): + """Test a basic run of the SpellChecker class.""" + text = """This is sme text with a few speling erors in it. Its gret + for checking wheather things are working proprly with the SpellChecker + class. Not gret for much elss though.""" + chkr = SpellChecker("en_US", text=text) + for n, err in enumerate(chkr): + if n == 0: + # Fix up "sme" -> "some" properly + self.assertEqual(err.word, "sme") + self.assertEqual(err.wordpos, 8) + self.assertTrue("some" in err.suggest()) + err.replace("some") + if n == 1: + # Ignore "speling" + self.assertEqual(err.word, "speling") + if n == 2: + # Check context around "erors", and replace + self.assertEqual(err.word, "erors") + self.assertEqual(err.leading_context(5), "ling ") + self.assertEqual(err.trailing_context(5), " in i") + err.replace(raw_unicode("errors")) + if n == 3: + # Replace-all on gret as it appears twice + self.assertEqual(err.word, "gret") + err.replace_always("great") + if n == 4: + # First encounter with "wheather", move offset back + self.assertEqual(err.word, "wheather") + err.set_offset(-1 * len(err.word)) + if n == 5: + # Second encounter, fix up "wheather' + self.assertEqual(err.word, "wheather") + err.replace("whether") + if n == 6: + # Just replace "proprly", but also add an ignore + # for "SpellChecker" + self.assertEqual(err.word, "proprly") + err.replace("properly") + err.ignore_always("SpellChecker") + if n == 7: + # The second "gret" should have been replaced + # So it's now on "elss" + self.assertEqual(err.word, "elss") + err.replace("else") + if n > 7: + self.fail("Extraneous spelling errors were found") + text2 = """This is some text with a few speling errors in it. Its great + for checking whether things are working properly with the SpellChecker + class. Not great for much else though.""" + self.assertEqual(chkr.get_text(), text2) + + def test_filters(self): + """Test SpellChecker with the 'filters' argument.""" + text = """I contain WikiWords that ShouldBe skipped by the filters""" + chkr = SpellChecker("en_US", text=text, + filters=[enchant.tokenize.WikiWordFilter]) + for err in chkr: + # There are no errors once the WikiWords are skipped + self.fail("Extraneous spelling errors were found") + self.assertEqual(chkr.get_text(), text) + + def test_chunkers(self): + """Test SpellChecker with the 'chunkers' argument.""" + text = """I contain tags that should be skipped""" + chkr = SpellChecker("en_US", text=text, + chunkers=[enchant.tokenize.HTMLChunker]) + for err in chkr: + # There are no errors when the tag is skipped + self.fail("Extraneous spelling errors were found") + self.assertEqual(chkr.get_text(), text) + + def test_chunkers_and_filters(self): + """Test SpellChecker with the 'chunkers' and 'filters' arguments.""" + text = """I contain tags that should be skipped + along with a >> dlg = wxSpellCheckerDialog(None,-1,"") + >>> chkr = SpellChecker("en_AU",text) + >>> dlg.SetSpellChecker(chkr) + >>> dlg.Show() + + This is most useful when the text to be checked is in the form of + a character array, as it will be modified in place as the user + interacts with the dialog. For checking strings, the final result + will need to be obtained from the SpellChecker object: + + >>> dlg = wxSpellCheckerDialog(None,-1,"") + >>> chkr = SpellChecker("en_AU",text) + >>> dlg.SetSpellChecker(chkr) + >>> dlg.ShowModal() + >>> text = dlg.GetSpellChecker().get_text() + + Currently the checker must deal with strings of the same type as + returned by wxPython - unicode or normal string depending on the + underlying system. This needs to be fixed, somehow... + """ + _DOC_ERRORS = ["dlg", "chkr", "dlg", "SetSpellChecker", "chkr", "dlg", + "dlg", "chkr", "dlg", "SetSpellChecker", "chkr", "dlg", + "ShowModal", "dlg", "GetSpellChecker"] + + # Remember dialog size across invocations by storing it on the class + sz = (300, 70) + + def __init__(self, parent=None, id=-1, title="Checking Spelling..."): + wx.Dialog.__init__(self, parent, id, title, size=wxSpellCheckerDialog.sz, + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + self._numContext = 40 + self._checker = None + self._buttonsEnabled = True + self.error_text = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH) + self.replace_text = wx.TextCtrl(self, -1, "", style=wx.TE_PROCESS_ENTER) + self.replace_list = wx.ListBox(self, -1, style=wx.LB_SINGLE) + self.InitLayout() + wx.EVT_LISTBOX(self, self.replace_list.GetId(), self.OnReplSelect) + wx.EVT_LISTBOX_DCLICK(self, self.replace_list.GetId(), self.OnReplace) + + def InitLayout(self): + """Lay out controls and add buttons.""" + sizer = wx.BoxSizer(wx.HORIZONTAL) + txtSizer = wx.BoxSizer(wx.VERTICAL) + btnSizer = wx.BoxSizer(wx.VERTICAL) + replaceSizer = wx.BoxSizer(wx.HORIZONTAL) + txtSizer.Add(wx.StaticText(self, -1, "Unrecognised Word:"), 0, wx.LEFT | wx.TOP, 5) + txtSizer.Add(self.error_text, 1, wx.ALL | wx.EXPAND, 5) + replaceSizer.Add(wx.StaticText(self, -1, "Replace with:"), 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + replaceSizer.Add(self.replace_text, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + txtSizer.Add(replaceSizer, 0, wx.EXPAND, 0) + txtSizer.Add(self.replace_list, 2, wx.ALL | wx.EXPAND, 5) + sizer.Add(txtSizer, 1, wx.EXPAND, 0) + self.buttons = [] + for label, action, tip in ( \ + ("Ignore", self.OnIgnore, "Ignore this word and continue"), + ("Ignore All", self.OnIgnoreAll, "Ignore all instances of this word and continue"), + ("Replace", self.OnReplace, "Replace this word"), + ("Replace All", self.OnReplaceAll, "Replace all instances of this word"), + ("Add", self.OnAdd, "Add this word to the dictionary"), + ("Done", self.OnDone, "Finish spell-checking and accept changes"), + ): + btn = wx.Button(self, -1, label) + btn.SetToolTip(wx.ToolTip(tip)) + btnSizer.Add(btn, 0, wx.ALIGN_RIGHT | wx.ALL, 4) + btn.Bind(wx.EVT_BUTTON, action) + self.buttons.append(btn) + sizer.Add(btnSizer, 0, wx.ALL | wx.EXPAND, 5) + self.SetAutoLayout(True) + self.SetSizer(sizer) + sizer.Fit(self) + + def Advance(self): + """Advance to the next error. + + This method advances the SpellChecker to the next error, if + any. It then displays the error and some surrounding context, + and well as listing the suggested replacements. + """ + # Disable interaction if no checker + if self._checker is None: + self.EnableButtons(False) + return False + # Advance to next error, disable if not available + try: + self._checker.next() + except StopIteration: + self.EnableButtons(False) + self.error_text.SetValue("") + self.replace_list.Clear() + self.replace_text.SetValue("") + if self.IsModal(): # test needed for SetSpellChecker call + # auto-exit when checking complete + self.EndModal(wx.ID_OK) + return False + self.EnableButtons() + # Display error context with erroneous word in red. + # Restoring default style was misbehaving under win32, so + # I am forcing the rest of the text to be black. + self.error_text.SetValue("") + self.error_text.SetDefaultStyle(wx.TextAttr(wx.BLACK)) + lContext = self._checker.leading_context(self._numContext) + self.error_text.AppendText(lContext) + self.error_text.SetDefaultStyle(wx.TextAttr(wx.RED)) + self.error_text.AppendText(self._checker.word) + self.error_text.SetDefaultStyle(wx.TextAttr(wx.BLACK)) + tContext = self._checker.trailing_context(self._numContext) + self.error_text.AppendText(tContext) + # Display suggestions in the replacements list + suggs = self._checker.suggest() + self.replace_list.Set(suggs) + self.replace_text.SetValue(suggs and suggs[0] or '') + return True + + def EnableButtons(self, state=True): + """Enable the checking-related buttons""" + if state != self._buttonsEnabled: + for btn in self.buttons[:-1]: + btn.Enable(state) + self._buttonsEnabled = state + + def GetRepl(self): + """Get the chosen replacement string.""" + repl = self.replace_text.GetValue() + return repl + + def OnAdd(self, evt): + """Callback for the "add" button.""" + self._checker.add() + self.Advance() + + def OnDone(self, evt): + """Callback for the "close" button.""" + wxSpellCheckerDialog.sz = self.error_text.GetSizeTuple() + if self.IsModal(): + self.EndModal(wx.ID_OK) + else: + self.Close() + + def OnIgnore(self, evt): + """Callback for the "ignore" button. + This simply advances to the next error. + """ + self.Advance() + + def OnIgnoreAll(self, evt): + """Callback for the "ignore all" button.""" + self._checker.ignore_always() + self.Advance() + + def OnReplace(self, evt): + """Callback for the "replace" button.""" + repl = self.GetRepl() + if repl: + self._checker.replace(repl) + self.Advance() + + def OnReplaceAll(self, evt): + """Callback for the "replace all" button.""" + repl = self.GetRepl() + self._checker.replace_always(repl) + self.Advance() + + def OnReplSelect(self, evt): + """Callback when a new replacement option is selected.""" + sel = self.replace_list.GetSelection() + if sel == -1: + return + opt = self.replace_list.GetString(sel) + self.replace_text.SetValue(opt) + + def GetSpellChecker(self): + """Get the spell checker object.""" + return self._checker + + def SetSpellChecker(self, chkr): + """Set the spell checker, advancing to the first error. + Return True if error(s) to correct, else False.""" + self._checker = chkr + return self.Advance() + + +def _test(): + class TestDialog(wxSpellCheckerDialog): + def __init__(self, *args): + wxSpellCheckerDialog.__init__(self, *args) + wx.EVT_CLOSE(self, self.OnClose) + + def OnClose(self, evnt): + chkr = dlg.GetSpellChecker() + if chkr is not None: + printf(["AFTER:", chkr.get_text()]) + self.Destroy() + + from enchant.checker import SpellChecker + text = "This is sme text with a fw speling errors in it. Here are a fw more to tst it ut." + printf(["BEFORE:", text]) + app = wx.PySimpleApp() + dlg = TestDialog() + chkr = SpellChecker("en_US", text) + dlg.SetSpellChecker(chkr) + dlg.Show() + app.MainLoop() + + +if __name__ == "__main__": + _test() diff --git a/PACK/enchant/errors.py b/PACK/enchant/errors.py new file mode 100644 index 0000000..cd6930c --- /dev/null +++ b/PACK/enchant/errors.py @@ -0,0 +1,57 @@ +# pyenchant +# +# Copyright (C) 2004-2008, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPsE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" +enchant.errors: Error class definitions for the enchant library +================================================================ + +All error classes are defined in this separate sub-module, so that they +can safely be imported without causing circular dependencies. + +""" + + +class Error(Exception): + """Base exception class for the enchant module.""" + pass + + +class DictNotFoundError(Error): + """Exception raised when a requested dictionary could not be found.""" + pass + + +class TokenizerNotFoundError(Error): + """Exception raised when a requested tokenizer could not be found.""" + pass + + +class DefaultLanguageNotFoundError(Error): + """Exception raised when a default language could not be found.""" + pass diff --git a/PACK/enchant/lib/enchant/README.txt b/PACK/enchant/lib/enchant/README.txt new file mode 100644 index 0000000..1943a29 --- /dev/null +++ b/PACK/enchant/lib/enchant/README.txt @@ -0,0 +1,4 @@ + +This directory contains the plugin DLLs for enchant when installed on +a Microsoft Windows system. + diff --git a/PACK/enchant/pypwl.py b/PACK/enchant/pypwl.py new file mode 100644 index 0000000..9356ca9 --- /dev/null +++ b/PACK/enchant/pypwl.py @@ -0,0 +1,285 @@ +# pyenchant +# +# Copyright (C) 2004-2011 Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + +pypwl: pure-python personal word list in the style of Enchant +============================================================== + +This module provides a pure-python version of the personal word list +functionality found in the spellchecking package Enchant. While the +same effect can be achieved (with better performance) using the python +bindings for Enchant, it requires a C extension. + +This pure-python implementation uses the same algorithm but without any +external dependencies or C code (in fact, it was the author's original +prototype for the C version found in Enchant). + +""" + +from __future__ import generators + +import os +import warnings + + +class Trie: + """Class implementing a trie-based dictionary of words. + + A Trie is a recursive data structure storing words by their prefix. + "Fuzzy matching" can be done by allowing a certain number of missteps + when traversing the Trie. + """ + + def __init__(self, words=()): + self._eos = False # whether I am the end of a word + self._keys = {} # letters at this level of the trie + for w in words: + self.insert(w) + + def insert(self, word): + if word == "": + self._eos = True + else: + key = word[0] + try: + subtrie = self[key] + except KeyError: + subtrie = Trie() + self[key] = subtrie + subtrie.insert(word[1:]) + + def remove(self, word): + if word == "": + self._eos = False + else: + key = word[0] + try: + subtrie = self[key] + except KeyError: + pass + else: + subtrie.remove(word[1:]) + + def search(self, word, nerrs=0): + """Search for the given word, possibly making errors. + + This method searches the trie for the given , making + precisely errors. It returns a list of words found. + """ + res = [] + # Terminate if we've run out of errors + if nerrs < 0: + return res + # Precise match at the end of the word + if nerrs == 0 and word == "": + if self._eos: + res.append("") + # Precisely match word[0] + try: + subtrie = self[word[0]] + subres = subtrie.search(word[1:], nerrs) + for w in subres: + w2 = word[0] + w + if w2 not in res: + res.append(w2) + except (IndexError, KeyError): + pass + # match with deletion of word[0] + try: + subres = self.search(word[1:], nerrs - 1) + for w in subres: + if w not in res: + res.append(w) + except (IndexError,): + pass + # match with insertion before word[0] + try: + for k in self._keys: + subres = self[k].search(word, nerrs - 1) + for w in subres: + w2 = k + w + if w2 not in res: + res.append(w2) + except (IndexError, KeyError): + pass + # match on substitution of word[0] + try: + for k in self._keys: + subres = self[k].search(word[1:], nerrs - 1) + for w in subres: + w2 = k + w + if w2 not in res: + res.append(w2) + except (IndexError, KeyError): + pass + # All done! + return res + + search._DOC_ERRORS = ["nerrs"] + + def __getitem__(self, key): + return self._keys[key] + + def __setitem__(self, key, val): + self._keys[key] = val + + def __iter__(self): + if self._eos: + yield "" + for k in self._keys: + for w2 in self._keys[k]: + yield k + w2 + + +class PyPWL: + """Pure-python implementation of Personal Word List dictionary. + This class emulates the PWL objects provided by PyEnchant, but + implemented purely in python. + """ + + def __init__(self, pwl=None): + """PyPWL constructor. + This method takes as its only argument the name of a file + containing the personal word list, one word per line. Entries + will be read from this file, and new entries will be written to + it automatically. + + If is not specified or None, the list is maintained in + memory only. + """ + self.provider = None + self._words = Trie() + if pwl is not None: + self.pwl = os.path.abspath(pwl) + self.tag = self.pwl + pwlF = file(pwl) + for ln in pwlF: + word = ln.strip() + self.add_to_session(word) + pwlF.close() + else: + self.pwl = None + self.tag = "PyPWL" + + def check(self, word): + """Check spelling of a word. + + This method takes a word in the dictionary language and returns + True if it is correctly spelled, and false otherwise. + """ + res = self._words.search(word) + return bool(res) + + def suggest(self, word): + """Suggest possible spellings for a word. + + This method tries to guess the correct spelling for a given + word, returning the possibilities in a list. + """ + limit = 10 + maxdepth = 5 + # Iterative deepening until we get enough matches + depth = 0 + res = self._words.search(word, depth) + while len(res) < limit and depth < maxdepth: + depth += 1 + for w in self._words.search(word, depth): + if w not in res: + res.append(w) + # Limit number of suggs + return res[:limit] + + def add(self, word): + """Add a word to the user's personal dictionary. + For a PWL, this means appending it to the file. + """ + if self.pwl is not None: + pwlF = file(self.pwl, "a") + pwlF.write("%s\n" % (word.strip(),)) + pwlF.close() + self.add_to_session(word) + + def add_to_pwl(self, word): + """Add a word to the user's personal dictionary. + For a PWL, this means appending it to the file. + """ + warnings.warn("PyPWL.add_to_pwl is deprecated, please use PyPWL.add", + category=DeprecationWarning, stacklevel=2) + self.add(word) + + def remove(self, word): + """Add a word to the user's personal exclude list.""" + # There's no exclude list for a stand-alone PWL. + # Just remove it from the list. + self._words.remove(word) + if self.pwl is not None: + pwlF = file(self.pwl, "wt") + for w in self._words: + pwlF.write("%s\n" % (w.strip(),)) + pwlF.close() + + def add_to_session(self, word): + """Add a word to the session list.""" + self._words.insert(word) + + def is_in_session(self, word): + """Check whether a word is in the session list.""" + warnings.warn("PyPWL.is_in_session is deprecated, please use PyPWL.is_added", category=DeprecationWarning) + # Consider all words to be in the session list + return self.check(word) + + def store_replacement(self, mis, cor): + """Store a replacement spelling for a miss-spelled word. + + This method makes a suggestion to the spellchecking engine that the + miss-spelled word is in fact correctly spelled as . Such + a suggestion will typically mean that appears early in the + list of suggested spellings offered for later instances of . + """ + # Too much work for this simple spellchecker + pass + + store_replacement._DOC_ERRORS = ["mis", "mis"] + + def is_added(self, word): + """Check whether a word is in the personal word list.""" + return self.check(word) + + def is_removed(self, word): + """Check whether a word is in the personal exclude list.""" + return False + + # No-op methods to support internal use as a Dict() replacement + + def _check_this(self, msg): + pass + + def _free(self): + pass diff --git a/PACK/enchant/share/enchant/README.txt b/PACK/enchant/share/enchant/README.txt new file mode 100644 index 0000000..6d9b66d --- /dev/null +++ b/PACK/enchant/share/enchant/README.txt @@ -0,0 +1,4 @@ + +This directory contains dictionary files for Enchant when installed on a +Microsoft Windows system. Each subdirectory contains dictionaries for +a particular spellchecking system. diff --git a/PACK/enchant/share/enchant/ispell/README.txt b/PACK/enchant/share/enchant/ispell/README.txt new file mode 100644 index 0000000..e69de29 diff --git a/PACK/enchant/share/enchant/myspell/README.txt b/PACK/enchant/share/enchant/myspell/README.txt new file mode 100644 index 0000000..ae8145f --- /dev/null +++ b/PACK/enchant/share/enchant/myspell/README.txt @@ -0,0 +1,3 @@ + +This directory contains dictionaries for the myspell backend to enchant. + diff --git a/PACK/enchant/tests.py b/PACK/enchant/tests.py new file mode 100644 index 0000000..c067377 --- /dev/null +++ b/PACK/enchant/tests.py @@ -0,0 +1,616 @@ +# pyenchant +# +# Copyright (C) 2004-2009, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPsE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + + enchant.tests: testcases for pyenchant + +""" + +import os +import sys +import unittest +import pickle + +try: + import subprocess +except ImportError: + subprocess = None + +import enchant +from enchant import * +from enchant import _enchant as _e +from enchant.utils import unicode, raw_unicode, printf, trim_suggestions + + +def runcmd(cmd): + if subprocess is not None: + kwds = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + p = subprocess.Popen(cmd, **kwds) + (stdout, stderr) = p.communicate() + if p.returncode: + if sys.version_info[0] >= 3: + stderr = stderr.decode(sys.getdefaultencoding(), "replace") + sys.stderr.write(stderr) + return p.returncode + else: + return os.system(cmd) + + +class TestBroker(unittest.TestCase): + """Test cases for the proper functioning of Broker objects. + + These tests assume that there is at least one working provider + with a dictionary for the "en_US" language. + """ + + def setUp(self): + self.broker = Broker() + + def tearDown(self): + del self.broker + + def test_HasENUS(self): + """Test that the en_US language is available.""" + self.assertTrue(self.broker.dict_exists("en_US")) + + def test_LangsAreAvail(self): + """Test whether all advertised languages are in fact available.""" + for lang in self.broker.list_languages(): + if not self.broker.dict_exists(lang): + assert False, "language '" + lang + "' advertised but non-existent" + + def test_ProvsAreAvail(self): + """Test whether all advertised providers are in fact available.""" + for (lang, prov) in self.broker.list_dicts(): + self.assertTrue(self.broker.dict_exists(lang)) + if not self.broker.dict_exists(lang): + assert False, "language '" + lang + "' advertised but non-existent" + if prov not in self.broker.describe(): + assert False, "provier '" + str(prov) + "' advertised but non-existent" + + def test_ProvOrdering(self): + """Test that provider ordering works correctly.""" + langs = {} + provs = [] + # Find the providers for each language, and a list of all providers + for (tag, prov) in self.broker.list_dicts(): + # Skip hyphenation dictionaries installed by OOo + if tag.startswith("hyph_") and prov.name == "myspell": + continue + # Canonicalize separators + tag = tag.replace("-", "_") + langs[tag] = [] + # NOTE: we are excluding Zemberek here as it appears to return + # a broker for any language, even nonexistent ones + if prov not in provs and prov.name != "zemberek": + provs.append(prov) + for prov in provs: + for tag in langs: + b2 = Broker() + b2.set_ordering(tag, prov.name) + try: + d = b2.request_dict(tag) + if d.provider != prov: + raise ValueError() + langs[tag].append(prov) + except: + pass + # Check availability using a single entry in ordering + for tag in langs: + for prov in langs[tag]: + b2 = Broker() + b2.set_ordering(tag, prov.name) + d = b2.request_dict(tag) + self.assertEqual((d.provider, tag), (prov, tag)) + del d + del b2 + # Place providers that dont have the language in the ordering + for tag in langs: + for prov in langs[tag]: + order = prov.name + for prov2 in provs: + if prov2 not in langs[tag]: + order = prov2.name + "," + order + b2 = Broker() + b2.set_ordering(tag, order) + d = b2.request_dict(tag) + self.assertEqual((d.provider, tag, order), (prov, tag, order)) + del d + del b2 + + def test_UnicodeTag(self): + """Test that unicode language tags are accepted""" + d1 = self.broker._request_dict_data(raw_unicode("en_US")) + self.assertTrue(d1) + _e.broker_free_dict(self.broker._this, d1) + d1 = Dict(raw_unicode("en_US")) + self.assertTrue(d1) + + def test_GetSetParam(self): + try: + self.broker.get_param("pyenchant.unittest") + except AttributeError: + return + self.assertEqual(self.broker.get_param("pyenchant.unittest"), None) + self.broker.set_param("pyenchant.unittest", "testing") + self.assertEqual(self.broker.get_param("pyenchant.unittest"), "testing") + self.assertEqual(Broker().get_param("pyenchant.unittest"), None) + + +class TestDict(unittest.TestCase): + """Test cases for the proper functioning of Dict objects. + These tests assume that there is at least one working provider + with a dictionary for the "en_US" language. + """ + + def setUp(self): + self.dict = Dict("en_US") + + def tearDown(self): + del self.dict + + def test_HasENUS(self): + """Test that the en_US language is available through default broker.""" + self.assertTrue(dict_exists("en_US")) + + def test_check(self): + """Test that check() works on some common words.""" + self.assertTrue(self.dict.check("hello")) + self.assertTrue(self.dict.check("test")) + self.assertFalse(self.dict.check("helo")) + self.assertFalse(self.dict.check("testt")) + + def test_broker(self): + """Test that the dict's broker is set correctly.""" + self.assertTrue(self.dict._broker is enchant._broker) + + def test_tag(self): + """Test that the dict's tag is set correctly.""" + self.assertEqual(self.dict.tag, "en_US") + + def test_suggest(self): + """Test that suggest() gets simple suggestions right.""" + self.assertTrue(self.dict.check("hello")) + self.assertTrue("hello" in self.dict.suggest("helo")) + + def test_suggestHang1(self): + """Test whether suggest() hangs on some inputs (Bug #1404196)""" + self.assertTrue(len(self.dict.suggest("Thiis")) >= 0) + self.assertTrue(len(self.dict.suggest("Thiiis")) >= 0) + self.assertTrue(len(self.dict.suggest("Thiiiis")) >= 0) + + def test_unicode1(self): + """Test checking/suggesting for unicode strings""" + # TODO: find something that actually returns suggestions + us1 = raw_unicode(r"he\u2149lo") + self.assertTrue(type(us1) is unicode) + self.assertFalse(self.dict.check(us1)) + for s in self.dict.suggest(us1): + self.assertTrue(type(s) is unicode) + + def test_session(self): + """Test that adding words to the session works as required.""" + self.assertFalse(self.dict.check("Lozz")) + self.assertFalse(self.dict.is_added("Lozz")) + self.dict.add_to_session("Lozz") + self.assertTrue(self.dict.is_added("Lozz")) + self.assertTrue(self.dict.check("Lozz")) + self.dict.remove_from_session("Lozz") + self.assertFalse(self.dict.check("Lozz")) + self.assertFalse(self.dict.is_added("Lozz")) + self.dict.remove_from_session("hello") + self.assertFalse(self.dict.check("hello")) + self.assertTrue(self.dict.is_removed("hello")) + self.dict.add_to_session("hello") + + def test_AddRemove(self): + """Test adding/removing from default user dictionary.""" + nonsense = "kxhjsddsi" + self.assertFalse(self.dict.check(nonsense)) + self.dict.add(nonsense) + self.assertTrue(self.dict.is_added(nonsense)) + self.assertTrue(self.dict.check(nonsense)) + self.dict.remove(nonsense) + self.assertFalse(self.dict.is_added(nonsense)) + self.assertFalse(self.dict.check(nonsense)) + self.dict.remove("pineapple") + self.assertFalse(self.dict.check("pineapple")) + self.assertTrue(self.dict.is_removed("pineapple")) + self.assertFalse(self.dict.is_added("pineapple")) + self.dict.add("pineapple") + self.assertTrue(self.dict.check("pineapple")) + + def test_DefaultLang(self): + """Test behaviour of default language selection.""" + defLang = utils.get_default_language() + if defLang is None: + # If no default language, shouldnt work + self.assertRaises(Error, Dict) + else: + # If there is a default language, should use it + # Of course, no need for the dict to actually exist + try: + d = Dict() + self.assertEqual(d.tag, defLang) + except DictNotFoundError: + pass + + def test_pickling(self): + """Test that pickling doensn't corrupt internal state.""" + d1 = Dict("en") + self.assertTrue(d1.check("hello")) + d2 = pickle.loads(pickle.dumps(d1)) + self.assertTrue(d1.check("hello")) + self.assertTrue(d2.check("hello")) + d1._free() + self.assertTrue(d2.check("hello")) + + +class TestPWL(unittest.TestCase): + """Test cases for the proper functioning of PWLs and DictWithPWL objects. + These tests assume that there is at least one working provider + with a dictionary for the "en_US" language. + """ + + def setUp(self): + self._tempDir = self._mkdtemp() + self._fileName = "pwl.txt" + + def tearDown(self): + import shutil + shutil.rmtree(self._tempDir) + + def _mkdtemp(self): + import tempfile + return tempfile.mkdtemp() + + def _path(self, nm=None): + if nm is None: + nm = self._fileName + nm = os.path.join(self._tempDir, nm) + if not os.path.exists(nm): + open(nm, 'w').close() + return nm + + def setPWLContents(self, contents): + """Set the contents of the PWL file.""" + pwlFile = open(self._path(), "w") + for ln in contents: + pwlFile.write(ln) + pwlFile.write("\n") + pwlFile.flush() + pwlFile.close() + + def getPWLContents(self): + """Retrieve the contents of the PWL file.""" + pwlFile = open(self._path(), "r") + contents = pwlFile.readlines() + pwlFile.close() + return [c.strip() for c in contents] + + def test_check(self): + """Test that basic checking works for PWLs.""" + self.setPWLContents(["Sazz", "Lozz"]) + d = request_pwl_dict(self._path()) + self.assertTrue(d.check("Sazz")) + self.assertTrue(d.check("Lozz")) + self.assertFalse(d.check("hello")) + + def test_UnicodeFN(self): + """Test that unicode PWL filenames are accepted.""" + d = request_pwl_dict(unicode(self._path())) + self.assertTrue(d) + + def test_add(self): + """Test that adding words to a PWL works correctly.""" + d = request_pwl_dict(self._path()) + self.assertFalse(d.check("Flagen")) + d.add("Esquilax") + d.add("Esquilam") + self.assertTrue(d.check("Esquilax")) + self.assertTrue("Esquilax" in self.getPWLContents()) + self.assertTrue(d.is_added("Esquilax")) + + def test_suggestions(self): + """Test getting suggestions from a PWL.""" + self.setPWLContents(["Sazz", "Lozz"]) + d = request_pwl_dict(self._path()) + self.assertTrue("Sazz" in d.suggest("Saz")) + self.assertTrue("Lozz" in d.suggest("laz")) + self.assertTrue("Sazz" in d.suggest("laz")) + d.add("Flagen") + self.assertTrue("Flagen" in d.suggest("Flags")) + self.assertFalse("sazz" in d.suggest("Flags")) + + def test_DWPWL(self): + """Test functionality of DictWithPWL.""" + self.setPWLContents(["Sazz", "Lozz"]) + d = DictWithPWL("en_US", self._path(), self._path("pel.txt")) + self.assertTrue(d.check("Sazz")) + self.assertTrue(d.check("Lozz")) + self.assertTrue(d.check("hello")) + self.assertFalse(d.check("helo")) + self.assertFalse(d.check("Flagen")) + d.add("Flagen") + self.assertTrue(d.check("Flagen")) + self.assertTrue("Flagen" in self.getPWLContents()) + self.assertTrue("Flagen" in d.suggest("Flagn")) + self.assertTrue("hello" in d.suggest("helo")) + d.remove("hello") + self.assertFalse(d.check("hello")) + self.assertTrue("hello" not in d.suggest("helo")) + d.remove("Lozz") + self.assertFalse(d.check("Lozz")) + + def test_DWPWL_empty(self): + """Test functionality of DictWithPWL using transient dicts.""" + d = DictWithPWL("en_US", None, None) + self.assertTrue(d.check("hello")) + self.assertFalse(d.check("helo")) + self.assertFalse(d.check("Flagen")) + d.add("Flagen") + self.assertTrue(d.check("Flagen")) + d.remove("hello") + self.assertFalse(d.check("hello")) + d.add("hello") + self.assertTrue(d.check("hello")) + + def test_PyPWL(self): + """Test our pure-python PWL implementation.""" + d = PyPWL() + self.assertTrue(list(d._words) == []) + d.add("hello") + d.add("there") + d.add("duck") + ws = list(d._words) + self.assertTrue(len(ws) == 3) + self.assertTrue("hello" in ws) + self.assertTrue("there" in ws) + self.assertTrue("duck" in ws) + d.remove("duck") + d.remove("notinthere") + ws = list(d._words) + self.assertTrue(len(ws) == 2) + self.assertTrue("hello" in ws) + self.assertTrue("there" in ws) + + def test_UnicodeCharsInPath(self): + """Test that unicode chars in PWL paths are accepted.""" + self._fileName = raw_unicode(r"test_\xe5\xe4\xf6_ing") + d = request_pwl_dict(self._path()) + self.assertTrue(d) + + +class TestUtils(unittest.TestCase): + """Test cases for various utility functions.""" + + def test_trim_suggestions(self): + word = "gud" + suggs = ["good", "god", "bad+"] + self.assertEquals(trim_suggestions(word, suggs, 40), ["god", "good", "bad+"]) + self.assertEquals(trim_suggestions(word, suggs, 4), ["god", "good", "bad+"]) + self.assertEquals(trim_suggestions(word, suggs, 3), ["god", "good", "bad+"]) + self.assertEquals(trim_suggestions(word, suggs, 2), ["god", "good"]) + self.assertEquals(trim_suggestions(word, suggs, 1), ["god"]) + self.assertEquals(trim_suggestions(word, suggs, 0), []) + + +class TestDocStrings(unittest.TestCase): + """Test the spelling on all docstrings we can find in this module. + + This serves two purposes - to provide a lot of test data for the + checker routines, and to make sure we don't suffer the embarrassment + of having spelling errors in a spellchecking package! + """ + + WORDS = ["spellchecking", "utf", "dict", "unicode", "bytestring", "bytestrings", + "str", "pyenchant", "ascii", "utils", "setup", "distutils", "pkg", + "filename", "tokenization", "tuple", "tuples", "tokenizer", + "tokenizers", "testcase", "testcases", "whitespace", "wxpython", + "spellchecker", "dialog", "urls", "wikiwords", "enchantobject", + "providerdesc", "spellcheck", "pwl", "aspell", "myspell", + "docstring", "docstrings", "stopiteration", "pwls", "pypwl", + "dictwithpwl", "skippable", "dicts", "dict's", "filenames", + "trie", "api", "ctypes", "wxspellcheckerdialog", "stateful", + "cmdlinechecker", "spellchecks", "callback", "clunkier", "iterator", + "ispell", "cor", "backends"] + + def test_docstrings(self): + """Test that all our docstrings are error-free.""" + import enchant + import enchant.utils + import enchant.pypwl + import enchant.tokenize + import enchant.tokenize.en + import enchant.checker + import enchant.checker.CmdLineChecker + try: + import enchant.checker.GtkSpellCheckerDialog + except ImportError: + pass + try: + import enchant.checker.wxSpellCheckerDialog + except ImportError: + pass + errors = [] + # Naive recursion here would blow the stack, instead we + # simulate it with our own stack + tocheck = [enchant] + checked = [] + while tocheck: + obj = tocheck.pop() + checked.append(obj) + newobjs = list(self._check_docstrings(obj, errors)) + tocheck.extend([obj for obj in newobjs if obj not in checked]) + self.assertEqual(len(errors), 0) + + def _check_docstrings(self, obj, errors): + import enchant + if hasattr(obj, "__doc__"): + skip_errors = [w for w in getattr(obj, "_DOC_ERRORS", [])] + chkr = enchant.checker.SpellChecker("en_AU", obj.__doc__, filters=[enchant.tokenize.URLFilter]) + for err in chkr: + if len(err.word) == 1: + continue + if err.word.lower() in self.WORDS: + continue + if skip_errors and skip_errors[0] == err.word: + skip_errors.pop(0) + continue + errors.append((obj, err.word, err.wordpos)) + msg = "\nDOCSTRING SPELLING ERROR: %s %s %d %s\n" % (obj, err.word, err.wordpos, chkr.suggest()) + printf([msg], file=sys.stderr) + # Find and yield all child objects that should be checked + for name in dir(obj): + if name.startswith("__"): + continue + child = getattr(obj, name) + if hasattr(child, "__file__"): + if not hasattr(globals(), "__file__"): + continue + if not child.__file__.startswith(os.path.dirname(__file__)): + continue + else: + cmod = getattr(child, "__module__", None) + if not cmod: + cclass = getattr(child, "__class__", None) + cmod = getattr(cclass, "__module__", None) + if cmod and not cmod.startswith("enchant"): + continue + yield child + + +class TestInstallEnv(unittest.TestCase): + """Run all testcases in a variety of install environments.""" + + def setUp(self): + self._tempDir = self._mkdtemp() + self._insDir = "build" + + def tearDown(self): + import shutil + shutil.rmtree(self._tempDir) + + def _mkdtemp(self): + import tempfile + return tempfile.mkdtemp() + + def install(self): + import os, sys, shutil + insdir = os.path.join(self._tempDir, self._insDir) + os.makedirs(insdir) + shutil.copytree("enchant", os.path.join(insdir, "enchant")) + + def runtests(self): + import os, sys + insdir = os.path.join(self._tempDir, self._insDir) + if str is not unicode and isinstance(insdir, unicode): + insdir = insdir.encode(sys.getfilesystemencoding()) + os.environ["PYTHONPATH"] = insdir + script = os.path.join(insdir, "enchant", "__init__.py") + res = runcmd("\"%s\" %s" % (sys.executable, script,)) + self.assertEqual(res, 0) + + def test_basic(self): + """Test proper functioning of TestInstallEnv suite.""" + self.install() + self.runtests() + + test_basic._DOC_ERRORS = ["TestInstallEnv"] + + def test_UnicodeInstallPath(self): + """Test installation in a path containing unicode chars.""" + self._insDir = raw_unicode(r'test_\xe5\xe4\xf6_ing') + self.install() + self.runtests() + + +class TestPy2exe(unittest.TestCase): + """Run all testcases inside a py2exe executable""" + _DOC_ERRORS = ["py", "exe"] + + def setUp(self): + self._tempDir = self._mkdtemp() + + def tearDown(self): + import shutil + shutil.rmtree(self._tempDir) + + def test_py2exe(self): + """Test pyenchant running inside a py2exe executable.""" + import os, sys, shutil + from os import path + from os.path import dirname + try: + import py2exe + except ImportError: + return + os.environ["PYTHONPATH"] = dirname(dirname(__file__)) + setup_py = path.join(dirname(__file__), "..", "tools", "setup.py2exe.py") + if not path.exists(setup_py): + return + buildCmd = '%s %s -q py2exe --dist-dir="%s"' + buildCmd = buildCmd % (sys.executable, setup_py, self._tempDir) + res = runcmd(buildCmd) + self.assertEqual(res, 0) + testCmd = self._tempDir + "\\test_pyenchant.exe" + self.assertTrue(os.path.exists(testCmd)) + res = runcmd(testCmd) + self.assertEqual(res, 0) + + test_py2exe._DOC_ERRORS = ["py", "exe"] + + def _mkdtemp(self): + import tempfile + return tempfile.mkdtemp() + + +def buildtestsuite(recurse=True): + from enchant.checker.tests import TestChecker + from enchant.tokenize.tests import TestTokenization, TestFilters + from enchant.tokenize.tests import TestTokenizeEN + suite = unittest.TestSuite() + if recurse: + suite.addTest(unittest.makeSuite(TestInstallEnv)) + suite.addTest(unittest.makeSuite(TestPy2exe)) + suite.addTest(unittest.makeSuite(TestBroker)) + suite.addTest(unittest.makeSuite(TestDict)) + suite.addTest(unittest.makeSuite(TestPWL)) + suite.addTest(unittest.makeSuite(TestUtils)) + suite.addTest(unittest.makeSuite(TestDocStrings)) + suite.addTest(unittest.makeSuite(TestChecker)) + suite.addTest(unittest.makeSuite(TestTokenization)) + suite.addTest(unittest.makeSuite(TestTokenizeEN)) + suite.addTest(unittest.makeSuite(TestFilters)) + return suite + + +def runtestsuite(recurse=False): + return unittest.TextTestRunner(verbosity=0).run(buildtestsuite(recurse=recurse)) diff --git a/PACK/enchant/tokenize/__init__.py b/PACK/enchant/tokenize/__init__.py new file mode 100644 index 0000000..50514da --- /dev/null +++ b/PACK/enchant/tokenize/__init__.py @@ -0,0 +1,536 @@ +# pyenchant +# +# Copyright (C) 2004-2009, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + +enchant.tokenize: String tokenization functions for PyEnchant +================================================================ + +An important task in spellchecking is breaking up large bodies of +text into their constituent words, each of which is then checked +for correctness. This package provides Python functions to split +strings into words according to the rules of a particular language. + +Each tokenization function accepts a string as its only positional +argument, and returns an iterator that yields tuples of the following +form, one for each word found:: + + (,) + +The meanings of these fields should be clear: is the word +that was found and is the position within the text at which +the word began (zero indexed, of course). The function will work +on any string-like object that supports array-slicing; in particular +character-array objects from the 'array' module may be used. + +The iterator also provides the attribute 'offset' which gives the current +position of the tokenizer inside the string being split, and the method +'set_offset' for manually adjusting this position. This can be used for +example if the string's contents have changed during the tokenization +process. + +To obtain an appropriate tokenization function for the language +identified by , use the function 'get_tokenizer(tag)':: + + tknzr = get_tokenizer("en_US") + for (word,pos) in tknzr("text to be tokenized goes here") + do_something(word) + +This library is designed to be easily extendible by third-party +authors. To register a tokenization function for the language +, implement it as the function 'tokenize' within the +module enchant.tokenize.. The 'get_tokenizer' function +will automatically detect it. Note that the underscore must be +used as the tag component separator in this case, in order to +form a valid python module name. (e.g. "en_US" rather than "en-US") + +Currently, a tokenizer has only been implemented for the English +language. Based on the author's limited experience, this should +be at least partially suitable for other languages. + +This module also provides various implementations of "Chunkers" and +"Filters". These classes are designed to make it easy to work with +text in a vareity of common formats, by detecting and excluding parts +of the text that don't need to be checked. + +A Chunker is a class designed to break a body of text into large chunks +of checkable content; for example the HTMLChunker class extracts the +text content from all HTML tags but excludes the tags themselves. +A Filter is a class designed to skip individual words during the checking +process; for example the URLFilter class skips over any words that +have the format of a URL. + +For exmaple, to spellcheck an HTML document it is necessary to split the +text into chunks based on HTML tags, and to filter out common word forms +such as URLs and WikiWords. This would look something like the following:: + + tknzr = get_tokenier("en_US",(HTMLChunker,),(URLFilter,WikiWordFilter))) + + text = "the url is http://example.com" + for (word,pos) in tknzer(text): + ...check each word and react accordingly... + +""" +_DOC_ERRORS = ["pos", "pos", "tknzr", "URLFilter", "WikiWordFilter", + "tkns", "tknzr", "pos", "tkns"] + +import re +import warnings + +import enchant +from enchant.utils import next, xrange +from enchant.errors import * + +# For backwards-compatability. This will eventually be removed, but how +# does one mark a module-level constant as deprecated? +Error = TokenizerNotFoundError + + +class tokenize: + """Base class for all tokenizer objects. + + Each tokenizer must be an iterator and provide the 'offset' + attribute as described in the documentation for this module. + + While tokenizers are in fact classes, they should be treated + like functions, and so are named using lower_case rather than + the CamelCase more traditional of class names. + """ + _DOC_ERRORS = ["CamelCase"] + + def __init__(self, text): + self._text = text + self._offset = 0 + + def __next__(self): + return self.next() + + def next(self): + raise NotImplementedError() + + def __iter__(self): + return self + + def set_offset(self, offset, replaced=False): + self._offset = offset + + def _get_offset(self): + return self._offset + + def _set_offset(self, offset): + msg = "changing a tokenizers 'offset' attribute is deprecated;" \ + " use the 'set_offset' method" + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + self.set_offset(offset) + + offset = property(_get_offset, _set_offset) + + +def get_tokenizer(tag=None, chunkers=None, filters=None): + """Locate an appropriate tokenizer by language tag. + + This requires importing the function 'tokenize' from an appropriate + module. Modules tried are named after the language tag, tried in the + following order: + * the entire tag (e.g. "en_AU.py") + * the base country code of the tag (e.g. "en.py") + + If the language tag is None, a default tokenizer (actually the English + one) is returned. It's unicode aware and should work OK for most + latin-derived languages. + + If a suitable function cannot be found, raises TokenizerNotFoundError. + + If given and not None, 'chunkers' and 'filters' must be lists of chunker + classes and filter classes resectively. These will be applied to the + tokenizer during creation. + """ + if tag is None: + tag = "en" + # "filters" used to be the second argument. Try to catch cases + # where it is given positionally and issue a DeprecationWarning. + if chunkers is not None and filters is None: + chunkers = list(chunkers) + if chunkers: + try: + chunkers_are_filters = issubclass(chunkers[0], Filter) + except TypeError: + pass + else: + if chunkers_are_filters: + msg = "passing 'filters' as a non-keyword argument " \ + "to get_tokenizer() is deprecated" + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + filters = chunkers + chunkers = None + # Ensure only '_' used as separator + tag = tag.replace("-", "_") + # First try the whole tag + tkFunc = _try_tokenizer(tag) + if tkFunc is None: + # Try just the base + base = tag.split("_")[0] + tkFunc = _try_tokenizer(base) + if tkFunc is None: + msg = "No tokenizer found for language '%s'" % (tag,) + raise TokenizerNotFoundError(msg) + # Given the language-specific tokenizer, we now build up the + # end result as follows: + # * chunk the text using any given chunkers in turn + # * begin with basic whitespace tokenization + # * apply each of the given filters in turn + # * apply language-specific rules + tokenizer = basic_tokenize + if chunkers is not None: + chunkers = list(chunkers) + for i in xrange(len(chunkers) - 1, -1, -1): + tokenizer = wrap_tokenizer(chunkers[i], tokenizer) + if filters is not None: + for f in filters: + tokenizer = f(tokenizer) + tokenizer = wrap_tokenizer(tokenizer, tkFunc) + return tokenizer + + +get_tokenizer._DOC_ERRORS = ["py", "py"] + + +class empty_tokenize(tokenize): + """Tokenizer class that yields no elements.""" + _DOC_ERRORS = [] + + def __init__(self): + tokenize.__init__(self, "") + + def next(self): + raise StopIteration() + + +class unit_tokenize(tokenize): + """Tokenizer class that yields the text as a single token.""" + _DOC_ERRORS = [] + + def __init__(self, text): + tokenize.__init__(self, text) + self._done = False + + def next(self): + if self._done: + raise StopIteration() + self._done = True + return (self._text, 0) + + +class basic_tokenize(tokenize): + """Tokenizer class that performs very basic word-finding. + + This tokenizer does the most basic thing that could work - it splits + text into words based on whitespace boundaries, and removes basic + punctuation symbols from the start and end of each word. + """ + _DOC_ERRORS = [] + + # Chars to remove from start/end of words + strip_from_start = '"' + "'`([" + strip_from_end = '"' + "'`]).!,?;:" + + def next(self): + text = self._text + offset = self._offset + while True: + if offset >= len(text): + break + # Find start of next word + while offset < len(text) and text[offset].isspace(): + offset += 1 + sPos = offset + # Find end of word + while offset < len(text) and not text[offset].isspace(): + offset += 1 + ePos = offset + self._offset = offset + # Strip chars from font/end of word + while sPos < len(text) and text[sPos] in self.strip_from_start: + sPos += 1 + while 0 < ePos and text[ePos - 1] in self.strip_from_end: + ePos -= 1 + # Return if word isnt empty + if (sPos < ePos): + return (text[sPos:ePos], sPos) + raise StopIteration() + + +def _try_tokenizer(modName): + """Look for a tokenizer in the named module. + + Returns the function if found, None otherwise. + """ + modBase = "enchant.tokenize." + funcName = "tokenize" + modName = modBase + modName + try: + mod = __import__(modName, globals(), {}, funcName) + return getattr(mod, funcName) + except ImportError: + return None + + +def wrap_tokenizer(tk1, tk2): + """Wrap one tokenizer inside another. + + This function takes two tokenizer functions 'tk1' and 'tk2', + and returns a new tokenizer function that passes the output + of tk1 through tk2 before yielding it to the calling code. + """ + # This logic is already implemented in the Filter class. + # We simply use tk2 as the _split() method for a filter + # around tk1. + tkW = Filter(tk1) + tkW._split = tk2 + return tkW + + +wrap_tokenizer._DOC_ERRORS = ["tk", "tk", "tk", "tk"] + + +class Chunker(tokenize): + """Base class for text chunking functions. + + A chunker is designed to chunk text into large blocks of tokens. It + has the same interface as a tokenizer but is for a different purpose. + """ + pass + + +class Filter(object): + """Base class for token filtering functions. + + A filter is designed to wrap a tokenizer (or another filter) and do + two things: + + * skip over tokens + * split tokens into sub-tokens + + Subclasses have two basic options for customising their behaviour. The + method _skip(word) may be overridden to return True for words that + should be skipped, and false otherwise. The method _split(word) may + be overridden as tokenization function that will be applied to further + tokenize any words that aren't skipped. + """ + + def __init__(self, tokenizer): + """Filter class constructor.""" + self._tokenizer = tokenizer + + def __call__(self, *args, **kwds): + tkn = self._tokenizer(*args, **kwds) + return self._TokenFilter(tkn, self._skip, self._split) + + def _skip(self, word): + """Filter method for identifying skippable tokens. + + If this method returns true, the given word will be skipped by + the filter. This should be overridden in subclasses to produce the + desired functionality. The default behaviour is not to skip any words. + """ + return False + + def _split(self, word): + """Filter method for sub-tokenization of tokens. + + This method must be a tokenization function that will split the + given word into sub-tokens according to the needs of the filter. + The default behaviour is not to split any words. + """ + return unit_tokenize(word) + + class _TokenFilter(object): + """Private inner class implementing the tokenizer-wrapping logic. + + This might seem convoluted, but we're trying to create something + akin to a meta-class - when Filter(tknzr) is called it must return + a *callable* that can then be applied to a particular string to + perform the tokenization. Since we need to manage a lot of state + during tokenization, returning a class is the best option. + """ + _DOC_ERRORS = ["tknzr"] + + def __init__(self, tokenizer, skip, split): + self._skip = skip + self._split = split + self._tokenizer = tokenizer + # for managing state of sub-tokenization + self._curtok = empty_tokenize() + self._curword = "" + self._curpos = 0 + + def __iter__(self): + return self + + def __next__(self): + return self.next() + + def next(self): + # Try to get the next sub-token from word currently being split. + # If unavailable, move on to the next word and try again. + try: + (word, pos) = next(self._curtok) + return (word, pos + self._curpos) + except StopIteration: + (word, pos) = next(self._tokenizer) + while self._skip(word): + (word, pos) = next(self._tokenizer) + self._curword = word + self._curpos = pos + self._curtok = self._split(word) + return self.next() + + # Pass on access to 'offset' to the underlying tokenizer. + def _get_offset(self): + return self._tokenizer.offset + + def _set_offset(self, offset): + msg = "changing a tokenizers 'offset' attribute is deprecated;" \ + " use the 'set_offset' method" + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + self.set_offset(offset) + + offset = property(_get_offset, _set_offset) + + def set_offset(self, val, replaced=False): + self._tokenizer.set_offset(val, replaced=replaced) + # If we stay within the current word, also set on _curtok. + # Otherwise, throw away _curtok and set to empty iterator. + subval = val - self._curpos + if subval >= 0 and subval < len(self._curword) and not replaced: + self._curtok.set_offset(subval) + else: + self._curtok = empty_tokenize() + self._curword = "" + self._curpos = 0 + + +# Pre-defined chunkers and filters start here + +class URLFilter(Filter): + """Filter skipping over URLs. + This filter skips any words matching the following regular expression: + + ^[a-zA-z]+:\/\/[^\s].* + + That is, any words that are URLs. + """ + _DOC_ERRORS = ["zA"] + _pattern = re.compile(r"^[a-zA-z]+:\/\/[^\s].*") + + def _skip(self, word): + if self._pattern.match(word): + return True + return False + + +class WikiWordFilter(Filter): + """Filter skipping over WikiWords. + This filter skips any words matching the following regular expression: + + ^([A-Z]\w+[A-Z]+\w+) + + That is, any words that are WikiWords. + """ + _pattern = re.compile(r"^([A-Z]\w+[A-Z]+\w+)") + + def _skip(self, word): + if self._pattern.match(word): + return True + return False + + +class EmailFilter(Filter): + """Filter skipping over email addresses. + This filter skips any words matching the following regular expression: + + ^.+@[^\.].*\.[a-z]{2,}$ + + That is, any words that resemble email addresses. + """ + _pattern = re.compile(r"^.+@[^\.].*\.[a-z]{2,}$") + + def _skip(self, word): + if self._pattern.match(word): + return True + return False + + +class HTMLChunker(Chunker): + """Chunker for breaking up HTML documents into chunks of checkable text. + + The operation of this chunker is very simple - anything between a "<" + and a ">" will be ignored. Later versions may improve the algorithm + slightly. + """ + + def next(self): + text = self._text + offset = self.offset + while True: + if offset >= len(text): + break + # Skip to the end of the current tag, if any. + if text[offset] == "<": + maybeTag = offset + if self._is_tag(text, offset): + while text[offset] != ">": + offset += 1 + if offset == len(text): + offset = maybeTag + 1 + break + else: + offset += 1 + else: + offset = maybeTag + 1 + sPos = offset + # Find the start of the next tag. + while offset < len(text) and text[offset] != "<": + offset += 1 + ePos = offset + self._offset = offset + # Return if chunk isnt empty + if (sPos < offset): + return (text[sPos:offset], sPos) + raise StopIteration() + + def _is_tag(self, text, offset): + if offset + 1 < len(text): + if text[offset + 1].isalpha(): + return True + if text[offset + 1] == "/": + return True + return False + +# TODO: LaTeXChunker diff --git a/PACK/enchant/tokenize/en.py b/PACK/enchant/tokenize/en.py new file mode 100644 index 0000000..8ee1204 --- /dev/null +++ b/PACK/enchant/tokenize/en.py @@ -0,0 +1,172 @@ +# pyenchant +# +# Copyright (C) 2004-2008, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + + enchant.tokenize.en: Tokenizer for the English language + + This module implements a PyEnchant text tokenizer for the English + language, based on very simple rules. + +""" + +import unicodedata + +import enchant.tokenize +from enchant.utils import unicode + + +class tokenize(enchant.tokenize.tokenize): + """Iterator splitting text into words, reporting position. + + This iterator takes a text string as input, and yields tuples + representing each distinct word found in the text. The tuples + take the form: + + (,) + + Where is the word string found and is the position + of the start of the word within the text. + + The optional argument may be used to specify a + list of additional characters that can form part of a word. + By default, this list contains only the apostrophe ('). Note that + these characters cannot appear at the start or end of a word. + """ + + _DOC_ERRORS = ["pos", "pos"] + + def __init__(self, text, valid_chars=("'",)): + self._valid_chars = valid_chars + self._text = text + self._offset = 0 + # Select proper implementation of self._consume_alpha. + # 'text' isn't necessarily a string (it could be e.g. a mutable array) + # so we can't use isinstance(text,unicode) to detect unicode. + # Instead we typetest the first character of the text. + # If there's no characters then it doesn't matter what implementation + # we use since it won't be called anyway. + try: + char1 = text[0] + except IndexError: + self._consume_alpha = self._consume_alpha_b + else: + if isinstance(char1, unicode): + self._consume_alpha = self._consume_alpha_u + else: + self._consume_alpha = self._consume_alpha_b + + def _consume_alpha_b(self, text, offset): + """Consume an alphabetic character from the given bytestring. + + Given a bytestring and the current offset, this method returns + the number of characters occupied by the next alphabetic character + in the string. Non-ASCII bytes are interpreted as utf-8 and can + result in multiple characters being consumed. + """ + assert offset < len(text) + if text[offset].isalpha(): + return 1 + elif text[offset] >= "\x80": + return self._consume_alpha_utf8(text, offset) + return 0 + + def _consume_alpha_utf8(self, text, offset): + """Consume a sequence of utf8 bytes forming an alphabetic character.""" + incr = 2 + u = "" + while not u and incr <= 4: + try: + try: + # In the common case this will be a string + u = text[offset:offset + incr].decode("utf8") + except AttributeError: + # Looks like it was e.g. a mutable char array. + try: + s = text[offset:offset + incr].tostring() + except AttributeError: + s = "".join([c for c in text[offset:offset + incr]]) + u = s.decode("utf8") + except UnicodeDecodeError: + incr += 1 + if not u: + return 0 + if u.isalpha(): + return incr + if unicodedata.category(u)[0] == "M": + return incr + return 0 + + def _consume_alpha_u(self, text, offset): + """Consume an alphabetic character from the given unicode string. + + Given a unicode string and the current offset, this method returns + the number of characters occupied by the next alphabetic character + in the string. Trailing combining characters are consumed as a + single letter. + """ + assert offset < len(text) + incr = 0 + if text[offset].isalpha(): + incr = 1 + while offset + incr < len(text): + if unicodedata.category(text[offset + incr])[0] != "M": + break + incr += 1 + return incr + + def next(self): + text = self._text + offset = self._offset + while offset < len(text): + # Find start of next word (must be alpha) + while offset < len(text): + incr = self._consume_alpha(text, offset) + if incr: + break + offset += 1 + curPos = offset + # Find end of word using, allowing valid_chars + while offset < len(text): + incr = self._consume_alpha(text, offset) + if not incr: + if text[offset] in self._valid_chars: + incr = 1 + else: + break + offset += incr + # Return if word isnt empty + if (curPos != offset): + # Make sure word doesn't end with a valid_char + while text[offset - 1] in self._valid_chars: + offset = offset - 1 + self._offset = offset + return (text[curPos:offset], curPos) + self._offset = offset + raise StopIteration() diff --git a/PACK/enchant/tokenize/tests.py b/PACK/enchant/tokenize/tests.py new file mode 100644 index 0000000..569b593 --- /dev/null +++ b/PACK/enchant/tokenize/tests.py @@ -0,0 +1,326 @@ +# pyenchant +# +# Copyright (C) 2004-2008, Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + + enchant.tokenize.tests: unittests for enchant tokenization functions. + +""" + +import unittest +import array + +from enchant.tokenize import * +from enchant.tokenize.en import tokenize as tokenize_en +from enchant.utils import raw_unicode, unicode, bytes + + +class TestTokenization(unittest.TestCase): + """TestCases for testing the basic tokenization functionality.""" + + def test_basic_tokenize(self): + """Simple regression test for basic white-space tokenization.""" + input = """This is a paragraph. It's not very special, but it's designed +2 show how the splitter works with many-different combos +of words. Also need to "test" the (handling) of 'quoted' words.""" + output = [ + ("This", 0), ("is", 5), ("a", 8), ("paragraph", 10), ("It's", 22), + ("not", 27), ("very", 31), ("special", 36), ("but", 45), ("it's", 49), + ("designed", 54), ("2", 63), ("show", 65), ("how", 70), ("the", 74), + ("splitter", 78), ("works", 87), ("with", 93), ("many-different", 98), + ("combos", 113), ("of", 120), ("words", 123), + ("Also", 130), ("need", 135), + ("to", 140), ("test", 144), ("the", 150), ("handling", 155), + ("of", 165), ("quoted", 169), ("words", 177) + ] + self.assertEqual(output, [i for i in basic_tokenize(input)]) + for (itmO, itmV) in zip(output, basic_tokenize(input)): + self.assertEqual(itmO, itmV) + + def test_tokenize_strip(self): + """Test special-char-stripping edge-cases in basic_tokenize.""" + input = "((' \"\" 'text' has (lots) of (special chars} >>]" + output = [("", 4), ("text", 15), ("has", 21), ("lots", 26), ("of", 32), + ("special", 36), ("chars}", 44), (">>", 51)] + self.assertEqual(output, [i for i in basic_tokenize(input)]) + for (itmO, itmV) in zip(output, basic_tokenize(input)): + self.assertEqual(itmO, itmV) + + def test_wrap_tokenizer(self): + """Test wrapping of one tokenizer with another.""" + input = "this-string will be split@according to diff'rnt rules" + from enchant.tokenize import en + tknzr = wrap_tokenizer(basic_tokenize, en.tokenize) + tknzr = tknzr(input) + self.assertEqual(tknzr._tokenizer.__class__, basic_tokenize) + self.assertEqual(tknzr._tokenizer.offset, 0) + for (n, (word, pos)) in enumerate(tknzr): + if n == 0: + self.assertEqual(pos, 0) + self.assertEqual(word, "this") + if n == 1: + self.assertEqual(pos, 5) + self.assertEqual(word, "string") + if n == 2: + self.assertEqual(pos, 12) + self.assertEqual(word, "will") + # Test setting offset to a previous token + tknzr.set_offset(5) + self.assertEqual(tknzr.offset, 5) + self.assertEqual(tknzr._tokenizer.offset, 5) + self.assertEqual(tknzr._curtok.__class__, empty_tokenize) + if n == 3: + self.assertEqual(word, "string") + self.assertEqual(pos, 5) + if n == 4: + self.assertEqual(pos, 12) + self.assertEqual(word, "will") + if n == 5: + self.assertEqual(pos, 17) + self.assertEqual(word, "be") + # Test setting offset past the current token + tknzr.set_offset(20) + self.assertEqual(tknzr.offset, 20) + self.assertEqual(tknzr._tokenizer.offset, 20) + self.assertEqual(tknzr._curtok.__class__, empty_tokenize) + if n == 6: + self.assertEqual(pos, 20) + self.assertEqual(word, "split") + if n == 7: + self.assertEqual(pos, 26) + self.assertEqual(word, "according") + # Test setting offset to middle of current token + tknzr.set_offset(23) + self.assertEqual(tknzr.offset, 23) + self.assertEqual(tknzr._tokenizer.offset, 23) + self.assertEqual(tknzr._curtok.offset, 3) + if n == 8: + self.assertEqual(pos, 23) + self.assertEqual(word, "it") + # OK, I'm pretty happy with the behaviour, no need to + # continue testing the rest of the string + + +class TestFilters(unittest.TestCase): + """TestCases for the various Filter subclasses.""" + + text = """this text with http://url.com and SomeLinksLike + ftp://my.site.com.au/some/file AndOthers not:/quite.a.url + with-an@aemail.address as well""" + + def setUp(self): + pass + + def test_URLFilter(self): + """Test filtering of URLs""" + tkns = get_tokenizer("en_US", filters=(URLFilter,))(self.text) + out = [t for t in tkns] + exp = [("this", 0), ("text", 5), ("with", 10), ("and", 30), + ("SomeLinksLike", 34), ("AndOthers", 93), ("not", 103), ("quite", 108), + ("a", 114), ("url", 116), ("with", 134), ("an", 139), ("aemail", 142), + ("address", 149), ("as", 157), ("well", 160)] + self.assertEqual(out, exp) + + def test_WikiWordFilter(self): + """Test filtering of WikiWords""" + tkns = get_tokenizer("en_US", filters=(WikiWordFilter,))(self.text) + out = [t for t in tkns] + exp = [("this", 0), ("text", 5), ("with", 10), ("http", 15), ("url", 22), ("com", 26), + ("and", 30), ("ftp", 62), ("my", 68), ("site", 71), ("com", 76), ("au", 80), + ("some", 83), ("file", 88), ("not", 103), ("quite", 108), + ("a", 114), ("url", 116), ("with", 134), ("an", 139), ("aemail", 142), + ("address", 149), ("as", 157), ("well", 160)] + self.assertEqual(out, exp) + + def test_EmailFilter(self): + """Test filtering of email addresses""" + tkns = get_tokenizer("en_US", filters=(EmailFilter,))(self.text) + out = [t for t in tkns] + exp = [("this", 0), ("text", 5), ("with", 10), ("http", 15), ("url", 22), ("com", 26), + ("and", 30), ("SomeLinksLike", 34), + ("ftp", 62), ("my", 68), ("site", 71), ("com", 76), ("au", 80), + ("some", 83), ("file", 88), ("AndOthers", 93), ("not", 103), ("quite", 108), + ("a", 114), ("url", 116), + ("as", 157), ("well", 160)] + self.assertEqual(out, exp) + + def test_CombinedFilter(self): + """Test several filters combined""" + tkns = get_tokenizer("en_US", filters=(URLFilter, WikiWordFilter, EmailFilter))(self.text) + out = [t for t in tkns] + exp = [("this", 0), ("text", 5), ("with", 10), + ("and", 30), ("not", 103), ("quite", 108), + ("a", 114), ("url", 116), + ("as", 157), ("well", 160)] + self.assertEqual(out, exp) + + +class TestChunkers(unittest.TestCase): + """TestCases for the various Chunker subclasses.""" + + def test_HTMLChunker(self): + """Test filtering of URLs""" + text = """hellomy titlethis is a + simple HTML document for

testing purposes

. + It < contains > various <-- special characters. + """ + tkns = get_tokenizer("en_US", chunkers=(HTMLChunker,))(text) + out = [t for t in tkns] + exp = [("hello", 0), ("my", 24), ("title", 27), ("this", 53), ("is", 58), + ("a", 61), ("simple", 82), ("HTML", 93), ("document", 98), ("for", 107), + ("test", 115), ("ing", 122), ("purposes", 130), ("It", 160), + ("contains", 165), ("various", 176), ("special", 188), + ("characters", 196)] + self.assertEqual(out, exp) + for (word, pos) in out: + self.assertEqual(text[pos:pos + len(word)], word) + + +class TestTokenizeEN(unittest.TestCase): + """TestCases for checking behaviour of English tokenization.""" + + def test_tokenize_en(self): + """Simple regression test for English tokenization.""" + input = """This is a paragraph. It's not very special, but it's designed +2 show how the splitter works with many-different combos +of words. Also need to "test" the handling of 'quoted' words.""" + output = [ + ("This", 0), ("is", 5), ("a", 8), ("paragraph", 10), ("It's", 22), + ("not", 27), ("very", 31), ("special", 36), ("but", 45), ("it's", 49), + ("designed", 54), ("show", 65), ("how", 70), ("the", 74), + ("splitter", 78), ("works", 87), ("with", 93), ("many", 98), + ("different", 103), ("combos", 113), ("of", 120), ("words", 123), + ("Also", 130), ("need", 135), + ("to", 140), ("test", 144), ("the", 150), ("handling", 154), + ("of", 163), ("quoted", 167), ("words", 175) + ] + for (itmO, itmV) in zip(output, tokenize_en(input)): + self.assertEqual(itmO, itmV) + + def test_unicodeBasic(self): + """Test tokenization of a basic unicode string.""" + input = raw_unicode( + r"Ik ben ge\u00EFnteresseerd in de co\u00F6rdinatie van mijn knie\u00EBn, maar kan niet \u00E9\u00E9n \u00E0 twee enqu\u00EAtes vinden die recht doet aan mijn carri\u00E8re op Cura\u00E7ao") + output = input.split(" ") + output[8] = output[8][0:-1] + for (itmO, itmV) in zip(output, tokenize_en(input)): + self.assertEqual(itmO, itmV[0]) + self.assertTrue(input[itmV[1]:].startswith(itmO)) + + def test_unicodeCombining(self): + """Test tokenization with unicode combining symbols.""" + input = raw_unicode( + r"Ik ben gei\u0308nteresseerd in de co\u00F6rdinatie van mijn knie\u00EBn, maar kan niet e\u0301e\u0301n \u00E0 twee enqu\u00EAtes vinden die recht doet aan mijn carri\u00E8re op Cura\u00E7ao") + output = input.split(" ") + output[8] = output[8][0:-1] + for (itmO, itmV) in zip(output, tokenize_en(input)): + self.assertEqual(itmO, itmV[0]) + self.assertTrue(input[itmV[1]:].startswith(itmO)) + + def test_utf8_bytes(self): + """Test tokenization of UTF8-encoded bytes (bug #2500184).""" + # Python3 doesn't support bytestrings, don't run this test + if str is unicode: + return + input = "A r\xc3\xa9sum\xc3\xa9, also spelled resum\xc3\xa9 or resume" + output = input.split(" ") + output[1] = output[1][0:-1] + for (itmO, itmV) in zip(output, tokenize_en(input)): + self.assertEqual(itmO, itmV[0]) + self.assertTrue(input[itmV[1]:].startswith(itmO)) + + def test_utf8_bytes_at_end(self): + """Test tokenization of UTF8-encoded bytes at end of word.""" + # Python3 doesn't support bytestrings, don't run this test + if str is unicode: + return + input = "A r\xc3\xa9sum\xc3\xa9, also spelled resum\xc3\xa9 or resume" + output = input.split(" ") + output[1] = output[1][0:-1] + for (itmO, itmV) in zip(output, tokenize_en(input)): + self.assertEqual(itmO, itmV[0]) + + def test_utf8_bytes_in_an_array(self): + """Test tokenization of UTF8-encoded bytes stored in an array.""" + # Python3 doesn't support bytestrings, don't run this test + if str is unicode: + return + input = "A r\xc3\xa9sum\xc3\xa9, also spelled resum\xc3\xa9 or resume" + output = input.split(" ") + output[1] = output[1][0:-1] + input = array.array('c', input) + output = [array.array('c', w) for w in output] + for (itmO, itmV) in zip(output, tokenize_en(array.array('c', input))): + self.assertEqual(itmO, itmV[0]) + self.assertEqual(input[itmV[1]:itmV[1] + len(itmV[0])], itmO) + + def test_bug1591450(self): + """Check for tokenization regressions identified in bug #1591450.""" + input = """Testing markup and {y:i}so-forth...leading dots and trail--- well, you get-the-point. Also check numbers: 999 1,000 12:00 .45. Done?""" + output = [ + ("Testing", 0), ("i", 9), ("markup", 11), ("i", 19), ("and", 22), + ("y", 27), ("i", 29), ("so", 31), ("forth", 34), ("leading", 42), + ("dots", 50), ("and", 55), ("trail", 59), ("well", 68), + ("you", 74), ("get", 78), ("the", 82), ("point", 86), + ("Also", 93), ("check", 98), ("numbers", 104), ("Done", 134), + ] + for (itmO, itmV) in zip(output, tokenize_en(input)): + self.assertEqual(itmO, itmV) + + def test_bug2785373(self): + """Testcases for bug #2785373""" + input = "So, one dey when I wes 17, I left." + for _ in tokenize_en(input): + pass + input = raw_unicode("So, one dey when I wes 17, I left.") + for _ in tokenize_en(input): + pass + + def test_finnish_text(self): + """Test tokenizing some Finnish text. + + This really should work since there are no special rules to apply, + just lots of non-ascii characters. + """ + inputT = raw_unicode( + 'T\\xe4m\\xe4 on kappale. Eip\\xe4 ole kovin 2 nen, mutta tarkoitus on n\\xe4ytt\\xe4\\xe4 miten sanastaja \\ntoimii useiden-erilaisten sanarypp\\xe4iden kimpussa.\\nPit\\xe4\\xe4p\\xe4 viel\\xe4 \'tarkistaa\' sanat jotka "lainausmerkeiss\\xe4". Heittomerkki ja vaa\'an.\\nUlkomaisia sanoja s\\xfcss, spa\\xdf.') + outputT = [ + (raw_unicode('T\\xe4m\\xe4'), 0), (raw_unicode('on'), 5), (raw_unicode('kappale'), 8), + (raw_unicode('Eip\\xe4'), 17), (raw_unicode('ole'), 22), (raw_unicode('kovin'), 26), + (raw_unicode('nen'), 34), (raw_unicode('mutta'), 39), (raw_unicode('tarkoitus'), 45), + (raw_unicode('on'), 55), (raw_unicode('n\\xe4ytt\\xe4\\xe4'), 58), (raw_unicode('miten'), 66), + (raw_unicode('sanastaja'), 72), (raw_unicode('toimii'), 83), (raw_unicode('useiden'), 90), + (raw_unicode('erilaisten'), 98), (raw_unicode('sanarypp\\xe4iden'), 109), (raw_unicode('kimpussa'), 123), + (raw_unicode('Pit\\xe4\\xe4p\\xe4'), 133), (raw_unicode('viel\\xe4'), 141), (raw_unicode('tarkistaa'), 148), + (raw_unicode('sanat'), 159), (raw_unicode('jotka'), 165), (raw_unicode('lainausmerkeiss\\xe4'), 172), + (raw_unicode('Heittomerkki'), 191), (raw_unicode('ja'), 204), (raw_unicode("vaa'an"), 207), + (raw_unicode('Ulkomaisia'), 215), (raw_unicode('sanoja'), 226), (raw_unicode('s\\xfcss'), 233), + (raw_unicode('spa\\xdf'), 239), ] + for (itmO, itmV) in zip(outputT, tokenize_en(inputT)): + self.assertEqual(itmO, itmV) diff --git a/PACK/enchant/utils.py b/PACK/enchant/utils.py new file mode 100644 index 0000000..e919fd7 --- /dev/null +++ b/PACK/enchant/utils.py @@ -0,0 +1,354 @@ +# pyenchant +# +# Copyright (C) 2004-2008 Ryan Kelly +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# In addition, as a special exception, you are +# given permission to link the code of this program with +# non-LGPL Spelling Provider libraries (eg: a MSFT Office +# spell checker backend) and distribute linked combinations including +# the two. You must obey the GNU Lesser General Public License in all +# respects for all of the code used other than said providers. If you modify +# this file, you may extend this exception to your version of the +# file, but you are not obligated to do so. If you do not wish to +# do so, delete this exception statement from your version. +# +""" + +enchant.utils: Misc utilities for the enchant package +======================================================== + +This module provides miscellaneous utilities for use with the +enchant spellchecking package. Currently available functionality +includes: + + * string/unicode compatibility wrappers + * functions for dealing with locale/language settings + * ability to list supporting data files (win32 only) + * functions for bundling supporting data files from a build + +""" + +import os +import sys +import codecs + +from enchant.errors import * + +# Attempt to access local language information +try: + import locale +except ImportError: + locale = None + +# +# Unicode/Bytes compatabilty wrappers. +# +# These allow us to support both Python 2.x and Python 3.x from +# the same codebase. +# +# We provide explicit type objects "bytes" and "unicode" that can be +# used to construct instances of the appropriate type. The class +# "EnchantStr" derives from the default "str" type and implements the +# necessary logic for encoding/decoding as strings are passed into +# the underlying C library (where they must always be utf-8 encoded +# byte strings). +# + +try: + unicode = unicode +except NameError: + str = str + unicode = str + bytes = bytes + basestring = (str, bytes) +else: + str = str + unicode = unicode + bytes = str + basestring = basestring + + +def raw_unicode(raw): + """Make a unicode string from a raw string. + + This function takes a string containing unicode escape characters, + and returns the corresponding unicode string. Useful for writing + unicode string literals in your python source while being upwards- + compatible with Python 3. For example, instead of doing this: + + s = u"hello\u2149" # syntax error in Python 3 + + Or this: + + s = "hello\u2149" # not what you want in Python 2.x + + You can do this: + + s = raw_unicode(r"hello\u2149") # works everywhere! + + """ + return raw.encode("utf8").decode("unicode-escape") + + +def raw_bytes(raw): + """Make a bytes object out of a raw string. + + This is analogous to raw_unicode, but processes byte escape characters + to produce a bytes object. + """ + return codecs.escape_decode(raw)[0] + + +class EnchantStr(str): + """String subclass for interfacing with enchant C library. + + This class encapsulates the logic for interfacing between python native + string/unicode objects and the underlying enchant library, which expects + all strings to be UTF-8 character arrays. It is a subclass of the + default string class 'str' - on Python 2.x that makes it an ascii string, + on Python 3.x it is a unicode object. + + Initialise it with a string or unicode object, and use the encode() method + to obtain an object suitable for passing to the underlying C library. + When strings are read back into python, use decode(s) to translate them + back into the appropriate python-level string type. + + This allows us to following the common Python 2.x idiom of returning + unicode when unicode is passed in, and byte strings otherwise. It also + lets the interface be upwards-compatible with Python 3, in which string + objects are unicode by default. + """ + + def __new__(cls, value): + """EnchantStr data constructor. + + This method records whether the initial string was unicode, then + simply passes it along to the default string constructor. + """ + if type(value) is unicode: + was_unicode = True + if str is not unicode: + value = value.encode("utf-8") + else: + was_unicode = False + if str is not bytes: + raise Error("Don't pass bytestrings to pyenchant") + self = str.__new__(cls, value) + self._was_unicode = was_unicode + return self + + def encode(self): + """Encode this string into a form usable by the enchant C library.""" + if str is unicode: + return str.encode(self, "utf-8") + else: + return self + + def decode(self, value): + """Decode a string returned by the enchant C library.""" + if self._was_unicode: + if str is unicode: + # On some python3 versions, ctypes converts c_char_p + # to str() rather than bytes() + if isinstance(value, str): + value = value.encode() + return value.decode("utf-8") + else: + return value.decode("utf-8") + else: + return value + + +def printf(values, sep=" ", end="\n", file=None): + """Compatability wrapper from print statement/function. + + This function is a simple Python2/Python3 compatability wrapper + for printing to stdout. + """ + if file is None: + file = sys.stdout + file.write(sep.join(map(str, values))) + file.write(end) + + +try: + next = next +except NameError: + def next(iter): + """Compatability wrapper for advancing an iterator.""" + return iter.next() + +try: + xrange = xrange +except NameError: + xrange = range + + +# +# Other useful functions. +# + + +def levenshtein(s1, s2): + """Calculate the Levenshtein distance between two strings. + + This is straight from Wikipedia. + """ + if len(s1) < len(s2): + return levenshtein(s2, s1) + if not s1: + return len(s2) + + previous_row = xrange(len(s2) + 1) + for i, c1 in enumerate(s1): + current_row = [i + 1] + for j, c2 in enumerate(s2): + insertions = previous_row[j + 1] + 1 + deletions = current_row[j] + 1 + substitutions = previous_row[j] + (c1 != c2) + current_row.append(min(insertions, deletions, substitutions)) + previous_row = current_row + + return previous_row[-1] + + +def trim_suggestions(word, suggs, maxlen, calcdist=None): + """Trim a list of suggestions to a maximum length. + + If the list of suggested words is too long, you can use this function + to trim it down to a maximum length. It tries to keep the "best" + suggestions based on similarity to the original word. + + If the optional "calcdist" argument is provided, it must be a callable + taking two words and returning the distance between them. It will be + used to determine which words to retain in the list. The default is + a simple Levenshtein distance. + """ + if calcdist is None: + calcdist = levenshtein + decorated = [(calcdist(word, s), s) for s in suggs] + decorated.sort() + return [s for (l, s) in decorated[:maxlen]] + + +def get_default_language(default=None): + """Determine the user's default language, if possible. + + This function uses the 'locale' module to try to determine + the user's preferred language. The return value is as + follows: + + * if a locale is available for the LC_MESSAGES category, + that language is used + * if a default locale is available, that language is used + * if the keyword argument is given, it is used + * if nothing else works, None is returned + + Note that determining the user's language is in general only + possible if they have set the necessary environment variables + on their system. + """ + try: + import locale + tag = locale.getlocale()[0] + if tag is None: + tag = locale.getdefaultlocale()[0] + if tag is None: + raise Error("No default language available") + return tag + except Exception: + pass + return default + + +get_default_language._DOC_ERRORS = ["LC"] + + +def get_resource_filename(resname): + """Get the absolute path to the named resource file. + + This serves widely the same purpose as pkg_resources.resource_filename(), + but tries to avoid loading pkg_resources unless we're actually in + an egg. + """ + path = os.path.dirname(os.path.abspath(__file__)) + path = os.path.join(path, resname) + if os.path.exists(path): + return path + if hasattr(sys, "frozen"): + exe_path = unicode(sys.executable, sys.getfilesystemencoding()) + exe_dir = os.path.dirname(exe_path) + path = os.path.join(exe_dir, resname) + if os.path.exists(path): + return path + else: + import pkg_resources + try: + path = pkg_resources.resource_filename("enchant", resname) + except KeyError: + pass + else: + path = os.path.abspath(path) + if os.path.exists(path): + return path + raise Error("Could not locate resource '%s'" % (resname,)) + + +def win32_data_files(): + """Get list of supporting data files, for use with setup.py + + This function returns a list of the supporting data files available + to the running version of PyEnchant. This is in the format expected + by the data_files argument of the distutils setup function. It's + very useful, for example, for including the data files in an executable + produced by py2exe. + + Only really tested on the win32 platform (it's the only platform for + which we ship our own supporting data files) + """ + # Include the main enchant DLL + try: + libEnchant = get_resource_filename("libenchant.dll") + except Error: + libEnchant = get_resource_filename("libenchant-1.dll") + mainDir = os.path.dirname(libEnchant) + dataFiles = [('', [libEnchant])] + # And some specific supporting DLLs + for dll in os.listdir(mainDir): + if not dll.endswith(".dll"): + continue + for prefix in ("iconv", "intl", "libglib", "libgmodule"): + if dll.startswith(prefix): + break + else: + continue + dataFiles[0][1].append(os.path.join(mainDir, dll)) + # And anything found in the supporting data directories + dataDirs = ("share/enchant/myspell", "share/enchant/ispell", "lib/enchant") + for dataDir in dataDirs: + files = [] + fullDir = os.path.join(mainDir, os.path.normpath(dataDir)) + for fn in os.listdir(fullDir): + fullFn = os.path.join(fullDir, fn) + if os.path.isfile(fullFn): + files.append(fullFn) + dataFiles.append((dataDir, files)) + return dataFiles + + +win32_data_files._DOC_ERRORS = ["py", "py", "exe"] diff --git a/PACK/maskgen.py b/PACK/maskgen.py new file mode 100755 index 0000000..9c941f8 --- /dev/null +++ b/PACK/maskgen.py @@ -0,0 +1,312 @@ +#!/usr/bin/python +# MaskGen - Generate Password Masks +# +# This tool is part of PACK (Password Analysis and Cracking Kit) +# +# VERSION 0.0.3 +# +# Copyright (C) 2013 Peter Kacherginsky +# All rights reserved. +# +# Please see the attached LICENSE file for additional licensing information. + +import sys +import csv +import datetime +from operator import itemgetter +from optparse import OptionParser, OptionGroup + +VERSION = "0.0.3" + + +class MaskGen: + def __init__(self): + # Masks collections with meta data + self.masks = dict() + + self.target_time = None + self.output_file = None + + self.minlength = None + self.maxlength = None + self.mintime = None + self.maxtime = None + self.mincomplexity = None + self.maxcomplexity = None + self.minoccurrence = None + self.maxoccurrence = None + + # PPS (Passwords per Second) Cracking Speed + self.pps = 1000000000 + self.showmasks = False + + # Counter for total masks coverage + self.total_occurrence = 0 + + def getcomplexity(self, mask): + """ Return mask complexity. """ + count = 1 + for char in mask[1:].split("?"): + if char == "l": + count *= 26 + elif char == "u": + count *= 26 + elif char == "d": + count *= 10 + elif char == "s": + count *= 33 + elif char == "a": + count *= 95 + else: + print + "[!] Error, unknown mask ?%s in a mask %s" % (char, mask) + + return count + + def loadmasks(self, filename): + """ Load masks and apply filters. """ + maskReader = csv.reader(open(args[0], 'r'), delimiter=',', quotechar='"') + + for (mask, occurrence) in maskReader: + + if mask == "": continue + + mask_occurrence = int(occurrence) + mask_length = len(mask) / 2 + mask_complexity = self.getcomplexity(mask) + mask_time = mask_complexity / self.pps + + self.total_occurrence += mask_occurrence + + # Apply filters based on occurrence, length, complexity and time + if (self.minoccurrence == None or mask_occurrence >= self.minoccurrence) and \ + (self.maxoccurrence == None or mask_occurrence <= self.maxoccurrence) and \ + (self.mincomplexity == None or mask_complexity <= self.mincomplexity) and \ + (self.maxcomplexity == None or mask_complexity <= self.maxcomplexity) and \ + (self.mintime == None or mask_time <= self.mintime) and \ + (self.maxtime == None or mask_time <= self.maxtime) and \ + (self.maxlength == None or mask_length <= self.maxlength) and \ + (self.minlength == None or mask_length >= self.minlength): + self.masks[mask] = dict() + self.masks[mask]['length'] = mask_length + self.masks[mask]['occurrence'] = mask_occurrence + self.masks[mask]['complexity'] = 1 - mask_complexity + self.masks[mask]['time'] = mask_time + self.masks[mask]['optindex'] = 1 - mask_complexity / mask_occurrence + + def generate_masks(self, sorting_mode): + """ Generate optimal password masks sorted by occurrence, complexity or optindex """ + sample_count = 0 + sample_time = 0 + sample_occurrence = 0 + + # TODO Group by time here 1 minutes, 1 hour, 1 day, 1 month, 1 year.... + # Group by length 1,2,3,4,5,6,7,8,9,10.... + # Group by occurrence 10%, 20%, 30%, 40%, 50%.... + + if self.showmasks: print + "[L:] Mask: [ Occ: ] [ Time: ]" + for mask in sorted(self.masks.keys(), key=lambda mask: self.masks[mask][sorting_mode], reverse=True): + + if self.showmasks: + time_human = ">1 year" if self.masks[mask]['time'] > 60 * 60 * 24 * 365 else str( + datetime.timedelta(seconds=self.masks[mask]['time'])) + print + "[{:>2}] {:<30} [{:<7}] [{:>8}] ".format(self.masks[mask]['length'], mask, + self.masks[mask]['occurrence'], time_human) + + if self.output_file: + self.output_file.write("%s\n" % mask) + + sample_occurrence += self.masks[mask]['occurrence'] + sample_time += self.masks[mask]['time'] + sample_count += 1 + + if self.target_time and sample_time > self.target_time: + print + "[!] Target time exceeded." + break + + print + "[*] Finished generating masks:" + print + " Masks generated: %s" % sample_count + print + " Masks coverage: %d%% (%d/%d)" % ( + sample_occurrence * 100 / self.total_occurrence, sample_occurrence, self.total_occurrence) + time_human = ">1 year" if sample_time > 60 * 60 * 24 * 365 else str(datetime.timedelta(seconds=sample_time)) + print + " Masks runtime: %s" % time_human + + def getmaskscoverage(self, checkmasks): + + sample_count = 0 + sample_occurrence = 0 + + total_complexity = 0 + + if self.showmasks: print + "[L:] Mask: [ Occ: ] [ Time: ]" + for mask in checkmasks: + mask = mask.strip() + mask_complexity = self.getcomplexity(mask) + + total_complexity += mask_complexity + + if mask in self.masks: + + if self.showmasks: + time_human = ">1 year" if self.masks[mask]['time'] > 60 * 60 * 24 * 365 else str( + datetime.timedelta(seconds=self.masks[mask]['time'])) + print + "[{:>2}] {:<30} [{:<7}] [{:>8}] ".format(self.masks[mask]['length'], mask, + self.masks[mask]['occurrence'], time_human) + + if self.output_file: + self.output_file.write("%s\n" % mask) + + sample_occurrence += self.masks[mask]['occurrence'] + sample_count += 1 + + if self.target_time and total_complexity / self.pps > self.target_time: + print + "[!] Target time exceeded." + break + + # TODO: Something wrong here, complexity and time doesn't match with estimated from policygen + total_time = total_complexity / self.pps + time_human = ">1 year" if total_time > 60 * 60 * 24 * 365 else str(datetime.timedelta(seconds=total_time)) + print + "[*] Finished matching masks:" + print + " Masks matched: %s" % sample_count + print + " Masks coverage: %d%% (%d/%d)" % ( + sample_occurrence * 100 / self.total_occurrence, sample_occurrence, self.total_occurrence) + print + " Masks runtime: %s" % time_human + + +if __name__ == "__main__": + + header = " _ \n" + header += " MaskGen %s | |\n" % VERSION + header += " _ __ __ _ ___| | _\n" + header += " | '_ \ / _` |/ __| |/ /\n" + header += " | |_) | (_| | (__| < \n" + header += " | .__/ \__,_|\___|_|\_\\\n" + header += " | | \n" + header += " |_| iphelix@thesprawl.org\n" + header += "\n" + + parser = OptionParser("%prog pass0.masks [pass1.masks ...] [options]", version="%prog " + VERSION) + + parser.add_option("-t", "--targettime", dest="target_time", type="int", metavar="86400", + help="Target time of all masks (seconds)") + parser.add_option("-o", "--outputmasks", dest="output_masks", metavar="masks.hcmask", help="Save masks to a file") + + filters = OptionGroup(parser, "Individual Mask Filter Options") + filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length") + filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length") + filters.add_option("--mintime", dest="mintime", type="int", metavar="3600", help="Minimum mask runtime (seconds)") + filters.add_option("--maxtime", dest="maxtime", type="int", metavar="3600", help="Maximum mask runtime (seconds)") + filters.add_option("--mincomplexity", dest="mincomplexity", type="int", metavar="1", help="Minimum complexity") + filters.add_option("--maxcomplexity", dest="maxcomplexity", type="int", metavar="100", help="Maximum complexity") + filters.add_option("--minoccurrence", dest="minoccurrence", type="int", metavar="1", help="Minimum occurrence") + filters.add_option("--maxoccurrence", dest="maxoccurrence", type="int", metavar="100", help="Maximum occurrence") + parser.add_option_group(filters) + + sorting = OptionGroup(parser, "Mask Sorting Options") + sorting.add_option("--optindex", action="store_true", dest="optindex", help="sort by mask optindex (default)", + default=False) + sorting.add_option("--occurrence", action="store_true", dest="occurrence", help="sort by mask occurrence", + default=False) + sorting.add_option("--complexity", action="store_true", dest="complexity", help="sort by mask complexity", + default=False) + parser.add_option_group(sorting) + + coverage = OptionGroup(parser, "Check mask coverage") + coverage.add_option("--checkmasks", dest="checkmasks", help="check mask coverage", + metavar="?u?l?l?l?l?l?d,?l?l?l?l?l?d?d") + coverage.add_option("--checkmasksfile", dest="checkmasks_file", help="check mask coverage in a file", + metavar="masks.hcmask") + parser.add_option_group(coverage) + + parser.add_option("--showmasks", dest="showmasks", help="Show matching masks", action="store_true", default=False) + + misc = OptionGroup(parser, "Miscellaneous options") + misc.add_option("--pps", dest="pps", help="Passwords per Second", type="int", metavar="1000000000") + misc.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") + parser.add_option_group(misc) + + (options, args) = parser.parse_args() + + # Print program header + if not options.quiet: + print + header + + if len(args) < 1: + parser.error("no masks file specified! Please provide statsgen output.") + exit(1) + + print + "[*] Analyzing masks in [%s]" % args[0] + + maskgen = MaskGen() + + # Settings + if options.target_time: maskgen.target_time = options.target_time + if options.output_masks: + print + "[*] Saving generated masks to [%s]" % options.output_masks + maskgen.output_file = open(options.output_masks, 'w') + + # Filters + if options.minlength: maskgen.minlength = options.minlength + if options.maxlength: maskgen.maxlength = options.maxlength + if options.mintime: maskgen.mintime = options.mintime + if options.maxtime: maskgen.maxtime = options.maxtime + if options.mincomplexity: maskgen.mincomplexity = options.mincomplexity + if options.maxcomplexity: maskgen.maxcomplexity = options.maxcomplexity + if options.minoccurrence: maskgen.minoccurrence = options.minoccurrence + if options.maxoccurrence: maskgen.maxoccurrence = options.maxoccurrence + + # Misc + if options.pps: maskgen.pps = options.pps + if options.showmasks: maskgen.showmasks = options.showmasks + + print + "[*] Using {:,d} keys/sec for calculations.".format(maskgen.pps) + + # Load masks + for arg in args: + maskgen.loadmasks(arg) + + # Matching masks from the command-line + if options.checkmasks: + checkmasks = [m.strip() for m in options.checkmasks.split(',')] + print + "[*] Checking coverage of the these masks [%s]" % ", ".join(checkmasks) + maskgen.getmaskscoverage(checkmasks) + + # Matching masks from a file + elif options.checkmasks_file: + checkmasks_file = open(options.checkmasks_file, 'r') + print + "[*] Checking coverage of masks in [%s]" % options.checkmasks_file + maskgen.getmaskscoverage(checkmasks_file) + + # Printing masks in a file + else: + # Process masks according to specified sorting algorithm + if options.occurrence: + sorting_mode = "occurrence" + elif options.complexity: + sorting_mode = "complexity" + else: + sorting_mode = "optindex" + + print + "[*] Sorting masks by their [%s]." % sorting_mode + maskgen.generate_masks(sorting_mode) diff --git a/PACK/policygen.py b/PACK/policygen.py new file mode 100755 index 0000000..8451b3d --- /dev/null +++ b/PACK/policygen.py @@ -0,0 +1,243 @@ +#!/usr/bin/python +# PolicyGen - Analyze and Generate password masks according to a password policy +# +# This tool is part of PACK (Password Analysis and Cracking Kit) +# +# VERSION 0.0.2 +# +# Copyright (C) 2013 Peter Kacherginsky +# All rights reserved. +# +# Please see the attached LICENSE file for additional licensing information. + +import sys, string, random +import datetime +from optparse import OptionParser, OptionGroup +import itertools + +VERSION = "0.0.2" + + +class PolicyGen: + def __init__(self): + self.output_file = None + + self.minlength = 8 + self.maxlength = 8 + self.mindigit = None + self.minlower = None + self.minupper = None + self.minspecial = None + self.maxdigit = None + self.maxlower = None + self.maxupper = None + self.maxspecial = None + + # PPS (Passwords per Second) Cracking Speed + self.pps = 1000000000 + self.showmasks = False + + def getcomplexity(self, mask): + """ Return mask complexity. """ + count = 1 + for char in mask[1:].split("?"): + if char == "l": + count *= 26 + elif char == "u": + count *= 26 + elif char == "d": + count *= 10 + elif char == "s": + count *= 33 + elif char == "a": + count *= 95 + else: + print + "[!] Error, unknown mask ?%s in a mask %s" % (char, mask) + + return count + + def generate_masks(self, noncompliant): + """ Generate all possible password masks matching the policy """ + + total_count = 0 + sample_count = 0 + + # NOTE: It is better to collect total complexity + # not to lose precision when dividing by pps + total_complexity = 0 + sample_complexity = 0 + + # TODO: Randomize or even statistically arrange matching masks + for length in xrange(self.minlength, self.maxlength + 1): + print + "[*] Generating %d character password masks." % length + total_length_count = 0 + sample_length_count = 0 + + total_length_complexity = 0 + sample_length_complexity = 0 + + for masklist in itertools.product(['?d', '?l', '?u', '?s'], repeat=length): + + mask = ''.join(masklist) + + lowercount = 0 + uppercount = 0 + digitcount = 0 + specialcount = 0 + + mask_complexity = self.getcomplexity(mask) + + total_length_count += 1 + total_length_complexity += mask_complexity + + # Count charachter types in a mask + for char in mask[1:].split("?"): + if char == "l": + lowercount += 1 + elif char == "u": + uppercount += 1 + elif char == "d": + digitcount += 1 + elif char == "s": + specialcount += 1 + + # Filter according to password policy + # NOTE: Perform exact opposite (XOR) operation if noncompliant + # flag was set when calling the function. + if ((self.minlower == None or lowercount >= self.minlower) and \ + (self.maxlower == None or lowercount <= self.maxlower) and \ + (self.minupper == None or uppercount >= self.minupper) and \ + (self.maxupper == None or uppercount <= self.maxupper) and \ + (self.mindigit == None or digitcount >= self.mindigit) and \ + (self.maxdigit == None or digitcount <= self.maxdigit) and \ + (self.minspecial == None or specialcount >= self.minspecial) and \ + (self.maxspecial == None or specialcount <= self.maxspecial)) ^ noncompliant: + + sample_length_count += 1 + sample_length_complexity += mask_complexity + + if self.showmasks: + mask_time = mask_complexity / self.pps + time_human = ">1 year" if mask_time > 60 * 60 * 24 * 365 else str( + datetime.timedelta(seconds=mask_time)) + print + "[{:>2}] {:<30} [l:{:>2} u:{:>2} d:{:>2} s:{:>2}] [{:>8}] ".format(length, mask, lowercount, + uppercount, digitcount, + specialcount, time_human) + + if self.output_file: + self.output_file.write("%s\n" % mask) + + total_count += total_length_count + sample_count += sample_length_count + + total_complexity += total_length_complexity + sample_complexity += sample_length_complexity + + total_time = total_complexity / self.pps + total_time_human = ">1 year" if total_time > 60 * 60 * 24 * 365 else str(datetime.timedelta(seconds=total_time)) + print + "[*] Total Masks: %d Time: %s" % (total_count, total_time_human) + + sample_time = sample_complexity / self.pps + sample_time_human = ">1 year" if sample_time > 60 * 60 * 24 * 365 else str( + datetime.timedelta(seconds=sample_time)) + print + "[*] Policy Masks: %d Time: %s" % (sample_count, sample_time_human) + + +if __name__ == "__main__": + + header = " _ \n" + header += " PolicyGen %s | |\n" % VERSION + header += " _ __ __ _ ___| | _\n" + header += " | '_ \ / _` |/ __| |/ /\n" + header += " | |_) | (_| | (__| < \n" + header += " | .__/ \__,_|\___|_|\_\\\n" + header += " | | \n" + header += " |_| iphelix@thesprawl.org\n" + header += "\n" + + # parse command line arguments + parser = OptionParser("%prog [options]\n\nType --help for more options", version="%prog " + VERSION) + parser.add_option("-o", "--outputmasks", dest="output_masks", help="Save masks to a file", metavar="masks.hcmask") + parser.add_option("--pps", dest="pps", help="Passwords per Second", type="int", metavar="1000000000") + parser.add_option("--showmasks", dest="showmasks", help="Show matching masks", action="store_true", default=False) + parser.add_option("--noncompliant", dest="noncompliant", help="Generate masks for noncompliant passwords", + action="store_true", default=False) + + group = OptionGroup(parser, "Password Policy", + "Define the minimum (or maximum) password strength policy that you would like to test") + group.add_option("--minlength", dest="minlength", type="int", metavar="8", default=8, + help="Minimum password length") + group.add_option("--maxlength", dest="maxlength", type="int", metavar="8", default=8, + help="Maximum password length") + group.add_option("--mindigit", dest="mindigit", type="int", metavar="1", help="Minimum number of digits") + group.add_option("--minlower", dest="minlower", type="int", metavar="1", + help="Minimum number of lower-case characters") + group.add_option("--minupper", dest="minupper", type="int", metavar="1", + help="Minimum number of upper-case characters") + group.add_option("--minspecial", dest="minspecial", type="int", metavar="1", + help="Minimum number of special characters") + group.add_option("--maxdigit", dest="maxdigit", type="int", metavar="3", help="Maximum number of digits") + group.add_option("--maxlower", dest="maxlower", type="int", metavar="3", + help="Maximum number of lower-case characters") + group.add_option("--maxupper", dest="maxupper", type="int", metavar="3", + help="Maximum number of upper-case characters") + group.add_option("--maxspecial", dest="maxspecial", type="int", metavar="3", + help="Maximum number of special characters") + parser.add_option_group(group) + + parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") + + (options, args) = parser.parse_args() + + # Print program header + if not options.quiet: + print + header + + policygen = PolicyGen() + + # Settings + if options.output_masks: + print + "[*] Saving generated masks to [%s]" % options.output_masks + policygen.output_file = open(options.output_masks, 'w') + + # Password policy + if options.minlength != None: policygen.minlength = options.minlength + if options.maxlength != None: policygen.maxlength = options.maxlength + if options.mindigit != None: policygen.mindigit = options.mindigit + if options.minlower != None: policygen.minlower = options.minlower + if options.minupper != None: policygen.minupper = options.minupper + if options.minspecial != None: policygen.minspecial = options.minspecial + if options.maxdigit != None: policygen.maxdigits = options.maxdigit + if options.maxlower != None: policygen.maxlower = options.maxlower + if options.maxupper != None: policygen.maxupper = options.maxupper + if options.maxspecial != None: policygen.maxspecial = options.maxspecial + + # Misc + if options.pps: policygen.pps = options.pps + if options.showmasks: policygen.showmasks = options.showmasks + + print + "[*] Using {:,d} keys/sec for calculations.".format(policygen.pps) + + # Print current password policy + print + "[*] Password policy:" + print + " Pass Lengths: min:%d max:%d" % (policygen.minlength, policygen.maxlength) + print + " Min strength: l:%s u:%s d:%s s:%s" % ( + policygen.minlower, policygen.minupper, policygen.mindigit, policygen.minspecial) + print + " Max strength: l:%s u:%s d:%s s:%s" % ( + policygen.maxlower, policygen.maxupper, policygen.maxdigit, policygen.maxspecial) + + print + "[*] Generating [%s] masks." % ("compliant" if not options.noncompliant else "non-compliant") + policygen.generate_masks(options.noncompliant) diff --git a/PACK/rulegen.py b/PACK/rulegen.py new file mode 100755 index 0000000..c4ccc9b --- /dev/null +++ b/PACK/rulegen.py @@ -0,0 +1,1193 @@ +#!/usr/bin/env python +# Rulegen.py - Advanced automated password rule and wordlist generator for the +# Hashcat password cracker using the Levenshtein Reverse Path +# algorithm and Enchant spell checking library. +# +# This tool is part of PACK (Password Analysis and Cracking Kit) +# +# VERSION 0.0.3 +# +# Copyright (C) 2013 Peter Kacherginsky +# All rights reserved. +# +# Please see the attached LICENSE file for additional licensing information. + +import sys +import re +import time +import operator +import enchant + +from optparse import OptionParser, OptionGroup + +from collections import Counter + +import subprocess + +import multiprocessing + +VERSION = "0.0.3" + +# Testing rules with hashcat --stdout +HASHCAT_PATH = "hashcat/" + + +# Rule Generator class responsible for the complete cycle of rule generation +class RuleGen: + # Initialize Rule Generator class + def __init__(self, language="en", providers="aspell,myspell", basename='analysis', threads=4): + + self.enchant_broker = enchant.Broker() + self.enchant_broker.set_ordering("*", providers) + + self.enchant = enchant.Dict(language, self.enchant_broker) + + # Output options + self.basename = basename + + # Finetuning word generation + self.max_word_dist = 10 + self.max_words = 10 + self.more_words = False + self.simple_words = False + + # Finetuning rule generation + self.max_rule_len = 10 + self.max_rules = 10 + self.more_rules = False + self.simple_rules = False + self.brute_rules = False + + # Debugging options + self.verbose = False + self.debug = False + self.word = None # Custom word to use. + self.quiet = False + + ######################################################################## + # Word and Rule Statistics + self.numeric_stats_total = 0 + self.special_stats_total = 0 + self.foreign_stats_total = 0 + + ######################################################################## + # Preanalysis Password Patterns + self.password_pattern = dict() + self.password_pattern["insertion"] = re.compile('^[^a-z]*(?P.+?)[^a-z]*$', re.IGNORECASE) + self.password_pattern["email"] = re.compile('^(?P.+?)@[A-Z0-9.-]+\.[A-Z]{2,4}', re.IGNORECASE) + self.password_pattern["alldigits"] = re.compile('^(\d+)$', re.IGNORECASE) + self.password_pattern["allspecial"] = re.compile('^([^a-z0-9]+)$', re.IGNORECASE) + + ######################################################################## + # Hashcat Rules Engine + self.hashcat_rule = dict() + + # Dummy rule + self.hashcat_rule[':'] = lambda x: x # Do nothing + + # Case rules + self.hashcat_rule["l"] = lambda x: x.lower() # Lowercase all letters + self.hashcat_rule["u"] = lambda x: x.upper() # Capitalize all letters + self.hashcat_rule["c"] = lambda x: x.capitalize() # Capitalize the first letter + self.hashcat_rule["C"] = lambda x: x[0].lower() + x[ + 1:].upper() # Lowercase the first found character, uppercase the rest + self.hashcat_rule["t"] = lambda x: x.swapcase() # Toggle the case of all characters in word + self.hashcat_rule["T"] = lambda x, y: x[:y] + x[y].swapcase() + x[ + y + 1:] # Toggle the case of characters at position N + self.hashcat_rule["E"] = lambda x: " ".join( + [i[0].upper() + i[1:] for i in x.split(" ")]) # Upper case the first letter and every letter after a space + + # Rotation rules + self.hashcat_rule["r"] = lambda x: x[::-1] # Reverse the entire word + self.hashcat_rule["{"] = lambda x: x[1:] + x[0] # Rotate the word left + self.hashcat_rule["}"] = lambda x: x[-1] + x[:-1] # Rotate the word right + + # Duplication rules + self.hashcat_rule["d"] = lambda x: x + x # Duplicate entire word + self.hashcat_rule["p"] = lambda x, y: x * y # Duplicate entire word N times + self.hashcat_rule["f"] = lambda x: x + x[::-1] # Duplicate word reversed + self.hashcat_rule["z"] = lambda x, y: x[0] * y + x # Duplicate first character N times + self.hashcat_rule["Z"] = lambda x, y: x + x[-1] * y # Duplicate last character N times + self.hashcat_rule["q"] = lambda x: "".join([i + i for i in x]) # Duplicate every character + self.hashcat_rule["y"] = lambda x, y: x[:y] + x # Duplicate first N characters + self.hashcat_rule["Y"] = lambda x, y: x + x[-y:] # Duplicate last N characters + + # Cutting rules + self.hashcat_rule["["] = lambda x: x[1:] # Delete first character + self.hashcat_rule["]"] = lambda x: x[:-1] # Delete last character + self.hashcat_rule["D"] = lambda x, y: x[:y] + x[y + 1:] # Deletes character at position N + self.hashcat_rule["'"] = lambda x, y: x[:y] # Truncate word at position N + self.hashcat_rule["x"] = lambda x, y, z: x[:y] + x[y + z:] # Delete M characters, starting at position N + self.hashcat_rule["@"] = lambda x, y: x.replace(y, '') # Purge all instances of X + + # Insertion rules + self.hashcat_rule["$"] = lambda x, y: x + y # Append character to end + self.hashcat_rule["^"] = lambda x, y: y + x # Prepend character to front + self.hashcat_rule["i"] = lambda x, y, z: x[:y] + z + x[y:] # Insert character X at position N + + # Replacement rules + self.hashcat_rule["o"] = lambda x, y, z: x[:y] + z + x[y + 1:] # Overwrite character at position N with X + self.hashcat_rule["s"] = lambda x, y, z: x.replace(y, z) # Replace all instances of X with Y + self.hashcat_rule["L"] = lambda x, y: x[:y] + chr(ord(x[y]) << 1) + x[ + y + 1:] # Bitwise shift left character @ N + self.hashcat_rule["R"] = lambda x, y: x[:y] + chr(ord(x[y]) >> 1) + x[ + y + 1:] # Bitwise shift right character @ N + self.hashcat_rule["+"] = lambda x, y: x[:y] + chr(ord(x[y]) + 1) + x[ + y + 1:] # Increment character @ N by 1 ascii value + self.hashcat_rule["-"] = lambda x, y: x[:y] + chr(ord(x[y]) - 1) + x[ + y + 1:] # Decrement character @ N by 1 ascii value + self.hashcat_rule["."] = lambda x, y: x[:y] + x[y + 1] + x[ + y + 1:] # Replace character @ N with value at @ N plus 1 + self.hashcat_rule[","] = lambda x, y: x[:y] + x[y - 1] + x[ + y + 1:] # Replace character @ N with value at @ N minus 1 + + # Swappping rules + self.hashcat_rule["k"] = lambda x: x[1] + x[0] + x[2:] # Swap first two characters + self.hashcat_rule["K"] = lambda x: x[:-2] + x[-1] + x[-2] # Swap last two characters + self.hashcat_rule["*"] = lambda x, y, z: x[:y] + x[z] + x[y + 1:z] + x[y] + x[z + 1:] if z > y else x[:z] + x[ + y] + x[z + 1:y] + x[z] + x[y + 1:] # Swap character X with Y + + ######################################################################## + # Common numeric and special character substitutions (1337 5p34k) + self.leet = dict() + self.leet["1"] = "i" + self.leet["2"] = "z" + self.leet["3"] = "e" + self.leet["4"] = "a" + self.leet["5"] = "s" + self.leet["6"] = "b" + self.leet["7"] = "t" + self.leet["8"] = "b" + self.leet["9"] = "g" + self.leet["0"] = "o" + self.leet["!"] = "i" + self.leet["|"] = "i" + self.leet["@"] = "a" + self.leet["$"] = "s" + self.leet["+"] = "t" + + ######################################################################## + # Preanalysis rules to bruteforce for each word + self.preanalysis_rules = [] + self.preanalysis_rules.append(([], self.hashcat_rule[':'])) # Blank rule + self.preanalysis_rules.append((['r'], self.hashcat_rule['r'])) # Reverse rule + # self.preanalysis_rules.append((['{'],self.hashcat_rule['}'])) # Rotate left + # self.preanalysis_rules.append((['}'],self.hashcat_rule['{'])) # Rotate right + + ############################################################################ + # Calculate Levenshtein edit path matrix + def levenshtein(self, word, password): + matrix = [] + + # Generate and populate the initial matrix + for i in xrange(len(password) + 1): + matrix.append([]) + for j in xrange(len(word) + 1): + if i == 0: + matrix[i].append(j) + elif j == 0: + matrix[i].append(i) + else: + matrix[i].append(0) + + # Calculate edit distance for each substring + for i in xrange(1, len(password) + 1): + for j in xrange(1, len(word) + 1): + if password[i - 1] == word[j - 1]: + matrix[i][j] = matrix[i - 1][j - 1] + else: + insertion = matrix[i - 1][j] + 1 + deletion = matrix[i][j - 1] + 1 + substitution = matrix[i - 1][j - 1] + 1 + matrix[i][j] = min(insertion, deletion, substitution) + + return matrix + + def levenshtein_distance(self, s1, s2): + """Calculate the Levenshtein distance between two strings. + + This is straight from Wikipedia. + """ + if len(s1) < len(s2): + return self.levenshtein_distance(s2, s1) + if not s1: + return len(s2) + + previous_row = xrange(len(s2) + 1) + for i, c1 in enumerate(s1): + current_row = [i + 1] + for j, c2 in enumerate(s2): + insertions = previous_row[j + 1] + 1 + deletions = current_row[j] + 1 + substitutions = previous_row[j] + (c1 != c2) + current_row.append(min(insertions, deletions, substitutions)) + previous_row = current_row + + return previous_row[-1] + + def levenshtein_print(self, matrix, word, password): + """ Print word X password matrix """ + print + " %s" % " ".join(list(word)) + for i, row in enumerate(matrix): + if i == 0: print + " ", + else: print + password[i - 1], + print + " ".join("%2d" % col for col in row) + + def generate_levenshtein_rules(self, word, password): + """ Generates levenshtein rules. Returns a list of lists of levenshtein rules. """ + + # 1) Generate Levenshtein matrix + matrix = self.levenshtein(word, password) + + # 2) Trace reverse paths through the matrix. + paths = self.levenshtein_reverse_recursive(matrix, len(matrix) - 1, len(matrix[0]) - 1, 0) + + # 3) Return a collection of reverse paths. + return [path for path in paths if len(path) <= matrix[-1][-1]] + + def levenshtein_reverse_recursive(self, matrix, i, j, path_len): + """ Calculate reverse Levenshtein paths. + Recursive, Depth First, Short-circuited algorithm by Peter Kacherginsky + Generates a list of edit operations necessary to transform a source word + into a password. Edit operations are recorded in the form: + (operation, password_offset, word_offset) + Where an operation can be either insertion, deletion or replacement. + """ + + if i == 0 and j == 0 or path_len > matrix[-1][-1]: + return [[]] + else: + paths = list() + + cost = matrix[i][j] + + # Calculate minimum cost of each operation + cost_delete = cost_insert = cost_equal_or_replace = sys.maxint + if i > 0: cost_insert = matrix[i - 1][j] + if j > 0: cost_delete = matrix[i][j - 1] + if i > 0 and j > 0: cost_equal_or_replace = matrix[i - 1][j - 1] + cost_min = min(cost_delete, cost_insert, cost_equal_or_replace) + + # Recurse through reverse path for each operation + if cost_insert == cost_min: + insert_paths = self.levenshtein_reverse_recursive(matrix, i - 1, j, path_len + 1) + for insert_path in insert_paths: paths.append(insert_path + [('insert', i - 1, j)]) + + if cost_delete == cost_min: + delete_paths = self.levenshtein_reverse_recursive(matrix, i, j - 1, path_len + 1) + for delete_path in delete_paths: paths.append(delete_path + [('delete', i, j - 1)]) + + if cost_equal_or_replace == cost_min: + if cost_equal_or_replace == cost: + equal_paths = self.levenshtein_reverse_recursive(matrix, i - 1, j - 1, path_len) + for equal_path in equal_paths: paths.append(equal_path) + else: + replace_paths = self.levenshtein_reverse_recursive(matrix, i - 1, j - 1, path_len + 1) + for replace_path in replace_paths: paths.append(replace_path + [('replace', i - 1, j - 1)]) + + return paths + + def load_custom_wordlist(self, wordlist_file): + self.enchant = enchant.request_pwl_dict(wordlist_file) + + def generate_words(self, password): + """ Generate source word candidates.""" + + if self.debug: print + "[*] Generating source words for %s" % password + + words = list() + words_collection = list() + + # Let's collect best edit distance as soon as possible to prevent + # less efficient pre_rules like reversal and rotation from slowing + # us down with garbage + best_found_distance = 9999 + + ####################################################################### + # Generate words for each preanalysis rule + if not self.brute_rules: + self.preanalysis_rules = self.preanalysis_rules[:1] + + for pre_rule, pre_rule_lambda in self.preanalysis_rules: + + pre_password = pre_rule_lambda(password) + + # Generate word suggestions + if self.word: + suggestions = [self.word] + elif self.simple_words: + suggestions = self.generate_simple_words(pre_password) + else: + suggestions = self.generate_advanced_words(pre_password) + + # HACK: Perform some additional expansion on multi-word suggestions + # TODO: May be I should split these two and see if I can generate + # rules for each of the suggestions + for suggestion in suggestions[:self.max_words]: + suggestion = suggestion.replace(' ', '') + suggestion = suggestion.replace('-', '') + if not suggestion in suggestions: + suggestions.append(suggestion) + + if len(suggestions) != len(set(suggestions)): + print + sorted(suggestions) + print + sorted(set(suggestions)) + + for suggestion in suggestions: + distance = self.levenshtein_distance(suggestion, pre_password) + + word = dict() + word["suggestion"] = suggestion + word["distance"] = distance + word["password"] = pre_password + word["pre_rule"] = pre_rule + word["best_rule_length"] = 9999 + + words.append(word) + + ####################################################################### + # Perform Optimization + for word in sorted(words, key=lambda word: word["distance"], reverse=False): + + # Optimize for best distance + if not self.more_words: + if word["distance"] < best_found_distance: + best_found_distance = word["distance"] + + elif word["distance"] > best_found_distance: + if self.verbose: + print + "[-] %s => {edit distance suboptimal: %d (%d)} => %s" % \ + (word["suggestion"], word["distance"], best_found_distance, word["password"]) + break + + # Filter words with too big edit distance + if word["distance"] <= self.max_word_dist: + if self.debug: + print + "[+] %s => {edit distance: %d (%d)} = > %s" % \ + (word["suggestion"], word["distance"], best_found_distance, word["password"]) + + words_collection.append(word) + + else: + if self.verbose: + print + "[-] %s => {max distance exceeded: %d (%d)} => %s" % \ + (word["suggestion"], word["distance"], self.max_word_dist, word["password"]) + + if self.max_words: + words_collection = words_collection[:self.max_words] + + return words_collection + + def generate_simple_words(self, password): + """ Generate simple words. A simple spellcheck.""" + + return self.enchant.suggest(password) + + def generate_advanced_words(self, password): + """ Generate advanced words. + Perform some additional non-destructive cleaning to help spell-checkers: + 1) Remove non-alpha prefixes and appendixes. + 2) Perform common pattern matches (e.g. email). + 3) Replace non-alpha character substitutions (1337 5p34k) + """ + + # Remove non-alpha prefix and/or appendix + insertion_matches = self.password_pattern["insertion"].match(password) + if insertion_matches: + password = insertion_matches.group('password') + + # Pattern matches + email_matches = self.password_pattern["email"].match(password) + if email_matches: + password = email_matches.group('password') + + # Replace common special character replacements (1337 5p34k) + preanalysis_password = '' + for c in password: + if c in self.leet: + preanalysis_password += self.leet[c] + else: + preanalysis_password += c + password = preanalysis_password + + if self.debug: "[*] Preanalysis Password: %s" % password + + return self.enchant.suggest(password) + + ############################################################################ + # Hashcat specific offset definition 0-9,A-Z + def int_to_hashcat(self, N): + if N < 10: + return N + else: + return chr(65 + N - 10) + + def hashcat_to_int(self, N): + if N.isdigit(): + return int(N) + else: + return ord(N) - 65 + 10 + + def generate_hashcat_rules(self, suggestion, password): + """ Generate hashcat rules. Returns a length sorted list of lists of hashcat rules.""" + + # 2) Generate Levenshtein Rules + lev_rules = self.generate_levenshtein_rules(suggestion, password) + + # 3) Generate Hashcat Rules + hashcat_rules = [] + hashcat_rules_collection = [] + + ####################################################################### + # Generate hashcat rule for each levenshtein rule + for lev_rule in lev_rules: + + if self.simple_rules: + hashcat_rule = self.generate_simple_hashcat_rules(suggestion, lev_rule, password) + else: + hashcat_rule = self.generate_advanced_hashcat_rules(suggestion, lev_rule, password) + + if hashcat_rule == None: + print + "[!] Processing FAILED: %s => ;( => %s" % (suggestion, password) + print + " Sorry about that, please report this failure to" + print + " the developer: iphelix [at] thesprawl.org" + + else: + hashcat_rules.append(hashcat_rule) + + best_found_rule_length = 9999 + + ####################################################################### + # Perform Optimization + for hashcat_rule in sorted(hashcat_rules, key=lambda hashcat_rule: len(hashcat_rule)): + + rule_length = len(hashcat_rule) + + if not self.more_rules: + if rule_length < best_found_rule_length: + best_found_rule_length = rule_length + + elif rule_length > best_found_rule_length: + if self.verbose: + print + "[-] %s => {best rule length exceeded: %d (%d)} => %s" % \ + (suggestion, rule_length, best_found_rule_length, password) + break + + if rule_length <= self.max_rule_len: + hashcat_rules_collection.append(hashcat_rule) + + return hashcat_rules_collection + + def generate_simple_hashcat_rules(self, word, rules, password): + """ Generate basic hashcat rules using only basic insert,delete,replace rules. """ + hashcat_rules = [] + + if self.debug: print + "[*] Simple Processing %s => %s" % (word, password) + + # Dynamically apply rules to the source word + # NOTE: Special case were word == password this would work as well. + word_rules = word + + for (op, p, w) in rules: + + if self.debug: print + "\t[*] Simple Processing Started: %s - %s" % (word_rules, " ".join(hashcat_rules)) + + if op == 'insert': + hashcat_rules.append("i%s%s" % (self.int_to_hashcat(p), password[p])) + word_rules = self.hashcat_rule['i'](word_rules, p, password[p]) + + elif op == 'delete': + hashcat_rules.append("D%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['D'](word_rules, p) + + elif op == 'replace': + hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p), password[p])) + word_rules = self.hashcat_rule['o'](word_rules, p, password[p]) + + if self.debug: print + "\t[*] Simple Processing Ended: %s => %s => %s" % (word_rules, " ".join(hashcat_rules), password) + + # Check if rules result in the correct password + if word_rules == password: + return hashcat_rules + else: + if self.debug: print + "[!] Simple Processing FAILED: %s => %s => %s (%s)" % (word, " ".join(hashcat_rules), password, word_rules) + return None + + def generate_advanced_hashcat_rules(self, word, rules, password): + """ Generate advanced hashcat rules using full range of available rules. """ + hashcat_rules = [] + + if self.debug: print + "[*] Advanced Processing %s => %s" % (word, password) + + # Dynamically apply and store rules in word_rules variable. + # NOTE: Special case where word == password this would work as well. + word_rules = word + + # Generate case statistics + password_lower = len([c for c in password if c.islower()]) + password_upper = len([c for c in password if c.isupper()]) + + for i, (op, p, w) in enumerate(rules): + + if self.debug: print + "\t[*] Advanced Processing Started: %s - %s" % (word_rules, " ".join(hashcat_rules)) + + if op == 'insert': + hashcat_rules.append("i%s%s" % (self.int_to_hashcat(p), password[p])) + word_rules = self.hashcat_rule['i'](word_rules, p, password[p]) + + elif op == 'delete': + hashcat_rules.append("D%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['D'](word_rules, p) + + elif op == 'replace': + + # Detecting global replacement such as sXY, l, u, C, c is a non + # trivial problem because different characters may be added or + # removed from the word by other rules. A reliable way to solve + # this problem is to apply all of the rules the source word + # and keep track of its state at any given time. At the same + # time, global replacement rules can be tested by completing + # the rest of the rules using a simplified engine. + + # The sequence of if statements determines the priority of rules + + # This rule was made obsolete by a prior global replacement + if word_rules[p] == password[p]: + if self.debug: print + "\t[*] Advanced Processing Obsolete Rule: %s - %s" % (word_rules, " ".join(hashcat_rules)) + + # Swapping rules + elif p < len(password) - 1 and p < len(word_rules) - 1 and word_rules[p] == password[p + 1] and + word_rules[p + 1] == password[p]: + # Swap first two characters + if p == 0 and self.generate_simple_hashcat_rules(self.hashcat_rule['k'](word_rules), rules[i + 1:], + password): + hashcat_rules.append("k") + word_rules = self.hashcat_rule['k'](word_rules) + # Swap last two characters + elif p == len(word_rules) - 2 and self.generate_simple_hashcat_rules( + self.hashcat_rule['K'](word_rules), rules[i + 1:], password): + hashcat_rules.append("K") + word_rules = self.hashcat_rule['K'](word_rules) + # Swap any two characters (only adjacent swapping is supported) + elif self.generate_simple_hashcat_rules(self.hashcat_rule['*'](word_rules, p, p + 1), rules[i + 1:], + password): + hashcat_rules.append("*%s%s" % (self.int_to_hashcat(p), self.int_to_hashcat(p + 1))) + word_rules = self.hashcat_rule['*'](word_rules, p, p + 1) + else: + hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p), password[p])) + word_rules = self.hashcat_rule['o'](word_rules, p, password[p]) + + # Case Toggle: Uppercased a letter + elif word_rules[p].islower() and word_rules[p].upper() == password[ + p]: # Toggle the case of all characters in word (mixed cases) + if password_upper and password_lower and self.generate_simple_hashcat_rules( + self.hashcat_rule['t'](word_rules), rules[i + 1:], password): + hashcat_rules.append("t") + word_rules = self.hashcat_rule['t'](word_rules) + + # Capitalize all letters + elif self.generate_simple_hashcat_rules(self.hashcat_rule['u'](word_rules), rules[i + 1:], + password): + hashcat_rules.append("u") + word_rules = self.hashcat_rule['u'](word_rules) + + # Capitalize the first letter + elif p == 0 and self.generate_simple_hashcat_rules(self.hashcat_rule['c'](word_rules), + rules[i + 1:], password): + hashcat_rules.append("c") + word_rules = self.hashcat_rule['c'](word_rules) + + # Toggle the case of characters at position N + else: + hashcat_rules.append("T%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['T'](word_rules, p) + + # Case Toggle: Lowercased a letter + elif word_rules[p].isupper() and word_rules[p].lower() == password[p]: + + # Toggle the case of all characters in word (mixed cases) + if password_upper and password_lower and self.generate_simple_hashcat_rules( + self.hashcat_rule['t'](word_rules), rules[i + 1:], password): + hashcat_rules.append("t") + word_rules = self.hashcat_rule['t'](word_rules) + + # Lowercase all letters + elif self.generate_simple_hashcat_rules(self.hashcat_rule['l'](word_rules), rules[i + 1:], + password): + hashcat_rules.append("l") + word_rules = self.hashcat_rule['l'](word_rules) + + # Lowercase the first found character, uppercase the rest + elif p == 0 and self.generate_simple_hashcat_rules(self.hashcat_rule['C'](word_rules), + rules[i + 1:], password): + hashcat_rules.append("C") + word_rules = self.hashcat_rule['C'](word_rules) + + # Toggle the case of characters at position N + else: + hashcat_rules.append("T%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['T'](word_rules, p) + + # Special case substitution of 'all' instances (1337 $p34k) + elif word_rules[p].isalpha() and not password[p].isalpha() and self.generate_simple_hashcat_rules( + self.hashcat_rule['s'](word_rules, word_rules[p], password[p]), rules[i + 1:], password): + + # If we have already detected this rule, then skip it thus + # reducing total rule count. + # BUG: Elisabeth => sE3 sl1 u o3Z sE3 => 31IZAB3TH + # if not "s%s%s" % (word_rules[p],password[p]) in hashcat_rules: + hashcat_rules.append("s%s%s" % (word_rules[p], password[p])) + word_rules = self.hashcat_rule['s'](word_rules, word_rules[p], password[p]) + + # Replace next character with current + elif p < len(password) - 1 and p < len(word_rules) - 1 and password[p] == password[p + 1] and password[ + p] == word_rules[p + 1]: + hashcat_rules.append(".%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['.'](word_rules, p) + + # Replace previous character with current + elif p > 0 and w > 0 and password[p] == password[p - 1] and password[p] == word_rules[p - 1]: + hashcat_rules.append(",%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule[','](word_rules, p) + + # ASCII increment + elif ord(word_rules[p]) + 1 == ord(password[p]): + hashcat_rules.append("+%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['+'](word_rules, p) + + # ASCII decrement + elif ord(word_rules[p]) - 1 == ord(password[p]): + hashcat_rules.append("-%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['-'](word_rules, p) + + # SHIFT left + elif ord(word_rules[p]) << 1 == ord(password[p]): + hashcat_rules.append("L%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['L'](word_rules, p) + + # SHIFT right + elif ord(word_rules[p]) >> 1 == ord(password[p]): + hashcat_rules.append("R%s" % self.int_to_hashcat(p)) + word_rules = self.hashcat_rule['R'](word_rules, p) + + # Position based replacements. + else: + hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p), password[p])) + word_rules = self.hashcat_rule['o'](word_rules, p, password[p]) + + if self.debug: print + "\t[*] Advanced Processing Ended: %s %s" % (word_rules, " ".join(hashcat_rules)) + + ######################################################################## + # Prefix rules + last_prefix = 0 + prefix_rules = list() + for hashcat_rule in hashcat_rules: + if hashcat_rule[0] == "i" and self.hashcat_to_int(hashcat_rule[1]) == last_prefix: + prefix_rules.append("^%s" % hashcat_rule[2]) + last_prefix += 1 + elif len(prefix_rules): + hashcat_rules = prefix_rules[::-1] + hashcat_rules[len(prefix_rules):] + break + else: + break + else: + hashcat_rules = prefix_rules[::-1] + hashcat_rules[len(prefix_rules):] + + #################################################################### + # Appendix rules + last_appendix = len(password) - 1 + appendix_rules = list() + for hashcat_rule in hashcat_rules[::-1]: + if hashcat_rule[0] == "i" and self.hashcat_to_int(hashcat_rule[1]) == last_appendix: + appendix_rules.append("$%s" % hashcat_rule[2]) + last_appendix -= 1 + elif len(appendix_rules): + hashcat_rules = hashcat_rules[:-len(appendix_rules)] + appendix_rules[::-1] + break + else: + break + else: + hashcat_rules = hashcat_rules[:-len(appendix_rules)] + appendix_rules[::-1] + + #################################################################### + # Truncate left rules + last_precut = 0 + precut_rules = list() + for hashcat_rule in hashcat_rules: + if hashcat_rule[0] == "D" and self.hashcat_to_int(hashcat_rule[1]) == last_precut: + precut_rules.append("[") + elif len(precut_rules): + hashcat_rules = precut_rules[::-1] + hashcat_rules[len(precut_rules):] + break + else: + break + else: + hashcat_rules = precut_rules[::-1] + hashcat_rules[len(precut_rules):] + + #################################################################### + # Truncate right rules + last_postcut = len(password) + postcut_rules = list() + for hashcat_rule in hashcat_rules[::-1]: + + if hashcat_rule[0] == "D" and self.hashcat_to_int(hashcat_rule[1]) >= last_postcut: + postcut_rules.append("]") + elif len(postcut_rules): + hashcat_rules = hashcat_rules[:-len(postcut_rules)] + postcut_rules[::-1] + break + else: + break + else: + hashcat_rules = hashcat_rules[:-len(postcut_rules)] + postcut_rules[::-1] + + # Check if rules result in the correct password + if word_rules == password: + return hashcat_rules + else: + if self.debug: print + "[!] Advanced Processing FAILED: %s => %s => %s (%s)" % ( + word, " ".join(hashcat_rules), password, word_rules) + return None + + +def check_reversible_password(self, password): + """ Check whether the password is likely to be reversed successfuly. """ + + # Skip all numeric passwords + if password.isdigit(): + if self.verbose and not self.quiet: print + "[!] %s => {skipping numeric} => %s" % (password, password) + self.numeric_stats_total += 1 + return False + + # Skip passwords with less than 25% of alpha character + # TODO: Make random word detection more reliable based on word entropy. + elif len([c for c in password if c.isalpha()]) < len(password) / 4: + if self.verbose and not self.quiet: print + "[!] %s => {skipping alpha less than 25%%} => %s" % (password, password) + self.special_stats_total += 1 + return False + + # Only check english ascii passwords for now + # TODO: Add support for more languages. + elif [c for c in password if ord(c) < 32 or ord(c) > 126]: + if self.verbose and not self.quiet: print + "[!] %s => {skipping non ascii english} => %s" % (password, password) + self.foreign_stats_total += 1 + return False + + else: + return True + + +def analyze_password(self, password, rules_queue=multiprocessing.Queue(), words_queue=multiprocessing.Queue()): + """ Analyze a single password. """ + + if self.verbose: print + "[*] Analyzing password: %s" % password + + words = [] + + # Short-cut words in the dictionary + if self.enchant.check(password) and not self.word: + + word = dict() + word["password"] = password + word["suggestion"] = password + word["hashcat_rules"] = [[], ] + word["pre_rule"] = [] + word["best_rule_length"] = 9999 + + words.append(word) + + # Generate rules for words not in the dictionary + else: + + # Generate source words list + words = self.generate_words(password) + + # Generate levenshtein reverse paths for each suggestion + for word in words: + # Generate a collection of hashcat_rules lists + word["hashcat_rules"] = self.generate_hashcat_rules(word["suggestion"], word["password"]) + + self.print_hashcat_rules(words, password, rules_queue, words_queue) + + +def print_hashcat_rules(self, words, password, rules_queue, words_queue): + best_found_rule_length = 9999 + + # Sorted list based on rule length + for word in sorted(words, key=lambda word: len(word["hashcat_rules"][0])): + + words_queue.put(word["suggestion"]) + + for hashcat_rule in word["hashcat_rules"]: + + rule_length = len(hashcat_rule) + + if not self.more_rules: + if rule_length < best_found_rule_length: + best_found_rule_length = rule_length + + elif rule_length > best_found_rule_length: + if self.verbose: + print + "[-] %s => {best rule length exceeded: %d (%d)} => %s" % \ + (word["suggestion"], rule_length, best_found_rule_length, password) + break + + if rule_length <= self.max_rule_len: + + hashcat_rule_str = " ".join(hashcat_rule + word["pre_rule"] or [':']) + if self.verbose: print + "[+] %s => %s => %s" % (word["suggestion"], hashcat_rule_str, password) + + rules_queue.put(hashcat_rule_str) + + +def password_worker(self, i, passwords_queue, rules_queue, words_queue): + if self.debug: print + "[*] Password analysis worker [%d] started." % i + try: + while True: + password = passwords_queue.get() + + # Interrupted by a Death Pill + if password == None: break + + self.analyze_password(password, rules_queue, words_queue) + except (KeyboardInterrupt, SystemExit): + if self.debug: print + "[*] Password analysis worker [%d] terminated." % i + + if self.debug: print + "[*] Password analysis worker [%d] stopped." % i + + +def rule_worker(self, rules_queue, output_rules_filename): + """ Worker to store generated rules. """ + print + "[*] Saving rules to %s" % output_rules_filename + + f = open(output_rules_filename, 'w') + if self.debug: print + "[*] Rule worker started." + try: + while True: + rule = rules_queue.get() + + # Interrupted by a Death Pill + if rule == None: break + + f.write("%s\n" % rule) + f.flush() + + except (KeyboardInterrupt, SystemExit): + if self.debug: print + "[*] Rule worker terminated." + + f.close() + if self.debug: print + "[*] Rule worker stopped." + + +def word_worker(self, words_queue, output_words_filename): + """ Worker to store generated rules. """ + print + "[*] Saving words to %s" % output_words_filename + + f = open(output_words_filename, 'w') + if self.debug: print + "[*] Word worker started." + try: + while True: + word = words_queue.get() + + # Interrupted by a Death Pill + if word == None: break + + f.write("%s\n" % word) + f.flush() + + except (KeyboardInterrupt, SystemExit): + if self.debug: print + "[*] Word worker terminated." + + f.close() + if self.debug: print + "[*] Word worker stopped." + + +# Analyze passwords file +def analyze_passwords_file(self, passwords_file): + """ Analyze provided passwords file. """ + + print + "[*] Analyzing passwords file: %s:" % passwords_file + print + "[*] Press Ctrl-C to end execution and generate statistical analysis." + + # Setup queues + passwords_queue = multiprocessing.Queue(multiprocessing.cpu_count() * 100) + rules_queue = multiprocessing.Queue() + words_queue = multiprocessing.Queue() + + # Start workers + for i in range(multiprocessing.cpu_count()): + multiprocessing.Process(target=self.password_worker, + args=(i, passwords_queue, rules_queue, words_queue)).start() + multiprocessing.Process(target=self.rule_worker, args=(rules_queue, "%s.rule" % self.basename)).start() + multiprocessing.Process(target=self.word_worker, args=(words_queue, "%s.word" % self.basename)).start() + + # Continue with the main thread + + f = open(passwords_file, 'r') + + password_count = 0 + analysis_start = time.time() + segment_start = analysis_start + try: + for password in f: + password = password.rstrip('\r\n') + if len(password) > 0: + + # Provide analysis time feedback to the user + if not self.quiet and password_count != 0 and password_count % 5000 == 0: + segment_time = time.time() - segment_start + print + "[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" % \ + (password_count, segment_start - analysis_start, 5000 / segment_time) + segment_start = time.time() + + password_count += 1 + + # Perform preliminary checks and add password to the queue + if self.check_reversible_password(password): + passwords_queue.put(password) + + except (KeyboardInterrupt, SystemExit): + print + "\n[!] Rulegen was interrupted." + + else: + # Signal workers to stop. + for i in range(multiprocessing.cpu_count()): + passwords_queue.put(None) + + # Wait for all of the queued passwords to finish. + while not passwords_queue.empty(): + time.sleep(1) + + # Signal writers to stop. + rules_queue.put(None) + words_queue.put(None) + + f.close() + + analysis_time = time.time() - analysis_start + print + "[*] Finished processing %d passwords in %.2f seconds at the rate of %.2f p/sec" % ( + password_count, analysis_time, float(password_count) / analysis_time) + + print + "[*] Generating statistics for [%s] rules and words." % self.basename + print + "[-] Skipped %d all numeric passwords (%0.2f%%)" % \ + (self.numeric_stats_total, float(self.numeric_stats_total) * 100.0 / float(password_count)) + print + "[-] Skipped %d passwords with less than 25%% alpha characters (%0.2f%%)" % \ + (self.special_stats_total, float(self.special_stats_total) * 100.0 / float(password_count)) + print + "[-] Skipped %d passwords with non ascii characters (%0.2f%%)" % \ + (self.foreign_stats_total, float(self.foreign_stats_total) * 100.0 / float(password_count)) + + # TODO: Counter breaks on large files. uniq -c | sort -rn is still the most + # optimal way. + rules_file = open("%s.rule" % self.basename, 'r') + rules_sorted_file = open("%s-sorted.rule" % self.basename, 'w') + rules_counter = Counter(rules_file) + rule_counter_total = sum(rules_counter.values()) + + print + "\n[*] Top 10 rules" + rules_i = 0 + for (rule, count) in rules_counter.most_common(): + rules_sorted_file.write(rule) + if rules_i < 10: print + "[+] %s - %d (%0.2f%%)" % (rule.rstrip('\r\n'), count, count * 100 / rule_counter_total) + rules_i += 1 + + rules_file.close() + rules_sorted_file.close() + + words_file = open("%s.word" % self.basename, 'r') + words_sorted_file = open("%s-sorted.word" % self.basename, 'w') + words_counter = Counter(words_file) + word_counter_total = sum(rules_counter.values()) + + print + "\n[*] Top 10 words" + words_i = 0 + for (word, count) in words_counter.most_common(): + words_sorted_file.write(word) + if words_i < 10: print + "[+] %s - %d (%0.2f%%)" % (word.rstrip('\r\n'), count, count * 100 / word_counter_total) + words_i += 1 + + words_file.close() + words_sorted_file.close() + + +############################################################################ +def verify_hashcat_rules(self, word, rules, password): + f = open("%s/test.rule" % HASHCAT_PATH, 'w') + f.write(" ".join(rules)) + f.close() + + f = open("%s/test.word" % HASHCAT_PATH, 'w') + f.write(word) + f.close() + + p = subprocess.Popen(["%s/hashcat-cli64.bin" % HASHCAT_PATH, "-r", "%s/test.rule" % HASHCAT_PATH, "--stdout", + "%s/test.word" % HASHCAT_PATH], stdout=subprocess.PIPE) + out, err = p.communicate() + out = out.strip() + + if out == password: + hashcat_rules_str = " ".join(rules or [':']) + if self.verbose: print + "[+] %s => %s => %s" % (word, hashcat_rules_str, password) + + else: + print + "[!] Hashcat Verification FAILED: %s => %s => %s (%s)" % (word, " ".join(rules or [':']), password, out) + + +if __name__ == "__main__": + + header = " _ \n" + header += " RuleGen %s | |\n" % VERSION + header += " _ __ __ _ ___| | _\n" + header += " | '_ \ / _` |/ __| |/ /\n" + header += " | |_) | (_| | (__| < \n" + header += " | .__/ \__,_|\___|_|\_\\\n" + header += " | | \n" + header += " |_| iphelix@thesprawl.org\n" + header += "\n" + + parser = OptionParser("%prog [options] passwords.txt", version="%prog " + VERSION) + + parser.add_option("-b", "--basename", + help="Output base name. The following files will be generated: basename.words, basename.rules and basename.stats", + default="analysis", metavar="rockyou") + parser.add_option("-w", "--wordlist", help="Use a custom wordlist for rule analysis.", metavar="wiki.dict") + parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") + parser.add_option("--threads", type="int", default=10, help="Parallel threads to use for processing.") + + wordtune = OptionGroup(parser, "Fine tune source word generation:") + wordtune.add_option("--maxworddist", help="Maximum word edit distance (Levenshtein)", type="int", default=10, + metavar="10") + wordtune.add_option("--maxwords", help="Maximum number of source word candidates to consider", type="int", + default=5, metavar="5") + wordtune.add_option("--morewords", help="Consider suboptimal source word candidates", action="store_true", + default=False) + wordtune.add_option("--simplewords", help="Generate simple source words for given passwords", action="store_true", + default=False) + parser.add_option_group(wordtune) + + ruletune = OptionGroup(parser, "Fine tune rule generation:") + ruletune.add_option("--maxrulelen", help="Maximum number of operations in a single rule", type="int", default=10, + metavar="10") + ruletune.add_option("--maxrules", help="Maximum number of rules to consider", type="int", default=5, metavar="5") + ruletune.add_option("--morerules", help="Generate suboptimal rules", action="store_true", default=False) + ruletune.add_option("--simplerules", help="Generate simple rules insert,delete,replace", action="store_true", + default=False) + ruletune.add_option("--bruterules", help="Bruteforce reversal and rotation rules (slow)", action="store_true", + default=False) + parser.add_option_group(ruletune) + + spelltune = OptionGroup(parser, "Fine tune spell checker engine:") + spelltune.add_option("--providers", help="Comma-separated list of provider engines", default="aspell,myspell", + metavar="aspell,myspell") + parser.add_option_group(spelltune) + + debug = OptionGroup(parser, "Debuggin options:") + debug.add_option("-v", "--verbose", help="Show verbose information.", action="store_true", default=False) + debug.add_option("-d", "--debug", help="Debug rules.", action="store_true", default=False) + debug.add_option("--password", help="Process the last argument as a password not a file.", action="store_true", + default=False) + debug.add_option("--word", help="Use a custom word for rule analysis", metavar="Password") + debug.add_option("--hashcat", help="Test generated rules with hashcat-cli", action="store_true", default=False) + parser.add_option_group(debug) + + (options, args) = parser.parse_args() + + # Print program header + if not options.quiet: + print + header + + if len(args) < 1: + parser.error("no passwords file specified") + exit(1) + + rulegen = RuleGen(language="en", providers=options.providers, basename=options.basename, threads=options.threads) + + # Finetuning word generation + rulegen.max_word_dist = options.maxworddist + rulegen.max_words = options.maxwords + rulegen.more_words = options.morewords + rulegen.simple_words = options.simplewords + + # Finetuning rule generation + rulegen.max_rule_len = options.maxrulelen + rulegen.max_rules = options.maxrules + rulegen.more_rules = options.morerules + rulegen.simple_rules = options.simplerules + rulegen.brute_rules = options.bruterules + if rulegen.brute_rules: print + "[!] Bruteforcing reversal and rotation rules. (slower)" + + # Debugging options + rulegen.word = options.word + rulegen.verbose = options.verbose + rulegen.debug = options.debug + rulegen.hashcat = options.hashcat + rulegen.quiet = options.quiet + + # Custom wordlist + if not options.word: + if options.wordlist: rulegen.load_custom_wordlist(options.wordlist) + print + "[*] Using Enchant '%s' module. For best results please install" % rulegen.enchant.provider.name + print + " '%s' module language dictionaries." % rulegen.enchant.provider.name + + # Analyze a single password or several passwords in a file + if options.password: + rulegen.analyze_password(args[0]) + else: + rulegen.analyze_passwords_file(args[0]) diff --git a/PACK/statsgen.py b/PACK/statsgen.py new file mode 100755 index 0000000..17b9c0f --- /dev/null +++ b/PACK/statsgen.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python +# StatsGen - Password Statistical Analysis tool +# +# This tool is part of PACK (Password Analysis and Cracking Kit) +# +# VERSION 0.0.3 +# +# Copyright (C) 2013 Peter Kacherginsky +# All rights reserved. +# +# Please see the attached LICENSE file for additional licensing information. + +import sys +import re, operator, string +from optparse import OptionParser, OptionGroup +import time + +VERSION = "0.0.3" + + +class StatsGen: + def __init__(self): + self.output_file = None + + # Filters + self.minlength = None + self.maxlength = None + self.simplemasks = None + self.charsets = None + self.quiet = False + self.debug = True + + # Stats dictionaries + self.stats_length = dict() + self.stats_simplemasks = dict() + self.stats_advancedmasks = dict() + self.stats_charactersets = dict() + + # Ignore stats with less than 1% coverage + self.hiderare = False + + self.filter_counter = 0 + self.total_counter = 0 + + # Minimum password complexity counters + self.mindigit = None + self.minupper = None + self.minlower = None + self.minspecial = None + + self.maxdigit = None + self.maxupper = None + self.maxlower = None + self.maxspecial = None + + def analyze_password(self, password): + + # Password length + pass_length = len(password) + + # Character-set and policy counters + digit = 0 + lower = 0 + upper = 0 + special = 0 + + simplemask = list() + advancedmask_string = "" + + # Detect simple and advanced masks + for letter in password: + + if letter in string.digits: + digit += 1 + advancedmask_string += "?d" + if not simplemask or not simplemask[-1] == 'digit': simplemask.append('digit') + + elif letter in string.lowercase: + lower += 1 + advancedmask_string += "?l" + if not simplemask or not simplemask[-1] == 'string': simplemask.append('string') + + + elif letter in string.uppercase: + upper += 1 + advancedmask_string += "?u" + if not simplemask or not simplemask[-1] == 'string': simplemask.append('string') + + else: + special += 1 + advancedmask_string += "?s" + if not simplemask or not simplemask[-1] == 'special': simplemask.append('special') + + # String representation of masks + simplemask_string = ''.join(simplemask) if len(simplemask) <= 3 else 'othermask' + + # Policy + policy = (digit, lower, upper, special) + + # Determine character-set + if digit and not lower and not upper and not special: + charset = 'numeric' + elif not digit and lower and not upper and not special: + charset = 'loweralpha' + elif not digit and not lower and upper and not special: + charset = 'upperalpha' + elif not digit and not lower and not upper and special: + charset = 'special' + + elif not digit and lower and upper and not special: + charset = 'mixedalpha' + elif digit and lower and not upper and not special: + charset = 'loweralphanum' + elif digit and not lower and upper and not special: + charset = 'upperalphanum' + elif not digit and lower and not upper and special: + charset = 'loweralphaspecial' + elif not digit and not lower and upper and special: + charset = 'upperalphaspecial' + elif digit and not lower and not upper and special: + charset = 'specialnum' + + elif not digit and lower and upper and special: + charset = 'mixedalphaspecial' + elif digit and not lower and upper and special: + charset = 'upperalphaspecialnum' + elif digit and lower and not upper and special: + charset = 'loweralphaspecialnum' + elif digit and lower and upper and not special: + charset = 'mixedalphanum' + else: + charset = 'all' + + return (pass_length, charset, simplemask_string, advancedmask_string, policy) + + def generate_stats(self, filename): + """ Generate password statistics. """ + + f = open(filename, 'r') + + for password in f: + password = password.rstrip('\r\n') + + if len(password) == 0: continue + + self.total_counter += 1 + + (pass_length, characterset, simplemask, advancedmask, policy) = self.analyze_password(password) + (digit, lower, upper, special) = policy + + if (self.charsets == None or characterset in self.charsets) and \ + (self.simplemasks == None or simplemask in self.simplemasks) and \ + (self.maxlength == None or pass_length <= self.maxlength) and \ + (self.minlength == None or pass_length >= self.minlength): + + self.filter_counter += 1 + + if self.mindigit == None or digit < self.mindigit: self.mindigit = digit + if self.maxdigit == None or digit > self.maxdigit: self.maxdigit = digit + + if self.minupper == None or upper < self.minupper: self.minupper = upper + if self.maxupper == None or upper > self.maxupper: self.maxupper = upper + + if self.minlower == None or lower < self.minlower: self.minlower = lower + if self.maxlower == None or lower > self.maxlower: self.maxlower = lower + + if self.minspecial == None or special < self.minspecial: self.minspecial = special + if self.maxspecial == None or special > self.maxspecial: self.maxspecial = special + + if pass_length in self.stats_length: + self.stats_length[pass_length] += 1 + else: + self.stats_length[pass_length] = 1 + + if characterset in self.stats_charactersets: + self.stats_charactersets[characterset] += 1 + else: + self.stats_charactersets[characterset] = 1 + + if simplemask in self.stats_simplemasks: + self.stats_simplemasks[simplemask] += 1 + else: + self.stats_simplemasks[simplemask] = 1 + + if advancedmask in self.stats_advancedmasks: + self.stats_advancedmasks[advancedmask] += 1 + else: + self.stats_advancedmasks[advancedmask] = 1 + + f.close() + + def print_stats(self): + """ Print password statistics. """ + + print + "[+] Analyzing %d%% (%d/%d) of passwords" % ( + self.filter_counter * 100 / self.total_counter, self.filter_counter, self.total_counter) + print + " NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords" + print + "\n[*] Length:" + for (length, count) in sorted(self.stats_length.iteritems(), key=operator.itemgetter(1), reverse=True): + if self.hiderare and not count * 100 / self.filter_counter > 0: continue + print + "[+] %25d: %02d%% (%d)" % (length, count * 100 / self.filter_counter, count) + + print + "\n[*] Character-set:" + for (char, count) in sorted(self.stats_charactersets.iteritems(), key=operator.itemgetter(1), reverse=True): + if self.hiderare and not count * 100 / self.filter_counter > 0: continue + print + "[+] %25s: %02d%% (%d)" % (char, count * 100 / self.filter_counter, count) + + print + "\n[*] Password complexity:" + print + "[+] digit: min(%s) max(%s)" % (self.mindigit, self.maxdigit) + print + "[+] lower: min(%s) max(%s)" % (self.minlower, self.maxlower) + print + "[+] upper: min(%s) max(%s)" % (self.minupper, self.maxupper) + print + "[+] special: min(%s) max(%s)" % (self.minspecial, self.maxspecial) + + print + "\n[*] Simple Masks:" + for (simplemask, count) in sorted(self.stats_simplemasks.iteritems(), key=operator.itemgetter(1), reverse=True): + if self.hiderare and not count * 100 / self.filter_counter > 0: continue + print + "[+] %25s: %02d%% (%d)" % (simplemask, count * 100 / self.filter_counter, count) + + print + "\n[*] Advanced Masks:" + for (advancedmask, count) in sorted(self.stats_advancedmasks.iteritems(), key=operator.itemgetter(1), + reverse=True): + if count * 100 / self.filter_counter > 0: + print + "[+] %25s: %02d%% (%d)" % (advancedmask, count * 100 / self.filter_counter, count) + + if self.output_file: + self.output_file.write("%s,%d\n" % (advancedmask, count)) + + +if __name__ == "__main__": + + header = " _ \n" + header += " StatsGen %s | |\n" % VERSION + header += " _ __ __ _ ___| | _\n" + header += " | '_ \ / _` |/ __| |/ /\n" + header += " | |_) | (_| | (__| < \n" + header += " | .__/ \__,_|\___|_|\_\\\n" + header += " | | \n" + header += " |_| iphelix@thesprawl.org\n" + header += "\n" + + parser = OptionParser("%prog [options] passwords.txt\n\nType --help for more options", version="%prog " + VERSION) + + filters = OptionGroup(parser, "Password Filters") + filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length") + filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length") + filters.add_option("--charset", dest="charsets", help="Password charset filter (comma separated)", + metavar="loweralpha,numeric") + filters.add_option("--simplemask", dest="simplemasks", help="Password mask filter (comma separated)", + metavar="stringdigit,allspecial") + parser.add_option_group(filters) + + parser.add_option("-o", "--output", dest="output_file", help="Save masks and stats to a file", + metavar="password.masks") + parser.add_option("--hiderare", action="store_true", dest="hiderare", default=False, + help="Hide statistics covering less than 1% of the sample") + + parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") + (options, args) = parser.parse_args() + + # Print program header + if not options.quiet: + print + header + + if len(args) != 1: + parser.error("no passwords file specified") + exit(1) + + print + "[*] Analyzing passwords in [%s]" % args[0] + + statsgen = StatsGen() + + if not options.minlength == None: statsgen.minlength = options.minlength + if not options.maxlength == None: statsgen.maxlength = options.maxlength + if not options.charsets == None: statsgen.charsets = [x.strip() for x in options.charsets.split(',')] + if not options.simplemasks == None: statsgen.simplemasks = [x.strip() for x in options.simplemasks.split(',')] + + if options.hiderare: statsgen.hiderare = options.hiderare + + if options.output_file: + print + "[*] Saving advanced masks and occurrences to [%s]" % options.output_file + statsgen.output_file = open(options.output_file, 'w') + + statsgen.generate_stats(args[0]) + statsgen.print_stats() diff --git a/hashcat-utils/CHANGES b/hashcat-utils/CHANGES new file mode 100644 index 0000000..104eeb7 --- /dev/null +++ b/hashcat-utils/CHANGES @@ -0,0 +1,9 @@ +* v1.1 -> v1.2 + +- Open Source the project +- License is MIT +- Moved repository to github: https://github.com/hashcat/hashcat-utils +- Added CHANGES +- Added LICENSE +- Added README.md + diff --git a/hashcat-utils/LICENSE b/hashcat-utils/LICENSE new file mode 100644 index 0000000..ea53078 --- /dev/null +++ b/hashcat-utils/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jens Steube + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/hashcat-utils/README.md b/hashcat-utils/README.md new file mode 100644 index 0000000..554aaa5 --- /dev/null +++ b/hashcat-utils/README.md @@ -0,0 +1,28 @@ +hashcat-utils +============== + +Hashcat-utils are a set of small utilities that are useful in advanced password cracking + +Brief description +-------------- + +They all are packed into multiple stand-alone binaries. + +All of these utils are designed to execute only one specific function. + +Since they all work with STDIN and STDOUT you can group them into chains. + +Detailed description +-------------- + +tbd + +Compile +-------------- + +Simply run make + +Binary distribution +-------------- + +Binaries for Linux, Windows and OSX: https://github.com/hashcat/hashcat-utils/releases diff --git a/hashcat-utils/bin/cleanup-rules.app b/hashcat-utils/bin/cleanup-rules.app new file mode 100755 index 0000000000000000000000000000000000000000..52c50fd6ce773f341536fa69b82e74860b5d8f7f GIT binary patch literal 13072 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/cleanup-rules.bin b/hashcat-utils/bin/cleanup-rules.bin new file mode 100755 index 0000000000000000000000000000000000000000..d6e34d5368cc277d9a16e8ac7abfe95bbe6b79aa GIT binary patch literal 10440 zcmeHNZ){W76~DF<2mzj*wRGW+;ITrRR*f5nKT4=toevjX zumx;Bn+B3nI25iyG+Km{qHDr3VdnrWfuqiD2$0pI=Cc6R*xq`qN zd0jxX#nSqT*2&v0gEN<6Gz7K?F(LUq1%4RkofhhTgBY(^UapAw5emv4?hbETx%}bo z&g$-PB(bY{SKZ3$mCN1nUUw~@H};RVP3zluNR&qz7{+`z9JmHh|I;(_+0E(x!80p< z-SJh&pI^JS^YTfwp9Kf@!RKX@S&}iHO_Yb)0}9TA<^3Uq{#TzEB|C&Ei^TI0AJ{>+NQtU~hGs(^S zp<CMQ5$kD zYDAkyzpRwO`=7@ccxM`62C2!Mv#%XYJnwmO{pn+vggq|_`;K>^dN!A+V?){v$MV z&c++u?;D5``y7Ej6UVamqG}NvTxT;vsXw81ocYdphVA3Ohe`H9KkSG3WoKYaJfA;k zJRX1jFw7u(8K#Eo@VoQp+*bi3nQ{mT>z@vyq2o~HvPYm7BM#`t)ca6ge&_KGuCw29 z<{a537#`zq9BeQK_SUEI%naL&`AK4aMw|?oC#??rhA-Jpb=d;Gh7(Qd-jlehw~nX- z5-iP~_sHz+3$ql)*I?$ef5GHW=f+3Xf!SM8^^t499b+7BB`n_8U&P*anVs(-R` z{#&>*a674JKaPsr`TTlmAHmrdhCGJ)Hg)f1-oc4m4(LGDuc#+7t5w^m`tB`lPQJI9 z)EmUvK4OfYe01*|NJOHd4IDEO$Zq>+X#_;JulY#F<%Jm7YL+M+{}uSvoZvUN;P%Srl!8Isr~PbHrZfb z7)xC6q(_F5H)d*uyY?((PHyV?7+SI>Fdyqj7Em;SawhLbT^ zjkvK{pJ>)U@O;$rz$g4JapK>O%-I_l&LwRjEtwYmVv9b}2tDNP{#Z?o*wp&*#6=tk zL-V$KpY!@gh6>|-JpwBO?>S%rO}O{kSt1_T9$Y0ajmtf~oxyU3^(xsTKPs5Iid{ZRbG$?yA+?8*Q8;wC-WQ~y>@a>89=eq={Tw>KSHu2BztSTWPeV~i#Q?q6Bb)bKbjIo*IS}RqqbIyFBEXh{Sd*s1oOMZeqpg;V4V!wlacc4I& zhX9|&HpKiSVuR#5VE0Ou`y7puoGRH}E>(IZSA$gc_)Jvecn9E+!TvrJfN~$&ryOe~ z`Daq)8p-9A%DCNOP?tfj?sXV;DSMN2NO62=0x67!DjihzR5V!rmZNzUp{f zk_Sr~q^bj@s#LRYnkv<$ravhuQK`-=)p(_<21y1xu!rFI{FXp&tDqnFjW<=MGBA~a zsSHeIU@8Ms8JNnzR0gIpFqMI+4E)bBpv+WwU$Q{zSUle4-(}1a3jZ|2p5A@D5C6u) zGMCr*zSYlL^99G}Of2&ZV3k78pAA7q`fImxy=ZdKt{~*!`MOFBPVY9-c7vBQqr5JV zKh#0HQt&wf3GmU47unB&o~`h?77IO>(z_D;4=0u_r3y!qy9B4WRjy_5e%vTDKK!GV z&u2`;5C2=^obq}_@WVnc-2d-D@3E}|y(Z4<8v>6A92NMXz)uBE2%IMV4|b2h`vk5O zXdS0%&6-tmRok{iL`%ra-SzI;YEMmqQ_r#o3<>)#BM%udcf1QD7X6Z$opn7T7M>_DI6LEfIzn*Wpg)M%ERGcQJS8?nr!hkAYgu(CiGx;^E%NZNvw< zSg<>Q4x$z9)^Nx$)LL*C)FB|C>+K9^0p<>N`9iTkPte!Z3BJS(4__b_3+y&LsJ){D z%%Jx`Pq+hy?u8Daa`RD)9=FBg%-zx3(-Vwn)`b3-9IqE#i*y~-*%e@g?=$H7819L$BX}bc9v9u7hiV5SsgH7_M>w7K8a8DtB+NWG1(APm{F4p|5 z2PN(ak$1L;J`xr8xFI7a+-o-mK6SJ z&I%QMA3)d#DziVnhr*(WjsBN3aV{-r{4vY;=3!Wy_KH^Uks7P$JcUL#~ z`SnZd=R@!{k5Au`OfCWi6l=W12|ofGvp3Dd)bGd!PMU?# ICO;DY2T^t5X8-^I literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/cleanup-rules.exe b/hashcat-utils/bin/cleanup-rules.exe new file mode 100755 index 0000000000000000000000000000000000000000..584947aa633c58a52bbe7f889c2c340d97e04d66 GIT binary patch literal 18432 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 BMH>JB literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/combinator.bin b/hashcat-utils/bin/combinator.bin new file mode 100755 index 0000000000000000000000000000000000000000..177698b08ae099c8fbfd53f61a70bef3f10cb492 GIT binary patch literal 10488 zcmeHNeQ;dWb-%lktVOo>t!#ni573gzM56U#3CEQjjNi(Veb=l(fCSD2N0zN!OQ5gr zzQr`KC+O zravgLMMW9&3xMostiD@>2)aiwijK%}Oi8NjNF=)oX;&fb6rGdQl$RMfKSia; z?&$2;yK%!Eoo(wnJGv8n>-uUouG_dF5bp_8i+PiO(%pVfZy z+0wOg`Llm|Ls{*H|N8dJpPaj=b3N%(3do0;7cnlMo3Q0LDsMizruI@wl?RY7w!elb zQ}~6B@&)k67QlBbfZx0TzIXxr`33M53*djS08VrIvU$A#+>ax-{ULxa(|>3Iylnye zYYX5<7QpXc0N)C{n5|@^{ypFrQ`gIG@Ptq0;0$%PkIHHzi$xA}bhoi+q$kRv`y)o2 z#f?~Jq?@(0L}Ib-o|ev@eXT}E4*}8MSVy-JZQ0lU7_{1Y5(bOz>+Fd~SgaL3x>`G- zOg*q<(F3s#Bf_GwNNXExyCPltdJhWIXismXd*cQQ1jU{hdTBf@aicY6v~;z0bdyyC z@cyozZV9)r?cq?})|TqPoq-KmzB)iDWbT~5Jje}hX`-vbf#)e@ z(De>n?NQWSviBu z9Qu6@e5nKPci>(J{)_|H9Qcp}S8Gh&!w$UIq0f`=dw8m#q14a#z^GARjiHk#f9xUZ zXqm57LnX6z1I}8R4vyq@(nwEb(AM5gGA(U-QpjH+nU*v?D&$op=aGC|$jeBkB}|V9 zxrk(1y7Z8c-6YeJrTc~a`3lIiROwzJUm}^7D7{C>=SileNjD1l9g=BD(z=k}BAJ#V zy_^swoGRsbHdt5z`!Ue zbM5f+aD#52=(ein`~2GBQuKw*&%)-RaxCPba?Z_gxzBnZV}gAOyfzR+ntAGxa-F9P zsld%&S;NRKcbyeVympL&HO61&DGZR;f)APF+L70>WWoNsi?qX|;KJq%Pc@Y{PA}2Q z`Z0IbUsGgwd2-anYbO(z$hI+DIl~A0Tw?f#%6DmphlGnmi!w{YgY6lkEd3V%r2W?Z zLxIfF&|pJG8yEl_GEeUuxR9{OlV_)SG3>IqIYC1!MHCNDPR(+2oa7pA&T@nTyFNNZ zQ}^3b|L~e)>hGWeY3da;bz6a^{+E=s!SXyY_i*j6~6F=0_=r>|1s0{am|m50sG!{%jc4?xV+JR7F+i{B-tc?XP;kmcHmX5pn2 zSy`bC`~zT0ed&09X^~j;9fM0e>80~=8*FuMe!$H_6V=*OfD&jboF(n`ymH)i7DbVq z^~7ETt$NdZBaDuk`divsvQrT`I}-F=U4NoYAEqk1Il-r zn{FDVL8CO>a2;!gF7$B(HYjUbAFT?1S_*6md#s z%3OgtU&WNKrr&@`Fm;nz9K_fGxvr*RE&bbNer8uqB`PFiJtszrE7p4V zGkf&hdJ7!&eP1FK*0AJs>p5^$qqr@#e{*;6k>Kv&w}LHWJmux7&2hLs*@SIV_^%qZ z>frk&luu^*Csq`8sTymG2zQSU>h(|$nWHDW9Tq-08r`}r8+#2n8cm5rxU~tK@S8T^ z+*%9~uI=&PgIe31F8<%He<;m$>tAN=iB?#rz>(`8f!Yp#2M(hFm1=RlRb2>~Z+wfJ z@9^ZiGmTB5iBTOR<`btTi-}Ckcywm{93DQl>-wb^{d-t5Uw0gYCuhCfeDCqqFVW6H zxAo%PuTn>53d;6=vKyDV&gO%@LjBGB$q$5sQ*(K6z|9$c%G$zR|HnftZ~IUDgqvrDD|w%y@w%tK zpUod1dnR3i%|}YCaSslR=s(S zn-{HLTo!r3UT&y(cue9z*V&znHCJJtPW~li=^9nut)qZLeo1a+g;L!Y_ z>dneuetOyUAN2f3(;u6!5Al3sX6O+oJh(Qn#E)VbTt^gZbU9@HwCe3}<$L03^3=6F z>u)}x;ao6oQS}x!&*Rom4-c7=U(jOk+Udl33goz=hvwsbG=|EhXH)qoO7AU+c{R3qpv&@-eD>cg#=8vS2^n(K)Nn5)m{TD_#uGvq4t5l;UsXoT>4E`nGvXcW8?bc>39vRbRjT&hoF2i@R{(v4hRR@1GM;=k*b#z#9MxqVfJKj!Ed>M*0YPgvOD~+vfFu!&|!5>kE24ER3#?am->~L3(P3-{;+R-qPpt z>b(BJ{5o&N(So3N{gFjMZ%wjryLZHu_i#axw+2|yTLB&D!UhuxQec{UJ=WkE#EpP= z_-n}evj}=XufhO$=mgIJp

a6JRpGjKfv*E4WE1J^U~Th4&b zr;7+PnoU(WJerB7=n^R(E7S!?|B$6tkGEfHE5(_t2`iQT^sG%y z^?x##=^=U8trN+9^Bu{zWqNd_HtN=KW{ln`sQsHwvfn^TrF^5LisUH^Wf8K<=ZuuQWxsUuTPNx} zc5a|^a$PS=y2zsom765JP122$Zjn49i^WH?ak??N_)CQ{8Rjp45vFgrB8)5&KQk7C-fp|N<>lv+kSpeS(Be7l<=523sZB7A>u@SnEOCLh+C8 zgBrZIc6IDS=pH!4DIlWQ9{0xMEU>Sqt1HrN>sf#} zsXABkaxioaDf=e34r%`P5`H1gpYrEf!QG0p)826F0ym^XdLMSST>A~^#J?MQRD8A1 z3`?Lw8W%{au5ab@ZZMPsWk2fHA<=#$2%C}+P^Ccl;7&kAk70DtomLo{{!k zD(2=-&7bO!?CYeS+E;yYV^{ky+0(ryH-GfI2Fi(4XX-w5jEVwH-Cu0Uj-_~_2XQ#< zCku6g*`&1h=Oo-GdhKuT#2&`sw5R{`Qk#(jeZcwg8Mv|+N;o}>!)dSXoAvS|eU7=v zm9ix|B<)rH8<*(>vqouun~>&DvaGVeFgS{@?A7m!)bEa(vYIxNYyU!y{iKXPDdYcV zPX5&TDYwVL&_`Q!A6Cx^>UT)F`nmEy!d7$GtLKPP`8%y#{apDBY-xxpKK-weq**wbZ6_`mgehU`FT6Xp85CKs(zb{ z*d%*ac8b1>9%uX!X+Pq(P1HD*17)Y=^I!;5^B=gVzNos!k literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/combinator.exe b/hashcat-utils/bin/combinator.exe new file mode 100755 index 0000000000000000000000000000000000000000..8282b18bc929287344f12a3f4e9848448106c85f GIT binary patch literal 18944 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/combinator3.exe b/hashcat-utils/bin/combinator3.exe new file mode 100755 index 0000000000000000000000000000000000000000..a3412d11b6991b9f5ad3fa975c803ab12a8f7b3a GIT binary patch literal 20480 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℞29`Zup~{yVjG<$)W?;Ue<9K_3 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/combipow.app b/hashcat-utils/bin/combipow.app new file mode 100755 index 0000000000000000000000000000000000000000..e5459e24cf668cc872aa0076273b432636e68f3d GIT binary patch literal 13240 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 KOYy-w%0B^%N;pLT literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/combipow.bin b/hashcat-utils/bin/combipow.bin new file mode 100755 index 0000000000000000000000000000000000000000..499814a0ae909a870c4eb957584a384005dcf21f GIT binary patch literal 10496 zcmeHNdvH@%dOwnFa2zbjfg~g_%OSP1CX2N;7_SL=NRBYqS?|VO?1XLBv1RGX)<%|8 z=?d&LvoVoP(Yi|Lw%yXlwn=xpL(@)&>4Umt3wB6CvYkwToxD4(n}t?_WMh(TFod=G z`_4T_zH#u*&UE@8M|1CYzV~;&^PO{FdTeWZtKDW}s@d6Hj4UiY)=942MT>2%0?wXQgQc%cWciU@4AfyGdxadqhXU)g~D}%%&nFNdD%VM|Mqk z`Fayo5F-%;(xX^ipA#rxujnXvK*sTsgi4Q4(yNtvwNg*P6EZLb)%a*^baYC4opui} z!aW3-Pn_o=$x`b$V+5%`v_hbO9??P-J&N;JZ7wPJHR*4$yv)e)DJW6) z#YniXsqu@EU|l2}O%2rzH8<5YHM)~A_XaU;vQM(xzS<=`sXTIE6f+HvYJm7>|73IB zrSBe`nf}wvpP!tp@BTs0Z|xxY?StJd(mS|ts{my3s6}#v7VlIA{;eBJ%NFTStu6QqbzA8qJj7@SV_Yl zc$hdnA%8f+;wd9A;7_oGzAGFJvQWQnBw4^8iNpe=6oGC}kDf?GV?7}Gjc|+zA*%y2 z!9syZEU7~!2w@i5l?WTU@Rop*2n5!^iX}j$P$`gv(KF}|lfnMMSX5Ga*tYhzmV0_O zxEqVT4em|F{zlNz>{u1*S6b}RN-;U+nbt*#SQ}~`uLy@LX=VK@B#D-~IeZPRu-7DR z+(fvY{U>m0vuk<;CSs&?#2cGz*<#Qmw;6FV+y4J1lf0qS$&Z^ok;tu>lL6)`V(t3w@a)0uEW| z)_jgw=njkgUJKo6q3^TM)!Y($zlC09k$=iUx2{_){a(2?UUvOT&a|;fqb&DRC~4{E z%a2)<(JgGUL!?ksinDW#2R*_ENGLyDK&!cQBXFAX{0V`7NH|S(ep29P2`?f1pupcH zoTfJal)&F6oTfCtPvEBrr>V@32>i!{(-h|80)LKhn!0?izz-8nQvuQr(&iJan&ZPYUAIi9tvt3SBp@bV-;|#?S7sDe6ZX5 z4R5#iA#cwy=NdM0G2$G171KrPuL`f1C& z=p20(T0ZBYNcA!;8>y~uc*mPQV{6ZRc*H}YXFkaFn!<*zWzLRQXzAxlv%3p(b8{C? zu6?Oy1kK22rl#_)zZw}~%;}rbviI6Gr|%@2)Y5O4XzR{u=}WGiWm`wzPnD&Q+0K1m z8?Rh_180Z700&1XQAdZCdst3`pL2WKGZ%74eo-i-Kk+!9d=mw!1wJ`Kb(711DuRB& zIr=yl&O=E0)`miQ+P2qMy-MWQ8utNH-Z}bD&~_f`tgauqRN)+b8raCCN-dLfj{X7q zJOJ%a! zT(+Yi#-asY9(iw^9n3^U0@i&gXG5>+#Fu#u-fV1ubJ#yK=X}lQxcCd*-}v zyh=+?m3p(^p~QGEobs(bB~x=c^TyN%#nil^Wrry>uTpB>DrsMLUd#GyHsr_Y{OupW z)7G(qQ6&;(R7`Mk?ah=r4%5oO@VsKF9@Y(bAJC!^89#b^|G;3 z&aoXxiPgqYvXQ$T3SS=YJi{6$k0epn!VFoE2HyrB#vc27c;+m1*HPMA(l6L@HZZnC zkixMyP;d>C`9Bn+uFNb)Nv?;GS&}nUlK%+Vybk|VS4A&aoR5NW?r%`Y+ez!@qSg*X zl6wry^h<^OT`+vb-60!p`ng#r6GwS3>(c5b05G!-Epuk)stH6cE<;t_ zM~P9GhVUp6SvNK%4-2^!euVg4+U}nT15U!*{|I#E z<1w$Jm468fNZJzshczTXrR$Z8l*~;#K^JwCyF(BvQ>OewB+o7B{hle`T9jXA%5NgU z4&=kEsl9NK^P8-jiFIF*HBGEmlT~niN~``lu)bijPJf!Umsl>r+VnD5X0T7rQuLJ* zBumTQbiRd3^R&tO-lthUV!dp#9{)5;X#K5VZR#(2s552nC)u?{57QLQ<0fkhv2H7} z%9n%HZ?ZVC=mE)$tC?8sU`=3#3gTWSv`<`*GX=UNw`Xhas^lo1Y#qVvZJCeTGOsnf z)4uMEn8m4Il;qBQ49&6kjnAoiYtNi*&wT8Ig~Ez|({R<%HlIu3LiBvO<+{0Vz8*e0 zhDxS;D|str!#y>%nk28|DI**S8^hd)@jycN8#=!w6%GGpO6P&tU|%>Yo->lS@K}iV zhj;2x9**KEL)><9-VxFLNu5VyhQ6K;$5K4tj}pdHpbo|oIyVNO($vWD#H}aEh*0OD zSOTnYQn`2Ywy3a4adD1^9zGC*hB0i~4aTCMH~21p)Igx_d4s!RsYsCb>rp*Hq5I?U zL~JNLh<;?4H#C2VZ*8f7y;`^fw!W#Ue*NYR4eK{HH}Y*Q>-er%!Wf{af_}r#Ym=#X zJeCYc`?>B942YQFGs*pZv6R6Zc;B$0uX9`CYzv7|`)QzwVNygIi_sVZeuEFDV2R9& zfqWj42(i!Khxmdq(gx>jpq@A=QqfT~?tp0R2O zko@e2g+e{u8#!F9j+OU1uiaHX!tTC#%WWI0Zy_5bG7hjl=Uy{$!i8rjpx-hB>F{7S||o-RXOgB^3HB4bzE)eKzC zz|{;~&A`#^Djj2?-U4Zh5FFxXeV^n@c^Sr`~xt;_;N)P2x=7TYE)s zmBiKk61@qbwo>xx{)(F7|Ib_@M)-cahd}Y0?^C5FF5XswK4~{`bszseXw<|_6yQpM zF(y9?OU(TCC=k4Epr-DP4$1+l`$hF_;YJ~a@4!nuqNnJyWhSoTu9tG^8-wzXH%MYU z6H*>eumV^4oRRnwk}u8tzXSChd!eHn$~}T~tAtGwwn})vgdqucO1MYD?@IVX2@gs5 zqJ*zY_+Ju!Ea4KoPoj2%gttm4h?di~d+xcN*LL-#qDG20x;MKw)HT$n1lq81ok?N8 zOsGK#F?VtRAMp%-A9LgLqMnE|H$8;Ay)A8ZhQD88{n3=WFBQfI?{JX038P0k<_-== zlf#21G!iCfr=CdC7eyf^ksk08dc;o(vKNmSx)9&wplALt1s{DXSW zKoGhLGc|hriG+XH)KL9z1|SCO{=sknu46EWlUsN(E%qgo%#BwOgL>4k9DztipkM*ou#fN>KP&-7?zZEJy8^$oj6unnnS zEwK61Lj6Xt@J=YE{Hy(D{{nq{V-ZcQbBW^LGUi^R^e62eP-tHgfKAH*(|$!=rLXpd zR?tdtD1EiRJtYHG`x!(;ljao4zKS00B}!lIA5X}DYo(-$U&$%>Ao#R5DO~MO6H>t= z^%ur3$?TixP^d~@?T52cU+ss=|HAQ89g=LW(Ema3=wqx3Q{6A9|06AwUx@z*x=xF}x}R7{f80S^ zD8CSY2{II-@=u=!sPSJ>{}sqm?J0eAJyrh~J0Zuf>Qnhw_47J(t^U>jGG^C+fu`h5 zY#vwicfhdfGu~^8auXadLGi5g6g&-%RsVq0Kfp~D6{j+w^b~#;9ipl6PfC69{SI`s r&nbDXIud>WNdD^=(3SqZ3+N9#lnW-BC`l6(Bud&DG6Q#XkZ1z_HyM(dB${M~nL7lk z)ufX_uh+5p{NL~STA#MIKJ6<{TLiRrLV%!EjG&0373;r=fr?@=pk=<_KKD$RMD1E{ zed~Mc>w4U^&fVwiv(G;J?6c24=j=PllB;@|nK5QTN+ucG07#FGzyI;83)Lsh*mM&6 z-Nct>ZZNrDnpxM<8j=G+e{;~&E;o8QI{d1E9dc`j>{wbWxBHvCWx2W8g@SdB zld-0?6UTIPkbN&>Qbxw9jEE@ZAmt|hpU#M6Kt1?v>*A3=E?W5wS#DFs|~t5>MwvqbQd6{J)CYuC`70R>bHw>Uw^t-AYRrgLscpPpPPYz~M0Zgd5wbSc5Fd*qGcd=3=DYA+60`Q2QL3YD4l6}iFYSZ&YBHGWWGuDR>YOa zmg4@6BxAw?w!^>j^(u|Z0>!kwdp-wAz zp|?UBcg*@|2Zj*K{}l(hzXrg_tgCs^x{`qAD3n3J4D=2?E8#pHIr2jluiN?c!QS`Z&qfJFp&y0+i zq@Kwj)H5;Qo(ALwtee^yHxp4k0mx|5;D>=cSWVqAa8h%e74c1|WfjjS2NUnn+{bcn zIvM*;{v61G>3^AswTwjPWM0?sci6}c)1dtiu|a@YJ+HUcbn?E6jOsp{0jj>2n98^}3H(NFQ#tS`)_>Z0K`#+xrRQFWH>q07bL-XxypUf-#3QFs z^=Zgz)&jg?7CkS3nVz3PrJdnPiQk|%-TLJX;LyE=4D0Ia>aVq5W52dvdfHLgFl6OR zf}74<7drhTEjnYyqn}`!HqJmf;U_NKN}@-Agk;xp0dp4tBq~uGO%7ycfK-a^1A#Py z*3_-$pCH%!V`n$QD9)hvN?L>d?ar)jd4^|%UVeT&M)`~uT z4@6Dw!kT^xo1qd@8Zdu}`{M%^d($UJ5smkIDzuvMgRoVCrmFJ7fl)R~O?yaN5S)<)#r#OYaq4K~~ zV~Dv+zM*nZRHnK~m2=M@C7wCj%_AvJ3r8!@9<5xPs$4c&`Ge8QR#a|)y(Ve8ufDE- z%>2>xl`Gnx6g~G;92gQ0S}`?|ktBD#>Z*-2&cvyyOA2?Xa2#AAe*Lgd;Wh**AgKU~`uoPEG6 znP*_I7uyGy$@A0<2sL#ndsT`1M{Q4X%C^X`DSQ~t`X{4G9cn^Wnqh-) z8_v-BW3?tFvS2qxFc|)Hcyk(m-TFc^mVI2!Jk(G01J?B@lQ9qX(8NI0fBhkPPVO_N zsUn^lnNb$IU1Yxvi|vnRfpWu~huIhWo;P zsl+fP$$V`MmW19!+3S$iwhB0q%wL5vQI%`klKI_4cWx63M-^^EbSH9Ge4uEbC>5`3 zpW&rw$CR1dl*&J=XDIO1UkobR+%u_L&HBBuz0-J=4KH3R@ASQj34HEcA^7l1$MwGq z6QF&7Sqv!J4ybs)4+|(in`F|bzut`f1RgTBY!BH+{+FwPj>cVDpVadR8Ar^ykF+qK zwBUj@>C5(1JWuvmj;aJVOeJQ006e+vCiDA9DJ%zhwNOqh0%5OIjLs|KQZeto7~Mng)zBul=h*c5=x+Fap&xYbqVlcB%a5Tv zqzeCl`ju2p`3saQsa$uw{?g~y(^qMS(epS{I z)0-$uKemI?9_fSYj2GA|hr)0zZzTRD2hC}XZ;^WLCRVYwcF3(_>Tt?*X$Rcq(wAFk zxjXah15OMDlCFix+V#4^9Q%fHlwlZd?K4oo{aYUfv0E=q^7D$jILn@V4g95^Q^8z! z6xKyXEb7F_h)s%aM$?G39rPxH512rtRD&sL4XkJoKEtJrD4YZa(vUha+OHdW{UkET zF0g~-O@*`V8*Iob5Ky5lvK+UT7KpP%dpG&oD38Ryp_L(!O+AgI$_If1nAyi$CSRjf zl-5^2R}IED0}O|pup*Z>V}H$8wEfT-9kT7$;hb&gYry&;{3A5Y67S;ds63#=EHiLJGe=5OoVPwM1ci}zh<})4oG-q_L6m_Bp zhhA%OMt4fl`Mhv(7VeATY4C|T4&6*Q+UN|L@rx~VjpUPGn*|SxdD|atTuYeLLq;#H&A>ZyRF-;fb;!6% z;%@C@xAuu!iz{XrVHOQYa}Fk=IFE}igOB1&;iX$u#bF3}=kGCHZhh_yoT?zei;yJ; z+2K=-`$S|LmREnlg>&ym6Df8Z%`G-cvsFh~&%@U9wxTOpV%=y*?aAGIb({=wU<6)J zEzyRsu_hFK$)H2G6ySb5{MhLJ#C0%-i%=9l4{c&U8ssClZ@?7|!XHKVP{F%q6OH2! zU!woYL&3L(&WDO3N3+7M()!65u}jVAO~*R#_lDLa*t&90aw&(3!yD1~d`7PKDv z8#`yFbP+X*)3{@~tImZ;1I|HIvqaxfOp@TUWa4MzSdjE7788iY zkBEhNdTkR8GWVW20D3j%v^ooJEun%L&~F2sXhA;ZqiKKFO;!I3f!WT+su)1%#wtuJ z9MEK_Sh{sD`l*7vT~ZI3eU+8!qPu{t*tu~M$w~1SST8~kKcGn=-!&B(UYQ&eL7CKg ztGb8t-in5u)m3-#!mp`|yO9mw0-j`3iG0CR(Kf5965Ywwtp1)?CU=hVw6Ust(N1lx zx{sRM(46#=*iG>WE)wvDND*XsC5bfDf{Enk=c?({exT|O0<%WX8U1|!F=WxeEMYk! z&eL)Dx3u$5O5i5ZBLtSB7lV5m0|j;H^K(p4uVHn>!XV({o@Eg10)c_$dYx=w{{Zaq z{dM%bEhj1_U})M_-TKf9@|xiyC3--WG080zbZA@)ET(}aT$nPb<-A_G}k^;cT$h!Qy_t8*!YGGYi2-l{|oOFgTg9PP7|J-D<4B%TTplKD@x zV~e<)bSFdZ;rB^Cx(~{VK3UJOYjJz`KGe$g?vd*1#j@0cvp?geiw$17wVO(0!BPZo z#;sVL(^S1sX|8@8vc_9@aHH<1#a{G{ao*{d z9W;5GVfuqyxS{JnXZX*<3oY>YU%*C;BN|^1QqO!4@HHVt-3Cu?XwPLDgI3*qnO2}m ztO%Y6m5mCX0k$y}fQ z3ltRWvghKgv7e)=!T`PcBpiQm(|a8TeG=?Nod_=m+xnDl%~$84N%$?yz)ETo%e|Sn zU@?(F^EqI&k;7teFG3Y~NPK>-t55CUI0tGkZz{la8gc2Vf^S;)H z5JH}!EzC<4&^kjroD(hu;qYsmf%Xc{8|MN${1#4U`-|xs<_eyQD_uBSx6VQZy6~mE zLvg=uJ;}hW(5)8auu8)3f^i=51J=JbKzVqQ`46^&<#ye=uYouS|1w~G2}KhKxT8d~ zH&9OIKTX7M_a=tnT88f)-JcVCseFIRT=0Fsj_(6~74hy$`a+;)9O|!#`?}W@;Jd&* zh&}kaEAjbaBCdUg{SWUL7cVs0OHbRU;G|_Lke+sBMxR&bMutpEEJt(4a$YKiY%Wuu z(cYoka$J=-j`wwDnHAhNUkFaZdB50Qxjp<|Mf}kC`=GEd1OpORnrVz9S)J2h&rP#o z4M|`3g^+FhDv>EjsfjUd`aM_80i*v<`fu_wVVu&Y^ETBuO9Ed-LwvJIzt0K(JG}?&Xd;Lq6okRS{2WjKm?;LcA*Hc; z0m9J*nQ8SYed%)-5(jAGPrmN&b0)|2LYstnl*u9}Wt;T$C7IIGi>5@MM=US1Kz)Tz zphd%^ZZ1B&@GKdQ0q#Rq5c^Uho1vl2;lvna8}p0Z>l^hm_o=fX&R#>7Y$MV~!AKHi zHB1sNL2^PjIGyt#Wop0T?xh$L9-{U_A=#ruDgDmnEYy!i^_yq}h8(%1+X}xJ>`TlL zZ7Sl!eQEdSQGcmdO{N|WXghq}KY+fbD%dH7tLs009#FI+iS0OigY%!2#>ETtdnWY4 zzF|f$YY4a$X`i)Q}l~)iwIB97v_L&TJ-QOAL-ZU zD5k%scgp9x;y^m8xA)Q-rHa0XQ)s0ic&gGp#7mKhOey6#UfT&p^eI4IapjokKyPod zXi+oVKHxk7&lGT~fSCdw_8D|^|04R`0&WvEVdp@ zE3LQf%B|MjgyKFkS+tGQC5zSz_^5#Q3Ak3kRRa11TrS`u0p|&Lrhrog%oOl2&S<3f z4+Y#U;5Gr*3;3vj_X)UGz*PeJI82SletBBNrhETwe2BZ?T^omBw7NNR3}4}_nHaCM z)@flZw)~h9tIl#(yzi_yY~Nre`q-4$V1bCLyou=58E0Uh`R=HFcU{P&=mcy>ZtT2> z^(d8tYMoopvUgRM;@fGg>Tk2CZU;7iLyt;5Z*zWC?~OtJ!9c#kk$oUO^e8_(I3&lX zf@k;%>Zs>uoHGC4K}nAB=f;{>v;nE-If#ldh=S2O5NSZb$X2m8@_`BQl}$Ek2FKHB z&`UkI7a?Bpsa-d@B7K%vZ}Ql&V_yu;+}!<_E59Bzc9neUiX6#XWwrGj2$$ef!$k+6s5c$$3{p5uV$lUm+P5ipH+wd?hZ7a_AsBmce zDh@>UnQn6w%IO2V&!Cffegp2(dPkvcLAKQM0169oF@oW>XKameGF2Yvy2)b8bxO`7uEv)W zXF~?u*C~YOcnx#54Z3m07GreSSo+KS#PY#(s{k7gp-5NJjVKRu42(HsB zPy(;idbn7x`x|TlC$>7odk@pC9WgE_amcPuB(*qV-CQrP*k^9Hckkn>dEcS+^$q3O zzlf7|V3vKZn2Xf&yfdYrAs0?VaP=dSbnCyOKW8+lo(yfB!_-L|WFUMCHAgOb9umXU z(X|VAJV)iBn=jHv9NMS5_C*h>`|& z;B?D+G$?0b2mkJh>cj$6T&O~a(VehP+>+@>1 zthTnbs%vD`FE<9g9@Q()3U{=w41489fBTBo4v*UE?+DG3{XV(5b(NQX%ozx)*=$+J z)9k%K29K-#0ZwzR>}zfFR%A0-u4(gnLS7j^0rk$7*Z9Nqdr&ILVPM<+L9eW~fYrS7 zHo3)*7U~*Q%Q z{%~89-0bb}25Cs10DhRdy0snowl#9a{PSh!LXxu>-Jvvl-n{bJm2)a)pF96NdGW#$ zxzit1TSzMUp`u(I3I_uIP-{oC?DaIZaGB9(NcODohgG>kUa>~?mXxK*>GJVWduX7+ zHPjI59hF9H@u+fp7@UYbAIMptgorCVDXj=3n<0);S zw=6fch5~IKOd~0;-5a9WNeM*tr6GFDd;&rNPoq~3c>^9O2O7Z7b1@D+tK0yIuC$Hb zGRE2tL5x|#7%JwJ-ww80M z^Q+%VRX0k-SX~Q#p6v~V+f*4wqx$`FsGU|A6u5$F0;t4rFiFMBE^(x!Sk`evhrd&d zj>}z-b=88w7ME~;fRPu4ii?_O%SFv4SYtu2y2{g5g#wHqXL~ZaWFVP*7U@N#w~+QD zeTB3Hdg(0fgjc|i#?{t#uk7=*wuOV<3k*xPH#I?`kX#gyVRG;%GFEi6w>0E!BnQM~ zd3kG44SU)y4SR!Ycw@*;E(K;oYZNP1l|7?e46U={?P`K>SdB0zbc>dqtuWL^{AgV+ zUtP4C=u%@3guLMI>xJ#g{BAk& ziim8PD7qEEpB({SIvIc3gM0(ve<0-`zYcK66vn0?KM434QZe%MH(9fg6y(bR-<;7~0^Epn5At!qzac${JS$*q8PYG1ZvearX&v%ifVU(44*6cdyOA~^ z{}AA+QyF`fXaH}-&HF{l1O682E#%h$o;r=O_mP(Y&pS@D8M}kng2D z{=kB*L>};Nqypq00{jKi>Bz4G{2h{vd>nA*bo^-+^5uY6%m75b0r1LM2#io3Ftd=c zYmmiV5l`)n@DTsanry+QNG6U8cOFUz6qM#WmMLpWQ3Lmf^iR_x-_<9Tc!Rbg;PtcTUW|<2!%1sT( zpOVc^F->MU<&zq+0~2eq;+cb%eHjN$)(mF7%v3%`9+E4)Em(4y31(-q>|kcOwZRfF z*JQ*^*;Z@z&P=npJfp!>gZk9C#0RsZw zF5u4ud{)3M0`3v;Ljg1BM|yas2xyeBkBjmG0WT8pDgk{0ssi>1c%Oj(F5vG3+$`XW z0`3-YpMd)X{9M3e0%qNBNR%hwX#&m?aJGQw3AjMOMFL(T;Bo;Q1iW5ARlu7CyiLHn z1bk4yM+JORz-I;ACg7leDgB+8e?t@hQ|EsVb9mdls<#?ZOhh2tYDElgggFs0430;$ zmrRSjYIQgmL_iT?wnoJ9*p9E`Tup3e8n&(lpCp=qWO*5A@AiaLCk0h;#-(ztiil`9 z;Og-C?d*1rSrbyd?RAJ9+Cz&F#G@ShCDqimHr`O}M_iGyBN?s^NbPAu+-%ROyjFWX`HRwit}MRY16pN#!Ah2g_x?0i;V zUtJ#xcpE9wTyIDN{a*rn69VcT-qo!tyA-8nuS!vGPp~<}EKl#Rv#cHw=nnkDfwx$_kN(pL?kpzW3Dnijx=i(m)!x90GU|oQ+cN6I9UOPF z86R?jD$~s{WMCM(&uno0soB?vXghfQ-0Z^uL+qDkpFiO3V81f^0>Rb})yEz~F03}P z|1kT4UQli{`#P~Gko(9?;dJ8ni5cG<8UvW=!)DQp9Whfgd`w|hOVDe~d5(o*`kppi zuPq@ph_4llO{X0Hl)%^w%Hsa@`6*)&;PHfJ+!JZsHP+}eD;a=SPe8xwv9!IQeK{c?<)A_`qcNYuG7A`Ua&a< e{#iA!&Y0H*0k2tWlx+_g@Sk;9C*(FX@P7bPZqL;K literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/cutb.app b/hashcat-utils/bin/cutb.app new file mode 100755 index 0000000000000000000000000000000000000000..fea8977cf5c004b96bb7df8e4e6e8f2541fe971e GIT binary patch literal 8944 zcmeHNeQXrR6`#Xr`${o)34}rm;+kS9i6V?6oR}0hT6!*HI8lP3wL>NI?Nl^dlAH65dypMVB&71e;_I79YPnZ93y^%3r9b;?>+ETQkdd7x<3D%%-G*#WL zZ0~%ebI-R)x)6K|*_(3YX9|j{y0^1u??M$}A6h75l1FR|;fO{Ws%mHh22n0}{T=-R z7K0&+76c&P?~-osXG7Jjs^$kXhjM8(Zxjx-E{<afsXSjJZgi}=s?m&G1&w@SaEFY>oZ`nyX`NqlwgX@4x6 z4+r+?*ys7{k^T-!8{(_|QT}eL@;CoGjdtzY*%|4o{Kmp=9cC-W;0Y=``4M#zQg@Ek zM28vs5|I5>#sW}4-Pvw50@9~Oze`$IbsS(9KtJr)Es(ejwiOGbQBGP4dA%i_I@D5C zvadi+alC%)TTiVyHug*V!mT4v+6jmSgl5Qb||%Rnsy|5q9K0psTTy!hTMFa0>ctwPAWTpaVg`EDKe zePq)`_u@V%DBn@c>&iZ5ZX4@8l?zo3gX*nOB;FjVmtl(Bt zBd(g|+lMZ<@L=RLcSeK9cRNSkF8q_5Q`~I&5ZBhSpBG01yx7hPpG2*`058=q6@C~W z9yOLYlMwh82<)@CoFULjZgu<)|Dg6G2-4X{>?Bx>OxfEZ!g{9ouw&HQzb1oH{TQZ; zn3tmFG&j#h%?qbQo$V49X`Zrw4ke{KYOUMC(d2c=3ZJ913V{Xr#(?jh|h4Uv7BlKp4Ue&mm&pa?2HMKd)x?)~6Ki+c52-@|CICA&jTi?F) zvEn-uG0*eT+6~cCVnbkJJh?Hr{)kwg*S^cVYM;fDxi#Rkj}u{zBN-eicujCqRc^3; zX>VhG#E_vF!rZ)qeb8n8#g4-Sbz1SN1K_0tcC-YwQf6*q97&Ft7oz6B?1(!H74$64 z( zzM@Wy`_A$B*bZy!rt+_NX%)BHC?(i!37Wgq-a2aUgmaOf@|#_yA8iQPcS4|;Z_UTK z3A)qWl)~mVkWTXhqTeF=uZzAz^bd(%5&bts-zoa-qVE#@PV@lm>AS8W71p}< ziC*i{LLPGICTLIlUHUen=dRA{&$#r5WP9GFUm^O8OaGqe(=KfheWFVKqDvnm`SYMf zQ0UzIWHl2ztf>ht9UIgVY9^N`q%}2_O&O_JI`x>QtMOPmohO@$elCFrF+HOu3)wgg z6!NiN?ZI$!KAcM?^O_OfpVqRyM&E(()^IADOr^E(H^RAsK@>e>t|Vsw-(!22Zy@9g zEo%%MMX9zzM)&w%#+D)YfRqUQ7-Y*sPc89NLYo@=SP6T&&QCY6BlVWwkA1+34WH%r zUW}4o+KL1kXaQcoQoI>BiWWky2=15i#c|M`-AG-LrU#JS5wz9Pu7iXG-g*3s?M^@~ zEbS7)=w^0!5Wv&zh`)&IpgZ41;2vprjf4b?<|Dge<4A*5|Fr*S?xqxqx9CEns`CV% z0VKYK_Ko4nbFLwuPtQm^B=HG}PfI)_@$(X&mv}_t7bSjH;u{myh*}0}8K`BTmVsIZ zY8j|ypq7DJ25K3oWuTUUS_WzvxMc?D?10W(wspqDc>*0{1RFwZHI57D#Dz{Nh!07A zrR3@TJdKCZ>g3CM;_3W@^aFC7&Odz2M?(S*q6c$>KkVTP9)3jd^Y+hp_+NPRpYx2r z;NgGc;a~RfuM2)YzDdvcl!yPDhkwt*&v~^#jXq4kS z&{m<*yH$L`AnFhRrsJ;v)(1=ksJnU;y*JKW{3y=y)P6nJo28EdR5jD5Y6DulU}*Sy zA(p_`3#yiVG^OXVqcM%jP!malEsAp$+#KPWIxADJFY=nuBEwB6sjxoz`(ZVvtfSGO?p literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/cutb.bin b/hashcat-utils/bin/cutb.bin new file mode 100755 index 0000000000000000000000000000000000000000..c4aeaef428d0c8682910b084e80e19aa4c6003ec GIT binary patch literal 6336 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

X)v^d~-$JO}BC3TFs;%Mn=bvZC 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/expander.exe b/hashcat-utils/bin/expander.exe new file mode 100755 index 0000000000000000000000000000000000000000..69966b929cc5246a8beb4f0e7de1e8e70b2e0c92 GIT binary patch literal 17920 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 BpW*-j literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/gate.app b/hashcat-utils/bin/gate.app new file mode 100755 index 0000000000000000000000000000000000000000..930020cd577cb4c7e17a411c657a5c427c3e2e22 GIT binary patch literal 8976 zcmeHNeQX>@6`zaG$u-2eYrY7gm9Hfhw4k^yaY?Eq(nhvAZjUs?2~AtV^m4c7J8~b^ z-d-BF1unW6Wp%!^f=EcHMX5vuf3*CCLWD}?;C$2w5i5MuLQx5!r0Y0<)jGBF(Ja3= zI~SifA0YlgLiePZ_c8CidGp@v-rnq%9Yu z!O-_*_YF6s7uR;*=($oFM-CYAT7SuKQ$NJG3oY%?RozT~zDp!BkiBTdj={@u(S{u@~az{_d)( zz+Y`#RdE%=Utc<2<){v`{e@M0)v$=ay}vB>h`UvKP2&cC4=8`1Q&SROn|sc zA|1V*zirB2QdtmR>yPrc=(@Ms>on4}@v+X1uIe=wcIyzTE)G0Fl_x*4PEzX5v7G2I zV-nvBT_~XLY!k@=q)&~mOIlZL9AIZb@gBySXGvTJ+lqzJ7NE5j@{qJCGwEkf#EvTGf>aK|5XOQ!MOD|UV3kWm%ka{l|sn6T>6#o$k_(&`@p7~ z>f}}^XkXQ=YuZ+AOP4j-&8^?@(uI( zpdp*JD*YR2V!*7k{rrB74VHS4PN;hHHkXfH*?9ryjU?bOtV zn(y}=))`*DYel3SUlAA{N~{bv@0IKG+ILx3?f0={UfJ!lj}u`HAsHMg__gFlYusS{ z%HGEOh#^BUgt>JE`=HDEgPnm1>a_T<1K{QDcBBloa%OsX2ubd+&PA+$+8f!FRm!=)F z9g)4StvPMp+hLtWB9B~dp1PtrN3}KY6i(6#egp^O$7EI7PHAr_#ze@Bz&@w>PxzEWW6WytcxSo#o>2Z3pqXOe{h+$y5rCml;EcdE)0EVj-L|UG}n(+vHcBxdXODx ztoZ%dA*?j_R(`*LQS$o}>{tRFv;?nTC0+#_L9>pd$o2)gV5kcsf@hp5D*Xco?lgy{spm&OK;6pvLLk!^eCyB;gtKV6O8!J$%8# z4@iE-{(y)7fk*#U&-jl${2LzrXCD5S9{!l*XW||8jGyuF=REuc4?pSQan>%sxuEx} zIMQHrNEt*!`q-Uli_w;#Ek#>~_DM8)--^!~WNiY#boBM#_<@-Ov2mMb^u(Bpcj1gr z^cuOIEPW&(L?$Wp-FmEG>iDW58pl@+LeK6>8M!Pe?$rA-J%)%s8#NLS3PbNn;T@(S z@_Nin<+6EF$(t}TdKH<<5*0I(2As(=Mq%J^FqOqzQGDBxG70BWn8h%%IUp%Y=(z+F zi5}gA&qOc2h%kwN)<~H!%tT{JkFN|)EOS^>Z-Fd~Xg1Cei4p7VlQ^wsnGuZ}54X0q OKh(CWebs|*F8&3~ygf?* literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/gate.bin b/hashcat-utils/bin/gate.bin new file mode 100755 index 0000000000000000000000000000000000000000..5cbf5bfcda563d91b018e00ab918b7ff3da56b0f GIT binary patch literal 6344 zcmeHLeQZ)v6*Ah>JLN8=(>t*5o+==4O>~Ginqo$r>|O*r%=2-{B^9^Gc$2Nmn4BMR3E7g)aNF`_OFBC$ zJHwI0p2|J7%PW^J^TeZ`8ZmG5kGh+lZx)eg9aTV>^Gvv~25|hRE8c9VJhyP(lDF@6 z&HUB&Py5&Y_#M>8TZulzylg5N_OM8xx=G}i@1ykn zlgO7&BKJ%pkAqx=tB{@s!K32tm_)8kB7c4o`2vv7V6$1$)e1evs6MwsALYjj zJKo0StUIB{na>y3{q3*%+C#7Ug8pzPi|ZZX2(&Rh+R1`}XpjYW1wauboq-7G2fJh8 zh#o`>7V}3s&>|3vvEb{mupR&fjDTKay06O*zI?u2UD1e?_}He#hWd5B8qZSCvRpsk ztMNPwncLx4Bw8#8M8y)>K#ny-^{vkiPr-^iC}lIl;ptd;M*!^$gT5gvdF16(EoOe92O`?trmHCPRdxwB9}*(80D!O4&GbRH$`RqP+Bj^ya8gK z8gvYp#@S~N!ccZm546tt8uSo{aB$>G7HH8v#JGebLjuPTTJqmu%#Hf!h${ zQjHuJcn4x!qLF@qw;;x)8A%Gf88I%&NVmZCh#iPq1zw35mtdqx;2Oj@^$`uQx7oX` zc`G+=^8+{BDq|hIw|G$njB=xAp#ucf=~TAJdFW>->fP@+g_hOhnI5p^y>GZ0jC0lF zLTH@JMA2d0Ij=G3Jo*FFyWG!vSAPm_xbc_M47_FL!OT+=S$zd8=3nbZ_4&qdYQXlR zyZ60yze`-=#@{lh9%Qql7oGEazyY|p^h;d3(JRjRNrBV6_lQWWgD;&XGEFq{1B00R z&KB>>-j)I0t3cEgL_IZJuCdc-pSc5GQx~(NXL;}R5Dww%m3vjciT zPcHt=2IHewAbp;?I@YwU;X*nMKKX^SO@dv}%D{L9nA{0E=dWk%PLO3}4pa>jrV~n58tmU@z{G$i@ ztem&0Vf1a3&n`I7x0vfUPu&ZzJLz*B2kqGa^@V3%|D zcQnWjwT$con|waJ@1-opxBZ?U8rLhGA2^d=mFu%cpO*K-6eQd{b=}5Ow`}~-rSqF? zaKDEVSF6(l{mHMU>#orSx%np7PER}`6jK=+Y9?-CV(hleAF8pzxB~_GF*ojI9{ekt zJ(Nz=aN`f_jnBDpEAtshU?sURpwHvRh0HsMjX!haeC9<+97{mV^CW=m=vaUAR^uM- z;kvu}4CBkpeNZ>PsJ^uLPj|;{zr}rQ4DW=s%iVA9x4kcU4Y4g6`xgD8NQIlxSBZRU zgE8J`wo(A-xg-ug$dqTRZ2uXO=GfCb~d-*iI}7x$Z`Rq;CO#8LnZFzksrO#dXB)RosV*)+=hN zcyGDl_A0J=#j!yg;~4JIGF1lxY7_O037YVhxSK{HYjRe zalKM;q=YL~hfBFqn<{%j(YlpduTlkm>lGFBOlM-=s~~;`{Fs%1&i+r2y8=~HG@g@Jx3@9TS#e<1mJ$v=_& zbIHdgFNNnh(vy-uCHZp6#h|6xv~Jx>wW7H#5z!OsGEbeSrn0&!Az<~=#ioS)C#7mq zVxD*iKTG^=%mW`Ffmk>5M521YFUXdrvtojQgLL#qe&KpO-AW6=)3?q{Ar$QO+Hy8^yY z2iOuaEqwl1%)i&Pp#JW5Py_G&u5deqj)FsIJR*weu`M2Fp7v;0S0JKW*7CpU;(oze zq~!O%GDA&O7u#xW_VkftS)(|#q7YoLQ`K=x_71_;j=!B~@=FrGWuC41W68$pI= z3fa>+<&ufM3u9I)TtEDN$1{QK=^QvA?P>iff6^nq6YTMfAx!7YkhIri@WTA1nBhAD zs**jOYc3g(&NYf(IDf1|w6BK?iO$LG(tyrYg!t?zwBHXhTocR>iJmL1Qb5lObJUDQ zedK%Lvf3w0H59X?v`-6^Z|MI4;yo~Awa4!gBuyUl2XhY$Apk(#N&3Qa=E+r}ZBy*TnFUwBIC9zM=Ia$ua0y?dktQ`X8~86eTZ=|6YN82tMkN zLh?b{gU&5f82^13!%sq5CVKAE|B8kBh4@3Tbz1D{y}*r5feQ5t@kP+V6e&J_rXi`1 zY5yD0#Zn=AdM?uczC&{UvYc}HQ2l%Yw$}LcKhgwF3aBt&!o)9wj@6#Y_Yx|!Zs8^0 zkR9=>1@_(2zFQ_pd6EaRBYYh?D5Lq~=QC1L8deFEZ`6EO^lm|m`4{Su{pJFFxtziV UhaQ@{MfcY-{)9zF`c~1u0c+=vApigX literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/gate.exe b/hashcat-utils/bin/gate.exe new file mode 100755 index 0000000000000000000000000000000000000000..43b8a2e05aa49bd4f47ba0862d2fa92c274c629f GIT binary patch literal 17408 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/generate-rules.app b/hashcat-utils/bin/generate-rules.app new file mode 100755 index 0000000000000000000000000000000000000000..4f929e9aee57ebf9f7fbcbc73797e87d374129d4 GIT binary patch literal 13104 zcmeHOeQZw3{@0s z-Ro+$)NJ?~^-e9esp4HF!dVOhqPVeU^Tw$ve7aAVq$`nO0(ivuq}!n_eOWd zpi9RQoJJpHUJRMGoBT{+i=yAB_;eL&?}azHovx|tOwZIg;B~h7)4gUq7v{t5w0n>< z#ysKC{l2F^>V}^(1>BHSP`%y zU`4=+fE58N0#*d92v`xYB49(wiDO#K6kU2v*v7?QV4EwAEa-clm*S*+&-OSo;( zxh#?Q39>WebGmL6GlyrGX@idbTCMw3^sh`EWoq814`9^JVuQIXRw+cUdK0a=ES@ui z;|Q#0P|4Cp&|#a>k=%(?6dWC4i8U_+KS`NVZ4-%aK*Y#savi#`o>A7LDLKhHnh?)9 z409fJ!mExk^^8~jc!4!n@A;jmV8)x zYT~lncG{z!WATNB-nhRo_r#Ir<&J`GzCONtPhtPCd39{pg~dob0fVWR-3d?O?btP& z7r>RI4eys0YGP&;=!70MQmo zwWJ-&t8*PM9)#%VA7qJnI0XW0vOmvkBigyXLr6kZ0OAwS9jJ3q_ruKNsKd}KL>(Zr zz&dbS(>&^V6)1V_I3tdJyRCY+eT6&GU>l&s7Ia>P4k^s5fP%--UznwyeQbB(O6X*9 z9eoW!QcDeFrzStedpZ6D&XIw?(j7Zy_o&J0u`%jV^(=QZp=)JfCEQCrj2XVZ19kPVyXvTNUL93WVewn|l>FJ% zyYovtiEXyAtr;G?0`*js*gZT5P4Qr+$pc5hS|Jq@&!q8-{#~LYlvPdlE1}JY8^YwM$!oTpvHq~ieFk| z7uiU~Z!%;LlPO9@V?APqDaXA{RT5Cj*{rcKtZ-nCj<6^L;YWB>5RqJdG;us={|10c zmi~c*lUmg4x?cJSV;hV|>?G8T;DvN8J$SKWSz|K68Bf>GGT>I5;q)hB{7rg<*WnS; z_#>`A_EnsB^vs9frGsqOQMym+-A}t4+*{q^i6bojoHLn(KWr~kYlg>$V2H)%vqbq4 zoB`az9_X`pW$9q@473j5{AuF{KI6VyuioOCEYV?0?k8aN2rh4iEBkFOjb>bXxUY@- zGKu>VcV>|8Oczsc;&W@g`Y!*hz~{Hlee$K-65^$1snjaXg6t-;Z<{3gr8Zxu z~N?b!Cjw`$5NHhRAN@K?MWmM<$QU9qyPyyB6{ zDtC4J_AQ;?DBQX>5(qEt*qp-tcc^nv)V_mw8}xotQonC6!|%|v)3pYzH0VKt4jZ)B zpg%FFV$d#wdJNiX&^HXa*Pu@u^nQL><5mQ$2v`xYB49%!B|BB0Le~z2RPnzWSO!A*h z@&_h)%p`wmlCPU&${kZu7^yY>E^{6zz1vLhde26sJT<)^P5XeqHVE@kKZi>10n;wz zqSCw7lur9kurOT^pWN)0Tbc~|EOLWlyBux_(VquIG1w|f9a2+Nk??N^pCA8r5T($w z0XZBZ#cfh&utgUA-}cGPD@0jp3E*91S&T?cN&tTtAeD%MNpd^bCZ$yd1kXr{ayY64 zLa^Y&KRp5p(Qp9RvK$J7@UCVl+$@OAEs_GH=63u)LV+CgHMN>Bg2-!zv4Wr!j7Y$0 tkJ1d;7xD{Y#2|{?)ZPhEk=rErt^}|=+2@y66&IIRl$Mp3;g$8t@-G4A*INJp literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/generate-rules.bin b/hashcat-utils/bin/generate-rules.bin new file mode 100755 index 0000000000000000000000000000000000000000..0dd33e1956120b94c47742713814dd616c054628 GIT binary patch literal 10448 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 Wz?;__Y1bA5|8_cZUea@E;Qs)QaN{Zf literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/hcstatgen.app b/hashcat-utils/bin/hcstatgen.app new file mode 100755 index 0000000000000000000000000000000000000000..f8b9e56709275e149ff2543ca842c117e8022c22 GIT binary patch literal 9144 zcmeHNeQZ zX!T>O*L;Vh)0<~Hdrgk~j6zXS)~(*KZn}!F4^Edc$s;y~a6~5!MbXtRohYZd{q3g3 zVCa(187E$nj=;xy9KwN$lI>1y&Lowrp4;4Tb$sS*$6rz!5ntN;(qEMn!XF$d9cr>u z)gW`ncTxHqmHNa}-mljbr7f##iFCVEaQhprcH;Y`94DS?S!{0KilTNXaV?rs9Syg? zt~rjs9$8f4Oa0Aa><2~PgsHAOMO~Z2>%t`?sGo3Atg8!CUCD2;Hi{C9>d`5CMY*=W z1w|G3D~+otE@JrWPR5EH)nU57kc_Vw7V)3&FO5CoY?V%3yUO4F(%;>3O5#g%Py6GR zsc>MQjDDWK4btCkX+wOeKg!>iO8iYdr;(ONzO}l!rTC16oI30&E)G1kB2RuqorKhx zV?NO##ufr~-N9HB6i|28icXF6snc^w>ne={?6fv=J7f1ulQ;~v6$_!$<5Zu^YW2y) z=K7M7eFbugBc9sUy8matzVn^vt`~m3;p?q5=N@$0H}|6dw_EXj^F?vcIvdexO;ksp zCj;o2T07RiwmYk zEPCk9t%pMRyJ6#Uczt+Xi*ar(H{Rg+GeKh|_v5qLShifCTkj=Qn8SOIa5L=5&E;nG zDLg%6xAaWb^FZ@3w+90Kt@e)BbN}GRC^xD<#=Bt5$MXYzo^NEi^AWSd&kL0^g&)Ry z2lNVi1Om@Af%R=XiU@Rsn=7A!f9o*>X|E^tMX(qdwH|^9>lx*}wq9x7K?a4&gP5+_ z7>^jIx$#cK_+UuX+M0z$8bj7iPztY&m1-LO1=%2~WH9|ONaFvH~`?Z|Kjk8A%^<1dd=Z&3< z2NpNuO(P(;hkF{Ub0-E7BlOM+y^?V;pRvh`80y)Gk(xl}L)`cbb!ai(vJ|{!k*B$P zY&2egS^;W>)WnfPSY)&DLB#l{)dVGO9HN9C<5+4^@PphqZq>nTY^XFIQSC=jZxnki z-+pF(RRzo26@lLK$Wx>c_wPeB*nZZ8%pb*zrNubRjoy(5&l8|K^?Y%vBK?1@=tHnk(niik^XgZZ2C0I%1r%I*4_u+r^Cods!1>BFR+xO=9rv zz4%Nm3db!DAq}n&c-&edl#W}ofg1++_E)Ip8^e!>H-*0+RNB$j4axh#l}+1T(4zen^=hZgc5Idqidoh9;^LqA9I4GvvPbi|<(L^nC~GNP9{ zbeQP79r|lTFL3A!D9q`gLsygB=g@y7`NX7%P3WI-=$lFBghPKyI&V6(hvdVc#o{Dz zz6UF*=oVFpsmW-!8dFl4ST3n5iF86wM3adpR847%CX-n*De7lp_@Skxlz1-PMgzHQ zv|U{hs?CNnIX#|8s-Xu%u|%7m$fToM_e{1%jmGdRC6q{a=JdL{I`*gr?+|45sGcRk zw#}?IOCCgIH3H%r&=YHzrz_|Q&Yt1ljqUm%x$;q_u-2< zGrfJjJlVzIRoVklrvDy zKsf{diy8Q;JabvKIx5aD=vX086=d^qtU)I(blO0CQ1UgBr~7#t521VIWj*n99zptk zIZo#h9_FDT(Y^^-$|nRbT#0er#s9>`KO^`lf4_3^zj5im;2QtEi^oyyw0H(wJl#r^ z#`~I!Kjz}!b@3+!KefII*Eo%o`mb{Fbc9cC5yV5z?Jaqam2#!zKP3Sn_7k9n?3Eyxfn)1kou-4wj z9K020k4mSOX;0Ii6BH%Yp{QMITTWN;Z;EIP|E5sX^wxxyNt2?gX&JhOSGK6#sdi0? zZHsF0C5ooDCvb_XDOpvVO=d|YtHWCBlq60lLAB`}8oY`_P({O;W+IJAqxf@4LTBPQ zG=sokmd*ePVZ1Gw$wDcvX5xVDst)h*Os9%~;+^=9hE8HlC6#SB{RB=$+d5oRq;S$o su5nBXb1j;VF$Ac!b#@CZ*9A!uezj;!TTx%X?16@*%a$%F=8f>b0V_NP^#A|> literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/hcstatgen.bin b/hashcat-utils/bin/hcstatgen.bin new file mode 100755 index 0000000000000000000000000000000000000000..76d278b4082348d0dcb0b9da507ce84769d7b58e GIT binary patch literal 10480 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/hcstatgen.exe b/hashcat-utils/bin/hcstatgen.exe new file mode 100755 index 0000000000000000000000000000000000000000..b5bb198a66f44beab0e36d193bfa198751543592 GIT binary patch literal 18432 zcmeHv4Rn;%wf3365Fp_VErUTsMolnLhzW`%O4=DRfj2rxBmq&;WJqQpI!UH8?+~be z$(;=P`gZ(ML~ZY_mU~^-_I~Yh+4jnxww(|l_(x+9&#aPqkiHtP}0^kI@%skm_p203efl>~V9Vrhfr5WYThTcTAQGq@QBn;*|hZr9h zV-67T;ce40qvfm~P=X|8JkCT-;OEwyObE&@|67gM`#)K^ATN@4&wTAlZpxjqK9W0nAX*T3>Thm~O z|Elr4ke4W_Tv12V_4H>O67}y!O8YpOGf|=hc)y(QDO3{Qk~Ah{ova}-o^rO->*t>m zX?7Pkh8#UsBx^PwOk6h5$H-$*^rJXhXihK8O)tE?m@$9n!euup8_~#wD%JjY8)F)a zC6pU>(Uj=FJP#84dXo6)KblO^?F%absu{WSQ#o4~a#|jwJOi1s#?(Ch`U#Y?T>?UM zp-J(TCFJPdqTXGkqrL`WhkoqumK)_ovgttQLJBNWEP`&N9df?E^YmnsQbk~vV)dX# zcXpxO6lwDc6tw$>Q79XTJtsZ1XV6KFQuc`F9!7N1_u`M}FuGAevzcIWYaKdoR~1W8 z!wH~$)t(EX^!rfS6SqCliXp`9Kjx78697hL+sKQyZxg8Q0tfgb;CE};`lB;pL)$v4 ziSAw4>+g20bk(iAVO6c%dF=b>TGr)_w=m{Pz9Or`CuHg8m&>t4F!DfV&!y>$B}{Vcbnb^#l-ONkbk6@~o4(W00ieRFx%mqn4FD zm+aST5!B^)?y;%ZckJgv4^00@#H^b25At|j!x!+87tV$CSHb!ab`P(&)wJ`{_oxKX zhV*4+&karUcXtgai=szO%B9>bYqZO`i%aoqh!mwLbzJz?q?xzHuK4-CEMmuAC3 z!Jm`HbwXBcgB!^9RSc7=Oh4r=SYz5r!AXjJjK8Yw`MFeGI22&4Djp~>Eo(ep-SfW1 zqYmodngLquJz3o+YZZIIAU$vsytC{L|JeG{a^BWI$fxlIO|ts2J{fH=>wmyFroAnz zJ=N+7IofN|hrr`1fAgM$)zRbG*YvsPp>yJ#cN^sDb}NnDcK5dk?8`yYa&N?6GKQMx z(QJ!&!Cpbkt6@Sd_grLETR#56ExMkEFwJhJ(m-UgZi5z@?X%k

r=e*3Gxp-{M;1 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/keyspace.exe b/hashcat-utils/bin/keyspace.exe new file mode 100755 index 0000000000000000000000000000000000000000..ea5a0bae0e14013ead75dfbcc78b294cbf6777fc GIT binary patch literal 50176 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/len.app b/hashcat-utils/bin/len.app new file mode 100755 index 0000000000000000000000000000000000000000..e794812fb804dc53a0d999a74ce095e6dcdb7e8e GIT binary patch literal 8976 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/len.exe b/hashcat-utils/bin/len.exe new file mode 100755 index 0000000000000000000000000000000000000000..774599fbcaad2944969f2a8f7e6f79e599a6f795 GIT binary patch literal 17408 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 WLE!ohGwD#w7 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/mli2.bin b/hashcat-utils/bin/mli2.bin new file mode 100755 index 0000000000000000000000000000000000000000..d105fa113cc57c9a46df8431d9762aae3d0ff7b7 GIT binary patch literal 10464 zcmeHNZ*WxA6~DWikjS6iP@x8K;NcLBmaGIY4O-=G2=L;hib5=ohGa>0$!fBj?jH-b zYM4zJUIUsLnGSUaxS~3Jb;ez=zfV+1EY)k4N<3!#~(L zXXP(Hz3j==`_kSN`O~SS7;;|Lz_OZzFa5Zmmj<+Nw#9(H^G#Sx83(^(9DKnzIL+^3 z@gE!qziJ%3YaD#uIQXN$HGB$N0KmoasUHWAkAvSe4!$1vbTLcp(OS_Hg4emgI%uQQ zUL(2_@tA07iN*bG_qDWj+}9HHhdMitf=Hv&h|zdUmp??IHg-kA z3TY9m8XM}Cw=D85_Abfw^Swph>oF6j`g7%agcT{xfmKRNkFS^btk5O2f_E!yMkq9m zR`SzIPh3uRr+6MZwXwOaQqmfpEPK|0>qvW<+OnjfT-CP(nziIDf6a!gEkznCZMe&F zmB)oPoc9Pa)Y@?SKC0Vr`#xgWaLS3>Y8#$g3ox#;;V49|wc2od9Xf0{U7OsxZTM6M zAvf7@6h7B_Y`EGwrOilwSZeeaPn)F)qc0UNPCoy4zS^%r}qtFn#cFkKGkTROK(Ool4DuJJn8AJBioKA=95i1 zoHERJ(oL3+r*|+WXh3v`bD}2!K2X}J8wL5=JBA{Gq)Gf2t1Z9m9%#_ zzw}Mt4b^xl9>DB-Iar~$B|Z}g#3%I-H` zWwR@)M~N{>hDcP|ldV&u_WoI>buRs5_>=S7kXXvESD&TWe~}td^9h$Q^(i%@8c98s z-mRzmrhL6g@GMSeZ+Cwr5z1AyWCALf?iqAX-UM|=_ ziI5#?&O884J|APpoj8{xhU4!>-)Owb^Ma@6^U3jTeMb&q zAsXgT;zV_7aG>Y&Y4Nf%^K$d;SvDjZ)w%`gY!R~Y8u^a9e&V}^MseLE7Tg)is17fSZb$UxH?^Gmv? zEd4A#-TXBDE4Z5h5g|L{JtH~jFqWQ4oTh?($naQ+`s~BbHr$b*I*9wIv(8=qh^x-6?I@~q zS8gx%xfgDon90d8GHPt+F>;>GW)Y+U1rI<;qU~<5Oe% zrx&sj(swy^61jK2dXc5m19WOBr;cO(-AYPoLcZstZn@HDD#?QWWtUBBm4g3QUacm` z&m;Jq$7Pnc7%0|dkMUNebKVP;AHVzH@$oo8&PP-JcuXR7uIGr-g|e$K|98Rfu?q+K zE}lbDyGqF#C08nWr;?^klGlD|;$DJ5S}@{p2mD*1tuaxmLr)$-*xXq8PH65)74 zTjE{nT~t-QFd^0I#n)Jf_)kvN>?FLg4!kjp`!@(LUNQ!v-NGA=!~mW)R&%l(09j9y$yH>I?|77cXziJ*GjopBm6 zgkwCg3CAE5*dp!zxLWE(w-UsG*E8#vqC?jT^Vd3c zX!+koXj01`it=6An}?&FA9v~!kE?+6yw2VV`Ac9N#kIxb^M10cfM2YF@O8{FZ$^h| z!2Fa`herF36rx8>nD!&;GM}&emB47PVLtC)x=J(!o4pm*?^YOTuV8*|e^3c)ip=@5 zAIrO8r@e=DP3#_0q`$G8~fM|vO zh59S-p%i(1dR;|L`I?TVN04HB+cFAK~7VCIiv*ef^M4x#K{~Jsx1zi9D literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/mli2.exe b/hashcat-utils/bin/mli2.exe new file mode 100755 index 0000000000000000000000000000000000000000..ace59c5f9c43c8abecb2e6271b04b526142da5cc GIT binary patch literal 17920 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 z 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/morph.bin b/hashcat-utils/bin/morph.bin new file mode 100755 index 0000000000000000000000000000000000000000..b9e9928cadfc530571cb685ac8ab45b365e1b2b1 GIT binary patch literal 10512 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/permute.app b/hashcat-utils/bin/permute.app new file mode 100755 index 0000000000000000000000000000000000000000..1b0d886ce3911eed51e2a531ad80694d81b217b1 GIT binary patch literal 8920 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 ANdN!< literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/permute.bin b/hashcat-utils/bin/permute.bin new file mode 100755 index 0000000000000000000000000000000000000000..5d13f5133419f6d0ee707ed42b146369f684b0a8 GIT binary patch literal 6328 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 Ty_t9PnfUk9k+YVb$pilfU%dy{ literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/permute_exist.app b/hashcat-utils/bin/permute_exist.app new file mode 100755 index 0000000000000000000000000000000000000000..82e7739a5423c174c985750fff59a9e0142b8da6 GIT binary patch literal 8976 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 DwbQh@sWJ$(;m>HW-NbGTiIdsS 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/prepare.bin b/hashcat-utils/bin/prepare.bin new file mode 100755 index 0000000000000000000000000000000000000000..3aa6ec30b37476a02a648af5d03cbb4e40c5a894 GIT binary patch literal 6328 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/req-exclude.app b/hashcat-utils/bin/req-exclude.app new file mode 100755 index 0000000000000000000000000000000000000000..921818bbce7c2676b93b7e599a6ed0375eb0e395 GIT binary patch literal 8976 zcmeGieQ*?4{atc_gA(pqD1{N4wUQb%<^zEOO^S<`@~*um84@Z*VAwex zP#pj95AV&q_xt{MzxQ@;_wBto^SA5s3Gq}DLaLxIgr2VB^vvnA6<;Uc||O=wYuT zN1CG!!%OlG;*64%9ZL446H->s^|juZ-=#Yof1TV2>GI~|{^oHe_yb2$TuEdU4S4SS zf{Pq~-{ty9W8H7nBq@^BH8s`G72N(NxxeT5A|Q=xS!!*|8IuIRV4Hj)&Tb$QnQR{mC&RKQ<( zUL|%3g1@0ev_##QpTP4grG@mz`%A$dakffE*KU|^3-{N+Ln2*Xd)yzjv+00+LjE{^ zJGs9&w?VqxAJ*@#GJmtzX*ksRXh$$qy2b=&9Rj7v0gbIhqaU_TjO)Z$j<`SwgZF_h zDB!m5Mm{vu$Bx$}&Z|5RAm_CFCkd&W!*K}MRz!df+m$ruvRZRO?Q1SuvQGdX<6r#T z@Y}~v{qg0!-=2B={rkUPg)v`&j{D{_(Er!1bicV>9yrg9&~Z+N!mM*JPagACai71g859)VOy`U6f`K-`~chpjmu%<95vn! z8)uKPN?1jhq;bp&gOb=2Hdj4(*|xdU64WB$4zAY{PY_+Zmz-EX}Pmwj-}Us_LJ{y_A+6*MMk zapmf8F}m6}ek9i9uRF-*=e7?S*Q{s3lA41aD~E`21d2hS1VfCPD02hz=k^xFha8fa zgFuZBVIPEyGuBElfpuE)iVckx_gLW~s1=h}#*aXegT~pg@ek`?P+^FI3;KJEa}SGi zlp24wesbNmr;e4^1LNUF>!HtFfM+36u&eyT<7_>j_F2z^E75p^n%|rJ(6+%=YYSv08e@ekwmu{lt`Pm*r?)`Z*3+$*rj|Hng%ck8D7B9T zHyzdQ^^f{Z@wv7Y2hN$|ui91&+*deJRg&i36Nk^_p2w_%kj2!qj=d?KY+Ep}pm4%F z^|HV23&a#BjZ=kjU*SZcZLj~~Q&TUrj?rgc$Ekc-+%4{b!W3718nULvWz<~HYEyi$ zbKX)&#ef@5jpk2S0jr-Wx$HpidV{DAG@kur&*qCBt zhK(88GRc!l5OlSS}SoK`txzE1QM-te^}=z+HB~&?cygz>R>)=GB}8Z! zKEDv`p@qA?)UjP?<J`>G(J&sgo+lpL+7%Wy3JooTYg-_q&@%Xd zB6N233ZYIXN~05`QHZMjs=lUV?$~$rQi#x?%YBVPT#Vwe9m<4<4KVTO9a$l^2$n)Xww5?k3-? z_QGACjMSK3FYF*vn0G6`jUYw87Wbv#JW7?W(c5BWH}y@8`qde7~K>0v4_a=4qr!yNvQ!{Z#j#bKVqS2>JxIKrXM;eVP> zMXyqUN(Cwvs8pa*fl37`6{u98Qh`bZDix?ypi+VV=L%ed$+tn@+99)Z1U$a*&kK;{ z{QD!kW-~g#X*^d!IewqV_j&<(HUC(TG@g5)+{fj3?%^RG6tT7sMj$?-_q*tviymS0 zto^rK^mknPFSz7Ca?w9_(LZ(3zjV=WFnTuM3734*MW1!i7hLqTi=GEG9-86zRXiBR z@1lO_P(E@e^d-=jLcbgOGU)d}$M3BRpyS%WM;wAjU;m9CFk?;X+$n1P5#qoDaP}#| zA!UCG|0Ezu$+)BpDv_M7z`q*gDEzBIQc?$0EuBKe{mM|XUz4H(vKCt_X-dBe-!L^P zt3-4)oyww0RtF<3!%;PbSVWI&;EbKUN*WxSsws#i!+$$e9bsC9D4Lc^17NI}l8zB6 z21lOYGnRpWMCeElXmF59;2bs*clk=fNp2eEl*yrmCa0nVGSMQLAqEplifD3F+uYo| OenacJ_3PF;wfHwRl&z!y literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/req-exclude.bin b/hashcat-utils/bin/req-exclude.bin new file mode 100755 index 0000000000000000000000000000000000000000..df012179cf27cb01143357d9e5bc219845a41a79 GIT binary patch literal 10440 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/req-exclude.exe b/hashcat-utils/bin/req-exclude.exe new file mode 100755 index 0000000000000000000000000000000000000000..4a8e9f000f62558769254ebbe6a88813ee023b8e GIT binary patch literal 18432 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 GME?ulTqHdJ literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/req-include.exe b/hashcat-utils/bin/req-include.exe new file mode 100755 index 0000000000000000000000000000000000000000..08a27f17aa749e1ca6c145552a8f38a820235b4d GIT binary patch literal 18432 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/rli.app b/hashcat-utils/bin/rli.app new file mode 100755 index 0000000000000000000000000000000000000000..d1e4464709df519b4716445f828e0f022fa69881 GIT binary patch literal 13528 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/rli.bin b/hashcat-utils/bin/rli.bin new file mode 100755 index 0000000000000000000000000000000000000000..f22ae118c340229f7f0a45b9179f0bbf7c1e2275 GIT binary patch literal 10552 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/seprule.pl b/hashcat-utils/bin/seprule.pl new file mode 100755 index 0000000..9be08c6 --- /dev/null +++ b/hashcat-utils/bin/seprule.pl @@ -0,0 +1,71 @@ +#!/usr/bin/env perl + +## Name........: seprule +## Autor.......: Jens Steube +## License.....: MIT + +use strict; +use warnings; + +## +## configuration +## + +my @rp = ('0'..'9', 'A'..'Z'); + +my $width = 3; +my $rule = "i"; +my $sep = " "; + +## +## code +## + +my $rp_size = scalar @rp; + +my $total = $rp_size ** $width; + +my $db; + +for (my $i = 0; $i < $total; $i++) +{ + my $left = $i; + + my @out; + + for (my $c = 0; $c < $width; $c++) + { + my $m = $left % $rp_size; + my $d = $left / $rp_size; + + push (@out, $m); + + $left = $d; + } + + @out = sort { $a <=> $b } @out; + + my $val = join ("", @out); + + next if (exists $db->{$val}); + + $db->{$val} = undef; + + my @final; + + for (my $c = 0; $c < $width; $c++) + { + my $s = sprintf ("T%s", $rp[$out[$c]]); + + push (@final, $s); + } + + for (my $c = 0; $c < $width; $c++) + { + my $s = sprintf ("%s%s%s", $rule, $rp[$out[$c]], $sep); + + push (@final, $s); + } + + print join (" ", "l", @final), "\n"; +} diff --git a/hashcat-utils/bin/splitlen.app b/hashcat-utils/bin/splitlen.app new file mode 100755 index 0000000000000000000000000000000000000000..fa1f9d91e23ccf4f7b618f3d36ca0ff6543fa215 GIT binary patch literal 9112 zcmeHNeT-Dq6~DvI!bn|a1w?E$jCMEGR#=ySZi%!E&el72)GfPIV#Vt)^JaI-d~Dvl zLDrzG8Mfg$j5V!BOlxXC2>sBSwkaX0l%Y$3P1=g~!>#?$T1(%u+HNX^W%+pho%`-| zXCFxUqe+uyPIB-0xaXXE&bfEqym#MwY2ux;GZ^#KF~+8$%|z?3XKVnN;7T-(CPYW@ zw)T74SKUR@soGC zLpCJ@Al~bcPVZ&?)vOS?zVy0mO5}|Ey4G{!+k2_wFQJTxuLhs;H(d$g4~|5Smg?1X z$lUQ=*Wmd3nbIeo>i%9JL@a0M$xOFWaQhoq{x+#`;#KNW;`S|s)*}*nG_5%XZhsqR zIq~(YMJ2x4-}@N5Uri+ut?O2~uAyL8uxbSBCtXz5)rDzY$!}$CgosCt=%l?6=l6G2 zMFsw<upwZ8%C7;t znamyeclPBBExl;vBGe(d4#PF5RiIXZ3s&H>jGKSqg}26d@gW~CqLF8gmCGYuO{|cuoxM(Zi5K<4D$iosJEKPpjiI} zxCxmjBIXD;UyGPW4$8V%MI>n+wCbT0?2MGI{>X9L4tCnjw?f^&fZ5F}K|KC%pla}e z<3S2Zr@GuMtqOl_R19yCvz?|$@z;kQg&21D2X zx%HJ3?*=`;3Yl;4;#EzNV!X*WG?-ZAU$8~y=e7@$;S^3q0+wSow95XImqK}d4L zRc?^KvbQilVn|aA0dAhcJ_wt?wtk1=VV&mewgJ4j)`}FNR!ol%4Wh^)^GL+}yY(WJ zFbgf{t27U7D{da(=IhpX&)W9r!RmU@e8Ic1->}&_`6F-7#|?GCp5`A|2&+hGgD+Cr zQ$AzccbKPt1PMgXmrugU75)YNlyKM#HTmqpaACi%a4Kl5H-8&0&f}$}KSXi}rnPL{ zP1Bc_KIaImCSnK5`2*G%%!_T-tt2q_Ma-knq9SAO+($dB*aquUP;#<;WIx6Bnc%&_ zwZZ#>VrVc@+EM~%(=Q=;o_#(ebyahWe@VgsgLL1 zX)TWkxg3=D*C?A}{WJ16KnW~^s!U*H87*4#7cpWtKPmgCWdF46za)FIvzxyn`>)FW z>$2Y=`<=4?mh8VH`{!l1;fo(nKwD?W zZd+Y}rU#JS7BsHxmXna+BJ5s-T8FZWOQZAI;X#0v>b7|cbSo_9I|jT}*<~pmf^+60 zyTXiZ(@XV_c|T7zyf|nq3wtwTK7}vNh_UC?b7|#ycZuRxC~PUbN#U&uKdbP5g@3Q` zVTI!gM-+ZS;a-JzE4)VGi;cIYSF1p+0<{X%Dp0FHtpc?Q)GAP`K&=9`3e+l4tHA$r z1+GzNE-Txk@;rf#G5pgT*?b%q&`|=NZV=z7csgGpp5D*XcmS z_}{qrKS+KupE1|?xQjpQ;%A^z)$#aU{FN>qhx?3SM{Tz{PKPCY~VR) zv~KjC_6jt52TAXX-;YM`SuaPUQ#o2|d@jK*LHlp`g&PUt&NV^3JH{Nm0cVJ!SI>55 z=pzIn(mg`ksKxSzhOZ`~aeOr)w9JO2p3RV=rt4XH+b-5?ed%sp#2<+2iN!+Kx|4WW zs*9W!Gm_a%j#P36to2?+CNo6Ej2<0cn(&n#lHQ2qe*qZ!ioJ_vQ^`LI|VjXspNOI*pUkEHw0JCeB2zVuz17V!eG3 d7`jRcR0^*3Xk1^`(z5iX)+I}qEUuJ{{R^qDn=k+X literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/splitlen.bin b/hashcat-utils/bin/splitlen.bin new file mode 100755 index 0000000000000000000000000000000000000000..8746968c86753aa9c9c9de2d6ee6b578110ceb43 GIT binary patch literal 10472 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 b#!|1KEs9ao*m{dzUX^uy*GNL0{D}S+SU2?+ literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/splitlen.exe b/hashcat-utils/bin/splitlen.exe new file mode 100755 index 0000000000000000000000000000000000000000..46e71f10c372ac06330ac84e0bf04a0a6dd32b40 GIT binary patch literal 17408 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/strip-bsn.app b/hashcat-utils/bin/strip-bsn.app new file mode 100755 index 0000000000000000000000000000000000000000..0a4e0e39e466f6941a2f7bb7adacec5f6162e7bf GIT binary patch literal 8976 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 wP)74lO8oJa*o8Ow;yG3_RlSN&U5(Dn22 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 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/strip-bsr.app b/hashcat-utils/bin/strip-bsr.app new file mode 100755 index 0000000000000000000000000000000000000000..ff56c9729a8c10e4288cd457831e474973279b7f GIT binary patch literal 8976 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 BVe|k1 literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/strip-bsr.exe b/hashcat-utils/bin/strip-bsr.exe new file mode 100755 index 0000000000000000000000000000000000000000..b92935c7d1eb63568f1b66602558b487d2b88fce GIT binary patch literal 17408 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 B()a)X literal 0 HcmV?d00001 diff --git a/hashcat-utils/bin/tmesis.pl b/hashcat-utils/bin/tmesis.pl new file mode 100755 index 0000000..0b9d43e --- /dev/null +++ b/hashcat-utils/bin/tmesis.pl @@ -0,0 +1,59 @@ +#!/usr/bin/env perl + +## Name........: tmesis +## Autor.......: Jens Steube +## License.....: MIT + +use strict; +use warnings; + +#tmesis will take a wordlist and produce insertion rules that would insert each word of the wordlist to preset positions. +#For example: +#Word ‘password’ will create insertion rules that would insert ‘password’ from position 0 to position F (15) and It will mutate the string ‘123456’ as follows. +#password123456 +#1password23456 +#12password3456 +#123password456 +#1234password56 +#12345password6 +#123456password +# +#Hints: +#*Use tmesis to create rules to attack hashlists the came from the source. Run initial analysis on the cracked passwords , collect the top 10 – 20 words appear on the passwords and use tmesis to generate rules. +#*use tmesis generated rules in combination with best64.rules +# +# inspired by T0XlC + +my $min_rule_pos = 0; +my $max_rule_pos = 15; + +my $db; + +my @intpos_to_rulepos = ('0'..'9', 'A'..'Z'); + +my $function = "i"; +#my $function = "o"; + +while (my $word = <>) +{ + chomp $word; + + my $word_len = length $word; + + my @word_buf = split "", $word; + + for (my $rule_pos = $min_rule_pos; $rule_pos < $max_rule_pos - $word_len; $rule_pos++) + { + my @rule; + + for (my $word_pos = 0; $word_pos < $word_len; $word_pos++) + { + my $function_full = $function . $intpos_to_rulepos[$rule_pos + $word_pos] . $word_buf[$word_pos]; + + push @rule, $function_full; + } + + print join (" ", @rule), "\n"; + } +} + diff --git a/hashcat-utils/bin/topmorph.pl b/hashcat-utils/bin/topmorph.pl new file mode 100755 index 0000000..d98b0ef --- /dev/null +++ b/hashcat-utils/bin/topmorph.pl @@ -0,0 +1,71 @@ +#!/usr/bin/env perl + +## Name........: topmorph +## Autor.......: Jens Steube +## License.....: MIT + +use strict; +use warnings; + +my @intpos_to_rulepos = ('0'..'9', 'A'..'Z'); + +my $function = "i"; +#my $function = "o"; + +if (scalar @ARGV != 5) +{ + print "usage: $0 dictionary depth width pos_min pos_max\n"; + + exit -1; +} + +my ($dictionary, $depth, $width, $pos_min, $pos_max) = @ARGV; + +if ($width > 20) +{ + print "width > 20\n"; + + exit -1; +} + +for (my $pos = $pos_min; $pos <= $pos_max; $pos++) +{ + my $db; + + open (IN, $dictionary) or die "$dictionary: $!\n"; + + while (my $line = ) + { + chomp $line; + + my $len = length $line; + + next if (($len - $pos) < $width); + + my $word = substr ($line, $pos, $width); + + next unless defined $word; + + $db->{$word}++; + } + + close (IN); + + my @keys = sort { $db->{$b} <=> $db->{$a} } keys %{$db}; + + for (my $i = 0; $i < $depth; $i++) + { + my @chars = split "", $keys[$i]; + + my @rule; + + for (my $j = 0; $j < $width; $j++) + { + my $function_full = join "", $function, $intpos_to_rulepos[$pos + $j], $chars[$j]; + + push @rule, $function_full; + } + + print join (" ", @rule), "\n"; + } +} diff --git a/princeprocessor/CHANGES b/princeprocessor/CHANGES new file mode 100644 index 0000000..59363f6 --- /dev/null +++ b/princeprocessor/CHANGES @@ -0,0 +1,55 @@ +* v0.20 -> v0.21: + +- Exit if stdout is closed or has a error +- Fix for "Bug --pw-min" issue +- Print position when stopped +- Allow wordlist as fileparameter +- Load only NUM words from input wordlist or use 0 to disable + +* v0.19 -> v0.20: + +- Add dupe suppression +- Add a fake-GMP header using uint128_t macros. This is to replace depency on GMP +- Add --case-permute amplifier option, default is disabled +- Fixed buffer overflow +- Fixed accidental reverted changes +- Fixed a bug where ee actually couldn't correctly support output longer than 31 but 32 is supported +- More memory savings: Use only the actual space needed for each word + +* v0.18 -> v0.19: + +- Fixed missing free() in shutdown section +- Fixed wrong version number in source +- Fixed discrepancies with logic and error messages +- Added validation check pw-max > elem-cnt-max +- Untie IN_LEN_* from PW_* to allow --pw-max > 16 without recompilation +- If out of memory, tell how much we tried to allocate +- Allow hex input for --skip and --limit +- Optimized output performance + +* v0.17 -> v0.18: + +- Fixed major bug where all candidates are of the same length till chain changes + +* v0.16 -> v0.17: + +- Fixed download url for binaries in README +- Fixed copy paste bug in input verification +- Fixed bug where pw_orders is not sorted +- Fixed memory leak +- Removed O_BINARY for stderr +- Removed some unused code +- Renamed variables so that they match the meaning from the presentation slides +- Optimized seeking performance +- Optimized output performance + +* v0.15 -> v0.16: + +- Open Source the project +- License is MIT +- Moved repository to github: https://github.com/jsteube/princeprocessor +- Added CHANGES +- Added LICENSE +- Added README.md +- Changed default value for --pw-max from 24 to 16 for faster startup time + diff --git a/princeprocessor/LICENSE b/princeprocessor/LICENSE new file mode 100644 index 0000000..ebcbc29 --- /dev/null +++ b/princeprocessor/LICENSE @@ -0,0 +1,33 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jens Steube, +Copyright (c) 2015 magnum + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------ + +malloc_tiny() and the hashed dupe suppression are based on code from John the +Ripper password cracker: +Copyright (c) 1996-99,2002-2003,2005-2006,2010-2012 by Solar Designer + +Redistribution and use in source and binary forms, with or without +modification, are permitted. + +There's ABSOLUTELY NO WARRANTY, express or implied. diff --git a/princeprocessor/README.md b/princeprocessor/README.md new file mode 100644 index 0000000..0c39e41 --- /dev/null +++ b/princeprocessor/README.md @@ -0,0 +1,38 @@ +princeprocessor +============== + +Standalone password candidate generator using the PRINCE algorithm + +The name PRINCE is used as an acronym and stands for PRobability INfinite Chained Elements, which are the building blocks of the algorithm + +Brief description +-------------- + +The princeprocessor is a password candidate generator and can be thought of as an advanced combinator attack. Rather than taking as input two different wordlists and then outputting all the possible two word combinations though, princeprocessor only has one input wordlist and builds "chains" of combined words. These chains can have 1 to N words from the input wordlist concatenated together. So for example if it is outputting guesses of length four, it could generate them using combinations from the input wordlist such as: + +- 4 letter word +- 2 letter word + 2 letter word +- 1 letter word + 3 letter word +- 3 letter word + 1 letter word +- 1 letter word + 1 letter word + 2 letter word +- 1 letter word + 2 letter word + 1 letter word +- 2 letter word + 1 letter word + 1 letter word +- 1 letter word + 1 letter word + 1 letter word + 1 letter word + +Detailed description +-------------- + +I'm going to write a detailed description in case I'm extremely bored. Till that, use the following resources: + +- My talk about princeprocessor on Passwords^14 conference in Trondheim, Norway. Slides: https://hashcat.net/events/p14-trondheim/prince-attack.pdf +- Thanks to Matt Weir, he made a nice analysis of princeprocessor. You can find the post on his blog: http://reusablesec.blogspot.de/2014/12/tool-deep-dive-prince.html + +Compile +-------------- + +Simply run make + +Binary distribution +-------------- + +Binaries for Linux, Windows and OSX: https://github.com/jsteube/princeprocessor/releases diff --git a/princeprocessor/pp.save b/princeprocessor/pp.save new file mode 100644 index 0000000..573541a --- /dev/null +++ b/princeprocessor/pp.save @@ -0,0 +1 @@ +0 diff --git a/princeprocessor/pp64.app b/princeprocessor/pp64.app new file mode 100755 index 0000000000000000000000000000000000000000..36fd6dae201605425a9db41ae5779d136f831b71 GIT binary patch literal 26040 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 RMT{;xP);qK?NK}I`M*An70>_x literal 0 HcmV?d00001 diff --git a/princeprocessor/pp64.bin b/princeprocessor/pp64.bin new file mode 100755 index 0000000000000000000000000000000000000000..416507cb73d48faee6d2e6ed32e26099091c8181 GIT binary patch literal 22848 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 literal 0 HcmV?d00001 diff --git a/princeprocessor/pp64.exe b/princeprocessor/pp64.exe new file mode 100644 index 0000000000000000000000000000000000000000..1eaff1d5a897e7523b2f5f44ee28451ff59a0f45 GIT binary patch literal 56832 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 +*34 D7 +*34 K } +*34 l +*34 r +*34 r u +sd> +sdg +sm_ +stc +*34 u r +*34 x13 +stc +*34 x51 +*34 x52 +[ { *35 +{ *35 +*35 ++3 +5 +*35 *04 +*35 +1 +*35 *24 +*35 *31 +-3 -5 -4 +*35 +4 ++3 +5 +4 +*35 *41 +-3 -5 -4 -6 ++3 +5 +4 +6 +} *35 '5 +*35 +5 +} *35 *50 +*35 *50 +5 +*35 *54 +*35 *56 +-3 -5 -6 -4 ++3 +5 +6 +4 +-3 *57 +*35 D2 +{ *35 K +*35 L2 +*35 L3 +*35 r +*35 R4 +*35 t +*35 x12 +*35 x23 +*35 x43 +-3 -6 +-3 '6 +-3 +6 +*36 +*36 [ +*36 ] +*36 { ++3 -6 ++3 +6 +*36 -0 +5 +*36 [ *05 +*36 *13 +*36 *13 x62 +*36 *17 +*36 *19 ++3 *62 +*36 *26 +-3 *64 +*36 +4 +-3 -6 -4 -5 ++3 +6 +4 +5 +-3 *65 +*36 *50 +*36 +5 -0 +-3 -6 -5 -4 ++3 +6 +5 +4 +*36 '6 +*36 c +*36 [ D3 +*36 D3 [ +*36 D3 R0 +*36 D5 +*36 R0 D3 +*36 x32 +] *36 x43 +*36 x62 +*36 x62 *13 +{ *37 +*37 +*37 ] ++3 '7 +*37 *05 +*37 *14 +*37 *32 +*37 *87 +*37 c +-3 '7 u +*37 x12 +*37 x13 +*37 x25 +-3 '8 +*38 +-3 *82 +*38 'A +*38 D0 +*38 x23 +*38 x26 +*39 ++3 '9 +*39 +5 +*39 +8 +*39 'A +*39 D3 -5 +*39 x04 +*39 x05 +*39 x32 +*3A '8 +*3A x05 +*3A x24 +*3B '8 ++3 c +-3 D2 +-3 D2 u ++3 D5 +-3 D6 ++3 D6 +-3 D7 +-3 f x12 +-3 k ++3 k ++3 K ++3 { K ++3 k *23 +-3 l +-3 L2 +-3 r ++3 r ++3 r *34 ++3 R5 +sc6 +s0b +s1d +s2+ +s27 +s27 +sc6 +sCg +sfk +sl0 +smu +snz +soe +spj +sr1 +sr1 +ssp +ssv +sTB +stg +suf ++3 t ++3 T5 +-3 T6 +s27 +-3 u +} +3 u ++3 u +-3 u '7 +-3 u D2 +-3 x02 ++3 x12 +-3 x41 +-3 x51 +-3 x52 ++3 x53 ++3 x71 +-3 x72 +-4 +-4 } +] -4 +] -4 } +} -4 +} +4 +} +4 } ++4 ++4 [ ++4 ] ++4 { ++4 } +-4 -0 +} *40 ++4 +0 +*40 *02 +*40 *06 +*40 *12 +*40 *15 +-4 *02 ++4 *02 +*40 *34 +[ *40 *51 +*40 *54 +*40 *57 +-4 -0 '7 +*40 '9 +*40 *96 +*40 c +*40 D3 +*40 D4 +*40 D6 x41 +[ *40 k +*40 t +*40 u x34 +*40 x24 +*40 x34 +*40 x34 u +*40 x41 D6 +*40 x42 +*40 x42 x61 +*40 x45 +-4 -1 +-4 -1 ] +-4 ] -1 +[ *41 +{ *41 +*41 +*41 ] +*41 { +*41 } } ++4 +1 +*41 -0 +*41 *16 +*41 *32 +-4 *14 +*41 *47 +*41 *86 +*41 ] c +*41 c ] +*41 D1 +*41 f +*41 k { +*41 u +*41 x21 +*41 x32 +*41 x34 +*41 x37 +*41 x47 +-4 -2 +-4 +2 +*42 +*42 } +*42 *21 ++4 *23 +*42 *41 ++4 *25 +-4 *26 +*42 +6 +*42 *64 +*42 r +*42 R0 +*42 R5 +*42 R6 +skL +[ *42 t +*42 t +*42 T0 +*42 x46 +-4 +3 +*43 +*43 [ { +*43 ] +*43 { +*43 { [ +*43 -0 ++4 *30 +*43 *01 } +*43 *12 +*43 *16 +*43 +2 +*43 ] *20 +*43 *20 ] +*43 *35 K +-4 *34 +*43 *48 +*43 -5 +*43 *52 +*43 -5 t +*43 -6 +*43 D2 +*43 k +*43 K +*43 K *35 +*43 R3 +s25 +skC +suo +*43 t +*43 { t +*43 t { +*43 t -5 +*43 T6 +*43 x53 +-4 -4 ++4 +4 +-4 *40 ++4 *40 +-4 *42 +-4 *42 x42 +-4 -4 -4 ++4 +4 +4 +-4 -4 -4 -4 ++4 +4 +4 +4 +-4 *45 +-4 -5 +*45 +*45 { +*45 } ++4 +5 +*45 *01 +*45 *03 +*45 -2 +*45 { *25 +*45 *35 +-4 -5 -6 ++4 +5 +6 +*45 *62 +-4 -5 -6 -7 ++4 +5 +6 +7 +*45 -7 +*45 *71 +-4 -5 -7 -6 ++4 +5 +7 +6 +*45 D0 +*45 r +*45 R1 +*45 R4 +s2_ +sl8 +so9 +*45 T1 +*45 u +*45 x04 +*45 x32 +*45 } x52 +*45 x52 } +-4 '6 +*46 +*46 [ +*46 ] +*46 { ++4 '6 +*46 *16 +*46 *23 +*46 *25 +*46 -4 +*46 +4 ++4 *64 +-4 -6 -5 ++4 +6 +5 +-4 -6 -5 -7 ++4 +6 +5 +7 +*46 '6 +*46 '6 } +*46 } '6 +*46 *60 +*46 -7 +-4 -6 -7 -5 ++4 +6 +7 +5 +*46 D1 +*46 D4 +*46 l +*46 L7 +s6b +*46 T0 +*46 T5 +*46 ] x24 +*46 x24 ] +*47 +-4 '7 -0 +*47 *34 +*47 *36 +-4 -7 -5 -6 ++4 +7 +5 +6 +*47 *58 +-4 -7 -6 -5 +*47 *65 ++4 +7 +6 +5 +*47 *67 +*47 *75 +*47 *81 +*47 -9 +*47 C +*47 D4 +*47 T4 +-4 -8 +{ *48 +} *48 +*48 +*48 [ +*48 *08 +*48 +5 '6 ++4 *86 +*48 '6 +5 +*48 *86 +*48 C x43 +*48 D1 +*48 D1 t +*48 l +*48 T7 +*48 t D1 +*48 x43 +*48 x43 C +*49 ++4 '9 +*49 '9 +*49 L4 +swh +*49 x14 +*49 x24 +*49 x53 +*4A +*4B x16 +*4B x63 +-4 c +{ -4 c ++4 c ++4 C +-4 D2 ++4 D3 +-4 D5 ++4 D5 +-4 D5 L6 +-4 D6 +-4 D7 +'4 f +-4 k +-4 l ++4 l +-4 L4 ++4 L5 +-4 L5 +5 ++4 L7 ++4 r +s0M +s2m +s7 +se. +seg +sK9 +so5 +srB +s.z +-4 t ++4 t +-4 T4 ++4 T5 +-4 u +[ -4 u ++4 u +-4 x03 ++4 x03 +-4 x12 +-4 x31 ++4 x54 ++4 x62 +-4 x63 ++4 x74 +-4 x75 +-4 x82 ++4 x84 +-5 +-5 [ +-5 { +-5 } +'5 +[ -5 } +] -5 ++5 ++5 ] ++5 { ++5 } +} *50 +*50 { ++5 -0 +*50 +0 +*50 *02 +*50 +2 +*50 +3 { +*50 *31 +*50 -4 +*50 *41 +*50 +5 +*50 c K +*50 K c +*50 L5 +} *50 r +*50 R5 +*50 t +*50 u +*50 x23 +*50 x26 +*50 x32 +*50 x43 +*50 x51 +*50 x53 +'5 -1 +[ *51 +*51 +*51 [ +*51 { +*51 -0 +*51 +1 +*51 *14 +*51 ] *25 +*51 *25 ] +-5 *13 +'5 *13 +*51 -3 +*51 *31 +*51 *37 +'5 *14 +[ *51 *40 +'5 *14 *20 +*51 *52 +*51 *54 +-5 *16 +*51 { +6 +{ *51 C +*51 C +*51 D1 *12 +*51 D2 +*51 D5 +*51 K +*51 K R6 +-5 +1 L0 +*51 r +*51 R1 +-5 +1 R5 +*51 R6 +*51 R6 K +*51 r K +*51 T6 +*51 x32 +*51 x32 *30 +*51 x35 +-5 +2 +*52 [ +*52 ] ++5 [ -2 +'5 *20 +*52 +0 +'5 *20 *14 +*52 +1 +*52 *24 +-5 *24 +-5 *25 +*52 *53 u +-5 *25 l +*52 '6 +*52 +6 +*52 *70 +*52 ] c +*52 c ] +s3v +*52 [ k +*52 L0 +s3v +se0 +shl +srd +srd +*52 u +*52 u *53 +*52 x04 +*52 x12 +*52 x61 +-5 +3 +'5 -3 +'5 +3 +*53 +*53 [ +*53 ] +*53 +0 +} *53 -1 +*53 *12 +*53 *12 L5 +*53 *20 +*53 -4 +*53 *46 +*53 '5 ++5 *35 +*53 *50 +saj +*53 -7 +*53 +8 +*53 C +*53 f +*53 L5 +*53 L5 *12 +*53 L7 +*53 L7 u +-5 +3 R3 +*53 R5 +*53 R6 +s5s +saj +sbm +sbn +smb +sun +sur +*53 T4 +*53 T5 +*53 u +*53 x13 +*53 x31 +*53 x41 +*53 x52 +*53 x52 T5 +-5 +4 +'5 +4 +{ *54 +*54 +*54 } ++5 -4 +*54 +0 +[ *54 +2 +-5 *43 +*54 *35 t +*54 -4 +-5 *46 +*54 +7 +*54 *71 '5 +*54 -8 +*54 *86 +*54 c +*54 C +*54 D2 +*54 D7 +*54 l +*54 R3 +sBN +svN +syn +*54 t +*54 t *35 +sBN +*54 x12 +*54 x23 +*54 x64 +-5 -5 ++5 +5 +-5 *50 +-5 *50 K ++5 *54 +-5 -5 -5 ++5 +5 +5 +-5 -5 -5 -5 ++5 +5 +5 +5 +-5 -6 +-5 '6 +] { *56 +*56 ++5 -6 ++5 '6 ++5 +6 ++5 *60 +*56 *13 u +-5 *62 '6 +*56 *30 +*56 -4 +*56 +4 +*56 +5 ++5 *65 +*56 -6 +*56 } -6 +-5 -6 -7 +*56 '7 ++5 +6 +7 +*56 *76 +-5 -6 -7 -8 ++5 +6 +7 +8 +-5 *68 +*56 *81 x03 +*56 *85 +-5 -6 -8 -7 ++5 +6 +8 +7 +*56 'A +*56 D0 +*56 l +*56 L0 L5 +*56 L5 L0 +*56 r +*56 R4 +s40 +se2 +sgo +*56 u +*56 u *13 +*56 x03 +*57 +*57 ] +*57 } ++5 -7 ++5 +7 +*57 *06 +*57 *25 +*57 ] *36 +*57 *36 ] +-5 *74 +*57 *46 +-5 *75 +-5 *75 *56 +-5 -7 -6 ++5 +7 +6 +-5 -7 -6 -8 ++5 +7 +6 +8 ++5 *78 +-5 -7 -8 -6 ++5 +7 +8 +6 +-5 *79 ++5 *79 +*57 c +*57 c D6 +*57 D3 +*57 D6 +*57 D6 c +*57 D8 +sdl +*57 T5 D3 +*57 x15 +*57 x32 +-5 +8 +*58 +*58 ] { +*58 { ] ++5 '8 +-5 -8 -6 -7 ++5 +8 +6 +7 +-5 -8 -7 -6 +*58 *76 ++5 +8 +7 +6 +*58 '8 +*58 D4 +*58 D5 +s13 +*58 D7 +*58 l +s13 +*58 x91 +-5 -9 +-5 '9 +*59 ++5 '9 +*59 D2 +*59 x25 +*59 x42 +*5A +*5A x43 +*5A x73 +-5 c ++5 c +-5 C +'5 C +-5 D1 ++5 D1 +-5 D2 +-5 D3 +sw7 +-5 D4 ++5 D4 +-5 D6 ++5 D6 +'5 f ++5 f +-5 k ++5 k +-5 K +-5 K *50 +-5 l +'5 l ++5 l ++5 L0 +-5 L0 +1 +-5 l *25 ++5 L6 +-5 r [ ++5 r +-5 R3 +3 +-5 R5 +1 +s1i +s1N +s36 +s6a +s6b +s6v +saj +sb- +sgm +shH +sjy +sL7 +slr +smj +sro +suV +sw7 +-5 t +'5 t ++5 t +-5 T0 +-5 T2 +-5 T3 ++5 T3 ++5 T4 ++5 T7 +'5 u ++5 x01 +-5 x02 +-5 x03 ++5 x04 ++5 x62 +-5 x63 ++5 x63 +-5 x81 +-6 +-6 [ +-6 ] +-6 } +'6 +{ +6 +} -6 +} '6 ++6 ++6 [ ++6 { +-6 +0 +] *60 +*60 *01 +*60 *02 +*60 *07 +*60 -1 ++6 *02 +'6 *03 +*60 -5 +*60 *54 +*60 -8 +*60 c +*60 C +*60 D0 +*60 D1 +*60 } D4 +*60 D4 } +*60 D6 +*60 k T5 +*60 T5 k +*60 u x34 +*60 x03 +*60 x34 u +*60 x35 +-6 -0 x51 +*60 x51 +*60 x54 +-6 -1 +'6 -1 +'6 +1 +} *61 ++6 +1 +*61 -1 +*61 *21 +*61 -4 ++6 *14 +*61 '5 +*61 D6 +*61 T0 +*61 T2 +*61 x03 k +*61 x12 +*61 x21 +*61 x21 *13 +*61 x52 +*61 x54 +*61 x61 +6 +*62 } +-6 *20 +*62 -0 +'6 *21 +'6 *21 u +*62 -2 +*62 +2 +'6 *23 +*62 *46 +-6 *24 D0 +*62 *52 +*62 *73 +*62 D4 x43 +*62 l +*62 x01 r +*62 x02 +*62 x03 +*62 x32 +*62 x35 +*62 x38 +*62 x43 D4 +*62 x45 +*62 x46 +*62 x51 +-6 -3 +'6 +3 +} *63 +*63 } +*63 +1 ++6 *32 ++6 *32 l +*63 '5 +*63 +7 +*63 D3 +*63 D7 +*63 K +*63 L6 +slp +*63 T2 +*63 u +*63 x53 +'6 +4 +*64 ++6 +4 +*64 *20 ++6 *43 +*64 *34 +*64 '5 +*64 *65 [ +'6 +4 c +*64 D0 +1 +*64 K +*64 R3 +*64 x53 +*64 x61 +*64 x72 +*64 x73 +'6 +5 +{ *65 +*65 +*65 [ { +*65 { [ ++6 -5 +'6 *51 +*65 *24 +*65 +3 +*65 *35 +'6 *54 +*65 -5 +*65 *51 ++6 *57 +*65 *73 +*65 *8A +*65 '9 +*65 D3 +*65 R3 +*65 R7 +*65 x42 +*65 x65 +-6 -6 ++6 +6 ++6 *60 +-6 *63 ++6 *63 +-6 -6 -6 ++6 +6 +6 +-6 -6 -6 -6 ++6 +6 +6 +6 +-6 *67 +-6 -7 +[ *67 +*67 ++6 -7 ++6 +7 +-6 *70 ++6 *73 +*67 -5 +*67 *56 +scu +-6 -7 -8 ++6 +7 +8 +*67 *86 +-6 -7 -8 -9 ++6 +7 +8 +9 +-6 -7 -9 -8 ++6 +7 +9 +8 +*67 c +*67 D9 +*67 k +*67 K +*67 r +*67 R4 +s- +s1/ +scu +*67 T3 +-6 -8 +*68 +*68 *20 ++6 *83 D3 +-6 *85 +*68 -6 +-6 -8 -7 ++6 +8 +7 +-6 -8 -7 -9 ++6 +8 +7 +9 +*68 *85 +-6 -8 -9 -7 ++6 +8 +9 +7 +*68 D6 +*68 x42 +*68 { x52 +*68 x52 { +-6 -9 +*69 ++6 '9 +-6 -9 -7 -8 ++6 +9 +7 +8 +*69 '8 +-6 -9 -8 -7 ++6 +9 +8 +7 +*69 'A +*69 x92 +*6A ++6 'A +*6A D7 +s70 +*6A x34 +s70 +*6A x37 +*6A x94 +*6B x16 +*6B x84 +-6 c +'6 c ++6 c +-6 C +'6 c +4 +-6 D0 +'6 D1 +'6 D2 ++6 D2 ++6 D3 +-6 D4 ++6 D4 ++6 D7 ++6 D8 +'6 f x45 ++6 k +-6 l +'6 l ++6 l +-6 L0 +'6 L0 +'6 L1 +s61 +-6 L2 +'6 L2 +-6 L2 u ++6 l *32 +-6 L5 +'6 L5 +-6 L6 +'6 R2 ++6 R2 +sob +-6 R4 +'6 R5 ++6 R5 +-6 R6 +-6 R6 u +s0g +s3* +s57 +s61 +s87 +sfs +shb +sjv +skP +slg +slK +snG +sob +sqg ++6 t +-6 T0 +'6 T1 +-6 T1 x51 +-6 T2 +-6 T7 ++6 T7 +-6 u +'6 u ++6 u +'6 u *21 +-6 u L2 ++6 x02 +-6 x03 +-6 x04 ++6 x12 +-6 x13 ++6 [ x21 ++6 x21 [ +-6 x23 +-6 x31 ++6 x31 ++6 x32 ++6 x42 +-6 x51 ++6 x51 +-6 x51 -0 +-6 x51 T1 +-6 x72 ++6 x73 +-6 ] x74 +-6 x74 ] ++6 x74 +-6 x82 +-6 x84 +-7 +-7 [ +-7 } +'7 [ +'7 } +[ +7 +{ -7 +{ +7 +} '7 ++7 ++7 [ ++7 } +[ *70 +*70 +*70 ] ++7 +0 +*70 *05 ++7 +0 *13 +'7 +0 +4 +*70 *41 x52 +*70 '5 +-7 *06 +-7 *06 u +*70 +9 +*70 L0 +*70 L0 T1 +*70 t +*70 T1 L0 +*70 x01 +*70 x52 +*70 x52 *41 +*70 x53 +*70 x64 +*70 x72 +*71 [ ++7 -1 +*71 *29 ++7 *13 +0 +'7 ] *14 +'7 *14 ] +*71 '5 +*71 *62 +*71 -7 +*71 '7 +*71 '9 +*71 c +*71 D2 r +*71 D6 +*71 l +*71 x02 +*71 x03 +*71 x04 +*71 x13 +*71 x35 +} [ *72 +*72 +*72 -2 +*72 +3 +*72 -4 +*72 '6 +*72 D7 +s32 +*72 x03 +*72 x23 +*72 x53 +*72 x73 +-7 -3 +'7 -3 +*73 +*73 [ +'7 *30 +*73 *02 +'7 *32 +*73 *72 +*73 *72 C +*73 C +*73 C *72 +*73 D1 +*73 L5 +*73 r +*73 x21 +*73 x31 +*73 x62 +*73 x74 +'7 -4 +*74 +'7 +4 +0 +'7 [ *41 +-7 *43 +*74 -3 +*74 +4 +*74 +5 +'7 *46 +*74 c +*74 D1 +*74 k +*74 L5 +s6a +sds +*74 u +*74 u x72 +*74 x31 +*74 x34 +*74 x54 +*74 x56 +*74 x64 +*74 x65 +*74 x72 +*74 x72 u +'7 -5 +'7 +5 +*75 +*75 [ +*75 { ++7 -5 ++7 *56 [ ++7 *57 +*75 *76 u ++7 *59 +'7 -5 C +*75 D1 +} *75 D3 +*75 D4 +*75 D5 +*75 D6 +sGw +*75 t +*75 T5 +*75 u +*75 u *76 +sGw +'7 -6 +'7 +6 +*76 +*76 ] +*76 } ++7 -6 +*76 *31 +*76 *34 +*76 *36 +*76 *47 +-7 *65 +*76 +5 +*76 +6 +*76 +6 x42 +*76 +7 +-7 *68 +*76 '8 +*76 +8 +*76 -9 +*76 'B +*76 l +s17 +s51 +scv +sti +sti +*76 x21 +*76 x41 +*76 x42 +*76 x52 +*76 x74 +-7 -7 ++7 +7 +-7 *70 +-7 *73 +{ +7 *75 +-7 *76 +-7 -7 -7 ++7 +7 +7 +-7 -7 -7 -7 ++7 +7 +7 +7 +-7 -8 +*78 ++7 -8 ++7 +8 +*78 *31 +*78 -6 +*78 +6 +*78 *62 +-7 *87 +-7 -8 -9 +*78 '9 ++7 +8 +9 +*78 -A +s71 +*78 T3 +*78 x31 ++7 -8 x31 +*78 x53 +*78 x62 +-7 -9 +-7 +9 +*79 ++7 '9 +-7 -9 -8 +*79 -8 ++7 *98 ++7 +9 +8 +*79 -9 +*79 *95 +*79 'A +*79 x81 +-7 'A +*7A 'A +*7A x53 +*7A x62 +*7A x92 +*7B x64 +-7 c +'7 c ++7 c +'7 C +'7 C -5 +'7 c x01 +'7 D1 ++7 D1 ++7 D2 ++7 D2 x32 +'7 D3 +-7 D4 +'7 D4 +-7 D6 +-7 D8 +'7 f +-7 l +'7 l ++7 l ++7 L3 +sul ++7 r ++7 r [ +-7 R2 ++7 R3 +'7 R5 ++7 R5 +s0s ++7 R6 +s0s +s1- +s6/ +s7r +sat +saZ +scu +sdr +sfb +sfn +skD +srv +sul +sul +-7 t +'7 T1 ++7 T2 +'7 T3 +-7 T4 ++7 T4 +[ +7 T8 +-7 u +'7 u ++7 u +-7 u *06 +'7 x01 +'7 x01 c +-7 x03 ++7 x12 ++7 x24 ++7 x31 ++7 x32 D2 ++7 x41 ++7 x43 +'7 x51 ++7 x82 +-7 x94 ++7 xA2 +-8 +-8 [ +'8 +'8 [ +[ -8 +{ -8 ++8 ++8 } +-8 -0 +'8 +0 +} } *80 +'8 *02 +*80 *38 +*80 '6 +*80 D3 +*80 l x45 +*80 x04 +*80 x31 +*80 x36 +*80 x45 l +'8 *12 +*81 '7 +*81 '7 t +*81 '8 +*81 +8 +*81 t '7 +-8 +2 +'8 -2 ++8 -2 +*82 '6 +*82 'A +*82 l +*82 T5 ++8 -2 u +*82 x06 +*82 x45 +*82 x72 +*82 x81 +-8 -3 +'8 +3 +[ *83 +*83 +'8 *32 +*83 +3 +*83 *47 +*83 -7 +*83 D5 +s2+ +*83 x04 +*83 x54 +*83 x64 +*83 x64 -7 +*83 x81 +-8 +4 +*84 '6 +*84 '7 +*84 D8 +*84 u +*84 x23 +*84 x53 +'8 -5 +*85 +*85 +1 D8 +-8 *53 +*85 +5 +'8 *56 +*85 '6 +*85 *63 +-8 *57 +*85 '7 +*85 D8 +*85 D8 +1 +*85 x23 +*85 x42 +-8 +6 +'8 -6 +'8 +6 +*86 ++8 -6 ++8 +6 +*86 *50 +*86 +7 +*86 *70 +*86 -8 +*86 '8 +*86 *95 ++8 +6 C +*86 D3 +*86 R0 +sr5 +*86 x14 +*86 x34 +*86 x41 +*87 +*87 { ++8 -7 +'8 *70 ++8 *70 +*87 *36 +'8 *74 +-8 *76 +*87 +8 ++8 *78 +-8 *7B x62 +*87 c +s81 +sti +*87 { t +*87 t { +*87 t x26 +*87 x26 t +-8 -8 ++8 +8 +-8 -8 -8 ++8 +8 +8 +-8 -8 -8 -8 ++8 +8 +8 +8 +-8 *8A +-8 -9 +-8 +9 +*89 +*89 [ +*89 } ++8 -9 ++8 '9 ++8 +9 +*89 -7 +*89 +8 ++8 *9A +*89 D5 +*89 x63 x04 +*8A ++8 +A +*8A D8 +*8A t +*8A x54 +-8 *BA +] *8B x63 +*8B x64 +-8 c +'8 c ++8 c ++8 C +6 +-8 c x12 +-8 c x25 ++8 D0 ++8 D4 +'8 D4 +6 +-8 D6 +-8 D7 +-8 DA +'8 K +-8 l +-8 [ l +-8 l [ ++8 l +'8 L3 +'8 [ L6 +'8 R2 ++8 R6 +-8 R7 ++8 R7 +siz +sjs +spi +-8 t +'8 t ++8 T2 +-8 T4 ++8 T6 +'8 T7 +[ '8 T7 +'8 u ++8 u ++8 u -2 +-8 x04 +-8 x12 +-8 x12 c +-8 x16 ++8 x17 ++8 x21 +-8 x24 +-8 x25 +-8 x25 c ++8 x26 ++8 x42 +'8 x51 ++8 x52 +'8 x61 +-8 x62 +-8 x91 +-9 +-9 } +'9 +'9 [ +[ +9 +{ +9 +} -9 } +} '9 ++9 ++9 [ +-9 -0 +-9 +0 +'9 +0 ++9 +0 +-9 *01 +'9 *08 +*90 L0 +*90 x26 +} *91 +*91 +*91 *37 +*91 R8 +*91 x43 +'9 -2 +'9 +2 +*92 '9 +*92 x46 +*92 x56 +'9 -3 +*93 *43 +*93 '9 +*93 c +*93 x21 +-9 +3 x71 +-9 -4 +'9 -4 +*94 '8 +*94 u +*94 x34 +*94 x61 +*94 x73 +*95 *27 +'9 *53 +-9 *56 +*95 *87 +*95 D8 +*95 D8 u +*95 u D8 +'9 -6 +*96 ++9 +6 +*96 D7 +*96 x02 +*96 x15 +*96 x52 +*97 ++9 +7 +*97 *21 +*97 *32 x34 +'9 *76 +*97 c +*97 D1 +syi +*97 u +*97 x34 +*97 x36 +*98 +0 +*98 +1 +'9 *83 +*98 *32 D9 +'9 *85 +*98 *85 +*98 '9 +*98 +9 +*98 D9 +*98 D9 *32 +*98 x71 +-9 -9 ++9 +9 +-9 *98 +-9 -9 -9 ++9 +9 +9 +-9 -9 -9 -9 ++9 +9 +9 +9 +*9A ++9 'A +*9A *89 +s1c +*9A x73 +*9B *68 +*9B x62 ++9 c +-9 D0 ++9 D0 +6 +-9 D1 +'9 D2 ++9 D3 +'9 D4 ++9 D5 +-9 D7 ++9 D7 ++9 D8 +-9 l +'9 l ++9 l +-9 L9 +'9 r ++9 R1 +-9 R8 +'9 R8 +s09 +s25 +s91 +s9e +sah +se2 +sfg +[ -9 T0 +-9 T2 +'9 T2 +'9 T5 +'9 T8 +-9 t x02 +-9 u +'9 u ++9 u +-9 u x24 +-9 x02 t ++9 x15 ++9 x23 +-9 x24 +-9 x24 u +-9 x25 ++9 x34 +-9 x41 +'9 x43 ++9 x45 +-9 x53 +'9 x61 +-9 x71 +3 ++9 x72 +-9 xA2 +-A +[ -A +[ 'A +} 'A ++A +*A0 { +*A0 *5A x46 +*A0 x56 +'A +1 +*A1 D1 +*A1 RA +*A1 x16 +*A1 x83 +'A +2 +*A2 +*A2 '8 +'A +3 +*A3 '7 +*A3 x75 +*A3 x93 +*A4 -4 'A +*A4 '9 +*A4 'A -4 +*A4 x86 +*A5 '8 +*A5 *B7 '8 +*A5 x76 +*A6 '7 +*A6 *A9 +*A6 x26 +*A6 x42 +'A +7 +*A7 +*A7 [ +*A7 '8 +*A8 [ ++A *8A +*A8 l +s0x +*A8 x35 +-A +9 +*A9 ++A *9A +s05 +*A9 x52 +-A *A8 +*AB +'A c +'A C +'A D1 +'A D3 ++A D4 +'A D7 ++A DB +'A l +'A L1 +-A LA +'A R9 ++A R9 +s97 ++A t +'A T5 +'A T8 +-A x01 +-A x35 +'A x41 +'A x42 ++A x45 +'A x52 ++A x53 +'A x61 ++A x62 +'A x62 +6 ++A x82 ++A x91 +-B ++B [ +*B0 '8 +*B1 x03 +*B1 x45 +'B *23 +*B2 '7 +*B2 '8 +*B5 '8 +*B5 xA1 +*B7 '8 +*B8 'A +*B8 *A9 +-B -9 +*B9 +'B *97 +*B9 'A ++B *9A +s29 +*B9 x65 +-B -A +-B +A +'B +A +*BA +*BA c +*BA D9 +*BA x02 +*BA x83 +'B D3 +'B D4 +-B D7 +'B k +-B l +'B l ++B l +'B r +'B [ R1 +soa ++B x02 ++B x62 +'B x71 +'B x72 ++B x73 ++B x92 +c +[ [ c +[ [ [ c +[ [ [ [ c +] c +] ] c +] c ] +{ c +{ { c +{ { { c +{ { { { c +{ { { c { +{ { c { +{ { c { { +{ c { +{ c { { +{ c { { { +} c +} } c +} } } c +} } } } c +} } } c } +} } c } +} } c } } +} c } +} c } } +} c } } } +c { +c } +C +[ C +{ C +{ [ C +{ C [ +C [ +C } +c +0 +c *05 +c +0 *52 +c *05 D7 +C -1 +C +1 +c *14 K +c *16 +} c -2 +c -2 +C -2 +C +2 +c *21 +c *24 +c *24 T0 +c *25 x13 +c -3 +[ c *31 +c -3 '6 +{ c -4 +C -4 +c *40 t +c *46 +c -4 T4 +c +4 T5 +C -5 +C +5 +{ C *51 +C *52 +c *52 +0 +c *58 +C [ +6 +C +6 +C *61 +c '6 -3 +c *65 +c *68 +C -7 +C +7 +c *70 +C *71 +c +7 L3 +C '8 +c *83 +c *8A +c -9 +c '9 +C -9 +c '9 T3 +C +9 x34 +c +A +c -B +c +B T6 +C D0 +c D0 D8 +s5P +c D1 +C D3 +c D4 +C D4 +c D5 +C D5 +] c D6 +c D6 +C D6 +c D7 +c D7 *05 +c DA +c f +C f +c K +c [ K +c K [ +C k +c K *14 +c k *16 +c L0 +c L3 +7 +c L5 +C L5 +c r +C r +C R0 +C R1 +c R2 +[ c R3 +c R3 +C R5 +c R6 +c R7 +s0# +s04 +s0I +s1, +s10 +s2* +s29 +s2I +s2m +s49 +s4b +s4K +s5P +s62 +s8! +s87 +s8M +sa@ +sa4 +sa4 +sa@ +se3 +se3 +sa@ +sa@ +si1 +si1 +si! +sa@ +sa@ +so0 +so0 +so0 +sa@ +sa@ +sa@ +saw +sb1 +sb8 +sd1 +se3 +se3 +sa@ +sa@ +se3 +se3 +si1 +si1 +si! +se3 +se3 +so0 +so0 +so0 +se3 +se3 +se3 +se3 +se3 +se3 +se3 +se3 +seu +sex +sg5 +sg9 +sgV +si! +si1 +si1 +sa@ +sa@ +si1 +se3 +se3 +si1 +so0 +so0 +si1 +si1 +si1 +si1 +si1 +si1 +si1 +si2 +si! +sa@ +si! +se3 +si! +si! +so0 +so0 +si! +si! +si! +si! +si! +si! +siu +siU +siZ +sJw +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +slt +slz +sMz +snj +so* +so0 +so0 +so0 +sa@ +sa@ +sa@ +so0 +se3 +se3 +se3 +so0 +so0 +si1 +si1 +si! +si! +so0 +so0 +so0 +so0 +so0 +so0 +so0 +so0 +spv +ss3 +ss4 +ss5 +ss5 +ss- +ssN +ssO +st7 +stC +sti +su[ +su4 +svz +sy1 +sy2 +c T0 *24 +c T1 +C T1 +c T1 f +se3 +si! +si1 +sl1 +so0 +c T1 T2 +c T1 T2 T3 +c T1 T2 T4 T5 +c T1 T2 T5 T4 +c T1 T3 +c T1 T3 T2 +c T1 T4 T2 T5 +c T1 T4 T5 T2 +c T1 T5 T2 T4 +c T1 T5 T4 T2 +c T2 +c T2 f +se3 +si! +si1 +sl1 +so0 +c T2 T1 +c T2 T1 T3 +c T2 T1 T4 T5 +c T2 T1 T5 T4 +c T2 T3 +c T2 T3 T1 +c T2 T4 +c T2 T4 T1 T5 +c T2 T4 T5 +c T2 T4 T5 T1 +c T2 T4 T6 +c T2 T4 T6 T8 +c T2 T4 T8 T6 +c T2 T5 T1 T4 +c T2 T5 T4 +c T2 T5 T4 T1 +c T2 T6 +c T2 T6 T4 +c T2 T6 T4 T8 +c T2 T6 T8 T4 +c T2 T8 T4 T6 +c T2 T8 T6 T4 +C T3 +c T3 '9 +se3 +si! +si1 +sJw +so0 +C T3 t +c T3 T1 +c T3 T1 T2 +c T3 T2 +c T3 T2 T1 +C T3 x42 +c T4 +C T4 +c t *40 +c T4 -4 +sa@ +se3 +si! +si1 +sl1 +so0 +c T4 T1 T2 T5 +c T4 T1 T5 T2 +c T4 T2 +c T4 T2 T1 T5 +c T4 T2 T5 +c T4 T2 T5 T1 +c T4 T2 T6 +c T4 T2 T6 T8 +c T4 T2 T8 T6 +c T4 T5 T1 T2 +c T4 T5 T2 +c T4 T5 T2 T1 +c T4 T6 T2 +c T4 T6 T2 T8 +c T4 T6 T8 T2 +c T4 T8 T2 T6 +c T4 T8 T6 T2 +c T5 +C T5 +c T5 +4 +sa@ +se3 +si! +si1 +sl1 +so0 +c T5 T1 T2 T4 +c T5 T1 T4 T2 +c T5 T2 T1 T4 +c T5 T2 T4 +c T5 T2 T4 T1 +c T5 T4 T1 T2 +c T5 T4 T2 +c T5 T4 T2 T1 +] c T6 +c T6 +c T6 +B +sa@ +se3 +si! +si1 +sl1 +so0 +c T6 T2 +c T6 T2 T4 +c T6 T2 T4 T8 +c T6 T2 T8 T4 +c T6 T4 T2 +c T6 T4 T2 T8 +c T6 T4 T8 T2 +c T6 T8 T2 T4 +c T6 T8 T4 T2 +C T7 +se3 +si1 +sl1 +so0 +c T8 +c T8 T2 T4 T6 +c T8 T2 T6 T4 +c T8 T4 T2 T6 +c T8 T4 T6 T2 +c T8 T6 T2 T4 +c T8 T6 T4 T2 +c T9 +C T9 +c TA +C TA +c TB +C t T3 +c x01 +C x01 +C x01 -5 +c x02 +C x02 +c x02 L1 +c x03 +c x12 +C x12 +C x14 +C x15 +{ c x21 +c x23 +c x24 +c x25 +c x26 +C x31 [ +c x32 +C x32 +c x35 +c x36 +C x42 +C x42 T3 +c x43 +C x43 +s87 +c x52 +C x52 +c x53 +C x53 +c x54 +c x64 +c x65 +C x73 +[ c x74 +c x84 +c x92 +C x92 +c xA2 +D0 +D0 } +D0 *01 } +D0 +1 +D0 *14 +D0 *30 +D0 *30 t +D0 } *54 +D0 'B +D0 c +D0 c x91 +D0 D1 +D0 D1 k +D0 D8 +D0 k t +D0 l +D0 L4 D1 +D0 R2 t +D0 R4 +s30 +s4C +s7b +s9A +se6 +seh +sfw +skv +smd +spe +D0 t *30 +D0 t k +] D0 u +D0 x13 +D0 x14 +D0 x43 +D0 x71 +D0 x91 +D0 x91 c +D1 +[ D1 +[ { D1 +[ D1 { +D1 +0 +D1 *12 +D1 *13 +D1 *21 +D1 -3 +D1 +3 +D1 -3 '6 +D1 +5 +D1 '6 -3 +D1 '8 +D1 'A +D1 C +D1 D2 +D1 D4 +D1 D5 +] D1 f +D1 f +D1 k +D1 K +D1 l +D1 L0 +D1 L1 +D1 L3 +D1 r { +D1 R0 +sD4 +sds +si6 +sih +sn_ +{ D1 t +D1 t +D1 T1 +D1 T2 +D1 T3 +D1 u +sD4 +D1 x35 +D1 x72 +D1 x91 +D2 +D2 { +D2 } +D2 *02 +D2 -1 +D2 -1 x34 +D2 +3 +D2 *46 +D2 -5 +D2 *53 +D2 -6 +D2 '6 +D2 '7 +D2 'A +D2 c +D2 C +D2 c L3 +D2 D3 +D2 l +D2 L2 +D2 L3 +D2 L3 c +D2 r +D2 R1 +D2 R4 +D2 R6 +s1` +s3- +sdy +sdy +sgc +sgy +sm1 +snq +soV +suq +D2 t +D2 T0 +D2 T3 +D2 T4 +D2 T5 +D2 T6 +sdy +D2 u +D2 x34 -1 +D2 x41 +D2 x45 +D2 x53 +D2 x73 +D3 +{ D3 +D3 [ +D3 ] +D3 } +D3 -1 +D3 +1 +D3 -1 c +D3 *34 +D3 *42 +D3 -5 +D3 *51 +D3 *56 +D3 *56 k +D3 -6 +D3 +6 +D3 +6 } +D3 '7 +D3 -8 +D3 +A x64 +D3 'B +D3 c +D3 c -1 +sAl +D3 C x32 +D3 D8 +D3 k +D3 k *56 +D3 l +[ D3 R0 +] D3 R0 +D3 R0 +D3 R2 +D3 R3 +s3 +s85 +s9D +sao +sbz +sck +sea +slW +D3 t +D3 T3 +D3 T5 +D3 x32 +D3 x32 C +D3 x47 +D3 x54 +D3 x65 +D3 x71 +D3 x72 +D3 x75 +D3 xA1 D4 +D3 xA1 x54 +D4 +[ D4 +} D4 +D4 { +D4 } +D4 *14 +D4 *15 +D4 *21 T4 +D4 *25 +D4 +3 +D4 '5 +D4 +6 +D4 *62 +D4 -7 +D4 '7 +D4 *85 +D4 *87 +D4 *87 l +D4 { D2 +D4 D2 { +D4 D7 +D4 f +D4 k +D4 K +D4 k L7 +sah +D4 l +D4 L5 +D4 L6 +D4 L7 +D4 L7 k +D4 l *87 +D4 r +D4 R3 +s3v +sah +sah +sg +sg- +sj# +skD +ssx +stG +sxp +D4 t +D4 { t +D4 t { +D4 T0 +D4 T1 +D4 T4 +D4 T4 *21 +D4 T5 +D4 T6 +D4 x02 +D4 { x21 +D5 +] D5 +D5 } +D5 -1 +D5 +1 +sa4 +D5 *24 +D5 +2 K +D5 *41 +D5 *43 K +D5 *45 +D5 *46 +D5 *57 +D5 +6 +D5 *65 +D5 -8 +D5 } D2 +D5 D2 } +D5 D7 +D5 DA +] D5 K +D5 K +D5 K +2 +D5 K *43 +D5 l +D5 L4 +D5 L8 +D5 R1 +D5 R4 +s0! +s13 +s2< +s63 +sa4 +sKp +spz +sr +sr +suw +D5 t +D5 T0 +D5 u +D5 x04 +D5 x12 +D5 x13 +D5 x54 +sr +D6 +] D6 +D6 [ +D6 { +D6 } +D6 *05 +D6 *15 +D6 +2 +D6 *24 +D6 +2 T6 +D6 +4 +D6 +6 +D6 +7 +D6 *9A +D6 +A +] D6 c +D6 l +D6 L1 +D6 L2 +D6 L5 +D6 L6 +D6 l x91 +D6 R1 +D6 R4 +D6 R6 +sKk +s2; +s82 +s9k +s9k +sbk +sby +seb +sGM +sKk +sKk +sn. +sng +D6 { T1 +D6 T6 +2 +D6 x03 +D6 x31 +D6 x41 +D6 x72 +D6 x91 +D6 x91 l +D6 x92 +D7 +D7 *05 +D7 *05 L2 +D7 *29 +D7 +6 [ +D7 *67 +D7 +A +D7 D0 +D7 D3 +D7 D6 +D7 D7 +D7 D8 +D7 D9 +D7 l +D7 L0 +D7 L2 +D7 L2 *05 +D7 l xA3 +D7 R5 +se2 +skt +D7 t +D7 T5 +D7 u +D7 x12 +D7 x23 +se2 +D7 xA3 l +D8 +} D8 +D8 *02 +D8 -4 +D8 *56 +D8 c +D8 k +D8 L0 +s3 +D8 u +D8 u x91 +D8 x23 +D8 x24 +D8 x91 +D8 x91 u +D9 +D9 *08 +D9 -5 +D9 +6 x81 +D9 -7 +D9 +7 +D9 -A +D9 'A +D9 c +D9 D4 +D9 D6 +D9 l +D9 L7 +D9 T3 +D9 x32 +D9 x81 +6 +DA +DA *86 +DA -A +DA -B +DA *BA +DA D4 +DA D5 +DA l +DA R7 x25 +DA x31 +DA x54 +DA x63 x21 +DB +A l +DB D0 +DB l +A +s68 +DB x13 +DB x62 +DB x64 +DB x71 +DB x91 +f +[ f +] f +{ f +} f +f [ { +f ] +f { +f { [ +f } +f +0 +f *06 x13 +f +0 x16 +f +0 x29 +f *15 +f *16 +f -2 +f +2 +f *20 +f *21 +f *24 +f ] *25 +f *25 ] +f -2 x3B +f *31 +f *36 +f *37 +f -4 +f +4 +f -5 +f +5 +f *50 +f ] *51 +f *51 ] +f *53 +f +6 +f *60 +f { *65 +f *67 +f *69 +f +6 x7A +f -7 +f '7 +f +7 +f *71 +f *75 +f *78 +f *79 +s!g +f '8 +f *86 +f '9 +f +9 +f *94 +f *98 x27 +f *9B +f '9 x61 +f 'A +f *A5 '8 +f 'B +f c +f C +f c D0 +f c x08 +f D0 +f D0 c +f D1 +f D1 x58 +f D2 +f D3 +f D4 +f D8 +f DB +f f +f k +f K +f K L3 +f k x3A +f K x69 +f l +f L2 +f L3 K +f R4 +f R6 +f R8 +s4k +s!g +sns +ss0 +sts +f t +f T0 +f T0 x34 +f T0 x5A +f T4 +f T4 D0 +f T6 x75 +f T9 x08 +f t x65 +f [ u +f u [ +f x02 +f x03 +f x04 +f x05 +f x06 +f x08 c +f x0A +f x13 +f x14 +f x15 +f x16 +f x16 +0 +f x17 +f x17 *0A +f x18 +f { x18 +f x18 { +f x18 *50 +f x19 +f ] x19 +f x19 ] +f x1A +f x1B +f x23 +f x24 +f x25 f +f x26 +f x27 +f x28 +f x29 +f x29 +0 +f x2A +f x2B +f x31 *31 +f x34 +f x34 T0 +f x36 *25 +f x37 +f x38 +f x39 +f x3A +f x3A k +f x3B -2 +f x42 +f x42 *40 +f x43 +f x45 +f x46 +f x47 +f x48 +f x48 D8 +f x49 +f x4A +f x4A L5 +f x4B +] f x51 +f x51 +f x52 +f x53 +f } x53 +f x53 } +f x54 +f x56 +f x57 +f x58 D1 +f x59 +f [ x59 +f x59 [ +f x5A +sts +f x5A T0 +f x5B +f x61 +f x63 +f x64 +f x64 L7 +f x65 +f x65 t +f x67 +f x68 +f x69 +f x69 K +f x6A +f x6B +f ] x72 +f x72 ] +f x73 +f x74 +f x75 +f [ x75 +f x75 [ +f x75 T6 +f x76 +f x78 +f x7A +f x7A +6 +f x7B +f x82 +f x83 +f x85 +f x86 +f x8A +f x8B +f x91 +f x93 +f x96 +f xA5 +f xA8 +f xB4 +k +] k +{ k +} k +k { +k } +K +{ K +} K +K [ +K [ } +K { +K } +K } } +k -0 +k +0 +k *04 +k *04 l +k *07 +K +1 +k *13 +K *13 +k -1 *34 +k *13 -5 +K *14 +[ K *15 +K +1 *50 +k *15 K +K *16 +k *17 +k +1 f +} k -2 +k +2 +K +2 +k *20 +K *25 +K *25 *30 +sir +k +3 +K *30 +K *30 *25 +K *32 +k *34 -1 +{ K *35 +K *35 +k -3 t +k +4 +K *43 +K *43 -5 +K *45 t +k *46 +K *46 r +k { *48 +k +5 +K -5 +K *50 +1 +k *51 +k -5 *13 +K *53 x41 +K *54 +K -5 *43 +k *54 R3 +k '6 +k *63 +k '6 l +k -7 [ +k '7 +k '8 +k '9 +k C +k { C +k C { +K c +k D4 +] K D5 +K f +k K +K k +k K *15 +k l *04 +k L2 +k l '6 +k r +k [ r +k } r +K r +k R1 x41 +k R2 +k R3 +K R3 +k R3 *54 +k R4 +K r *46 +[ k R5 +K R6 +k r R1 +K r u +s2c +s2o +s3H +s52 +s5b +s8k +saj +sbd +scr +sd; +sd3 +se6 +sej +ser +sex +sfp +sfp +sgd +si7 +sir +sjt +smO +snb +srJ +srp +ss2 +ssL +ssy +suz +sy6 +sy6 +szs +K t +K T0 +k t -3 +K t *45 +k T5 +} k u +k u +K u r +] K x01 +K x21 +k x23 +K x31 +K x32 +K x41 +k x41 R1 +K x42 +k x51 +k { x51 +k x51 { +k x62 +k x71 +sfp +l +l [ +l { +l } +l +0 +L0 +} L0 +L0 { +L0 } +L0 +1 +L0 *13 +L0 *13 T1 +l { *02 +L0 +2 +l *03 +L0 +4 +L0 -5 +L0 -7 +L0 '7 +L0 '7 l +L0 c +L0 l +L0 L0 +L0 L1 +L0 L5 +L0 l '7 +s67 +s6u +sdu +L0 T0 +s67 +L0 T2 +L0 u +L0 u x14 +L0 x12 +L0 x14 u +L0 x26 +L0 x31 +L0 x41 +L0 x51 +L0 x53 +l } -1 +l +1 [ +L1 +[ L1 +{ L1 +L1 [ +L1 { +L1 -0 +L1 +1 +L1 *15 +L1 -4 +L1 +4 +l { *15 +L1 '6 +L1 *62 +sw@ +L1 -7 +L1 +7 +L1 *79 D6 +L1 D5 +L1 R0 +L1 R5 +s61 +sa3 +sw@ +L1 t +L1 T0 +L1 T3 +L1 x23 +L1 x26 +L1 x35 +L1 x41 +L1 x42 +L1 x52 +l +2 +L2 +L2 } +l *20 +L2 *05 +L2 -1 +L2 *20 +L2 *3A x34 +l *23 L4 +spe +l *24 -5 +L2 +5 +L2 D0 +L2 D3 +L2 L3 +L2 L4 +L2 L7 D0 +L2 r +L2 R4 +s6u +s?9 +s9d +sd_ +sds +L2 t +L2 T1 +L2 u +L2 x02 +L2 x34 +L2 x62 +l +3 +L3 +L3 { +L3 } +L3 +0 +l *30 +4 +L3 +0 '6 +l *30 *64 +L3 +1 +L3 +3 +L3 *32 +L3 *35 +L3 -4 +L3 +4 +l *35 +L3 -5 +L3 +5 +l +3 -6 +L3 -6 +L3 '6 +L3 '6 +0 +l *37 +l +3 '7 +L3 D0 +L3 l +L3 L1 +sb` +slk +slk +L3 T0 +slk +L3 x02 +L3 x52 +L3 x62 +l +4 +L4 +l *41 +L4 *14 +L4 -3 +l +4 *30 +L4 -5 +L4 '5 +L4 -5 T1 +L4 -6 +L4 '6 +L4 D0 +L4 D2 t +L4 D3 +L4 l +L4 L4 +L4 r +L4 R0 +L4 T1 -5 +L4 t D2 +L4 u +L5 +L5 { +L5 *02 +L5 -2 +L5 *21 +L5 ] *23 +L5 *23 ] +l -5 *24 +L5 { *30 +L5 *34 +L5 -5 +L5 '7 +L5 c +L5 D0 +L5 l +L5 L5 +L5 R1 +seo +sgw +st6 +L5 t +L5 u +L5 x32 +L5 x62 +L6 +[ L6 +{ L6 +} L6 +L6 [ +l -6 +3 +l *63 +l *63 K +L6 +4 +l *64 *30 +L6 ] *50 +L6 *50 ] +L6 +6 +L6 +6 '9 +l *68 +L6 '9 +L6 '9 +6 +L6 c +L6 D3 +L6 D5 +{ L6 l +L6 L7 +L6 r +L6 R3 +s61 +sm2 +L6 t +L6 x71 +L6 x72 +L6 x74 +L7 +} L7 +L7 } +L7 *03 +L7 *03 *38 +L7 -1 +L7 *13 +L7 *17 +l '7 +3 +L7 *38 +l *74 +L7 *47 +l *75 +L7 *63 +L7 *67 +L7 '8 +l *79 +L7 c +L7 l +sj= +sj= +L7 t +L7 T1 t +L7 t T1 +L7 x15 +L7 x31 +L7 x52 +l '8 +L8 +{ L8 +L8 *08 +L8 *52 +l *86 +l *87 +L8 { *78 +L8 +8 +L8 '9 +s5s +L8 x21 +L8 x25 +L8 x31 +L8 x35 +L9 +L9 [ +l *92 +l *98 +L9 l +L9 x13 +L9 x24 +L9 x35 +L9 x42 +L9 x62 +L9 x72 +l -A +l +A +LA +l +A x45 +LA x82 +l *B9 +LB x53 +l D3 L5 +l D4 +l D8 +sns +l f x1B +l f x46 +l K +l K *63 +l L1 +l L2 +l L4 *23 +{ l L6 +l r +l R0 +l R1 +l R3 +l R6 +l R8 +s0? +s0i +s12 +s1a +s5d +s6- +s6p +s70 +s8p +sdk +sgc +sgj +shj +slh +smb +smb +smK +sns +so1 +sp1 +spe +spe +sr[ +ss& +ssd +ssf +ssi +suy +swn +sys +sz2 +l T1 +l T2 +l T3 +l T4 +l T5 +l T6 +[ l T7 +l T7 +l T8 +l T9 +l x12 +l x13 +l x14 +l x15 +{ l x21 +l x23 +l x24 +l x26 +l x27 +l x32 +l x35 -5 +l x42 +l x43 +l x52 +l x53 +l x63 +l x72 +l x75 +l x83 +l x83 D9 +l x92 +r +[ r +[ r } +] r +] { r +} r +r [ +r { +r } +r -0 +r +0 +R0 +{ R0 +} R0 +R0 } +R0 *06 +R0 *08 +R0 *09 +R0 *17 +r *02 +r *02 K +R0 +3 +R0 *37 +R0 } *38 +R0 +3 t +r { *04 +r *05 +r *06 +R0 -6 +R0 '6 +r *07 +R0 -7 +R0 '7 +r *07 *64 +r *09 +r -0 c +R0 c x12 +[ R0 D3 +] R0 D3 +R0 D3 +R0 D5 +R0 D6 +R0 K +R0 k *16 +R0 l +R0 L3 +R0 r +R0 R2 +shd +R0 T1 +R0 T1 *17 +R0 t +3 +R0 x12 +R0 x12 c +R0 x13 +R0 x24 +R0 x35 +r -1 +R1 +[ R1 +R1 [ ] +R1 ] +R1 ] [ +R1 } +r *10 +R1 *03 +R1 *12 +r *12 +r *12 *60 +R1 -3 +r *14 +R1 +4 +r *14 D5 +r *15 +R1 -5 +r *15 L2 +r -1 *61 +R1 -7 +R1 '7 +R1 c +R1 C +R1 D0 +R1 D3 +R1 D7 +R1 f +R1 } k +R1 r +R1 R0 { +R1 R2 +R1 R3 +R1 R4 +sed +sed +sfn +shc +R1 t +R1 x23 +R1 x35 +sed +R1 x41 +R1 x42 +R1 x53 +r -2 +R2 +{ R2 +} R2 +R2 } +[ r *20 +R2 +1 +R2 *24 +R2 +3 +R2 *61 +R2 *70 +R2 f +R2 l +R2 L1 +R2 L7 +R2 r +sob +R2 u +R2 x41 +R2 x51 +R2 x62 +R3 +{ R3 [ +R3 [ +r [ *30 +r *30 +R3 +1 +R3 +3 +R3 *30 +r *34 +r *34 [ +R3 -4 +R3 *40 +R3 *46 l +R3 -4 t +r *35 [ +R3 -5 +R3 +5 +r *36 +R3 '6 +R3 +6 +R3 -7 +[ R3 c +R3 D5 +R3 K +R3 l +R3 L0 +R3 L4 +R3 l *46 +s1j +shf +R3 t +R3 t -4 +R3 x21 +R3 x54 +R3 x73 +R4 +} R4 +R4 [ +R4 { +r *40 +r *40 t +R4 *30 +R4 +4 +R4 *41 +R4 *45 R4 +r ] *45 +r *45 +r *45 ] +R4 -5 +R4 -5 +6 +R4 *64 +R4 +6 -5 +R4 *87 [ +R4 D0 +R4 D1 +R4 D2 +R4 D2 T0 +R4 f +R4 r +r +4 R0 +R4 R2 +R4 R4 +R4 R5 +s20 +s9H +smD +ss) +R4 t +R4 T0 D2 +R4 T5 +R4 x02 +R4 x04 +R4 x21 +R4 x53 +R4 x63 +r '5 +r +5 +R5 +{ R5 +} r *50 +R5 +1 +r *52 +R5 +2 +r *53 +R5 *34 +r [ *54 +R5 -4 +R5 *53 +r *56 +R5 *64 +R5 *64 x63 +R5 *65 +R5 *65 K +R5 '8 +R5 '9 +R5 D4 +[ R5 k +R5 k +R5 K +R5 K *65 +R5 l +R5 R6 +s1n +s1z +s2* +s4a +sd4 +se7 +st3 +st9 +R5 u +R5 x31 +R5 x63 +r '6 +r +6 +R6 +R6 [ +r *60 *12 +R6 *07 -4 +R6 *23 +R6 -3 +R6 -4 +R6 +4 +r *64 *07 +R6 -4 *07 +R6 +5 +r *65 x32 +R6 *64 +R6 *64 C +R6 *65 +R6 -7 +r '6 C +R6 C +R6 C *64 +R6 D1 +R6 D2 +R6 D5 +R6 L2 +R6 r +R6 R0 +R6 R2 +R6 R7 +s2r +sc4 +ss8 +R6 t +R6 u +R6 x15 +R6 { x21 +R6 x21 { +R6 x24 +R6 x41 +R6 x75 +R7 +[ R7 +{ R7 +R7 [ +R7 *02 +R7 -0 x12 +R7 -5 +R7 *56 +R7 -6 +R7 *67 +R7 '8 +R7 +9 +R7 *97 +R7 [ D3 +R7 D3 [ +R7 l +sgs +syi +R7 t +R7 T4 +R7 x12 -0 +R7 x14 +R7 x34 +R7 x41 +R7 x43 +R7 x81 +r +8 +R8 +{ R8 +R8 -5 +R8 D1 +R8 R7 +R8 x04 +R8 x16 +R8 x31 +R8 x32 +R8 x34 +R9 +{ R9 +R9 [ +R9 *25 +R9 *36 +R9 -9 +R9 D7 +R9 u +R9 x02 +R9 x14 +R9 x24 +r 'A +RA [ +RA +1 x82 +RA x73 +RA x82 +1 +RB x02 +r c +r C +r c -0 +r C '6 +r c K +} r D0 +r D2 +r D3 +r { D3 +r D3 { +r D4 +r D5 +r D5 *14 +r D5 R8 +r D9 +r f +r } f +r f x3A +r K +r ] K +r K *02 +r K c +r K x51 +r L2 +r L2 *15 +r L5 +r L5 L5 +r L7 +r L9 +r R0 +r R0 +4 +r R2 +r R3 +r R4 } +r R4 C +r R6 +s0- +s0+ +s0l +s1 +sa3 +sb9 +sbq +sil +sJ7 +snw +so% +sra +stE +stk +stp +swg +r t +r T0 +r T1 +r t *40 +r T7 +r t x41 +] r u +r x04 +r x05 +r x15 +r x32 +r x37 +r x41 t +r x42 +r x51 +r x51 K +r x54 +r x62 +r x64 +s!- +s!. +s0- +s0` +s0< +s0 +s0_ +s0_ +s0- +s0, +s0! +s0? +s0/ +s0. +s0) +s0@ +s0* +s0\ +s0& +s0# +s0+ +s02 +s03 +s03 +s04 +s05 +s05 +s06 +s0- +s0* +s06 +s07 +s07 +s07 +s08 +s08 +s09 +s09 +s0a +s0a +s0A +s0a +s0b +s0B +s0c +s0+ +s0C +s0d +s0D +s0, +s0, +s0e +s0E +s0f +s0F +s0g +s0g +s0G +s0G +s0h +s0i +s0I +s0j +s0J +s0j +s0k +s0K +s0k +s0l +s0m +s0M +s0n +s0o +s0o +s0p +s0P +s0q +s0q +s0r +s0r +s0s +s0S +s0s +s0s +s0U +s0U +s0v +s0w +s0x +s0X +s0% +s0X +s0x +s0X +s0y +s0Y +s0z +s0Z +s1` +s1= +s1 +s1 +s1_ +s1- +s1, +s1; +s1! +s1? +s1/ +s1. +s1@ +s1& +s1# +s1+ +s10 +s12 +s13 +s13 +s14 +s15 +s15 +s15 +s16 +s16 +s17 +s18 +s1# +s18 +s18 +s18 +s18 +s19 +s19 +s1a +s1A +s1B +s1B +s1c +s1c +s1d +s1d +s1D +s1* +s1e +s1E +s1f +s1F +s1g +s1g +s1h +s1i +s1I +s1k +s1K +s1K +s1l +s1L +s1m +s1n +s1o +s1p +s1P +s1p +s1q +s1q +s1r +s1s +s1S +s1t +s1u +s1U +s1u +s1v +s1V +s1v +s1w +s1W +s1w +s1w +s1w +s1x +s1X +s1y +s1y +s1z +s2< +s2 +s2_ +s2- +s2, +s2; +s2? +s2. +s2@ +s2* +s2# +s2+ +s20 +s2@ +s21 +s21 +s21 +s23 +s24 +s25 +s26 +s26 +s27 +s27 +s28 +s28 +s29 +s2a +s2A +s2b +s2B +s2B +s2c +s2c +s2d +s2e +s2f +s2F +s2g +s2G +s2G +s2G +s2G +s2h +s2H +s2i +s2I +s2K +s2k +s2l +s2L +s2l +s2m +s2o +s2o +s2p +s2Q +s2r +s2* +s2s +s2t +s2T +s2u +s2v +s2v +s2w +s2W +s2x +s2* +s2y +s2y +s2y +s2y +s2z +s2Z +s2z +s3` +s3 +s3_ +s3, +s3! +s3. +s3" +s3[ +s3} +s3@ +s3* +s3# +s3% +s30 +s30 +s31 +s32 +s3+ +s34 +s34 +s35 +s36 +s36 +s37 +s37 +s38 +s38 +s38 +s38 +s38 +s39 +s3a +s3A +s3A +s3b +s3c +s3d +s3e +s3E +s3e +s3f +s3f +s3G +s3g +s3h +s3H +s3i +s3j +s3k +s3K +s3l +s3L +s3l +s3m +s3o +s3q +s3r +s3s +s3S +s3t +s3t +s3u +s3. +s3V +s3v +s3W +s3x +s3X +s3y +s3Y +s3z +s4 +s4_ +s4- +s4, +s4. +s4] +s4* +s4\ +s4& +s4+ +s40 +s41 +s42 +s43 +s45 +s45 +s46 +s47 +s48 +s49 +s49 +s4a +s4A +s4b +s4+ +s4C +s4d +s4d +s4e +s4f +s4F +s4G +s4i +s4I +s4j +s4j +s4k +s4K +s4k +s4n +s4o +s4O +s4p +s4q +s4r +s4R +s4r +s4s +s4s +s4t +s4T +s4* +s4w +s4w +s4x +s4X +s4_ +s4y +s4y +s4z +s!5 +s5= +s5 +s5_ +s5, +s5! +s5/ +s5* +s50 +s51 +s52 +s52 +s53 +s5% +s53 +s54 +s54 +s5% +s56 +s57 +s58 +s58 +s59 +s5a +s5b +s5b +s5c +s5d +s5e +s5e +s5f +s5g +s5G +s5g +s5i +s5k +s5m +s5n +s5o +s5o +s5s +s5t +s5w +s5x +s5. +s5% +s5= +s5Z +s6- +s6! +s6! +s6/ +s6. +s6@ +s6* +s6& +s6% +s60 +s61 +s62 +s63 +s63 +s64 +s64 +s65 +s67 +s68 +s68 +s69 +s69 +s6a +s6b +s6B +s6b +s6c +s6c +s6d +s6e +s6f +s6g +s6J +s6k +s6K +s6K +s6% +s6m +s6n +s6o +s6p +s6q +s6r +s6S +s6S +s6t +s6V +s6w +s6W +s6X +s6y +s6z +s6z +s7+ +s!7 +s7= +s7 +s7- +s7/ +s7. +s7" +s70 +s70 +s71 +s7- +s71 +s72 +s73 +s74 +s74 +s74 +s75 +s76 +s76 +s76 +s76 +s76 +s76 +s78 +s78 +s79 +s7a +s7A +s7b +s7c +s7C +s7d +s7D +s7e +s7f +s7g +s7i +s7j +s7K +s7l +s7L +s7M +s7n +s7N +s7P +s7q +s7r +s7r +s7s +s7W +s7x +s7 +s7Y +s!8 +s8- +s8/ +s8. +s8* +s8# +s80 +s8. +s80 +s8. +s80 +s81 +s82 +s84 +s84 +s84 +s85 +s86 +s87 +s87 +s87 +s89 +s89 +s8a +s8a +s8B +s8B +s8B +s8c +s8c +s8c +s8d +s8D +s8e +s8F +s8h +s8H +s8i +s8j +s8J +s8k +s8l +s8l +s8m +s8M +s8m +s8n +s8n +s8o +s8p +s8P +s8Q +s8R +s8S +s8t +s8t +s8T +s8u +s8U +s8u +s8v +s8W +s8x +s8y +s8z +s8z +s9 +s9_ +s9- +s9! +s9. +s9" +s9@ +s9* +s9\ +s9# +s9% +s90 +s90 +s91 +s92 +s92 +s92 +s93 +s94 +s94 +s95 +s97 +s97 +s97 +s97 +s98 +s9a +s9b +s9b +s9c +s9d +s9D +s9d +s9 +s9. +s9e +s9E +s9f +s9i +s9J +s9k +s9n +s9o +s9p +s9q +s9r +s9_ +s9S +s9" +s9T +s9u +s9u +s9u +s9w +s9x +s9 +s9y +s9z +s9Z +sa@ +sa= +sa +sa- +sa; +sa? +sa/ +sa. +sa' +sa@ +sa* +sa0 +sa0 +sa0 +sa1 +sa@ +sa2 +sa2 +sa3 +sa4 +sa4 +sa4 +sa5 +sa6 +sa7 +sa7 +sa8 +sA8 +sa9 +saA +saA +sac +sa@ +saC +sAC +sad +sad +sae +saE +sae +sae +saE +saf +saF +saF +sag +sah +saH +sai +saj +sak +sak +sa- +sam +sam +sam +san +san +sao +saO +sap +sap +saP +sAP +saq +saQ +sar +sa! +sar +sas +sa@ +se3 +se3 +se3 +sa@ +sa@ +si1 +si1 +si! +si! +sa@ +sa@ +so0 +so0 +so0 +so0 +saS +sat +sat +sa@ +sa@ +sa@ +sat +sau +sau +sa@ +saU +sAu +sau +sau +sav +sav +saw +saW +sax +sa. +sax +say +saY +sAy +saz +saZ +saZ +sb +sb- +sb# +sb0 +sb0 +sb0 +sb1 +sb2 +sb3 +sb4 +sb5 +sb5 +sb6 +sb7 +sb7 +sb7 +sb8 +sb8 +sb9 +sb9 +sb9 +sba +sbB +sbc +sb +sbC +sbd +sbd +sbe +sbf +sbf +sbg +sbg +sbH +sBH +sbi +sbj +sbJ +sbk +sbK +sbk +sbl +sbL +sbm +sbM +sBM +sbM +sbm +sbm +sbM +sbM +sbn +sbN +sbo +sbp +sbP +sbq +sbr +sbR +sbs +sbs +sbt +sbt +sbT +sbu +sbu +sbu +sbv +sBv +sbx +sbX +sby +sby +sby +sby +sbz +sbz +sc" +sc@ +sc/ +sc2 +sc3 +sc4 +sc5 +sC6 +sc7 +sc8 +sc9 +scb +scb +scD +sce +scf +scg +scg +sch +scH +sCh +sch +sci +scj +scj +scj +sck +scK +sck +sck +sck +scl +scL +scm +scn +sCN +sco +scp +scP +scq +scr +scr +scs +scs +scS +sCS +sct +scu +scv +scw +scx +scy +scz +scZ +scz +sd- +sd. +sd0 +sd1 +sd1 +sd3 +sd3 +sd5 +sd6 +sd7 +sd7 +sd9 +sdb +sdB +sdb +sdb +sdc +sdc +sdD +sdf +sdF +sDF +sdf +sdf +sdg +sdG +sdg +sdh +sdi +sdi +sdj +sdJ +sdJ +sdk +sdK +sdl +sdL +sDl +sDL +sdL +sdm +sdm +sdn +sdN +sdn +sdo +sdp +sdP +sdp +sdp +sdp +sdq +sdr +sds +sds +sDs +sdt +sdT +sdv +sdW +sDw +sdW +sdX +sdX +sdy +sdY +sdY +sdz +sdZ +sDz +se +se- +se; +se. +se@ +sE. +se0 +se1 +se1 +se2 +se3 +se3 +se3 +se3 +se3 +se3 +sl1 +se3 +se3 +se3 +se3 +se3 +se3 +se3 +se3 +se4 +se5 +se6 +se7 +se8 +se9 +sea +seb +sec +seC +sed +sed +seE +sef +seg +seg +seh +sei +sej +seJ +sEJ +sej +sej +sek +sem +sem +sem +sem +sen +seN +seo +seO +seO +sep +ser +ser +ser +sEs +ses +set +seu +seU +sEU +sev +sew +seW +sew +sex +sey +sez +sez +sf[ +sf1 +sf2 +sf5 +sf6 +sf7 +sfa +sfb +sfB +sfc +sFC +sfd +sfd +sfd +sfF +sfF +sfg +sfg +sfh +sfj +sfJ +sfk +sfk +sfK +sFl +sfm +sfn +sfn +sfN +sfo +sfp +sfq +sfr +sfr +sfs +ssB +sft +sfU +sfv +sfv +sfw +sfX +sfz +sg1 +sg2 +sg3 +sg4 +sg_ +sg4 +sg_ +sg5 +sg6 +s!g +sg8 +sg9 +sg9 +sga +sgb +sgc +sgC +sgd +sgD +sge +sgh +sgh +sgh +sgi +sgj +sgJ +sgj +sgk +sgK +sgl +sgl +sgm +sgm +sgn +sgn +sgo +sgo +sgO +sgo +sgp +sgq +sgq +sgr +sgR +sgs +sgS +sgt +sg +sgT +sgT +sGt +sgv +sgV +sgw +sGw +sgw +sGw +sgx +sgx +sgy +sgz +sh0 +sh1 +sh2 +sh2 +sh2 +sh2 +sh4 +sh6 +sh7 +sh7 +sh9 +sha +shb +shB +shb +shc +shC +shd +she +shf +shf +shf +shg +shG +shi +shj +shk +sh* +shk +shk +shk +shl +shL +sHL +shm +shM +shm +shn +shp +shq +shr +shr +shs +shS +sht +shT +shu +shU +shv +shW +shx +shx +shX +shy +shY +shy +shz +shz +shz +shz +si +si- +si; +si! +si. +si@ +si* +si1 +si1 +si1 +se3 +si1 +si1 +si1 +si1 +si1 +si1 +si1 +si2 +si3 +si3 +si5 +si6 +si7 +si7 +si7 +si9 +sia +sib +sib +siC +siC +siC +siC +siC +sid +sie +sIE +siE +sif +sif +sih +sij +sik +sil +si +sim +sin +siN +siN +sio +siO +sio +sio +sip +siq +siq +sir +sir +si! +sit +si! +si! +si! +si! +si! +si! +siu +si! +siU +siv +siw +siW +siw +siy +siy +siz +siZ +siz +sj= +sj- +sj. +s!J +sj1 +sj6 +sj6 +sj7 +sj8 +sj9 +sjb +sjB +sjd +sj +sjF +sjF +sjg +sJg +sjh +sjk +sjl +sjl +sjl +sjm +sjn +sjn +sjo +sjo +sjp +sjP +sjq +sjq +sjr +sjs +sjs +sjt +sjt +sjt +sjv +sjW +sjx +sjx +sjy +sjY +sjz +sk_ +sk- +sk0 +sk2 +sk4 +sk8 +sk9 +ska +skA +skb +skB +skb +skc +skc +skC +skd +skD +ske +skf +skg +skG +skg +skh +sKH +ski +skl +skL +skL +skm +skM +skm +skn +sko +skp +skP +skq +skr +sks +skt +skT +sku +skW +skW +skw +skx +sky +sKy +sky +sky +sky +skz +skz +skZ +sl +sl; +sl! +sl] +sl@ +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl1 +sl2 +sl4 +sl5 +sl6 +sl7 +sL7 +sl7 +sl8 +sl9 +sla +slA +slb +slB +slb +slc +slc +slc +sld +sld +sl; +sle +slf +slf +slF +slf +slf +slg +slh +slH +slh +sli +slj +slj +slk +slK +sl@ +slL +slm +slM +slM +sln +sln +slo +slo +slo +slp +slP +slq +slr +slR +slr +sls +slS +slt +slt +slT +slt +slt +slu +sLU +slv +slw +slW +slx +sl- +sly +sly +sly +slz +slz +slZ +sm_ +sm; +sm. +sm1 +sm1 +sm1 +sm2 +sm2 +sm3 +sm3 +sm4 +sm4 +sm5 +sm6 +sm8 +sm8 +sm9 +sma +smb +smb +smB +smB +smc +smd +smD +smd +sme +sme +smf +smg +smg +smG +smg +smH +sMH +smH +smj +smj +smj +smj +smk +smK +sml +sml +sml +smM +smn +smn +smo +smo +smo +smo +smo +smp +smp +smP +smP +smq +smQ +smr +smR +sms +sms +smt +sm@ +smu +smu +smu +smv +smw +smw +smW +smw +smy +smz +smZ +sMz +sn, +sn. +sn+ +sn0 +sn1 +sn2 +sn3 +sn4 +sn4 +sn4 +sn5 +sn6 +sn6 +sn7 +sn7 +sn8 +sn9 +sn9 +sna +snb +snB +snb +snc +snc +snd +snD +snd +sne +snf +sng +sng +snG +snG +sng +sng +snh +snh +snh +snj +snk +snl +snl +sn_ +snl +snL +snm +sNm +snm +snN +snp +snp +snq +snq +snr +sns +snt +snT +sNt +snt +snu +snu +snv +snw +snw +snw +snx +snX +sny +snz +snz +so +so, +so; +so! +so/ +so. +so] +so@ +so# +so0 +so0 +so0 +se3 +se3 +se3 +so0 +so0 +si1 +si1 +si! +si! +so0 +sl1 +so0 +so0 +so0 +so0 +so0 +so0 +so0 +so0 +sO0 +so1 +so3 +so3 +so3 +so3 +so4 +so5 +so6 +so8 +so9 +soa +soa +sob +soB +soc +soc +soC +sod +sod +sod +soe +sof +sog +sog +soh +soi +soi +soI +soK +sol +som +som +son +soN +soO +sop +soq +soQ +sor +sor +sos +soS +sot +soT +sou +sou +sov +sow +sOW +so' +soy +soY +soy +soz +sp` +sp= +sp. +sp) +sp[ +sp/ +sp1 +sp2 +sp6 +sp9 +spb +spb +spc +spc +spd +spD +spe +spf +spg +spG +sph +spi +spj +spk +spl +spm +spm +spr +spR +sps +spS +spt +spT +spT +spT +spT +spu +spv +spV +spW +spY +spz +spZ +sq +sq9 +sqb +sqc +sqg +sqg +sqk +sqm +sQn +sqt +sr +sr[ +sr0 +sr0 +sr1 +sr2 +sr2 +sr2 +sr4 +sr5 +sr5 +sr6 +sr +sr7 +sr8 +sra +sra +sra +srb +src +src +src +src +srd +srd +sre +srf +sRf +srf +srG +srg +srg +srh +srH +sri +srI +srj +srJ +srj +srj +srk +srk +srl +srm +sRm +srn +srN +sRN +srn +sro +sro +srR +srs +srS +srs +srt +srt +sru +sr& +sru +srv +srw +srW +srw +srx +sr +srx +srX +sry +sry +srY +srY +srz +srz +ss? +ss` +ss_ +ss- +ss, +ss. +ss' +ss* +ss\ +ss& +ss# +ss% +ss+ +ss0 +ss1 +ss1 +ss2 +ss2 +ss2 +ss3 +ss3 +ss4 +ss4 +ss5 +ss5 +ss5 +ss5 +ss6 +ss6 +ss7 +ss8 +ss9 +ssa +ssa +ssb +ssB +sSB +ssb +ssB +ssc +ssC +sS/ +ssd +ssd +ssE +ssf +sSf +ssF +ssg +ssg +ssh +ssH +ssh +ssH +soB +ssi +ssj +ssj +ssk +ssK +ssk +ssl +ss\ +ssL +ss- +ssm +ssn +ssn +sSn +sso +ssO +sso +ssp +ssp +ssr +ssS +sst +sst +ssT +ssu +ssv +ssv +ssw +ssW +ssx +ssy +ssy +ssy +ssz +ssZ +ssz +st +st` +st< +st +st, +st +st1 +s#_ +st1 +st1 +st1 +st3 +st4 +st4 +st5 +st5 +st6 +st7 +st7 +st8 +st9 +stA +stb +stB +stc +stC +sTC +std +stD +ste +ste +stf +stf +stF +stg +stg +stg +sth +sth +sth +sti +sti +stj +stk +stk +stl +stL +stl +stl +stl +stm +stn +stp +stq +str +st* +sts +sts +stS +st@ +stu +stu +stu +stv +stw +stw +stw +stx +sTX +stx +stx +stx +sty +stz +su_ +su. +su\ +su0 +su1 +su3 +su4 +su8 +su8 +suA +sub +sub +sub +sub +sub +sue +sue +sUe +suf +sug +suh +sui +suI +sui +sui +suk +sul +sul +sum +suM +sun +sun +suN +sun +sun +suo +suo +suo +sup +sup +sur +sus +sut +suw +suy +suY +suy +sv_ +sv3 +sv6 +sv7 +sv9 +svb +svd +svf +svg +svi +svj +svj +svl +svm +svm +svm +svm +svm +svn +svN +svp +svr +svr +svS +svt +svu +svw +svx +svy +svz +sw2 +sw2 +sw4 +sw6 +sw8 +sw9 +swA +swa +skz +swa +swa +swc +swd +sw; +swe +swe +swf +swf +swg +swh +swh +swj +swk +swL +swm +swn +swn +swp +swr +sw- +swr +sWt +swz +sx* +sx0 +sx1 +sx3 +sx4 +sx5 +sx6 +sx6 +sx7 +sx8 +sx8 +sx9 +sxa +sxb +sxc +sxf +sxh +sxh +sxh +sxh +sxk +sxl +sxm +sxo +sxp +sxq +sxr +sxr +sxs +sxt +sxv +sxw +sxy +sy. +sy1 +sy1 +sy3 +sy5 +sy6 +sy7 +syb +syb +syc +syd +sye +sye +sye +syf +syg +syh +syi +syI +syj +syl +syn +syo +syp +syq +sys +syt +syu +syU +syu +syv +syz +sz; +sz. +sz2 +sz6 +sz7 +sz8 +sz9 +sza +szb +szc +szd +sze +szf +szh +sZJ +szj +szk +szk +szl +szL +szl +szm +szm +szn +szp +szs +szt +sz +szu +szw +szw +szx +s.z +szy +szZ +t +[ } t +[ t } +{ t +t ] { +t { +t } +{ t -0 +T0 +{ T0 +T0 { +T0 *02 +T0 *03 +T0 *06 r +T0 *07 x21 +T0 *13 +T0 -4 +T0 *43 +t *04 k +t *05 +T0 +5 +t *05 R1 +T0 '6 +T0 +6 +T0 +7 +[ T0 -9 +T0 '9 +T0 *9A +T0 D3 +T0 D5 D8 +T0 D6 +T0 D8 D5 +T0 D9 +T0 L3 +T0 L4 +T0 L7 +T0 r +T0 r *06 +T0 R5 +s3! +se4 +sgK +sM +so +spR +sr@ +st5 +std +stj +T0 t +T0 T2 +T0 T3 +T0 T5 +T0 T9 +T0 x12 +T0 x63 +T0 x74 +T1 +[ T1 +{ T1 +T1 [ +T1 +0 +T1 +1 +sAP +T1 -3 +t *14 +T1 -4 +T1 +4 +t *15 +t -1 -6 +T1 +6 +T1 +7 +T1 '8 +T1 '9 +T1 f +T1 k +T1 K +T1 L0 +T1 r +sAP +sbN +T1 t +T1 T0 +T1 T2 +T1 T2 T3 +T1 T2 T4 +T1 T3 +T1 T3 T2 +T1 T3 T5 +T1 T3 T5 T7 +T1 T3 T7 T5 +T1 T4 +T1 T4 T2 +T1 T5 T3 +T1 T5 T3 T7 +T1 T5 T7 T3 +T1 T6 +T1 T7 +T1 T7 T3 T5 +T1 T7 T5 T3 +T1 x42 +T1 x54 +T2 +{ T2 +} T2 +t *20 +T2 -0 +T2 *03 +t *21 +T2 *57 +T2 '7 +T2 D7 +T2 k +T2 K +sA8 +T2 r +s03 +sA8 +sA8 +sx3 +T2 t +T2 T3 +T2 T4 +T2 T4 T5 +T2 T4 T6 +T2 T4 T6 T8 +T2 T4 T8 T6 +T2 T5 +T2 T5 T4 +T2 T6 +T2 T6 T4 +T2 T6 T4 T8 +T2 T6 T8 T4 +T2 T8 T4 T6 +T2 T8 T6 T4 +T2 x02 +T2 x41 +T2 x52 +T2 x52 *52 +T2 x57 +T2 x62 +t +3 +T3 +} T3 +T3 } +T3 +0 +t *31 +t *34 +T3 -5 x63 +t *37 +T3 '9 +T3 D0 +T3 D6 +T3 D7 +T3 L0 +T3 r +s0@ +si1 +sr2 +T3 t +T3 T5 +T3 T5 T7 +T3 T5 T7 T9 +T3 T5 T9 T7 +T3 T6 +T3 T7 T5 +T3 T7 T5 T9 +T3 T7 T9 T5 +T3 T8 +T3 T9 T5 T7 +T3 T9 T7 T5 +T3 x12 +T3 x43 +T3 x53 +T3 x63 -5 +T4 +] T4 +T4 [ +[ t *42 +T4 +2 +T4 +4 +T4 -5 +t *46 +T4 -6 +T4 '8 +T4 'A +T4 D5 +T4 D7 +T4 k +T4 r +T4 R4 +T4 R6 +T4 r t +s2u +s32 +s3H +sau +sIs +T4 T3 +T4 T5 +T4 T6 +T4 T6 T8 +T4 T8 T6 +T4 t r +T4 x02 +T4 x54 +t -5 +T5 +T5 [ +T5 { +T5 } +T5 -0 +t *51 +T5 -3 +t { *54 +T5 *43 +T5 *45 +T5 *45 T1 +T5 -6 +T5 '6 +T5 +6 +T5 *67 +T5 'B +T5 D1 +T5 D3 +s85 +T5 D4 +T5 K +s0+ +s2b +s85 +shn +T5 T1 *45 +T5 T7 +T5 x13 +T5 x61 +T5 x62 +t '6 +t +6 +T6 +] T6 +} [ T6 +T6 [ +t -6 -1 +T6 *23 +t *65 -7 +T6 -7 +T6 '7 +T6 '9 +T6 +B +T6 D1 +T6 D8 +T6 R0 +T6 T0 +T6 T5 +T6 T7 +T6 x02 +T6 x12 +T6 x14 +T6 x24 T3 +T6 x71 +T6 x72 +t '7 +T7 +[ T7 +t -7 *65 +T7 +6 t +t *78 +[ T7 '8 +T7 'A +T7 L6 +sg7 +sgp +T7 t +T7 T0 +T7 T2 +T7 T3 +T7 T4 +T7 t +6 +T7 T8 +t -7 x03 +T7 x24 +t +8 +T8 +[ T8 +} T8 +T8 } +[ T8 +7 +T8 -7 D4 +T8 D2 +T8 D5 +T8 D6 +sdn +T8 t +T8 } T8 +T8 x02 +T8 x31 +t '9 +T9 +T9 +B +T9 x13 +T9 x23 x51 +t -A +TA +TA RA +TA T0 +TA x53 x41 +{ t D1 +t D1 +t D1 -3 +sLA +t D8 +t D8 x24 +t DA x72 +t f x3B +t f x4A +t k +t } k +t L4 +t L5 +t r +t R0 +t R1 +t R1 *05 +t R3 +t R4 +t R7 +t r D2 +s0D +s0e +s0w +s0Y +s1n +s2) +s3e +s4f +s4U +s52 +s6b +s9! +sBZ +sF9 +sg7 +sgX +sJk +skC +sLA +sLH +sLN +sMD +sN8 +sNs +sS9 +sTc +sTk +sUe +sUR +sVF +sVG +sXB +t T1 +t T4 +t T5 +t T6 +t T9 +] t x02 +t x04 +t x12 +t x14 +t x23 +t x24 +t x27 +t x32 +t x32 [ +t x34 +t x36 +t x42 +t x45 +t x52 +t x54 +t x61 +t x62 +t x64 +t x72 +t x74 +t xA2 +u +[ { u +[ u { +{ u +} u +u { +u } +u +0 +u ] *04 +u *04 ] +u *05 -5 +u +1 +u +1 x21 +u -2 +u +2 +{ u *20 +u *21 +} u +3 +u -3 ] +u ] -3 +u *32 +u *32 +2 +[ u -4 +u +4 L1 +u -5 +u +5 +u *51 +u *52 +u -5 '6 +u *56 +u '6 +u '6 -5 +u *67 +u *68 +u *75 D5 +u *76 +u +7 R7 +u -8 +] u D0 +u D0 +u D0 +2 +u D0 D7 +u D3 +u D3 T1 +u D4 +u D4 x43 +u D6 +u D7 D0 +u D8 +u D9 +u f +} u k +u k x21 +u K x21 +u L0 +u L1 +4 +u L2 +} u L7 +] u r +u r +u { r +u R5 +u R7 +7 +sTz +s0b +s1c +s2n +s4T +s5R +s6& +s6c +s7t +s9_ +s94 +s9D +sBN +sCK +sCY +sEs +sG! +sLH +sMK +sP= +sPC +sRN +sTG +sTz +sVX +sYM +u T1 +u T1 D3 +u T2 +u [ T2 +u [ T3 +u T5 [ +u x01 +u x03 +u x13 +u x15 +u x21 +1 +u x21 k +u x21 K +u x24 +u x32 +u x35 +[ u x41 +u x41 +u x42 +u x43 D4 +u x47 +u x53 +u x56 +u x75 +u xA1 +x01 +] x01 } +x01 '9 +sRu +x01 f t +] x01 K +x01 r +sl. +sry +sta +swf +x01 t f +x01 x54 +x02 +{ x02 +} x02 +x02 { +x02 *02 +3 +x02 +1 +x02 +2 +x02 *20 +x02 *23 +x02 +3 +x02 +3 *02 +x02 *31 +x02 -4 +x02 *50 +x02 *50 t +x02 +6 +x02 -7 +x02 +8 +x02 C +x02 D3 +x02 D5 +x02 D6 +x02 f +x02 f +0 +x02 k +x02 K { +x02 L5 +x02 r +x02 R5 +x02 r k +str +syl +] x02 t +x02 t +x02 T5 +x02 t *50 +x02 x13 +x02 x53 +x02 x63 +x03 +{ x03 +x03 -0 +x03 +0 +x03 *12 +x03 *12 *50 +x03 +2 +x03 *30 +x03 *31 +x03 *34 +x03 *50 *12 +x03 +6 +x03 C +x03 D4 +x03 f +x03 f ] +x03 k +x03 { K +x03 L6 +x03 R1 +x03 R2 +x03 R3 +s2 +sld +x03 T6 +x04 +x04 *02 +x04 *78 +x04 l +x04 L4 +x04 L7 +x04 R5 +x05 +x05 -4 +x05 k +x06 +x12 +[ x12 +{ x12 +x12 } +x12 -1 +x12 +2 +x12 *21 +x12 *30 x31 +x12 *34 +x12 -4 +x12 *41 +x12 *41 *52 +x12 *52 +x12 *52 *41 +x12 '8 +x12 '8 u +x12 c +x12 D7 +x12 K +x12 K l +x12 L1 +x12 L3 +x12 l K +x12 r +x12 R3 +s2z +s81 +s9n +sf4 +sgo +shs +sna +x12 } t +x12 t } +x12 T3 +x12 u +x12 u '8 +x12 x23 +x12 x32 +x12 x91 +x13 +} x13 +x13 *03 +x13 *04 +x13 -0 K +x13 +1 r +x13 -2 +x13 *34 +x13 C +x13 D6 +x13 D7 +x13 f +x13 K -0 +x13 R2 +x13 R5 +s2f +s9i +sgP +spw +svz +x13 T1 +x13 T5 +x13 x32 +x13 x63 +x14 +} x14 +x14 { +x14 -1 +x14 *50 +x14 '8 +s28 +x14 D3 +x14 f +x14 f *10 +x14 L2 +x14 R0 +s28 +s28 +sam +x15 +x15 { +x15 +0 +x15 +1 +x15 +2 +x15 +3 +x15 c +x15 D5 L2 +x15 L2 D5 +sza +x15 T1 +x16 -1 +x16 +3 +x21 +[ x21 +{ x21 +} x21 +x21 { +x21 -0 +x21 +1 +x21 +2 +x21 *25 +x21 *30 +x21 *68 +x21 '9 +{ x21 c +x21 D7 +x21 f +x21 k R0 +{ x21 l +x21 { l +x21 l { +x21 R5 D4 +x21 R7 +s5 +scs +scZ +sm0 +stl +syu +x21 x52 +x23 +[ x23 +{ x23 +x23 { +x23 +0 +x23 *23 { +x23 '6 +x23 '7 +x23 -8 +x23 +9 +x23 C +x23 D3 +x23 f +x23 K +x23 L4 +x23 L5 +x23 r +x23 R3 +siL +x24 +{ x24 +x24 ] +x24 *13 +x24 *13 t +x24 +2 +x24 -3 +x24 *36 +x24 +5 +x24 '6 +x24 '8 +x24 f +x24 L2 +x24 L6 +sa. +sat +sod +sr4 +x24 T1 +x24 t *13 +x24 t R1 +[ x25 +x25 *13 +x25 -1 f +x25 +2 +x25 +2 x51 +x25 -5 +x25 D4 +x25 R6 +s5c +x25 u +x25 x51 +2 +x26 +x26 } +x26 +1 +x26 -5 +x26 R0 +skf +ssa +x27 +x28 +3 +x31 +[ x31 +x31 [ +x31 } +x31 -0 +ssr +x31 -2 +x31 *24 +x31 *26 +x31 *28 +x31 +4 +x31 +4 L7 +x31 ] *54 +x31 *54 ] +x31 '6 +x31 *61 +x31 *80 +x31 c +x31 f +x31 L7 +4 +x31 l R6 +x31 r +x31 R6 +x31 R6 l +s5X +sar +sar +si' +sl9 +smf +ssr +ssr +syT +x31 T2 +x31 x21 *45 +x31 x42 +x32 +[ x32 +} x32 +x32 [ +x32 } +x32 +1 +x32 +2 +x32 -3 +x32 *43 +x32 -5 +x32 +5 +x32 '6 +x32 +7 +x32 '8 +x32 D1 +x32 k +x32 l +x32 L4 +x32 R4 +x32 R5 +sg* +sG6 +sqc +srl +srs +sts +x32 T3 +x32 x42 +x32 x72 +x34 +[ x34 +x34 +1 +x34 *13 +x34 -4 +x34 -5 +x34 +6 D2 +x34 -7 +x34 +8 +x34 c +x34 D6 +x34 f u +x34 l +x34 L0 +s4d +seB +skz +som +ssm +ssp +svj +x34 [ t +x34 t [ +x34 u f +x34 x52 +x35 +x35 +0 +x35 -1 +x35 *43 +x35 -6 +x35 k +x35 l +x35 R1 +sr7 +x35 x63 +x36 +x36 +3 +x36 L3 +sbt +x36 T3 +x41 +x41 [ +x41 -0 +x41 +1 +x41 +1 *30 +x41 *30 +1 +x41 *36 r +x41 *40 +x41 *53 +x41 *56 C +x41 *78 +x41 C *56 +x41 D1 +x41 r *36 +sh5 +sjL +sku +snl +x41 T3 +[ x41 u +x41 x61 +x41 x82 +x41 x83 +x42 +[ x42 +x42 { +x42 } +x42 -0 +x42 +3 +x42 *34 +x42 -4 +x42 *52 +x42 *52 t +x42 *53 +x42 -6 +x42 '6 +x42 -7 +x42 -8 +x42 +9 +x42 c +x42 D1 +x42 D1 *21 +x42 D8 +sjb +x42 L4 +x42 L5 +x42 L6 +x42 r +x42 R5 +x42 R7 +sa +sdp +sjb +sjb +slL +srz +x42 T5 +x42 t *52 +x42 { T6 +x43 +] x43 +} x43 +x43 [ +x43 *04 +x43 *05 +x43 +2 +x43 +3 +x43 +4 +x43 *42 +x43 *43 +x43 +7 +x43 '8 +x43 C +x43 D4 +x43 D7 +x43 f +x43 L0 +x43 L4 +x43 R0 +s2i +s6n +sbh +sKi +x43 t +x43 u +x45 +[ x45 +x45 { +x45 -3 +x45 -5 +x45 c +x45 l +x45 R0 +x45 R1 +spb +x46 -1 +x46 -2 +x46 +2 +x46 l +x47 -4 +x47 c +x51 +[ x51 +{ x51 +x51 { +x51 *04 +x51 -1 +x51 -2 +x51 *68 +x51 -7 +x51 '9 +x51 K +se6 +x51 L1 L3 +x51 L3 +x51 L3 L1 +x51 r +x51 R1 r +x51 R8 +s9u +se6 +se6 +sh2 +sj4 +sjm +so +so +ssa +ssS +x51 T1 +x51 T3 +x51 x62 +x52 +x52 { +x52 } +x52 *02 +x52 *05 +x52 +2 +x52 *31 +x52 *32 +x52 +3 +3 +x52 *41 +x52 +5 +x52 -6 +x52 *67 +x52 'A +x52 D7 +x52 L4 +x52 r +x52 R2 +x52 R3 +x52 R4 +x52 T5 x13 +x52 u +x52 x02 +x53 +[ x53 +{ x53 +x53 *10 +x53 -4 +x53 -9 +x53 +9 +x53 *97 +x53 D7 +x53 L1 +x53 R5 +x53 R6 +s2r +x53 R7 +s0. +s1B +s2r +s2r +sln +x53 T0 +x53 T1 +x53 T4 +x53 x41 +x53 x62 +x54 +x54 -1 R0 +x54 +2 -4 +x54 -4 +x54 -4 +2 +x54 -5 +x54 c +x54 C +x54 D3 +x54 l +x54 R0 -1 +x54 R1 +x54 R5 +x54 R6 +x54 R7 +set +x54 T5 +x54 ] x21 +x54 x21 ] +x56 +x56 +5 +x56 -6 +x56 +6 +x57 R5 +x61 +{ x61 +x61 } -1 +x61 -2 +x61 *41 +x61 *53 +x61 +6 +x61 *64 +x61 *78 +x61 } -9 +sjx +x61 L0 +x61 r +x61 R5 +s8m +sjx +x61 t +x61 T4 +x61 x14 +x61 x21 +x62 +x62 [ +x62 +0 +x62 +2 +x62 +6 +x62 *65 +x62 +7 +x62 -8 +x62 '8 +x62 c +x62 D3 +s0l +x62 l +x62 r +s0l +s0l +s1i +sbd +x62 T3 +x62 T6 +x62 x02 +x62 x23 +x63 +] x63 +x63 -0 +x63 *34 +x63 *62 +x63 c +x63 r +x63 R1 +x63 R6 +x63 R7 +scH +x63 u +x64 +[ x64 +x64 +1 +] x64 +6 +x64 -6 +x64 -7 +x64 l +skL +x64 T0 +x64 x32 +x65 +x65 +6 +x65 *76 +x65 l +x65 R0 +slb +x71 +{ x71 +x71 } +x71 -0 c +x71 +5 +x71 *53 +x71 *56 +x71 *58 D5 +x71 *64 +x71 *64 k +x71 } c +x71 c } +x71 c -0 +x71 k *64 +x71 L7 +x71 r +x71 R6 +s2! +s2x +sh- +x71 T7 +x71 x21 +x71 [ x41 +x71 x41 [ +sh- +x72 +} x72 +x72 [ +x72 +1 +x72 *13 +x72 +2 +x72 +2 T6 +x72 -5 +x72 +8 +x72 c +x72 D1 +x72 [ l +x72 l [ +x72 R7 +sa1 +x72 T2 +x72 T6 +2 +x72 x31 +x72 x42 +x73 +[ x73 +x73 D8 +x73 l +x73 L1 +x73 R4 +x73 R6 +x73 R8 +s23 +x73 T6 +x73 u +x73 x51 +x74 +x74 +1 +[ x74 c +x74 c +x74 D2 +x74 l +x74 T1 +x75 +x75 T0 +x76 +7 +x81 +x81 -1 +x81 *34 +x81 -6 +x81 -8 +x81 -9 +x81 *91 +x81 -A +x81 D1 +x81 l x25 +x81 r +x81 R8 +s21 +seo +sry +x81 T3 +x81 T4 +x81 T5 +x81 x12 +x81 x25 +x81 x25 l +x81 x42 +x82 +x82 -0 +x82 -1 +x82 +1 +x82 -3 +x82 *86 +x82 -9 +x82 *94 '9 +x82 *96 +x82 *98 +x82 'A +x82 c +x82 D5 +x82 l +x82 L1 +x82 R7 +s45 +x82 T7 +x82 u +x82 x32 +x83 +x83 *57 +x83 c +x83 D9 +x83 l +x83 L8 +x83 x12 +x83 x42 +x83 x51 +x84 -7 +x84 +7 +x84 *78 +x84 -8 +x84 l +x85 D0 +x85 x43 +x91 +x91 +6 +x91 R8 +x91 R9 +s92 +x91 x24 +x91 x51 +x91 x51 'A +x92 +{ x92 +x92 +7 +x92 -8 +x92 -9 +x92 *98 +x92 +A +x92 c x71 +x92 D0 +x92 u +x92 x21 +x92 x71 c +x93 -7 +x93 c +x93 C +x93 D6 +x93 l +x93 L9 +x93 x41 +x93 x62 +xA1 +7 +xA1 -9 +xA1 +A +xA1 D8 +xA1 x04 +xA1 x15 +xA1 x21 +xA2 [ +xA2 *97 diff --git a/princeprocessor/rules/prince_optimized.rule b/princeprocessor/rules/prince_optimized.rule new file mode 100644 index 0000000..7c22f61 --- /dev/null +++ b/princeprocessor/rules/prince_optimized.rule @@ -0,0 +1,1255 @@ +## Name: prince.rule +## Version: 1.00 +## Compatibility: hashcat v0.48+, oclHashcat v1.31+ +## +## Description: +## +## Handwritten ruleset, documented, optimized rule for use with princeprocessor +## This rule can be used as amplifier in case you're attacking a fast hash + +: + +## Global cases + +l +u +c +C +t +E + +## Reverse the entire word, Duplicate entire word, Duplicate word reversed + +r +d +f + +## Duplicate every character, multiple times + +q +qq +qqq + +## Swaps first two characters, Swaps last two characters, both + +k +K +kK + +## Deletes character at position N, up to length 15 + +D1 +D2 +D3 +D4 +D5 +D6 +D7 +D8 +D9 +DA +DB +DC +DD +DE + +## Truncate word at position N, positions 8 - 15 + +'9 +'A +'B +'C +'D +'E + +## Duplicates first character N times, up to 6 times + +z1 +z2 +z3 +z4 +z5 +z6 + +## Duplicates last character N times, up to 6 times + +Z1 +Z2 +Z3 +Z4 +Z5 +Z6 + +## Duplicates first N characters, up to 6 times + +y1 +y2 +y3 +y4 +y5 +y6 + +## Duplicates last N characters, up to 6 times + +Y1 +Y2 +Y3 +Y4 +Y5 +Y6 + +## Deletes first character, Deletes last character, both combined (remove mutations) + +[ +[ [ +[ [ [ +] +] ] +] ] ] +[ ] +[ ] [ ] +[ ] [ ] [ ] + +## Deletes first character, Deletes last character, both combined (remove mutations), Capitalize word + +[ c +[ [ c +[ [ [ c +] c +] ] c +] ] ] c +[ ] c +[ ] [ ] c +[ ] [ ] [ ] c + +## Deletes first character, Deletes last character, both combined (remove mutations), Uppercase word + +[ u +[ [ u +[ [ [ u +] u +] ] u +] ] ] u +[ ] u +[ ] [ ] u +[ ] [ ] [ ] u + +## Rotates the word left and right + +{ +{ { +{ { { +} +} } +} } } + +## Rotates the word left and right, Capitalize word + +{ c +{ { c +{ { { c +} c +} } c +} } } c + +## Rotates the word left and right, Uppercase word + +[ u +[ [ u +[ [ [ u +] u +] ] u +] ] ] u +[ ] u +[ ] [ ] u +[ ] [ ] [ ] u + +## Increment character @ N by 1 ascii value, one char, up to length 15 + ++0 ++1 ++2 ++3 ++4 ++5 ++6 ++7 ++8 ++9 ++A ++B ++C ++D ++E + +## Increment character @ N by 1 ascii value, two chars, up to length 15 + ++0 +1 ++0 +2 ++0 +3 ++0 +4 ++0 +5 ++0 +6 ++0 +7 ++0 +8 ++0 +9 ++0 +A ++0 +B ++0 +C ++0 +D ++0 +E ++1 +2 ++1 +3 ++1 +4 ++1 +5 ++1 +6 ++1 +7 ++1 +8 ++1 +9 ++1 +A ++1 +B ++1 +C ++1 +D ++1 +E ++2 +3 ++2 +4 ++2 +5 ++2 +6 ++2 +7 ++2 +8 ++2 +9 ++2 +A ++2 +B ++2 +C ++2 +D ++2 +E ++3 +4 ++3 +5 ++3 +6 ++3 +7 ++3 +8 ++3 +9 ++3 +A ++3 +B ++3 +C ++3 +D ++3 +E ++4 +5 ++4 +6 ++4 +7 ++4 +8 ++4 +9 ++4 +A ++4 +B ++4 +C ++4 +D ++4 +E ++5 +6 ++5 +7 ++5 +8 ++5 +9 ++5 +A ++5 +B ++5 +C ++5 +D ++5 +E ++6 +7 ++6 +8 ++6 +9 ++6 +A ++6 +B ++6 +C ++6 +D ++6 +E ++7 +8 ++7 +9 ++7 +A ++7 +B ++7 +C ++7 +D ++7 +E ++8 +9 ++8 +A ++8 +B ++8 +C ++8 +D ++8 +E ++9 +A ++9 +B ++9 +C ++9 +D ++9 +E ++A +B ++A +C ++A +D ++A +E ++B +C ++B +D ++B +E ++C +D ++C +E ++D +E + +## Decrement character @ N by 1 ascii value, one char, up to length 15 + +-0 +-1 +-2 +-3 +-4 +-5 +-6 +-7 +-8 +-9 +-A +-B +-C +-D +-E + +## Decrement character @ N by 1 ascii value, two chars, up to length 15 + +-0 -1 +-0 -2 +-0 -3 +-0 -4 +-0 -5 +-0 -6 +-0 -7 +-0 -8 +-0 -9 +-0 -A +-0 -B +-0 -C +-0 -D +-0 -E +-1 -2 +-1 -3 +-1 -4 +-1 -5 +-1 -6 +-1 -7 +-1 -8 +-1 -9 +-1 -A +-1 -B +-1 -C +-1 -D +-1 -E +-2 -3 +-2 -4 +-2 -5 +-2 -6 +-2 -7 +-2 -8 +-2 -9 +-2 -A +-2 -B +-2 -C +-2 -D +-2 -E +-3 -4 +-3 -5 +-3 -6 +-3 -7 +-3 -8 +-3 -9 +-3 -A +-3 -B +-3 -C +-3 -D +-3 -E +-4 -5 +-4 -6 +-4 -7 +-4 -8 +-4 -9 +-4 -A +-4 -B +-4 -C +-4 -D +-4 -E +-5 -6 +-5 -7 +-5 -8 +-5 -9 +-5 -A +-5 -B +-5 -C +-5 -D +-5 -E +-6 -7 +-6 -8 +-6 -9 +-6 -A +-6 -B +-6 -C +-6 -D +-6 -E +-7 -8 +-7 -9 +-7 -A +-7 -B +-7 -C +-7 -D +-7 -E +-8 -9 +-8 -A +-8 -B +-8 -C +-8 -D +-8 -E +-9 -A +-9 -B +-9 -C +-9 -D +-9 -E +-A -B +-A -C +-A -D +-A -E +-B -C +-B -D +-B -E +-C -D +-C -E +-D -E + +## Swaps character X with Y, one char, up to length 15 + +*01 +*02 +*03 +*04 +*05 +*06 +*07 +*08 +*09 +*0A +*0B +*0C +*0D +*0E +*12 +*13 +*14 +*15 +*16 +*17 +*18 +*19 +*1A +*1B +*1C +*1D +*1E +*23 +*24 +*25 +*26 +*27 +*28 +*29 +*2A +*2B +*2C +*2D +*2E +*34 +*35 +*36 +*37 +*38 +*39 +*3A +*3B +*3C +*3D +*3E +*45 +*46 +*47 +*48 +*49 +*4A +*4B +*4C +*4D +*4E +*56 +*57 +*58 +*59 +*5A +*5B +*5C +*5D +*5E +*67 +*68 +*69 +*6A +*6B +*6C +*6D +*6E +*78 +*79 +*7A +*7B +*7C +*7D +*7E +*89 +*8A +*8B +*8C +*8D +*8E +*9A +*9B +*9C +*9D +*9E +*AB +*AC +*AD +*AE +*BC +*BD +*BE +*CD +*CE +*DE + +## Replace all instances of X with Y (most common leetspeak chars) + +se3 +so0 +si1 +si! +sa@ +se3 so0 +se3 si1 +se3 si! +se3 sa@ +so0 si1 +so0 si! +so0 sa@ +si1 sa@ +si! sa@ +se3 so0 si1 +se3 so0 si! +se3 so0 sa@ +se3 si1 sa@ +se3 si! sa@ +so0 si1 sa@ +so0 si! sa@ + +## Replace all instances of X with Y (less common leetspeak chars) + +sl1 +slk +sa4 +ss2 +ss5 +se6 +sun + +## Replace all instances of X with Y, toggle the case of characters at position N + +se3 T0 +se3 T1 +se3 T2 +se3 T3 +se3 T4 +se3 T5 +se3 T6 +se3 T7 +se3 T8 +se3 T9 +se3 TA +se3 TB +se3 TC +se3 TD +se3 TE +so0 T0 +so0 T1 +so0 T2 +so0 T3 +so0 T4 +so0 T5 +so0 T6 +so0 T7 +so0 T8 +so0 T9 +so0 TA +so0 TB +so0 TC +so0 TD +so0 TE +si1 T0 +si1 T1 +si1 T2 +si1 T3 +si1 T4 +si1 T5 +si1 T6 +si1 T7 +si1 T8 +si1 T9 +si1 TA +si1 TB +si1 TC +si1 TD +si1 TE +si! T0 +si! T1 +si! T2 +si! T3 +si! T4 +si! T5 +si! T6 +si! T7 +si! T8 +si! T9 +si! TA +si! TB +si! TC +si! TD +si! TE +sa@ T0 +sa@ T1 +sa@ T2 +sa@ T3 +sa@ T4 +sa@ T5 +sa@ T6 +sa@ T7 +sa@ T8 +sa@ T9 +sa@ TA +sa@ TB +sa@ TC +sa@ TD +sa@ TE +se3 so0 T0 +se3 so0 T1 +se3 so0 T2 +se3 so0 T3 +se3 so0 T4 +se3 so0 T5 +se3 so0 T6 +se3 so0 T7 +se3 so0 T8 +se3 so0 T9 +se3 so0 TA +se3 so0 TB +se3 so0 TC +se3 so0 TD +se3 so0 TE +se3 si1 T0 +se3 si1 T1 +se3 si1 T2 +se3 si1 T3 +se3 si1 T4 +se3 si1 T5 +se3 si1 T6 +se3 si1 T7 +se3 si1 T8 +se3 si1 T9 +se3 si1 TA +se3 si1 TB +se3 si1 TC +se3 si1 TD +se3 si1 TE +se3 si! T0 +se3 si! T1 +se3 si! T2 +se3 si! T3 +se3 si! T4 +se3 si! T5 +se3 si! T6 +se3 si! T7 +se3 si! T8 +se3 si! T9 +se3 si! TA +se3 si! TB +se3 si! TC +se3 si! TD +se3 si! TE +se3 sa@ T0 +se3 sa@ T1 +se3 sa@ T2 +se3 sa@ T3 +se3 sa@ T4 +se3 sa@ T5 +se3 sa@ T6 +se3 sa@ T7 +se3 sa@ T8 +se3 sa@ T9 +se3 sa@ TA +se3 sa@ TB +se3 sa@ TC +se3 sa@ TD +se3 sa@ TE +so0 si1 T0 +so0 si1 T1 +so0 si1 T2 +so0 si1 T3 +so0 si1 T4 +so0 si1 T5 +so0 si1 T6 +so0 si1 T7 +so0 si1 T8 +so0 si1 T9 +so0 si1 TA +so0 si1 TB +so0 si1 TC +so0 si1 TD +so0 si1 TE +so0 si! T0 +so0 si! T1 +so0 si! T2 +so0 si! T3 +so0 si! T4 +so0 si! T5 +so0 si! T6 +so0 si! T7 +so0 si! T8 +so0 si! T9 +so0 si! TA +so0 si! TB +so0 si! TC +so0 si! TD +so0 si! TE +so0 sa@ T0 +so0 sa@ T1 +so0 sa@ T2 +so0 sa@ T3 +so0 sa@ T4 +so0 sa@ T5 +so0 sa@ T6 +so0 sa@ T7 +so0 sa@ T8 +so0 sa@ T9 +so0 sa@ TA +so0 sa@ TB +so0 sa@ TC +so0 sa@ TD +so0 sa@ TE +si1 sa@ T0 +si1 sa@ T1 +si1 sa@ T2 +si1 sa@ T3 +si1 sa@ T4 +si1 sa@ T5 +si1 sa@ T6 +si1 sa@ T7 +si1 sa@ T8 +si1 sa@ T9 +si1 sa@ TA +si1 sa@ TB +si1 sa@ TC +si1 sa@ TD +si1 sa@ TE +si! sa@ T0 +si! sa@ T1 +si! sa@ T2 +si! sa@ T3 +si! sa@ T4 +si! sa@ T5 +si! sa@ T6 +si! sa@ T7 +si! sa@ T8 +si! sa@ T9 +si! sa@ TA +si! sa@ TB +si! sa@ TC +si! sa@ TD +si! sa@ TE +se3 so0 si1 T0 +se3 so0 si1 T1 +se3 so0 si1 T2 +se3 so0 si1 T3 +se3 so0 si1 T4 +se3 so0 si1 T5 +se3 so0 si1 T6 +se3 so0 si1 T7 +se3 so0 si1 T8 +se3 so0 si1 T9 +se3 so0 si1 TA +se3 so0 si1 TB +se3 so0 si1 TC +se3 so0 si1 TD +se3 so0 si1 TE +se3 so0 si! T0 +se3 so0 si! T1 +se3 so0 si! T2 +se3 so0 si! T3 +se3 so0 si! T4 +se3 so0 si! T5 +se3 so0 si! T6 +se3 so0 si! T7 +se3 so0 si! T8 +se3 so0 si! T9 +se3 so0 si! TA +se3 so0 si! TB +se3 so0 si! TC +se3 so0 si! TD +se3 so0 si! TE +se3 so0 sa@ T0 +se3 so0 sa@ T1 +se3 so0 sa@ T2 +se3 so0 sa@ T3 +se3 so0 sa@ T4 +se3 so0 sa@ T5 +se3 so0 sa@ T6 +se3 so0 sa@ T7 +se3 so0 sa@ T8 +se3 so0 sa@ T9 +se3 so0 sa@ TA +se3 so0 sa@ TB +se3 so0 sa@ TC +se3 so0 sa@ TD +se3 so0 sa@ TE +se3 si1 sa@ T0 +se3 si1 sa@ T1 +se3 si1 sa@ T2 +se3 si1 sa@ T3 +se3 si1 sa@ T4 +se3 si1 sa@ T5 +se3 si1 sa@ T6 +se3 si1 sa@ T7 +se3 si1 sa@ T8 +se3 si1 sa@ T9 +se3 si1 sa@ TA +se3 si1 sa@ TB +se3 si1 sa@ TC +se3 si1 sa@ TD +se3 si1 sa@ TE +se3 si! sa@ T0 +se3 si! sa@ T1 +se3 si! sa@ T2 +se3 si! sa@ T3 +se3 si! sa@ T4 +se3 si! sa@ T5 +se3 si! sa@ T6 +se3 si! sa@ T7 +se3 si! sa@ T8 +se3 si! sa@ T9 +se3 si! sa@ TA +se3 si! sa@ TB +se3 si! sa@ TC +se3 si! sa@ TD +se3 si! sa@ TE +so0 si1 sa@ T0 +so0 si1 sa@ T1 +so0 si1 sa@ T2 +so0 si1 sa@ T3 +so0 si1 sa@ T4 +so0 si1 sa@ T5 +so0 si1 sa@ T6 +so0 si1 sa@ T7 +so0 si1 sa@ T8 +so0 si1 sa@ T9 +so0 si1 sa@ TA +so0 si1 sa@ TB +so0 si1 sa@ TC +so0 si1 sa@ TD +so0 si1 sa@ TE +so0 si! sa@ T0 +so0 si! sa@ T1 +so0 si! sa@ T2 +so0 si! sa@ T3 +so0 si! sa@ T4 +so0 si! sa@ T5 +so0 si! sa@ T6 +so0 si! sa@ T7 +so0 si! sa@ T8 +so0 si! sa@ T9 +so0 si! sa@ TA +so0 si! sa@ TB +so0 si! sa@ TC +so0 si! sa@ TD +so0 si! sa@ TE + +## Toggle the case of characters at position N, one char, up to position 15 + +T0 +T1 +T2 +T3 +T4 +T5 +T6 +T7 +T8 +T9 +TA +TB +TC +TD +TE + +## Toggle the case of characters at position N, two chars, up to position 15 + +T0 T1 +T0 T2 +T0 T3 +T0 T4 +T0 T5 +T0 T6 +T0 T7 +T0 T8 +T0 T9 +T0 TA +T0 TB +T0 TC +T0 TD +T0 TE +T1 T2 +T1 T3 +T1 T4 +T1 T5 +T1 T6 +T1 T7 +T1 T8 +T1 T9 +T1 TA +T1 TB +T1 TC +T1 TD +T1 TE +T2 T3 +T2 T4 +T2 T5 +T2 T6 +T2 T7 +T2 T8 +T2 T9 +T2 TA +T2 TB +T2 TC +T2 TD +T2 TE +T3 T4 +T3 T5 +T3 T6 +T3 T7 +T3 T8 +T3 T9 +T3 TA +T3 TB +T3 TC +T3 TD +T3 TE +T4 T5 +T4 T6 +T4 T7 +T4 T8 +T4 T9 +T4 TA +T4 TB +T4 TC +T4 TD +T4 TE +T5 T6 +T5 T7 +T5 T8 +T5 T9 +T5 TA +T5 TB +T5 TC +T5 TD +T5 TE +T6 T7 +T6 T8 +T6 T9 +T6 TA +T6 TB +T6 TC +T6 TD +T6 TE +T7 T8 +T7 T9 +T7 TA +T7 TB +T7 TC +T7 TD +T7 TE +T8 T9 +T8 TA +T8 TB +T8 TC +T8 TD +T8 TE +T9 TA +T9 TB +T9 TC +T9 TD +T9 TE +TA TB +TA TC +TA TD +TA TE +TB TC +TB TD +TB TE +TC TD +TC TE +TD TE + +## Inserts character X at position N (passphrase maker), one char, up to length 15 + +i0 : +i1 : +i2 : +i3 : +i4 : +i5 : +i6 : +i7 : +i8 : +i9 : +iA : +iB : +iC : +iD : +iE : + +## Inserts character X at position N (passphrase maker), two chars, up to length 15 + +i0 i1 : +i0 i2 : +i0 i3 : +i0 i4 : +i0 i5 : +i0 i6 : +i0 i7 : +i0 i8 : +i0 i9 : +i0 iA : +i0 iB : +i0 iC : +i0 iD : +i0 iE : +i1 i2 : +i1 i3 : +i1 i4 : +i1 i5 : +i1 i6 : +i1 i7 : +i1 i8 : +i1 i9 : +i1 iA : +i1 iB : +i1 iC : +i1 iD : +i1 iE : +i2 i3 : +i2 i4 : +i2 i5 : +i2 i6 : +i2 i7 : +i2 i8 : +i2 i9 : +i2 iA : +i2 iB : +i2 iC : +i2 iD : +i2 iE : +i3 i4 : +i3 i5 : +i3 i6 : +i3 i7 : +i3 i8 : +i3 i9 : +i3 iA : +i3 iB : +i3 iC : +i3 iD : +i3 iE : +i4 i5 : +i4 i6 : +i4 i7 : +i4 i8 : +i4 i9 : +i4 iA : +i4 iB : +i4 iC : +i4 iD : +i4 iE : +i5 i6 : +i5 i7 : +i5 i8 : +i5 i9 : +i5 iA : +i5 iB : +i5 iC : +i5 iD : +i5 iE : +i6 i7 : +i6 i8 : +i6 i9 : +i6 iA : +i6 iB : +i6 iC : +i6 iD : +i6 iE : +i7 i8 : +i7 i9 : +i7 iA : +i7 iB : +i7 iC : +i7 iD : +i7 iE : +i8 i9 : +i8 iA : +i8 iB : +i8 iC : +i8 iD : +i8 iE : +i9 iA : +i9 iB : +i9 iC : +i9 iD : +i9 iE : +iA iB : +iA iC : +iA iD : +iA iE : +iB iC : +iB iD : +iB iE : +iC iD : +iC iE : +iD iE : + +## Inserts character X at position N (passphrase maker), one char, up to length 15 + +i0- : +i1- : +i2- : +i3- : +i4- : +i5- : +i6- : +i7- : +i8- : +i9- : +iA- : +iB- : +iC- : +iD- : +iE- : + +## Inserts character X at position N (passphrase maker), two chars, up to length 15 + +i0- i1- : +i0- i2- : +i0- i3- : +i0- i4- : +i0- i5- : +i0- i6- : +i0- i7- : +i0- i8- : +i0- i9- : +i0- iA- : +i0- iB- : +i0- iC- : +i0- iD- : +i0- iE- : +i1- i2- : +i1- i3- : +i1- i4- : +i1- i5- : +i1- i6- : +i1- i7- : +i1- i8- : +i1- i9- : +i1- iA- : +i1- iB- : +i1- iC- : +i1- iD- : +i1- iE- : +i2- i3- : +i2- i4- : +i2- i5- : +i2- i6- : +i2- i7- : +i2- i8- : +i2- i9- : +i2- iA- : +i2- iB- : +i2- iC- : +i2- iD- : +i2- iE- : +i3- i4- : +i3- i5- : +i3- i6- : +i3- i7- : +i3- i8- : +i3- i9- : +i3- iA- : +i3- iB- : +i3- iC- : +i3- iD- : +i3- iE- : +i4- i5- : +i4- i6- : +i4- i7- : +i4- i8- : +i4- i9- : +i4- iA- : +i4- iB- : +i4- iC- : +i4- iD- : +i4- iE- : +i5- i6- : +i5- i7- : +i5- i8- : +i5- i9- : +i5- iA- : +i5- iB- : +i5- iC- : +i5- iD- : +i5- iE- : +i6- i7- : +i6- i8- : +i6- i9- : +i6- iA- : +i6- iB- : +i6- iC- : +i6- iD- : +i6- iE- : +i7- i8- : +i7- i9- : +i7- iA- : +i7- iB- : +i7- iC- : +i7- iD- : +i7- iE- : +i8- i9- : +i8- iA- : +i8- iB- : +i8- iC- : +i8- iD- : +i8- iE- : +i9- iA- : +i9- iB- : +i9- iC- : +i9- iD- : +i9- iE- : +iA- iB- : +iA- iC- : +iA- iD- : +iA- iE- : +iB- iC- : +iB- iD- : +iB- iE- : +iC- iD- : +iC- iE- : +iD- iE- :