diff -Nur --ignore-all-space unhash-0.4/functions.c unhash-0.4-withmd4/functions.c
--- unhash-0.4/functions.c	2006-12-22 22:04:37.000000000 -0700
+++ unhash-0.4-withmd4/functions.c	2007-02-05 15:28:25.000000000 -0700
@@ -63,6 +63,7 @@
 		{
 			case 1:	printf ("MD5");	break;
 			case 2:	printf ("SHA1");	break;
+			case 3:	printf ("MD4");	break;
 			default:	printf ("Unknown");	break;
 		}
 	printf (" HASH***\n");
diff -Nur --ignore-all-space unhash-0.4/includes.h unhash-0.4-withmd4/includes.h
--- unhash-0.4/includes.h	2006-12-22 22:03:23.000000000 -0700
+++ unhash-0.4-withmd4/includes.h	2007-02-05 15:19:15.000000000 -0700
@@ -51,6 +51,7 @@
 void Usage (void);
 void Error (const int exit, const int errcode, const char *msg);
 char * digest2hash (const unsigned char *digest, const int mode);
+void md4crack (const char *word);
 void md5crack (const char *word);
 void sha1crack (const char *word);
 void (*calculation) (const char *word);
diff -Nur --ignore-all-space unhash-0.4/Makefile unhash-0.4-withmd4/Makefile
--- unhash-0.4/Makefile	2006-05-23 22:53:48.000000000 -0600
+++ unhash-0.4-withmd4/Makefile	2007-02-05 15:12:15.000000000 -0700
@@ -4,7 +4,7 @@
 #CFLAGS = -O3 -funroll-all-loops -fomit-frame-pointer
 CFLAGS =
 LIBS = -lssl
-OBJS = functions.o md5c_opt.o md5crack.o sha1.o sha1crack.o unhash.o
+OBJS = functions.o md5c_opt.o md5crack.o sha1.o sha1crack.o unhash.o md4.o md4crack.o
 
 # Default, will link with openssl.
 all: $(OBJS)
@@ -19,6 +19,12 @@
 functions.o: functions.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
+# MD4
+md4.o: md4/md4.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+md4crack.o: md4crack.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
 # MD5	
 md5c_opt.o: md5/md5c_opt.c
 	$(CC) $(CFLAGS) -c -o $@ $<
