Browse Source

ethernet over spam draft

seba 8 years ago
parent
commit
5cbd187da3
5 changed files with 763 additions and 0 deletions
  1. 7
    0
      TODO
  2. 1
    0
      tunnel/spam/.gitignore
  3. 80
    0
      tunnel/spam/conf.sample.py
  4. 234
    0
      tunnel/spam/spam.py
  5. 441
    0
      tunnel/spam/wtbase.py

+ 7
- 0
TODO View File

@@ -0,0 +1,7 @@
1
+TODO
2
+ - 00 80 at beginning of every packet - what is this?
3
+ - writing random data to dev generates OSError - handle this
4
+
5
+FEATURES
6
+ - implement configurable packet aggregation
7
+ - if possible and useful: helper to replace ethernet

+ 1
- 0
tunnel/spam/.gitignore View File

@@ -0,0 +1 @@
1
+conf.py

+ 80
- 0
tunnel/spam/conf.sample.py View File

@@ -0,0 +1,80 @@
1
+import os
2
+
3
+ENCRYPTION_NONE, ENCRYPTION_STARTTLS, ENCRYPTION_SSL = range(3)
4
+
5
+Conf = {
6
+	# ======== network settings ========
7
+	# select the kind of tunnel
8
+	# True: tap-device, tunneling ethernet
9
+	# False: tun-device, tunneling ip
10
+	'tunnelEthernet': True,
11
+	
12
+	# ipsettings for the device
13
+	'network':
14
+		{
15
+			'address':	'10.10.10.3',
16
+			'netmask':	'255.255.255.0',
17
+			#gateway:	'',
18
+			'mtu':		1400,
19
+		},
20
+	
21
+	# ========  mail settings   ========
22
+	# mail adress to which all packets are sent
23
+	'mailTo': '',
24
+	
25
+	# mail address to send mail from (only for the mailheader / display)
26
+	'mailFrom': '',
27
+	
28
+	# extra address to handle broadcast packets
29
+	# set to None if you want broadcasts also handled by mailTo addr
30
+	# ATM this is not implemented
31
+	'broadcastTo': None,
32
+	
33
+	# smtp data for outgoing packets
34
+	'smtp':
35
+		{
36
+			'server':			'',
37
+			'crypto':			ENCRYPTION_SSL,
38
+			'authentication':	True,
39
+			'user':				'',
40
+			'password':			'',
41
+		},
42
+	
43
+	# smtpd - a small mailserver to handle incoming packets
44
+	'smtpd':
45
+		{
46
+			'enabled':			False,
47
+			'listen':			('0.0.0.0', 25),
48
+		},
49
+	
50
+	# imap - get incoming data from an imap server
51
+	'imap':
52
+		{
53
+			'enabled':			False,
54
+			'server':			'',
55
+			'crypto':			ENCRYPTION_SSL,
56
+			'authentication':	True,
57
+			'user':				'',
58
+			'password':			'',
59
+			'folder':			'INBOX',
60
+			'deleteAfterwards':	True,
61
+			# intervals to wait when fetching mail fails
62
+			# afterwards the fetcher will go into IMAPv4 IDLE mode
63
+			'mailWait':			[0.25, 0.5, 0.75],
64
+			'useIDLE':			True
65
+		},
66
+	
67
+	# mail handler configuration
68
+	# this is the part which handles incoming mail delivered by imap or smtpd
69
+	'handler':
70
+		{
71
+			# list of all allowed senders, None for everyone
72
+			# e.g. ["foo@somedomain.de", "bar@someserver.de"]
73
+			'allowFrom':		None,
74
+			
75
+			# list of all allowed recipients, None for everyone
76
+			# e.g. ["foo@somedomain.de", "bar@someserver.de"]
77
+			'allowTo':			None,
78
+		},
79
+}
80
+

+ 234
- 0
tunnel/spam/spam.py View File

