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
        }
  • 相关阅读:
    删除 Visual studio 生成后的临时文件
    C# 中的委托和事件(转)
    C#复制DataRow出现“该行已经属于此表”错误的解决办法(转)
    ini配置文件读取类
    c# wpf窗体前端显示问题
    注册系统热键类(原创)
    C# 窗体最小化的托盘/系统通知区域(转)
    php explode()返回值
    $_SERVER['SCRIPT_NAME']
    svn合并初次使用心得
  • 原文地址:https://www.cnblogs.com/chear/p/2691035.html
Copyright © 2011-2022 走看看