pdfcrack-0.18/0000775000175000017500000000000013526357605013073 5ustar henninghenningpdfcrack-0.18/md5.c0000644000175000017500000002761412600567407013727 0ustar henninghenning/** * Copyright (C) 2006-2015 Henning Norén * Copyright (C) 1996-2005 Glyph & Cog, LLC. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include "md5.h" #define ROTATE_LEFT(x, r) ((x << r) | (x >> (32 - r))) #define RnA(a, b, s) a = ROTATE_LEFT(a, s); a += b /** MD5_ROUND1 optimized according to Colin Plumb's implementation */ #define MD5_ROUND1(a, b, c, d, Xk, s, Ti) \ a += (d ^ (b & (c ^ d))) + Xk + Ti; \ RnA(a,b,s) /** MD5_ROUND2 optimized like above */ #define MD5_ROUND2(a, b, c, d, Xk, s, Ti) \ a += (c ^ (d & (b ^ c))) + Xk + Ti; \ RnA(a,b,s) #define MD5_ROUND3(a, b, c, d, Xk, s, Ti) \ a += (b ^ c ^ d) + Xk + Ti; \ RnA(a,b,s) #define MD5_ROUND4(a, b, c, d, Xk, s, Ti) \ a += (c ^ (b | ~d)) + Xk + Ti; \ RnA(a,b,s) #define AA 0x67452301 #define BB 0xefcdab89 #define CC 0x98badcfe #define DD 0x10325476 static void (*md5_50_variant)(); static void md5_50f(uint8_t *msg, const unsigned int msgLen); static void md5_50s(uint8_t *msg, const unsigned int msgLen); void md5(const uint8_t *msg, const unsigned int msgLen, uint8_t *digest) { uint32_t x[16]; register uint32_t a, b, c, d; uint32_t aa, bb, cc, dd; int n64; int i, j; unsigned int k; /** compute number of 64-byte blocks (length + pad byte (0x80) + 8 bytes for length) */ n64 = (msgLen + 72) / 64; /** initialize a, b, c, d */ a = AA; b = BB; c = CC; d = DD; /** loop through blocks */ k = 0; for (i = 0; i < n64; ++i) { /** grab a 64-byte block */ for (j = 0; j < 16 && k < msgLen - 3; ++j, k += 4) x[j] = ((((((unsigned)msg[k+3] << 8) + (unsigned)msg[k+2]) << 8) + (unsigned)msg[k+1]) << 8) + msg[k]; if (i == n64 - 1) { if (k == msgLen - 3) x[j] = 0x80000000 + ((((unsigned)msg[k+2] << 8) + (unsigned)msg[k+1]) << 8) + msg[k]; else if (k == msgLen - 2) x[j] = 0x800000 + ((unsigned)msg[k+1] << 8) + msg[k]; else if (k == msgLen - 1) x[j] = 0x8000 + msg[k]; else x[j] = 0x80; ++j; while (j < 16) x[j++] = 0; x[14] = msgLen << 3; } /** save a, b, c, d */ aa = a; bb = b; cc = c; dd = d; /** round 1 */ MD5_ROUND1(a, b, c, d, x[0], 7, 0xd76aa478); MD5_ROUND1(d, a, b, c, x[1], 12, 0xe8c7b756); MD5_ROUND1(c, d, a, b, x[2], 17, 0x242070db); MD5_ROUND1(b, c, d, a, x[3], 22, 0xc1bdceee); MD5_ROUND1(a, b, c, d, x[4], 7, 0xf57c0faf); MD5_ROUND1(d, a, b, c, x[5], 12, 0x4787c62a); MD5_ROUND1(c, d, a, b, x[6], 17, 0xa8304613); MD5_ROUND1(b, c, d, a, x[7], 22, 0xfd469501); MD5_ROUND1(a, b, c, d, x[8], 7, 0x698098d8); MD5_ROUND1(d, a, b, c, x[9], 12, 0x8b44f7af); MD5_ROUND1(c, d, a, b, x[10], 17, 0xffff5bb1); MD5_ROUND1(b, c, d, a, x[11], 22, 0x895cd7be); MD5_ROUND1(a, b, c, d, x[12], 7, 0x6b901122); MD5_ROUND1(d, a, b, c, x[13], 12, 0xfd987193); MD5_ROUND1(c, d, a, b, x[14], 17, 0xa679438e); MD5_ROUND1(b, c, d, a, x[15], 22, 0x49b40821); /** round 2 */ MD5_ROUND2(a, b, c, d, x[1], 5, 0xf61e2562); MD5_ROUND2(d, a, b, c, x[6], 9, 0xc040b340); MD5_ROUND2(c, d, a, b, x[11], 14, 0x265e5a51); MD5_ROUND2(b, c, d, a, x[0], 20, 0xe9b6c7aa); MD5_ROUND2(a, b, c, d, x[5], 5, 0xd62f105d); MD5_ROUND2(d, a, b, c, x[10], 9, 0x02441453); MD5_ROUND2(c, d, a, b, x[15], 14, 0xd8a1e681); MD5_ROUND2(b, c, d, a, x[4], 20, 0xe7d3fbc8); MD5_ROUND2(a, b, c, d, x[9], 5, 0x21e1cde6); MD5_ROUND2(d, a, b, c, x[14], 9, 0xc33707d6); MD5_ROUND2(c, d, a, b, x[3], 14, 0xf4d50d87); MD5_ROUND2(b, c, d, a, x[8], 20, 0x455a14ed); MD5_ROUND2(a, b, c, d, x[13], 5, 0xa9e3e905); MD5_ROUND2(d, a, b, c, x[2], 9, 0xfcefa3f8); MD5_ROUND2(c, d, a, b, x[7], 14, 0x676f02d9); MD5_ROUND2(b, c, d, a, x[12], 20, 0x8d2a4c8a); /** round 3 */ MD5_ROUND3(a, b, c, d, x[5], 4, 0xfffa3942); MD5_ROUND3(d, a, b, c, x[8], 11, 0x8771f681); MD5_ROUND3(c, d, a, b, x[11], 16, 0x6d9d6122); MD5_ROUND3(b, c, d, a, x[14], 23, 0xfde5380c); MD5_ROUND3(a, b, c, d, x[1], 4, 0xa4beea44); MD5_ROUND3(d, a, b, c, x[4], 11, 0x4bdecfa9); MD5_ROUND3(c, d, a, b, x[7], 16, 0xf6bb4b60); MD5_ROUND3(b, c, d, a, x[10], 23, 0xbebfbc70); MD5_ROUND3(a, b, c, d, x[13], 4, 0x289b7ec6); MD5_ROUND3(d, a, b, c, x[0], 11, 0xeaa127fa); MD5_ROUND3(c, d, a, b, x[3], 16, 0xd4ef3085); MD5_ROUND3(b, c, d, a, x[6], 23, 0x04881d05); MD5_ROUND3(a, b, c, d, x[9], 4, 0xd9d4d039); MD5_ROUND3(d, a, b, c, x[12], 11, 0xe6db99e5); MD5_ROUND3(c, d, a, b, x[15], 16, 0x1fa27cf8); MD5_ROUND3(b, c, d, a, x[2], 23, 0xc4ac5665); /** round 4 */ MD5_ROUND4(a, b, c, d, x[0], 6, 0xf4292244); MD5_ROUND4(d, a, b, c, x[7], 10, 0x432aff97); MD5_ROUND4(c, d, a, b, x[14], 15, 0xab9423a7); MD5_ROUND4(b, c, d, a, x[5], 21, 0xfc93a039); MD5_ROUND4(a, b, c, d, x[12], 6, 0x655b59c3); MD5_ROUND4(d, a, b, c, x[3], 10, 0x8f0ccc92); MD5_ROUND4(c, d, a, b, x[10], 15, 0xffeff47d); MD5_ROUND4(b, c, d, a, x[1], 21, 0x85845dd1); MD5_ROUND4(a, b, c, d, x[8], 6, 0x6fa87e4f); MD5_ROUND4(d, a, b, c, x[15], 10, 0xfe2ce6e0); MD5_ROUND4(c, d, a, b, x[6], 15, 0xa3014314); MD5_ROUND4(b, c, d, a, x[13], 21, 0x4e0811a1); MD5_ROUND4(a, b, c, d, x[4], 6, 0xf7537e82); MD5_ROUND4(d, a, b, c, x[11], 10, 0xbd3af235); MD5_ROUND4(c, d, a, b, x[2], 15, 0x2ad7d2bb); MD5_ROUND4(b, c, d, a, x[9], 21, 0xeb86d391); /** increment a, b, c, d */ a += aa; b += bb; c += cc; d += dd; } /** break digest into bytes */ digest[ 0] = (uint8_t)(a & 0xff); digest[ 1] = (uint8_t)((a >>= 8) & 0xff); digest[ 2] = (uint8_t)((a >>= 8) & 0xff); digest[ 3] = (uint8_t)((a >>= 8) & 0xff); digest[ 4] = (uint8_t)(b & 0xff); digest[ 5] = (uint8_t)((b >>= 8) & 0xff); digest[ 6] = (uint8_t)((b >>= 8) & 0xff); digest[ 7] = (uint8_t)((b >>= 8) & 0xff); digest[ 8] = (uint8_t)(c & 0xff); digest[ 9] = (uint8_t)((c >>= 8) & 0xff); digest[10] = (uint8_t)((c >>= 8) & 0xff); digest[11] = (uint8_t)((c >>= 8) & 0xff); digest[12] = (uint8_t)(d & 0xff); digest[13] = (uint8_t)((d >>= 8) & 0xff); digest[14] = (uint8_t)((d >>= 8) & 0xff); digest[15] = (uint8_t)((d >>= 8) & 0xff); } static void md5_50s(uint8_t *msg, const unsigned int msgLen) { int i; for(i=0; i<50; i++) { md5(msg, msgLen, msg); } } /** fast version of "for(i=0; i<50; i++) { md5(msg, 16, msg); }" */ static void md5_50f(uint8_t *msg, const unsigned int msgLen __attribute__((unused))) { register uint32_t a, b, c, d; int i; a = ((((((unsigned)msg[ 3] << 8) + (unsigned)msg[ 2]) << 8) + (unsigned)msg[ 1]) << 8) + msg[ 0]; b = ((((((unsigned)msg[ 7] << 8) + (unsigned)msg[ 6]) << 8) + (unsigned)msg[ 5]) << 8) + msg[ 4]; c = ((((((unsigned)msg[11] << 8) + (unsigned)msg[10]) << 8) + (unsigned)msg[ 9]) << 8) + msg[ 8]; d = ((((((unsigned)msg[15] << 8) + (unsigned)msg[14]) << 8) + (unsigned)msg[13]) << 8) + msg[12]; for(i = 0; i < 50; i++) { const uint32_t aa=a, bb=b, cc=c, dd=d; /** round 1 */ /**MD5_ROUND1(a,BB,CC,DD, aa, 7, 0xd76aa478); MD5_ROUND1(d, a,BB,CC, bb,12, 0xe8c7b756); MD5_ROUND1(c, d, a,BB, cc,17, 0x242070db); MD5_ROUND1(b, c, d, a, dd,22, 0xc1bdceee);*/ a += 0xd76aa477; RnA(a,BB,7); d = 0xf8fa0bcc + b + (CC ^ (a & 0x77777777)); RnA(d,a,12); c += 0xbcdb4dd9 + (BB ^ (d & (a ^ BB))); RnA(c,d,17); b = 0xb18b7a77 + dd + ( a ^ (c & (d ^ a))); RnA(b,c,22); MD5_ROUND1(a, b, c, d, 0x80, 7, 0xf57c0faf); MD5_ROUND1(d, a, b, c, 0, 12, 0x4787c62a); MD5_ROUND1(c, d, a, b, 0, 17, 0xa8304613); MD5_ROUND1(b, c, d, a, 0, 22, 0xfd469501); MD5_ROUND1(a, b, c, d, 0, 7, 0x698098d8); MD5_ROUND1(d, a, b, c, 0, 12, 0x8b44f7af); MD5_ROUND1(c, d, a, b, 0, 17, 0xffff5bb1); MD5_ROUND1(b, c, d, a, 0, 22, 0x895cd7be); MD5_ROUND1(a, b, c, d, 0, 7, 0x6b901122); MD5_ROUND1(d, a, b, c, 0, 12, 0xfd987193); MD5_ROUND1(c, d, a, b, 0x80,17, 0xa679438e); MD5_ROUND1(b, c, d, a, 0, 22, 0x49b40821); /** round 2 */ MD5_ROUND2(a, b, c, d, bb, 5, 0xf61e2562); MD5_ROUND2(d, a, b, c, 0, 9, 0xc040b340); MD5_ROUND2(c, d, a, b, 0, 14, 0x265e5a51); MD5_ROUND2(b, c, d, a, aa, 20, 0xe9b6c7aa); MD5_ROUND2(a, b, c, d, 0, 5, 0xd62f105d); MD5_ROUND2(d, a, b, c, 0, 9, 0x02441453); MD5_ROUND2(c, d, a, b, 0, 14, 0xd8a1e681); MD5_ROUND2(b, c, d, a, 0x80,20, 0xe7d3fbc8); MD5_ROUND2(a, b, c, d, 0, 5, 0x21e1cde6); MD5_ROUND2(d, a, b, c, 0x80, 9, 0xc33707d6); MD5_ROUND2(c, d, a, b, dd, 14, 0xf4d50d87); MD5_ROUND2(b, c, d, a, 0, 20, 0x455a14ed); MD5_ROUND2(a, b, c, d, 0, 5, 0xa9e3e905); MD5_ROUND2(d, a, b, c, cc, 9, 0xfcefa3f8); MD5_ROUND2(c, d, a, b, 0, 14, 0x676f02d9); MD5_ROUND2(b, c, d, a, 0, 20, 0x8d2a4c8a); /** round 3 */ MD5_ROUND3(a, b, c, d, 0, 4, 0xfffa3942); MD5_ROUND3(d, a, b, c, 0, 11, 0x8771f681); MD5_ROUND3(c, d, a, b, 0, 16, 0x6d9d6122); MD5_ROUND3(b, c, d, a, 0x80,23, 0xfde5380c); MD5_ROUND3(a, b, c, d, bb, 4, 0xa4beea44); MD5_ROUND3(d, a, b, c, 0x80,11, 0x4bdecfa9); MD5_ROUND3(c, d, a, b, 0, 16, 0xf6bb4b60); MD5_ROUND3(b, c, d, a, 0, 23, 0xbebfbc70); MD5_ROUND3(a, b, c, d, 0, 4, 0x289b7ec6); MD5_ROUND3(d, a, b, c, aa, 11, 0xeaa127fa); MD5_ROUND3(c, d, a, b, dd, 16, 0xd4ef3085); MD5_ROUND3(b, c, d, a, 0, 23, 0x04881d05); MD5_ROUND3(a, b, c, d, 0, 4, 0xd9d4d039); MD5_ROUND3(d, a, b, c, 0, 11, 0xe6db99e5); MD5_ROUND3(c, d, a, b, 0, 16, 0x1fa27cf8); MD5_ROUND3(b, c, d, a, cc, 23, 0xc4ac5665); /** round 4 */ MD5_ROUND4(a, b, c, d, aa, 6, 0xf4292244); MD5_ROUND4(d, a, b, c, 0, 10, 0x432aff97); MD5_ROUND4(c, d, a, b, 0x80,15, 0xab9423a7); MD5_ROUND4(b, c, d, a, 0, 21, 0xfc93a039); MD5_ROUND4(a, b, c, d, 0, 6, 0x655b59c3); MD5_ROUND4(d, a, b, c, dd, 10, 0x8f0ccc92); MD5_ROUND4(c, d, a, b, 0, 15, 0xffeff47d); MD5_ROUND4(b, c, d, a, bb, 21, 0x85845dd1); MD5_ROUND4(a, b, c, d, 0, 6, 0x6fa87e4f); MD5_ROUND4(d, a, b, c, 0, 10, 0xfe2ce6e0); MD5_ROUND4(c, d, a, b, 0, 15, 0xa3014314); MD5_ROUND4(b, c, d, a, 0, 21, 0x4e0811a1); MD5_ROUND4(a, b, c, d, 0x80, 6, 0xf7537e82); MD5_ROUND4(d, a, b, c, 0, 10, 0xbd3af235); MD5_ROUND4(c, d, a, b, cc, 15, 0x2ad7d2bb); MD5_ROUND4(b, c, d, a, 0, 21, 0xeb86d391); a += AA; b += BB; c += CC; d += DD; } /** break digest into bytes */ msg[ 0] = (uint8_t)(a & 0xff); msg[ 1] = (uint8_t)((a >>= 8) & 0xff); msg[ 2] = (uint8_t)((a >>= 8) & 0xff); msg[ 3] = (uint8_t)((a >>= 8) & 0xff); msg[ 4] = (uint8_t)(b & 0xff); msg[ 5] = (uint8_t)((b >>= 8) & 0xff); msg[ 6] = (uint8_t)((b >>= 8) & 0xff); msg[ 7] = (uint8_t)((b >>= 8) & 0xff); msg[ 8] = (uint8_t)(c & 0xff); msg[ 9] = (uint8_t)((c >>= 8) & 0xff); msg[10] = (uint8_t)((c >>= 8) & 0xff); msg[11] = (uint8_t)((c >>= 8) & 0xff); msg[12] = (uint8_t)(d & 0xff); msg[13] = (uint8_t)((d >>= 8) & 0xff); msg[14] = (uint8_t)((d >>= 8) & 0xff); msg[15] = (uint8_t)((d >>= 8) & 0xff); } void md5_50_init(const unsigned int msgLen) { if(msgLen == 16) md5_50_variant = &md5_50f; else md5_50_variant = &md5_50s; } void md5_50(uint8_t *msg, const unsigned int msgLen) { md5_50_variant(msg, msgLen); } pdfcrack-0.18/pdfparser.h0000644000175000017500000000232610520400370015206 0ustar henninghenning/** * Copyright (C) 2006 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _PDFPARSER_H_ #define _PDFPARSER_H_ #include #include #include #include "common.h" #define EENCNF -1 /* Encryption Object Not Found */ #define ETRANF -2 /* Trailer Information Not Found */ #define ETRENF -3 /* Trailer: Encryption Object Not Found */ #define ETRINF -4 /* Trailer: FileID Object Not Found */ bool openPDF(FILE *file, EncData *e); int getEncryptedInfo(FILE *file, EncData *e); #endif /** _PDFPARSER_H_ */ pdfcrack-0.18/pdfcrack.10000664000175000017500000000437212614467646014744 0ustar henninghenning.\" .\" Man page for pdfcrack .\" .\" Copyright (c) 2009, Henning Noren .\" .\" You may distribute under the terms of the GNU General Public .\" License as specified in the README file that comes with the pdfcrack .\" distribution. .\" .\" Henning Noren .\" confusion42@users.sourceforge.net .\" .TH PDFCRACK "1" "February 2009" "User Commands" .SH NAME pdfcrack \- Password recovery tool for PDF-files .SH SYNOPSIS .B pdfcrack [ \fB\-f\fR ]\fR FILE [\fIOPTION\fR]... .SH DESCRIPTION .B PDFCrack is a tool for recovering passwords and content from PDF-files. .PP If aborted with Ctrl-C or by receiving a SIGINT signal, \fBpdfcrack\fR will automatically save current position. The position will be saved in a file called \fBsavedstate.sav\fR in current working directory. .PP Mandatory arguments for long options are mandatory for short options too. .SH OPTIONS .TP \fB\-b\fR, \fB\-\-bench\fR perform benchmark and exit .TP \fB\-c\fR, \fB\-\-charset=\fISTRING\fR Use the characters in \fISTRING\fR as charset .TP \fB\-w\fR, \fB\-\-wordlist=\fIFILE\fR Use \fIFILE\fR as source of passwords to try .TP \fB\-n\fR, \fB\-\-minpw=\fIINTEGER\fR Skip trying passwords shorter than \fIINTEGER .TP \fB\-m\fR, \fB\-\-maxpw=\fIINTEGER\fR Stop when reaching this \fIINTEGER\fR passwordlength .TP \fB\-l\fR, \fB\-\-loadstate=\fIFILE\fR Continue from the state saved in \fIFILE .TP \fB\-o\fR, \fB\-\-owner\fR Work with the ownerpassword .TP \fB\-u\fR, \fB\-\-user\fR Work with the userpassword (default) .TP \fB\-p\fR, \fB\-\-password=\fISTRING\fR Give userpassword to speed up breaking ownerpassword (implies \fB-o\fR) .TP \fB\-q\fR, \fB\-\-quiet\fR Run quietly .TP \fB\-s\fR, \fB\-\-permutate\fR Try permutating the passwords (currently only supports switching first character to uppercase) .TP \fB\-v\fR, \fB\-\-version\fR Print version and exit .SH REPORTING BUGS Via e-mail to Henning Noren or report on project page at http://pdfcrack.sourceforge.net/ .SH AUTHOR Henning Noren .SH COPYRIGHT Copyright \(co 2009 Henning Noren .br This is free software. You may redistribute copies of it under the terms of the GNU General Public License . There is NO WARRANTY, to the extent permitted by law. pdfcrack-0.18/sha256.h0000664000175000017500000000174012274676545014264 0ustar henninghenning/** * Copyright (C) 2014 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _SHA256_H_ #define _SHA256_H_ #include void sha256(const uint8_t *msg, const int msgLen, uint8_t *hash); void sha256f(const uint8_t *msg, const int msgLen, uint8_t *hash); #endif /** _SHA256_H_ */ pdfcrack-0.18/passwords.c0000644000175000017500000001440013104752264015251 0ustar henninghenning/** * Copyright (C) 2006-2017 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include #include #include #include "passwords.h" #define PASSLENGTH 33 static FILE *wordList = NULL; static const char *wordListName; static bool wlMore; static bool (*npw)() = NULL; static unsigned int (*spw)(uint8_t *) = NULL; static passwordMethod pwMethod; bool nextPassword() { return npw(); } unsigned int setPassword(uint8_t *outbuf) { return spw(outbuf); } static bool wlNextPassword() { return wlMore; } static unsigned int wlSetPassword(uint8_t *outbuf) { int ch; unsigned int passlength; passlength = 0; ch = getc(wordList); while(ch != '\n' && ch != '\r' && ch != EOF && passlength < 32) { outbuf[passlength++] = ch; ch = getc(wordList); } /** clean up garbage of passwords longer than 32 chars */ if(unlikely(passlength == 32)) while(ch != '\n' && ch != '\r' && ch != EOF) ch = getc(wordList); if(ch == '\r') { ch = getc(wordList); if(ch != '\n') ungetc(ch, wordList); } if(unlikely(ch == EOF)) wlMore = false; return passlength; } static void setWordList(FILE *file, const char *wl) { wordList = file; wordListName = wl; npw = &wlNextPassword; spw = &wlSetPassword; wlMore = true; } static const uint8_t stdchars[] = {"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"}; static const uint8_t *charset; static unsigned int charsetLen; static unsigned int maxPasswordLen; static int password[PASSLENGTH]; static unsigned int genSetPassword(uint8_t *outbuf) { unsigned int i; for(i=0;password[i] != -1;i++) outbuf[i] = charset[password[i]]; return i; } static bool genNextPassword() { unsigned int i = 0; /** this is pretty simple... Change the current position (i) to the value in the next position of the charset. If we have reached the end of the charset, move the current position to the next one and return true unless we have reached the last position we want to try. */ while(++password[i] == (int)charsetLen) password[i++] = 0; return (i != maxPasswordLen); } static bool recovery = false; static void setCharset(const char *cs, const unsigned int minPw, const unsigned int maxPw) { int i; unsigned int min; npw = &genNextPassword; spw = &genSetPassword; if(!recovery) { /** This should already be set if we are loading from a saved state */ if(cs) charset = (const uint8_t*)cs; else charset = stdchars; charsetLen = strlen((const char*)charset); /** Make sure that max- and min-password are smaller than 32 */ if(maxPw < PASSLENGTH) maxPasswordLen = maxPw; else maxPasswordLen = PASSLENGTH-1; if(minPw < PASSLENGTH) min = minPw; else min = PASSLENGTH-1; /** Initialize starting position */ for(i=0;i<(int)maxPasswordLen;i++) { if(i<((int)min)-1) password[i] = charsetLen-1; else password[i] = -1; } while(i < PASSLENGTH-1) password[i++] = -1; } /** Put terminator (-1) at the last position */ password[PASSLENGTH-1] = -1; } void initPasswords(const passwordMethod pm, FILE *file, const char *wl, const char *cs, const unsigned int minPw, const unsigned int maxPw) { if(!recovery) pwMethod = pm; switch(pwMethod) { case Generative: setCharset(cs, minPw, maxPw); break; case Wordlist: setWordList(file, wl); break; default: /** The programmer is a twit! */ break; } } /** Common patterns that is shared between pw_loadState and pw_saveState */ static const char string_PM[] = "\nPM: %d\n"; static const char string_MPCLC[] = "MaxPWL: %d\nCharset(%d): "; bool pw_loadState(FILE *file, char **wl) { int pm; unsigned int i, len; char * __restrict string; if(fscanf(file, string_PM, &pm) < 1) return false; if(pm == Generative) { if(fscanf(file, string_MPCLC, &maxPasswordLen, &charsetLen) < 2) return false; /** check for very long and negative charsets */ if(charsetLen > 256 || charsetLen < 1) return false; string = malloc(sizeof(uint8_t)*charsetLen+1); for(i=0;i 32767) return false; string = malloc(sizeof(char)*len+1); for(i=0;i void md5(const uint8_t *msg, const unsigned int msgLen, uint8_t *digest); /** init function for md5_50 which chooses a md5_50 optimised for msgLen, if one is available */ void md5_50_init(const unsigned int msgLen); /** md5_50 is basically for(i=0; i<50; i++) { md5(msg, msgLen, msg); } */ void md5_50(uint8_t *msg, const unsigned int msgLen); #endif /** _MD5_H_ */ pdfcrack-0.18/benchmark.h0000644000175000017500000000154510520400370015154 0ustar henninghenning/** * Copyright (C) 2006 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _PDFBENCH_H_ #define _PDFBENCH_H_ void runBenchmark(void); #endif /** _PDFBENCH_H_ */ pdfcrack-0.18/TODO0000644000175000017500000000227012273055311013545 0ustar henninghenningBy now the pdfcrack project is pretty much stalled and lacks support for newer pdf-versions and security schemes Replace pdfparser with a more robust and complete parsing of a PDF Optimize the crack and crypto-routines for greater performance Add support for third-party security handlers Add real permutation-support. Maybe John the Ripper could be a source of inspiration? Add support for masked passwords, when we already knows parts of the password Furthermore: Replace pdfparser with a complete representation of the structure of a PDF-file that can be used to find information and more importantly, can be written out again to a file. For this we need to understand/represent objects (indirect and direct), the file structure (lineraized and standard) and xreftables with trailers. We need to be able to update/fix the xreftables when writing it. We also need support for LZM-compression that is used for many streams. When the above item is done there is nothing stopping us from adding complete RC4-keyspace search and decrypt without having to bother cracking the passwords. An example of this search can be viewed at: http://www.upl.cs.wisc.edu/~hamblin/files/rc4_single_brute.c pdfcrack-0.18/rc4.h0000644000175000017500000000211610520400370013705 0ustar henninghenning/** * Copyright (C) 2006 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _RC4_H_ #define _RC4_H_ #include #include void rc4Decrypt(const uint8_t *key, const uint8_t *bs, const unsigned int len, uint8_t *out); bool rc4Match40b(const uint8_t *key, const uint8_t *bs, const uint8_t *match); bool setrc4DecryptMethod(const unsigned int length); #endif /** _RC4_H_ */ pdfcrack-0.18/pdfcrack.c0000644000175000017500000005071013526357517015017 0ustar henninghenning/** * Copyright (C) 2006-2019 Henning Norén * Copyright (C) 1996-2005 Glyph & Cog, LLC. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include #include #include #include #include #include "pdfcrack.h" #include "md5.h" #include "rc4.h" #include "sha256.h" #include "passwords.h" /** sets the number of bytes to decrypt for partial test in revision 3. Three should be a good number for this as this mean a match should only happen every 256^3=16777216 check and that should be unique enough to motivate a full retry on that entry. */ #define PARTIAL_TEST_SIZE 3 static const uint8_t pad[32] = { 0x28,0xBF,0x4E,0x5E,0x4E,0x75,0x8A,0x41, 0x64,0x00,0x4E,0x56,0xFF,0xFA,0x01,0x08, 0x2E,0x2E,0x00,0xB6,0xD0,0x68,0x3E,0x80, 0x2F,0x0C,0xA9,0xFE,0x64,0x53,0x69,0x7A }; /** buffers for stuff that we can precompute before the actual cracking */ static uint8_t *encKeyWorkSpace; static uint8_t password_user[40]; /** need to cover 32 char + salt */ static const uint8_t *rev3TestKey; static unsigned int ekwlen; /** points to the current password in clear-text */ static uint8_t *currPW; /** current length of the password we are working with */ static unsigned int currPWLen; /** statistics */ static unsigned int nrprocessed; static time_t startTime; /** pointer to the actual encoding-data from the pdf */ static const EncData *encdata; /** some configuration switches */ static bool crackDone; static bool knownPassword; static bool workWithUser; /** Print out some statistics */ bool printProgress(void) { time_t currentTime; char str[33]; if(crackDone) return true; currentTime = time(NULL); memcpy(str,currPW,currPWLen); str[currPWLen] = '\0'; printf("Average Speed: %.1f w/s. ", nrprocessed/difftime(currentTime,startTime)); printf("Current Word: '%s'\n",str); fflush(stdout); nrprocessed = 0; startTime = time(NULL); return false; } /** * Initialisation of the encryption key workspace to manage a bit faster * switching between keys */ static unsigned int initEncKeyWorkSpace(const int revision, const bool encMetaData, const int permissions, const uint8_t *ownerkey, const uint8_t *fileID, const unsigned int fileIDLen) { unsigned int size; /** * Algorithm 3.2 Computing an encryption key (PDF Reference, v 1.7, p.125) * * Make space for: * field | bytes * ----------------------- * padded password | 32 * O entry | 32 * P entry | 4 * fileID | * [extra padding] | [4] (Special for step 6) **/ size = (revision >= 3 && !encMetaData) ? 72 : 68; encKeyWorkSpace = malloc(size + fileIDLen); /** Just to be sure we have no uninitalized stuff in the workspace */ memcpy(encKeyWorkSpace, pad, 32); /** 3 */ memcpy(encKeyWorkSpace + 32, ownerkey, 32); /** 4 */ encKeyWorkSpace[64] = permissions & 0xff; encKeyWorkSpace[65] = (permissions >> 8) & 0xff; encKeyWorkSpace[66] = (permissions >> 16) & 0xff; encKeyWorkSpace[67] = (permissions >> 24) & 0xff; /** 5 */ memcpy(encKeyWorkSpace + 68, fileID, fileIDLen); /** 6 */ if(revision >= 3 && !encMetaData) { encKeyWorkSpace[68+fileIDLen] = 0xff; encKeyWorkSpace[69+fileIDLen] = 0xff; encKeyWorkSpace[70+fileIDLen] = 0xff; encKeyWorkSpace[71+fileIDLen] = 0xff; } return size+fileIDLen; } #if 0 /** For debug */ static void printHexString(const uint8_t *str, const unsigned int len) { unsigned int i; for(i=0;i= 0xe0 && b <= 0xf6)) return b-0x20; else return toupper(b); } /** Really stupid permutate that needs to be replaced with a better framwork for smart permutations of the current password */ static bool do_permutate(void) { static bool ret = false; uint8_t tmp; tmp = isolat1ToUpper(currPW[0]); if(tmp != currPW[0]) { currPW[0] = tmp; ret = !ret; } else ret = false; return ret; } /** Dummy-function to use when no permutations are wanted */ static bool no_permutate(void) { return false; } /** Placeholder for the correct permutation-function to run */ static bool (*permutate)() = NULL; /** Prints out the password found */ static void foundPassword(void) { char str[33]; int fin_search; size_t pad_start; memcpy(str,currPW,currPWLen); str[currPWLen] = '\0'; printf("found %s-password: '%s'\n", workWithUser?"user":"owner", str); /** * Print out the user-password too if we know the ownerpassword. * It is placed in password_user and we need to find where the pad * starts before we can print it out (without ugly artifacts) **/ if(!workWithUser && (encdata->revision < 5)) { pad_start=0; do { fin_search = memcmp(password_user+pad_start, pad, 32-pad_start); pad_start++; } while (pad_start < 32 && fin_search != 0); memcpy(str, password_user, pad_start); if(!fin_search) str[pad_start-1] = '\0'; printf("found user-password: '%s'\n", str); } } /** Common handling of the key for all rev3-functions */ #define RC4_DECRYPT_REV3(n) { \ for(i = 19; i >= 0; --i) { \ for(j = 0; j < length; ++j) \ tmpkey[j] = enckey[j] ^ i; \ rc4Decrypt(tmpkey, test, n, test); \ } \ } /** Checks if the rev2-password set up in encKeyWorkSpace is the correct one and return true if it is and false otherwise. */ static bool isUserPasswordRev2(void) { uint8_t enckey[16]; md5(encKeyWorkSpace, ekwlen, enckey); return rc4Match40b(enckey, encdata->u_string, pad); } /** Checks if the rev3-password set up in encKeyWorkSpace is the correct one and return true if it is and false otherwise. */ static bool isUserPasswordRev3(void) { uint8_t test[16], enckey[16], tmpkey[16]; unsigned int length, j; int i; length = encdata->length/8; md5(encKeyWorkSpace, ekwlen, enckey); md5_50(enckey, length); memcpy(test, encdata->u_string, PARTIAL_TEST_SIZE); /** Algorithm 3.5 reversed */ RC4_DECRYPT_REV3(PARTIAL_TEST_SIZE); /** if partial test succeeds we make a full check to be sure */ if(unlikely(memcmp(test, rev3TestKey, PARTIAL_TEST_SIZE) == 0)) { memcpy(test, encdata->u_string, length); RC4_DECRYPT_REV3(length); if(memcmp(test, rev3TestKey, length) == 0) return true; } return false; } /** Common beginning of the main-loop in all the cracking-functions */ #define BEGIN_CRACK_LOOP() { \ currPWLen = setPassword(currPW); \ if(unlikely(lpasslength != currPWLen)) { \ if(likely(currPWLen < 32)) \ memcpy(currPW + currPWLen, pad, 32-currPWLen); \ lpasslength = currPWLen; \ } \ } bool runCrackRev2_o(void) { uint8_t enckey[16]; unsigned int lpasslength; lpasslength = 0; startTime = time(NULL); do { BEGIN_CRACK_LOOP(); do { md5(currPW, 32, enckey); rc4Decrypt(enckey, encdata->o_string, 32, encKeyWorkSpace); md5(encKeyWorkSpace, ekwlen, enckey); if(rc4Match40b(enckey, encdata->u_string, pad)) { memcpy(password_user, encKeyWorkSpace, 32); return true; } ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev3_o(void) { uint8_t test[32], enckey[16], tmpkey[16]; unsigned int j, length, lpasslength; int i; length = encdata->length/8; lpasslength = 0; startTime = time(NULL); do { BEGIN_CRACK_LOOP(); do { md5(currPW, 32, enckey); md5_50(enckey, length); memcpy(test, encdata->o_string, 32); RC4_DECRYPT_REV3(32); memcpy(encKeyWorkSpace, test, 32); if(isUserPasswordRev3()) { memcpy(password_user, encKeyWorkSpace, 32); return true; } ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev2_of(void) { uint8_t enckey[16]; unsigned int lpasslength; lpasslength = 0; startTime = time(NULL); do { BEGIN_CRACK_LOOP(); do { md5(encKeyWorkSpace, 32, enckey); /* Algorithm 3.4 reversed */ if(rc4Match40b(enckey, encdata->o_string, password_user)) return true; ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev3_of(void) { uint8_t test[32], enckey[16], tmpkey[16]; unsigned int j, length, lpasslength; int i; length = encdata->length/8; lpasslength = 0; startTime = time(NULL); do { BEGIN_CRACK_LOOP(); do { md5(encKeyWorkSpace, 32, enckey); md5_50(enckey, length); memcpy(test, encdata->o_string, PARTIAL_TEST_SIZE); RC4_DECRYPT_REV3(PARTIAL_TEST_SIZE); /** if partial test succeeds we make a full check to be sure */ if(unlikely(memcmp(test, password_user, PARTIAL_TEST_SIZE) == 0)) { memcpy(test, encdata->o_string, 32); RC4_DECRYPT_REV3(32); if(memcmp(test, password_user, 32) == 0) return true; } ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev3(void) { uint8_t test[16], enckey[16], tmpkey[16]; unsigned int j, length, lpasslength; int i; length = encdata->length/8; lpasslength = 0; startTime = time(NULL); do { BEGIN_CRACK_LOOP(); do { md5(encKeyWorkSpace, ekwlen, enckey); md5_50(enckey, length); memcpy(test, encdata->u_string, PARTIAL_TEST_SIZE); /** Algorithm 3.5 reversed */ RC4_DECRYPT_REV3(PARTIAL_TEST_SIZE); /** if partial test succeeds we make a full check to be sure */ if(unlikely(memcmp(test, rev3TestKey, PARTIAL_TEST_SIZE) == 0)) { memcpy(test, encdata->u_string, length); RC4_DECRYPT_REV3(length); if(memcmp(test, rev3TestKey, length) == 0) return true; } ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev2(void) { uint8_t enckey[16]; unsigned int lpasslength; lpasslength = 0; startTime = time(NULL); do { BEGIN_CRACK_LOOP(); do { md5(encKeyWorkSpace, ekwlen, enckey); /* Algorithm 3.4 reversed */ if(rc4Match40b(enckey, encdata->u_string, pad)) return true; ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev5(void) { uint8_t enckey[32]; unsigned int lpasslength; lpasslength = 0; startTime = time(NULL); do { currPWLen = setPassword(currPW); if(unlikely(lpasslength != currPWLen)) { /** Add the 8 byte user validation salt to pad */ if(likely(currPWLen < 32)) memcpy(currPW + currPWLen, encdata->u_string+32, 8); lpasslength = currPWLen; } do { sha256f(currPW, currPWLen+8, enckey); if(memcmp(enckey, encdata->u_string, 32) == 0) return true; ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } bool runCrackRev5_o(void) { uint8_t enckey[32]; unsigned int lpasslength; lpasslength = 0; startTime = time(NULL); do { currPWLen = setPassword(currPW); if(unlikely(lpasslength != currPWLen)) { /** Add the 8 byte user validation and 48 byte u-key salt to pad */ if(likely(currPWLen < 32)) { memcpy(currPW + currPWLen, encdata->o_string+32, 8); memcpy(currPW + currPWLen+8, encdata->u_string, 48); } lpasslength = currPWLen; } do { sha256(currPW, currPWLen+56, enckey); if(memcmp(enckey, encdata->o_string, 32) == 0) return true; ++nrprocessed; } while(permutate()); } while(nextPassword()); return false; } /** Start cracking and does not stop until it has either been interrupted by a signal or the password either is found or wordlist or charset is exhausted */ void runCrack(void) { bool found = false; uint8_t cpw[88]; if(encdata->revision == 5) { if(workWithUser) { memcpy(currPW, encdata->u_string+32, 8); found = runCrackRev5(); } else { memcpy(currPW, encdata->o_string+32, 8); memcpy(currPW + 8, encdata->u_string, 48); found = runCrackRev5_o(); } } else if(!workWithUser && !knownPassword) { memcpy(cpw, pad, 32); currPW = cpw; if(encdata->revision == 2) found = runCrackRev2_o(); else found = runCrackRev3_o(); } else if(encdata->revision == 2) { if(workWithUser) found = runCrackRev2(); else /** knownPassword */ found = runCrackRev2_of(); } else { if(workWithUser) found = runCrackRev3(); else /** knownPassword */ found = runCrackRev3_of(); } crackDone = true; if(!found) printf("Could not find password\n"); else foundPassword(); currPW = NULL; } /** returns the number of processed passwords */ unsigned int getNrProcessed(void) { return nrprocessed; } /** These are shared variables between loading and initialisation and controls how to do the initialisation. Should not be touched by anything except loadState and cleanPDFCrack. */ static bool recovery = false; static bool permutation = false; /** cleans up everything as is needed to do a any initPDFCrack-calls after the first one. */ void cleanPDFCrack(void) { if(rev3TestKey) { /** Do a really ugly const to non-const cast but this one time it should be safe */ free((uint8_t*)rev3TestKey); rev3TestKey = NULL; } if(encKeyWorkSpace) { free(encKeyWorkSpace); encKeyWorkSpace = NULL; } knownPassword = false; recovery = false; permutation = false; } /** initPDFCrack is doing all the initialisations before you are able to call runCrack(). Make sure that you run cleanPDFCrack before you call this after the first time. */ bool initPDFCrack(const EncData *e, const uint8_t *upw, const bool user, const char *wl, const passwordMethod pm, FILE *file, const char *cs, const unsigned int minPw, const unsigned int maxPw, const bool perm) { uint8_t *buf; unsigned int upwlen; uint8_t *tmp; ekwlen = initEncKeyWorkSpace(e->revision, e->encryptMetaData, e->permissions, e->o_string, e->fileID, e->fileIDLen); encdata = e; currPW = encKeyWorkSpace; currPWLen = 0; nrprocessed = 0; workWithUser = user; crackDone = false; if(e->revision == 5) { permutation = (perm || permutation); if(permutation) permutate = do_permutate; else permutate = no_permutate; initPasswords(pm, file, wl, cs, minPw, maxPw); return true; } md5_50_init(encdata->length/8); if(!setrc4DecryptMethod((const unsigned int)e->length)) exit(234); if(upw) { upwlen = strlen((const char*)upw); if(upwlen > 32) upwlen = 32; memcpy(password_user, upw, upwlen); memcpy(password_user+upwlen, pad, 32-upwlen); memcpy(encKeyWorkSpace, password_user, 32); knownPassword = true; } /** Workaround to set password_user when loading state from file */ if(recovery) memcpy(encKeyWorkSpace, password_user, 32); if(encdata->revision == 2) { if(knownPassword) { if(!isUserPasswordRev2()) return false; memcpy(encKeyWorkSpace, pad, 32); } else { memcpy(password_user, pad, 32); knownPassword = isUserPasswordRev2(); } } else if(e->revision >= 3) { buf = malloc(32+sizeof(uint8_t)*e->fileIDLen); memcpy(buf, pad, 32); memcpy(buf + 32, e->fileID, e->fileIDLen); tmp = malloc(sizeof(uint8_t)*16); md5(buf, 32+e->fileIDLen, tmp); free(buf); rev3TestKey = tmp; if(knownPassword) { if(!isUserPasswordRev3()) return false; memcpy(encKeyWorkSpace, pad, 32); } else { memcpy(password_user, pad, 32); knownPassword = isUserPasswordRev3(); } } permutation = (perm || permutation); if(permutation) permutate = do_permutate; else permutate = no_permutate; initPasswords(pm, file, wl, cs, minPw, maxPw); return true; } /** Some common patterns between the loadState and saveState */ static const char string_PRVPL[] = "PDF: %d.%d\nR: %d\nV: %d\nP: %d\nL: %d\n" "MetaData: %d\nFileID(%d):"; static const char string_FILTER[] = "\nFilter(%zu): "; static const char string_UUPWP[] = "\nUser: %d\nUserPw: %d\nPermutate: %d\n"; /** Reads from file and tries to set up the state that it contains. Returns true on success and false otherwise. Very little checks for data validitiy is made. */ bool loadState(FILE *file, EncData *e, char **wl, bool *user) { unsigned int i; int tmp, tmp2, tmp3; size_t len; /** Load all the simple values bound to the document */ if(fscanf(file,string_PRVPL, &e->version_major, &e->version_minor, &e->revision, &e->version, &e->permissions, &e->length, &tmp, &e->fileIDLen) < 8) return false; /** Unsupported revision might be indication of corrupt file */ if(e->revision > 5 || e->revision < 2) return false; /** bork out if length is insanely high */ if(e->fileIDLen > 256) return false; e->encryptMetaData = (tmp == true); /** Load the FileID */ e->fileID = malloc(sizeof(uint8_t)*e->fileIDLen); for(i=0;ifileIDLen;i++) { if(fscanf(file, " %d", &tmp) < 1) return false; e->fileID[i] = tmp; } /** Load the Security Handler */ if(fscanf(file,string_FILTER, &len) < 1) return false; /** bork out if length is obviously wrong */ if(len > 256 || len <= 0) return false; e->s_handler = malloc((sizeof(uint8_t)*len)+1); for(i=0;i<(unsigned int)len;i++) { e->s_handler[i] = getc(file); } /** Make sure we null-terminate the string */ e->s_handler[i] = '\0'; /** Currently we only handle Standard, so probably corrupt otherwise */ if(strcmp(e->s_handler,"Standard") != 0) return false; /** Load the U- and O-strings */ if(fscanf(file, "\nO:") == EOF) return false; if(e->revision == 5) len = 48; else len = 32; e->o_string = malloc(sizeof(uint8_t)*len); e->u_string = malloc(sizeof(uint8_t)*len); for(i=0;io_string[i] = tmp; } if(fscanf(file, "\nU:") == EOF) return false; for(i=0;iu_string[i] = tmp; } /** Load the simple values bound to the state */ if(fscanf(file, string_UUPWP, &tmp, &tmp2, &tmp3) < 3) return false; *user = (tmp == true); knownPassword = (tmp2 == true); permutation = (tmp3 == true); /** Load the saved userpassword if that is used */ if(knownPassword) { for(i=0;i<32;i++) { if(fscanf(file, " %d", &tmp) < 1) return false; password_user[i] = tmp; } } /** Load the password-specific stuff for the state */ if(!pw_loadState(file, wl)) return false; /** Remember to set the recovery-boolean to make sure the right things happen in initPDFCrack */ recovery = true; return true; } /** Saves the current state in the engine to file */ void saveState(FILE *file) { unsigned int i; size_t len; fprintf(file, string_PRVPL, encdata->version_major, encdata->version_minor, encdata->revision, encdata->version, encdata->permissions, encdata->length, (int)encdata->encryptMetaData, encdata->fileIDLen); for(i=0;ifileIDLen;i++) fprintf(file, " %d", encdata->fileID[i]); fprintf(file, string_FILTER, strlen(encdata->s_handler)); fprintf(file, "%s", encdata->s_handler); fprintf(file, "\nO:"); if(encdata->revision == 5) len = 48; else len = 32; for(i=0;io_string[i]); fprintf(file,"\nU:"); for(i=0;iu_string[i]); fprintf(file, string_UUPWP, (int)workWithUser, (int)knownPassword, (int)permutation); if(knownPassword) { for(i=0;i<32;i++) fprintf(file, " %d", password_user[i]); } /** Save the password-specific stuff */ pw_saveState(file); } pdfcrack-0.18/benchmark.c0000644000175000017500000002145413526357477015204 0ustar henninghenning/** * Copyright (C) 2006-2019 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include #include #include #include #include #include "benchmark.h" #include "common.h" #include "md5.h" #include "rc4.h" #include "sha256.h" #include "pdfcrack.h" #define COMMON_MD5_SIZE 88 #define COMMON_SHA256_SIZE 40 #define COMMON_SHA256_SLOW_SIZE 56 #define BENCHINTERVAL 3 /** The interval to run the specific benchmarks */ static volatile bool finished = false; /** interruptBench is used to stop the current benchmark */ static void interruptBench() { finished = true; } /** print_and_clean was supposed to make the binary somewhat smaller but I think I failed and have not bothered to investigate it further (like checking if the function is inlined or if one can force it to not be inline). As this stuff is pretty boring and not critical to performance I would prefer that all the boring stuff between benchmarks would be as small as possible. */ static void print_and_clean(const char *str, unsigned int nrprocessed, const clock_t *start, const clock_t *end) { printf("%s\t%.1f\n", str, nrprocessed/(((double)(*end-*start))/CLOCKS_PER_SEC)); cleanPDFCrack(); finished = false; } static void sha256_bench(void) { uint8_t *buf; uint8_t hash[32]; unsigned int nrprocessed = 0; clock_t startTime, endTime; buf = calloc(COMMON_SHA256_SLOW_SIZE, sizeof(uint8_t)); alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { sha256f(buf, COMMON_SHA256_SIZE, hash); buf[0]++; nrprocessed++; } endTime = clock(); print_and_clean("SHA256 (fast):\t", nrprocessed, &startTime, &endTime); buf[0] = 0; nrprocessed = 0; alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { sha256(buf, COMMON_SHA256_SLOW_SIZE, hash); buf[0]++; nrprocessed++; } endTime = clock(); print_and_clean("SHA256 (slow):\t", nrprocessed, &startTime, &endTime); free(buf); } static void md5_bench(void) { uint8_t *buf; uint8_t digest[16]; unsigned int nrprocessed = 0; clock_t startTime, endTime; buf = calloc(COMMON_MD5_SIZE, sizeof(uint8_t)); alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { md5(buf, COMMON_MD5_SIZE, digest); buf[0]++; nrprocessed++; } endTime = clock(); print_and_clean("MD5:\t\t", nrprocessed, &startTime, &endTime); free(buf); } static void md5_50_bench(void) { uint8_t *buf; unsigned int nrprocessed = 0; clock_t startTime, endTime; buf = calloc(16, sizeof(uint8_t)); md5_50_init(16); alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { md5_50(buf, 16); buf[0]++; nrprocessed++; } endTime = clock(); print_and_clean("MD5_50 (fast):\t", nrprocessed, &startTime, &endTime); buf[0] = 0; md5_50_init(15); // lie about size to get slow version nrprocessed = 0; alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { md5_50(buf, 16); buf[0]++; nrprocessed++; } endTime = clock(); print_and_clean("MD5_50 (slow):\t", nrprocessed, &startTime, &endTime); free(buf); } static void rc4_bench(void) { uint8_t *enckey; uint8_t match[32] = {0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD}; uint8_t cipher[32] = {0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD, 0xBE, 0xAD, 0xDE, 0xAD}; unsigned int nrprocessed = 0; clock_t startTime, endTime; enckey = calloc(16, sizeof(uint8_t)); alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { rc4Match40b(enckey, cipher, match); enckey[0]++; nrprocessed++; } endTime = clock(); print_and_clean("RC4 (40, static):", nrprocessed, &startTime, &endTime); setrc4DecryptMethod(40); nrprocessed = 0; alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { rc4Decrypt(enckey, cipher, 3, match); enckey[0]++; nrprocessed++; } endTime = clock(); print_and_clean("RC4 (40, no check):", nrprocessed, &startTime, &endTime); setrc4DecryptMethod(128); nrprocessed = 0; alarm(BENCHINTERVAL); startTime = clock(); while(!finished) { rc4Decrypt(enckey, cipher, 3, match); enckey[0]++; nrprocessed++; } endTime = clock(); print_and_clean("RC4 (128, no check):", nrprocessed, &startTime, &endTime); free(enckey); } static const uint8_t password[] = "Forty-Two is the Ultimate Answer"; static char handler[] = "Standard"; static const char charset[] = "abcdefghij"; static void pdf_128b_bench(void) { clock_t startTime, endTime; uint8_t o_string[32] = { 0xcf, 0xeb, 0x57, 0x1b, 0xa4, 0x56, 0x35, 0x19, 0x4e, 0x09, 0x95, 0x24, 0x23, 0xf3, 0x9b, 0x81, 0x05, 0xae, 0xbc, 0xb2, 0x8c, 0x18, 0xd2, 0xbb, 0xff, 0x00, 0xc9, 0xaa, 0x3f, 0x36, 0xe3, 0x13 }; uint8_t u_string[32] = { 0x72, 0xf6, 0x56, 0x9e, 0xda, 0x7d, 0x20, 0x1a, 0x10, 0x6d, 0x8a, 0x5b, 0xfa, 0xb2, 0xe9, 0xc0, 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08 }; uint8_t fileid[16] = { 0xc9, 0xaa, 0x55, 0xc3, 0x6f, 0x3f, 0x5e, 0x84, 0x0d, 0x3d, 0x96, 0x8b, 0x97, 0xdb, 0xb2, 0xfe }; EncData e = { handler, o_string, u_string, fileid, true, 16, 1, 4, 128, -2359344, 3, 2 }; initPDFCrack(&e, NULL, true, NULL, Generative, NULL, charset, 0, 4, true); startTime = clock(); runCrackRev3(); endTime = clock(); print_and_clean("PDF (128, user):", getNrProcessed(), &startTime, &endTime); initPDFCrack(&e, NULL, false, NULL, Generative, NULL, charset, 0, 4, true); startTime = clock(); runCrackRev3_o(); endTime = clock(); print_and_clean("PDF (128, owner):", getNrProcessed(), &startTime, &endTime); initPDFCrack(&e,password, false, NULL, Generative, NULL, charset, 0, 4,true); startTime = clock(); runCrackRev3_of(); endTime = clock(); print_and_clean("PDF (128, owner, fast):", getNrProcessed(), &startTime, &endTime); } static void pdf_40b_bench(void) { clock_t startTime, endTime; uint8_t o_string[32] = { 0xb7, 0x81, 0xc8, 0x3d, 0x93, 0x79, 0x21, 0xcc, 0x0f, 0x3d, 0x40, 0xed, 0x18, 0xe7, 0x7f, 0x7e, 0xc0, 0x15, 0xb1, 0x63, 0xf5, 0xc8, 0x34, 0xe0, 0x54, 0x37, 0x41, 0x29, 0xe7, 0xc5, 0x1d, 0xe3 }; uint8_t u_string[32] = { 0x61, 0x74, 0x7c, 0x5c, 0xb5, 0x38, 0x3d, 0xdd, 0x6f, 0xcb, 0xb2, 0xf2, 0xfe, 0xe3, 0x34, 0x8d, 0x81, 0xe2, 0x49, 0x99, 0xc4, 0x14, 0xf6, 0x6f, 0xd0, 0x0f, 0x97, 0xe8, 0xb8, 0x29, 0xe6, 0x27 }; uint8_t fileid[16] = { 0x21, 0x76, 0x36, 0x66, 0x67, 0xf0, 0x86, 0xd5, 0x09, 0x88, 0xc3, 0xa7, 0xe9, 0x3a, 0x92, 0xca }; EncData e = { handler, o_string, u_string, fileid, true, 16, 1, 4, 40, -64, 2, 1 }; initPDFCrack(&e, NULL, true, NULL, Generative, NULL, charset, 0, 5, true); startTime = clock(); runCrackRev2(); endTime = clock(); print_and_clean("PDF (40, user):\t",getNrProcessed(),&startTime, &endTime); initPDFCrack(&e, NULL, false, NULL, Generative, NULL, charset, 0, 5, true); startTime = clock(); runCrackRev2_o(); endTime = clock(); print_and_clean("PDF (40, owner):",getNrProcessed(), &startTime, &endTime); initPDFCrack(&e, password, false, NULL, Generative, NULL, charset,0, 5,true); startTime = clock(); runCrackRev2_of(); endTime = clock(); print_and_clean("PDF (40, owner, fast):", getNrProcessed(), &startTime, &endTime); } void runBenchmark(void) { struct sigaction act; act.sa_handler = interruptBench; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGALRM, &act, 0); printf("Benchmark:\tAverage Speed (calls / second):\n"); sha256_bench(); printf("\n"); md5_bench(); md5_50_bench(); printf("\n"); rc4_bench(); printf("\n"); printf("Benchmark:\tAverage Speed (passwords / second):\n"); pdf_40b_bench(); printf("\n"); pdf_128b_bench(); return; } pdfcrack-0.18/sha256.c0000664000175000017500000001703712600567521014247 0ustar henninghenning/** * Copyright (C) 2014-2015 Henning Norén * Copyright (C) 1996-2011 Glyph & Cog, LLC * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include "sha256.h" #include "string.h" /** Rotate right **/ #define ROTR(x, n) (( x >> n ) | ( x << (32 - n))) #define Choice(x, y, z) ( z ^ ( x & ( y ^ z ))) #define Majority(x, y, z) (( x & y ) ^ ( z & ( x ^ y ))) #define Sigma0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) #define Sigma1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) #define sigma0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ (x >> 3)) #define sigma1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ (x >> 10)) #define ROUND(a,b,c,d,e,f,g,h,k,data) \ h += Sigma1(e) + Choice(e, f, g) + k + data; \ d += h; \ h += Sigma0(a) + Majority(a ,b ,c); static void sha256HashBlock(const uint8_t *blk, uint32_t *hash) { uint32_t W[64]; uint32_t A, B, C, D, E, F, G, H; int i; /* 1. Prepare the message schedule */ for (i = 0; i < 16; ++i) { W[i] = ((unsigned)blk[i*4 ] << 24) | ((unsigned)blk[i*4 + 1] << 16) | ((unsigned)blk[i*4 + 2] << 8) | blk[i*4 + 3]; } for (; i < 64; ++i) { W[i] = sigma1(W[i-2]) + W[i-7] + sigma0(W[i-15]) + W[i-16]; } /* 2. Initialize the eight working variables */ A = hash[0]; B = hash[1]; C = hash[2]; D = hash[3]; E = hash[4]; F = hash[5]; G = hash[6]; H = hash[7]; /* 3. Compression loop unrolled */ ROUND(A, B, C, D, E, F, G, H, 0x428a2f98, W[ 0]); ROUND(H, A, B, C, D, E, F, G, 0x71374491, W[ 1]); ROUND(G, H, A, B, C, D, E, F, 0xB5C0FBCF, W[ 2]); ROUND(F, G, H, A, B, C, D, E, 0xE9B5DBA5, W[ 3]); ROUND(E, F, G, H, A, B, C, D, 0x3956C25B, W[ 4]); ROUND(D, E, F, G, H, A, B, C, 0x59F111F1, W[ 5]); ROUND(C, D, E, F, G, H, A, B, 0x923F82A4, W[ 6]); ROUND(B, C, D, E, F, G, H, A, 0xAB1C5ED5, W[ 7]); ROUND(A, B, C, D, E, F, G, H, 0xD807AA98, W[ 8]); ROUND(H, A, B, C, D, E, F, G, 0x12835B01, W[ 9]); ROUND(G, H, A, B, C, D, E, F, 0x243185BE, W[10]); ROUND(F, G, H, A, B, C, D, E, 0x550C7DC3, W[11]); ROUND(E, F, G, H, A, B, C, D, 0x72BE5D74, W[12]); ROUND(D, E, F, G, H, A, B, C, 0x80DEB1FE, W[13]); ROUND(C, D, E, F, G, H, A, B, 0x9BDC06A7, W[14]); ROUND(B, C, D, E, F, G, H, A, 0xC19BF174, W[15]); ROUND(A, B, C, D, E, F, G, H, 0xE49B69C1, W[16]); ROUND(H, A, B, C, D, E, F, G, 0xEFBE4786, W[17]); ROUND(G, H, A, B, C, D, E, F, 0x0FC19DC6, W[18]); ROUND(F, G, H, A, B, C, D, E, 0x240CA1CC, W[19]); ROUND(E, F, G, H, A, B, C, D, 0x2DE92C6F, W[20]); ROUND(D, E, F, G, H, A, B, C, 0x4A7484AA, W[21]); ROUND(C, D, E, F, G, H, A, B, 0x5CB0A9DC, W[22]); ROUND(B, C, D, E, F, G, H, A, 0x76F988DA, W[23]); ROUND(A, B, C, D, E, F, G, H, 0x983E5152, W[24]); ROUND(H, A, B, C, D, E, F, G, 0xA831C66D, W[25]); ROUND(G, H, A, B, C, D, E, F, 0xB00327C8, W[26]); ROUND(F, G, H, A, B, C, D, E, 0xBF597FC7, W[27]); ROUND(E, F, G, H, A, B, C, D, 0xC6E00BF3, W[28]); ROUND(D, E, F, G, H, A, B, C, 0xD5A79147, W[29]); ROUND(C, D, E, F, G, H, A, B, 0x06CA6351, W[30]); ROUND(B, C, D, E, F, G, H, A, 0x14292967, W[31]); ROUND(A, B, C, D, E, F, G, H, 0x27B70A85, W[32]); ROUND(H, A, B, C, D, E, F, G, 0x2E1B2138, W[33]); ROUND(G, H, A, B, C, D, E, F, 0x4D2C6DFC, W[34]); ROUND(F, G, H, A, B, C, D, E, 0x53380D13, W[35]); ROUND(E, F, G, H, A, B, C, D, 0x650A7354, W[36]); ROUND(D, E, F, G, H, A, B, C, 0x766A0ABB, W[37]); ROUND(C, D, E, F, G, H, A, B, 0x81C2C92E, W[38]); ROUND(B, C, D, E, F, G, H, A, 0x92722C85, W[39]); ROUND(A, B, C, D, E, F, G, H, 0xA2BFE8A1, W[40]); ROUND(H, A, B, C, D, E, F, G, 0xA81A664B, W[41]); ROUND(G, H, A, B, C, D, E, F, 0xC24B8B70, W[42]); ROUND(F, G, H, A, B, C, D, E, 0xC76C51A3, W[43]); ROUND(E, F, G, H, A, B, C, D, 0xD192E819, W[44]); ROUND(D, E, F, G, H, A, B, C, 0xD6990624, W[45]); ROUND(C, D, E, F, G, H, A, B, 0xF40E3585, W[46]); ROUND(B, C, D, E, F, G, H, A, 0x106AA070, W[47]); ROUND(A, B, C, D, E, F, G, H, 0x19A4C116, W[48]); ROUND(H, A, B, C, D, E, F, G, 0x1E376C08, W[49]); ROUND(G, H, A, B, C, D, E, F, 0x2748774C, W[50]); ROUND(F, G, H, A, B, C, D, E, 0x34B0BCB5, W[51]); ROUND(E, F, G, H, A, B, C, D, 0x391C0CB3, W[52]); ROUND(D, E, F, G, H, A, B, C, 0x4ED8AA4A, W[53]); ROUND(C, D, E, F, G, H, A, B, 0x5B9CCA4F, W[54]); ROUND(B, C, D, E, F, G, H, A, 0x682E6FF3, W[55]); ROUND(A, B, C, D, E, F, G, H, 0x748F82EE, W[56]); ROUND(H, A, B, C, D, E, F, G, 0x78A5636F, W[57]); ROUND(G, H, A, B, C, D, E, F, 0x84C87814, W[58]); ROUND(F, G, H, A, B, C, D, E, 0x8CC70208, W[59]); ROUND(E, F, G, H, A, B, C, D, 0x90BEFFFA, W[60]); ROUND(D, E, F, G, H, A, B, C, 0xA4506CEB, W[61]); ROUND(C, D, E, F, G, H, A, B, 0xBEF9A3F7, W[62]); ROUND(B, C, D, E, F, G, H, A, 0xC67178F2, W[63]); /* 4. Compute the intermediate hash value */ hash[0] += A; hash[1] += B; hash[2] += C; hash[3] += D; hash[4] += E; hash[5] += F; hash[6] += G; hash[7] += H; } void sha256(const uint8_t *msg, const int msgLen, uint8_t *hash) { uint8_t blk[64]; uint32_t H[8]; int blkLen, i; H[0] = 0x6a09e667; H[1] = 0xbb67ae85; H[2] = 0x3c6ef372; H[3] = 0xa54ff53a; H[4] = 0x510e527f; H[5] = 0x9b05688c; H[6] = 0x1f83d9ab; H[7] = 0x5be0cd19; for (i = 0; i + 64 <= msgLen; i += 64) { sha256HashBlock(msg+i, H); } blkLen = msgLen - i; memcpy(blk, msg + i, blkLen); /* pad the message */ blk[blkLen++] = 0x80; if (blkLen > 56) { while (blkLen < 64) { blk[blkLen++] = 0; } sha256HashBlock(blk, H); blkLen = 0; } while (blkLen < 56) { blk[blkLen++] = 0; } blk[56] = 0; blk[57] = 0; blk[58] = 0; blk[59] = 0; blk[60] = (uint8_t)(msgLen >> 21); blk[61] = (uint8_t)(msgLen >> 13); blk[62] = (uint8_t)(msgLen >> 5); blk[63] = (uint8_t)(msgLen << 3); sha256HashBlock(blk, H); /* copy the output into the buffer (convert words to bytes) */ for (i = 0; i < 8; ++i) { hash[i*4] = (uint8_t)(H[i] >> 24); hash[i*4 + 1] = (uint8_t)(H[i] >> 16); hash[i*4 + 2] = (uint8_t)(H[i] >> 8); hash[i*4 + 3] = (uint8_t)H[i]; } } /** Fast sha256 for msgLen < 56 */ void sha256f(const uint8_t *msg, const int msgLen, uint8_t *hash) { uint8_t blk[64]; uint32_t H[8]; int blkLen, i; H[0] = 0x6a09e667; H[1] = 0xbb67ae85; H[2] = 0x3c6ef372; H[3] = 0xa54ff53a; H[4] = 0x510e527f; H[5] = 0x9b05688c; H[6] = 0x1f83d9ab; H[7] = 0x5be0cd19; blkLen = msgLen; memcpy(blk, msg, blkLen); /* pad the message */ blk[blkLen++] = 0x80; while (blkLen < 56) { blk[blkLen++] = 0; } blk[56] = 0; blk[57] = 0; blk[58] = 0; blk[59] = 0; blk[60] = (uint8_t)(msgLen >> 21); blk[61] = (uint8_t)(msgLen >> 13); blk[62] = (uint8_t)(msgLen >> 5); blk[63] = (uint8_t)(msgLen << 3); sha256HashBlock(blk, H); /* copy the output into the buffer (convert words to bytes) */ for (i = 0; i < 8; ++i) { hash[i*4 ] = (uint8_t)(H[i] >> 24); hash[i*4 + 1] = (uint8_t)(H[i] >> 16); hash[i*4 + 2] = (uint8_t)(H[i] >> 8); hash[i*4 + 3] = (uint8_t)H[i]; } } pdfcrack-0.18/pdfparser.c0000644000175000017500000004133213526344245015222 0ustar henninghenning/** * Copyright (C) 2006-2019 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include "pdfparser.h" #include #include #define BUFFSIZE 256 /** Please rewrite all of this file in a clean and stable way */ struct p_str { uint8_t *content; uint8_t len; }; static int o_len, u_len; typedef struct p_str p_str; __attribute__ ((pure)) static bool isWhiteSpace(const int ch) { return (ch == 0x20 || (ch >= 0x09 && ch <= 0x0d) || ch == 0x00); } __attribute__ ((pure)) static bool isDelimiter(const int ch) { switch(ch) { case '(': case ')': case '<': case '>': case '[': case ']': case '{': case '}': case '/': case '%': return true; default: return false; } } __attribute__ ((pure)) static inline bool isEndOfLine(const int ch) { return (ch == 0x0a || ch == 0x0d); } static int parseIntWithC(FILE *file, const int c) { bool neg = false; int i = 0; int ch = c; if(ch == '-') { neg = true; ch = getc(file); } else if(ch == '+') ch = getc(file); while(ch >= '0' && ch <= '9') { i *= 10; i += ch-'0'; ch = getc(file); } ungetc(ch,file); if(neg) i *= -1; return i; } static int parseInt(FILE *file) { return parseIntWithC(file, getc(file)); } static char parseWhiteSpace(FILE *file) { int ch; do { ch = getc(file); } while(isWhiteSpace(ch)); return ch; } static char parseWhiteSpaceOrComment(FILE *file) { int ch; do { do { ch = getc(file); } while(isWhiteSpace(ch)); if(ch == '%') { do { ch = getc(file); } while(!(ch >= 0x09 && ch <= 0x0d) && ch != EOF); ch = getc(file); } } while(isWhiteSpace(ch) || ch == '%'); return ch; } static char* parseName(FILE *file) { int ch; unsigned int i; char *ret; char buff[BUFFSIZE]; ch = parseWhiteSpace(file); if(ch != '/') { ungetc(ch, file); return NULL; } ch = getc(file); for(i=0; i= 0) ret = true; } if(ret) { e->version_major = major_v; e->version_minor = minor_v; } return ret; } __attribute__ ((pure)) static uint8_t hexToInt(const int b) { if(b >= '0' && b <= '9') return b-'0'; else if(b >= 'a' && b <= 'f') return b-'a'+10; else if(b >= 'A' && b <= 'F') return b-'A'+10; else return 0; } static p_str* parseHexString(const uint8_t *buf, const unsigned int len) { unsigned int i,j; p_str *ret; ret = malloc(sizeof(p_str)); ret->content = malloc(sizeof(uint8_t)*(len/2)); ret->len = (len/2); for(i=0, j=0; icontent[j] = hexToInt(buf[i]) * 16; ret->content[j] += hexToInt(buf[i+1]); j++; } return ret; } static p_str* objStringToByte(const uint8_t* str, const unsigned int len) { unsigned int i, j, l; uint8_t b, d; uint8_t tmp[len]; p_str *ret; for(i=0, l=0; i= '0' && str[i] < '8') { d = 0; for(j=0; i < len && j < 3 && str[i] >= '0' && str[i] < '8' && (d*8)+(str[i]-'0') < 256; j++, i++) { d *= 8; d += (str[i]-'0'); } /** * We need to step back one step if we reached the end of string * or the end of digits (like for example \0000) **/ if(i < len || j < 3) { i--; } b = d; } } } tmp[l] = b; } ret = malloc(sizeof(p_str)); ret->content = malloc(sizeof(uint8_t)*(l)); ret->len = l-1; memcpy(ret->content, tmp, l); return ret; } static p_str* parseRegularString(FILE *file) { unsigned int len, p; int ch; p_str *ret = NULL; uint8_t buf[BUFFSIZE]; bool skip = false; ch = parseWhiteSpace(file); if(ch == '(') { p = 1; ch = getc(file); for(len=0; len < BUFFSIZE && p > 0 && ch != EOF; len++) { buf[len] = ch; if(skip == false) { if(ch == '(') p++; else if(ch == ')') p--; if(ch == '\\') skip = true; } else skip = false; ch = getc(file); } ungetc(ch, file); if(len > 0) ret = objStringToByte(buf, len); } else if(ch == '<') { len = 0; while(ch != '>' && len < BUFFSIZE && ch != EOF) { if((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { buf[len++] = ch; } ch = getc(file); } ungetc(ch,file); if((len > 1) && ((len%2) == 0)) ret = parseHexString(buf,len); } return ret; } static int findTrailerDict(FILE *file, EncData *e) { int ch; /** int pos_i; */ bool encrypt = false; bool id = false; int e_pos = -1; p_str *str = NULL; int dict = 0; ch = getc(file); while(ch != EOF) { if(isEndOfLine(ch)) { ch = parseWhiteSpace(file); if(ch == '<' && getc(file) == '<') { /** This should be the first dict we stumble upon*/ /** pos_i = ftell(file); printf("found Trailer at pos %x\n", pos_i); */ ch = getc(file); while(ch != EOF) { if(ch == '<') { ch = getc(file); if(ch == '<') { dict++; } } if(ch == '>') { ch = getc(file); if(ch == '>') { if(dict == 0) break; } dict--; } while(ch != '/' && ch != EOF) { ch = getc(file); } ch = getc(file); /**printf("found a name: %c\n", ch);*/ if(e_pos < 0 && ch == 'E' && isWord(file, "ncrypt")) { e_pos = parseIntWithC(file,parseWhiteSpace(file)); if(e_pos >= 0) { /** pos_i = ftell(file); printf("found Encrypt at pos %x, ", pos_i); printf("%d\n", e_pos); */ encrypt = true; } } else if(ch == 'I' && getc(file) == 'D') { ch = parseWhiteSpace(file); while(ch != '[' && ch != EOF) ch = getc(file); if(str) { free(str->content); free(str); } str = parseRegularString(file); /** pos_i = ftell(file); printf("found ID at pos %x\n", pos_i); */ if(str) id = true; else id = false; ch = getc(file); } else ch = getc(file); if(encrypt && id) { /**printf("found all, returning: epos: %d\n",e_pos);*/ e->fileID = str->content; e->fileIDLen = str->len; free(str); return e_pos; } } } else { ch = getc(file); } } else ch = getc(file); } /** printf("finished searching\n");*/ if(str) { free(str->content); free(str); } if(!encrypt && id) return ETRENF; else if(!id && encrypt) { /** We found a encrypt object, but not an ID. Let us try parsing the object as some security handlers seems to encrypt/skip the ID. Logic changed to show these handles correctly instead of printing out a error that the document is not encrypted. **/ return e_pos; } else return ETRANF; } static int findTrailer(FILE *file, EncData *e) { int ch; /** int pos_i; */ bool encrypt = false; bool id = false; int e_pos = -1; p_str *str = NULL; ch = getc(file); while(ch != EOF) { if(isEndOfLine(ch)) { if(isWord(file, "trailer")) { /** printf("found trailer\n");*/ ch = parseWhiteSpace(file); if(ch == '<' && getc(file) == '<') { /** we can be pretty sure to have found the trailer. start looking for the Encrypt-entry */ /** pos_i = ftell(file); printf("found Trailer at pos %x\n", pos_i); */ ch = getc(file); while(ch != EOF) { if(ch == '>') { ch = getc(file); if(ch == '>') break; } while(ch != '/' && ch != EOF) { ch = getc(file); } ch = getc(file); /**printf("found a name: %c\n", ch);*/ if(e_pos < 0 && ch == 'E' && isWord(file, "ncrypt")) { e_pos = parseIntWithC(file,parseWhiteSpace(file)); if(e_pos >= 0) { /** pos_i = ftell(file); printf("found Encrypt at pos %x, ", pos_i); printf("%d\n", e_pos); */ encrypt = true; } } else if(!id && ch == 'I' && getc(file) == 'D') { ch = parseWhiteSpace(file); while(ch != '[' && ch != EOF) ch = getc(file); if(str) { if(str->content) free(str->content); free(str); } str = parseRegularString(file); /** pos_i = ftell(file); printf("found ID at pos %x\n", pos_i); */ if(str) id = true; ch = getc(file); } else ch = getc(file); if(encrypt && id) { /**printf("found all, returning: epos: %d\n",e_pos);*/ e->fileID = str->content; e->fileIDLen = str->len; free(str); return e_pos; } } } } else { ch = getc(file); } } else ch = getc(file); } /** printf("finished searching\n");*/ if(str) { free(str->content); free(str); } if(!encrypt && id) return ETRENF; else if(!id && encrypt) { /** We found a encrypt object, but not an ID. Let us try parsing the object as some security handlers seems to encrypt/skip the ID. Logic changed to show these handles correctly instead of printing out a error that the document is not encrypted. **/ return e_pos; } else return ETRANF; } static bool parseEncrypObject(FILE *file, EncData *e) { int ch, dict = 1; bool fe = false; bool ff = false; bool fl = false; bool fo = false; bool fp = false; bool fr = false; bool fu = false; bool fv = false; bool cf = false; bool aesv2 = false; p_str *str = NULL; ch = getc(file); while(ch != EOF) { if(ch == '>') { ch = getc(file); if(ch == '>') { dict--; if(dict <= 0) break; } } else if(ch == '<') { ch = getc(file); if(ch == '<') { dict++; } } if(dict > 1) { ch = getc(file); if(ch == '/') { ch = getc(file); switch(ch) { case 'A': if(isWord(file, "ESV2")) { aesv2 = true; } break; default: break; } } continue; } if(ch == '/') { ch = getc(file); switch(ch) { case 'C': if(isWord(file, "F")) { cf = true; } break; case 'E': if(isWord(file, "ncryptMetadata")) { ungetc(parseWhiteSpace(file), file); if(isWord(file, "false")) fe = true; } break; case 'F': if(isWord(file, "ilter")) { char *s_handler = parseName(file); if(s_handler != NULL) { e->s_handler = s_handler; ff = true; } } break; case 'L': if(isWord(file, "ength")) { int tmp_l = parseIntWithC(file,parseWhiteSpace(file)); if(!fl) { /* BZZZT!! This is sooo wrong but will work for most cases. only use the first length we stumble upon */ if(tmp_l > 256 || tmp_l < 40 || ((tmp_l % 8) != 0)) { fprintf(stderr, "WARNING: Length = %d\n", tmp_l); } e->length = tmp_l; } fl = true; } break; case 'O': /** Check if it is OE string and ignore it then */ ch = getc(file); if(ch == 'E') { str = parseRegularString(file); if(str) { free(str->content); free(str); } break; } else ungetc(ch,file); /** Parse O-String */ str = parseRegularString(file); if(!str) break; e->o_string = str->content; o_len = str->len; free(str); fo = true; break; case 'P': ch = getc(file); if(isWhiteSpace(ch)) { ch = parseWhiteSpace(file); e->permissions = parseIntWithC(file,ch); fp = true; } break; case 'R': ch = getc(file); if(isWhiteSpace(ch)) { ch = parseWhiteSpace(file); e->revision = parseIntWithC(file,ch); fr = true; } break; case 'U': /** Check if it is UE string and ignore it then */ ch = getc(file); if(ch == 'E') { str = parseRegularString(file); if(str) { free(str->content); free(str); } break; } else ungetc(ch,file); /** Parse U-string */ str = parseRegularString(file); if(!str) break; e->u_string = str->content; u_len = str->len; free(str); fu = true; break; case 'V': ch = getc(file); if(isWhiteSpace(ch)) { e->version = parseIntWithC(file, parseWhiteSpace(file)); fv = true; } break; default: break; } } ch = parseWhiteSpace(file); } if(!fe) e->encryptMetaData = true; if(!fl || e->length == 0) e->length = 40; if(!fv) e->version = 0; if(fr) { if (e->revision >= 5 && fu && fo) { if(o_len < 48) { fprintf(stderr, "WARNING: O-String < 48 Bytes: %d\n", o_len); fo = false; } if(u_len < 48) { fprintf(stderr, "WARNING: U-String < 48 Bytes: %d\n", u_len); fu = false; } } else { if(fu && fo) { if(o_len != 32) { fprintf(stderr, "WARNING: O-String != 32 Bytes: %d\n", o_len); fo = false; } if(u_len != 32) { fprintf(stderr, "WARNING: U-String != 32 Bytes: %d\n", u_len); fu = false; } } if (cf && aesv2) { e->revision = 3; e->version = 2; } } } if(ff && strcmp(e->s_handler,"Standard") != 0) return true; /** if(ff) printf("Found Filter\n"); if(fo) printf("Found Owner string\n"); if(fp) printf("Found Permission\n"); if(fr) printf("Found Revision\n"); if(fu) printf("Found User String\n"); */ return ff & fo && fp && fr && fu; } /** This is not a really definitive search. Should be replaced with something better */ static bool findEncryptObject(FILE *file, const int e_pos, EncData *e) { int ch; /** only find the encrypt object if e_pos > -1 */ if(e_pos < 0) return false; ch = getc(file); while(ch != EOF) { if(isEndOfLine(ch)) { if(parseInt(file) == e_pos) { ch = parseWhiteSpace(file); if(ch >= '0' && ch <= '9') { ch = parseWhiteSpace(file); if(ch == 'o' && getc(file) == 'b' && getc(file) == 'j' && parseWhiteSpaceOrComment(file) == '<' && getc(file) == '<') { return parseEncrypObject(file, e); } } } } ch = getc(file); } /** If this fails, do relaxed check to see if we might find it without a EndOfLine character before the object */ rewind(file); ch = getc(file); while(ch != EOF) { if(ch >= '0' && ch <= '9') { ungetc(ch,file); if(parseInt(file) == e_pos) { ch = parseWhiteSpace(file); if(ch >= '0' && ch <= '9') { ch = parseWhiteSpace(file); if(ch == 'o' && getc(file) == 'b' && getc(file) == 'j' && parseWhiteSpaceOrComment(file) == '<' && getc(file) == '<') { return parseEncrypObject(file, e); } } } } ch = getc(file); } /** give up */ return false; } int getEncryptedInfo(FILE *file, EncData *e) { int e_pos = -1; bool ret; /* Start checking if the block is in the end of the file */ if(fseek(file, -1024, SEEK_END)) e_pos = findTrailer(file, e); if(e_pos < 0) { rewind(file); e_pos = findTrailer(file, e); } if(e_pos < 0) { rewind(file); e_pos = findTrailerDict(file, e); } if(e_pos < 0) { return e_pos; } rewind(file); ret = findEncryptObject(file, e_pos, e); if(!ret) return EENCNF; return 0; } pdfcrack-0.18/pdfreader.c0000644000175000017500000000407413526357452015176 0ustar henninghenning/** * Copyright (C) 2006-2019 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include "pdfparser.h" #include #include #include //#include //#include #define _FILE_OFFSET_BITS 64 int main(int argc, char **argv) { int ret = 0; EncData *e; if(argc != 2) { fprintf(stderr,"Usage: %s filename\n", argv[0]); exit(1); } FILE *file; if((file = fopen(argv[1], "r")) == 0) { fprintf(stderr,"Error: file %s not found\n", argv[1]); exit(2); } // int ch; e = calloc(1,sizeof(EncData)); if(!openPDF(file,e)) { fprintf(stderr, "Error: Not a valid PDF\n"); ret = 3; goto out; } ret = getEncryptedInfo(file, e); if(ret != 0) { if(ret == EENCNF) fprintf(stderr, "Error: Could not extract encryption information\n"); else if(ret == ETRANF) fprintf(stderr, "Error: First trailer not found\n"); else if(ret == ETRENF) fprintf(stderr, "Error: Encryption object not found in trailer\n"); else if(ret == ETRINF) fprintf(stderr, "Error: ID object not found in trailer\n"); ret = 4; goto out; } printEncData(e); freeEncData(e); if(fclose(file) != 0) { fprintf(stderr, "Error: closing file %s\n", argv[1]); } return 0; out: freeEncData(e); if(fclose(file) != 0) { fprintf(stderr, "Error: closing file %s\n", argv[1]); } exit(ret); } pdfcrack-0.18/main.c0000644000175000017500000002107613526357507014170 0ustar henninghenning/** * Copyright (C) 2006-2019 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include #include #include #include #include #include #include "pdfparser.h" #include "pdfcrack.h" #include "benchmark.h" #define PRINTERVAL 20 /** Print Progress Interval (seconds) */ #define CRASHFILE "savedstate.sav" #define VERSION_MAJOR 0 #define VERSION_MINOR 18 #define _FILE_OFFSET_BITS 64 /** alarmInterrupt is used to print out the progress at specific intervals */ static void alarmInterrupt() { if(!printProgress()) alarm(PRINTERVAL); } /** autoSave is used to save down the current state when interrupted */ __attribute__ ((noreturn)) static void autoSave(int sig) { FILE *file; if(sig) fprintf(stderr,"Caught signal %d!\nTrying to save state...\n",sig); if((file = fopen(CRASHFILE, "w")) == 0) { fprintf(stderr,"Could not open %s for writing\n", CRASHFILE); } else{ saveState(file); fclose(file); fprintf(stderr,"Successfully saved state to %s!\n",CRASHFILE); } exit(sig); } /** print some help for the user */ static void printHelp(char *progname) { printf("Usage: %s -f filename [OPTIONS]\n" "OPTIONS:\n" "-b, --bench\t\tperform benchmark and exit\n" "-c, --charset=STRING\tUse the characters in STRING as charset\n" "-w, --wordlist=FILE\tUse FILE as source of passwords to try\n" "-n, --minpw=INTEGER\tSkip trying passwords shorter than this\n" "-m, --maxpw=INTEGER\tStop when reaching this passwordlength\n" "-l, --loadState=FILE\tContinue from the state saved in FILENAME\n" "-o, --owner\t\tWork with the ownerpassword\n" "-u, --user\t\tWork with the userpassword (default)\n" "-p, --password=STRING\tGive userpassword to speed up breaking\n" "\t\t\townerpassword (implies -o)\n" "-q, --quiet\t\tRun quietly\n" "-s, --permutate\t\tTry permutating the passwords (currently only\n" "\t\t\tsupports switching first character to uppercase)\n" "-v, --version\t\tPrint version and exit\n", progname); } int main(int argc, char** argv) { int ret = 0, minpw = 0, maxpw = 32; struct sigaction act1, act2; FILE *file = NULL, *wordlist = NULL; bool recovery = false, quiet = false, work_with_user = true, permutation = false; uint8_t *userpassword = NULL; char *charset = NULL, *inputfile = NULL, *wordlistfile = NULL; EncData *e = NULL; /** Parse arguments */ while(true) { int c, option_index; static struct option long_options[] = { {"bench", no_argument , 0, 'b'}, {"charset", required_argument, 0, 'c'}, {"file", required_argument, 0, 'f'}, {"loadState",required_argument, 0, 'l'}, {"maxpw", required_argument, 0, 'm'}, {"minpw", required_argument, 0, 'n'}, {"owner", no_argument , 0, 'o'}, {"password", required_argument, 0, 'p'}, {"quiet", required_argument, 0, 'q'}, {"permutate",no_argument, 0, 's'}, {"user", no_argument , 0, 'u'}, {"wordlist", required_argument, 0, 'w'}, {"version", no_argument , 0, 'v'}, {0, 0, 0, 0}}; /* getopt_long stores the option index here. */ option_index = 0; c = getopt_long(argc, argv, "bc:f:l:m:n:op:qsuw:v", long_options, &option_index); /* Detect the end of the options. */ if(c == -1) break; switch(c) { case 'b': runBenchmark(); return 0; case 'c': if(charset) fprintf(stderr, "Charset already set\n"); else charset = strdup(optarg); break; case 'f': if(!recovery) inputfile = strdup(optarg); break; case 'l': if(inputfile) { fprintf(stderr, "Inputfile already set\n"); } else { inputfile = strdup(optarg); recovery = true; } break; case 'm': maxpw = atoi(optarg); break; case 'n': minpw = atoi(optarg); break; case 'o': work_with_user = false; break; case 'p': if(userpassword) fprintf(stderr, "User password already set\n"); else { userpassword = (uint8_t*)strdup(optarg); work_with_user = false; } break; case 'q': quiet = true; break; case 'u': if(!work_with_user) { fprintf(stderr, "Cannot work with both user- and owner-password\n"); exit(1); } work_with_user = true; break; case 's': permutation = true; break; case 'w': if(wordlistfile) fprintf(stderr, "Wordlist already set\n"); else wordlistfile = strdup(optarg); break; case 'v': printf("pdfcrack version %d.%d\n", VERSION_MAJOR, VERSION_MINOR); return 0; default: printHelp(argv[0]); ret = 1; goto out2; } } /** If there are any unhandled arguments and the filename and/or wordlist is not set: try to match the first as the filename and the second as wordlist. */ { int i = optind; if(i > 0) { if(i < argc && !inputfile) inputfile = strdup(argv[i++]); if(i < argc && !wordlistfile) wordlistfile = strdup(argv[i++]); if(i < argc) while(irevision < 2 || (strcmp(e->s_handler,"Standard") != 0 || e->revision > 5)) { fprintf(stderr, "The specific version is not supported (%s - %d)\n", e->s_handler, e->revision); ret = 5; goto out1; } } if(fclose(file)) { fprintf(stderr, "Error: closing file %s\n", inputfile); } if(minpw > maxpw) { fprintf(stderr, "Warning: minimum pw-length bigger than max\n"); } if(wordlistfile) { if((wordlist = fopen(wordlistfile, "r")) == 0) { fprintf(stderr,"Error: file %s not found\n", wordlistfile); ret = 6; goto out2; } } act2.sa_handler = autoSave; sigfillset(&act2.sa_mask); act2.sa_flags = 0; sigaction(SIGINT, &act2, 0); if(!quiet) { printEncData(e); act1.sa_handler = alarmInterrupt; sigemptyset(&act1.sa_mask); act1.sa_flags = 0; sigaction(SIGALRM, &act1, 0); alarm(PRINTERVAL); } /** Try to initialize the cracking-engine */ if(!initPDFCrack(e, userpassword, work_with_user, wordlistfile, wordlistfile?Wordlist:Generative, wordlist, charset, (unsigned int)minpw, (unsigned int)maxpw, permutation)) { cleanPDFCrack(); fprintf(stderr, "Wrong userpassword, '%s'\n", userpassword); ret = 7; goto out2; } /** Do the actual crunching */ runCrack(); /** cleanup */ cleanPDFCrack(); freeEncData(e); if(wordlistfile) { fclose(wordlist); free(wordlistfile); } free(inputfile); free(charset); free(userpassword); return 0; out1: if(fclose(file)) fprintf(stderr, "Error: closing file %s\n", inputfile); out2: freeEncData(e); free(inputfile); free(charset); free(userpassword); exit(ret); } pdfcrack-0.18/common.h0000644000175000017500000000333312274236475014534 0ustar henninghenning/** * Copyright (C) 2006 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _PDFCOMMON_H_ #define _PDFCOMMON_H_ #include #include #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) /** EncData holds all the information regarding the encryption-setting of a specific pdf. s_handler - Security handler string. o_string - Owner-string, 32 bytes, not null-terminated u_string - User-string, 32 bytes, not null-terminated fileID - file ID in fileIDLen bytes, not null-terminated */ struct EncData { char *s_handler; uint8_t *o_string; uint8_t *u_string; uint8_t *fileID; bool encryptMetaData; unsigned int fileIDLen; unsigned int version_major; unsigned int version_minor; int length; int permissions; int revision; int version; }; typedef struct EncData EncData; typedef enum passwordMethod { Wordlist=1, Generative } passwordMethod; void freeEncData(EncData *e); void printEncData(EncData *e); #endif /** _PDFCOMMON_H_ */ pdfcrack-0.18/Makefile0000644000175000017500000000100212477741741014524 0ustar henninghenningCFLAGS += -Wall -Wextra -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -O3 -g OBJS = main.o sha256.o rc4.o md5.o pdfcrack.o pdfparser.o passwords.o common.o \ benchmark.o OBJS_PDFREADER = pdfparser.o pdfreader.o common.o all: pdfcrack pdfcrack: $(OBJS) $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(OBJS) pdfreader: $(OBJS_PDFREADER) $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $(OBJS_PDFREADER) clean: rm -f pdfcrack pdfreader testreader *.o %.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -c -o $@ $+ pdfcrack-0.18/common.c0000644000175000017500000000371513526357470014533 0ustar henninghenning/** * Copyright (C) 2006-2019 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include #include "common.h" void freeEncData(EncData *e) { if(!e) return; free(e->o_string); free(e->u_string); free(e->fileID); free(e->s_handler); free(e); } void printEncData(EncData *e) { unsigned int i; uint8_t ch; printf("PDF version %u.%u\n", e->version_major, e->version_minor); if(e->s_handler) printf("Security Handler: %s\n",e->s_handler); printf("V: %d\nR: %d\nP: %d\nLength: %d\nEncrypted Metadata: %s\n", e->version, e->revision, e->permissions, e->length, e->encryptMetaData?"True":"False"); printf("FileID: "); for(i=0; i < e->fileIDLen; i++) { ch = e->fileID[i]; if(ch < 16) printf("0%x", ch); else printf("%x", ch); } /** Assume u_string and o_string is of length 32. Not safe, but the code as a whole needs a rewrite anyway */ if(e->u_string) { printf("\nU: "); for(i=0; i<32; i++) { ch = e->u_string[i]; if(ch < 16) printf("0%x", ch); else printf("%x", ch); } } if(e->o_string) { printf("\nO: "); for(i=0; i<32; i++) { ch = e->o_string[i]; if(ch < 16) printf("0%x", ch); else printf("%x", ch); } } printf("\n"); } pdfcrack-0.18/passwords.h0000644000175000017500000000225510520400370015246 0ustar henninghenning/** * Copyright (C) 2006 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _PDFPASSWORDS_H_ #define _PDFPASSWORDS_H_ #include #include "common.h" void initPasswords(const passwordMethod pm, FILE *file, const char *wl, const char *cs, const unsigned int minPw, const unsigned int maxPw); bool nextPassword(void); unsigned int setPassword(uint8_t *outbuf); bool pw_loadState(FILE *file, char **wl); void pw_saveState(FILE *file); #endif /** _PDFPASSWORDS_H_ */ pdfcrack-0.18/COPYING0000644000175000017500000004310710520400370014104 0ustar henninghenning GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. pdfcrack-0.18/README0000644000175000017500000000404312273055455013746 0ustar henninghenningCode and documentation are copyright 2006-2008 Henning Norén Parts of pdfcrack.c and md5.c is derived/copied/inspired from xpdf/poppler and are copyright 1995-2006 Glyph & Cog, LLC. The PDF data structures, operators, and specification are copyright 1985-2006 Adobe Systems Inc. Project page: http://sourceforge.net/projects/pdfcrack/ pdfcrack is a simple tool for recovering passwords from pdf-documents. It should be able to handle all pdfs that uses the standard security handler but the pdf-parsing routines are a bit of a quick hack so you might stumble across some pdfs where the parser needs to be fixed to handle. Type 'make' (or 'gmake' if you have BSD-make as default) to build the program. You will need to have GNU Make and a recent version of GCC installed but there are no external dependencies on libraries. You will have to add the -march-switch in the CFLAGS-option in Makefile for best optimization on your platform. Look into the GCC-manual (http://gcc.gnu.org/onlinedocs/) if you are unsure. The program is distributed under GPL version 2 (or later). Features available in this release (check TODO for features that might come): * Both owner- and user-passwords with the Standard Security Handler, rev 2 & 3. * Search by wordlist * Search by bruteforcing with specific charset * Optimized search for owner-password when user-password is known (or empty) * Extremely simple permutations of passwords (makes first letter uppercase) - currently only useful for bruteforcing with charsets: * Auto-save when interrupted (Ctrl-C or send SIGINT to the process) * Loading saved state - currently only for bruteforcing with charsets: * Minimum length of password to start at * Maximum length of password to try Sort your wordlist by length for best performance and consider that almost all passwords in PDFs are in iso latin 1 so use the correct character encoding in your terminal and/or wordlist when using special characters. This tool can not decrypt a Password Protected PDF. Look up the pdftk toolkit which can do that, when you know the password. pdfcrack-0.18/rc4.c0000644000175000017500000001464613104752330013722 0ustar henninghenning/** * Copyright (C) 2006-2017 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include "rc4.h" #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) /** Seems to be faster to do a memcpy of this on my machine than to create the array with a loop */ static const uint8_t initial_state[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; #define key_pass(n) { \ tmp = state[++i]; \ j = (j + tmp + key[n]); \ state[i] = state[j]; \ state[j] = tmp; \ } /** Do rc4-decrypt with key on bs of length 32 and compare it to match */ __attribute__ ((pure)) bool rc4Match40b(const uint8_t *key, const uint8_t *bs,const uint8_t *match) { uint8_t state[256]; register unsigned int i; register uint8_t j, tmp; /** initialize the state */ memcpy(state, initial_state, 256); /** do the shuffle */ j = key[0]; state[0] = j; state[j] = 0; i = 0; do { key_pass(1); key_pass(2); key_pass(3); key_pass(4); key_pass(0); } while(i < 255); j = 0; for(i=1;i<=32;++i) { tmp = state[i]; j += tmp; state[i] = state[j]; state[j] = tmp; /** Only continue if we match the match-strings characters. The match should only happen once every 256 try or so and that is the motivation behind the likely-hint */ tmp += state[i]; if(likely((bs[i-1]^state[tmp]) != match[i-1])) return false; } return true; } static void (*rc4d)(const uint8_t *key, const uint8_t *bs, const unsigned int len, uint8_t *out) = NULL; /** Do 40-bit rc4-decrypt with key on bs of length len and put the result in out */ static void rc4Decrypt40b(const uint8_t *key, const uint8_t *bs, const unsigned int len, uint8_t *out) { uint8_t state[256]; register unsigned int i; register uint8_t j, tmp; /** initialize the state */ memcpy(state, initial_state, 256); /** do the shuffle */ j = key[0]; state[0] = j; state[j] = 0; i = 0; do { key_pass(1); key_pass(2); key_pass(3); key_pass(4); key_pass(0); } while(i < 255); j = 0; for(i=1;i<=len;++i) { tmp = state[i]; j += tmp; state[i] = state[j]; state[j] = tmp; tmp += state[i]; out[i-1] = bs[i-1]^state[tmp]; } } /** Do 128-bit rc4-decrypt with key on bs of length len and put the result in out */ static void rc4Decrypt128b(const uint8_t *key, const uint8_t *bs, const unsigned int len, uint8_t *out) { uint8_t state[256]; register int i; register uint8_t j, tmp; /** initialize the state */ memcpy(state, initial_state, 256); /** do the shuffle */ j = 0; i = -1; do { key_pass( 0); key_pass( 1); key_pass( 2); key_pass( 3); key_pass( 4); key_pass( 5); key_pass( 6); key_pass( 7); key_pass( 8); key_pass( 9); key_pass(10); key_pass(11); key_pass(12); key_pass(13); key_pass(14); key_pass(15); } while(i < 255); j = 0; for(i=1;(unsigned int)i<=len;++i) { tmp = state[i]; j += tmp; state[i] = state[j]; state[j] = tmp; tmp += state[i]; out[i-1] = bs[i-1]^state[tmp]; } } static int keyLen; static void rc4DecryptArb(const uint8_t *key, const uint8_t *bs, const unsigned int len, uint8_t *out) { uint8_t state[256]; register unsigned int i; register uint8_t j, tmp; /** initialize the state */ memcpy(state, initial_state, 256); /** do the shuffle */ j = 0; i = -1; do { key_pass( (i % keyLen) ); } while(i < 255); j = 0; for(i=1;(unsigned int)i<=len;++i) { tmp = state[i]; j += tmp; state[i] = state[j]; state[j] = tmp; tmp += state[i]; out[i-1] = bs[i-1]^state[tmp]; } } /** Just a wrapper for the function optimized for a specific length */ inline void rc4Decrypt(const uint8_t *key, const uint8_t *bs, const unsigned int len, uint8_t *out) { rc4d(key, bs, len, out); } /** sets which function the wrapper should call */ bool setrc4DecryptMethod(const unsigned int length) { if(length > 255 || length < 40 || length % 8 != 0) return false; if(length == 128) { rc4d = &rc4Decrypt128b; } else if(length == 40) { rc4d = &rc4Decrypt40b; } else { keyLen = length / 8; rc4d = &rc4DecryptArb; } return true; } pdfcrack-0.18/changelog0000644000175000017500000001335413526357601014745 0ustar henninghenning2019-08-19 Henning Norén - 0.18 * Update copyright years for all changed files * Relax the search for encrypt object a bit more (thanks to Oleg for report) * Tweak benchmark to make better comparison between md5_50 slow and fast * Multiple inputfiles (-l) is not supported, so warn if used * Remove a bunch of unecessary if(ptr) before free(ptr) * Bump version to 0.18 2019-04-17 Henning Norén - 0.17 * Ignore comments after obj in pdfparser. Thanks to Hima for report * Make loading save-state files a bit more robust Thanks to Murmus CTF for showing these problems * Fix incorrect fallthrough in pdfparser * Bump version to 0.17 2017-04-19 Henning Norén - 0.16 * Remove incorrectly used pure-attribute (thanks to Carl Shapiro for report) * Fix a possible overflow by one in the parser * Fix pdfcrack starting despite being unable to extract encryption info * Fix indention in man-page. Thanks to Jakub Wilk for patch * Bump version to 0.16 2015-09-25 Henning Norén - 0.15 * Fix a few crashes found by "american fuzzy lop" * Make the benchmark a bit more robust with volatile * Fix a couple of undefined behaviours with left shift on int-type * Fix GNU Make-isms in Makefile, thanks to Leonardo Taccari * Fix various clang and cppchecker issues and warnings * Bump version to 0.15 2014-08-31 Henning Norén - 0.14 * Add man-page * s/CFLAGS=/CFLAGS +=/ in Makefile * s/gcc/($CC)/ in Makefile * Add support for wordlists and pdfs > 2GB (thanks to Gary Dewey) * Bump version to 0.14 2014-02-10 Henning Norén - 0.13 * Skip strip and add debug symbols in Makefile * Bail out if encountering rev 5+ (Not supported) * Fix parsing of certain rev 4+ documents * Add initial support for rev 5 (SHA256) * Fix rev 3 owner password issues when other than 128 bit keylengths * Update copyright dates in header of files * Fix a gcc warning * Fix filemode when opening a file on unix to make it more portable Thanks to Shinobu Maehara for the notice * Fix a pdfparser logic bug, thanks to Shinobu Maehara for reporting * bump version to 0.13 2014-02-01 Henning Norén - 0.12 * Print warning for odd key lengths * Add support for handling Rev 3 with other than 128 bit keylengths * Try parsing Encrypt object even when ID object is not found * Fix a few gcc-warnings * bump version to 0.12 2008-08-03 Henning Norén - 0.11 * Print out the user password when finding the ownerpassword * Be less strict when parsing magic number * Fixed two null-pointer dereferences * Actually start the search for trailer in last KB * bump version to 0.11 2008-05-01 Henning Norén - 0.10 * Add support for different newline-styles in wordlists * Fix three nasty issues when parsing binary strings A big thanks to Will McCullen who reported and helped debug them * Change step 6 when computing the encryption key to only apply to rev 3+ * Add warnings when the parser got a O/U binary string != 32 bytes * Add support for non-hex ID strings * Change error messages when not finding the ID or Encrypt object in trailer * Try with empty password when bruteforcing from 0 characters (#1767244) * Mention pdftk in the README (#1767245) * Start search for the trailer in the last KB * bump version to 0.10 2007-10-23 Henning Norén - 0.9 * flush the buffers in printProgress * make use of %zu when printf-ing size_t * bump version to 0.9 2006-10-16 Henning Norén - 0.8 * Removed fweb and Wextra from CFlags in Makefile to support gcc 3.3.5 * Added snippet about needing GNU Make in README * Support version >3 Standard security handlers * Actually notice when EncryptMetadata is false and print out it's value * Print a warning when someone sets minpw > maxpw * Fix some warnings * bump version to 0.8 2006-07-08 Henning Norén - 0.7 * Fix a problem with initializations of passwords when using max-length < 32. Thanks to Antonis Tassis for pointing it out. * Fix some inconsistency when using --version-argument * bump version to 0.7 2006-07-06 Henning Norén - 0.6 * Actually save the permutation-setting when saving * bump version to 0.6 2006-07-06 Henning Norén - 0.5 * Make loading and saving states much more robust * Block all signals when terminating from a SIGINT * Add support for turning on permutations that is now off by default * Various small tweaks, cleanups and bugfixes * Optimizations of md5 and rc4 * bump version to 0.5 2006-06-18 Henning Norén - 0.4 * Switch from using processor-time to using real time when reporting progress * Add support for some simple benchmarking * Add a check for the correct security-handler version to avoid a segfault * Don't try the same password twice when permutating * Fix a bug that made pdfcrack unable to load the upw from saved state file * Fix a bug that ignored the saved max password when loading a saved state * Various small fixes and changes * bump version to 0.4 2006-05-28 Henning Norén - 0.3 * Move CREDITS into README and add reference to project-page at sourceforge * Various cleanups and comments * Optimize md5 round 1 according to Colin Plumb's implementation * bump version to 0.3 2006-05-26 Henning Norén - 0.2 * Fix stupid bug in main that crept in right before the release * Change the updateinterval to 20 seconds and spellfix the define * add a newline to the help-text * bump version to 0.2 2006-05-26 Henning Norén - 0.1 * First release pdfcrack-0.18/pdfcrack.h0000644000175000017500000000300512274365475015020 0ustar henninghenning/** * Copyright (C) 2006 Henning Norén * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #ifndef _PDFCRACK_H_ #define _PDFCRACK_H_ #include #include "common.h" bool initPDFCrack(const EncData *e, const uint8_t *upw, const bool user, const char *wl, const passwordMethod pm, FILE *file, const char *cs, const unsigned int minPw, const unsigned int maxPw, const bool perm); bool loadState(FILE *file, EncData *e, char **wl, bool *user); void saveState(FILE *file); void cleanPDFCrack(void); bool runCrackRev2(void); bool runCrackRev2_o(void); bool runCrackRev2_of(void); bool runCrackRev3(void); bool runCrackRev3_o(void); bool runCrackRev3_of(void); bool runCrackRev5_o(void); bool runCrackRev5(void); void runCrack(void); bool printProgress(void); unsigned int getNrProcessed(void); #endif /** _PDFCRACK_H_ */