hook android api偽造設備信息做刷量

概述

Android平臺上app的統計數據都是基于Android的設備信息的,比如首次使用(激活),活躍(日活躍DAU,月活躍MAU)等都需要根據設備唯一信息來統計。不僅統計,業務需求也都是根據設備唯一信息來展開的,比如簽到,同一臺設備不能簽第二次,比如運營推廣發積分發紅包,每臺手機只能領取一次,再比如app分發渠道統計應用的激活,同一臺手機第一次使用某app才算激活等等這些場景都需要靠設備信息來唯一確定一臺手機。

怎么唯一確定一臺Android手機,Android程序員都知道,

  • IMEI
  • IMSI
  • SIM_SER sim卡序列號
  • MAC地址,
  • Android_ID android系統第一次啟動生成的一個唯一ID
  • Brand 手機品牌
  • Model 手機型號

這些信息基本能唯一定位一臺手機,為了便于使用,我們的設備ID一般取其中的部分字段做hash,比如hash(MAC地址+IMEI)。

有些刷量的工具依賴于虛擬機,開發虛擬機或者修改android模擬器來模擬不同的硬件設備。從分層角度上看,android系統從下到上可以抽象為這幾層 :設備硬件層,應用框架層API,和應用APP,其中設備+API 兩層就可以看做是實現了虛擬機。
應用層App直接與API打交道,相同的api調用,不同的返回就相當于不同的虛擬機了。

  • APP
  • API
  • 硬件設備

hook簡介

Cydia Substrate hook框架可以hook Java和 C native層的代碼,運行時改變代碼的實現,是一個很強大的hook工具,也能做為很強大的調試工具。

官網地址:http://www.cydiasubstrate.com/

SDK下載地址:http://asdk.cydiasubstrate.com/zips/cydia_substrate-r2.zip

demo地址:https://github.com/zencodex/cydia-android-hook.git

hook框架:https://cache.saurik.com/apks/com.saurik.substrate_0.9.4010.apk

Cydia Substrate的使用:

  1. 前提,手機需要root權限
  2. 手機上安裝hook框架apk
  3. 參考demo編寫hook的源程序
  4. 使用hook框架程序link,重啟手機驗證

注意hook是針對這個系統的,而不是某個特定的app。某個api被hook后,比如獲取手機號的api被hook,這臺手機上所有的app獲取的手機號都是被修改后的手機號,因此,如果手機上的設備相關api都被hook了,相當于是虛擬了一個新的手機了。

cydia substrate的使用可以參考這兩篇文章

http://www.csdn.net/article/2015-08-07/2825405
http://drops.wooyun.org/tips/8084

設備信息API的hook

獲取imei

Java 代碼

public String getIMEI() {
    String imei = "";
    TelephonyManager telepManager;
    telepManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
    imei = telepManager.getDeviceId();
    return imei;
}

獲取IMEI調用api是 android.telephony.TelephonyManager.getDeviceId(),
如何hook呢

            MS.hookClassLoad("android.telephony.TelephonyManager",
            new MS.ClassLoadHook() {

                @Override
                public void classLoaded(Class<?> clz) {

                    // hook getDeviceId
                    Method methodGetImei;
                    try {
                        methodGetImei = clz.getMethod("getDeviceId",
                                new Class<?>[0]);
                    } catch (NoSuchMethodException e) {
                        methodGetImei = null;
                    }
                    if (methodGetImei != null) {
                        final MS.MethodPointer old = new MS.MethodPointer();

                        MS.hookMethod(clz, methodGetImei,
                                new MS.MethodHook() {
                                    @Override
                                    public Object invoked(Object obj,
                                            Object... args)
                                            throws Throwable {
                                        int index = readIndex();
                                        return deviceIds[index];
                                    }
                                }, old);
                    }

            });
}

這樣,被hook的這臺android設備上調用TelephonyManager.getDeviceId()返回的就不再是設備的真實IMEI,而是deviceIds[index],deviceIds是一個設備IMEI號的數組,我們構造一個樣本足夠大deviceIds,就容易做到了IMEI號的偽造和刷量。

獲取imsi / sim卡序列號

android.telephony.TelephonyManager.getSubscriberId();

android.telephony.TelephonyManager.getSimSerialNumber();

imsi和sim卡序列號的hook方法與 imei的hook類似,不再重復。

mac地址

public String getMacAddress() {
    WifiManager wifi = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
    WifiInfo info = wifi.getConnectionInfo();
    return info.getMacAddress();
}

android.net.wifi.WifiInfo.getMacAddress();

getMacAddress()的hook與IMEI的寫法基本一樣,替換一下類名和方法名就可,
不再重復。

Android_Id

            // Secure.ANDROID_ID : public static final String ANDROID_ID = "android_id";
    return android.provider.Settings.System.getString(mContext.getContentResolver(),Secure.ANDROID_ID);

