使用按键精灵脚本实现《自由幻想》游戏内"神医"验证码自动校验

按键精灵编写的脚本简单方便,最直接的体验是可以代替我们处理简单重复的鼠标键盘操作。
《QQ自由幻想》这款游戏系统内置自动挂机功能,但是每隔1个小时就会自动弹出验证码校验,校验不通过就会被踢下线,然后只能通过①先花钱开通VIP-》②再花钱购买2/4/8小时免校验卡(道具)跳过校验,不氪金体验极差。

对于这款已经很老的游戏,情怀党表示还是要尝试实现自动过验证码,增加游戏体验

验证码示例

按键论坛可以搜到大漠插件建立字库识别验证码的教程:『笨海绵』用大漠插件实现QQ自由幻想验证码后台识别方法详解
这种方法主要原理是收集图片像素点数据建立字库并进行对比识别出验证码字符,缺点是建立字库繁杂,识别率也是个问题。

接下来说下处理过程,首先截取左边难处理的验证码区域,上传图片到打码平台(若快),然后截取右边列4个较为规则的答案选项上传到百度文字识别,最终对比返回结果得出第几个是正确答案就可以通过验证了,思路非常简单。

主要涉及下面几点:
首先是大漠插件的使用
  1. 首先下载大漠插件V3.1233(最后免费版本),然后根据里边提供的大漠接口说明.CHM注册大漠插件
    大漠接口说明.CHM

为什么要使用大漠插件?
优点:大漠插件建立字库找字的功能比较方便。
按键精灵自带命令也可以满足大部分的使用要求,大漠插件可以补充按键不足部分,混合使用。
大漠插件通过dm.BindWindow命令可以轻松切换前后台模式,而不需要修改脚本中的大量命令
测试过程中经常发现按键的后台截图方法截图失败时会导致按键程序直接崩溃,使用大漠插件就没有出现这种情况
缺点:注册多一个大漠插件意味着有更高的安全风险,360之类的就可能会报错。

  1. 然后在脚本中创建大漠对象就可以直接使用了(具体方法/参数说明可以查看大漠接口文档)
//首先获取当前游戏句柄(这里通过游戏类名获取,也可以直接鼠标获取Plugin.Window.MousePoint)
Hwnd = Plugin.Window.Find("QQSwordWinClass", 0) 
//创建大漠插件对象
set dm = createobject("dm.dmsoft")
//后台模式
dm_ret = dm.BindWindow(Hwnd,"dx2","windows","windows", 0)
//前台模式
//dm_ret = dm.BindWindowEx(Hwnd, "dx2", "normal", "normal", "", 0)

//大漠插件绑定句柄后就可以直接调用方法了,例如鼠标的移动点击
//dm.MoveTo X,Y
//dm.LeftClick
//屏幕截图保存到本地:先设置大漠插件统一保存的文件目录,再截图保存
//设置文件保存路径
//dm_ret = dm.SetPath("D:\temp\ZYHXCaptcha")
//验证码截图,在上面的目录可以找到截图
//dm_ret = dm.CaptureJpg(x1, y1, x2, y2, "captcha.jpg", 100)
  1. 验证码区域的截图(具体方法/参数说明可以查看大漠接口文档)
  • 首先找到验证码区域的坐标,因为验证码是屏幕内随机区域出现的,这需要用到找字功能:目标是找到验证码左上角神医两个字。找字功能就需要我们先建字库:
    ①打开大漠综合工具,点击抓图截取需要的“神医”两个字
    大漠综合工具

    ②然后调用dm.FindStr就可以找到当前程序神医两个字的坐标了
//注意要先设置使用的大漠字库
dm_ret = dm.SetDict(0, "C:\test_game\zyhx.txt")
//然后找字成功就会返回intX、intY坐标
dm_ret = dm.FindStr(0, 0, 2000, 2000, "神医", "ff0000-000000", 0.9, intX, intY)
If dm_ret = 0 And intX >0 And intY >0 Then 
    TracePrint "神医坐标=" & intX & ":" & intY
End If
  • 对验证码区域进行截图
    ①截图需要确定截图范围即:左上角坐标(找字得到“神医”坐标),右下角坐标(按键自带抓抓工具简单计算出)
    按键抓抓工具计算坐标

    然后就可以调用dm.CaptureJpg方法对左边验证码和右边答案选项列表进行截图了
//注意要先设置文件保存路径
//dm_ret = dm.SetPath("D:\temp\ZYHXCaptcha")
//验证码截图
dm_ret = dm.CaptureJpg(intX, intY + 60, intX + 160, intY + 170, "captcha.jpg", 100)
Delay 50
//答案选项截图
dm_ret = dm.CaptureJpg(intX+195, intY + 36, intX + 233, intY + 114, "answer.jpg", 65)
截图保存到本地
使用打码平台实现复杂验证码的处理

左边区域的复杂验证码使用打码平台进行处理,这里使用若快打码平台:http://wiki.ruokuai.com/ApiDemo_Spirit.ashx

若快开发文档

可以直接调用示例中提供的方法实现上传并拿到返回值:

xml文本 = Lib.RK_API.上传本地验证码图片(用户名, 密码, 验证码类型, 超时时间, 软件id, 软件key, 图片路径)
TracePrint "若快识别结果:" & xml文本
response_captcha= Lib.RK_API.Xml解析(xml文本,"<Result>","</Result>")

这里参数的用户名, 密码需要我们到若快官网注册一个普通用户并进行充值,可以先充1块得到2500快豆进行测试,这里验证码类型是3位英数混合3030,验证一次需要消耗10快豆,而软件id和软件key则需要再注册一个开发者账号,添加软件并审核通过后就可以拿到软件Id和软件Key了(具体查看若快官方接入文档

调用百度OCR处理简单/规则的文字或数字
  1. 百度文字识别文档中有两种调用方式,第一种是先获取Token然后直接进行调用,请求参数为image=图像数据(base64编码后进行urlencode)
    这里我使用第二种带authorization请求头的鉴权方式(推荐使用Token的方式一,简单方便)
  • 先下载文档中的鉴权认证示例
    我选择了下载了Java的示例:git clone https://github.com/baidubce/bce-sdk-java.git
  • 然后在IDEA中打开,找到BceV1Signer.java,执行sign方法就可以生成authorization
  • 注意这里AK/SK需要登录百度云->用户账号->安全认证->获取Access Key/Secret Key,参考文档
  • headersTosign是请求头中需要参与加密的项,这里为了简单只加密了必须的host
public static void main(String[] args) throws Exception {
    BceV1Signer bs = new BceV1Signer();
    InternalRequest request = new InternalRequest(HttpMethodName.POST,new URI("https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic"));
    //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd'T'hh:mm:ssZ");
    //request.addHeader("x-bce-date",sdf.format(new Date()));
    //request.addHeader("content-type","multipart/form-data");
    
    //ak/sk
    BceCredentials bceCredentials = new DefaultBceCredentials("your_AccessKey","your_SecretKey ");
    
    SignOptions signOptions = new SignOptions();
    signOptions.setExpirationInSeconds(3600*24*365);//有效期一年
    Set<String> headersTosign = new HashSet<String>();
    headersTosign.add("HOST");//对请求头中的host进行加密处理
    //headersTosign.add("x-bce-date");
    //headersTosign.add("content-type");
    signOptions.setHeadersToSign(headersTosign);

    bs.sign(request,bceCredentials,signOptions);
}   
  1. 按键精灵中读取图片文件base64
    按键中可以使用vbs,还保留VBSBegin...VBSEnd直接调用vbs代码的方式,参考vbscript将图片转换为base64字符串
'vbscript将图片2进制信息转为base64字符,参数FilePath为图片文件物理路径
'@return:ImagesToBase64返回图片base64格式字符串,前缀为data:image/图片类型;base64,base64数据
Function ImagesToBase64(FilePath)
    Dim xml
    Dim root
    Dim fs
    Dim objStream
    Dim objXMLDoc
    Dim Base64
    Set objXMLDoc=CreateObject("msxml2.FreeThreadedDOMDocument")
    objXMLDoc.loadXML "<?xml version='1.0' ?><data></data>"
    Set fs = createObject("Scripting.FileSystemObject") ''FSO组件
    If fs.FileExists(FilePath) Then '判断File文件是否存在
    '用 stream 来读取数据
    Set objStream = CreateObject("ADODB.Stream")
    objStream.Type = 1
    objStream.Open
    objStream.LoadFromFile FilePath
    
    objXMLDoc.documentElement.dataType = "bin.base64"
    objXMLDoc.documentElement.nodeTypedvalue = objStream.Read

    '数据流读取结束.得到了值 objXMLDoc
    '创建XML文件
    Set xml = CreateObject("msxml2.FreeThreadedDOMDocument")
    xml.load objXMLDoc
    If xml.ReadyState>2 Then
    Set root=xml.getElementsByTagName("data")
    Base64 = root.Item(0).Text
    Base64 = Replace(Base64,vbLf,"") 
    TracePrint "Base64=" & Base64
    else
    Base64=""
    End If
    Set xml=Nothing
    Set objStream=Nothing
    else
    Base64=""
    End If
    Set fs=Nothing
    Set objXMLDoc=Nothing
    ImagesToBase64 = Base64
End Function

测试过程中发现base64后的字符串中间包含了多余的换行符需要去除Replace(Base64,vbLf,"")

  1. 图像数据,base64编码后进行urlencode
Function URLEncode(strURL)
    Dim I
    Dim tempStr
    For I = 1 To Len(strURL)
        If Asc(Mid(strURL, I, 1)) < 0 Then
            tempStr = "%" & Right(CStr(Hex(Asc(Mid(strURL, I, 1)))), 2)
            tempStr = "%" & Left(CStr(Hex(Asc(Mid(strURL, I, 1)))), Len(CStr(Hex(Asc(Mid(strURL, I, 1))))) - 2) & tempStr
            URLEncode = URLEncode & tempStr
        ElseIf (Asc(Mid(strURL, I, 1)) >= 65 And Asc(Mid(strURL, I, 1)) <= 90) Or (Asc(Mid(strURL, I, 1)) >= 97 And Asc(Mid(strURL, I, 1)) <= 122) Or (Asc(Mid(strURL, I, 1)) >= 48 And Asc(Mid(strURL, I, 1)) <= 57) Then
            URLEncode = URLEncode & Mid(strURL, I, 1)
        Else
            URLEncode = URLEncode & "%" & Hex(Asc(Mid(strURL, I, 1)))
        End If
    Next
End Function

图片质量高的时候base64长度变长,上面的这个方法执行速度会很慢。

  1. 调用百度文字识别api上传图片到百度云
    这里使用鉴权方式二,填入请求头中的Authorization参数,以及x-bce-date时间在生成的Authorization中可以找到,access_token的调用方式一则不需要设置请求头。
Function postBaiduOCR(Url)
    Set xmlHttp=CreateObject("Microsoft.XMLHTTP")
    xmlHttp.Open "POST", Url, False
    xmlHttp.setRequestHeader "Host", "aip.baidubce.com"
    xmlHttp.setRequestHeader "x-bce-date", "2019-02-15T02:23:01Z"
    authorization = "your_authorization"
    xmlHttp.setRequestHeader "Authorization", authorization
    //答案选项列表图片base64
    base64str = ImagesToBase64("D:\temp\ZYHXCaptcha\answer.jpg")
    //图片base64后再urlencode
    tmp = URLEncode(base64str)
    TracePrint "URLEncode="&tmp
    xmlHttp.send "image="&tmp
    If xmlHttp.readyState=4 then 
        response = xmlHttp.ResponseText
    End If
    xmlHttp.Abort 
    Set xmlHttp = Nothing
    TracePrint "调用百度识别返回结果=" & response
    postBaiduOCR = response
End Function    
  1. 得到返回的json数据并进行解析,可以参考这篇文章:按键精灵中解析json数据
    百度文字识别接口返回的json数据示例
Set sc = CreateObject("MSScriptControl.ScriptControl")
sc.Language = "JScript"
sc.AddCode "var o = " & json_result & ";"
//获取第i个words并去除两边空格,然后统一转成大写
word_str = UCase(Trim(sc.Eval("o.words_result[1].words")))
//然后可以逐个字符跟若快返回结果进行比较 TODO

测试过程中发现识别误差率高的字符有W容易被识别成N/MM被识别成N/H,以及第三个字符是Y识别返回成V需要做一点判断处理

通过对比若快返回验证码识别结果以及百度OCR返回的选项列表可以确定第几个是正确的验证码选项,最后可以dm.CmpColor找特定点颜色(“神医”十字边缘点黄色),判断是否快到时间,则点击按钮开始校验。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 常在网上晃悠的人,对下面这张图都不会陌生。特别是在注册新账号、确认交易时,它们都会频繁出现,要求我们输入正确的验证...
    datazhen阅读 953评论 0 0
  • 小波哥哥叫帮忙投票的网站有验证码,从网站下载的发送post包刷票的软件不能直接使用。所以写了这个脚本。通过大漠插件...
    禾白小三飘阅读 2,907评论 0 5
  • 巧纳隆中策, 腹藏锦绣诗。 空悲风露劲, 傲雪待新枝。
    似蘭斯馨阅读 384评论 2 9
  • 体验:今天C6维修完毕,心终于落地了,明天开始接车,核心:没有修不好的车,只有修不好车的修理工和不愿花钱的车主,转...
    郭家乐阅读 141评论 0 0
  • 待到秋闱御栏颜 门前自有种花家 力尽东藩战海关 马绝雪域勒石山
    傅南阅读 170评论 0 0