最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

軟件大廠,環(huán)境檢測(cè)思路和規(guī)避思路,安卓改機(jī)應(yīng)該改什么數(shù)據(jù)和參數(shù),安卓boot內(nèi)核修改

2023-06-25 12:48 作者:數(shù)字云信息技術(shù)  | 我要投稿

前言:

現(xiàn)在大廠的設(shè)備指紋層出不窮,但是想要確保穩(wěn)定性和唯一性高精準(zhǔn)其實(shí)也挺難的一件事,有的是通過設(shè)備信息比重進(jìn)行的設(shè)備ID唯一值確認(rèn)。比如A設(shè)備信息占比10%,B設(shè)備信息占比20%,當(dāng)比重超過60%以上,設(shè)備指紋才會(huì)發(fā)生變化。這樣的好處就是當(dāng)你只修改某一個(gè)字段的時(shí)候,設(shè)備指紋不發(fā)生變化。還有的干脆找一個(gè)隱蔽的并且唯一的設(shè)備信息,作為緩存,每次讀取緩存的方式去判斷,設(shè)備信息是唯一,比如常見的有Native獲取DRM,popen cat? /sys/devices/soc0/serial_number? ,svc讀取bootid并且保存到文件,netlinker獲取網(wǎng)卡。都是很常見并且隱蔽的的設(shè)備指紋。一個(gè)設(shè)備指紋大廠會(huì)使用多種方式去獲取,那么我們應(yīng)該如何進(jìn)行對(duì)抗,我也會(huì)在文章里面說一下我自己的見解和方案,如何在一個(gè)“最佳”點(diǎn)去解決問題


設(shè)備指紋:

設(shè)備指紋主要分為三部分,Java層設(shè)備指紋,Native設(shè)備指紋,popen執(zhí)行一些命令獲取設(shè)備信息,包括一些核心的設(shè)備指紋。


Android Id

聊到設(shè)備指紋最經(jīng)典的一個(gè)字段就是Android id,就我目前所知,他的獲取方式不下5種,分別介紹一下。


方法1:

最基礎(chǔ)的Android id獲取方式,這個(gè)不多說,直接Hook就行


//原始獲取android id

String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);

CLog.i(String.format("android_id -> 2222 %s", androidId));

方法2:

第一種獲取以后,系統(tǒng)會(huì)把Android id 保存起來,保存到一個(gè)HashMap里面,防止多次IPC初始化,所以為了驗(yàn)證第一種方法的準(zhǔn)確性,可以二次獲取cache。


和上面的Android id進(jìn)行對(duì)比,9.0以上需要繞過Android id 的反射限制


方法3:


方法3也是很基礎(chǔ)的Api,主要通過ContentResolver 進(jìn)行間接獲取,很多大廠也都在使用。


方法4:

通過query命令去查詢,獲取Android id,這種方式底層走的也是ContentResolver。


硬盤字節(jié)總大?。?/p>

在設(shè)備指紋里面,如果想回復(fù)出廠設(shè)置也能保證原有的設(shè)備信息,這個(gè)字段可以在服務(wù)端的相似度算法里面占比很重,可以以型號(hào)進(jìn)行分類。我之前測(cè)試過,回復(fù)出廠設(shè)置指紋也不發(fā)生變化的設(shè)備指紋核心的設(shè)備指紋就幾個(gè)。


比如硬盤大小,ipv6,還有一個(gè)就是MAC地址,這幾個(gè)設(shè)備指紋也是很核心的設(shè)備指紋。首先先介紹硬盤字節(jié)大小。也是三種獲取方法,但是方法底層都是一條系統(tǒng)調(diào)用。所以如果要進(jìn)行對(duì)抗的話,只需要在SVC層進(jìn)行處理即可。獲取三種方法如下,不建議分別進(jìn)行處理,可能會(huì)導(dǎo)致有地方泄漏,特別是直接開啟一條進(jìn)程通過execve去執(zhí)行,然后管道傳過來,導(dǎo)致很容易Hook不全。


jclass pJclass = env->FindClass("android/os/StatFs");

jmethodID id = env->GetMethodID(pJclass, "<init>", "(Ljava/lang/String;)V");

jobject pJobject =

? ? ? ? env->NewObject(pJclass, id, env->NewStringUTF("/storage/emulated/0"));


jlong i = env->CallLongMethod(pJobject, env->GetMethodID(pJclass, "getTotalBytes", "()J"));

LOG(ERROR) << "Java獲取getTotalBytes "<<i;




char buffer[1024];

FILE *fp = popen("stat -f /storage/emulated/0", "r");

if (fp != nullptr) {

? ? while (fgets(buffer, sizeof(buffer), fp) != nullptr) {

? ? ? ? //LOGI("ps -ef %s",buffer)

? ? ? ? LOG(INFO) << "stat -f /storage/emulated/0" << buffer;

? ? }

? ? pclose(fp);

}



struct statfs64 buf={};

