1
1
#!/usr/bin/env python2
2
2
# Author: Alamot
3
3
# This is a XOR plaintext attack tool: If we know a part of the plaintext maybe
4
- # we can recover the key and the whole text in successive steps .
4
+ # we can recover the key and the whole text.
5
5
from __future__ import print_function
6
6
from __future__ import division
7
7
import string , sys
13
13
14
14
15
15
def is_printable (text , ignore_code ):
16
+ ''' Function to check if every character in text is printable '''
16
17
for ch in text :
17
18
if ord (ch ) == ignore_code :
18
19
continue
@@ -26,15 +27,30 @@ def lrotate(s, d):
26
27
return s [d :] + s [0 :d ]
27
28
28
29
29
- if len (sys .argv ) < 2 :
30
- print ("Usage: " + sys .argv [0 ]+ "<filename> <known plaintext> [max_key_length]" )
30
+ if len (sys .argv ) < 2 or sys .argv [1 ].strip ().lower () == "--help" :
31
+ print ("Known-plaintext attack: " + sys .argv [0 ]+ " <filename> <known plaintext> [max_key_length]" )
32
+ print ("Decrypt using known key: " + sys .argv [0 ]+ " <filename> --key=the_known_key" )
31
33
exit ()
32
34
33
35
filename = sys .argv [1 ]
34
- known_plaintext = sys .argv [2 ]
36
+
37
+ if sys .argv [2 ].strip ().lower ()[:5 ] == "--key" :
38
+ known_key = sys .argv [2 ].strip ()[6 :]
39
+ with open (filename , "rb" ) as f :
40
+ data = f .read ()
41
+ decrypted_text = ""
42
+ repeated_key = (known_key )* ((len (data ) // len (known_key )) + 1 )
43
+ for x in range (len (data )):
44
+ decrypted_text += chr (ord (data [x ]) ^ ord (repeated_key [x ]))
45
+ print ("Key length: " + str (len (known_key )), "\n Partial Key: " + known_key , "\n Plaintext: " + decrypted_text )
46
+ exit ()
47
+ else :
48
+ known_plaintext = sys .argv [2 ]
49
+
35
50
if len (known_plaintext ) > max_key_length :
36
51
print ("The length of the known plaintext is greater than max_key_length (=" + str (max_key_length )+ "). Please give a smaller plaintext or incrase max_key_length." )
37
52
exit ()
53
+
38
54
if len (sys .argv ) > 3 :
39
55
print (len (sys .argv ))
40
56
max_key_length = int (sys .argv [3 ])+ 1
@@ -45,7 +61,7 @@ def lrotate(s, d):
45
61
print ("Searching XOR-encrypted " + filename + " for string '" + known_plaintext + "'" )
46
62
47
63
try :
48
- for i in range (len (data )- len (known_plaintext )):
64
+ for i in range (len (data )- len (known_plaintext )): # Try known plaintext in every position
49
65
partial_key = ""
50
66
for j in range (len (known_plaintext )):
51
67
if known_plaintext [j ] == ignore_code :
@@ -54,18 +70,18 @@ def lrotate(s, d):
54
70
partial_key += chr (ord (data [i + j ]) ^ ord (known_plaintext [j ]))
55
71
#print("Single key: "+partial_key)
56
72
if is_printable (partial_key , ignore_code ) or not printable_key :
57
- for n in range (len (partial_key ), max_key_length ): #try different key lengths
58
- for m in range (n ): #try different partial key positions
73
+ for n in range (len (partial_key ), max_key_length ): # Try different key lengths
74
+ for m in range (n ): # Try different partial key positions
59
75
expanded_key = lrotate (partial_key + chr (ignore_code )* (n - len (partial_key )), m )
60
76
#print(expanded_key, m)
61
77
repeated_key = (expanded_key )* ((len (data ) // len (expanded_key )) + 1 )
62
78
decrypted_text = ""
63
- for x in range (len (data )):
79
+ for x in range (len (data )): # Try to decrypt the encoded text
64
80
if ord (repeated_key [x ]) == ignore_code :
65
81
decrypted_text += chr (ignore_code )
66
82
else :
67
83
decrypted_text += chr (ord (data [x ]) ^ ord (repeated_key [x ]))
68
- if is_printable (decrypted_text , ignore_code ):
84
+ if is_printable (decrypted_text , ignore_code ): # Is the whole result printable?
69
85
print ("Key length: " + str (len (expanded_key )), "\n Partial Key: " + expanded_key , "\n Plaintext: " + decrypted_text )
70
86
print ("" )
71
87
except KeyboardInterrupt :
0 commit comments