Android的反编译(布局植入篇)

您所在的位置:网站首页 反编译systemui状态栏沉浸 Android的反编译(布局植入篇)

Android的反编译(布局植入篇)

2023-10-26 22:08| 来源: 网络整理| 查看: 265

第一次接触到安卓反编译是在我念初中的时候,那个时候会反编译修改一些东西,但是没有原生开发技术的支持,想想这么早就接触到了反编译,到目前还这么菜,最近想起了捡一捡

首先分享自己反编译植入布局的一些小经验

不要动resources.arsc(反编译/回编译都容易失败,容易打乱其它文件的地址) 不要植入findViewById这类语句 使用tag绑定布局 尽量使用自定义View 将所有的findViewById换成findViewByTag 尽量不要植入一整个xml文件(植入容易改变大量xml的地址) 在java中任何的R.id/R.layout最后编译成class/smali中都变成了0x**的16进制地址

该篇内容需要具备以下知识:

安卓原生开发基础 安卓原生自定义View 内容观察者 使用Handler更新View 简单的线程相关 使用到的软件 MT管理器(使用它的文本修改功能) Apktool反编译工具(这里我用自己的MToolkit,还有国外的同名Apktool) AIDE(可用android studio等替代)

我们在植入任何的View之前,先确保这些View能被正确的显示出来,也就是说,我们需要跑出一个demo。这篇我们植入布局进手机的状态栏

AIDE部分 1.新建一个空项目

2.点击右上角把这个简单的app跑起来

3.新建一个java文件,随便编写一个自定义View

