Android 之 “摇一摇”广告机制逆向分析

alt text

概述

  • 摇一摇”开屏广告是一种通过摇动手机触发广告跳转的广告形式,由芒果TV发明并申请了相关专利。
  • 该功能在App中普遍存在,但由于其高灵敏度,常在非用户主动操作的情况下触发广告跳转,侵犯用户权益,
  • 工信部发布通知,要求移动互联网应用不得利用高灵敏度“摇一摇”等方式诱导用户操作。

实现机制

“摇一摇”广告功能通常通过调用手机中的加速度、重力、陀螺仪等一种或多种传感器实现,当用户摇动手机触发相应的传感器阈值后可实现广告交互。

监管要求

根据电信终端产业协会发布的(T/TAF078.7-2022)《APP用户权益保障测评规范 第7部分:欺骗误导强迫行为》

  • 加速度不小于15m/s²
  • 转动角度不小于35°
  • 操作时间不少于3秒

定位函数

实现原理分析可知,App注册传感器监听器之后,需要传递一个监听器对象,这个对象包含了 onSensorChangedonAccuracyChanged 两个方法,对安装包进行反编译查找 onSensorChanged 相关代码

分析案例

京东广告

示例应用:喜马拉雅.apk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.jd.ad.sdk.bl.dynamicrender;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import com.umeng.analytics.pro.ak;

/* loaded from: classes13.dex */
public abstract class ShakeListener implements SensorEventListener {
public SensorManager jad_an;
public int jad_bo;

public ShakeListener(Context context, int i) {
this.jad_bo = 19;
try {
this.jad_an = (SensorManager) context.getSystemService(ak.ac);
} catch (SecurityException e2) {
e2.printStackTrace();
}
this.jad_bo = i;
}

@Override // android.hardware.SensorEventListener
public void onAccuracyChanged(Sensor sensor, int i) {
}

@Override // android.hardware.SensorEventListener
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == 1) {
float[] fArr = sensorEvent.values;
if (Math.sqrt(Math.pow(fArr[2], 2.0d) + Math.pow(fArr[1], 2.0d) + Math.pow(fArr[0], 2.0d)) > this.jad_bo) {
onShake();
unregister();
}
}
}

public abstract void onShake();

public void register() {
SensorManager sensorManager = this.jad_an;
if (sensorManager != null) {
sensorManager.registerListener(this, sensorManager.getDefaultSensor(1), 2);
}
}

public void unregister() {
SensorManager sensorManager = this.jad_an;
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
}
}

onSensorChanged方法中,它使用的算法

1
2
3
4
if (Math.sqrt(Math.pow(fArr[2], 2.0d) + Math.pow(fArr[1], 2.0d) + Math.pow(fArr[0], 2.0d)) > this.jad_bo) {
onShake();
unregister();
}

通过计算三轴值分别平方的和,再开方,与 this.jad_bo 的值进行比较,如果大于则进入摇一摇逻辑。

ShakeListener 的构造函数中 this.jad_bo 的默认值是 19,通过第二个参数 i 赋值动态的阀值给 this.jad_bo

淘宝广告

示例应用:天猫.apk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package android.taobao.windvane.jsbridge.api;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.taobao.windvane.util.TaoLog;

/* loaded from: classes.dex */
public class ShakeListener implements SensorEventListener {
private static final int SPEED_THRESHOLD = 10;
private long mCheckFrequency;
private Context mContext;
private long mLastUpdateTime;
private float mLastX;
private float mLastY;
private float mLastZ;
private SensorManager mSensorManager;
private OnShakeListener mShakeListener;

/* loaded from: classes.dex */
public interface OnShakeListener {
void onShake();
}

public ShakeListener(Context context, long j) {
this.mContext = context;
this.mCheckFrequency = j;
start();
}

@Override // android.hardware.SensorEventListener
public void onAccuracyChanged(Sensor sensor, int i) {
}

@Override // android.hardware.SensorEventListener
public void onSensorChanged(SensorEvent sensorEvent) {
OnShakeListener onShakeListener;
if (sensorEvent.sensor.getType() != 1) {
return;
}
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis - this.mLastUpdateTime < this.mCheckFrequency) {
return;
}
float[] fArr = sensorEvent.values;
float f = fArr[0];
float f2 = fArr[1];
float f3 = fArr[2];
float f4 = f - this.mLastX;
float f5 = f2 - this.mLastY;
float f6 = f3 - this.mLastZ;
if (Math.sqrt((f4 * f4) + (f5 * f5) + (f6 * f6)) > 10.0d && (onShakeListener = this.mShakeListener) != null && onShakeListener != null && Math.abs(this.mLastX) > 0.0f && Math.abs(this.mLastY) > 0.0f && Math.abs(this.mLastZ) > 0.0f) {
this.mShakeListener.onShake();
}
this.mLastUpdateTime = currentTimeMillis;
this.mLastX = f;
this.mLastY = f2;
this.mLastZ = f3;
}