@@ -0,0 +1,234 @@
1
+#!/usr/bin/python
2
+
3
+import time
4
+import asyncore
5
+import select
6
+import smtpd
7
+import smtplib
8
+import imaplib
9
+import email
10
+from email.mime.text import MIMEText
11
+import threading
12
+import sys
13
+sys.path.append("../../../")
14
+
15
+from wtbase import SpamGenerator, DecodingException
16
+from ether2any import Ether2Any
17
+from ether2any.helper import getDstMacFromPkt, isBroadcast, binToHexStr
18
+from conf import Conf, ENCRYPTION_NONE, ENCRYPTION_STARTTLS, ENCRYPTION_SSL
19
+
20
+# Todo
21
+#	Error checking at some places
22
+#	Check for closed imap/smtp connections
23
+
24
+class NetMailHandler():
25
+	devWriteMutex = threading.Lock()
26
+	""" Parse, decode and write incoming mails to output. """
27
+	def __init__(self, dev, allowFrom, allowTo):
28
+		self.dev = dev
29
+		self.allowFrom = allowFrom
30
+		self.allowTo = allowTo
31
+		self.generator = SpamGenerator()
32
+	
33
+	def receiveMail(self, mailfrom, mailto, data):
34
+		# FIXME: mailfrom can be None
35
+		mail = email.email.message_from_string(data)
36
+		
37
+		# try to harvest text/plain part of mail
38
+		if mail.get_content_type() == "text/plain":
39
+			self._handleText(mail.get_payload())
40
+		elif mail.get_content_type() == "multipart/alternative":
41
+			for msg in mail.get_payload():
42
+				if msg.get_content_type() == "text/plain":
43
+					self.handleText(msg.get_payload())
44
+		elif self.allowFrom == None and self.allowTo == None:
45
+			self._handleText(data)
46
+		else:
47
+			pass
48
+	
49
+	def _handleText(self, text):
50
+		data = None
51
+		# FIXME: Where do these "\n " or 0a 20 come from?
52
+		#        Seem to occure only when smtplib sends (long) mails to smtpd
53
+		text = text.replace("\n ", "")
54
+		try:
55
+			data = self.generator.decode(text)
56
+		except DecodingException, e:
57
+			print "Error: Could not decode text! See error below"
58
+			print " < ----------- 8< ----------- > "
59
+			print e
60
+			print " < ----------- 8< ----------- > "
61
+		if data:
62
+			self.devWriteMutex.acquire()
63
+			self.dev.write(data)
64
+			self.devWriteMutex.release()
65
+
66
+class SimpleSMTPServer(smtpd.SMTPServer):
67
+	""" Simple small SMTP Server, gives mails to a handler. """
68
+	def __init__(self, handler, *args, **kwargs):
69
+		smtpd.SMTPServer.__init__(self, *args, **kwargs)
70
+		self._handler = handler
71
+	def process_message(self, peer, mailfrom, mailto, data):
72
+		# give mail to handler
73
+		self._handler.receiveMail(mailfrom, mailto, data)
74
+
75
+class SMTPServerThread(threading.Thread):
76
+	def __init__(self, listen, handler):
77
+		threading.Thread.__init__(self)
78
+		self.server = SimpleSMTPServer(handler, listen, None)
79
+	
80
+	def run(self):
81
+		asyncore.loop()
82
+
83
+class SimpleIMAPClient(threading.Thread):
84
+	def __init__(self, imapConf, mailTo, handler):
85
+		threading.Thread.__init__(self)
86
+		self.imapConf = imapConf
87
+		self.imap = None
88
+		self.quit = False
89
+		self.mailTo = mailTo
90
+		self.handler = handler
91
+		
92
+		self.idleTagNum = 0
93
+	
94
+	def connect(self):
95
+		if self.imapConf['crypto'] == ENCRYPTION_SSL:
96
+			self.imap = imaplib.IMAP4_SSL(self.imapConf['server'], 993)
97
+		else:
98
+			self.smtp = imaplib.IMAP4(self.imapConf['server'])
99
+		
100
+		if self.imapConf['crypto'] == ENCRYPTION_STARTTLS:
101
+			self.imap.starttls()
102
+		
103
+		if self.imapConf['authentication']:
104
+			self.imap.login(self.imapConf['user'], self.imapConf['password'])
105
+		
106
+		ret = self.imap.select(self.imapConf['folder'])
107
+		if ret[0] != 'OK':
108
+			print "Error!"
109
+	
110
+	def fetchNewMailToDev(self):
111
+		l = self.imap.search(None, 'UNSEEN')
112
+		newMsgIds = l[1][0].replace(" ", ",")
113
+		if newMsgIds == '':
114
+			return False
115
+		msgs = self.imap.fetch(newMsgIds, '(RFC822)')
116
+		for msg in msgs[1]:
117
+			if msg == ")":
118
+				# where does this come from...?
119
+				continue
120
+			if len(msg) != 2:
121
+				print "Warning: Message broken, %d values in list, text '%s'" % (len(msg), msg)
122
+				continue
123
+			(flags, data) = msg
124
+			self.handler.receiveMail(None, self.mailTo, data)
125
+		return (len(msgs) > 0)
126
+	
127
+	def run(self):
128
+		self.connect()
129
+		
130
+		while not self.quit:
131
+			print "New IMAP loop"
132
+			tries = 0
133
+			while tries < len(self.imapConf['mailWait']):
134
+				# get new mail
135
+				if self.fetchNewMailToDev():
136
+					tries = 0
137
+				else:
138
+					time.sleep(self.imapConf['mailWait'][tries])
139
+					tries += 1
140
+			# go into idle mode
141
+			# NOT IMPLEMENTED
142
+			if self.imapConf['useIDLE']:
143
+				print "Going into IDLE mode..."
144
+				self.idleTagNum += 1
145
+				idleTag = "a%04d" % self.idleTagNum
146
+				self.imap.send("%s IDLE\r\n" % (idleTag,))
147
+				quitLoop = False
148
+				while not quitLoop:
149
+					(r, w, e) = select.select([self.imap.socket()], [], [])
150
+					msg = self.imap.readline()
151
+					# TODO: Check if this filters out all idle "no new message" status msgs
152
+					if not msg.startswith("+") and not msg.startswith("* OK"):
153
+					#if msg.find("RECENT") >= 0
154
+						quitLoop = True
155
+				self.imap.send("DONE\r\n")
156
+				# clear away ack msg
157
+				msg = self.imap.readline()
158
+				while msg.find(idleTag) < 0:
159
+					msg = self.imap.readline()
160
+
161
+class MailTunnel(Ether2Any):
162
+	def __init__(self):
163
+		Ether2Any.__init__(self, tap=Conf.get('tunnelEthernet', True))
164
+		
165
+		handlerConf = Conf.get('handler', {'allowFrom': None, 'allowTo': None})
166
+		self.mailHandler = NetMailHandler(self.dev, **handlerConf)
167
+		self.mailTo = Conf.get('mailTo', None)
168
+		self.mailFrom = Conf.get('mailFrom', None)
169
+		
170
+		self.smtpConf = Conf.get('smtp')
171
+		smtpd = Conf.get("smtpd", {'enabled': False})
172
+		if smtpd['enabled']:
173
+			self.smtpd = SMTPServerThread(smtpd['listen'], self.mailHandler)
174
+		else:
175
+			self.smtpd = None
176
+		
177
+		imapConf = Conf.get("imap", {'enabled': False})
178
+		if imapConf['enabled']:
179
+			self.imap = SimpleIMAPClient(imapConf, self.mailTo, self.mailHandler)
180
+		else:
181
+			self.imap = None
182
+		
183
+		self.generator = SpamGenerator()
184
+		
185
+		network = Conf.get('network', {'mtu': 1400})
186
+		self.dev.ifconfig(**network)
187
+	
188
+	def connectSMTP(self):
189
+		if self.smtpConf['crypto'] == ENCRYPTION_SSL:
190
+			self.smtp = smtplib.SMTP_SSL(self.smtpConf['server'], 465)
191
+		else:
192
+			self.smtp = smtplib.SMTP(self.smtpConf['server'])
193
+		
194
+		if self.smtpConf['crypto'] == ENCRYPTION_STARTTLS:
195
+			self.smtp.starttls()
196
+		
197
+		if self.smtpConf['authentication']:
198
+			self.smtp.login(self.smtpConf['user'], self.smtpConf['password'])
199
+	
200
+	def sendMail(self, fromAddr, toAddrs, subject, msg):
201
+		e = MIMEText(msg)
202
+		e['Subject'] = subject
203
+		e['From'] = fromAddr
204
+		e['To'] = ",\n".join(toAddrs)
205
+		try:
206
+			self.smtp.sendmail(fromAddr, toAddrs, e.as_string())
207
+		except smtplib.SMTPServerDisconnected:
208
+			self.connectSMTP()
209
+			self.smtp.sendmail(fromAddr, toAddrs, e.as_string())
210
+	
211
+	def sendToNet(self, packet):
212
+		data = self.generator.encode(packet)
213
+		self.sendMail(self.mailFrom, [self.mailTo], "Ohai!", data) 
214
+	
215
+	def sendToDev(self, socket):
216
+		pass
217
+	
218
+	def run(self):
219
+		# start threads / connections
220
+		self.connectSMTP()
221
+		
222
+		if self.imap:
223
+			self.imap.start()
224
+
225
+		if self.smtpd:
226
+			self.smtpd.start()
227
+		
228
+		# call super method
229
+		Ether2Any.run(self)
230
+
231
+if __name__ == '__main__':
232
+	mailtun = MailTunnel()
233
+	mailtun.run()
234
+