package com.nos; import android.annotation.*; import android.content.*; import android.database.*; import android.net.*; import android.os.*; import android.provider.Settings.*; import android.text.*; import android.util.*; import android.view.*; import android.widget.*; import java.io.*; import android.provider.Settings.System; public class StatusWeather extends TextView {     static final Uri WEATHER_URI = Uri.parse("content://weather/weather"); private final Context mContext; @SuppressLint({"HandlerLeak"}) private Handler mHandler; private final ContentObserver mWeatherObserver; private WeatherRunnable mWeatherRunnable; public StatusWeather(Context context) { this(context, null); } public StatusWeather(Context context,  AttributeSet attributeSet) { this(context, attributeSet, -1); } public StatusWeather(Context context, AttributeSet attributeSet, int defStyle) { super(context, attributeSet, defStyle); this.mHandler = new WeatherHandler(this); this.mWeatherObserver = new StatusWeatherObserver(this, this.mHandler); this.mContext = context; this.mWeatherRunnable = new WeatherRunnable(this, mHandler); setVisibility(this.GONE); this.mContext.getContentResolver().registerContentObserver(WEATHER_URI, true, this.mWeatherObserver); this.mContext.getContentResolver().registerContentObserver(System.getUriFor("your key"), true, this.mWeatherObserver); this.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View p1) { Runtime runtime = Runtime.getRuntime(); try { runtime.exec("input keyevent 4"); } catch (IOException ignored) { } Intent intent = new Intent(); intent.setClassName("com.miui.weather2", "com.miui.weather2.ActivityWeatherMain"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); mContext.startActivity(intent); } }); updateWeatherInfo(); } public void updateWeatherInfo() { this.mHandler.removeCallbacks(this.mWeatherRunnable); this.mHandler.postDelayed(this.mWeatherRunnable, 200); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (this.mWeatherObserver != null) { this.mContext.getContentResolver().unregisterContentObserver(this.mWeatherObserver); } } } class WeatherHandler extends Handler { final StatusWeather a; WeatherHandler(StatusWeather statusBarWeather) { this.a = statusBarWeather; } @Override public void handleMessage(Message message) { String str = (String) message.obj; this.a.setText(TextUtils.isEmpty(str) ? "点击获取\n天气数据" : str); this.a.setVisibility(message.what != 0 ? 0 : 8); } } class StatusWeatherObserver extends ContentObserver { final StatusWeather mStatusWeather; public StatusWeatherObserver(StatusWeather view, Handler handler) { super(handler); this.mStatusWeather = view; } @Override public void onChange(boolean z) { mStatusWeather.updateWeatherInfo(); } } class WeatherRunnable implements Runnable { final StatusWeather this$0; final Handler mHandler; public WeatherRunnable(StatusWeather weatherView, Handler handler) { this.this$0 = weatherView; this.mHandler = handler; } @Override public void run() { Object obj = ""; try { Cursor query = this$0.getContext().getContentResolver().query(StatusWeather.WEATHER_URI, null, null, null, null); if (query != null) { if (query.moveToFirst()) { String string = query.getString(query.getColumnIndexOrThrow("city_name")); String string2 = query.getString(query.getColumnIndexOrThrow("description")); String string3 = query.getString(query.getColumnIndexOrThrow("temperature")); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(string); stringBuilder.append("/"); stringBuilder.append(string2); stringBuilder.append(" "); stringBuilder.append(string3); obj = stringBuilder.toString(); } query.close(); } } catch (IllegalArgumentException e) { obj = "没有获取到天气信息"; } catch (Throwable th) { Message obtainMessage = mHandler.obtainMessage(); obtainMessage.what = 100; obtainMessage.obj = obj; mHandler.sendMessage(obtainMessage); } Message obtainMessage2 = mHandler.obtainMessage(); int bb=System.getInt(this.this$0.getContext().getContentResolver(),"your key", 0); // TODO: Implement this method obtainMessage2.what = bb; obtainMessage2.obj = obj; mHandler.sendMessage(obtainMessage2); } }

StatusWeather则是我们自定义的TextView,他用来获取本地的天气信息,内容观察者是为了其它的应用能够控制这个控件的显示与隐藏

添加进xml中

run起来康康

没有获取到天气,不过无事,可能是因为app的权限不是系统级别的,这篇也是讲个方法

植入部分 1.复制出你的状态栏apk

如下gif

2.反编译状态栏

由于我的工具箱还没有具备文本编写功能,所以我们用MT管理器,我这里需要植入这个TextView到我的下拉栏

3.在下拉栏xml中添加代码

这个xml的路径为res/layout/quick_status_bar_expanded_header.xml

可以发现这里植入了多个view了,不过都是差不多的

4.回编译

回编译耗时比较久,点击文件夹后面那个按钮即可看到弹窗

5.替换xml回原状态栏

可以观察到上面回编译已经生成新的apk了,这个apk是不能用的:

没有签名 地址被打乱了 换回去99%概率fc

我们需要将其中变换的部分提取出来 也就是

MiuiSystemUI_new.apk/res/layout/quick_status_bar_expanded_header.xml

按照原路径替换回我们原始的状态栏apk

没有完,这个替换回去后它是找不到这个自定义view的,自定义view在我们的aide项目中呢,所以我们

6.提取AIDE项目的dex

在你自己项目的路径下,有build的产物,apk/class/dex都有

我们只需要它的classes.dex

7.重命名classes.dex为classes2.dex

这步无细节操作,设为标题是怕大家忽略了

8.将classes2.dex添加进状态栏apk

是不是觉得熟悉,这里也是用到了Mutldex的做法 当然我们还可以将AIDE工程run出来的apk反编译,得到它的smali,将smali添加进状态栏apk反编译后的smali,随后回编译状态栏也就顺便把我们植入的smali一起回编译进了一个dex,提取出来,覆盖回原状态栏

最后效果

总结

植入布局的流程(只适用于无混淆无加固的app)

需要得到自己java对应的dex 反编译需要植入的app 添加布局到对应的xml 回编译app并提取出对应的xml 添加回编译后的xml到原apk 添加自己的dex到原apk

安卓原生动态添加布局用得好的话,植入addView对应的安卓字节码去添加一些View也是可行的



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3