public void pause() {
SensorManager sensorManager = this.mSensorManager;
if (sensorManager != null) {
sensorManager.unregisterListener(this);
}
}

public void resume() {
SensorManager sensorManager = this.mSensorManager;
if (sensorManager == null || sensorManager.registerListener(this, sensorManager.getDefaultSensor(1), 2)) {
return;
}
this.mSensorManager.unregisterListener(this);
TaoLog.m263w("ShakeListener", "start: Accelerometer not supported");
}

public void setOnShakeListener(OnShakeListener onShakeListener) {
this.mShakeListener = onShakeListener;
}

public void start() {
SensorManager sensorManager = (SensorManager) this.mContext.getSystemService("sensor");
this.mSensorManager = sensorManager;
if (sensorManager == null) {
TaoLog.m263w("ShakeListener", "start: Sensors not supported");
} else {
if (sensorManager.registerListener(this, sensorManager.getDefaultSensor(1), 2)) {
return;
}
this.mSensorManager.unregisterListener(this);
TaoLog.m263w("ShakeListener", "start: Accelerometer not supported");
}
}

public void stop() {
SensorManager sensorManager = this.mSensorManager;
if (sensorManager != null) {
sensorManager.unregisterListener(this);
this.mSensorManager = null;
}
}
}

onSensorChanged 的方法中我们可以看到

1
2
3
if (sensorEvent.sensor.getType() != 1) {
return;
}

判断传感器类型不是加速度就终止向下执行

当加速度类型条件满足,首先做了时间间隔的判断

1
2
3
4
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis - this.mLastUpdateTime < this.mCheckFrequency) {
return;
}

并接着分别获取了三轴的值

1
2
3
4
float[] fArr = sensorEvent.values;
float f = fArr[0];
float f2 = fArr[1];
float f3 = fArr[2];

计算三轴两次的变化差异

1
2
3
float f4 = f - this.mLastX;
float f5 = f2 - this.mLastY;
float f6 = f3 - this.mLastZ;

接着就是一个判断函数

当满足条件,执行具体的摇一摇函数 this.mShakeListener.onShake();

1
2
3
if (Math.sqrt((f4 * f4) + (f5 * f5) + (f6 * f6)) > 10.0d && (onShakeListener = this.mShakeListener) != null && onShakeListener != null && Math.abs(this.mLastX) > 0.0f &&Math.abs(this.mLastY) > 0.0f && Math.abs(this.mLastZ) > 0.0f) {
this.mShakeListener.onShake();
}

最后, 保存本次的时间戳和三轴值

1
2
3
4
this.mLastUpdateTime = currentTimeMillis;
this.mLastX = f;
this.mLastY = f2;
this.mLastZ = f3;

分析判断条件

条件判断 1
1
Math.sqrt((f4 * f4) + (f5 * f5) + (f6 * f6))>10.0d

同样也是,这里计算三轴分别变化差异的平方和,再开方,得到sqrt 加速度的值

判断这个返回值 sqrt 是否大于 10.0d (单位:m/s²)

条件判断 2/3
1
(onShakeListener = this.mShakeListener) != null && onShakeListener != null 

接下来重点看这两个函数

1
2
3
4
5
6
7
8
public ShakeListener(Context context, long j) {
this.mContext = context;
this.mCheckFrequency = j;
start();
}
public void setOnShakeListener(OnShakeListener onShakeListener) {
this.mShakeListener = onShakeListener;
}

ShakeListener(Context context, long j) 是 ShakeListener 类的构造函数,传递了两个参数

