您的位置:首页 > 编程语言 > Python开发

支持msnp15协议python可发送离线消息脚本

2009-01-06 16:20 627 查看
from socket import socket, AF_INET, SOCK_STREAM
from sys import exit, stdin, stdout, argv
from os import popen3

import ssl as sslmod

import getpass,md5,thread,time,re

import struct
from base64 import standard_b64encode, standard_b64decode
from Crypto.Hash import HMAC, SHA
from Crypto.Cipher import DES3
from Crypto.Util import randpool

import xml.sax.saxutils as xmlmodule
import base64

import httplib, urllib

CRYPT_MODE_CBC = 1
CALC_3DES = 0x6603
CALG_SHA1 = 0x8004

class MSNPost(object):
#ticket = ''
def MSNPostInit(self,xml):
#params = urllib.urlencode(xml)
headers = {"Content-type": "application/x-www-form-urlencoded",
"Accept": "text/plain","Content-Length":str(len(xml))}
conn = httplib.HTTPSConnection("login.live.com:443")
conn.request("POST", "/RST.srf", xml, headers)
response = conn.getresponse()
self.data = response.read()
conn.close()
print self.data

def MSNPostInit2(self,xml):
self.sock = socket(AF_INET,SOCK_STREAM)
ssl_conn = sslmod.wrap_socket(self.sock)
ssl_conn.connect(("login.live.com",443))

buffer_passport = "POST /RST.srf HTTP/1.1/r/n"
buffer_passport = buffer_passport + "Host:login.live.com:80/r/n"
buffer_passport = buffer_passport + "Content-Type: application/x-www-form-urlencoded/r/n"
buffer_passport = buffer_passport + "Content-Length: " + str(len(xml)) + "/r/n"
buffer_passport = buffer_passport + "Connection: Close/r/n/r/n"
buffer_passport = buffer_passport + xml + "/r/n"

ssl_conn.write(buffer_passport)

data = ""
while True:
buf = ssl_conn.read(1024)
if not buf: break
data = data + buf
self.data = data
print self.data
print "data >>>>>/r/n"

self.sock.close()
def getData(self):
return self.data

class MSN(object):
server = "messenger.hotmail.com"
port = 1863

protocol = "MSNP15"
buildver = '8.1.0178'
login_method = 'SSO'

prod_key = 'PK}_A_0N_K%O?A9S'
prod_id = 'PROD0114ES4Z%Q5W'

font_fn = 'Arial'
font_co = '333333'
font_ef = ''

oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx'
oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2'

oim_try = 3

max_msn_message_len = 1664

passport_url = "https://login.live.com/RST.srf"

def initmsn(self):
self.client_id = 0x7000800C
self.passport_url = 'https://login.live.com/RST.srf'
self.buildver = '8.1.0178'
self.login_method = 'SSO'

def get_passport_ticket(self):
print "now get_passport_ticket!!!!!!!!!!!!"
user = self.user
password = self.password
passport_url = self.passport_url
#with open('xml.txt', 'r') as file:
input = open('./xml.txt','r')#see http://blog.csdn.net/itclock/archive/2009/01/06/3721745.aspx
xml = input.read()
xml2 = xml.replace('<user>',user)
xml3 = xml2.replace('<password>',password)
xml = xml3.replace('<passport_policy>',self.passport_policy)
output = open('./post.xml','w')
output.write(xml)
msnpo = MSNPost()
msnpo.MSNPostInit(xml)
data = msnpo.getData()

output = open('./out.xml','w')
output.write(data)

aTickets = {'ticket': '', 'secret': '', 'web_ticket': '', 'contact_ticket': '', 'oim_ticket': '', 'space_ticket': '', 'storage_ticket': ''}
m = re.search(r'<wsse:BinarySecurityToken Id="Compact1">(.*?)</wsse:BinarySecurityToken>',data)
aTickets['ticket'] = xmlmodule.unescape(m.group(1))
m = re.search(r'<wsse:BinarySecurityToken Id="Compact1">(.*?)</wst:BinarySecret>',data)
secret = xmlmodule.unescape(m.group(1))
aTickets['secret'] = secret.split("<wst:BinarySecret>")[1]
m = re.search(r'<wsse:BinarySecurityToken Id="PPToken2">(.*?)</wsse:BinarySecurityToken>',data)
aTickets['web_ticket'] = xmlmodule.unescape(m.group(1))
m = re.search(r'<wsse:BinarySecurityToken Id="Compact3">(.*?)</wsse:BinarySecurityToken>',data)
aTickets['contact_ticket'] = xmlmodule.unescape(m.group(1))
m = re.search(r'<wsse:BinarySecurityToken Id="Compact4">(.*?)</wsse:BinarySecurityToken>',data)
aTickets['oim_ticket'] = xmlmodule.unescape(m.group(1))
m = re.search(r'<wsse:BinarySecurityToken Id="Compact5">(.*?)</wsse:BinarySecurityToken>',data)
aTickets['space_ticket'] = xmlmodule.unescape(m.group(1))
m = re.search(r'<wsse:BinarySecurityToken Id="Compact6">(.*?)</wsse:BinarySecurityToken>',data)
aTickets['storage_ticket'] = xmlmodule.unescape(m.group(1))