android_id 的hook方式

            MS.hookClassLoad("android.provider.Settings$Secure",
            new ClassLoadHook() {

                @Override
                public void classLoaded(Class<?> clz) {
                    // hook getAndroidId
                    Method methodGetAndroidId;
                    try {
                        methodGetAndroidId = clz.getMethod("getString",
                                ContentResolver.class, String.class);
                    } catch (NoSuchMethodException e) {
                        methodGetAndroidId = null;
                    }
                    if (methodGetAndroidId != null) {

                        final MS.MethodPointer old = new MS.MethodPointer();

                        MS.hookMethod(clz, methodGetAndroidId,
                                new MS.MethodHook() {
                                    @Override
                                    public Object invoked(Object obj,
                                            Object... args)
                                            throws Throwable {
                                        if ("android_id".equals(String
                                                .valueOf(args[1]))) {
                                            int index = readIndex();
                                            return andIds[index];
                                        }
                                        return old.invoke(obj, args);
                                    }
                                }, old);

                    }
                }
            });

Object invoked(Object obj,Object... args) 中 obj是指實例化的類對象,在當前這個場景下就是Secure的對象,args是obj.getString(ContentResolver,String)的參數列表,依次是ContentResolver對象和String類型的key,下標從0開始。args[0]是ContentResolver類型,args[1]是String類型,當args[1] == “android_id”時,也就是調用getString(ContentResolver,“android_id”)時,返回值被篡改,key為別的字符串時,按照原本的邏輯返回應有的屬性

return old.invoke(obj, args);

brand 和 model

android.os.Build.BRAND; // brand
android.os.Build.MODEL; // model;

brand和model是在Build類里定義的兩個靜態變量,沒發現cydia substrate針對類變量的修改機制,這個hook怎么做呢?請往下看:

/** The brand (e.g., carrier) the software is customized for, if any. */
public static final String BRAND = getString("ro.product.brand");

/** The end-user-visible name for the end product. */
public static final String MODEL = getString("ro.product.model");

注意到Brand和MODEL兩個變量初始化時調用了getString方法,hook住這個方法,根據傳人的參數做好判斷應該就可以了。繼續往下看:

  private static String getString(String property) {
    return SystemProperties.get(property, UNKNOWN);
}

這個方法是是private的,通過反射去getMethod時候麻煩一下,我們繼續往下走一步,來到了android.os.SystemProperties.get(String,String),于是乎,我們hook這個api。

    MS.hookClassLoad("android.os.SystemProperties", new ClassLoadHook() {
        
        @Override
        public void classLoaded(Class<?> clz) {
            // hook getString(String pro)
            Method methodGetString;
            try {
                methodGetString = clz.getMethod("get",
                         String.class,String.class);
            } catch (NoSuchMethodException e) {
                methodGetString = null;
            }
            if (methodGetString != null) {

                final MS.MethodPointer old = new MS.MethodPointer();

                MS.hookMethod(clz, methodGetString,
                        new MS.MethodHook() {
                            @Override
                            public Object invoked(Object obj,
                                    Object... args)
                                    throws Throwable {
                                if ("ro.product.brand".equals(String
                                        .valueOf(args[0]))) {
                                    int index = readIndex();
                                    return brands[index];
                                }if ("ro.product.model".equals(String
                                        .valueOf(args[0]))) {
                                    int index = readIndex();
                                    return models[index];
                                }
                                return old.invoke(obj, args);
                            }
                        }, old);

            }
        }
    });

源碼和demo

驗證的測試代碼

TelephonyManager telepManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
            String imei = telepManager.getDeviceId();
            String andId = Secure.getString(getContentResolver(),
                    "android_id");
            String mac = initMAC();

            String imsi = telepManager.getSubscriberId();
            String ss = telepManager.getSimSerialNumber();
            String brand = android.os.Build.BRAND;
            String model = android.os.Build.MODEL;
            String text = String.format("%s,%s,%s,%s,%s,%s,%s,---%s", imei,
                    andId, mac, imsi, ss,brand,model,String.valueOf(readIndex()));
            resText.setText(text);

hook前的設備真實數據

resText顯示 :null,6fdace0b66e3cac,null,,,FZS,FZS_Y80_NB,---30

hook后的偽造數據

resText顯示 :865761021428959,ab591f34ef3b9aa6,5c:f7:c3:33:b3:0c,460012538363135,606390041315,GiONEE,W900,---30

只要設備信息的數據空間足夠大,就可以創造很多的DAU,很多的激活。。。

當然,很多平臺都有自己的防作弊系統,比如會分析IP是否離散等等,單純修改設備信息可能不一定會有效果,需要自行評估。

hook程序的源代碼和測試demo

https://github.com/devxiaobai/android_device_info_hook.git

參考

http://www.csdn.net/article/2015-08-07/2825405

http://drops.wooyun.org/tips/8084

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容