一、前言

虽然腾讯官方不支持使用二维码充值Q币,但对于喜欢钻研的人来说这不是问题,本文利用WPF技术讲解从扫码登录到生成Q币充值二维码的一整套解决方案。

因为充值Q币需要先用QQ号登录官网。所以我们首先要解决登录问题。文章将分为两篇讲解,这是第一篇——扫码登录。既然是使用WPF技术,我们就要脱离腾讯充值官网(https://pay.qq.com),将相关操作在桌面上完成。

二、获取登录所需的数据

  1. 找到登录所需的二维码网址

打开官网首页,点击右上角的登录,通过抓包或分析html源码,我们可以很轻松的找到登录所需的二维码网址:

https://ssl.ptlogin2.qq.com/ptqrshow?appid=11000101&e=2&l=M&s=3&d=72&v=4&t=0.7299344722244967&pt_3rd_aid=0

我们单独将此链接在浏览器中打开,返回的是一个带cookei参数的二维码,通过抓包软件获取Response cookies,发现只有一个参数qrsig(如图1)。

图1

2.验证扫码状态的交互网址

通过步骤1获得的二维码有过期时间,服务器会使用一个get请求轮询二维码的状态(未失效、验证中、失效,如图2)。该网址是:

https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https%3A%2F%2Fpay.qq.com%2Fipay%2Flogin-proxy.html&ptqrtoken=1522270953&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1573959043618&js_ver=19111319&js_type=1&login_sig=jBa-WO7cEFUmtBpzpqH**RKJSuWDMToAbbQP97E*WArCCpvHlvFDQ*81wbTbV0*d&pt_uistyle=40&aid=11000101&has_onekey=1&

图2

该链接请求头中带有cookie,经过分析后发现,该cookie中含有上述步骤1中获取的qrig=xxxxxxxxxx。同时,该链接的get参数众多,通过多次抓包分析对比,发现ptqrtoken参数会根据qrig值的变化而改变,应该是前端JS加密生成的。其它参数可以固定不变。

3.找到ptqrtoken所需的js加密函数

扣出JS函数,B站的教程很多,就是按照套路来,首先全局搜索ptqrtoken,发现加密函数名为hahs33,如图3。

图3

找到函数名称那再找函数代码就简单了,这里就不细说。值此,我们已经获得了扫码登录需要的所有数据,下一步便可以用代码实现了。

三、C#代码实现

  1. 创建WPF项目

使用工具Vs2019,框架.net framework,UI界面见图4。

图4

实现扫码登录,我们有三步要走:第一步是通过本文第二章第1节的url请求登录所需的二维码,并得到cookie中的qrsig值(用于下一步JS加密)。第二步是通过第二章第2节的url实现二维码状态验证。而要实现第二步,需要JS加密得到ptqrtoken的实际值(C#调用JS)。第三步,扫码登录成功后,第二步的请求网址会即时返回cookies,它包含了登录的QQ号和skey值,这两个值是生成充值二维码的必要元素。

2.添加QqHttp类,并声明变量

在QqHttp类下,首先实例化一个HttpClient,用来发送GET、POST请求,并定义一个url,即本文第二章第1小节所提到的二维码网址。UseCookies = true表示自动获得cookie,AutomaticDecompression为解压缩方式,如果不声明,可能会出现乱码。

private static readonly HttpClient hc = new HttpClient(new HttpClientHandler() { UseCookies = true, AutomaticDecompression = DecompressionMethods.GZip });
String url = "https://ssl.ptlogin2.qq.com/ptqrshow?appid=11000101&e=2&l=M&s=3&d=72&v=4&t=0.1972804393669354&pt_3rd_aid=0";//此处的url,即本文第二章第1小节所提到的二维码网址

3.在QqHttp中添加方法

1)get请求二维码url,返回二维码图片和cookie值,注意:这里有两个返回值。我们知道,C#返回多个值时,需使用out关健字或元组,因为我们的方法使用了async异步,无法使用out关健字,须用元组返回多个值。

/// <summary>
/// 返回一个元组
/// 值1:包含cookies的IEnumerable<string>
/// 值2:网址二进制流
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private async Task<Tuple<IEnumerable<string>, byte[]>> httpGet()
{
hc.DefaultRequestHeaders.Connection.Add("keep-alive");
var resp = await hc.GetAsync(url);
byte[] rspby = await resp.Content.ReadAsByteArrayAsync();//二进制流
var cookies = resp.Headers.GetValues("Set-Cookie"); //获取cookies
var tuple = new Tuple<IEnumerable<string>, byte[]>(cookies, rspby);
return tuple;
}

2)定义一个提取cookie的方法。因为上一步存储cookie的类型是IEnumerable<string>,需要通过以下方法提取。代码如下:

 /// <summary>