return aTickets

def connecta(self,user, password, redirect_server = "", redirect_port = 1863):
self.id = 1
if redirect_server == "":
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.connect((self.server, self.port))
else:
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.connect((redirect_server, redirect_port))

self.authed = False

self.writeln("VER " + str(self.id) + " " + self.protocol + " CVR0")

busr = False
strdata = ""

while True:
print 'circle'
data = self.readln()
if not data: break
if busr == True:
strdata = strdata + data
ndx = strdata.find("USR")
if ndx > -1:
strdata = data.split("USR")[1]
data = "USR" + strdata
busr = False

code = data[:3]
print code
if "VER" == code:
self.writeln("CVR " + str(self.id) + " 0x0409 winnt 5.1 i386 MSMSGS " + self.buildver + " msmsgs " + user)
elif "CVR" == code:
self.writeln("USR " + str(self.id) + " " + self.login_method + " I " + user)
elif "USR" == code:
print 'get usr'
if self.authed: return True

self.user = user
self.password = password

if self.protocol == "MSNP15":
print data
policy = data.split(" ")[4]
nonce = data.split(" ")[5]
self.passport_policy = policy
aTickets = self.get_passport_ticket()

ticket = aTickets['ticket']
secret = aTickets['secret']
self.oim_ticket = aTickets['oim_ticket']
self.contact_ticket = aTickets['contact_ticket']
self.web_ticket = aTickets['web_ticket']
self.space_ticket = aTickets['space_ticket']
self.storage_ticket = aTickets['storage_ticket']

login_code = self.generateLoginBLOB(secret, nonce)

senddata = "USR " + str(self.id) + " " + self.login_method + " S " + ticket + " " + login_code
print senddata

self.writeln(senddata)

self.authed = True

elif "XFR" == code:
server = data.split(" ")[3]
ip = server.split(":")[0]
port = server.split(":")[1]
self.sock.close()
self.sock = socket(AF_INET, SOCK_STREAM)
self.sock.connect((ip, int(port)))
self.writeln("VER " + str(self.id) + " " + self.protocol + " CVR0")
elif "GCF" == code:
strsize = data.split(" ")[2]
strsize = strsize[:4]
print 'strsize'
print strsize
#return False
if strsize.isdigit() :
size = int(strsize)
if size > 0 :
#strdata = self.readdata(size)
#strdata = strdata.split("USR")[1]
#usrdata = "USR " + strdata
busr = True
else:
if code.isdigit() :
self.writeln("OUT")
self.sock.close()
return False
return False
def derive_key(self,key, magic):
hash1 = HMAC.new(key, magic, SHA).digest()

hash2 = HMAC.new(key, hash1 + magic, SHA).digest()
hash3 = HMAC.new(key, hash1, SHA).digest()

hash4 = HMAC.new(key, hash3 + magic, SHA).digest()

return hash2 + hash4[0:4]
def generateLoginBLOB(self,key, nonce):
#
# Read key and generate two derived keys
#
nonce = nonce.replace("/r/n","")
print key
print '0'+(nonce)+'0'
print len(nonce)
key1 = standard_b64decode(key)
key2 = self.derive_key(key1, "WS-SecureConversationSESSION KEY HASH")
key3 = self.derive_key(key1, "WS-SecureConversationSESSION KEY ENCRYPTION")

#
# Create a HMAC-SHA-1 hash of nonce using key2
#

hash = HMAC.new(key2, nonce, SHA).digest()

#
# Encrypt nonce with DES3 using key3
#

