Cydia Substrate 初尝试:刷计步软件数据

Cydia Substrate 初尝试:刷计步软件数据


  • Cydia Substrate 初尝试刷计步软件数据
    • Cydia
    • 设备准备
    • 探索
    • 思路
      • 监听器注册
      • 事件分发
    • 进行Hook


Cydia

Cydia Substrate是一个代码修改工具,可以修改IOS、Android系统代码,从而实现自己的奇思妙想。Cydia的使用方法教程不再赘述,下面直接进入实战。

设备准备

一台Root过得Android设备,Android系统版本2.3-4.3。我当初不信邪,以为Android4.4肯定也可以,结果搞了半天发现是真的不行,所以还是谨遵官网指导,版本最高4.3,不能再高了。不知道怎么准备手机,可以看我的这篇文章如何搞机。

探索

现在比较流行的是微信运动。微信做的真的很完善了,本希望反编译它的apk来找一个切入点,结果有两大难点让我无法进行下去:

  1. 代码量大:里面用了很多工具包,加上腾讯自己开发的代码,代码量着实不小。但是我们关注的点很明确,所以可以根据包名、类名来寻找,所以还是可以克服的。
  2. 代码名称混淆:里面好多类名和方法名反编译不出来,只能边看边猜,阅读难度略大。还有一些类直接反编译失败,所以我也是无能为力了,看的云里雾里,没找到正确的切入点。

这两点着实让我心灰意冷,如果哪位仁兄研究过,还请你赐教。还有,我不太清楚微信怎么自己统计步数的。

最后我找了一款比较流行的计步软件,是哪一款我就不说了,这个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。如图所示:
Cydia Substrate 初尝试:刷计步软件数据_第1张图片

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修改的地方。

进行Hook

经过输出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);
                }
            }
        });
    }
}

经测试,该方法可行。

你可能感兴趣的:(Android,hook,Cydia)