在 setOnShakeListener(OnShakeListener onShakeListener) 方法中,this.mShakeListener 被 onShakeListener 赋值过,如果后续 this.mShakeListener 不再被赋值,那么 this.mShakeListener = onShakeListener;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public synchronized void listeningShake(WVCallBackContext wVCallBackContext, String str) {
boolean z;
WVResult wVResult = new WVResult();
long j = 500;
long j2 = 1000;
boolean z2 = false;
if (TextUtils.isEmpty(str)) {
z = false;
} else {
try {
str = URLDecoder.decode(str, "utf-8");
} catch (Exception unused) {
TaoLog.m254e("WVMotion", "listeningShake: param decode error, param=" + str);
z2 = true;
}
try {
JSONObject jSONObject = new JSONObject(str);
z = jSONObject.getBoolean(Baggage.Amnet.TURN_ON);
j = jSONObject.optLong("frequency", 500L);
j2 = jSONObject.optLong("checkFrequency", 1000L);
} catch (JSONException unused2) {
TaoLog.m254e("WVMotion", "listeningShake: param parse to JSON error, param=" + str);
wVResult.setResult("HY_PARAM_ERR");
wVCallBackContext.error(wVResult);
return;
}
}
if (z2) {
if (TaoLog.getLogStatus()) {
TaoLog.m263w("WVMotion", "listeningShake: isFail");
}
wVCallBackContext.error(wVResult);
return;
}
if (z) {
TaoLog.m254e("WVMotion", "listeningShake: start ...");
ShakeListener shakeListener = this.mShakeListener;
if (shakeListener != null) {
shakeListener.stop();
}
ShakeListener shakeListener2 = new ShakeListener(this.mContext, j2);
this.mShakeListener = shakeListener2;
shakeListener2.setOnShakeListener(new MyShakeListener(wVCallBackContext, j));
wVCallBackContext.success(wVResult);
} else {
TaoLog.m254e("WVMotion", "listeningShake: stop.");
Message message = new Message();
message.what = 1;
message.obj = wVCallBackContext;
Handler handler = this.handler;
if (handler != null) {
handler.sendMessage(message);
}
}
}

在以上片段中,重点看两句代码

1
2
ShakeListener shakeListener2 = new ShakeListener(this.mContext, j2); // ShakeListener类构造函数 ShakeListener(Context context, long j),被调用初始化
shakeListener2.setOnShakeListener(new MyShakeListener(wVCallBackContext, j)); // setOnShakeListener(OnShakeListener onShakeListener) 被调用
1
2
long j = 500;  // setOnShakeListener(OnShakeListener onShakeListener)方法参数 OnShakeListener 的第二个参数 j (默认500)
long j2 = 1000; // ShakeListener类构造函数 第二个参数 j2 传递 (默认1000)

这两个值分别默认 500、100,通过 listeningShake 方法第二个参数 str 传入的 String 转成 json 字符串判断是否存在,并动态赋值

继续看判断的条件 4、5、6

条件判断 4/5/6
1
Math.abs(this.mLastX) > 0.0f && Math.abs(this.mLastY) > 0.0f && Math.abs(this.mLastZ) > 0.0f

已知 this.mLastX / this.mLastY / this.mLastZ 分别表示三轴上一次数据
可以分析得知,条件 4、5、6 的意义

1
2
3
Math.abs(this.mLastX) > 0.0f // x轴绝对值大于0
Math.abs(this.mLastY) > 0.0f // y轴绝对值大于0
Math.abs(this.mLastZ) > 0.0f // z轴绝对值大于0

即条件4、5、6代表三轴的数据的绝对值都要大于0

实现案例

我们按以上的实现方式,模拟实现一个类似的摇一摇跳转广告示例

ShakeListener.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.zcwx.sensorshake

import android.app.Activity
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
import android.widget.TextView
import java.util.Locale
import kotlin.math.acos
import kotlin.math.sqrt

class ShakeListener(//上下文
private val context: Context,
private val accLog: TextView,
) : SensorEventListener {

//传感器管理器
private var sensorManager: SensorManager? = null

//传感器
private var accSensor: Sensor? = null

//重力感应监听器
private var onShakeListener: OnShakeListener? = null

//手机上一个位置时重力感应坐标
private var lastX = 0f
private var lastY = 0f
private var lastZ = 0f

//构造器
init {
//获得监听对象
start()
}

//开始
private fun start() {
//获得传感器管理器
sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
if (sensorManager != null) {
//获得加速度传感器
accSensor = sensorManager?.getDefaultSensor(1)
}
//注册
if (accSensor != null) {
sensorManager?.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
}

//停止检测
fun stop() {
sensorManager?.unregisterListener(this)
}

//摇晃监听接口
interface OnShakeListener {
fun onShake()
}

//设置重力感应监听器
fun setOnShakeListener(listener: OnShakeListener?) {
onShakeListener = listener
}

//重力感应器感应获得变化数据
override fun onSensorChanged(event: SensorEvent) {
when (event.sensor.type) {
1 -> {
//获得x,y,z坐标
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]

lastX = x
lastY = y
lastZ = z

val speed = String.format (Locale.getDefault(),"%.1f", sqrt(lastX * lastX + lastY * lastY + lastZ * lastZ),).toDouble()

Log.d("TAG", "速度:$speed")

val str = "加速度\n" +
"X:\t${event.values[0]}\n" +
"Y:\t${event.values[1]}\n" +
"Z:\t${event.values[2]}\n" +
"sqrt:\t$speed\n"

accLog.text = str

//达到速度阀值,发出提示
if (speed >= SPEED_SHRES_HOLD) {
Log.d("TAG", "onSensorChanged: 触发摇一摇,跳转广告页面了...")
Log.d("TAG", "x:$x,y:$y,z:$z")
Log.d("TAG", "速度:$speed")
onShakeListener!!.onShake()
}
}
}

}

override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
}

