前言
首先声明,此文仅用于技术交流,若用于牟利,后果自负!由于这个小游戏高分者可获得实体奖励,通过外挂作弊取得高分获取奖励实属诈骗,相信游戏团队也有辨别作弊的实力,请大家不要拿自己的信用作赌注,三思后行!
正文
最近,相信大家也被《疯狂足球》这个小游戏刷屏了,得分前三名送手机啊,再便宜也要上千块一部吧。我也玩了几天,得分最高只能取得280分,再也上不去了。看来我还是不适合玩游戏,我还是回归本行继续写我的Bug吧…  ̄□ ̄||
所以,就有了今天的这篇分享…
言归正传,我们开始分析游戏。这个游戏和微信《跳一跳》的玩法很相识,都是用按压的时间长度来控制力度。但是《疯狂足球》还得控制方向,就是手指按下时的点指向手指抬起时的点。
力度控制
按压的时间非常好控制,都是同一个值,触摸屏幕固定的毫秒数后力度会达到正中间,这样我们就能保证力度控制不会失误了。
方向控制
方向控制就稍微麻烦了点,我们可以通过判断截图的像素来获取球门的位置;也可以在球门位置上加一层透明的窗口,手动指示球门的位置;甚至可以人肉计算球门的位置,这个就看大家喜欢了。
实现
知道了按压时间、球门位置,再通过人肉测量足球的初始位置,那实现简直不是事。Swipe就可以搞定了。
假设球的初始位置是x:100,y:200
球门的位置是x:300,y400
按压时间250ms
那么实现代码为:
adb shell input swipe 100 200 300 400 250
搞定!So easy!终于可以像姚明一样踢足球了!
等等!~好像哪里不对!
如果这样做的话,我们每次踢球都是划出一条完美的直线,如果防作弊系统记录了我们的触摸坐标,我们岂不是一秒就被红牌警告?
所以不行,我们还得想想办法解决这个问题。
手指触摸移动坐标问题
要解决这个问题,我们还得模拟出真实的手指触摸移动坐标,试问生成技术哪家强?出门左拐找Deep Learning。但是生成模拟坐标我们需要大量的训练数据啊,数据去哪里找?所以,生成模拟坐标这一项我们就放弃吧…
既然无法模拟,那我们就用真人手指去操作吧。那么问题又来了,真人操作怎么做到精准的控制时间?考验我们变通能力的时候到了~
我们可以监听手指的触摸事件,在手指按下的瞬间开始计时,250毫秒之后向系统发送手指抬起的事件啊~我简直是太聪明了,就这么干。
上代码,人生苦短,我用python,5行代码搞定:
import subprocess
import time
import random
for ii in range(20):
output = subprocess.getstatusoutput('adb shell getevent -c 1')
time.sleep(random.uniform(0.150,0.155))
output = subprocess.getstatusoutput('adb shell "cat /sdcard/up_event > /dev/input/event1"')
time.sleep(2)
但是,运行下来你会发现,功能是ok了,但是时间控制得不准啊。好吧,又要创建shell又要执行adb,是无法控制好时间的了,那我们先看看代码吧。后面再说说怎么改进这个问题。
首先,adb shell getevent –c 1。getevent大家应该都知道了,这个命令可以打印出android设备的所有事件,触摸屏事件只是其一,这里我们不考虑其他事件的影响。-c 1这个参数一定要加,否则getevent会一直处于打印状态,无法返回,我们的程序就会卡在这一行代码上。然后cat /sdcard/up_event > /dev/input/event1这段代码是模拟手指抬起事件的。up_event中的数据如下图所示,16进制,可以通过cat /dev/input/event1 > /sdcard/events获得,然后截取最后一段手指抬起事件的数据就行了。/dev/input/event1是我手机的触摸屏设备,在其他手机上可能是event0或者其他。
时间控制不准问题
前面我们还遗留了一个问题,就是python代码时间控制不准。那我们可以将代码转移到手机内去执行,这样就可以避免启动shell和adb时间的不确定性了。如果把这个过程做成一个APP,你会发现APP完全没有权限调用getevent,即使是系统级APP也不行。所以我把这段程序改用Java写并编译成dex文件,chmod 777 crack.dex后,就可以执行了,当然,需要root权限。虽然时间上还是有些偏差,但是没有大问题。
Java代码:
public class Main {
public static void main(String args[]) {
for (int i = 0; i < 20; i++) {
runOperation();
}
}
static Random r = new Random();
static DataOutputStream dos = null;
static Process p;
private static void runOperation() {
try {
p = Runtime.getRuntime().exec("sh");
// 监听触摸屏事件
dos = new DataOutputStream(p.getOutputStream());
dos.write("getevent -c 1 /dev/input/event1".getBytes());
dos.writeBytes("\n");
dos.writeBytes("exit\n");
dos.flush();
BufferedReader successResult = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader errorResult = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// 输出错误信息
String s = null;
StringBuilder errorMsg = new StringBuilder();
while ((s = errorResult.readLine()) != null) errorMsg.append(s);
if (errorMsg.toString().length() > 0)
System.out.println("fail getevent : " + errorMsg.toString());
int ignore = successResult.read();
// 关闭输入输出
successResult.close();
errorResult.close();
dos.close();
//利用sleep时间,在另一个线程中打开sh
new Thread(new Runnable() {
@Override
public void run() {
try {
p = Runtime.getRuntime().exec("sh");
dos = new DataOutputStream(p.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(240 + r.nextInt(20));
// 模拟手指抬起事件
dos.write("cat /sdcard/up_event >> /dev/input/event1".getBytes());
dos.writeBytes("\n");
dos.writeBytes("exit\n");
dos.flush();
errorResult = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// 输出错误信息
errorMsg = new StringBuilder();
while ((s = errorResult.readLine()) != null) errorMsg.append(s);
if (errorMsg.toString().length() > 0)
System.out.println("fail cat : " + errorMsg.toString());
//关闭输入输出
errorResult.close();
dos.close();
Thread.sleep(2000);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行dex文件:dalvik –cp /sdcard/crack.dex packagename.Main
其他问题
即便做到了这种程度,还是无法躲过作弊检测的。一是我们这种做法的缺陷,在我们模拟了up事件之后,由于手指还没有离开,系统又会产生一个down事件和一系列move事件及up事件,如果游戏检测了这一部分触摸事件,那我们一秒就穿帮了。还有就是游戏制作方可以记录我们每一次游戏的得分,再通过概率分布的相关算法来检测我们是否作弊。
当然,这两个问题完全可以通过深度学习解决,如果有人能完美的模拟人的操作和得分的概率分布,那么防作弊检测将无法判断是不是人在玩游戏。除非他们也用上深度学习技术,GAN技术了解一下,哈哈。
谢谢观赏~