调试MIUI天气,解除语言限制 前段时间将系统语言切换至英文后,发现常用的天气云图无法打开,气象预警也不显示,如果通过xposed模块单独将天气调至中文显得十分割裂,正好借此机会学一下安卓逆向
工具 手机上可以使用MT管理器对apk进行解包编辑,并且使用auto.js的悬浮窗调试工具对程序布局进行解析
之前已经装过MagiskHide Props Config,因此直接用它设置系统ro.debuggable
打开全局调试,再使用DDMS读取程序的log、跟踪函数调用栈
电脑上则使用了dex-tools(dex2jar)、以及cfr对程序进行反编译,使用vscode在java中定位到问题后再修改smali代码。
过程 无法打开云图 由动态调试代码,对比英文状态下与中文下不同
英文下
1 2 3 4 5 6 7 8 9 07 -31 10 :52 :24 .618 : D/Wth2:MajesticCloud(19455 ): afterFrictionValue 0 .0 07 -31 10 :52 :24 .622 : D/Wth2:WeatherScrollView(19455 ): mDailyForecastView bottom canSee:true07 -31 10 :52 :24 .734 : D/Wth2:MajesticWeather(19455 ): go_touch_move: false07 -31 10 :52 :24 .734 : D/Wth2:MajesticWeather(19455 ): go_touch_move: com.miui.weather2.majestic.detail.MajesticBackSunny@ba7ad2307 -31 10 :52 :24 .735 : D/Wth2:MajesticCloud(19455 ): startTouchAnim: 07 -31 10 :52 :24 .735 : D/Wth2:MajesticCloud(19455 ): startTouchAnim07 -31 10 :52 :24 .735 : D/Wth2:MajesticCloud(19455 ): touch up is -1400 .0 07 -31 10 :52 :24 .761 : D/Wth2:MajesticCloud(19455 ): afterFrictionValue 0 .0 07 -31 10 :52 :24 .781 : D/Wth2:MajesticCloud(19455 ): afterFrictionValue 0 .0
中文下
1 2 3 4 5 6 7 8 9 10 11 12 07-31 01:28:55.907: D/Wth2:MajesticCloud(29817): afterFrictionValue 0.0 07-31 01:28:55.909: D/Wth2:WeatherScrollView(29817): mDailyForecastView bottom canSee:true 07-31 01:28:55.962: D/Wth2:MajesticWeather(29817): go_touch_move: false 07-31 01:28:55.962: D/Wth2:MajesticWeather(29817): go_touch_move: com.miui.weather2.majestic.detail.MajesticBackNightSunny@d3e8e9a 07-31 01:28:55.962: D/Wth2:MajesticCloud(29817): startTouchAnim: 07-31 01:28:55.962: D/Wth2:MajesticCloud(29817): startTouchAnim 07-31 01:28:55.962: D/Wth2:MajesticCloud(29817): touch up is -1400.0 07-31 01:28:55.964: D/Wth2:Navigator(29817): gotoActivityMinuteRain 07-31 01:28:55.966: I/Timeline(29817): Timeline: Activity_launch_request time:817503 07-31 01:28:55.983: D/Wth2:AnalyzeTransferManager(29817): canAnalyzeByKey event: minute_rain_click, result: true ... 07-31 01:28:55.993: D/Wth2:MajesticCloud(29817): afterFrictionValue 0.0
可以观察到从Navigator(29817): gotoActivityMinuteRain
开始产生了不同,定位到log gotoActivityMinuteRain
的函数,并动态调试记录其调用栈,定位到com.miui.weather2.view.onOnePage.WeatherAqiMinuteView.c(View view)
,找到问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void c (View view) { if (this .x != null && c1.t(this .getContext())) { o0.a(this .getContext(), this .x, -1 , false , 1 ); r0.a("minute_rain_click" ); } }public static boolean t (Context context) { boolean bl = !Build.IS_INTERNATIONAL_BUILD && c1.r(context); return bl; }public static boolean r (Context context) { return c1.f(context).equals(Locale.SIMPLIFIED_CHINESE); }
因此只要在mt管理器中将该处条件跳转去除即可
1 2 3 4 5 6 7 8 9 10 11 invoke-static {p1}, Lcom/miui/weather2/tools/c1; ->t(Landroid/content/Context; )Zmove-result p1if-nez p1, :cond_e .line 2:cond_e invoke-virtual {p0}, Landroid/view/ViewGroup; ->getContext()Landroid/content/Context;
云图中提示空白 虽然强行打开了云图,但是提示文字显示为空白。这里看日志没能找到不同,只能老老实实看代码定位问题
观察代码分析出com.miui.weather2.w.f$c
中函数MinuteRainData a(Context object, CityData object2)
获取了CityData
的Locale
,并且作为参数传入至com.miui.weather2.a0.a.a
函数中请求数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private MinuteRainData a (Context object, CityData object2) { if (this .a) { WeatherData weatherData = i.a(object, (CityData)object2); j1.a(object, weatherData, false , ((CityData)object2).isFirstCity()); object = weatherData != null ? weatherData.getMinuteRainData() : null ; } else { String string2 = this .e; String string3 = this .d; String string4 = ((CityData)object2).getLocale(); String string5 = ((CityData)object2).getExtra(); object2 = ((CityDataLight)object2).isLocationCity() ? "true" : "false" ; object = com.miui.weather2.z.c.a(com.miui.weather2.a0.a.a(string2, string3, string4, string5, object, (String)object2), System.currentTimeMillis(), object); } return object; }
猜测是英文Locale
下返回的数据中包含的提示信息为空,这里改用中文代替 因此尝试将该处string4
修改为常量zh_cn
,至于为什么不是zh-cn
等值是因为CityData
底下用到了这个常量(
1 2 3 4 5 invoke-virtual {p2}, Lcom/miui/weather2/structures/CityData; ->getLocale()Ljava/lang/String; move-result-object v2const-string v2, "zh_cn"
经过修改,已经能正确显示提示信息了
预警卡片不显示 同样不好通过log定位问题,继续读代码(
定位到com.miui.weather2.tools.j1.a(String var0, String var1_4, Context var2_8)
中,有一个c1.r
对设备Locale
进行了判断,直接将返回值设为1
1 2 3 4 5 6 7 8 9 10 11 public static ArrayList<Alert> a (String var0, String var1_4, Context var2_8) { block15: { block16: { block14: { block17: { var3_9 = c1.r(var2_8); var4_10 = null ; var5_11 = null ; if (!var3_9) { return null ; }
1 2 3 4 5 6 .line 293invoke-static {p2}, Lcom/miui/weather2/tools/c1; ->r(Landroid/content/Context; )Zmove-result v1const/4 v1, 0x1
可以看到气象预警的框框已经能够正常显示,图标也正常,但是标题不显示
预警卡片标题 被迫学会打log才找到这个问题,中间甚至抓了几次包对比。抓包可以看出。response中并没有诸如”暴雨蓝色预警:南京气象局提醒…“的字样,可以猜测该标题为在客户端中拼接,其中中文冒号“:”出现在代码中都是log部分。如果熟悉安卓开发,应该直接意识到是在资源文件中的格式化字符串拼接的了,可惜不熟悉花了不少时间。
在com.miui.weather.view.onOnePage.VerticalCarousel.a(Alert alert)
中,使用了Context.getString()
获取xml资源文件中的字符串,并通过其格式化。而该资源与Locale
绑定,因此切换英文后会返回Null
。将该函数改为使用字符串拼接获取输出内容即可,直接用smali写一个StringBuilder
拼接即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 .method private a(Lcom/miui/weather2/structures/Alert; )Ljava/lang/String; .registers 7 .line 22 invoke-virtual {p1}, Lcom/miui/weather2/structures/Alert; ->getType()Ljava/lang/String; move-result-object v0 .line 23 invoke-virtual {p1}, Lcom/miui/weather2/structures/Alert; ->getLevel()Ljava/lang/String; move-result-object v1 .line 24 const-string v2, "\u9884\u8b66\uff1a" .line 25 invoke-virtual {p1}, Lcom/miui/weather2/structures/Alert; ->getDetail()Ljava/lang/String; move-result-object v3 .line 26 new-instance v4, Ljava/lang/StringBuilder; invoke-direct {v4}, Ljava/lang/StringBuilder; -><init>()V .line 27 invoke-virtual {v4,v0}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 28 invoke-virtual {v4,v1}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 29 invoke-virtual {v4,v2}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 30 invoke-virtual {v4,v3}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 31 invoke-virtual {v4}, Ljava/lang/StringBuilder; ->toString()Ljava/lang/String; move-result-object p1 .line 32 return-object p1.end method
预警详情标题 这里的“暴雨蓝色预警”字样同样没出现在response当中,因此猜测与预警卡片中标题的原因一致。这里由上一个问题的经验,通过auto.js的悬浮窗调试功能,获取该TextView
的ID:2131361878
,直接全局搜索2131361878
即可定位到代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public a (View view) { super (view); this .y = (TextView)view.findViewById(2131361878 ); this .z = (ImageView)view.findViewById(2131361876 ); this .A = (TextView)view.findViewById(2131361874 ); this .B = (TextView)view.findViewById(2131361875 ); this .x = (LinearLayout)view.findViewById(2131361903 ); } ... textView.setText((CharSequence)activityAlertDetail.getString(2131820591 , new Object []{string2, alert.getLevel()})); ((l)((d)b.a(ActivityAlertDetail.this ).a(alert.getIconUrl()).a((n)c.e())).b((Drawable)null )).a(this .z); this .A.setText((CharSequence)a1.c(alert.getPubTimeNum((Context)ActivityAlertDetail.this ), (Context)ActivityAlertDetail.this )); this .B.setText((CharSequence)alert.getDetail());
同样用StringBuilder
修改掉getString
即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 .line 4 invoke-virtual {p1}, Lcom/miui/weather2/structures/Alert; ->getType()Ljava/lang/String; move-result-object v2 .line 5 invoke-virtual {p1}, Lcom/miui/weather2/structures/Alert; ->getLevel()Ljava/lang/String; move-result-object v3 .line 6 const-string v4, "\u9884\u8b66" .line 7 new-instance v5, Ljava/lang/StringBuilder; invoke-direct {v5}, Ljava/lang/StringBuilder; -><init>()V .line 8 invoke-virtual {v5, v2}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 9 invoke-virtual {v5, v3}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 10 invoke-virtual {v5, v4}, Ljava/lang/StringBuilder; ->append(Ljava/lang/String; )Ljava/lang/StringBuilder; .line 11 invoke-virtual {v5}, Ljava/lang/StringBuilder; ->toString()Ljava/lang/String; move-result-object v1 .line 12 invoke-virtual {v0, v1}, Landroid/widget/TextView; ->setText(Ljava/lang/CharSequence; )V const/4 v5, 0x0
这里最后一行将v5
置零,否则会触发报错。原代码中将v5
置零不仅在这一段代码中使用,后面也用于if
的参数
1 com.miui .weather2 .ActivityAlertDetail$a $a .c (int) failed to verify: void com.miui .weather2 .ActivityAlertDetail$a $a .c (int): [0x93] args to 'if' (Precise Reference: java.lang .StringBuilder ,Integer) must be integral (declaration of 'com.miui.weather2.ActivityAlertDetail$a$a' appears in base.apk)