if (statfs64("/storage/emulated/0", &buf) == -1) {

? ? LOG(ERROR) << "statfs64系統(tǒng)信息失敗";

? ? return;

}

LOG(INFO) << "f_type (文件系統(tǒng)類型): " << buf.f_type;

LOG(INFO) << "f_bsize (塊大小): " << buf.f_bsize;

LOG(INFO) << "f_blocks (總數(shù)據(jù)塊): " << buf.f_blocks;

LOG(INFO) << "f_bfree (空閑塊): " << buf.f_bfree;

LOG(INFO) << "f_bavail (非特權(quán)用戶可用的空閑塊): " << buf.f_bavail;

LOG(INFO) << "f_files (總文件節(jié)點(diǎn)數(shù)): " << buf.f_files;

LOG(INFO) << "f_ffree (空閑文件節(jié)點(diǎn)數(shù)): " << buf.f_ffree;

LOG(INFO) << "f_fsid (文件系統(tǒng) ID): " << buf.f_fsid.__val[0] << ", " << buf.f_fsid.__val[1];

LOG(INFO) << "f_namelen (最大文件名長(zhǎng)度): " << buf.f_namelen;


,Hook的Java方法,但是發(fā)現(xiàn)Native層并不能全量攔截,


上面這種方式只適合Java獲取,


Mac地址:


這個(gè)沒啥好說的,基礎(chǔ)字段,Java層獲取,netlink獲取,命令行獲取。讀文件獲取,四種獲取方法,和上面類似,直接在svc的 recvmsg,recv,recvfrom的after進(jìn)行數(shù)據(jù)包替換即可


如果判斷是netlink的消息,并且是獲取網(wǎng)卡類型直接對(duì)里面的數(shù)據(jù)包解析和替換即可。


case SC_recvmsg: {

? ? //LOGI("start handle SC_recvmsg systexit after")

? ? if (isMockFingerptint()) {

? ? ? ? NetlinkMacHandler::netlinkHandler_recmsg(tracee);

? ? }

? ? break;

}

case SC_recv:

case SC_recvfrom: {

? ? //LOGE("start handle SC_recvfrom systexit after")

? ? //recv底層走的recvfrom,所以不需要處理recvfrom

? ? if (isMockFingerptint()) {

? ? ? ? NetlinkMacHandler::netlinkHandler_recv(tracee);

? ? }

? ? break;

}


在讀文件獲取這塊因?yàn)榫W(wǎng)卡信息已經(jīng)在內(nèi)存里面,所以直接IO重定向過去即可。


常用的獲取網(wǎng)卡信息的文件,以wlan0為例子,場(chǎng)景的獲取目錄如下:可以cat獲取,也可以直接讀文件。


/sys/class/net/wlan0/address

/sys/devices/virtual/net/wlan0/address

...

附近網(wǎng)卡信息:

這個(gè)字段主要是監(jiān)控群控的一些信息的,主要作用是獲取當(dāng)前wifi 附近的人MAC信息的。


比如大廠一般檢測(cè)群控的手段就是獲取附近的網(wǎng)卡,如果有聚集性就可以認(rèn)為是群控。獲取的方式也也跟上面一樣,五種獲取方法。


獲取方法底層也是和MAC獲取方法一樣,底層都是netlink,比如可以直接執(zhí)行 popen獲取。


popen("ip neigh show", "r");

也可以直接直接讀文件,路徑如下:


/proc/net/arp

還可以直接netlink獲取,在收到消息以后判斷消息類型是 hdr->nlmsg_type == RTM_NEWNEIGH 直接進(jìn)行替換即可。


IPV6:


設(shè)個(gè)設(shè)備指紋也是很核心的設(shè)備指紋,這個(gè)玩意底層獲取也是netlink,但是netlink獲取,但是這塊處理很不好處理,我暫時(shí)也沒進(jìn)行處理。


常用的獲取方式比如,Java獲取,命令獲取。如果需要進(jìn)行替換的話,只需要處理命令行和Java的Hook即可。


命令行可以在對(duì)方執(zhí)行命令之前,將命令換成cat命令,去cat自己提前Mock好的文件,效果是一樣的。


當(dāng)然,還有另一種思路,其實(shí)這個(gè)字段可以服務(wù)端獲取,客戶端二次上報(bào),進(jìn)行匹配。