diff -Nur --ignore-all-space unhash-0.4/md4/global.h unhash-0.4-withmd4/md4/global.h
--- unhash-0.4/md4/global.h	1969-12-31 17:00:00.000000000 -0700
+++ unhash-0.4-withmd4/md4/global.h	2007-02-05 15:09:01.000000000 -0700
@@ -0,0 +1,32 @@
+/*
+ * GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+     function argument prototyping.
+   The following makes PROTOTYPES default to 0 if it has not already
+     been defined with C compiler flags.
+ */
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+   If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+     returns an empty list.
+ */
+
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
diff -Nur --ignore-all-space unhash-0.4/md4/global.h~ unhash-0.4-withmd4/md4/global.h~
--- unhash-0.4/md4/global.h~	1969-12-31 17:00:00.000000000 -0700
+++ unhash-0.4-withmd4/md4/global.h~	2007-02-05 15:06:23.000000000 -0700
@@ -0,0 +1,31 @@
+* GLOBAL.H - RSAREF types and constants
+ */
+
+/* PROTOTYPES should be set to one if and only if the compiler supports
+     function argument prototyping.
+   The following makes PROTOTYPES default to 0 if it has not already
+     been defined with C compiler flags.
+ */
+#ifndef PROTOTYPES
+#define PROTOTYPES 0
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
+   If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
+     returns an empty list.
+ */
+
+#if PROTOTYPES
+#define PROTO_LIST(list) list
+#else
+#define PROTO_LIST(list) ()
+#endif
diff -Nur --ignore-all-space unhash-0.4/md4/md4.c unhash-0.4-withmd4/md4/md4.c
--- unhash-0.4/md4/md4.c	1969-12-31 17:00:00.000000000 -0700
+++ unhash-0.4-withmd4/md4/md4.c	2007-02-05 15:07:14.000000000 -0700
@@ -0,0 +1,303 @@
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD4 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+ */
+
+#include "global.h"
+#include "md4.h"
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
+static void Encode PROTO_LIST
+  ((unsigned char *, UINT4 *, unsigned int));
+static void Decode PROTO_LIST
+  ((UINT4 *, unsigned char *, unsigned int));
+static void MD4_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
+static void MD4_memset PROTO_LIST ((POINTER, int, unsigned int));
+
+static unsigned char PADDING[64] = {
+  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+
+#define FF(a, b, c, d, x, s) { \
+    (a) += F ((b), (c), (d)) + (x); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+  }
+#define GG(a, b, c, d, x, s) { \
+    (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+    (a) = ROTATE_LEFT ((a), (s)); \
+  }
+#define HH(a, b, c, d, x, s) { \
+    (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+    (a) = ROTATE_LEFT ((a), (s)); \
+  }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+void MD4Init (context)
+MD4_CTX *context;                                        /* context */
+{
+  context->count[0] = context->count[1] = 0;
+
+  /* Load magic initialization constants.
+   */
+  context->state[0] = 0x67452301;
+  context->state[1] = 0xefcdab89;
+  context->state[2] = 0x98badcfe;
+  context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+     operation, processing another message block, and updating the
+     context.
+ */
+void MD4Update (context, input, inputLen)
+MD4_CTX *context;                                        /* context */
+unsigned char *input;                                /* input block */
+unsigned int inputLen;                     /* length of input block */
+{
+  unsigned int i, index, partLen;
+
+  /* Compute number of bytes mod 64 */
+  index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+  /* Update number of bits */
+  if ((context->count[0] += ((UINT4)inputLen << 3))
+      < ((UINT4)inputLen << 3))
+    context->count[1]++;
+  context->count[1] += ((UINT4)inputLen >> 29);
+
+  partLen = 64 - index;
+
+  /* Transform as many times as possible.
+   */
+  if (inputLen >= partLen) {
+    MD4_memcpy
+      ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+    MD4Transform (context->state, context->buffer);
+
+    for (i = partLen; i + 63 < inputLen; i += 64)
+      MD4Transform (context->state, &input[i]);
+
+    index = 0;
+  }
+  else
+    i = 0;
+
+  /* Buffer remaining input */
+  MD4_memcpy
+    ((POINTER)&context->buffer[index], (POINTER)&input[i],
+     inputLen-i);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+     the message digest and zeroizing the context.
+ */
+void MD4Final (digest, context)
+unsigned char digest[16];                         /* message digest */
+MD4_CTX *context;                                        /* context */
+{
+  unsigned char bits[8];
+  unsigned int index, padLen;
+
+  /* Save number of bits */
+  Encode (bits, context->count, 8);
+
+  /* Pad out to 56 mod 64.
+   */
+  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+  padLen = (index < 56) ? (56 - index) : (120 - index);
+  MD4Update (context, PADDING, padLen);
+
+  /* Append length (before padding) */
+  MD4Update (context, bits, 8);
+  /* Store state in digest */
+  Encode (digest, context->state, 16);
+
+  /* Zeroize sensitive information.
+   */
+  MD4_memset ((POINTER)context, 0, sizeof (*context));
+
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+static void MD4Transform (state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  Decode (x, block, 64);
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11); /* 1 */
+  FF (d, a, b, c, x[ 1], S12); /* 2 */
+  FF (c, d, a, b, x[ 2], S13); /* 3 */
+  FF (b, c, d, a, x[ 3], S14); /* 4 */
+  FF (a, b, c, d, x[ 4], S11); /* 5 */
+  FF (d, a, b, c, x[ 5], S12); /* 6 */
+  FF (c, d, a, b, x[ 6], S13); /* 7 */
+  FF (b, c, d, a, x[ 7], S14); /* 8 */
+  FF (a, b, c, d, x[ 8], S11); /* 9 */
+  FF (d, a, b, c, x[ 9], S12); /* 10 */
+  FF (c, d, a, b, x[10], S13); /* 11 */
+  FF (b, c, d, a, x[11], S14); /* 12 */
+  FF (a, b, c, d, x[12], S11); /* 13 */
+  FF (d, a, b, c, x[13], S12); /* 14 */
+  FF (c, d, a, b, x[14], S13); /* 15 */
+  FF (b, c, d, a, x[15], S14); /* 16 */
+
+  /* Round 2 */
+  GG (a, b, c, d, x[ 0], S21); /* 17 */
+  GG (d, a, b, c, x[ 4], S22); /* 18 */
+  GG (c, d, a, b, x[ 8], S23); /* 19 */
+  GG (b, c, d, a, x[12], S24); /* 20 */
+  GG (a, b, c, d, x[ 1], S21); /* 21 */
+  GG (d, a, b, c, x[ 5], S22); /* 22 */
+  GG (c, d, a, b, x[ 9], S23); /* 23 */
+  GG (b, c, d, a, x[13], S24); /* 24 */
+  GG (a, b, c, d, x[ 2], S21); /* 25 */
+  GG (d, a, b, c, x[ 6], S22); /* 26 */
+  GG (c, d, a, b, x[10], S23); /* 27 */
+  GG (b, c, d, a, x[14], S24); /* 28 */
+  GG (a, b, c, d, x[ 3], S21); /* 29 */
+  GG (d, a, b, c, x[ 7], S22); /* 30 */
+  GG (c, d, a, b, x[11], S23); /* 31 */
+  GG (b, c, d, a, x[15], S24); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 0], S31); /* 33 */
+  HH (d, a, b, c, x[ 8], S32); /* 34 */
+  HH (c, d, a, b, x[ 4], S33); /* 35 */
+  HH (b, c, d, a, x[12], S34); /* 36 */
+  HH (a, b, c, d, x[ 2], S31); /* 37 */
+  HH (d, a, b, c, x[10], S32); /* 38 */
+  HH (c, d, a, b, x[ 6], S33); /* 39 */
+  HH (b, c, d, a, x[14], S34); /* 40 */
+  HH (a, b, c, d, x[ 1], S31); /* 41 */
+  HH (d, a, b, c, x[ 9], S32); /* 42 */
+  HH (c, d, a, b, x[ 5], S33); /* 43 */
+  HH (b, c, d, a, x[13], S34); /* 44 */
+  HH (a, b, c, d, x[ 3], S31); /* 45 */
+  HH (d, a, b, c, x[11], S32); /* 46 */
+  HH (c, d, a, b, x[ 7], S33); /* 47 */
+  HH (b, c, d, a, x[15], S34); /* 48 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  /* Zeroize sensitive information.
+   */
+  MD4_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+     a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4) {
+    output[j] = (unsigned char)(input[i] & 0xff);
+    output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+    output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+    output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+  }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+     a multiple of 4.
+
+ */
+static void Decode (output, input, len)
+
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4)
+    output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+      (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+static void MD4_memcpy (output, input, len)
+POINTER output;
+POINTER input;
+unsigned int len;
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+    output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD4_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+    ((char *)output)[i] = (char)value;
+}
diff -Nur --ignore-all-space unhash-0.4/md4/md4.h unhash-0.4-withmd4/md4/md4.h
--- unhash-0.4/md4/md4.h	1969-12-31 17:00:00.000000000 -0700
+++ unhash-0.4-withmd4/md4/md4.h	2007-02-05 15:06:45.000000000 -0700
@@ -0,0 +1,36 @@
+/* MD4.H - header file for MD4C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+   rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD4 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+ */
+
+/* MD4 context. */
+typedef struct {
+  UINT4 state[4];                                   /* state (ABCD) */
+  UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
+  unsigned char buffer[64];                         /* input buffer */
+} MD4_CTX;
+
+void MD4Init PROTO_LIST ((MD4_CTX *));
+void MD4Update PROTO_LIST
+  ((MD4_CTX *, unsigned char *, unsigned int));
+void MD4Final PROTO_LIST ((unsigned char [16], MD4_CTX *));
diff -Nur --ignore-all-space unhash-0.4/md4/mddriver.c unhash-0.4-withmd4/md4/mddriver.c
--- unhash-0.4/md4/mddriver.c	1969-12-31 17:00:00.000000000 -0700
+++ unhash-0.4-withmd4/md4/mddriver.c	2007-02-05 15:08:03.000000000 -0700
@@ -0,0 +1,234 @@
+/* MDDRIVER.C - test driver for MD2, MD4 and MD5
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
+   rights reserved.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+
+ */
+
+/* The following makes MD default to MD5 if it has not already been
+     defined with C compiler flags.
+ */
+#ifndef MD
+#define MD MD5
+#endif
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include "global.h"
+#if MD == 2
+#include "md2.h"
+#endif
+#if MD == 4
+#include "md4.h"
+#endif
+#if MD == 5
+#include "md5.h"
+#endif
+
+/* Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 1000
+#define TEST_BLOCK_COUNT 1000
+
+static void MDString PROTO_LIST ((char *));
+static void MDTimeTrial PROTO_LIST ((void));
+static void MDTestSuite PROTO_LIST ((void));
+static void MDFile PROTO_LIST ((char *));
+static void MDFilter PROTO_LIST ((void));
+static void MDPrint PROTO_LIST ((unsigned char [16]));
+
+#if MD == 2
+#define MD_CTX MD2_CTX
+#define MDInit MD2Init
+#define MDUpdate MD2Update
+#define MDFinal MD2Final
+
+#endif
+#if MD == 4
+#define MD_CTX MD4_CTX
+#define MDInit MD4Init
+#define MDUpdate MD4Update
+#define MDFinal MD4Final
+#endif
+#if MD == 5
+#define MD_CTX MD5_CTX
+#define MDInit MD5Init
+#define MDUpdate MD5Update
+#define MDFinal MD5Final
+#endif
+
+/* Main driver.
+
+   Arguments (may be any combination):
+     -sstring - digests string
+     -t       - runs time trial
+     -x       - runs test script
+     filename - digests file
+     (none)   - digests standard input
+ */
+int main (argc, argv)
+int argc;
+char *argv[];
+{
+  int i;
+
+  if (argc > 1)
+    for (i = 1; i < argc; i++)
+      if (argv[i][0] == '-' && argv[i][1] == 's')
+        MDString (argv[i] + 2);
+      else if (strcmp (argv[i], "-t") == 0)
+        MDTimeTrial ();
+      else if (strcmp (argv[i], "-x") == 0)
+        MDTestSuite ();
+      else
+        MDFile (argv[i]);
+  else
+    MDFilter ();
+
+  return (0);
+}
+
+/* Digests a string and prints the result.
+ */
+static void MDString (string)
+
+char *string;
+{
+  MD_CTX context;
+  unsigned char digest[16];
+  unsigned int len = strlen (string);
+
+  MDInit (&context);
+  MDUpdate (&context, string, len);
+  MDFinal (digest, &context);
+
+  printf ("MD%d (\"%s\") = ", MD, string);
+  MDPrint (digest);
+  printf ("\n");
+}
+
+/* Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte
+     blocks.
+ */
+static void MDTimeTrial ()
+{
+  MD_CTX context;
+  time_t endTime, startTime;
+  unsigned char block[TEST_BLOCK_LEN], digest[16];
+  unsigned int i;
+
+  printf
+    ("MD%d time trial. Digesting %d %d-byte blocks ...", MD,
+     TEST_BLOCK_LEN, TEST_BLOCK_COUNT);
+
+  /* Initialize block */
+  for (i = 0; i < TEST_BLOCK_LEN; i++)
+    block[i] = (unsigned char)(i & 0xff);
+
+  /* Start timer */
+  time (&startTime);
+
+  /* Digest blocks */
+  MDInit (&context);
+  for (i = 0; i < TEST_BLOCK_COUNT; i++)
+    MDUpdate (&context, block, TEST_BLOCK_LEN);
+  MDFinal (digest, &context);
+
+  /* Stop timer */
+  time (&endTime);
+
+  printf (" done\n");
+  printf ("Digest = ");
+  MDPrint (digest);
+
+  printf ("\nTime = %ld seconds\n", (long)(endTime-startTime));
+  printf
+    ("Speed = %ld bytes/second\n",
+     (long)TEST_BLOCK_LEN * (long)TEST_BLOCK_COUNT/(endTime-startTime));
+}
+
+/* Digests a reference suite of strings and prints the results.
+ */
+static void MDTestSuite ()
+{
+  printf ("MD%d test suite:\n", MD);
+
+  MDString ("");
+  MDString ("a");
+  MDString ("abc");
+  MDString ("message digest");
+  MDString ("abcdefghijklmnopqrstuvwxyz");
+  MDString
+    ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+  MDString
+
+    ("1234567890123456789012345678901234567890\
+1234567890123456789012345678901234567890");
+}
+
+/* Digests a file and prints the result.
+ */
+static void MDFile (filename)
+char *filename;
+{
+  FILE *file;
+  MD_CTX context;
+  int len;
+  unsigned char buffer[1024], digest[16];
+
+  if ((file = fopen (filename, "rb")) == NULL)
+    printf ("%s can't be opened\n", filename);
+
+  else {
+    MDInit (&context);
+    while (len = fread (buffer, 1, 1024, file))
+      MDUpdate (&context, buffer, len);
+    MDFinal (digest, &context);
+
+    fclose (file);
+
+    printf ("MD%d (%s) = ", MD, filename);
+    MDPrint (digest);
+
+    printf ("\n");
+  }
+}
+
+/* Digests the standard input and prints the result.
+ */
+static void MDFilter ()
+{
+  MD_CTX context;
+  int len;
+  unsigned char buffer[16], digest[16];
+
+  MDInit (&context);
+  while (len = fread (buffer, 1, 16, stdin))
+    MDUpdate (&context, buffer, len);
+  MDFinal (digest, &context);
+
+  MDPrint (digest);
+  printf ("\n");
+}
+
+/* Prints a message digest in hexadecimal.
+ */
+static void MDPrint (digest)
+unsigned char digest[16];
+
+{
+  unsigned int i;
+
+  for (i = 0; i < 16; i++)
+    printf ("%02x", digest[i]);
+}
diff -Nur --ignore-all-space unhash-0.4/md4crack.c unhash-0.4-withmd4/md4crack.c
--- unhash-0.4/md4crack.c	1969-12-31 17:00:00.000000000 -0700
+++ unhash-0.4-withmd4/md4crack.c	2007-02-05 15:28:40.000000000 -0700
@@ -0,0 +1,52 @@
+/*
+	Copyright (C) 2005 dxp
+
+	This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+#include "includes.h"
+
+#ifdef RFC
+	#include "md4/global.h"
+	#include "md4/md4.h"
+	#define MD4_Init MD4Init
+	#define MD4_Update MD4Update
+	#define MD4_Final MD4Final
+#else
+	#include <openssl/md4.h>
+#endif	
+
+void
+md4crack (const char *word)
+{
+	int			len = strlen (word);
+	int			err;
+	unsigned char		digest[16];
+	char			*hash;
+	MD4_CTX			md;
+
+	MD4_Init (&md);
+	MD4_Update (&md, word, len);
+	MD4_Final (digest, &md);
+/*	
+	hash = digest2hash (digest, 1);
+	err = strcmp (password_hash, hash);
+*/
+	err = digestcmp(digest);
+	if (!err) {
+		hash = digest2hash(digest, 1);
+		found (word, hash, password_hash, 3);
+	}
+}
+	
diff -Nur --ignore-all-space unhash-0.4/unhash.c unhash-0.4-withmd4/unhash.c
--- unhash-0.4/unhash.c	2006-12-22 22:01:51.000000000 -0700
+++ unhash-0.4-withmd4/unhash.c	2007-02-05 15:20:45.000000000 -0700
@@ -57,7 +57,7 @@
 			printf (VERSION);
 			printf ("Usage: unhash SIZE SET HASH\n");
 			printf ("Performs a brute-force attack on the given HASH.\n");