/// 遍历IEnumerable<string>,取出cookie
/// </summary>
/// <param name="ck"></param>
/// <returns></returns>
private string cookList(IEnumerable<string> ck)
{
string cookies = null;
try
{
foreach (string cookie in ck)
{
cookies += cookie;
}
return cookies;
}
catch (Exception ex)
{
return ex.Message;
} }

3)定义一个正则提取函数,从cookie中提取指定的字符串。

/// <summary>
/// 从字符串中正则提取指定内容
/// </summary>
/// <param name="str" 字符串></param>
/// <param name="re" 正则></param>
/// <returns></returns>
private string reGet(string str, string re)
{ try
{
Match s_m = Regex.Match(str, re); //正则提取,str为字符串,re为正则表达式
string s = s_m.Groups[].ToString();
return s;
}
catch (Exception ex)
{
return ex.Message;
} }

4)请求登录二维码,并将其显示在界面的控件中。利用上述正则方法提取cookie中qrsig的值,下一步js加密需用到。

/// <summary>
/// 请求登录二维码
/// 获得cookie里的重要参数,即qrsig值
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public async Task<string> qrSig(Image image)
{
var tt = await httpGet();//获取登录二维码
IEnumerable<string> cooklist = tt.Item1;//获取IEnumerable类型的cookie
var bty = tt.Item2;//获取照片的二进制流
MemoryStream ms = new MemoryStream(bty);
image.Source = BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.Default);//将图片显示在控件上
string cookies = cookList(cooklist);//通过我们定义的cookList方法获取cookies
string qrsig = reGet(cookies, "(?<=qrsig=).*?(?=;)");//利用正则获取到cookies中qrsig的值,下一步js加密需用到
return qrsig;
}

以上便完成了第一步,获得了二维码和cookie值。下一步便是验证二维码状态。

5)轮询二维码状态,其中参数ac是带返回值的委托。如果登录成功,就返回含有QQ号和skey的cookies。

private async Task<string> pollGet(string url, Func<IEnumerable<string>, string> ac)
{
while (true)
{
var resp = await hc.GetAsync(url);
string rspstr = await resp.Content.ReadAsStringAsync();
if (rspstr.Contains("二维码未失效") || rspstr.Contains("二维码认证中"))
{
Task ts = Task.Run(() =>
{
Thread.Sleep();//每1秒循环
});
await ts;//异步实现,不然会卡界面
}
else if (rspstr.Contains("二维码已失效"))
{
MessageBox.Show("二维码已失效,请重新生成");
return "二维码已失效";
}
else if (rspstr.Contains("登录成功"))
{
var cookies = resp.Headers.GetValues("Set-Cookie");
return ac(cookies);
}
}
}

注意,如果我们使用上述pollGet方法,则需要传入上文第二章2节中验证扫码状态的交互网址,而此网址中的ptqrtoken值由js加密完成。那么我们还需要添加一个专门调用JS函数的类QqJs,再定义一个方法算出ptqrtoken值。

4.添加QqJs类,定义C#调用js函数的静态方法

首先我们要在项目中创建一个Js文件夹,如图5,并将hh.js文件放至该文件夹中。如果我们要用Uri的相对地址,将文件作为资源嵌入生成的exe可执行文件中,还需要将js文件的属性设置如图6所示。

图5

图6

public static string GetToken(string array)
{
try
{
Stream src = Application.GetResourceStream(new Uri("../../Js/hh.js", UriKind.Relative)).Stream;//获取资源文件
string str = new StreamReader(src, Encoding.UTF8).ReadToEnd();//读取资源文件
string fun = string.Format(@"hash33('{0}')", array);
string token = ExecuteScript(fun, str);
return token;
}catch(Exception ex)
{
MessageBox.Show(ex.Message);
return ex.Message;
}
} /// <summary>
/// 执行JS
/// </summary>
/// <param name="sExpression">参数体</param>
/// <param name="sCode">JavaScript代码的字符串</param>
/// <returns></returns>
private static string ExecuteScript(string sExpression, string sCode)
{
MSScriptControl.ScriptControl scriptControl = new MSScriptControl.ScriptControl();
scriptControl.UseSafeSubset = true;
scriptControl.Language = "JScript";
scriptControl.AddCode(sCode);
try
{
string str = scriptControl.Eval(sExpression).ToString();
return str;
}
catch (Exception ex)
{
string str = ex.Message;
}
return null; }