try {

? ? NetworkInterface networkInterface;

? ? InetAddress inetAddress;

? ? for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {

? ? ? ? networkInterface = en.nextElement();

? ? ? ? for (Enumeration<InetAddress> enumIpAddr = networkInterface.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {

? ? ? ? ? ? inetAddress = enumIpAddr.nextElement();

? ? ? ? ? ? if (inetAddress instanceof Inet6Address) {

? ? ? ? ? ? ? ? CLog.e("Java 獲取 ipv6 " + inetAddress.getHostAddress());

? ? ? ? ? ? }

? ? ? ? }

? ? }

} catch (Throwable ex) {

? ? CLog.e("printf ipv6 info error " + ex);

}


命令行獲取如下,ip命令獲取如下:


ip -6 addr show

打印的內(nèi)容如下:




1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000

? ? inet6 ::1/128 scope host

? ? ? ?valid_lft forever preferred_lft forever

3: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000

? ? inet6 fe80::b86c:79ff:fe96:4945/64 scope link

? ? ? ?valid_lft forever preferred_lft forever

10: rmnet_data0@rmnet_ipa0: <UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000

? ? inet6 fe80::2ad1:b5a0:792b:9ec4/64 scope link

? ? ? ?valid_lft forever preferred_lft forever

30: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 3000

? ? inet6 fe80::8670:a04c:b8cf:467c/64 scope link stable-privacy

? ? ? ?valid_lft forever preferred_lft forever


系統(tǒng)內(nèi)核信息:

這玩意底層走的都是uname函數(shù),直接對(duì)uname系統(tǒng)調(diào)用處理即可。獲取方法比如,可以直接svc調(diào)用uname函數(shù),也可以直接根據(jù)命令行。


修改的話也很簡(jiǎn)單,直接在uname的after里面直接對(duì)數(shù)據(jù)進(jìn)行替換即可。


uname -a


包名隨機(jī)路徑:

這個(gè)是一個(gè)非常非常核心的字段,就是/data/app/隨機(jī)Base64路徑/base.apk。


這個(gè)隨機(jī)路徑就是設(shè)備指紋,比如一些大廠會(huì)玩,讀取你微信的隨機(jī)路徑,獲取微信的包信息,然后獲取里面的隨機(jī)路徑。


比如微信,快手,京東,淘寶這種隨機(jī)路徑,作為核心的唯一設(shè)備指紋,只要你不卸載微信,或者其他大廠apk,你得設(shè)備指紋永遠(yuǎn)不發(fā)生變化,無論你如何修改他自己Apk里面的信息,跟他都不產(chǎn)生任何影響。


系統(tǒng)賬號(hào):

一般嘗試比如小米之類的,登入了指定賬號(hào),可以得到一個(gè)賬號(hào)的id信息,這個(gè)也需要處理一下,最好的辦法是不登入賬號(hào)。


環(huán)境檢測(cè):


檢測(cè)環(huán)境大多數(shù)圍繞Hunter的源碼檢測(cè)思路去復(fù)現(xiàn),很多都是Hunter的源碼,很多也都是行業(yè)內(nèi)沒有公開的一些檢測(cè)思路,現(xiàn)在市面上檢測(cè)已經(jīng)很多沒更新了,加速行業(yè)內(nèi)卷,我輩刻不容緩。?


Apk簽名:

提到環(huán)境檢測(cè)不得不說的就是Apk重打包檢測(cè),現(xiàn)在檢測(cè)方法千奇百怪,


模擬器檢測(cè):

Java層基礎(chǔ)的獲取api架構(gòu)啥的,這塊就不一一敘述了。


檢測(cè)溫度掛載文件:




int thermal_check() {

? ? DIR *dir_ptr;

? ? int count = 0;

? ? struct dirent *entry;

? ? if ((dir_ptr = opendir("/sys/class/thermal/")) != nullptr) {

? ? ? ? while ((entry = readdir(dir_ptr))) {

? ? ? ? ? ? if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {

? ? ? ? ? ? ? ? continue;

? ? ? ? ? ? }

? ? ? ? ? ? char *tmp = entry->d_name;

? ? ? ? ? ? if (strstr(tmp, "thermal_zone") != nullptr) {

? ? ? ? ? ? ? ? count++;

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? closedir(dir_ptr);

? ? } else {

? ? ? ? count = -1;

? ? }

? ? return count;

}


模擬器特征文件:

string simulator_files_check() {

? ? if (file_exist("/system/bin/androVM-prop")) {//檢測(cè)androidVM

? ? ? ? return "/system/bin/androVM-prop";

? ? } else if (file_exist("/system/bin/microvirt-prop")) {//檢測(cè)逍遙模擬器--新版本找不到特征

? ? ? ? return "/system/bin/microvirt-prop";

? ? } else if (file_exist("/system/lib/libdroid4x.so")) {//檢測(cè)海馬模擬器

? ? ? ? return "/system/lib/libdroid4x.so";

? ? } else if (file_exist("/system/bin/windroyed")) {//檢測(cè)文卓爺模擬器

? ? ? ? return "/system/bin/windroyed";

? ? } else if (file_exist("/system/bin/nox-prop")) {//檢測(cè)夜神模擬器--某些版本找不到特征

? ? ? ? return "/system/bin/nox-prop";

? ? } else if (file_exist("system/lib/libnoxspeedup.so")) {//檢測(cè)夜神模擬器

? ? ? ? return "system/lib/libnoxspeedup.so";

? ? } else if (file_exist("/system/bin/ttVM-prop")) {//檢測(cè)天天模擬器

? ? ? ? return "/system/bin/ttVM-prop";

? ? } else if (file_exist("/data/.bluestacks.prop")) {//檢測(cè)bluestacks模擬器? 51模擬器

? ? ? ? return "/data/.bluestacks.prop";

? ? } else if (file_exist("/system/bin/duosconfig")) {//檢測(cè)AMIDuOS模擬器

? ? ? ? return "/system/bin/duosconfig";

? ? } else if (file_exist("/system/etc/xxzs_prop.sh")) {//檢測(cè)星星模擬器

? ? ? ? return "/system/etc/xxzs_prop.sh";

? ? } else if (file_exist("/system/etc/mumu-configs/device-prop-configs/mumu.config")) {//網(wǎng)易MuMu模擬器

? ? ? ? return "/system/etc/mumu-configs/device-prop-configs/mumu.config";

? ? } else if (file_exist("/system/priv-app/ldAppStore")) {//雷電模擬器

? ? ? ? return "/system/priv-app/ldAppStore";

? ? } else if (file_exist("system/bin/ldinit") && file_exist("system/bin/ldmountsf")) {//雷電模擬器

? ? ? ? return "system/bin/ldinit";

? ? } else if (file_exist("/system/app/AntStore") && file_exist("/system/app/AntLauncher")) {//小蟻模擬器

? ? ? ? return "/system/app/AntStore";

? ? } else if (file_exist("vmos.prop")) {//vmos虛擬機(jī)

? ? ? ? return "vmos.prop";

? ? } else if (file_exist("fstab.titan") && file_exist("init.titan.rc")) {//光速虛擬機(jī)

? ? ? ? return "fstab.titan";

? ? } else if (file_exist("x8.prop")) {//x8沙箱和51虛擬機(jī)

? ? ? ? return "x8.prop";

? ? } else if (file_exist("/system/lib/libc_malloc_debug_qemu.so")) {//AVD QEMU

? ? ? ? return "/system/lib/libc_malloc_debug_qemu.so";

? ? }

? ? LOGD("simulator file check info not find? ");

? ? return "";

}


模擬器基礎(chǔ)特征:


檢測(cè)云手機(jī):

這塊思路還是很多的,不同的云手機(jī)檢測(cè)的思路也不一樣。大部分云手機(jī)做的還是很好的,很多都可以過掉Hunter的檢測(cè)。


檢測(cè)電流&電壓:

private final BroadcastReceiver batteryInfoReceiver = new BroadcastReceiver() {

? ? @Override

? ? public void onReceive(Context context, Intent intent) {


? ? ? ? // 電池狀態(tài)

? ? ? ? int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);


? ? ? ? // 電壓(以毫伏為單位)

? ? ? ? int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);


? ? ? ? // 獲取電池電流(毫安)

? ? ? ? int currentNow = -1;

? ? ? ? if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {

? ? ? ? ? ? BatteryManager batteryManager = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);

? ? ? ? ? ? currentNow = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW);

? ? ? ? }


? ? ? ? // 判斷是否在充電

? ? ? ? if (plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS) {

? ? ? ? ? ? // 在充電

? ? ? ? ? ? if (voltage != -1 && currentNow != -1) {

? ? ? ? ? ? ? ? float voltageInVolts = voltage / 1000f; // 將電壓轉(zhuǎn)換為伏特

? ? ? ? ? ? ? ? float currentInAmperes = currentNow / 1000000f; // 將電流轉(zhuǎn)換為安培

? ? ? ? ? ? ? ? float chargingPower = voltageInVolts * currentInAmperes; // 計(jì)算充電功率(瓦特)

? ? ? ? ? ? ? ? CLog.i(String.format("充電功率: %.2fW", chargingPower));

? ? ? ? ? ? ? ? if (Math.abs(chargingPower) > 300) {

? ? ? ? ? ? ? ? ? ? CLog.e("充電功率過高");

? ? ? ? ? ? ? ? ? ? handlerItemData(new ListItemBean(

? ? ? ? ? ? ? ? ? ? ? ? ? ? "電池異常:充電功率過高(可能是云手機(jī))",

? ? ? ? ? ? ? ? ? ? ? ? ? ? ListItemBean.RiskLeave.Deadly,

? ? ? ? ? ? ? ? ? ? ? ? ? ? "檢測(cè)到過大的充電功率 -> " + String.format("%.2fW", Math.abs(chargingPower))

? ? ? ? ? ? ? ? ? ? ));

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }


? ? }

};


檢測(cè)攝像頭&傳感器相關(guān):

判斷攝像頭有個(gè)數(shù)。




try {

? ? CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);

? ? String[] cameraIds = manager.getCameraIdList();

? ? //攝像頭個(gè)數(shù)

? ? CLog.i("cameraIds -> "+ Arrays.toString(cameraIds));

? ? if(cameraIds.length < CAMERA_MINIMUM_QUANTITY_LIMIT){

? ? ? ? items.add(

? ? ? ? ? ? ? ? new ListItemBean(

? ? ? ? ? ? ? ? "當(dāng)前手機(jī)可能是模擬器&云手機(jī)",

? ? ? ? ? ? ? ? ListItemBean.RiskLeave.Warn,

? ? ? ? ? ? ? ? "camera size -> "+cameraIds.length

? ? ? ? ));

? ? }

} catch (Throwable ignored) {


}