-			printf ("Only MD5 and SHA1 hashes!\n");
+			printf ("Only MD4, MD5 and SHA1 hashes!\n");
 			printf ("\n");
 			printf ("SIZE\tMinimum amount of letters to start with.\n");
 			printf ("\n");
@@ -73,7 +73,7 @@
 			printf ("    \t9. Alpha-Numeric, both uppercase and lowercase\n");
 			printf ("    \t10. All printable ascii characters\n");
 			printf ("\n");
-			printf ("HASH\tHashed string to attack.\n");
+			printf ("HASH\tHashed string to attack (prepend : to make MD5 hashes treated as MD4).\n");
 
 			exit (EXIT_SUCCESS);
 		}
@@ -84,19 +84,24 @@
 	sscanf (argv[1], "%i", &min);
 	sscanf (argv[2], "%i", &set);
 	snprintf (password_hash, sizeof (password_hash), "%s", argv[3]);
-	password_digest = hash2digest(password_hash);
 
 	switch ( strlen (password_hash) )
 		{
 			case 32:	calculation = md5crack;
 						printf ("Computing MD5\n");
 						break;
+		        case 33:	calculation = md4crack;
+			  snprintf (password_hash, sizeof (password_hash), "%s", (argv[3] + 1));
+			  printf ("Computing MD4\n");
+			  break;
 			case 40:	calculation = sha1crack;
 						printf ("Computing SHA1\n");
 						break;
 			default:	Error (1, 0, "Incorrect HASH");	break;
 		}
 	
+	password_digest = hash2digest(password_hash);
+
 	switch (set)
 		{
 			case 1:		strncpy (alphabet, set_1, strlen (set_1) );	break;

