当前位置: 首页 >> 程序设计 >> 编程模拟实现MSN登陆过程(MSNP15)
 

编程模拟实现MSN登陆过程(MSNP15)

作者:      来源:zz     发表时间:2008-04-25     浏览次数:      字号:    

对应的,每一个RST都会返回一个这样的内容:

<wst:RequestSecurityTokenResponse>
  
<wst:TokenType>urn:passport:compact</wst:TokenType> 
<wsp:AppliesTo xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<wsa:EndpointReference>
  
<wsa:Address>messengerclear.live.com</wsa:Address> 
  
</wsa:EndpointReference>
  
</wsp:AppliesTo>
<wst:LifeTime>
  
<wsu:Created>2007-09-13T01:41:28Z</wsu:Created> 
  
<wsu:Expires>2007-09-13T09:41:28Z</wsu:Expires> 
  
</wst:LifeTime>
<wst:RequestedSecurityToken>
  
<wsse:BinarySecurityToken Id="Compact1">t=EwBIAswbAQAU...=&p=</wsse:BinarySecurityToken> 
  
</wst:RequestedSecurityToken>
<wst:RequestedTokenReference>
  
<wsse:KeyIdentifier ValueType="urn:passport:compact" /> 
  
<wsse:Reference URI="#Compact1" /> 
  
</wst:RequestedTokenReference>
<wst:RequestedProofToken>
  
<wst:BinarySecret>nQxBa/vqQ7rC/4lCKemlPPGYwiIf5sQk</wst:BinarySecret> 
  
</wst:RequestedProofToken>
  
</wst:RequestSecurityTokenResponse>


其中的BinarySecurityToken就是这个服务的ticket了,就是"t="后面的那一串。我们要实现msn的登陆过程,则上面内容中的BinarySecret也是需要用到的。

好,现在我们已经获得登陆必需的ticket和BinarySecret了,可以连接NS服务器了。

通信过程如下
VER 4 MSNP15 CVR0
VER 4 MSNP15 CVR0
CVR 5 0x0804 winnt 5.1 i386 MSG80BETA 8.5.1238 msmsgs test@hotmail.com
CVR 5 8.1.0178 8.1.0178 8.1.0178

http://msgruser.dlservice.microsoft.com/download/B/D/3/BD343317-2DBF-48FE-8BD9-

9E3212D65E6A/Install_Messenger.exe http://get.live.com/cn
USR 6 SSO I test@hotmail.com
USR 6 SSO S MBI_KEY_OLD PKD3fof9V9uwVWrUxMEpi+Dki1oMkO1tpthPVEKjB7DGHwrkyBYzb6mOnU3EHlPi
GCF 0 69 <Policies>...</Policies>
USR 7 SSO S t=EwBIAswbAQAU...=&p= HAAAAAEAAAADZgAA...
USR 7 OK test@hotmail.com 1 0
SBS 0 null ...
BLP 8 BL
BLP 8 BL
ADL 9 67 <ml l="1">...</ml>
ADL 9 OK
PRP 10 MFN g
PRP 10 MFN g
CHL 0 29987175131175130567
QRY 12 PROD0118R6%2WYOS 32 4c6b96c27122919ae4a8d24a3015313d
QRY 12

在USR 6 SSO S 处获得服务器发送的Nonce,就是MBI_KEY_OLD后面的那一串;然后在USR 7 SSO S 处发

送认证的内容,包含了两部分,第一个t=...就是上面获得的ticket了,后面的那一串实际上是一个结

构体的内容,这个结构体由BinarySecret和获得的Nonce生成,具体的生成算法见http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO,我把代码也一块贴上吧:

 