檢測(cè)傳感器個(gè)數(shù):

這塊思路就是直接獲取個(gè)數(shù),少于10個(gè)可以直接認(rèn)定為黑產(chǎn)。我目前沒發(fā)現(xiàn)那個(gè)手機(jī)少于10個(gè)傳感器,這塊如果可能的話可以嘗試調(diào)用一下傳感器,保證傳感器是否可用,防止云手機(jī)以假亂真。




try {

? ? //3,檢測(cè)傳感器類型,支持的全部類型傳感器

? ? SensorManager sm = (SensorManager) context.getSystemService(SENSOR_SERVICE);

? ? List<Sensor> sensorlist = sm.getSensorList(Sensor.TYPE_ALL);


? ? ArrayList<Integer> sensorTypeS = new ArrayList<>();

? ? for (Sensor sensor : sensorlist) {

? ? ? ? //獲取傳感器類型

? ? ? ? int type = sensor.getType();

? ? ? ? if (!sensorTypeS.contains(type)) {

? ? ? ? ? ? //發(fā)現(xiàn)一種類型則添加一種類型

? ? ? ? ? ? sensorTypeS.add(type);

? ? ? ? }

? ? }

? ? //小米k40 51個(gè)傳感器類型

? ? //普通的pix 27個(gè)

? ? //華為榮耀20 18個(gè)傳感器

? ? CLog.e("sensor types size -> " + sensorlist.size());

? ? //我們認(rèn)為傳感器少于20個(gè)則認(rèn)為是風(fēng)險(xiǎn)設(shè)備

? ? if (sensorlist.size() < SENSOR_MINIMUM_QUANTITY_LIMIT) {

? ? ? ? items.add(new ListItemBean(

? ? ? ? ? ? ? ? "當(dāng)前手機(jī)可能是模擬器&云手機(jī)",

? ? ? ? ? ? ? ? ListItemBean.RiskLeave.Warn,

? ? ? ? ? ? ? ? "sensor size -> ("+ sensorlist.size()+") \n" +

? ? ? ? ? ? ? ? "sensor type size -> ("+sensorTypeS.size()+") \n"

? ? ? ? ? ? ? ? //+ "sensor info -> \n"+ Sensorlist? ?//打印全部傳感器信息

? ? ? ? ));

? ? }


檢測(cè)傳感器名稱:

這塊檢測(cè)思路主要是檢測(cè)傳感器的名稱,正常小米之類的手機(jī)他是不可能存在叫什么 AOSP的傳感器的。


這種AOSP基本都是自己編譯的ROM,所以這塊也可以作為監(jiān)測(cè)點(diǎn)??梢陨蠄?bào)傳感器的一些名稱信息,也是環(huán)境檢測(cè)一個(gè)很重要的抓手。


