繞過最新補丁,AR紅包再遭技術(shù)流破解
一、前言
最近圈里流行破解支付寶的“AR紅包”,有使用PS進行圖片恢復的也有使用PHP腳本進行恢復的。當然支付寶那邊也沒閑著,先是聲稱減小了PS恢復圖片后識別成功的概率,然后又是限制每人每日開啟紅包的次數(shù),實話講這是好事,畢竟如果AR紅包步入Pokemon Go的后塵,那就失去了“尋寶”的樂趣。不過如果可以從技術(shù)上來解決問題,比如提高對于實景和偽裝圖片的判別能力,那必然是喜聞樂見的。
二、基于OpenCV恢復圖像破解“AR紅包”
前面提到PS恢復圖片識別成功的概率降低了許多,想想主要原因還是PS恢復出來的圖片太過粗糙,而且還留有一定的細線條,這樣機器進行識別時可以通過這些特征去判別圖片是否是PS的。本文介紹的方法是基于OpenCV對圖像進行處理,這種方法能夠盡可能地還原原圖像而不會產(chǎn)生條紋,目前成功率還不錯。
在介紹方法之前先分析一下加了黑線的圖片,可以對單一的淺色背景進行拍攝作為AR紅包的圖片,這樣就可以從圖片中了解黑線的分布。當然如果整幅圖都是一個顏色程序是不允許的,所以。。我放了個硬幣作為物體。
通過抓包得到的圖片是100*100像素的,而在AR紅包剛面世時圖片都是200*200像素的,后者圖片較為清晰,容易通過PS恢復,而前者較為模糊,因此PS難以對其進行較好的恢復,這也可能是支付寶的一個防作弊手段吧,但說實話,治標不治本,而且存在一個比較大的問題。
由于測試圖片背景單一,且大小只為100*100像素,因此我們可以用PS打開圖片并放大看看線條到底如何分布。
上圖取的是圖片的一部分,圖中每個小方格是一個像素點。可以看出,線條有一個像素點寬的,也有兩個像素點寬的,而線條顏色有黑和灰兩種。我們把
這種類型的稱為黑1,把
這種類型的稱為黑2,把
這種類型的稱為雜色,根據(jù)對整幅圖的觀察,其黑色線條分布規(guī)律為“黑2黑1黑2黑1黑2雜色”,相鄰兩種線條的距離為兩個像素點。這樣就可以確定線條的分布。
確定了線條的分布之后,就得考慮怎么去掉這些橫線。對于一張正常圖片而言,相鄰像素點的色差并不大,而加了橫線之后橫線附近的相鄰像素點色差非常大,如果可以將黑線部分的顏色修改為相鄰的未被處理的像素點的顏色,那就可以較好的恢復圖片的原貌。OpenCV提供了這項功能,廢話不多說,上代碼。(由于時間問題我直接將黑線條的像素坐標逐一處理,并沒有按照前面總結(jié)的規(guī)律進行處理)
- #include "stdafx.h"
- #include"cv.h"
- #include"highgui.h"
- #include "cvaux.h"
- #include "cxcore.h"
- #include "opencv2/opencv.hpp"
- #include "opencv2/imgproc.hpp"
- using namespace cv;
- int _tmain(int argc, _TCHAR* argv[])
- {
- Mat src = imread("test.jpg");
- imshow("Raw", src);
- int i, j;
- for (j = 0; j < src.cols; j++)
- for (i = 0; i < src.rows; i++)
- {
- if (i == 3 || i == 6 || i == 10 || i == 14 || i == 17 || i == 21 || i == 24 || i == 28 || i == 31 || i == 35 || i == 39 || i == 42 || i == 46 || i == 49 || i == 53 || i == 56 || i == 60 || i == 63 || i == 67 || i == 71 || i == 74 || i == 78 || i == 81 || i == 85 || i == 89 || i == 92 || i == 96)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- }
- if (i == 7 || i == 18 || i == 25 || i == 32 || i == 43 || i == 50 || i == 57 || i == 61 || i == 64 || i == 68 || i == 75 || i == 82 || i == 93)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i + 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i + 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i + 1, j)[2];
- }
- }
- imwrite("New.jpg", src);
- imshow("New.jpg", src);
- return 0;
- }
代碼中src.at
不過這也有一些缺陷,比如斜線可能會還原成鋸齒。對于支付寶的工作人員可以利用這個缺陷進行一下過濾。
前面說到的是100*100像素的圖片,接下來講講AR紅包剛推出時的200*200的圖片。方法基本相同,也是通過使用相鄰像素顏色修改黑線顏色來實現(xiàn),不同的是黑線的寬度和黑線分布的方式,只需把上述代碼的處理部分修改為如下所示即可處理。
- if (i == 5 || i == 55 || i == 105 || i == 155)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- }
- if (i == 48 || i == 97 || i == 148)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 1, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i + 1, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i + 1, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 2, j)[0] = src.at<Vec3b>(i + 4, j)[0];
- src.at<Vec3b>(i + 2, j)[1] = src.at<Vec3b>(i + 4, j)[1];
- src.at<Vec3b>(i + 2, j)[2] = src.at<Vec3b>(i + 4, j)[2];
- src.at<Vec3b>(i + 3, j)[0] = src.at<Vec3b>(i + 4, j)[0];
- src.at<Vec3b>(i + 3, j)[1] = src.at<Vec3b>(i + 4, j)[1];
- src.at<Vec3b>(i + 3, j)[2] = src.at<Vec3b>(i + 4, j)[2];
- }
- if (i>5 && i<48 && ((i + 1) % 7 == 0))
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 1, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i + 1, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i + 1, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 2, j)[0] = src.at<Vec3b>(i + 3, j)[0];
- src.at<Vec3b>(i + 2, j)[1] = src.at<Vec3b>(i + 3, j)[1];
- src.at<Vec3b>(i + 2, j)[2] = src.at<Vec3b>(i + 3, j)[2];
- }
- if (i > 48 && i < 97 && i % 7 == 0)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 1, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i + 1, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i + 1, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 2, j)[0] = src.at<Vec3b>(i + 3, j)[0];
- src.at<Vec3b>(i + 2, j)[1] = src.at<Vec3b>(i + 3, j)[1];
- src.at<Vec3b>(i + 2, j)[2] = src.at<Vec3b>(i + 3, j)[2];
- }
- if (i > 97 && i < 146 && ((i - 1) % 7 == 0))
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 1, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i + 1, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i + 1, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 2, j)[0] = src.at<Vec3b>(i + 3, j)[0];
- src.at<Vec3b>(i + 2, j)[1] = src.at<Vec3b>(i + 3, j)[1];
- src.at<Vec3b>(i + 2, j)[2] = src.at<Vec3b>(i + 3, j)[2];
- }
- if (i > 147 && i<(src.rows - 2) && ((i - 2)) % 7 == 0)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 1, j)[0] = src.at<Vec3b>(i - 1, j)[0];
- src.at<Vec3b>(i + 1, j)[1] = src.at<Vec3b>(i - 1, j)[1];
- src.at<Vec3b>(i + 1, j)[2] = src.at<Vec3b>(i - 1, j)[2];
- src.at<Vec3b>(i + 2, j)[0] = src.at<Vec3b>(i + 3, j)[0];
- src.at<Vec3b>(i + 2, j)[1] = src.at<Vec3b>(i + 3, j)[1];
- src.at<Vec3b>(i + 2, j)[2] = src.at<Vec3b>(i + 3, j)[2];
- }
- if (i == 43 || i == 93 || i == 143 || i == 193)
- {
- src.at<Vec3b>(i, j)[0] = src.at<Vec3b>(i + 2, j)[0];
- src.at<Vec3b>(i, j)[1] = src.at<Vec3b>(i + 2, j)[1];
- src.at<Vec3b>(i, j)[2] = src.at<Vec3b>(i + 2, j)[2];
- src.at<Vec3b>(i + 1, j)[0] = src.at<Vec3b>(i + 2, j)[0];
- src.at<Vec3b>(i + 1, j)[1] = src.at<Vec3b>(i + 2, j)[1];
- src.at<Vec3b>(i + 1, j)[2] = src.at<Vec3b>(i + 2, j)[2];
- }
來看看處理的效果。
三、后記
最后來說一下之前提到的支付寶使用100*100像素圖片代替200*200像素圖片存在的問題。由于后者清晰,因此容易進行PS,而前者比較模糊,PS效果不好,不過這也導致其識別效果不理想,按常理來說一個有棱有角的圖片應該比一個模糊的圖片容易識別的多,而圖片模糊會造成識別上的誤差,可能用戶對實景進行掃描也沒辦法得到紅包。因此個人認為比較好的解決方法應該是增強對于實景和圖片的區(qū)別能力,畢竟叫AR紅包,無法識別現(xiàn)實何來增強現(xiàn)實?