上述调用js函数代码需要如图7添加【MSScriptControl.ScriptControl】Com引用

图7

此时,我们便到了扫码登录的第三步,实现登录成功后的cookies提取。我们再次回到QqHttp类中添加一个异步方法,实现相关数据的获取,代码如下。

public async Task<string> signIn(string url_log, Image image)
{
string str = await pollGet(url_log, (a) =>
{
string cook = cookList(a);
string qq = reGet(reGet(cook, "(?<=uin=).*?(?=;)"), "[1-9][0-9]*");//获取登录的QQ号
string skey = reGet(cook, "(?<=skey=).*?(?=;)");//获取登录的skey return qq + ";" + skey;
});
if (str.Contains("二维码已失效"))
{
string qrsig = await qrSig(image);//重新加截获取图片
string token = QqJs.GetToken(qrsig);//将qrsig值加密
string url = $"https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https%3A%2F%2Fpay.qq.com%2Fmidas%2Fminipay_v2%2Fviews%2Fcpay%2Fgame.shtml%3Fzoneid%3D0%26provide_uin%3D1502220138%26buy_quantity%3D10000%26step%3D100%26game_type%3Dduanyou%26openid%3D%26openkey%3D%26show_header%3D0%26supportCloseConfirm%3D0%26appid%3D1450000238&ptqrtoken={token}&ptredirect=1&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1569858142839&js_ver=19092321&js_type=1&login_sig=8YcWYSPBNYOF4VyhO1em7918F8dhm6THd*x0kwJWPDGk*bN3KlUWuYoJf8vtAZEf&pt_uistyle=40&aid=11000101&";
string sign = await signIn(url, image);//重新再来
return sign;
}
else
{
image.Source = new BitmapImage(new Uri("./Img/登录成功.jpg", UriKind.Relative));
return str;
}
}

5.实例化类,调用函数,实现功能

QqHttp hp = new QqHttp();//实例化一个连接
string qrsig = await hp.qrSig(imCoed);//获取二维码及cookie中的qrsig值
string token = QqJs.GetToken(qrsig);//将qrsig值加密
string url = $"https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https%3A%2F%2Fpay.qq.com%2Fmidas%2Fminipay_v2%2Fviews%2Fcpay%2Fgame.shtml%3Fzoneid%3D0%26provide_uin%3D1502220138%26buy_quantity%3D10000%26step%3D100%26game_type%3Dduanyou%26openid%3D%26openkey%3D%26show_header%3D0%26supportCloseConfirm%3D0%26appid%3D1450000238&ptqrtoken={token}&ptredirect=1&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1569858142839&js_ver=19092321&js_type=1&login_sig=8YcWYSPBNYOF4VyhO1em7918F8dhm6THd*x0kwJWPDGk*bN3KlUWuYoJf8vtAZEf&pt_uistyle=40&aid=11000101&";
string qqkey = await hp.signIn(url, imCoed);//获取带qq和key的字符串
string[] qqkeyarr = qqkey.Split(';');
qq = qqkeyarr[];//获取登录的QQ值
key = qqkeyarr[];//获取登录的key值,此值第一个字符串为@
keyp = key.Substring();//截取标号1后面的字符串,不带@的key值

致此,扫码登录的功能已全部实现,下一篇将详诉如何实现Q币充值二维码的生成,谢谢。

利用WPF生成Q币充值二维码——扫码登录篇的更多相关文章

  1. 微信公众号生成带参数的二维码asp源码下载

    晚上闲着没事,一个朋友联系,让帮忙写一个微信公众号利用asp生成带参数的二维码,别人扫了后如果已经关注过该公众号的,则直接进入公众号里,如果没关注则提示关注,关注后自动把该微信用户资料获取到并且保存入 ...

  2. .NET使用ZXing.NET生成中间带图片的二维码

    很久之前就有写这样的代码了,只是一直没记录下来,偶然想写成博客. 把之前的代码封装成函数,以方便理解以及调用. 基于开源的 ZXing.NET 组件,代码如下: 先添加对ZXing.NET的引用,然后 ...

  3. phpqrcode生成带logo的二维码图片及带文字的二维码图片

    <?php require_once "./phpqrcode/phpqrcode.php"; /** * 这样就可以生成二维码了,实际上在png这个方法里还有几个参数需要使 ...

  4. .NET生成带Logo的二维码

    使用ThoughtWorks.QRCode生成,利用这个库来生成带Logo的二维码(就是中间嵌了一个图片的二维码),直接见代码: HttpContext context = HttpContext.C ...

  5. js实现生成中间带图片的二维码

    之前需要实现生成中间带图片的二维码,所以找了半天终于找到一个可以用的.于是在这里记录一下. 下面是需要注意的几点: 1.使用的js为jquery-qrcode 但是已经经过别人的修改,和网上原来的那些 ...

  6. C# 生成 DataMatrix 格式的二维码

    该文主要是利用OnBarcode.dll 生成 DataMatrix 格式的二维码的一些简单方法和操作技巧.关于QrBarcode的二维码比较常见和简单,网上有很多资源. 1.附件为dll 2.利用上 ...

  7. PHP生成带logo图像二维码的两种方法

    本文主要和大家分享PHP生成带logo图像二维码的两种方法,主要以文字和代码的形式和大家分享,希望能帮助到大家. 一.利用Google API生成二维码Google提供了较为完善的二维码生成接口,调用 ...

  8. C#生成带logo的二维码

    带logo的二维码生成分为两步骤:首先根据输入的内容生成二维码图片,然后读取本地的logo图片,通过图片处理生成带logo的二维码. 生成的二维码效果如下: 下面直接贴出二维码生成类   QRCode ...

  9. js生成带参的二维码

    最近项目中有需求生成带参的二维码,考虑过用JAVA后台生成返回前端展示,后面了解到用jquery的qrcode.js插件可以很好现实 引入js: require.config({ baseUrl : ...

随机推荐

  1. 【项目管理】Mybatis-Generator之最完美配置详解

    今天看到了一篇总结特别详细的关于Mybatis-Generator配置文件的文章,特转载进行记录学习使用. 先附上原文地址链接:张思全----全哥文章 <?xml version="1 ...

  2. python中的局部变量和全局变量

  3. Thinkphp5——数据库表名的大小写问题

    ThinkPHP5中数据库的表名如果是驼峰命名法,会被转换成小写加下划线,解决方法如下: 1.表名全部小写,因为数据库的表名区分大小写的. 2.使用Db::table("表名"), ...

  4. 转:Spring Boot启动过程

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

  5. Python ——selenium报错 'chromedriver.exe' executable needs to be in PATH

    from selenium import webdriver dr = webdriver.Chrome() 运行时报错: 问题分析: 1.没有下载chromedriver.exe 2.chromed ...

  6. 17.JAVA-Dom、Sax解析XML详解

    在JAVA中,解析有三种方式: Dom解析(支持改删,耗内存). Sax解析(不支持改删,不耗内存). Pull解析(在Android中推荐使用的一种解析XML的方式,在下章学习). 1.支持Dom与 ...

  7. CF832D Misha, Grisha and Underground

    有一棵n个节点的树,一共q 次询问 每次询问给定3个点,求两条起点终点在这三个点上且不完全重合的路径的最多公共节点数 简单LCA求距离,令a为汇合点,那么答案就是(dis(a,b) + dis(a,c ...

  8. windows系统安装git

    一.下载git的安装包 git官网的下载地址:https://git-scm.com/download/win 选择自己的机型进行安装. 二.安装配置 一直点下一步就可以 安装完毕之后,打开电脑命令窗 ...

  9. Asp.net Core dotnet 发布类库文件 带上注释,发布预发行版,带上所有引用

    带上注释 效果图 带上所有引用 效果图 预发行版 效果图 由于微软取消了  project.json  这个json 转而用了csproj 用于保存配置 所以懵逼很大一会 资料来源 project.j ...

  10. 模拟摄像头,AV视频信号线解码,PAL制 NTSC,输入解码显示,终于搞定,记录下!

    模拟摄像头,AV视频信号线解码,PAL制 NTSC,输入解码显示,终于搞定,记录下! 咱们常用的摄像头,监控等,大多数都是AV信号,国内制式都是PAL,采用同轴,传输,这样的好处在于,传输距离可以很长 ...