# IV: 8 bytes of random data
iv = randpool.KeyboardRandomPool().get_bytes(8)
obj = DES3.new(key3, DES3.MODE_CBC, iv)

# XXX: win32's Crypt API seems to pad the input with 0x08 bytes to align on 72/36/18/9 boundary
ciph = obj.encrypt(nonce + "/x08/x08/x08/x08/x08/x08/x08/x08")

#
# Generate the blob
#

blob = struct.pack("<LLLLLLL", 28, CRYPT_MODE_CBC, CALC_3DES, CALG_SHA1,
len(iv), len(hash), len(ciph))
blob += iv + hash + ciph

return standard_b64encode(blob)

def sendMessage(self,sMessage,aTo):
bquit = False
online_cnt = 0
offline_cnt = 0
other_cnt = 0
while True:
if (bquit) :break
data = self.readln()
if not data: break
code = data[:3]
print 'code:'+code
if "SBS" == code:
self.writeln("CHG " + str(self.id) + " NLN")
elif "MSG" == code:
strsize = data.split(" ")[3]
if strsize.isdigit() :
size = int(strsize)
if size > 0 :
self.readdata(size)
elif "CHL" == code:
chl_code = data.split(" ")[2]
fingerprint = self.getChallenge(chl_code)
self.writeln("QRY " + str(self.id) + " " + self.prod_id + " 32")
self.writedata(fingerprint)
elif "SYN" == code:
if self.protocol == 'MSNP9':
self.writeln("CHG " + str(self.id) + " NLN")
else:
pass
elif "CHG" == code:
aMSNUsers = []
aOfflineUsers = []
aOtherUsers = []
nMSNUsers = 0
for sUser in aTo:
to_email = sUser
aMSNUsers.append(to_email)
nMSNUsers = nMSNUsers + 1
nCurrentUser = 0
self.writeln("XFR " + str(self.id) + " SB")
elif "XFR" == code:
server = data.split(" ")[3]
cki_code = data.split(" ")[5]
ip = server.split(":")[0]
port = server.split(":")[1]
bSBresult = self.switchboard_control(ip,int(port),cki_code,aMSNUsers[nCurrentUser],sMessage)
if bSBresult == False:
aOfflineUsers.append(aMSNUsers[nCurrentUser])
else:
online_cnt = online_cnt + 1
nCurrentUser = nCurrentUser + 1
if (nCurrentUser < nMSNUsers):
self.writeln("XFR " + str(self.id) + " SB")
continue
lockkey = ''
re_login = False
for to in aOfflineUsers:
offline_cnt = offline_cnt + 1
for i in range(0,self.oim_try):
oim_result = self.sendOIM1(to, sMessage, lockkey)
lockkey = self.getChallenge(oim_result)
oim_result = self.sendOIM(to, sMessage, lockkey)
if oim_result == True:
break
bquit = True
else:
if code.isdigit() :
pass
self.writeln("OUT")
self.sock.close()
def sendOIM1(self, to, sMessage, lockkey):
#with open('./xml2.txt', 'r') as file:
input = open('./xml2.txt','r')#see http://blog.csdn.net/itclock/archive/2009/01/06/3721772.aspx
xml = input.read()
xml = xml.replace('<user>',self.user)
xml = xml.replace('<base64_user>',base64.b64encode(self.user))
xml = xml.replace('<protocol>',self.protocol)
xml = xml.replace('<buildver>',self.buildver)
xml = xml.replace('<to>',to)
xml = xml.replace('<prod_id>',self.prod_id)
xml = xml.replace('<lockkey>',lockkey)
xml = xml.replace('<oim_ticket>',xmlmodule.escape(self.oim_ticket))
xml = xml.replace('<base64_sMessage>',base64.b64encode(sMessage))

#output = open('./out2.xml','w')
#output.write(xml)

headers = {"SOAPAction":self.oim_send_soap,"Content-type": "text/xml","Content-Length":str(len(xml)),
"User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger " + self.buildver + ")",
"Host":"ows.messenger.msn.com","Connection":"Keep-Alive","Cache-Control":"no-cache"}
conn = httplib.HTTPSConnection("ows.messenger.msn.com:443")
conn.request("POST", "/OimWS/oim.asmx", xml, headers)
response = conn.getresponse()
data = response.read()
conn.close()
print data

retcode = data.split("<LockKeyChallenge ")[1]
retcode = retcode.split("</LockKeyChallenge>")[0]
retcode = retcode.split(">")[1]