companion object {
//速度阈值
private const val SPEED_SHRES_HOLD = 15
}
}

MainActivity.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.zcwx.sensorshake

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat


class MainActivity : AppCompatActivity() {

companion object{
private const val TAG = "MainActivity"
}

private lateinit var shakeListener: ShakeListener

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}

shakeListener = ShakeListener(this, findViewById(R.id.tvAccLog)) //创建一个对象
shakeListener.setOnShakeListener(object: ShakeListener.OnShakeListener{
override fun onShake() {
Toast.makeText(this@MainActivity, "摇一摇,手机摇晃了🫨", Toast.LENGTH_SHORT).show()
onVibrator().let {
openLinkInBrowser()
}
}
})
}

override fun onDestroy() {
super.onDestroy()
shakeListener.stop() //停止监听
}

private fun openLinkInBrowser() {
Log.d(TAG, "openLinkInBrowser: ")
val url = "https://www.apple.com/" // 要打开的链接地址
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
}

private fun onVibrator() {
Log.d(TAG, "onVibrator: ")
val vibrator:Vibrator
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.S) {
val vibratorManager: VibratorManager = getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibrator= vibratorManager.defaultVibrator
}else{
vibrator= getSystemService(VIBRATOR_SERVICE) as Vibrator
}
vibrator.vibrate(VibrationEffect.createOneShot(500, 255))
}
}

AndroidManifest.xml

1
<uses-permission android:name="android.permission.VIBRATE" />

运行

1
2
3
4
5
6
7
8
9
10
11
12
2024-12-25 14:28:08.830  6734-6734  ShakeListener           com.zcwx.sensorshake                 D  x:0.51834464,y:-0.094570965,z:9.829395 	 speed:9.8
2024-12-25 14:28:09.030 6734-6734 ShakeListener com.zcwx.sensorshake D x:0.5177461,y:-0.091578215,z:9.829993 speed:9.8
2024-12-25 14:28:09.227 6734-6734 ShakeListener com.zcwx.sensorshake D x:0.5177461,y:-0.093373865,z:9.829395 speed:9.8
2024-12-25 14:28:09.424 6734-6734 ShakeListener com.zcwx.sensorshake D x:0.5177461,y:-0.093972415,z:9.831789 speed:9.8
2024-12-25 14:28:09.631 6734-6734 ShakeListener com.zcwx.sensorshake D x:19.515951,y:-0.094570965,z:9.834184 speed:21.9
2024-12-25 14:28:09.636 6734-6734 ShakeListener com.zcwx.sensorshake D onSensorChanged: 触发摇一摇,跳转广告页面了...
2024-12-25 14:28:09.636 6734-6734 ShakeListener com.zcwx.sensorshake D x:19.515951,y:-0.094570965,z:9.834184
2024-12-25 14:28:09.636 6734-6734 ShakeListener com.zcwx.sensorshake D 速度:21.9
2024-12-25 14:28:09.643 6734-6734 MainActivity com.zcwx.sensorshake D onVibrator:
2024-12-25 14:28:09.647 6734-6734 MainActivity com.zcwx.sensorshake D openLinkInBrowser:
2024-12-25 14:28:09.827 6734-6734 ShakeListener com.zcwx.sensorshake D x:0.5177461,y:-0.092176765,z:9.835381 speed:9.8
2024-12-25 14:28:10.024 6734-6734 ShakeListener com.zcwx.sensorshake D x:0.521936,y:-0.093972415,z:9.834782 speed:9.8

合规检测

