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

来源:互联网 发布:淘宝店铺装修软件大全 编辑:程序博客网 时间:2024/05/01 03:19

 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)

原创粉丝点击