(转)WebQQ协议开发实战

时间:2022-10-30 16:55:36

首先装了一个Fiddler2,还真不错,事半功倍,webqq从登录到获取相关的信息的http全都抓下来了,然后慢慢的分析。

(转)WebQQ协议开发实战

 

    一、webqq的登录过程

    1、判断帐号状态。首先要判断QQ号码的状态,是否正常,是否需要使用验证码登录。 

http://check.ptlogin2.qq.com/check?appid=1003903&uin=qq号码&r=随机数

    该请求返回一个字符串:ptui_checkVC('1','5764292b490a0f82694f3f705ce40b2c67f9ec1bb6f4df98', '\x00\x00\x00\x00\x03\x4f\xf1\x29');

    或是:ptui_checkVC('0','!GWD', '\x00\x00\x00\x00\x03\x4f\xf1\x29'); 如果第一个参数是1则需要使用验证码,注意,第三个参数需要截取下来,我将它命名为B1,密码加密时需要。如果参数是0,则不需要验证,取出参数1取出,我将它命名为VC,作为后面加密和登录的验证码。

 

    2、获取验证码

http://check.ptlogin2.qq.com/check?appid=1003903&uin=qq号码&r=随机数

    该请求返回一个二进制流,需要将二进制流转换为图像,显示在窗体上。

   

    3、第一次登录

http://ptlogin2.qq.com/login?u=QQ号码&p=密码&verifycode=验证码&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=4-29-45297&mibao_css=m_webqq&t=1&g=1

    这里密码的加密方式并不是网上所说的三次加密方式,而是采用以下的方式进行:pwd=Md5(Md5(Md5(pwd) + B1) + VC.ToUpper()) 。注意,如果是使用验证码图片登录,则这里的VC要相应的换成你输入的验证码。

    这时,会返回一个字符串:ptuiCB('0','0','http://web.qq.com/loginproxy.html?login2qq=1&webqq_type=10','0','登录成功','旭'); 如果登录不成功,则第一个参数不为0。

 

    4、第二次登录 

http://d.web2.qq.com/channel/login2

    这次需要从cookies里取得ptwebqq ,发送一个 r={"status":"","ptwebqq":"ptwebqq","passwd_sig":"","clientid":"66933334"} 过去,再取回vfwebqq psessionid 的值。

 

    至此,webqq的登录完成,你就可以完成你想邪恶的事情了。

 

    二、webqq登录的代码实现

    1、请求响应类,请求一个http,并返回一个响应。

 

(转)WebQQ协议开发实战
     internal  class HttpHelper
    {
         private  static CookieContainer cookieContainer =  new CookieContainer();
         private  static String refer =  " http://ui.ptlogin2.qq.com/cgi-bin/login?target=self&style=5&mibao_css=m_webqq&appid=1003903&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fweb.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20120504001 ";
         private  static String userAgent =  " Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E) ";
         private  static String accept =  " */* ";
         private  static String contentType =  " application/x-www-form-urlencoded; charset=UTF-8 ";
         private  static Dictionary< stringstring> m_cookies =  new Dictionary< stringstring>();

         ///   <summary>
        
///  请求http,获得响应
        
///   </summary>
        
///   <param name="url"> http地址 </param>
        
///   <param name="data"> 要发送的数据 </param>
        
///   <returns></returns>
         internal  static HttpWebResponse GetResponse( string url,  byte[] data =  null)
        {
             var request = (HttpWebRequest)WebRequest.Create(url);
            request.ContentType =  " application/x-www-form-urlencoded; charset=UTF-8 ";
            request.UserAgent = userAgent;
            request.Accept = accept;
            request.ContentType = contentType;
            request.Referer = refer;
            request.CookieContainer = cookieContainer;

             if (data !=  null)
            {
                request.Method =  " POST ";
                request.ContentLength = data.Length;
                 using ( var stream = request.GetRequestStream())
                {
                    stream.Write(data,  0, data.Length);
                }
            }
             else
            {
                request.Method =  " GET ";
            }

             return (HttpWebResponse)request.GetResponse();
        }

         ///   <summary>
        
///  从响应获得字符串
        
///   </summary>
        
///   <param name="url"></param>
        
///   <param name="data"></param>
        
///   <returns></returns>
         internal  static  string GetHtml( string url,  byte[] data =  null)
        {
             using ( var response = GetResponse(url, data))
            {
                ProcessCookies(response.Cookies);

                 using ( var stream = response.GetResponseStream())
                {
                     using ( var sr =  new StreamReader(stream, Encoding.UTF8))
                    {
                         return sr.ReadToEnd();
                    }
                }
            }
        }

         ///   <summary>
        
///  从响应获得图像
        
///   </summary>
        
///   <param name="url"></param>
        
///   <returns></returns>
         internal  static Image GetImage( string url)
        {
             using ( var response = GetResponse(url))
            {
                ProcessCookies(response.Cookies);

                 using ( var stream = response.GetResponseStream())
                {
                     return Image.FromStream(stream);
                }
            }
        }

         ///   <summary>
        
///  获取指定键的cookies值
        
///   </summary>
        
///   <param name="key"></param>
        
///   <returns></returns>
         internal  static  string GetCookie( string key)
        {
             if (!m_cookies.ContainsKey(key))
            {
                 return  string.Empty;
            }
             return m_cookies[key];
        }

         ///   <summary>
        
///  处理响应的cookies
        
///   </summary>
        
///   <param name="cookies"></param>
         private  static  void ProcessCookies(CookieCollection cookies)
        {
             foreach (Cookie cookie  in cookies)
            {
                 if (m_cookies.ContainsKey(cookie.Name))
                {
                    m_cookies[cookie.Name] = cookie.Value;
                }
                 else
                {
                    m_cookies.Add(cookie.Name, cookie.Value);
                }
                cookieContainer.Add(cookie);
            }
        }
    }
(转)WebQQ协议开发实战

 

    2、请求解析类,对响应进行解析,生成不同的信息对象

 

(转)WebQQ协议开发实战
     internal  class ResponseHelper
    {
         ///   <summary>
        
///  解析检验用户状态的响应信息,获得是否需要验证码
        
///   </summary>
        
///   <param name="str"></param>
        
///   <returns></returns>
         internal  static CheckResponse ParseCheckResponse( string str)
        {
             var s = str.Split( ' \' ');
             return  new CheckResponse
                       {
                           NeedVerify = s[ 1] ==  " 1 "
                           VerifyCode = s[ 3], 
                           VerifyKey = ToBytes(s[ 5])
                       };
        }

         ///   <summary>
        
///  解析第一次登录的响应信息,获得是否登录成功
        
///   </summary>
        
///   <param name="str"></param>
        
///   <returns></returns>
         internal  static LoginRespose ParseLoginResponse( string str)
        {
             var s = str.Split( ' \' ');
             return  new LoginRespose
                       {
                           Code =  int.Parse(s[ 1]), 
                           Message = s[ 9]
                       };
        }

         ///   <summary>
        
///  解析第二次登录的响应信息,取得vfwebqq和psessionid
        
///   </summary>
        
///   <param name="str"></param>
        
///   <returns></returns>
         internal  static LoginContextResponse ParseContextRespones( string str)
        {
             return  new LoginContextResponse
                       {
                           VfWebQQ = GetParameterValue(str,  " vfwebqq "), 
                           SessionId = GetParameterValue(str,  " psessionid ")
                       };
        }

         ///   <summary>
        
///  转换为字节数组表示
        
///   </summary>
        
///   <param name="str"></param>
        
///   <returns></returns>
         private  static  byte[] ToBytes( string str)
        {
             var bytes =  new  byte[ 8];
             for ( var i =  0; i <  8; i++)
            {
                bytes[i] =  byte.Parse(str.Substring((i *  4) +  22), NumberStyles.HexNumber);
            }
             return bytes;
        }

         private  static  string GetParameterValue( string str,  string key)
        {
             var l = key.Length;
             var i = str.IndexOf(key);
             if (i == - 1)
            {
                 return  string.Empty;
            }
             return str.Substring(i + l +  3, str.IndexOf( ' , ', i + l +  4) - i - l -  3).Replace( " \" """);
        }
    }

     internal  class CheckResponse
    {
         ///   <summary>
        
///  是否需要验证码
        
///   </summary>
         public  bool NeedVerify {  getset; }

         ///   <summary>
        
///  验证码
        
///   </summary>
         public  string VerifyCode {  getset; }

         ///   <summary>
        
///  这个该叫什么key?
        
///   </summary>
         public  byte[] VerifyKey {  getset; }
    }

     internal  class LoginRespose
    {
         ///   <summary>
        
///  登录返回码
        
///   </summary>
         public  int Code {  getset; }

         ///   <summary>
        
///  登录信息
        
///   </summary>
         public  string Message {  getset; }
    }

     internal  class LoginContextResponse
    {
         public  string VfWebQQ {  getset; }

         public  string SessionId {  getset; }
    }
}
(转)WebQQ协议开发实战

 

    3、密码加密类,对QQ密码进行加密

 

(转)WebQQ协议开发实战
     internal  class MD5Helper
    {
         ///   <summary>
        
///  连接两个字节数组
        
///   </summary>
        
///   <param name="b1"></param>
        
///   <param name="b2"></param>
        
///   <returns></returns>
         private  static  byte[] JoinBytes( byte[] b1,  byte[] b2)
        {
             var b3 =  new  byte[b1.Length + b2.Length];
            Array.Copy(b1, b3, b1.Length);
            Array.Copy(b2,  0, b3,  16, b2.Length);
             return b3;
        }

         ///   <summary>
        
///  将字符串加密为字节数组
        
///   </summary>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private  static  byte[] Md5ToArray( string input)
        {
             return MD5.Create().ComputeHash(Encoding.Default.GetBytes(input));
        }

         ///   <summary>
        
///  加密字符串,并转换十六进制表示的字符串
        
///   </summary>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private  static  string Md5( string input)
        {
             var buffer = MD5.Create().ComputeHash(Encoding.Default.GetBytes(input));
             var builder =  new StringBuilder();
             for ( var i =  0; i < buffer.Length; i++)
            {
                builder.Append(buffer[i].ToString( " X2 "));
            }
             return builder.ToString();
        }

         ///   <summary>
        
///  对一个字节数组加密,并转换十六进制表示的字符串
        
///   </summary>
        
///   <param name="input"></param>
        
///   <returns></returns>
         private  static  string Md5( byte[] input)
        {
             var buffer = MD5.Create().ComputeHash(input);
             var builder =  new StringBuilder();
             for ( var i =  0; i < buffer.Length; i++)
            {
                builder.Append(buffer[i].ToString( " X2 "));
            }
             return builder.ToString();
        }

         ///   <summary>
        
///  对密码进行加密
        
///   </summary>
        
///   <param name="password"> QQ密码 </param>
        
///   <param name="vcode"> 第一次检验用户状态时获取的key </param>
        
///   <param name="verifyCode"> 验证码 </param>
        
///   <returns></returns>
         internal  static  string Md5( string password,  byte[] vcode,  string verifyCode)
        {
             var b1 = Md5ToArray(password);
             var s1 = Md5(JoinBytes(b1, vcode));
             return Md5(s1 + verifyCode);
        }
    }
(转)WebQQ协议开发实战

 

    4、QQ辅助类,呵呵,客户端类了

 

(转)WebQQ协议开发实战
     internal  class QQHelper
    {
         private Random m_ran =  new Random();
         private  byte[] m_bytes;
         private LoginContextResponse m_context;

         internal QQHelper( string number)
        {
            Number = number;
        }

         ///   <summary>
        
///  QQ号码
        
///   </summary>
         internal  string Number {  getset; }

         ///   <summary>
        
///  登录到webqq
        
///   </summary>
        
///   <param name="password"> 密码 </param>
        
///   <param name="verifyCode"> 验证码 </param>
        
///   <returns></returns>
         internal LoginStatus Login( string password,  string verifyCode =  null)
        {
             if ( string.IsNullOrEmpty(verifyCode))
            {
                 var url =  string.Format( " http://check.ptlogin2.qq.com/check?appid=1003903&uin={0}&r={1} ", Number, GetRandomNumber());
                 var vc = ResponseHelper.ParseCheckResponse(HttpHelper.GetHtml(url));
                verifyCode = vc.VerifyCode;
                m_bytes = vc.VerifyKey;

                 if (vc.NeedVerify)
                {
                     return LoginStatus.NeedVerify;
                }
            }

             var pwd = MD5Helper.Md5(password, m_bytes, verifyCode);
             var loginUrl =  string.Format( " http://ptlogin2.qq.com/login?u={0}&p={1}&verifycode={2}&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=4-29-45297&mibao_css=m_webqq&t=1&g=1 ", Number, pwd, verifyCode);

             var login = ResponseHelper.ParseLoginResponse(HttpHelper.GetHtml(loginUrl));

             if (login.Code ==  0)
            {
                Login2();
                 return LoginStatus.Successd;
            }
             throw  new Exception(login.Message);
        }

         ///   <summary>
        
///  第二次登录
        
///   </summary>
         private  void Login2()
        {
             var url =  " http://d.web2.qq.com/channel/login2 ";
             var data =  " r={\"status\":\"\",\"ptwebqq\":\" " + HttpHelper.GetCookie( " ptwebqq ") +  " \",\"passwd_sig\":\"\",\"clientid\":\"66933334\"} ";
            m_context = ResponseHelper.ParseContextRespones(HttpHelper.GetHtml(url, Encoding.UTF8.GetBytes(data)));
        }

         ///   <summary>
        
///  获取验证码图片
        
///   </summary>
        
///   <returns></returns>
         internal Image GetVerifyImage()
        {
             var url =  " http://captcha.qq.com/getimage?aid=1003903&uin= " + Number +  " &r= " + GetRandomNumber();
             return HttpHelper.GetImage(url);
        }

         private  string GetRandomNumber()
        {
             return m_ran.NextDouble().ToString();
        }
    }

     internal  enum LoginStatus
    {
        NeedVerify,
        Successd,
        Faild
    }