一般小白肯定不會(huì)說去改傳感器名稱。


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

? ? ArrayList<Sensor> aospSensor = new ArrayList<>();

? ? for(Sensor sensor:sensorlist){

? ? ? ? if(sensor.getVendor().contains("AOSP")){

? ? ? ? ? ? aospSensor.add(sensor);

? ? ? ? }

? ? }

? ? if (aospSensor.size()>3) {

? ? ? ? CLog.e("傳感器參數(shù)是否異常(生產(chǎn)廠商為AOSP)");

? ? ? ? items.add(new ListItemBean(

? ? ? ? ? ? ? ? "當(dāng)前手機(jī)可能是模擬器&云手機(jī)",

? ? ? ? ? ? ? ? ListItemBean.RiskLeave.Warn,

? ? ? ? ? ? ? ? aospSensor.size()

? ? ? ? ? ? ? ? ? ? ? ? +"/"+sensorlist.size()+"傳感器參數(shù)異常 -> "+ aospSensor

? ? ? ? ));

? ? }

}


檢測(cè)掛載文件:

這塊就是去遍歷mounts 下面這幾個(gè)文件,檢測(cè)里面是否包含docker關(guān)鍵字,防止一些云手機(jī)搞虛擬化,通過使用docker進(jìn)行掛載。


這塊也是很好的監(jiān)測(cè)點(diǎn)




String[] marks = {"docker"};

//檢測(cè)proc/mounts是否包含docker關(guān)鍵字

String mark = NativeEngine.getZhenxiInfoK("/proc/mounts",marks );

