Android 联想K5 Play 8.0 Notification突破拦截(vdex反编译 )
前言:
部分國(guó)內(nèi)ROM系統(tǒng)對(duì)消息欄做了攔截,因此,需要用戶手動(dòng)授權(quán)開(kāi)啟消息欄通知。 若是能夠找到開(kāi)啟的代碼,是否可以繞過(guò)攔截,默認(rèn)開(kāi)啟呢?
本篇文章,介紹如何找到攔截點(diǎn),如何去突破。至于其他的ROM系統(tǒng)的攔截,也是類(lèi)似。
聯(lián)想K5 Play為案 , 探究消息欄突破攔截
1. 獲取到system文件夾
通過(guò)下載系統(tǒng)的Rom 包,或者adb pull方式獲取到系統(tǒng)的system下的文件。
具體如何操作,請(qǐng)閱讀Android反編譯之各大手機(jī)廠商的系統(tǒng)(adb pull和Rom包).
這里,本人通過(guò)去聯(lián)想官網(wǎng)下載到對(duì)應(yīng)Rom壓縮包,再通過(guò)Rom助手,提取出system文件夾,如下圖所示:
2. 反編譯Framework層源碼或者系統(tǒng)app的源碼
找到system/framework文件夾下,通常都是反編譯boot.oat就可以獲取到Android.jar對(duì)應(yīng)的系統(tǒng)源碼,但這里會(huì)發(fā)現(xiàn)多個(gè)了一個(gè)vdex文件,如下圖所示:
通過(guò)查找度娘,這里,稍微總結(jié)一下:
- dex: 是Android平臺(tái)上(Dalvik虛擬機(jī))的可執(zhí)行文件,里面包含了該app的所有源碼, 通過(guò)反編譯工具可以獲取到相應(yīng)的java源碼。
- odex: 是同名apk經(jīng)系統(tǒng)優(yōu)化后的dex文件,通過(guò)反編方式可獲取到dex文件。
- vdex:Android 8.0引入了全新的vdex文件,為了避免不必要的驗(yàn)證Dex 文件合法性的過(guò)程,通過(guò)反編方式獲取到dex文件。
更多dex, odex,vdex,oat的資料,請(qǐng)自行度娘搜索。
將oat2dex.jar拷貝到該system/framework文件夾下,然后,打開(kāi)命令行執(zhí)行java -jar oat2dex.jar boot.vdex ,納尼,神奇發(fā)現(xiàn),里面沒(méi)有需要的源碼,找到一個(gè)假的李逵。
苦命的娃,繼續(xù)找,直到反編譯boot-framework.vdex,會(huì)發(fā)現(xiàn)android.jar里面的源碼藏到這里了,如下圖所示:
實(shí)際上,部分Rom廠商的android.jar的源碼放到boot.oat或者boot-framework.oat 中
依葫蘆畫(huà)瓢,繼續(xù)反編譯service.vdex,獲取到sevice層的系統(tǒng)源碼。
3. 查找對(duì)應(yīng)的攔截或者開(kāi)啟的源碼
查看framework.dex ,service.dex的源碼,一路追蹤,會(huì)發(fā)現(xiàn)最后跑到SystemUI 中PhoneStatubar類(lèi)開(kāi)啟了Notification。這里不探討Notificatin源碼分析走向,否則,文章篇幅會(huì)過(guò)大。
打開(kāi)聯(lián)想K5 Play的設(shè)置,一直到到開(kāi)啟消息欄的界面,如下圖所示:
這時(shí)候,只需要確定該Activity處于哪個(gè)apk中,就可以反編譯,從而查看到開(kāi)啟的代碼。
查看界面屬于哪個(gè)Activity:
執(zhí)行以下命令行adb shell dumpsys activity top查看到棧頂?shù)腁ctivity ,這里是Activity是SubSettings,顯示的fragment是AppNotificationSettings。
查看當(dāng)前Activity屬于哪個(gè)Apk:
執(zhí)行以下命令行adb shell dumpsys activity activities ,發(fā)現(xiàn)當(dāng)前activity屬于sysytem/priv-app/ZuiSetttings/ZuiSettings.apk
反編譯ZuiSettings.vdex獲取到對(duì)應(yīng)的dex文件,打開(kāi)如下:
在AppNotificationSettings類(lèi)中,查找到開(kāi)啟消息欄的源碼:
protected void setupBlock() {View inflate = LayoutInflater.from(getPrefContext()).inflate(R.layout.styled_switch_bar, null);this.mSwitchBar = (SwitchBar) inflate.findViewById(R.id.switch_bar);this.mSwitchBar.addOnSwitchChangeListener(new OnSwitchChangeListener() {public void onSwitchChanged(Switch switchR, boolean z) {boolean z2 = false;if (AppNotificationSettings.this.mShowLegacyChannelConfig && AppNotificationSettings.this.mChannel != null) {int i = z ? NotificationManagerCompat.IMPORTANCE_UNSPECIFIED : 0;RestrictedSwitchPreference restrictedSwitchPreference = AppNotificationSettings.this.mImportanceToggle;if (i == NotificationManagerCompat.IMPORTANCE_UNSPECIFIED) {z2 = true;}restrictedSwitchPreference.setChecked(z2);//這里是重點(diǎn)AppNotificationSettings.this.mChannel.setImportance(i);AppNotificationSettings.this.mChannel.lockFields(4);AppNotificationSettings.this.mBackend.updateChannel(AppNotificationSettings.this.mPkg, AppNotificationSettings.this.mUid, AppNotificationSettings.this.mChannel);}//這里是重點(diǎn)AppNotificationSettings.this.mBackend.setNotificationsEnabledForPackage(AppNotificationSettings.this.mPkgInfo.packageName, AppNotificationSettings.this.mUid, z);AppNotificationSettings.this.mAppRow.banned = true;AppNotificationSettings.this.updateDependents(z ^ 1);if (z) {AppNotificationSettings.this.mSwitchBar.setText(AppNotificationSettings.this.getResources().getString(R.string.notification_allow));} else {AppNotificationSettings.this.mSwitchBar.setText(AppNotificationSettings.this.getResources().getString(R.string.notification_close));}}});}以上的代碼中mBackend屬性對(duì)應(yīng)是NotificationBackend類(lèi)對(duì)象,打開(kāi)該類(lèi)查看調(diào)用的updateChannel()和setNotificationsEnabledForPackage():
public class NotificationBackend {private static final String TAG = "NotificationBackend";static INotificationManager sINM = Stub.asInterface(ServiceManager.getService("notification"));//省略部分代碼public boolean setNotificationsEnabledForPackage(String str, int i, boolean z) {try {sINM.setNotificationsEnabledForPackage(str, i, z);return true;} catch (Throwable e) {Log.w(TAG, "Error calling NoMan", e);return false;}}public void updateChannel(String str, int i, NotificationChannel notificationChannel) {try {sINM.updateNotificationChannelForPackage(str, i, notificationChannel);} catch (Throwable e) {Log.w(TAG, "Error calling NoMan", e);}} }最終,發(fā)現(xiàn)是跨進(jìn)程通訊,調(diào)用在NotificationManagerService中。
4. 編寫(xiě)適配對(duì)應(yīng)Rom的代碼
根據(jù)以上的源碼追蹤,鎖定如何開(kāi)啟的功能代碼。編寫(xiě)以下反射代碼:
/*** 聯(lián)想 K5 play 8.0系統(tǒng)適配: 需要系統(tǒng)進(jìn)程(未適配成功)** @param context*/private static void adapterZUISystem8(Context context) {String packageName = context.getPackageName();try {Class<?> NotificationManager = Class.forName("android.app.NotificationManager");Method getService = NotificationManager.getDeclaredMethod("getService");getService.setAccessible(true);Object mNotificationService = getService.invoke(null);int uid = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).uid;try {final int IMPORTANCE_UNSPECIFIED = -1000;//先獲取到NotificationChannelMethod getNotificationChannelForPackageMethod = mNotificationService.getClass().getMethod("getNotificationChannelForPackage", String.class, int.class, String.class, boolean.class);getNotificationChannelForPackageMethod.setAccessible(true);Object NotificationChannel = getNotificationChannelForPackageMethod.invoke(mNotificationService, packageName, uid, "miscellaneous", true);//設(shè)置NotificationChannel中值Method setImportanceMethod = NotificationChannel.getClass().getDeclaredMethod("setImportance", int.class);setImportanceMethod.setAccessible(true);setImportanceMethod.invoke(NotificationChannel, IMPORTANCE_UNSPECIFIED);Method lockFieldsMethod = NotificationChannel.getClass().getDeclaredMethod("lockFields", int.class);lockFieldsMethod.setAccessible(true);lockFieldsMethod.invoke(NotificationChannel, 4);//更新NotificationChannelMethod updateNotificationChannelForPackageMethod = mNotificationService.getClass().getMethod("updateNotificationChannelForPackage", String.class, int.class, Class.forName("android.app.NotificationChannel"));updateNotificationChannelForPackageMethod.setAccessible(true);updateNotificationChannelForPackageMethod.invoke(mNotificationService, packageName, uid, NotificationChannel);} catch (Exception e) {e.printStackTrace();}Method setNotificationsEnabledForPackage = mNotificationService.getClass().getMethod("setNotificationsEnabledForPackage", String.class, int.class, boolean.class);setNotificationsEnabledForPackage.setAccessible(true);setNotificationsEnabledForPackage.invoke(mNotificationService, context.getPackageName(), uid, true);} catch (Exception e) {e.printStackTrace();}}開(kāi)開(kāi)心心的運(yùn)行以上代碼,結(jié)果還是很悲催,報(bào)錯(cuò)安全 SecurityException異常。猜測(cè)該方式只能在系統(tǒng)進(jìn)程中調(diào)用。
為了驗(yàn)證一番,打開(kāi)service.dex,找到NotificationManagerService類(lèi),找到該調(diào)用方法,如下圖所示:
查看checkCallerIsSystem()
最后,不得不放棄,宣告適配失敗。
這里扯上幾句屁話:
- 適配國(guó)產(chǎn)ROM系統(tǒng),程序員還是很辛苦的,特別是遇到混淆后的系統(tǒng)源碼,盯著幾個(gè)a ,b ,c,簡(jiǎn)直需要誦讀佛經(jīng),消除戾氣。
- 適配工作長(zhǎng)路漫漫,需要不斷的探索,和打抗日持久戰(zhàn)的耐心。
- 適配沒(méi)有成功也不需要?dú)怵H,不過(guò)接下來(lái)的時(shí)候,就該讓公司的商務(wù)出馬,和手機(jī)廠商談合作,增加白名單。
資源參考:
- Android反編譯之各大手機(jī)廠商的系統(tǒng)(adb pull和Rom包)
- Android反編譯之APK(apktool、dex2jar、jd、jadx)
總結(jié)
以上是生活随笔為你收集整理的Android 联想K5 Play 8.0 Notification突破拦截(vdex反编译 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《通用版CISCO交换机配置命令及释义》
- 下一篇: Android 上能提高学习工作效率的应