检测加速度不小于15m/s²

假如,现在需要模拟手机摇一摇来测试广告的阀值,检测广告是否合规,只考虑加速度的情况下

可以通过Frida Hook的方式,模拟加速度数据

hook_sensor.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
var isHooked = false;
var SPEED_SHRES_HOLD = 15 // 假定阀值

function main() {
Java.perform(function () {
var Random = Java.use("java.util.Random");
var random = Random.$new()
var SystemSensorManagerSensorEventQueue = Java.use("android.hardware.SystemSensorManager$SensorEventQueue");
SystemSensorManagerSensorEventQueue.dispatchSensorEvent.implementation = function (type, values, accuracy, timestamp) {
if (type === 11) { // ACCELEROMETER 的值为 11
if (!isHooked) {
isHooked = true;
// 原始的x, y, z值
var originalX = values[0];
var originalY = values[1];
var originalZ = values[2];
// 计算原始的加速度大小
var originalSpeed = Math.sqrt(Math.pow(originalX, 2.0) + Math.pow(originalY, 2.0) + Math.pow(originalZ, 2.0));
// 打印原始值
console.log("\n原始值: [ x=" + originalX + ", y=" + originalY + ", z=" + originalZ + "] speed=" + originalSpeed + " m/s^2");
// 模拟摇一摇操作,设置加速度值
console.log("\n模拟摇一摇操作,设置加速度值");
// 随机选择x, y, z中的一个值进行调整
var indexToAdjust = random.nextInt(3);
var newValues = [originalX, originalY, originalZ];
if (SPEED_SHRES_HOLD < 9.8) SPEED_SHRES_HOLD = 9.8;
// 随机生成新的值
if (indexToAdjust == 0) {
var xSquared = (Math.pow(SPEED_SHRES_HOLD, 2.0)) - (Math.pow(newValues[1], 2.0) + Math.pow(newValues[2], 2.0));
if (xSquared < 0) {
console.error("Cannot generate valid acceleration X values.");
return;
}
newValues[0] = Math.sqrt(xSquared);
}
if (indexToAdjust == 1) {
var ySquared = (Math.pow(SPEED_SHRES_HOLD, 2.0)) - (Math.pow(newValues[0], 2.0) + Math.pow(newValues[2], 2.0));
if (ySquared < 0) {
console.error("Cannot generate valid acceleration Y values.");
return;
}
newValues[1] = Math.sqrt(ySquared);
}
if (indexToAdjust == 2) {
var zSquared = (Math.pow(SPEED_SHRES_HOLD, 2.0)) - (Math.pow(newValues[0], 2.0) + Math.pow(newValues[1], 2.0));
if (zSquared < 0) {
console.error("Cannot generate valid acceleration Z values.");
return;
}
newValues[2] = Math.sqrt(zSquared);
}

// 取出新的值
var newX = newValues[0];
var newY = newValues[1];
var newZ = newValues[2];

// 计算新的加速度
var newSpeed = Math.sqrt(Math.pow(newX, 2.0) + Math.pow(newY, 2.0) + Math.pow(newZ, 2.0));
// 设置新的加速度值
values[0] = newX;
values[1] = newY;
values[2] = newZ;
// 打印新的值和新的加速度大小
console.log("\n新的值: [ x=" + newX + ", y=" + newY + ", z=" + newZ + "] speed=" + newSpeed + " m/s^2");
}
}
return this.dispatchSensorEvent.call(this, type, values, accuracy, timestamp);
};
});
}
setTimeout(main, 5000); // 执行脚本,5秒后触发main函数

执行hook 脚本 hook_sensor.js,输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(.venv) ~/frida-agent-example $ frida -U -f com.zcwx.sensorshake -l ./android/hook_sensor.js
____
/ _ | Frida 16.4.8 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to LE2110 (id=988b0e34)
Spawned `com.zcwx.sensorshake`. Resuming main thread!
[LE2110::com.zcwx.sensorshake ]->
原始值: [ x=0.33279404044151306, y=8.785523414611816, z=4.349067211151123] speed=9.80869813732463 m/s^2

模拟摇一摇操作,设置加速度值

新的值: [ x=11.3534220710849, y=8.785523414611816, z=4.349067211151123] speed=15.000000000000002 m/s^2
[LE2110::com.zcwx.sensorshake ]->

静态情况下,加速度为9.8 m/s²,通过Hook的方式,模拟出加速度为15+ m/s²,从而自动触发阀值,跳转模拟的广告页面