if(mark == null){

? ? mark = NativeEngine.getZhenxiInfoK("/proc/self/mountstats", marks);

? ? if(mark == null){

? ? ? ? mark = NativeEngine.getZhenxiInfoK("/proc/self/mountinfo", marks);

? ? }

}

if(mark!=null){

? ? items.add(new ListItemBean(

? ? ? ? ? ? "當(dāng)前手機(jī)可能是模擬器&云手機(jī)",

? ? ? ? ? ? ListItemBean.RiskLeave.Warn,

? ? ? ? ? ? "(mounts異常)\n"+mark

? ? ));

}


檢測(cè)ROM是否Match:

檢測(cè)環(huán)境信息:

這塊思路主要好多種,主要是為了防止一些自定義ROM,通過修改機(jī)型的方法,繞過自定義ROM檢測(cè)逃逸。


可以直接執(zhí)行g(shù)etprop 把所有的環(huán)境信息都拿到手,如果是小米手機(jī),里面環(huán)境信息里面,肯定是有MIUI關(guān)鍵字。


比如小米的手機(jī),我會(huì)去檢測(cè)是否包含這幾個(gè)關(guān)鍵環(huán)境信息。




private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";

private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";

private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage";

這塊可以采集以后服務(wù)端進(jìn)行判斷,防止自定義ROM 機(jī)型偽造。


檢測(cè)服務(wù)列表:

這塊還是執(zhí)行 service list,一般小米手機(jī)之類的,都會(huì)有小米的系統(tǒng)服務(wù),這種東西很難去偽造,如果他偽造了假的,你就嘗試調(diào)用即可。


這塊還是建議上傳到服務(wù)端,由服務(wù)端算法同學(xué)去根據(jù)相似度算法去推斷,不要再本地進(jìn)行判斷,因?yàn)镠unter是非聯(lián)網(wǎng)Apk,所以只是在客戶端打了個(gè)樣子。


檢測(cè)當(dāng)前環(huán)境是否被Hook:

這塊檢測(cè)方法千奇百怪,首先最基本maps去檢測(cè)frida或者根據(jù)調(diào)用棧檢測(cè)lsp特征,基礎(chǔ)的檢測(cè)方案不說了。因?yàn)槲矣X得并不是一個(gè)很好的方案。改個(gè)名就繞過了。


比如frida特征三件套,檢測(cè)思路主要:




static const char *FRIDA_THREAD_GUM_JS_LOOP = "gum-js-loop";

static const char *FRIDA_THREAD_GMAIN = "gmain";

static const char *FRIDA_NAMEDPIPE_LINJECTOR = "linjector";

Hook檢測(cè),我們其實(shí)只需要檢測(cè)內(nèi)存沒有被修改即可。


檢測(cè)沙箱:

這塊檢測(cè)核心邏輯全部放在ISO線程檢測(cè)。可以配置一個(gè)服務(wù),然后服務(wù)里使用如下變量即可。




<service

? ? android:name=".ZhenxiServer"

? ? android:isolatedProcess="true"

? ? android:useAppZygote="true"

? ? />

這個(gè)服務(wù)非常惡心,一般沙箱對(duì)這個(gè)線程都會(huì)進(jìn)行跳過。


這塊有人可能會(huì)問什么是iso線程?可以理解成一個(gè)獨(dú)立的安全的線程,只能通過和外部IPC交互的方式進(jìn)行通訊。useAppZygote 相當(dāng)于讓這個(gè)進(jìn)程運(yùn)行在Zygote中。這個(gè)時(shí)候時(shí)機(jī)特別早,早到什么程度呢?就連libart.so 都沒加載,所以這個(gè)檢測(cè)進(jìn)程只能調(diào)用一些原始的libc方法,不能調(diào)用任何Art相關(guān)的函數(shù)。


檢測(cè)多余線程PID:

主要實(shí)現(xiàn)思路就是去檢測(cè)proc下面是否有除了main進(jìn)程以外的其他pid,因?yàn)檎?dòng)的話,肯定是只有一個(gè)main進(jìn)程。


但是沙箱的話會(huì)在啟動(dòng)之前去啟動(dòng)別的進(jìn)程,所以這塊可以進(jìn)行bypass。后面我會(huì)統(tǒng)一說這塊應(yīng)該如何對(duì)抗,包括如何繞過。


這塊先介紹檢測(cè)思路,和檢測(cè)原理。這塊里面有一個(gè)replaceSecInsns是我自己封裝的一個(gè)函數(shù),我擔(dān)心 opendir 被Hook了,所以每次執(zhí)行都去把指令替換成本地文件的指令,而不是去執(zhí)行內(nèi)存里面的指令。


內(nèi)核文件相關(guān)(重要):

內(nèi)核文件指的是系統(tǒng)的相關(guān)文件,很多大廠會(huì)直接通過popen cat或者直接fopen只讀的方式去讀取文件內(nèi)容。核心的也就那幾個(gè)。