CString GenerateLoginBlob(CString key, CString challenge)
{
    BYTE key1[
24= {0};
    BYTE key2[
24= {0}
    BYTE key3[
24= {0};
    BYTE hash[
20= {0};
    BYTE randomdata[
8= {0};
    CString szRet 
="";
    DWORD dwBase64Size;

    CryptStringToBinary(key,
0,CRYPT_STRING_BASE64,0,&dwBase64Size,0,0);
    ASSERT(dwBase64Size
<=24);
    
if (dwBase64Size>24)
        
return "";

    dwBase64Size 
= 24;
    CryptStringToBinary(key,
0,CRYPT_STRING_BASE64,key1,&dwBase64Size,0,0);
    DeriveLoginKey(key1,
24,"WS-SecureConversationSESSION KEY HASH",key2,24);
    DeriveLoginKey(key1,
24,"WS-SecureConversationSESSION KEY ENCRYPTION",key3,24);

    HCRYPTPROV hProvider;
    
if ( !CryptAcquireContext(&hProvider,0,0,PROV_RSA_FULL,0) )
        
if(!CryptAcquireContext(&hProvider,0,0,PROV_RSA_FULL,CRYPT_NEWKEYSET))
            
return "";
    
{
        HCRYPTKEY hCryptKey;
        HCRYPTKEY hCryptKey2;

        BYTE 
* pImportKey = new BYTE[ 24+STDKEYHDRSIZE ];
        memcpy(pImportKey,cKeyStdHeader,STDKEYHDRSIZE);
        memcpy(pImportKey
+STDKEYHDRSIZE,key2,24);

        CryptImportKey(hProvider,pImportKey,
24+STDKEYHDRSIZE,0,CRYPT_SF,&hCryptKey2);

        memcpy(pImportKey
+STDKEYHDRSIZE,key3,24);
        
if ( CryptImportKey(hProvider,pImportKey,24+STDKEYHDRSIZE,0,CRYPT_SF,&hCryptKey) )
        
{
            HCRYPTKEY hKeyDupe1;
            HCRYPTKEY hKeyDupe2;
            HCRYPTHASH hHash;

            CryptDuplicateKey(hCryptKey,
0,0,&hKeyDupe1);
            DWORD dwMode 
= CRYPT_MODE_CBC;
            CryptSetKeyParam(hKeyDupe1,KP_MODE,(BYTE
*)&dwMode,0);
            
if (CryptCreateHash(hProvider,CALG_HMAC,hCryptKey2,0,&hHash))
            
{
                HMAC_INFO hmcinfo;
                ZeroMemory(
&hmcinfo, sizeof(HMAC_INFO));
                hmcinfo.HashAlgid 
= CALG_SHA1;
                CryptSetHashParam(hHash,HP_HMAC_INFO,(BYTE
*)&hmcinfo,0);

                DWORD dwDataLen 
= challenge.GetLength();
                CryptDuplicateKey(hKeyDupe1,
0,0,&hKeyDupe2);
                CryptEncrypt(hKeyDupe2,
0,TRUE,0,0,&dwDataLen,0);
                CryptDestroyKey(hKeyDupe2);

                
if ( dwDataLen > 0)
                
{
                    CryptGenRandom(hProvider,
8,randomdata);
                    CryptSetKeyParam(hKeyDupe1,KP_IV,randomdata,
0);
                    
                    BYTE 
* pEncryptBytes = new BYTE[dwDataLen];
                    ZeroMemory(pEncryptBytes,dwDataLen);
                    memcpy(pEncryptBytes,(LPCSTR)(LPCTSTR)challenge,challenge.GetLength());
                    
                    DWORD dwData 
= challenge.GetLength();
                    
if (CryptEncrypt(hKeyDupe1,hHash,TRUE,0,pEncryptBytes,&dwData,dwDataLen))
                    
{
                        ASSERT(dwData 
== 72); // The size of the encryption *should* always be 72. If it's not you'll need to fix it.
                        dwData = 20;
                        CryptGetHashParam(hHash,HP_HASHVAL,hash,
&dwData,0);

                        MSGUSRKEY usrkey;
                        memcpy(usrkey.aIVBytes, randomdata,
8);
                        memcpy(usrkey.aHashBytes, hash,
20);
                        memcpy(usrkey.aCipherBytes , pEncryptBytes,
72);

                        CryptBinaryToString((BYTE
*)&usrkey,sizeof(MSGUSRKEY),CRYPT_STRING_BASE64, 0,&dwBase64Size);
                        CryptBinaryToString((BYTE
*)&usrkey,sizeof(MSGUSRKEY),CRYPT_STRING_BASE64, szRet.GetBuffer(dwBase64Size),&dwBase64Size);
                        szRet.ReleaseBuffer();

                        szRet.Replace(
" ","");
                    }

                    delete[] pEncryptBytes;
                }

                CryptDestroyHash(hHash);
            }

            CryptDestroyKey(hKeyDupe1);

            CryptDestroyKey(hCryptKey);
        }

        delete[] pImportKey;
        
if ( hCryptKey2 )
            CryptDestroyKey(hCryptKey2);
        CryptReleaseContext(hProvider,
0);
    }

    
return szRet;
}

 

好了,当服务器回应USR 7 OK时,就登陆上去了。ADL是发送联系人列表,这个列表是通过https方式从contacts.msn.com服务器得到的,这个过程同样也需要对应的ticket,这里就不详述了。(msn只在第一次登陆时从服务器上获取列表,以后就直接从本地的加密文件中读取。)

登陆成功以后,服务器会发送一个验证请求,就是CHL命令,带了一串challenge,需要我们回应正确的验证值。验证值由challenge、PRODUCT_KEY和PRODUCT_ID生成,其中PRODUCT_KEY和PRODUCT_ID是两个固定的字符串,不同版本的msn有着不同的key和id,比如我们现在模拟的这个版本就分别是:

PRODUCT_KEY:YIXPX@5I2P0UT*LK
PRODUCT_ID:PROD0118R6%2WYOS

验证值生成如下:

 

int MSN_handle_chl(char *input, char *output)
{
    
char *productKey = MSN_PRODUCT_KEY,
        
*productID  = MSN_PRODUCT_ID,
        
*hexChars   = "0123456789abcdef",
        buf[BUFSIZE];
    unsigned 
char md5Hash[16], *newHash;
    unsigned 
int *md5Parts, *chlStringParts, newHashParts[5];
    
    LONG64 nHigh
=0, nLow=0;
    
    
int i, bigEndian;
    
    
/* Determine our endianess */
    bigEndian 
= isBigEndian();
    
    
/* Create the MD5 hash */
    _snprintf(buf, BUFSIZE
-1"%s%s", input, productKey);
    MD5((unsigned 
char *)buf, strlen(buf), md5Hash);
    
    
/* Split it into four integers */
    md5Parts 
= (unsigned int *)md5Hash;
    
for(i=0; i<4; i++)
    
{  
        
/* check for endianess */
        
if(bigEndian)
            md5Parts[i] 
= swapInt(md5Parts[i]);
        
        
/* & each integer with 0x7FFFFFFF          */
        
/* and save one unmodified array for later */
        newHashParts[i] 
= md5Parts[i];
        md5Parts[i] 
&= 0x7FFFFFFF;
    }

    
    
/* make a new string and pad with '0' */
    _snprintf(buf, BUFSIZE
-5"%s%s", input, productID);
    i 
= strlen(buf);
    memset(
&buf[i], '0'8 - (i % 8));
    buf[i 
+ (8 - (i % 8))]='\0';
/* split into integers */
 chlStringParts = (unsigned int *)buf;
 
 /* this is magic */
 for (i=0; i<(strlen(buf)/4)-1; i+=2)
 {
  LONG64 temp;
  
  if(bigEndian)
  {
   chlStringParts[i]   = swapInt(chlStringParts[i]);
   chlStringParts[i+1] = swapInt(chlStringParts[i+1]);
  }
  
  temp=(md5Parts[0] * (((0x0E79A9C1 * (LONG64)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF;
  nHigh=(md5Parts[2] * (((LONG64)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF;
  nLow=nLow + nHigh + temp;
 }
 nHigh=(nHigh+md5Parts[1]) % 0x7FFFFFFF;
 nLow=(nLow+md5Parts[3]) % 0x7FFFFFFF;
 
 newHashParts[0]^=nHigh;
 newHashParts[1]^=nLow;
 newHashParts[2]^=nHigh;
 newHashParts[3]^=nLow;
 
 /* swap more bytes if big endian */
 for(i=0; i<4 && bigEndian; i++)
  newHashParts[i] = swapInt(newHashParts[i]);
 
 /* make a string of the parts */
 newHash = (unsigned char *)newHashParts;
 
 /* convert to hexadecimal */
 for (i=0; i<16; i++)
 {
  output[i*2]=hexChars[(newHash[i]>>4)&0xF];
  output[(i*2)+1]=hexChars[newHash[i]&0xF];
 }
 
 output[32]='\0';

 return 0;
}

 

如上向服务器发回验证值,服务器回复QRY则说明通过验证,否则会断开连接。至此登陆验证过程完毕。

[1] [2]

责任编辑 webmaster

 
 
 
 
 
评论更多>>
 
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •