使用按鍵精靈腳本實現《自由幻想》游戲內"神醫"驗證碼自動校驗

按鍵精靈編寫的腳本簡單方便,最直接的體驗是可以代替我們處理簡單重復的鼠標鍵盤操作。
《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找特定點顏色(“神醫”十字邊緣點黃色),判斷是否快到時間,則點擊按鈕開始校驗。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 常在網上晃悠的人,對下面這張圖都不會陌生。特別是在注冊新賬號、確認交易時,它們都會頻繁出現,要求我們輸入正確的驗證...
    datazhen閱讀 988評論 0 0
  • 小波哥哥叫幫忙投票的網站有驗證碼,從網站下載的發送post包刷票的軟件不能直接使用。所以寫了這個腳本。通過大漠插件...
    禾白小三飄閱讀 2,941評論 0 5
  • 巧納隆中策, 腹藏錦繡詩。 空悲風露勁, 傲雪待新枝。
    似蘭斯馨閱讀 402評論 2 9
  • 體驗:今天C6維修完畢,心終于落地了,明天開始接車,核心:沒有修不好的車,只有修不好車的修理工和不愿花錢的車主,轉...
    郭家樂閱讀 143評論 0 0
  • 待到秋闈御欄顏 門前自有種花家 力盡東藩戰海關 馬絕雪域勒石山
    傅南閱讀 181評論 0 0