print retcode

return retcode
def sendOIM(self, to, sMessage, lockkey):
#with open('./xml2.txt', 'r') as file:
input = open('./xml2.txt','r')#see http://blog.csdn.net/itclock/archive/2009/01/06/3721772.aspx
xml = input.read()
xml = xml.replace('<user>',self.user)
xml = xml.replace('<base64_user>',base64.b64encode(self.user))
xml = xml.replace('<protocol>',self.protocol)
xml = xml.replace('<buildver>',self.buildver)
xml = xml.replace('<to>',to)
xml = xml.replace('<prod_id>',self.prod_id)
xml = xml.replace('<lockkey>',lockkey)
xml = xml.replace('<oim_ticket>',xmlmodule.escape(self.oim_ticket))
xml = xml.replace('<base64_sMessage>',base64.b64encode(sMessage))

#output = open('./out2.xml','w')
#output.write(xml)

headers = {"SOAPAction":self.oim_send_soap,"Content-type": "text/xml","Content-Length":str(len(xml)),
"User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger " + self.buildver + ")",
"Host":"ows.messenger.msn.com","Connection":"Keep-Alive","Cache-Control":"no-cache"}
conn = httplib.HTTPSConnection("ows.messenger.msn.com:443")
conn.request("POST", "/OimWS/oim.asmx", xml, headers)
response = conn.getresponse()
data = response.read()
conn.close()
print data

return True

def sendOIM2(self, to, sMessage, lockkey):
#with open('./xml2.txt', 'r') as file:
input = open('./xml2.txt','r')#see http://blog.csdn.net/itclock/archive/2009/01/06/3721772.aspx
xml = input.read()
xml = xml.replace('<user>',self.user)
xml = xml.replace('<base64_user>',base64.b64encode(self.user))
xml = xml.replace('<protocol>',self.protocol)
xml = xml.replace('<buildver>',self.buildver)
xml = xml.replace('<to>',to)
xml = xml.replace('<prod_id>',self.prod_id)
xml = xml.replace('<lockkey>',lockkey)
xml = xml.replace('<oim_ticket>',xmlmodule.escape(self.oim_ticket))
xml = xml.replace('<base64_sMessage>',base64.b64encode(sMessage))
oimsock = socket(AF_INET,SOCK_STREAM)
ssl_conn = sslmod.wrap_socket(oimsock)
ssl_conn.connect(("ows.messenger.msn.com",443))

buffer_passport = "POST /OimWS/oim.asmx HTTP/1.1/r/n"
buffer_passport = buffer_passport + "SOAPAction: " + self.oim_send_soap
buffer_passport = buffer_passport + "Content-Type: text/xml; charset=UTF-8/r/n"
buffer_passport = buffer_passport + "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; "
buffer_passport = buffer_passport + "Windows NT 5.1; SV1; Messenger " + self.buildver + ")/r/n"
buffer_passport = buffer_passport + "Host:ows.messenger.msn.com/r/n"
buffer_passport = buffer_passport + "Expect: 100-continue/r/n"
buffer_passport = buffer_passport + "Content-Length: " + str(len(xml)) + "/r/n"

buffer_passport = buffer_passport + xml + "/r/n"

ssl_conn.write(buffer_passport)

data = ""
while True:
buf = ssl_conn.read(1024)
if not buf: break
data = data + buf

print data
print "data >>>>>/r/n"

oimsock.close()

return True

def getMessage(self, sMessage, network = 1):
msg_header = "MIME-Version: 1.0/r/nContent-Type: text/plain; " + /
"charset=UTF-8/r/nX-MMS-IM-Format: FN=" + self.font_fn + /
"; EF=" + self.font_ef + "; CO=" + self.font_co + "; CS=0; PF=22/r/n/r/n"
msg_header_len = len(msg_header)
maxlen = self.max_msn_message_len - msg_header_len
aMessage = []
aStr = sMessage.split('/n')
cur_len = 0
msg = ''
add_crlf = False
for sstr in aStr:
sstr = sstr.replace('/r','')
nlen = len(sstr)
while nlen > maxlen:
if cur_len > 0:
aMessage.append(msg_header+msg)
cur_len = 0
msg = ''
add_crlf = False
aMessage.append(msg_header+sstr[:maxlen])
sstr = sstr[maxlen:]
nlen = len(sstr)
if (cur_len + nlen) > maxlen :
aMessage.append(msg_header+msg)
cur_len = 0
msg = ''
add_crlf = False
if (msg != '' or add_crlf) :
msg = msg + "/r/n"
cur_len = cur_len + 2
add_crlf = True
msg = msg + sstr
cur_len = cur_len + nlen
if cur_len != 0:
aMessage.append(msg_header+msg)
return aMessage
def switchboard_control(self,ip,port,cki_code,sTo, sMessage):
self.sbsock = socket(AF_INET, SOCK_STREAM)
self.sbsock.connect((ip, port))
user = sTo
self.sb_writeln("USR " + str(self.id) + " " + self.user + " " +cki_code)

