Sophie

Sophie

distrib > Fedora > 15 > i386 > by-pkgid > 9cf73da8613f86947acd9c843104bdac > files > 1

john-1.7.6-2.fc15.i686.rpm

#
# This file is part of John the Ripper password cracker,
# Copyright (c) 1996-2006,2008-2010 by Solar Designer
#

[Options]
# Wordlist file name, to be used in batch mode
Wordlist = $JOHN/password.lst
# Use idle cycles only
Idle = Y
# Crash recovery file saving delay in seconds
Save = 600
# Beep when a password is found (who needs this anyway?)
Beep = N

# "Single crack" mode rules
[List.Rules:Single]
# Simple rules come first...
:
-s x**
-c (?a c Q
-c l Q
-s-c x** /?u l
# These were not included in crackers I've seen, but are pretty efficient,
# so I include them near the beginning
>6 '6
>7 '7 l
-c >6 '6 /?u l
>5 '5
# Weird order, eh? Can't do anything about it, the order is based on the
# number of successful cracks...
<* d
r c
-c <* (?a d c
-c >5 '5 /?u l
-c u Q
-c )?a r l
-[:c] <* !?A \p1[lc] p
-c <* c Q d
-c >7 '7 /?u
>4 '4 l
-c <+ (?l c r
-c <+ )?l l Tm
>3 '3
-c >4 '4 /?u
-c >3 '3 /?u l
-c u Q r
<* d M 'l f Q
-c <* l Q d M 'l f Q
# About 50% of single-mode-crackable passwords get cracked by now...
# >2 x12 ... >8 x18
>[2-8] x1\1
>9 \[
# >3 x22 ... >9 x28
>[3-9] x2\p[2-8]
# >4 x32 ... >9 x37
>[4-9] x3\p[2-7]
# >2 x12 /?u l ... >8 x18 /?u l
-c >[2-8] x1\1 /?u l
-c >9 \[ /?u l
# >3 x22 /?u l ... >9 x28 /?u l
-c >[3-9] x2\p[2-8] /?u l
# >4 x32 /?u l ... >9 x37 /?u l
-c >[4-9] x3\p[2-7] /?u l
# Now to the suffix stuff...
<* l $[1-9!0a-rt-z"-/:-@\[-`{-~]
-c <* (?a c $[1-9!0a-rt-z"-/:-@\[-`{-~]
-[:c] <* !?A (?\p1[za] \p1[lc] $s M 'l p Q X0z0 'l $s
-[:c] <* /?A (?\p1[za] \p1[lc] $s
<* l r $[1-9!]
-c <* /?a u $[1-9!]
-[:c] <- (?\p1[za] \p1[lc] Az"'s"
-[:c] <- (?\p1[za] \p1[lc] Az"!!"
-[:c] (?\p1[za] \p1[lc] $! <- Az"!!"
# Removing vowels...
-[:c] /?v @?v >2 (?\p1[za] \p1[lc]
/?v @?v >2 <* d
# crack -> cracked, crack -> cracking
<* l [PI]
-c <* l [PI] (?a c
# mary -> marie
-[:c] <* (?\p1[za] \p1[lc] )y omi $e
# marie -> mary
-[:c] <* (?\p1[za] \p1[lc] )e \] )i val1 oay
# The following are some 3l33t rules
-[:c] l /[aelos] s\0\p[4310$] (?\p1[za] \p1[:c]
-[:c] l /a /[elos] sa4 s\0\p[310$] (?\p1[za] \p1[:c]
-[:c] l /e /[los] se3 s\0\p[10$] (?\p1[za] \p1[:c]
-[:c] l /l /[os] sl1 s\0\p[0$] (?\p1[za] \p1[:c]
-[:c] l /o /s so0 ss$ (?\p1[za] \p1[:c]
-[:c] l /a /e /[los] sa4 se3 s\0\p[10$] (?\p1[za] \p1[:c]
-[:c] l /a /l /[os] sa4 sl1 s\0\p[0$] (?\p1[za] \p1[:c]
-[:c] l /a /o /s sa4 so0 ss$ (?\p1[za] \p1[:c]
-[:c] l /e /l /[os] se3 sl1 s\0\p[0$] (?\p1[za] \p1[:c]
-[:c] l /[el] /o /s s\0\p[31] so0 ss$ (?\p1[za] \p1[:c]
-[:c] l /a /e /l /[os] sa4 se3 sl1 s\0\p[0$] (?\p1[za] \p1[:c]
-[:c] l /a /[el] /o /s sa4 s\0\p[31] so0 ss$ (?\p1[za] \p1[:c]
-[:c] l /e /l /o /s se3 sl1 so0 ss$ (?\p1[za] \p1[:c]
-[:c] l /a /e /l /o /s sa4 se3 sl1 so0 ss$ (?\p1[za] \p1[:c]
# Now to the prefix stuff...
l ^[1a-z2-90]
-c l Q ^[A-Z]
^[A-Z]
l ^["-/:-@\[-`{-~]
-[:c] <9 (?a \p1[lc] A0"[tT]he"
-[:c] <9 (?a \p1[lc] A0"[aA]my"
-[:c] <9 (?a \p1[lc] A0"[mdMD]r"
-[:c] <9 (?a \p1[lc] A0"[mdMD]r."
-[:c] <9 (?a \p1[lc] A0"__"
<- !?A l p ^[240-9]
# Some word pair rules...
# johnsmith -> JohnSmith, johnSmith
-p-c (?a 2 (?a c 1 [cl]
# JohnSmith -> john smith, john_smith, john-smith
-p 1 <- $[ _\-] + l
# JohnSmith -> John smith, John_smith, John-smith
-p-c 1 <- (?a c $[ _\-] 2 l
# JohnSmith -> john Smith, john_Smith, john-Smith
-p-c 1 <- l $[ _\-] 2 (?a c
# johnsmith -> John Smith, John_Smith, John-Smith
-p-c 1 <- (?a c $[ _\-] 2 (?a c
# Applying different simple rules to each of the two words
-p-[c:] 1 \p1[ur] 2 l
-p-c 2 (?a c 1 [ur]
-p-[c:] 1 l 2 \p1[ur]
-p-c 1 (?a c 2 [ur]
# jsmith -> smithj, etc...
-[:c] (?a \p1[lc] [{}]
-[:c] (?a \p1[lc] [{}] \0
# Toggle case...
-c <+ )?u l Tm
-c T0 Q M c Q l Q u Q C Q X0z0 'l
-c T[1-9A-E] Q M l Tm Q C Q u Q l Q c Q X0z0 'l
-c l Q T[1-9A-E] Q M T\0 Q l Tm Q C Q u Q X0z0 'l
-c >2 <G %2?a [lu] T0 M T2 T4 T6 T8 TA TC TE Q M l Tm Q X0z0 'l
-c >2 /?l /?u t Q M c Q C Q l Tm Q X0z0 'l
# Deleting chars...
>[2-8] D\p[1-7]
>[8-9A-E] D\1
-c /?u >[2-8] D\p[1-7] l
-c /?u >[8-9A-E] D\1 l
=1?a \[ M c Q
-c (?a >[1-9A-E] D\1 c
# Inserting a dot...
-[:c] >3 (?a \p1[lc] i[12].
# More suffix stuff...
<- l Az"[190][0-9]"
-c <- (?a c Az"[190][0-9]"
<- l Az"[782][0-9]"
-c <- (?a c Az"[782][0-9]"
<* l $[A-Z]
-c <* (?a c $[A-Z]
# cracking -> CRACKiNG
-c u /I sIi
# Crack96 -> cRACK96
%2?a C Q
# Crack96 -> cRACK(^
/?A S Q
# Crack96 -> CRaCK96
-c /?v V Q
# Really weird charset conversions, like "england" -> "rmh;smf"
:[RL] Q
l Q [RL]
-c (?a c Q [RL]
:[RL] \0 Q
# Both prefixing and suffixing...
<- l ^[1!@#$%^&*\-=_+.?|:'"] $\1
<- l ^[({[<] $\p[)}\]>]
# The rest of two-digit suffix stuff, less common numbers...
<- l Az"[63-5][0-9]"
-c <- (?a c Az"[63-5][0-9]"
# Some three-digit numbers...
-[:c] (?a \p1[lc] Az"007" <+
-[:c] (?a \p1[lc] Az"123" <+
-[:c] (?a \p1[lc] Az"[1-9]\0\0" <+
# Some [birth] years...
l Az"19[7-96-0]" <+ >-
l Az"20[01]" <+ >-
l Az"19[7-9][0-9]" <+
l Az"20[01][0-9]" <+
l Az"19[6-0][9-0]" <+
# Uncomment the following lines if you're really crazy
;# Insert/overstrike some characters...
;!?A >[1-6] l i\0[a-z]
;!?A l o0[a-z]
;!?A >[1-7] l o\0[a-z]
;# Toggle case everywhere (up to length 8), assuming that certain case
;# combinations were already tried.
;-c T1 Q M T0 Q
;-c T2 Q M T[z0] T[z1] Q
;-c T3 Q M T[z0] T[z1] T[z2] Q
;-c T4 Q M T[z0] T[z1] T[z2] T[z3] Q
;-c T5 Q M T[z0] T[z1] T[z2] T[z3] T[z4] Q
;-c T6 Q M T[z0] T[z1] T[z2] T[z3] T[z4] T[z5] Q
;-c T7 Q M T[z0] T[z1] T[z2] T[z3] T[z4] T[z5] T[z6] Q
;# Very slow stuff...
;l Az"[1-90][0-9][0-9]" <+
;-c (?a c Az"[1-90][0-9][0-9]" <+
;<[\-9] l A\p[z0]"[a-z][a-z]"
;<- l ^[a-z] $[a-z]

# Wordlist mode rules
[List.Rules:Wordlist]
# Try words as they are
:
# Lowercase every pure alphanumeric word
-c >3 !?X l Q
# Capitalize every pure alphanumeric word
-c (?a >2 !?X c Q
# Lowercase and pluralize pure alphabetic words
<* >2 !?A l p
# Lowercase pure alphabetic words and append '1'
<* >2 !?A l $1
# Capitalize pure alphabetic words and append '1'
-c <* >2 !?A c $1
# Duplicate reasonably short pure alphabetic words (fred -> fredfred)
<7 >1 !?A l d
# Lowercase and reverse pure alphabetic words
>3 !?A l M r Q
# Prefix pure alphabetic words with '1'
>2 !?A l ^1
# Uppercase pure alphanumeric words
-c >2 !?X u Q M c Q u
# Lowercase pure alphabetic words and append a digit or simple punctuation
<* >2 !?A l $[2!37954860.?]
# Words containing punctuation, which is then squeezed out, lowercase
/?p @?p >3 l
# Words with vowels removed, lowercase
/?v @?v >3 l
# Words containing whitespace, which is then squeezed out, lowercase
/?w @?w >3 l
# Capitalize and duplicate short pure alphabetic words (fred -> FredFred)
-c <7 >1 !?A c d
# Capitalize and reverse pure alphabetic words (fred -> derF)
-c <+ >2 !?A c r
# Reverse and capitalize pure alphabetic words (fred -> Derf)
-c >2 !?A l M r Q c
# Lowercase and reflect pure alphabetic words (fred -> fredderf)
<7 >1 !?A l d M 'l f Q
# Uppercase the last letter of pure alphabetic words (fred -> freD)
-c <+ >2 !?A l M r Q c r
# Prefix pure alphabetic words with '2' or '4'
>2 !?A l ^[24]
# Capitalize pure alphabetic words and append a digit or simple punctuation
-c <* >2 !?A c $[2!3957468.?0]
# Prefix pure alphabetic words with digits
>2 !?A l ^[379568]
# Capitalize and pluralize pure alphabetic words of reasonable length
-c <* >2 !?A c p
# Lowercase/capitalize pure alphabetic words of reasonable length and convert:
# crack -> cracked, crack -> cracking
-[:c] <* >2 !?A \p1[lc] M [PI] Q
# Try the second half of split passwords
-s x**
-s-c x** M l Q

# Case toggler for cracking MD4-based NTLM hashes (with the contributed patch)
# given already cracked DES-based LM hashes.
# Rename this section to [List.Rules:Wordlist] to activate it.
[List.Rules:NT]
:
-c T0Q
-c T1QT[z0]
-c T2QT[z0]T[z1]
-c T3QT[z0]T[z1]T[z2]
-c T4QT[z0]T[z1]T[z2]T[z3]
-c T5QT[z0]T[z1]T[z2]T[z3]T[z4]
-c T6QT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]
-c T7QT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]
-c T8QT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]T[z7]
-c T9QT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]T[z7]T[z8]
-c TAQT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]T[z7]T[z8]T[z9]
-c TBQT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]T[z7]T[z8]T[z9]T[zA]
-c TCQT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]T[z7]T[z8]T[z9]T[zA]T[zB]
-c TDQT[z0]T[z1]T[z2]T[z3]T[z4]T[z5]T[z6]T[z7]T[z8]T[z9]T[zA]T[zB]T[zC]

# Incremental modes
[Incremental:All]
File = $JOHN/all.chr
MinLen = 0
MaxLen = 8
CharCount = 95

[Incremental:Alpha]
File = $JOHN/alpha.chr
MinLen = 1
MaxLen = 8
CharCount = 26

[Incremental:Digits]
File = $JOHN/digits.chr
MinLen = 1
MaxLen = 8
CharCount = 10

[Incremental:Alnum]
File = $JOHN/alnum.chr
MinLen = 1
MaxLen = 8
CharCount = 36

[Incremental:LanMan]
File = $JOHN/lanman.chr
MinLen = 0
MaxLen = 7
CharCount = 69

# Some pre-defined word filters
[List.External:Filter_Alpha]
void filter()
{
	int i, c;

	i = 0;
	while (c = word[i++])
	if (c < 'a' || c > 'z') {
		word = 0; return;
	}
}

[List.External:Filter_Digits]
void filter()
{
	int i, c;

	i = 0;
	while (c = word[i++])
	if (c < '0' || c > '9') {
		word = 0; return;
	}
}

[List.External:Filter_Alnum]
void filter()
{
	int i, c;

	i = 0;
	while (c = word[i++])
	if ((c < 'a' || c > 'z') && (c < '0' || c > '9')) {
		word = 0; return;
	}
}

[List.External:Filter_LanMan]
void filter()
{
	int i, c;

	word[7] = 0;			// Truncate at 7 characters

	i = 0;				// Convert to uppercase
	while (c = word[i]) {
		if (c >= 'a' && c <= 'z') word[i] &= 0xDF;
		i++;
	}
}

# A simple cracker for LM hashes
[List.External:LanMan]
int length;				// Current length

void init()
{
	word[0] = 'A' - 1;		// Start with "A"
	word[length = 1] = 0;
}

void generate()
{
	int i;

	i = length - 1;			// Start from the last character
	while (++word[i] > 'Z')		// Try to increase it
	if (i)				// Overflow here, any more positions?
		word[i--] = 'A';	// Yes, move to the left, and repeat
	else				// No
	if (length < 7) {
		word[i = ++length] = 0;	// Switch to the next length
		while (i--)
			word[i] = 'A';
		return;
	} else {
		word = 0; return;	// We're done
	}
}

void restore()
{
	length = 0;			// Calculate the length
	while (word[length]) length++;
}

# Simple and well-commented, yet useful external mode example
[List.External:Double]
/*
 * This cracking mode tries all the possible duplicated lowercase alphabetic
 * "words" of up to 8 characters long.  Since word halves are the same, it
 * only has to try about 500,000 words.
 */

/* Global variables: current length and word */
int length, current[9];

/* Called at startup to initialize the global variables */
void init()
{
	int i;

	i = length = 2;			// Start with 4 character long words
	while (i--) current[i] = 'a';	// Set our half-word to "aa"
}

/* Generates a new word */
void generate()
{
	int i;

/* Export last generated word, duplicating it at the same time; here "word"
 * is a pre-defined external variable. */
	word[(i = length) << 1] = 0;
	while (i--) word[length + i] = word[i] = current[i];

/* Generate a new word */
	i = length - 1;			// Start from the last character
	while (++current[i] > 'z')	// Try to increase it
	if (i)				// Overflow here, any more positions?
		current[i--] = 'a';	// Yes, move to the left, and repeat
	else {				// No
		current = 0;		// Request a length switch
		break;			// Break out of the loop
	}

/* Switch to the next length, unless we were generating 8 character long
 * words already. */
	if (!current && length < 4) {
		i = ++length;
		while (i--) current[i] = 'a';
	}
}

/* Called when restoring an interrupted session */
void restore()
{
	int i;

/* Import the word back */
	i = 0;
	while (current[i] = word[i]) i++;

/* ...and calculate the half-word length */
	length = i >> 1;
}

# Trivial parallel processing example
[List.External:Parallel]
/*
 * This word filter makes John process some of the words only, for running
 * multiple instances on different CPUs.  It can be used with any cracking
 * mode except for "single crack".  Note: this is not a good solution, but
 * is just an example of what can be done with word filters.
 */

int node, total;			// This node's number, and node count
int number;				// Current word number

void init()
{
	node = 1; total = 2;		// Node 1 of 2, change as appropriate
	number = node - 1;		// Speedup the filter a bit
}

void filter()
{
	if (number++ % total)		// Word for a different node?
		word = 0;		// Yes, skip it
}

# Strip 0.5 ("Secure Tool for Recalling Important Passwords") cracker,
# based on analysis done by Thomas Roessler and Ian Goldberg.  This will
# crack passwords you may have generated with Strip; other uses of Strip
# are unaffected.
[List.External:Strip]
int minlength, maxlength, mintype, maxtype;
int crack_seed, length, type;
int count, charset[128];

void init()
{
	int c;

/* Password lengths to try; Strip can generate passwords of 4 to 16
 * characters, but traditional crypt(3) hashes are limited to 8. */
	minlength = 4;	// 4
	maxlength = 8;	// 16

/* Password types to try (Numeric, Alpha-Num, Alpha-Num w/ Meta). */
	mintype = 0;	// 0
	maxtype = 2;	// 2

	crack_seed = 0x10000;
	length = minlength - 1;
	type = mintype;

	count = 0;
	c = '0'; while (c <= '9') charset[count++] = c++;
}

void generate()
{
	int seed, random;
	int i, c;

	if (crack_seed > 0xffff) {
		crack_seed = 0;

		if (++length > maxlength) {
			length = minlength;

			if (++type > maxtype) {
				word[0] = 0;
				return;
			}
		}

		count = 10;
		if (type >= 1) {
			c = 'a'; while (c <= 'f') charset[count++] = c++;
			c = 'h'; while (c <= 'z') charset[count++] = c++;
			c = 'A'; while (c <= 'Z') charset[count++] = c++;
		}
		if (type == 2) {
			charset[count++] = '!';
			c = '#'; while (c <= '&') charset[count++] = c++;
			c = '('; while (c <= '/') charset[count++] = c++;
			c = '<'; while (c <= '>') charset[count++] = c++;
			charset[count++] = '?'; charset[count++] = '@';
			charset[count++] = '['; charset[count++] = ']';
			charset[count++] = '^'; charset[count++] = '_';
			c = '{'; while (c <= '~') charset[count++] = c++;
		}
	}

	seed = (crack_seed++ << 16 >> 16) * 22695477 + 1;

	i = 0;
	while (i < length) {
		random = ((seed = seed * 22695477 + 1) >> 16) & 0x7fff;
		word[i++] = charset[random % count];
	}

	word[i] = 0;
}

# Try sequences of adjacent keys on a keyboard as candidate passwords
[List.External:Keyboard]
int maxlength, length;	// Maximum password length to try, current length
int fuzz;		// The desired "fuzz factor", either 0 or 1
int id[15];		// Current character indices for each position
int m[0x400], mc[0x80];	// The keys matrix, counts of adjacent keys
int f[0x40], fc;	// Characters for the first position, their count

void init()
{
	int minlength;
	int i, j, c, p;
	int k[0x40];

	minlength = 1;	// Initial password length to try
	maxlength = 15;	// Maximum password length to try, up to 15
	fuzz = 1;	// "Fuzz factor", set to 0 for much quicker runs

/*
 * This defines the keyboard layout, by default for a QWERTY keyboard.
 * Please note that the sizes of m[] and mc[] arrays assume 7-bit
 * characters and will need to be doubled for 8-bit characters such as
 * umlauts.
 */
	i = 0; while (i < 0x40) k[i++] = 0;
	k[0] = '`';
	i = 0; while (++i <= 9) k[i] = '0' + i;
	k[10] = '0'; k[11] = '-'; k[12] = '=';
	k[0x11] = 'q'; k[0x12] = 'w'; k[0x13] = 'e'; k[0x14] = 'r';
	k[0x15] = 't'; k[0x16] = 'y'; k[0x17] = 'u'; k[0x18] = 'i';
	k[0x19] = 'o'; k[0x1a] = 'p'; k[0x1b] = '['; k[0x1c] = ']';
	k[0x1d] = '\\';
	k[0x21] = 'a'; k[0x22] = 's'; k[0x23] = 'd'; k[0x24] = 'f';
	k[0x25] = 'g'; k[0x26] = 'h'; k[0x27] = 'j'; k[0x28] = 'k';
	k[0x29] = 'l'; k[0x2a] = ';'; k[0x2b] = '\'';
	k[0x31] = 'z'; k[0x32] = 'x'; k[0x33] = 'c'; k[0x34] = 'v';
	k[0x35] = 'b'; k[0x36] = 'n'; k[0x37] = 'm'; k[0x38] = ',';
	k[0x39] = '.'; k[0x3a] = '/';

	i = 0; while (i < 0x80) mc[i++] = 0;
	fc = 0;

	/* rows */
	c = 0;
	i = 0;
	while (i < 0x40) {
		p = c;
		c = k[i++];
		if (!c) continue;
		f[fc++] = c;
		if (!p) continue;
		m[(c << 3) + mc[c]++] = p;
		m[(p << 3) + mc[p]++] = c;
	}
	f[fc] = 0;

	/* columns */
	i = 0;
	while (i < 0x30) {
		p = k[i++];
		if (!p) continue;
		j = 1 - fuzz;
		while (j <= 1 + fuzz) {
			c = k[i + 0x10 - j++];
			if (!c) continue;
			m[(c << 3) + mc[c]++] = p;
			m[(p << 3) + mc[p]++] = c;
		}
	}

	id[0] = 0;
	length = minlength;
}

void generate()
{
	int i, p, maxcount;

	word[i = 0] = p = f[id[0]];
	while (++i < length)
		word[i] = p = m[(p << 3) + id[i]];
	word[i--] = 0;

	if (i) maxcount = mc[word[i - 1]]; else maxcount = fc;
	while (++id[i] >= maxcount) {
		if (!i) {
			if (length < maxlength) {
				id[0] = 0;
				id[length++] = 0;
			}
			return;
		}
		id[i--] = 0;
		if (i) maxcount = mc[word[i - 1]]; else maxcount = fc;
	}
}

void restore()
{
	int i;

	/* Calculate the length */
	length = 0;
	while (word[length]) length++;

	/* Infer the first character index */
	i = -1;
	while (++i < fc) {
		if (f[i] == word[0]) {
			id[0] = i;
			break;
		}
	}

	/* This sample can be enhanced to infer the rest of the indices here */
}

# Generic implementation of "dumb" exhaustive search, given a range of lengths
# and an arbitrary charset.  This is pre-configured to try 8-bit characters
# against LM hashes, which is only reasonable to do for very short password
# half lengths.
[List.External:DumbForce]
int maxlength;		// Maximum password length to try
int last;		// Last character position, zero-based
int lastid;		// Character index in the last position
int id[0x7f];		// Current character indices for other positions
int charset[0x100], c0;	// Character set

void init()
{
	int minlength;
	int i, c;

	minlength = 1;	// Initial password length to try, must be at least 1
	maxlength = 7;	// Must be at least same as minlength

/*
 * This defines the character set.
 *
 * Let's say, we want to try TAB, all non-control ASCII characters, and all
 * 8-bit characters, including the 8-bit terminal controls range (as these are
 * used as regular national characters with some 8-bit encodings), but except
 * for known terminal controls (risky for the terminal we may be running on).
 *
 * Also, let's say our hashes are case-insensitive, so skip lowercase letters
 * (this is right for LM hashes).
 */
	i = 0;
	charset[i++] = 9;		// Add horizontal TAB (ASCII 9), then
	c = ' ';			// start with space (ASCII 32) and
	while (c < 'a')			// proceed till lowercase 'a'
		charset[i++] = c++;
	c = 'z' + 1;			// Skip lowercase letters and
	while (c <= 0x7e)		// proceed for all printable ASCII
		charset[i++] = c++;
	c++;				// Skip DEL (ASCII 127) and
	while (c < 0x84)		// proceed over 8-bit codes till IND
		charset[i++] = c++;
	charset[i++] = 0x86;		// Skip IND (84 hex) and NEL (85 hex)
	charset[i++] = 0x87;
	c = 0x89;			// Skip HTS (88 hex)
	while (c < 0x8d)		// Proceed till RI (8D hex)
		charset[i++] = c++;
	c = 0x91;			// Skip RI, SS2, SS3, DCS
	while (c < 0x96)		// Proceed till SPA (96 hex)
		charset[i++] = c++;
	charset[i++] = 0x99;		// Skip SPA, EPA, SOS
	c = 0xa0;			// Skip DECID, CSI, ST, OSC, PM, APC
	while (c <= 0xff)		// Proceed with the rest of 8-bit codes
		charset[i++] = c++;

/* Zero-terminate it, and cache the first character */
	charset[i] = 0;
	c0 = charset[0];

	last = minlength - 1;
	i = 0;
	while (i <= last) {
		id[i] = 0;
		word[i++] = c0;
	}
	lastid = -1;
	word[i] = 0;
}

void generate()
{
	int i;

/* Handle the typical case specially */
	if (word[last] = charset[++lastid]) return;

	lastid = 0;
	word[i = last] = c0;
	while (i--) {			// Have a preceding position?
		if (word[i] = charset[++id[i]]) return;
		id[i] = 0;
		word[i] = c0;
	}

	if (++last < maxlength) {	// Next length?
		id[last] = lastid = 0;
		word[last] = c0;
		word[last + 1] = 0;
	} else				// We're done
		word = 0;
}

void restore()
{
	int i, c;

/* Calculate the current length and infer the character indices */
	last = 0;
	while (c = word[last]) {
		i = 0; while (charset[i] != c && charset[i]) i++;
		if (!charset[i]) i = 0;	// Not found
		id[last++] = i;
	}
	lastid = id[--last];
}

# Generic implementation of exhaustive search for a partially-known password.
# This is pre-configured for length 8, lowercase and uppercase letters in the
# first 4 positions (52 different characters), and digits in the remaining 4
# positions - however, the corresponding part of init() may be modified to use
# arbitrary character sets or even fixed characters for each position.
[List.External:KnownForce]
int last;		// Last character position, zero-based
int lastofs;		// Last character position offset into charset[]
int lastid;		// Current character index in the last position
int id[0x7f];		// Current character indices for other positions
int charset[0x7f00];	// Character sets, 0x100 elements for each position

void init()
{
	int length;
	int pos, ofs, i, c;

	length = 8;	// Password length to try

/* This defines the character sets for different character positions */
	pos = 0;
	while (pos < 4) {
		ofs = pos++ << 8;
		i = 0;
		c = 'a';
		while (c <= 'z')
			charset[ofs + i++] = c++;
		c = 'A';
		while (c <= 'Z')
			charset[ofs + i++] = c++;
		charset[ofs + i] = 0;
	}
	while (pos < length) {
		ofs = pos++ << 8;
		i = 0;
		c = '0';
		while (c <= '9')
			charset[ofs + i++] = c++;
		charset[ofs + i] = 0;
	}

	last = length - 1;
	pos = -1;
	while (++pos <= last)
		word[pos] = charset[id[pos] = pos << 8];
	lastid = (lastofs = last << 8) - 1;
	word[pos] = 0;
}

void generate()
{
	int pos;

/* Handle the typical case specially */
	if (word[last] = charset[++lastid]) return;

	word[pos = last] = charset[lastid = lastofs];
	while (pos--) {			// Have a preceding position?
		if (word[pos] = charset[++id[pos]]) return;
		word[pos] = charset[id[pos] = pos << 8];
	}

	word = 0;			// We're done
}

void restore()
{
	int i, c;

/* Calculate the current length and infer the character indices */
	last = 0;
	while (c = word[last]) {
		i = lastofs = last << 8;
		while (charset[i] != c && charset[i]) i++;
		if (!charset[i]) i = lastofs; // Not found
		id[last++] = i;
	}
	lastid = id[--last];
}