+ 441
- 0
tunnel/spam/wtbase.py View File

@@ -0,0 +1,441 @@
1
+#!/usr/bin/python
2
+
3
+""" wtbase - what the base
4
+Provides classes to encode text to arbitrary bases (base 2^n supported) and
5
+then to textual forms. """
6
+
7
+import math
8
+import random
9
+
10
+class DecodingException(Exception):
11
+	pass
12
+
13
+class Token():
14
+	""" A Token contains a word/sentence and a list of tokenlists which can follow the word. """
15
+	def __init__(self, word, nextLists):
16
+		self.word = word
17
+		self.nextLists = nextLists
18
+	
19
+	def __str__(self):
20
+		return "Token: word \"%s\"" % self.word
21
+
22
+class TextGenerator():
23
+	""" Basis generator to en- and decode text-bits. """
24
+	def __init__(self, base=16):
25
+		self.lists = {}
26
+		self.base = base
27
+		self.baseBit = math.log(base, 2)
28
+		if self.baseBit != int(self.baseBit):
29
+			raise ValueError("base must be a power of 2")
30
+		self.baseBit = int(self.baseBit)
31
+		self.startList = "initial"
32
+	
33
+	def addList(self, identifier, newList):
34
+		self.lists[identifier] = newList
35
+	
36
+	def getList(self, identifier):
37
+		return self.lists.get(identifier, None)
38
+	
39
+	def convToBits(self, data):
40
+		""" Converts a data string into n-bit parts defined by self.base. 
41
+		
42
+		Returns a list of integers. """
43
+		l = []
44
+		bit = 0
45
+		rest = 0
46
+		for c in data:
47
+			n = ord(c)
48
+			if bit != 0:
49
+				nc = rest | ((n & ((1 << bit) - 1 << 8-bit)) >> 8-bit)
50
+				l.append(nc)
51
+			while 8-bit >= self.baseBit:
52
+				nc = (n & (((1 << self.baseBit)-1) << 8-bit-self.baseBit)) >> (8-bit-self.baseBit)
53
+				l.append(nc)
54
+				bit += self.baseBit
55
+			rest = (n & (1 << 8-bit) - 1) << self.baseBit-(8-bit)
56
+			bit = (bit+self.baseBit) % 8 % self.baseBit
57
+		if bit != 0:
58
+			l.append(rest)
59
+		return l
60
+	
61
+	def convToNums(self, data):
62
+		""" Reassemble a list of bits back to the original data bytestring. """
63
+		l = ""
64
+		w = 0
65
+		rest = 0
66
+		bit = 0
67
+		for n in data:
68
+			if bit+self.baseBit >= 8:
69
+				w |= n >> self.baseBit - (8-bit)
70
+				bit = self.baseBit - (8-bit)
71
+				l += chr(w)
72
+				if bit != 0:
73
+					w = (n & (1 << bit) - 1) << 8 - bit
74
+				else:
75
+					w = 0
76
+			else:
77
+				w |= n << 8-bit-self.baseBit
78
+				bit = (bit + self.baseBit) % 8
79
+				if bit == 0:
80
+					l += chr(w)
81
+					w = 0
82
+		return l
83
+	
84
+	#def convTo4Bits(self, data):
85
+	#	l = []
86
+	#	for c in data:
87
+	#		n = ord(c)
88
+	#		lo = n & ((1 << 4)-1)
89
+	#		hi = (n & (((1 << 4)-1) << 4)) >> 4
90
+	#		l.extend([hi, lo])
91
+	#	return l
92
+
93
+
94
+class SpamGenerator(TextGenerator):
95
+	""" De- and encode data in base8 spam text. """
96
+	def __init__(self):
97
+		TextGenerator.__init__(self, base=8)
98
+		self.startList = "greeting"
99
+		self.addList("greeting",
100
+		 {
101
+			0:  Token("Hi,\n\n", ["start"]),
102
+			1:  Token("Hey,\n\n", ["start"]),
103
+			2:  Token("Greetings,\n\n", ["start"]),
104
+			3:  Token("Dear Mr. or Mrs.,,\n\n", ["start"]),
105
+			4:  Token("SPECIAL OFFER!\n", ["start"]),
106
+			5:  Token("High Quality! Read on!\n", ["start"]),
107
+			6:  Token("Best buy!\n\n", ["start"]),
108
+			7:  Token("Dear Valued Customer,\n\n", ["start"]),
109
+			8:  Token("Well, uhm, ", ["start"]),
110
+		 })
111
+		self.addList("start",
112
+		 {
113
+			0:  Token("we are happy to ",		["inform_them"]),
114
+			1:  Token("we are glad to ",		["inform_them"]),
115
+			2:  Token("we gladly ", 			["inform_them"]),
116
+			3:  Token("it happens that we can ",["inform_them"]),
117
+			4:  Token("we want to ", 			["inform_them"]),
118
+			5:  Token("today ", 				["you_have"]),
119
+			6:  Token("ITS TRUE! ", 			["you_have"]),
120
+			7:  Token("you won! ", 				["you_have"]),
121
+			8:  Token("awesome for you, buddy! ",["leaving"]),
122
+		 })
123
+		self.addList("inform_them",
124
+		 {
125
+			0:  Token("inform you, that ",			["you_have"]),
126
+			1:  Token("make a remarkt, that ",		["you_have"]),
127
+			2:  Token("announce, that ", 			["you_have"]),
128
+			3:  Token("celebrate with you! ",		["you_have"]),
129
+			4:  Token("congratulate you, because ",	["you_have"]),
130
+			5:  Token("take the extra step: ",		["you_have"]),
131
+			6:  Token("tell you, that ", 			["you_have"]),
132
+			7:  Token("don't forget about you, ",	["you_have"]),
133
+			8:  Token("move property! ",			["you_have"]),
134
+		 })
135
+		
136
+		self.addList("you_have",
137
+		 {
138
+			0:  Token("you won ",								["won_item"]),
139
+			1:  Token("you have won ",							["won_item"]),
140
+			2:  Token("you aqcuired ", 							["won_item"]),
141
+			3:  Token("one time offer only: ",				 	["won_item"]),
142
+			4:  Token("at your account we found ",				["won_item"]),
143
+			5:  Token("the prince of nigeria offers to you ",	["won_item"]),
144
+			6:  Token("off shore accounts brought you ",		["won_item"]),
145
+			7:  Token("insider traging brought you ", 			["won_item"]),
146
+			8:  Token("you managed to get", 					["won_item"]),
147
+		 })
148
+		
149
+		self.addList("won_item",
150
+		 {
151
+			0:  Token("a sum of ",								["money_sum"]),
152
+			1:  Token("the priceless diamond of Zalanda. " ,	["claim"]),
153
+			2:  Token("free viagra! ", 							["claim"]),
154
+			3:  Token("an inheritance of ", 					["money_sum"]),
155
+			4:  Token("the opportunity to make money online! ", ["claim"]),
156
+			5:  Token("a part of an oil pipe line, worth ", 	["money_sum"]),
157
+			6:  Token("free money - ",							["money_sum"]),
158
+			7:  Token("a rare antique item worth", 				["money_sum"]),
159
+			8:  Token("quiet a bit o' stuff. ", 				["claim"]),
160
+		 })
161
+		
162
+	
163
+		self.addList("money_sum",
164
+		 {
165
+			0:  Token( "5,000,000 USD. ", ["claim"]),
166
+			1:  Token("10,000,000 USD. ", ["claim"]),
167
+			2:  Token(   "300,000 USD. ", ["claim"]),
168
+			3:  Token("13,412,573 USD. ", ["claim"]),
169
+			4:  Token( "7,555,530 USD. ", ["claim"]),
170
+			5:  Token(    "50,000 USD. ", ["claim"]),
171
+			6:  Token( "4,500,000 USD. ", ["claim"]),
172
+			7:  Token("42,000,000 USD. ", ["claim"]),
173
+			8:  Token("87,000,000 USD. ", ["claim"]),
174
+		 })
175
+		
176
+		self.addList("claim",
177
+		 {
178
+			0:  Token("To claim ",			["claimable_item"]),
179
+			1:  Token("To get hold ",		["claimable_item"]),
180
+			2:  Token("To acquire ",		["claimable_item"]),
181
+			3:  Token("To receive ", 		["claimable_item"]),
182
+			4:  Token("To obtain ", 		["claimable_item"]),
183
+			5:  Token("To gatherh ", 		["claimable_item"]),
184
+			6:  Token("To take ownership ", ["claimable_item"]),
185
+			7:  Token("To collect ",		["claimable_item"]),
186
+			8:  Token("To finally get ", 	["claimable_item"]),
187
+		 })
188
+			
189
+		self.addList("claimable_item",
190
+		 {
191
+			0:  Token("this item, please send ",			["sendables"]),
192
+			1:  Token("this stuff, please send ",			["sendables"]),
193
+			2:  Token("your profit, please send ",			["sendables"]),
194
+			3:  Token("these assets, please send ",			["sendables"]),
195
+			4:  Token("this price, please send ",			["sendables"]),
196
+			5:  Token("your earnings, please send ",		["sendables"]),
197
+			6:  Token("this top-line profit, please send ",	["sendables"]),
198
+			7:  Token("this treasure, please send ",		["sendables"]),
199
+			8:  Token("this your winnings, please send ",	["sendables"]),
200
+		 })
201
+			
202
+		self.addList("sendables",
203
+		 {
204
+			0:  Token("us all your information.\n\n",		["more_stuff", "jibberjabber_start"]),
205
+			1:  Token("us your account data.\n\n",			["more_stuff", "jibberjabber_start"]),
206
+			2:  Token("us a transfer-free of 50 USD.\n\n",	["more_stuff", "jibberjabber_start"]),
207
+			3:  Token("us a list of your passwords.\n\n", 	["more_stuff", "jibberjabber_start"]),
208
+			4:  Token("10 valid TAN Numbers.\n\n", 			["more_stuff", "jibberjabber_start"]),
209
+			5:  Token("us your mothers maiden name.\n\n", 	["more_stuff", "jibberjabber_start"]),
210
+			6:  Token("your birth certificate.\n\n", 		["more_stuff", "jibberjabber_start"]),
211
+			7:  Token("a listing of your incomes.\n\n", 	["more_stuff", "jibberjabber_start"]),
212
+			8:  Token("us your personal information.\n\n",	["jibberjabber_start", "leaving"]),
213
+		 })
214
+
215
+		self.addList("more_stuff",
216
+		 {
217
+			0:  Token("But wait, there is more! ",								["you_have"]),
218
+			1:  Token("But that is not all! ",									["you_have"]),
219
+			2:  Token("And there is even more! ",								["you_have"]),
220
+			3:  Token("Also ",													["you_have"]),
221
+			4:  Token("And because you seem to be the luckiest person alive: ",	["you_have"]),
222
+			5:  Token("And how does this sound: ",								["you_have"]),
223
+			6:  Token("In addition ",											["you_have"]),
224
+			7:  Token("But... what is this? ",									["you_have"]),
225
+			8:  Token("AND! ",													["you_have"]),
226
+		 })
227
+		
228
+		# loop this. random conversation starter
229
+		self.addList("jibberjabber_start",
230
+		 {
231
+			0:  Token("Would you ",						["jj_consider"]), # have you <tought> <get/buy> <stuff>
232
+			1:  Token("Will you ",						["jj_consider"]),
233
+			2:  Token("Did you ever ",					["jj_consider"]),
234
+			3:  Token("Maybe you ",						["jj_consider"]),
235
+			4:  Token("In ",							["jj_times"]), # in <time> there is <stuff>
236
+			5:  Token("At ",							["jj_times"]),
237
+			6:  Token("Living in ",						["jj_times"]),
238
+			7:  Token("Considering ",					["jj_times"]),
239
+			8:  Token("Everything will be better!",		["leaving"]),
240
+		 })
241
+		
242
+		self.addList("jj_times",
243
+		 {
244
+			0:  Token("times like these ",				["jj_whattodo"]),
245
+			1:  Token("the age of the internet ",		["jj_whattodo"]),
246
+			2:  Token("mobile times ",					["jj_whattodo"]),
247
+			3:  Token("this economic crisis ",			["jj_whattodo"]),
248
+			4:  Token("the time of globalisation ",		["jj_whattodo"]),
249
+			5:  Token("the age of the global village ",	["jj_whattodo"]),
250
+			6:  Token("a world of networks ",			["jj_whattodo"]),
251
+			7:  Token("times of moral values ",			["jj_whattodo"]),
252
+			8:  Token("the here and now ",				["jj_whattodo"]),
253
+		 })
254
+		
255
+		self.addList("jj_consider",
256
+		 {
257
+			0:  Token("consider ",				["jj_buyverb"]),
258
+			1:  Token("think about ",			["jj_buyverb"]),
259
+			2:  Token("take into account ",		["jj_buyverb"]),
260
+			3:  Token("have the desire for ",	["jj_buyverb"]),
261
+			4:  Token("evaluate ",				["jj_buyverb"]),
262
+			5:  Token("reason about ",			["jj_buyverb"]),
263
+			6:  Token("keep in mind ",			["jj_buyverb"]),
264
+			7:  Token("suggest ",				["jj_buyverb"]),
265
+			8:  Token("imagine ",				["jj_buyverb"]),
266
+		 })
267
+		
268
+		self.addList("jj_buyverb",
269
+		 {
270
+			0:  Token("buying ",			["jj_buynoun"]),
271
+			1:  Token("obtaining ",			["jj_buynoun"]),
272
+			2:  Token("purchasing ",		["jj_buynoun"]),
273
+			3:  Token("posessing ",			["jj_buynoun"]),
274
+			4:  Token("owning ",			["jj_buynoun"]),
275
+			5:  Token("creating ",			["jj_buynoun"]),
276
+			6:  Token("crafting ",			["jj_buynoun"]),
277
+			7:  Token("receiving ",			["jj_buynoun"]),
278
+			8:  Token("getting ",			["jj_buynoun"]),
279
+		 })
280
+		
281
+		self.addList("jj_buynoun",
282
+		 {
283
+			0:  Token("a new car? ",						["jibberjabber_start"]),
284
+			1:  Token("an own house? ",						["jibberjabber_start"]),
285
+			2:  Token("the women of your dreams? ",			["jibberjabber_start"]),
286
+			3:  Token("a healthy sexual relationship? ",	["jibberjabber_start"]),
287
+			4:  Token("an own country? ",					["jibberjabber_start"]),
288
+			5:  Token("your penis size? ",					["jibberjabber_start"]),
289
+			6:  Token("free viagra? ",						["jibberjabber_start"]),
290
+			7:  Token("the newest of apples products? ",	["jibberjabber_start"]),
291
+			8:  Token("a brand new kitchentable? ",			["jibberjabber_start", "leaving"]),
292
+		 })
293
+		
294
+		self.addList("jj_whattodo",
295
+		 {
296
+			0:  Token("you should always think about ",					["jj_whattodonoun"]),
297
+			1:  Token("the moral values predict good values for  ",		["jj_whattodonoun"]),
298
+			2:  Token("society will talk about ",						["jj_whattodonoun"]),
299
+			3:  Token("all your friends will admire ",					["jj_whattodonoun"]),
300
+			4:  Token("the talk of your social group will be ",			["jj_whattodonoun"]),
301
+			5:  Token("genderstudies will celebrate ",					["jj_whattodonoun"]),
302
+			6:  Token("considering everything about ",					["jj_whattodonoun"]),
303
+			7:  Token("your possibilities are unimaginable regarding ",	["jj_whattodonoun"]),
304
+			8:  Token("things are looking good regarding",				["jj_whattodonoun"]),
305
+		 })
306
+		
307
+		self.addList("jj_whattodonoun",
308
+		 {
309
+			0:  Token("the stock market. ",										["jibberjabber_start"]),
310
+			1:  Token("your penis size. ",										["jibberjabber_start"]),
311
+			2:  Token("how attractive you are to the opposite sex. ",			["jibberjabber_start"]),
312
+			3:  Token("your investment in foreign oil company funds. ",			["jibberjabber_start"]),
313
+			4:  Token("mobility options for going into the mobile business. ",	["jibberjabber_start"]),
314
+			5:  Token("a bottle from our best collection of tasteful wines. ",	["jibberjabber_start"]),
315
+			6:  Token("buying viagra online NOW! ",								["jibberjabber_start"]),
316
+			7:  Token("getting more money out of your job! ",					["jibberjabber_start"]),
317
+			8:  Token("winning money in Las Vegas! ",							["leaving"]),
318
+		 })
319
+		# HACK: At the moment this does not support choosing random words
320
+		self.addList("leaving",
321
+		 {
322
+			0:  Token(None,			[None]),
323
+			1:  Token(None,			[None]),
324
+			2:  Token(None,			[None]),
325
+			3:  Token(None,			[None]),
326
+			4:  Token(None,			[None]),
327
+			5:  Token(None,			[None]),
328
+			6:  Token(None,			[None]),
329
+			7:  Token(None,			[None]),
330
+			8:  Token("\n\nYours faithfully,\n\n",	["leave_name_%d" % i for i in range(8)]),
331
+		 })
332
+		for i in zip(range(8), ["Ernest Schlempl", "Bernhard Vonneguth", "Maria Peters", "Sibille Harstall", "Richmond Maltitz", "Benno Boch", "Tatjana Horn", "Marcell Hintzenstern"]):
333
+			self.addList("leave_name_%d" % i[0], {8: Token(i[1], [None])}) 
334
+		
335
+		self.addList("m",
336
+		 {
337
+			0:  Token("",			[None]),
338
+			1:  Token("",			[None]),
339
+			2:  Token("",			[None]),
340
+			3:  Token("",			[None]),
341
+			4:  Token("",			[None]),
342
+			5:  Token("",			[None]),
343
+			6:  Token("",			[None]),
344
+			7:  Token("",			[None]),
345
+			8:  Token("",			[None]),
346
+		 })
347
+	
348
+	def encode(self, data):
349
+		""" Encode data: Traverse wordlists. Return spam-text. """
350
+		listBits = self.convToBits(data)
351
+		nextList = self.startList
352
+		pos = 0
353
+		text = ""
354
+		while nextList:
355
+			bit = 8
356
+			if pos < len(listBits):
357
+				bit = listBits[pos]
358
+			#print "Pos:", pos, "- Entering list", nextList, "- Bit:", bit
359
+			l = self.getList(nextList)
360
+			idx = pos < len(listBits) and listBits[pos] or self.base
361
+			t = l[bit]
362
+			text += t.word
363
+			nextList = t.nextLists[random.randint(0, len(t.nextLists)-1)]
364
+			if bit != 8:
365
+				pos += 1
366
+		return text
367
+	
368
+	def hexdump(self, data):
369
+		return ((len(data)*"%02x ") % tuple(map(lambda x: ord(x), data))).rstrip()
370
+	
371
+	def decode(self, text):
372
+		""" Decode spam-text to original data. """
373
+		text = text.lstrip().replace("\r\n", "\n")
374
+		nextLists = [self.startList]
375
+		result = []
376
+		while len(text) > 0 and len(nextLists) > 0 and nextLists[0]:
377
+			match = False
378
+			for listname in nextLists:
379
+				(match, bits, token) = self.findInList(text, listname)
380
+				if match:
381
+					#print "matched value ", bits, "word", token.word
382
+					if bits != 8:
383
+						result.append(bits)
384
+					text = text.replace(token.word, "", 1)
385
+					nextLists = token.nextLists
386
+					#print "next possible lists are", nextLists
387
+					break
388
+			if not match:
389
+				#print "BASEWTF"
390
+				#print nextLists
391
+				#print self.getList(nextLists[0])
392
+				#print self.hexdump(text)
393
+				#print " --------------- "
394
+				print "Beginning of text (hex): ", self.hexdump(text[:10])
395
+				for l in self.getList(nextLists[0]):
396
+					print l, self.getList(nextLists[0])[l], self.hexdump(self.getList(nextLists[0])[l].word)
397
+				raise DecodingException("Could not decode text (no more possible lists). Remaining text is \"%s\"" % text)
398
+		# print "text remaining", text
399
+		convBack = self.convToNums(result)		
400
+		return convBack
401
+	
402
+	def findInList(self, text, listname):
403
+		sList = self.getList(listname)
404
+		for key in sList:
405
+			#print "\tTesting word: ", sList[key].word
406
+			if sList[key].word and (text.startswith(sList[key].word) or \
407
+				text.replace("\n", "").startswith(sList[key].word.replace("\n", ""))):
408
+				# HACK: Newline matching problem, mail classes add extra newlines
409
+				#       ==> matching not possible
410
+				token = sList[key]
411
+				#print "\t==> MATCH in list", listname
412
+				return (True, key, token)
413
+		#print "\tNO match in list", listname
414
+		return (False, -1, None)
415
+
416
+def main():
417
+	""" Main function, does en- and decoding test for testing purposes. """
418
+	data = "\xF2\x51\x92\x61\x9d\x1f\x0F\xb7\xaa\xc1"
419
+	for d in data:
420
+		print ord(d),
421
+	print ""
422
+	t = SpamGenerator()
423
+	d = t.convToBits(data)
424
+	e = t.convToNums(d)
425
+	print d
426
+	for a in e:
427
+		print ord(a),
428
+	print ""
429
+	msg = t.encode(data)
430
+	#print res, "\n"
431
+	res = t.decode(msg)
432
+	print 
433
+	print d
434
+	for d in data:
435
+		print ord(d),
436
+	print ""
437
+	print msg
438
+
439
+if __name__ == '__main__':
440
+	main()
441
+

Loading…
Cancel
Save