sent = False
got_error = False
offline = False
while True:
if (sent) :break
if (offline) :break
data = self.sb_readln()
if not data: break
code = data[:3]
if "USR" == code:
self.sb_writeln("CAL " + str(self.id) + " " + user)
elif "CAL" == code:
pass
elif "217" == code:
offline = True
elif "JOI" == code:
aMessage = self.getMessage(sMessage)
for message in aMessage:
nlen = len(message)
self.sb_writeln("MSG 20 N " + str(nlen))
self.sb_writedata(message)
sent = True
else:
if code.isdigit() :
got_error = True
self.sb_writeln("OUT")
self.sbsock.close()
if (offline or got_error): return False
return True
def getChallenge(self,data):
#import struct
#import md5
def little_endify(value, c_type="L"):
"""Transform the given value into little endian"""
return struct.unpack(">" + c_type, struct.pack("<" + c_type, value))[0]

md5_digest = md5.md5(data + self.prod_key).digest()
# Make array of md5 string ints
md5_integers = struct.unpack("<llll", md5_digest)
md5_integers = [(x & 0x7fffffff) for x in md5_integers]
# Make array of chl string ints
data += self.prod_id
amount = 8 - len(data) % 8
data += "".zfill(amount)
chl_integers = struct.unpack("<%di" % (len(data)/4), data)
# Make the key
high = 0
low = 0
i = 0
magic_num = 0x0E79A9C1
while i < len(chl_integers) - 1:
temp = chl_integers[i]
temp = (magic_num * temp) % 0x7FFFFFFF
temp += high
temp = md5_integers[0] * temp + md5_integers[1]
temp = temp % 0x7FFFFFFF
high = chl_integers[i + 1]
high = (high + temp) % 0x7FFFFFFF
high = md5_integers[2] * high + md5_integers[3]
high = high % 0x7FFFFFFF
low = low + high + temp
i += 2
high = little_endify((high + md5_integers[1]) % 0x7FFFFFFF)
low = little_endify((low + md5_integers[3]) % 0x7FFFFFFF)
key = (high << 32L) + low
key = little_endify(key, "Q")
longs = [x for x in struct.unpack(">QQ", md5_digest)]
longs = [little_endify(x, "Q") for x in longs]
longs = [x ^ key for x in longs]
longs = [little_endify(abs(x), "Q") for x in longs]
out = ""
for value in longs:
value = hex(long(value))
value = value[2:-1]
value = value.zfill(16)
out += value.lower()
return out
def readdata(self,size):
data = ""
count = 0

while True:
buf = self.sock.recv(size - count)
if not buf: break
nlen = len(buf)
data = data + buf
count = count + nlen
if count >= size:
break
print size
print count
print data
return data

def readln(self):
#data = self.sock.recv(4096)
alldata = ''
data = None
while data != "/n":
data = self.sock.recv(1)
if not data:
break
alldata += data
print alldata
return alldata

def writeln(self,data):
self.sock.send(data + "/r/n")
self.id = self.id + 1
def writedata(self,data):
self.sock.send(data)
def sb_readdata(self,size):
data = ""
count = 0

while True:
buf = self.sbsock.recv(size - count)
if not buf: break
nlen = len(buf)
data = data + buf
count = count + nlen
if count >= size:
break
print data
return data

def sb_readln(self):
data = self.sbsock.recv(4096)
print data

return data

def sb_writeln(self,data):
self.sbsock.send(data + "/r/n")
self.id = self.id + 1
def sb_writedata(self,data):
self.sbsock.send(data)

msn1 = MSN()
msn1.initmsn()
bret = msn1.connecta('user@example.com','123456')
ato = []
ato.append('user@hotmail.com')
if bret == True:
print 'con ok'
msn1.sendMessage('hello',ato)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: