zoukankan      html  css  js  c++  java
  • (转)WebQQ协议开发实战

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

        一、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,并返回一个响应。

    复制代码
        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);
                }
            }
        }
    复制代码

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

    复制代码
        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; }
        }
    }
    复制代码

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

    复制代码
        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);
            }
        }
    复制代码

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

    复制代码
        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
        }
  • 相关阅读:
    当Django模型迁移时,报No migrations to apply 问题时
    django--各个文件的含义
    django--创建项目
    1013. Battle Over Cities (25)
    1011. World Cup Betting (20)
    1009. Product of Polynomials (25)
    1007. Maximum Subsequence Sum (25)
    1006. Sign In and Sign Out (25)
    1008. Elevator (20)
    1004. Counting Leaves (30)
  • 原文地址:https://www.cnblogs.com/chear/p/2691035.html
Copyright © 2011-2022 走看看