From c40bb9ad9ea065457f34b0e1d701c2274d1cc666 Mon Sep 17 00:00:00 2001
From: magnum <magnum>
Date: Thu, 11 Aug 2011 01:13:38 +0200
Subject: [PATCH 5/8] User-defined character classes

---
 doc/RULES     |    2 +
 run/john.conf |   30 +++++++++++++++++++++
 src/rules.c   |   82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 114 insertions(+), 0 deletions(-)

diff --git a/doc/RULES b/doc/RULES
index 5ffaca0..ac609df 100644
--- a/doc/RULES
+++ b/doc/RULES
@@ -60,6 +60,8 @@ than that of "l" (length).
 ?x	matches letters and digits [a-zA-Z0-9]
 ?z	matches all characters
 ?b	matches characters with 8th bit set (mnemonic "b for binary")
+?N	where N is 0...9 are user-defined character classes. They match characters
+	as defined in john.conf, section [UserClasses]
 
 The complement of a class can be specified by uppercasing its name.  For
 example, "?D" matches everything but digits.
diff --git a/run/john.conf b/run/john.conf
index 47f1801..f2d12b9 100644
--- a/run/john.conf
+++ b/run/john.conf
@@ -42,6 +42,36 @@ TimeFormat = %c
 # reported figures will be less accurate (default 0.05%)
 ETAthreshold = 0.05%
 
+
+# A user defined character class is named with a single digit, ie. 0..9. After
+# the equal-sign, just list all characters that this class should match. You
+# can specify ranges within brackets, much like pre-processor ranges in rules.
+# BEWARE of encoding if using non-ASCII characters. If you put UTF-8 characters
+# here, it will *not* work! You must use a singlebyte encoding and it should
+# be the same here as you intend to use for your dictionary.
+# You can however put characters here in \xA3 format (for codepoint 0xA3 - in
+# many iso-8859-x codepages that would mean a pound sign). This works in ranges
+# too but NOTE that this syntax does NOT work in any other section of john.conf
+#
+# This is a couple of example classes:
+# ?0 matches (one version of) base64 characters
+# ?1 matches hex digits
+# ?2 matches the TAB character (never try to use \x00!)
+[UserClasses]
+0 = [a-zA-Z0-9/.]
+1 = [0-9a-fA-F]
+2 = \x09
+
+
+# This silly example rule use the user-defined character class
+# example ?1 above. It will prepend any hex-string of at least
+# 4 digits within a candidate with 0x as well as with  \x like
+# this:
+#  'hello'   -> rejected
+#  'alibaba' -> 'ali0xbaba' and 'ali\xbaba'
+[List.Rules:Example]
+%4?1vap2Aa"[\\0]x"
+
 # "Single crack" mode rules
 [List.Rules:Single]
 # Simple rules come first...
diff --git a/src/rules.c b/src/rules.c
index e2c5ac4..4c87a91 100644
--- a/src/rules.c
+++ b/src/rules.c
@@ -190,6 +190,72 @@ static void rules_init_class(char name, char *valid)
 	}
 }
 
+static char *userclass_expand(const char *src)
+{
+	unsigned const char *src2 = (unsigned char*)src;
+	char *dst_tmp = malloc(0x200);
+	char *dst = dst_tmp, *dstend = &dst_tmp[0x100];
+	int j, br = 0;
+
+	// pass 1: decode \xNN characters
+	while(*src && dst < dstend) {
+		if (*src == '\\' && (src[1]|0x20) == 'x' &&
+		    strlen(&src[2]) >= 2 && (sscanf(&src[2], "%2x", &j)))
+		{
+			*dst++ = (char) j;
+			src += 4;
+		} else
+			*dst++ = *src++;
+	}
+	*dst = 0;
+	strcpy((char*)src2, dst_tmp);
+	dst = dst_tmp;
+
+	// pass 2: parse ranges between brackets
+	while(*src2 && dst < dstend) {
+		if (*src2 == '\\') {
+			if (src2[1]) {
+				*dst++ = *++src2;
+				src2++;
+				continue;
+			} else
+				return NULL;
+		}
+
+		if (*src2 == '[' && br == 0) {
+			br = 1;
+			src2++;
+			continue;
+		}
+
+		if (br == 1) {
+			if (*src2 == ']') {
+				br = 0;
+				src2++;
+				continue;
+			}
+			if (*src2 == '-' && src2[1] && src2[1] != ']') {
+				if (src2[-1] < src2[1])
+					for (j=src2[-1] + 1; j < src2[1]; j++)
+						*dst++ = j;
+				else
+					for (j=src2[-1] - 1; j > src2[1]; j--)
+						*dst++ = j;
+				*dst++ = *++src2;
+				src2++;
+				continue;
+			}
+		}
+		*dst++ = *src2++;
+	}
+	*dst = 0;
+	if (br)
+		return NULL;
+	dst = str_alloc_copy(dst_tmp);
+	free(dst_tmp);
+	return dst;
+}
+
 static void rules_init_classes(void)
 {
 	static unsigned char eightbitchars[129];
@@ -236,6 +302,22 @@ static void rules_init_classes(void)
 	}
 	rules_init_class('b', (char *)&eightbitchars);
 	rules_init_class('Z', "");
+
+	// Load user-defined character classes ?0 .. ?9 from john.conf
+	char user_class_num[] = "0";
+	char *user_class;
+	for(i='0'; i <= '9'; i++) {
+		user_class_num[0] = i;
+		if ((user_class = cfg_get_param("UserClasses", NULL, user_class_num))) {
+			if ((user_class = userclass_expand(user_class)))
+				rules_init_class(i, user_class);
+			else {
+				fprintf(stderr, "Invalid user-defined character class ?%c: "
+				        "Unexpected end of line\n", i);
+				error();
+			}
+		}
+	}
 }
 
 static char *rules_init_conv(char *src, char *dst)
-- 
1.7.4.1