一般讀取的時(shí)候都是直接svc openat 底層需要用到svc的IO重定向,如果這塊不處理的話,基本沒辦法進(jìn)行mock和修改 。


build.prop相關(guān)? (MIUI系統(tǒng)所在路徑不同)

"/system/build.prop"


"/odm/etc/build.prop"


"/product/build.prop"


"/vendor/build.prop"


/proc/sys/kernel/random/boot_id

這個(gè)ID重啟或者刷機(jī)以后發(fā)生變化,很多大廠會(huì)讀取這個(gè)值,這個(gè)值類似一個(gè)UUID,SVC讀取這個(gè)值,然后將這個(gè)值保存到私有目錄。


跟DRM ID 相比,好處就是不同App讀取的值是一樣的。一個(gè)設(shè)備指紋占比很重的值。


/proc/sys/kernel/random/uuid

同上


/sys/block/mmcblk0/device/cid

同上


/sys/devices/soc0/serial_number

同上


/proc/misc

同上


/proc/version

這個(gè)是一個(gè)linux系統(tǒng)內(nèi)核文件,里面記錄了當(dāng)前Linux系統(tǒng)版本的相關(guān)信息。里面的值類似如下


eg. Linux version 3.18.31-perf-g9b0888a(builder@c3-miui-ota-bd96.bj)

這個(gè)文件在android 11以上基本讀不到了 ,但是在android 9是可以讀到的 。但是android 11有沒有什么代替方案呢?答案是有的,svc 調(diào)用uname 。使用方式類似如下,uname也是一個(gè)命令行,還可以通過popen uname -a的方式去獲取 (popen部分會(huì)介紹到)。這個(gè)函數(shù)在IOS上面也比較實(shí)用。


struct utsname buff;

? ? int i = uname(&buff);

? ? LOGE("uname sysname %s ", buff.sysname)

? ? LOGE("uname nodename %s ", buff.nodename)

? ? LOGE("uname release %s ", buff.release)

? ? LOGE("uname version %s ", buff.version)

? ? LOGE("uname machine %s ", buff.machine)

? ? LOGE("uname domainname %s ", buff.domainname)

通過這幾項(xiàng)就可以拿到/proc/version 里面的所有信息,


很多大廠會(huì)用/ popen uname -a / svc uname函數(shù) / 和svc openat去讀/proc/version以此判斷獲取的值是否準(zhǔn)確,如果有一個(gè)對(duì)不上都會(huì)認(rèn)為當(dāng)前設(shè)備被修改。


getprop

這個(gè)執(zhí)行的內(nèi)容返回的值和,adb shell 以后執(zhí)行g(shù)etprop 結(jié)果是一樣的。輸出的是當(dāng)前手機(jī)全部的Build相關(guān)配置。獲取代碼具體如下 。




pfile = popen("getprop", "r");

pfile = popen("getprop | grep dalvik", "r");

pfile = popen("getprop ro.odm.build.id", "r");

?

while (fgets(buf, sizeof(buf), pfile)) {

? ? LOGE("getprop -> %s", buf);

}

返回結(jié)果就不展示了,自己用手機(jī) adb shell 在執(zhí)行g(shù)etprop 即可 。


ip a(重要)

這個(gè)也是很核心的設(shè)備指紋,里面會(huì)獲取當(dāng)前手機(jī)的網(wǎng)卡信息,whan0 wlan1 p2p0 這些信息。這個(gè)底層走的也是netlinker


所以在netlinker層直接修改攔截,他哪怕執(zhí)行的命令行也是生效的 。返回的東西很多,可以自己嘗試打印一下。很多大廠也會(huì)用這種方式去掃描你得網(wǎng)卡Mac地址 。


ls -al /sdcard/Android/data

掃描私有目錄,返回私有目錄的一些信息 ??梢耘袛喈?dāng)前App是否存在其他App目錄下,主要用于檢測(cè)沙箱。


其實(shí)檢測(cè)沙箱還有一個(gè)很好的辦法,就是檢測(cè)手機(jī)的進(jìn)程信息 。如果當(dāng)前App在自己正常情況啟動(dòng),只會(huì)有一條線程。


但是如果放在VA沙盒內(nèi)部的話,VA沙盒本身會(huì)啟動(dòng)一條線程,自己的App本身也會(huì)啟動(dòng)一條線程。所以線程數(shù)量就對(duì)不上。也可以認(rèn)為作弊


popen掃描Magisk

這些命令都可以進(jìn)行magisk的列表的掃描,判斷當(dāng)前線程是否存在magisk等關(guān)鍵字,都是很好的辦法




popen("df | grep /sbin/.magisk", "r");

popen("mount? | grep /sbin/.magisk", "r");

popen("ps | grep magisk", "r");

修改的話也很簡(jiǎn)單,如果是ps 或者 df 直接生成一份不存在magisk關(guān)鍵字的文件,(還有一些痕跡關(guān)鍵字,比如xposed,edxp,riru這些都是常用的檢測(cè)關(guān)鍵字)


