一、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<string, string> m_cookies = new Dictionary<string, string>();

/// <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) + 2, 2), 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 { get; set; }

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

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

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

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

internal class LoginContextResponse
    {
        public string VfWebQQ { get; set; }

public string SessionId { get; set; }
    }
}

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 { get; set; }

/// <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
    }

QQweb登录通讯协议相关推荐

  1. ntrip获取源列表_Ntrip通讯协议怎么样?

    1 什么是Ntrip? CORS(Continuously Operating Reference Stations)就是网络基准站,通过网络收发GPS差分数据.用户访问CORS后,不用单独架设GPS ...

  2. Skype通讯协议分析

    晚上在看Salman A. Baset和Henning Schulzrinne写的<An Analysis of the Skype Peer-to-Peer Internet Telephon ...

  3. 【线上分享】下一代互联网通讯协议:QUIC

    随着互联网的发展,出现了越来越多的应用场景如短视频和直播,用户对网络时延.交互体验.数据隐私的要求也越来越高.TCP协议自 1983 年诞生之后,成为当今互联网的基石,然而 TCP 在弱网.丢包率较高 ...

  4. kbengine通讯协议

    通讯协议格式 客户端想要与KBEngine进行通讯需要与KBEngine使用相同的协议,KBEngine通讯协议分为如下两种类型: 确定长度类型: |------------------------- ...

  5. Ntrip通讯协议1.0

    Ntrip通讯协议1.0 1 什么是Ntrip? CORS(Continuously Operating Reference Stations)就是网络基准站,通过网络收发GPS差分数据.用户访问CO ...

  6. 《 Python程序设计项目案例》— 用Python开发的基于TCP通讯协议的私人聊天室 (期末大作业、结课作业、课程设计、毕业设计)

    基于Python与TCP协议的私人聊天室(GUI交互界面,用户注册.用户登录.实时聊天,文件上传与下载) 用Python开发的基于TCP通讯协议的实时聊天通讯和文件共享应用 目录 基于Python与T ...

  7. 企业即时通讯软件,网络通讯协议和机制怎么选?

    一个大型组织如果需要从头开发一套自主可控的即时通讯软件,从技术角度第一个要考虑的核心问题就是:如何确定客户端和服务器之间的通讯协议和通讯机制? 通讯协议怎么选? 大型组织架构就意味着员工人数多,网络情 ...

  8. 通讯协议,网络通讯协议基本原理透析

    文章目录 通讯协议 TCP/IP协议 TCP/IP模型 osi版 基本版 应用层 传输层 网络层 数据链层 物理层 通讯协议 我们想要进⾏数据通讯分⼏步? 1.找到对⽅ip 2 .数据要发送到对⽅指定 ...

  9. 野火多功能调试助手】-摄像头调试助手通讯协议

    野火多功能调试助手] [复制链接] 摄像头调试助手通讯协议     LONG_R3acc 电梯直达 1#  发表于 2020-6-8 14:49:48 | 只看该作者  ] 本帖最后由 LONG_R3 ...

最新文章

  1. 使用高斯混合模型对不同的股票市场状况进行聚类
  2. Java EE之RMI
  3. numpy教程:快速傅里叶变换模块numpy.fft
  4. 他用五年研究百位百万富翁生活习惯 结果很震撼
  5. python列表中enumerate和zip函数用法
  6. 结对和团队项目建议 - 黄金点游戏
  7. anaconda3 tensorflow安装踩坑记(WIN10+tensorflow带gpu版本)
  8. arm qt mysql插件_Ubuntu下编译ARM平台Qt的MySQL插件
  9. http报文和协议首部
  10. mysql负载时高时低_Mysql服务器负载很高,性能问题排查思路是怎样的?
  11. 云服务下的安全特点及基础防护
  12. el 能否定义作用域变量_JS块级作用域和let,const,var区别
  13. 计算机毕业设计php的校园电影网站系统
  14. 1083. Windy数
  15. 如何将音视频中的伴奏背景音乐和人声分离?
  16. 调和曲线图和轮廓图的比较
  17. 解决GitHub/GitLab官网访问慢的问题
  18. python学习笔记---中文词云
  19. 从零开始开发一个自动抓取教务系统课表等信息并动态显示的安卓课程表APP,原理分析及功能实现完美教程
  20. 手机号码如何检测开通微信

热门文章

  1. 高德、腾讯、百度实时路况切片地址
  2. JavaEE基础:java常量、变量、数据类型、数据类型转化、运算符-第二个学习日
  3. ZFS详解及具体操作代码及流程
  4. mysql完全删除文件_MySQL完全删除教程
  5. Python数据分析-折线图
  6. HDMI延长器之HDMI网络延长技术
  7. 读书笔记:你就是极客-软件开发人员生存指南
  8. 会计毕业生的转行之路:坚持无畏,我是我自己的英雄
  9. 推荐一款Linux平台下的BT下载工具
  10. UOS如何添加window字体或者非商业字体