Cydia Substrate是一个代码修改工具,可以修改IOS、Android系统代码,从而实现自己的奇思妙想。Cydia的使用方法教程不再赘述,下面直接进入实战。
一台Root过得Android设备,Android系统版本2.3-4.3。我当初不信邪,以为Android4.4肯定也可以,结果搞了半天发现是真的不行,所以还是谨遵官网指导,版本最高4.3,不能再高了。不知道怎么准备手机,可以看我的这篇文章如何搞机。
现在比较流行的是微信运动。微信做的真的很完善了,本希望反编译它的apk来找一个切入点,结果有两大难点让我无法进行下去:
这两点着实让我心灰意冷,如果哪位仁兄研究过,还请你赐教。还有,我不太清楚微信怎么自己统计步数的。
最后我找了一款比较流行的计步软件,是哪一款我就不说了,这个APP通过加速度传感器数据来计算步数。
我同样也是先反编译这个APP,结果问题和微信一样,暂时无法解决。最后,我选择了不Hook应用,而是Hook Android的API。这款软件采用了加速计数据统计步数,我则着眼于系统的加速计事件的产生过程,从中选择可以Hook的点。
Android系统事件的收发机制很有意思,下面我们来研究一下一个系统事件,从注册监听到接收事件的过程。这里我只关注加速计事件。
Android应用开发,想要利用系统事件,则需要构造一个Listener,这里就是SensorEventListener。首先,你需要得到系统的SensorManager
,然后从中获得想要的传感器,然后让SensorManager将该传感器注册入自己的Listener。
//获得传感器管理器
SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
//获得加速度传感器
Sensor mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//注册监听器
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
我写的代码里的this
就是这个Activity,我让Activity实现了SensorEventListener接口,监听器需要实现的方法如下:
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType()==Sensor.TYPE_ACCELEROMETER){
double ax=event.values[0];
double ay=event.values[1];
double az=event.values[2];
Log.d("YOULOG", ax+" "+ay+" "+az);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
看完APP里的代码,让我们深入一层,看一下Android Framework里,监听器是怎么注册进系统的。与传感器相关的API放置于android.hardware
包中,SensorManager是一个基本类,具体实现在SystemSensorManager
中。其中registerListener
函数在SensorManager
中,具体实现为SystemSensorManager的函数registerListenerImpl
:
@Override
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
int delay, Handler handler)
{
// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
// We map SensorEventListener to a SensorEventQueue, which holds the looper
if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
// Trigger Sensors should use the requestTriggerSensor call.
if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) return false;
synchronized (mSensorListeners) {
//确定该listener是否已经注册过,如果注册过,则已经有event queue与之对应
SensorEventQueue queue = mSensorListeners.get(listener);
if (queue == null) {
//第一次注册,需要创建一个event queue
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
queue = new SensorEventQueue(listener, looper, this);
//将这种sensor放入queue中,告诉系统,这种sensor我需要进行监听。
if (!queue.addSensor(sensor, delay)) {
queue.dispose();
return false;
}
mSensorListeners.put(listener, queue);
return true;
} else {
//已经有event queue,所以直接将sensor放入queue
return queue.addSensor(sensor, delay);
}
}
}
由此可知,SensorListener和SensorEventQueue一一对应,但是一个SensorListener可以监听多个Sensor。比如,一个Activity实现了监听加速计的,也实现了监听网络连接的接口,所以它可以监听多个Sensor。如图所示:
在queue.addSensor(sensor, delay)
函数中,它将sensor放入mActiveSensors
,让其准备好接受该sensor的事件。同时,调用了enableSensor
函数,它会调用native方法,将SensorEventQueue和这个sensor告知底层的传感器服务。
在传感器事件产生时,native方法会调用SensorEventQueue的dispatchSensorEvent函数来发出Event:
// Called from native code.
@SuppressWarnings("unused")
@Override
protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
long timestamp) {
final Sensor sensor = sHandleToSensor.get(handle);
SensorEvent t = mSensorsEvents.get(handle);
if (t == null) {
Log.e(TAG, "Error: Sensor Event is null for Sensor: " + sensor);
return;
}
// Copy from the values array.
System.arraycopy(values, 0, t.values, 0, t.values.length);
t.timestamp = timestamp;
t.accuracy = inAccuracy;
t.sensor = sensor;
switch (t.sensor.getType()) {
// Only report accuracy for sensors that support it.
case Sensor.TYPE_MAGNETIC_FIELD:
case Sensor.TYPE_ORIENTATION:
// call onAccuracyChanged() only if the value changes
final int accuracy = mSensorAccuracies.get(handle);
if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
mSensorAccuracies.put(handle, t.accuracy);
mListener.onAccuracyChanged(t.sensor, t.accuracy);
}
break;
default:
// For other sensors, just report the accuracy once
if (mFirstEvent.get(handle) == false) {
mFirstEvent.put(handle, true);
mListener.onAccuracyChanged(
t.sensor, SENSOR_STATUS_ACCURACY_HIGH);
}
break;
}
mListener.onSensorChanged(t);
}
该函数参数handle
指明了传感器种类,values
则是值。这里加速计传感器的hanlde为0(Sensor
中有public static final int TYPE_ACCELEROMETER = 1;
和该值不对应,原因还未细究)。这里就是我们需要利用Cydia修改的地方。
经过输出values可以发现,设备在不停的发送加速计事件,我不确定是因为加速计值一直在变还是因为周期采样的原因,总之该类事件在不断发送中。应用通过一定的算法来将这些数据进行处理,辨别出其中是否有步行导致的加速变化。我尝试过生硬的让该值不断发生跳变,发现应用没有做出反应。不难发现,人的行走是有规律且成周期性变化的,因此加速计采样值也应该有一定的周期性变化,所以,最后代码如下:
package com.tianchi.hellocydia;
import android.hardware.Sensor;
import android.util.Log;
import com.saurik.substrate.MS;
import java.lang.reflect.Method;
/**
* Created by Tianchi on 16/7/12.
*/
public class Main {
static int count = 0;
//每次变化角度为60°
static int step=60;
static void initialize() {
MS.hookClassLoad("android.hardware.SystemSensorManager$SensorEventQueue", new MS.ClassLoadHook() {
public void classLoaded(Class> resources) {
Method method2;
try {
method2 = resources.getDeclaredMethod("dispatchSensorEvent", int.class, float[].class, int.class, long.class);
} catch (NoSuchMethodException e) {
method2 = null;
}
if (method2 != null) {
method2.setAccessible(true);
final MS.MethodPointer old = new MS.MethodPointer();
MS.hookMethod(resources, method2, new MS.MethodHook() {
public Object invoked(Object resources, Object... args) throws Throwable {
int handle = (int) args[0];
float[] values = (float[]) args[1];
if (handle == 0) {
count += step;
count = count % 360;
double rd=Math.toRadians((double)count);
//利用sin函数,使其呈现周期性变化
float dev=(float) (Math.random()-0.5)*2;
values[0] = 10*(float) Math.sin(rd)+dev;
values[1] = 20*(float) Math.cos(rd)+dev;
values[2] = 9.8f+10*(float) Math.sin(rd)+dev;
}
args[1] = values;
old.invoke(resources, args);
return null;
}
}, old);
}
}
});
}
}
经测试,该方法可行。