mout直接 svc IO重定向繞過即可 。


popen logcat

有很多大廠,他當(dāng)發(fā)現(xiàn)你設(shè)備信息異常的時(shí)候,會(huì)直接執(zhí)行popen logcat 直接掃描你當(dāng)前手機(jī)的日志系統(tǒng) 。


把異常的log都進(jìn)行上報(bào),用于石錘當(dāng)前用戶是否作弊 。所以這個(gè)也需要處理?


Native獲取DRM ID(重要)

這個(gè)指紋也是很多大廠用作唯一ID的核心指紋。處理的話也需要注意,很核心的一個(gè)設(shè)備指紋ID。


Java層DRM相關(guān)(重要字段):

這個(gè)DRM是水印相關(guān),主要為了處理不同手機(jī)加水印的唯一ID 核心的是一個(gè)叫deviceUniqueId 的東西,這玩意是一個(gè)隨機(jī)的32位字節(jié)數(shù)組。很多大廠用這個(gè)作為核心的設(shè)備指紋,不僅在Java層進(jìn)行獲取,還有在Native層進(jìn)行獲取


配置相關(guān):

常見的配置如下,這些字段其實(shí)修改不修改不重要,因?yàn)楹芏啻髲S如果手機(jī)開了開發(fā)者選項(xiàng)或者debug模式之類的。


會(huì)增加當(dāng)前手機(jī)的風(fēng)險(xiǎn)值。


PUT_MOCK_AND_SAVE_ORG("sys.usb.config", "none", null, true);

PUT_MOCK_AND_SAVE_ORG("sys.usb.state", "none", null, true);

PUT_MOCK_AND_SAVE_ORG("persist.sys.usb.config", "none", null, true);

PUT_MOCK_AND_SAVE_ORG("persist.sys.usb.qmmi.func", "none", null, true);

//這兩個(gè)config可能會(huì)拿不到,拿不到則不進(jìn)行mock

PUT_MOCK_AND_SAVE_ORG("vendor.usb.mimode", "none", null, true);

PUT_MOCK_AND_SAVE_ORG("persist.vendor.usb.config", "none", null, true);

PUT_MOCK_AND_SAVE_ORG("ro.debuggable", "0", null, true);

PUT_MOCK_AND_SAVE_ORG("init.svc.adbd", "stopped", null, true);

PUT_MOCK_AND_SAVE_ORG("ro.secure", "1", null, true);

//手機(jī)解鎖狀態(tài)

PUT_MOCK_AND_SAVE_ORG("ro.boot.flash.locked", "1", null, true);

PUT_MOCK_AND_SAVE_ORG("sys.oem_unlock_allowed", "1", null, true);


Build相關(guān):

Build里面還是有很多有用的東西,比如手機(jī)是否開啟adb ,usb接口的狀態(tài)之類


的。


IMEI , IMSI ,ICCID,Line1Number:

這些基礎(chǔ)的Java設(shè)備指紋字段沒啥好說的,百度一下就能找到具體的獲取方法,但是修改的時(shí)候需要注意,不要直接Hook,嘗試優(yōu)先Hook ipc即可 。


藍(lán)牙網(wǎng)卡MAC:

藍(lán)牙的網(wǎng)卡不是普通的網(wǎng)卡


Setting相關(guān)

其實(shí)Setting里面還有很多別的功能東西,常見的就是Settings.Secure 和 Settings.Global


在Settings.Global 里面其實(shí)還有一些別的字段,具體API如下。這些都是一些比較隱蔽的設(shè)備指紋。




Settings.Global.getString(context.getContentResolver(),"mi_health_id")

Settings.Global.getString(context.getContentResolver(),"mi_health_id")

Settings.Global.getString(context.getContentResolver(),"gcbooster_uuid")

Settings.Global.getString(context.getContentResolver(),"key_mqs_uuid")

Settings.Global.getString(context.getContentResolver(),"ad_aaid")

————————————————

版權(quán)聲明:

原文鏈接:https://mp.weixin.qq.com/s/P0fq3EVGPYyIvDI96VtukQ


軟件大廠,環(huán)境檢測(cè)思路和規(guī)避思路,安卓改機(jī)應(yīng)該改什么數(shù)據(jù)和參數(shù),安卓boot內(nèi)核修改的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
沂源县| 湘阴县| 文昌市| 安顺市| 柏乡县| 安阳县| 巴林左旗| 苍梧县| 应城市| 临湘市| 乌兰浩特市| 鄂温| 孟连| 海伦市| 九寨沟县| 措勤县| 祥云县| 康乐县| 冀州市| 汉中市| 开封县| 夏邑县| 内江市| 峨边| 滦平县| 介休市| 新昌县| 丰原市| 隆昌县| 自治县| 贺兰县| 健康| 渝北区| 永福县| 浦北县| 平远县| 蓝山县| 佛坪县| 霞浦县| 吉木乃县| 咸丰县|