解決Xposed攔截並修改:獲取手機已安裝應用的App包名和App標籤問題
首先,我們先明白一個問題,如何去遍歷獲取手機已安裝應用的App相應的資訊。
大多數情況下,我們使用PackageManager類提供的getInstalledPackages()介面來獲取手機已安裝應用資訊。
例如博主這裡的程式碼為:
PackageManager packageManager=getPackageManager();
List<PackageInfo> packageInfos=packageManager.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
getInstalledPackages()原始碼分析:
/** * Return a List of all packages that are installed on the device. * * @param flags Additional option flags to modify the data returned. * @return A List of PackageInfo objects, one for each installed package, * containing information about the package. In the unlikely case * there are no installed packages, an empty list is returned. If * flag {@code MATCH_UNINSTALLED_PACKAGES} is set, the package * information is retrieved from the list of uninstalled * applications (which includes installed applications as well as * applications with data directory i.e. applications which had been * deleted with {@code DONT_DELETE_DATA} flag set). */ public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
首先我們明確一下,PackageManager是一個抽象類,它內部的getInstalledPackages介面也是一個抽象方法,返回的是一個PackageInfo類的List集合,PackageInfo類即為App資訊的實體類。
下面我們開始探討如何去攔截getInstalledPackages()介面。
我們可以看到getInstalledPackages()是一個抽象方法,所以肯定不能直接去Hook它,只能去Hook它的實現方法,即找到PackageManager抽象類的具體實現類。我們繼續往下查詢程式碼,看看是誰實現了PackageManager抽象類。
首當其衝很容易就會找到MockPackageManager類,我們發現它確實繼承了PackageManager抽象類,但是它並不是真正的實現類。看一下它內部實現的getInstalledPackages介面:
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
throw new UnsupportedOperationException();
}
僅僅是丟擲了一個UnsupportedOperationException異常!
我們找到這個類的具體介紹:
*
* A mock {@link android.content.pm.PackageManager} class. All methods are non-functional and throw
* {@link java.lang.UnsupportedOperationException}. Override it to provide the operations that you
* need.
*
* @deprecated Use a mocking framework like <a href="https://github.com/mockito/mockito">Mockito</a>.
* New tests should be written using the
* <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
意思為:MockPackageManager類是PackageManager的模擬類,重寫它以提供您所需要的操作。所以它並不是真正的實現類。
那麼真正實現PackageManager抽象類的到底是誰??
答案是:ApplicationPackageManager類。
ApplicationPackageManager類中具體實現了PackageManager抽象類的一系列介面,getInstalledPackages介面也是在ApplicationPackageManager類中實現的。
這個時候你可能要抓狂了,因為你發現在編輯器中寫程式碼根本找不到這個ApplicationPackageManager類!
博主一開始也是對這個非常抓狂,一直找ApplicationPackageManager類的路徑位置,終於讓我給找到了,它位於AndroidSDK安裝路徑下。(ps:找到真的很不容易,平時開發很少關注SDK這塊---TAT----)
具體博主的路徑為:
找到了就很好辦了,開啟看看它的原始碼實現:
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.XmlRes;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.ArrayMap;
import android.util.IconDrawableFactory;
import android.util.LauncherIcons;
import android.util.Log;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.internal.util.UserIcons;
import dalvik.system.VMRuntime;
import libcore.util.EmptyArray;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** @hide */
public class ApplicationPackageManager extends PackageManager {
private static final String TAG = "ApplicationPackageManager";
private final static boolean DEBUG_ICONS = false;
private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
// Default flags to use with PackageManager when no flags are given.
private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
private final Object mLock = new Object();
@GuardedBy("mLock")
private UserManager mUserManager;
@GuardedBy("mLock")
private PackageInstaller mInstaller;
@GuardedBy("mDelegates")
private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
@GuardedBy("mLock")
private String mPermissionsControllerPackageName;
.......
//程式碼過長,有兩千多行,這裡貼出具體實現程式碼
@SuppressWarnings("unchecked")
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
return getInstalledPackagesAsUser(flags, mContext.getUserId());
}
/** @hide */
@Override
@SuppressWarnings("unchecked")
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
ParceledListSlice<PackageInfo> parceledList =
mPM.getInstalledPackages(flags, userId);
if (parceledList == null) {
return Collections.emptyList();
}
return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
........
}
從原始碼中我們可以很清楚的看到,首先我們可以看到ApplicationPackageManager類確實是繼承了PackageManager抽象類,其中關鍵的getInstalledPackages介面實現可以看到,原來它是返回了getInstalledPackagesAsUser()方法呼叫,getInstalledPackagesAsUser()才是具體的實現,接著看getInstalledPackagesAsUser()方法,關鍵程式碼為:
ParceledListSlice<PackageInfo> parceledList =
mPM.getInstalledPackages(flags, userId);
原來真正呼叫的是 IPackageManager類的getInstalledPackages()方法,引數flags即為我們在getInstalledPackages介面傳入的值。userId為Context類中的userId變數。
更加深入的原始碼分析就到這裡,有興趣的讀者可以自行檢視。在這裡我們的目的已經達到了,即找到PackageManager抽象類的具體實現類,下面我們開始探討如何去攔截它的getInstalledPackages介面。
Hook->getInstalledPackages介面
上面我們已經分析到,getInstalledPackages介面本身是一個抽象方法,在ApplicationPackageManager類中進行了該抽象方法的具體實現,所以我們Hook的目標類是ApplicationPackageManager,目標方法是getInstalledPackages(Int)。
實現程式碼為:
XposedHelpers.findAndHookMethod("android.app.ApplicationPackageManager", loadPackageParam.classLoader, "getInstalledPackages", int.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//Hook後的具體邏輯操作
}
});
在這裡還有一個疑問,通常我們攔截到該方法後,需要返回我們自己的處理資訊,我們瞭解到getInstalledPackages(Int)方法返回的是一個PackageInfo類的List集合,具體做修改的話難以下手,比如修改其中的某一個item資訊,或者修改的是整條list,這該怎麼辦?具體的處理邏輯為:遍歷原先的List集合,找到我們要修改的某條item,然後賦值修改該item的具體資訊。
程式碼如下:
XposedHelpers.findAndHookMethod("android.app.ApplicationPackageManager", loadPackageParam.classLoader, "getInstalledPackages", int.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//自定義一個新的List用來接收原來的返回資訊
List<PackageInfo> packageInfos=(List) param.getResult();
//開始遍歷查詢
for (PackageInfo packageInfo:packageInfos){
//匹配資訊為你想要修改的某個App標籤識別碼
if (packageInfo.applicationInfo.labelRes==2131230747){
//找到了,修改該App的應用包名
packageInfo.packageName="com.test.testing";
}
}
//把修改後的List當作結果返回去
param.setResult(packageInfos);
}
});
這裡具體再說一下出現的標籤識別碼labelRes
labelRes位於PackageItemInfo類中,官方描述為:
/**
* A string resource identifier (in the package's resources) of this
* component's label. From the "label" attribute or, if not set, 0.
*/
意思就是此包的字串資源識別符號(在包的資源中),主要作用是在查詢App標籤文字的時候作為標籤文字的識別符號,已確定查詢到相應的App標籤文字,我把它喊作是App標籤識別碼,意思更形象一些。
具體labelRes用到的地方就是我們接下來要將的部分,獲取App標籤文字資訊。
博主獲取App標籤文字的程式碼是這樣寫的:
packageInfo.applicationInfo.loadLabel(getPackageManager()).toString()
其中packageInfo為PackageInfo類的物件。這裡我們主要關注的是loadLabel()方法
loadLabel()方法位於PackageItemInfo類中,引數為一個PackageManager類的物件,這裡並沒有出現labelRes的影子,別急,我們接下來去看一下loadLabel()方法的原始碼:
/**
* Retrieve the current textual label associated with this item. This
* will call back on the given PackageManager to load the label from
* the application.
*
* @param pm A PackageManager from which the label can be loaded; usually
* the PackageManager from which you originally retrieved this item.
*
* @return Returns a CharSequence containing the item's label. If the
* item does not have a label, its name is returned.
*/
public CharSequence loadLabel(PackageManager pm) {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
if (labelRes != 0) {
CharSequence label = pm.getText(packageName, labelRes, getApplicationInfo());
if (label != null) {
return label.toString().trim();
}
}
if (name != null) {
return name;
}
return packageName;
}
我們先一步一步的看,首先是判斷了一個變數nonLocalizedLabel 是否為空,不為空的話就直接返回該變數。那麼nonLocalizedLabel變數又是什麼呢?我們看一下它的官方描述:
/**
* The string provided in the AndroidManifest file, if any. You
* probably don't want to use this. You probably want
* {@link PackageManager#getApplicationLabel}
*/
意思為:AndroidManifest檔案中提供的字串(如果有的話)。也就是說如果App的核心配置檔案裡面寫上了該App的標籤,那麼就可以直接返回該標籤資訊,不用執行下一步。很可惜的是大多數情況下nonLocalizedLabel變數為空,所以我們繼續往下分析:
我們看到判斷labelRes是否為0,這裡主要是判斷是否獲取到了識別符號labelRes,在獲得labelRes值的時候,如果獲取失敗則返回0,成功就返回labelRes碼,只有獲取labelRes碼成功才會進入獲取App標籤文字的方法getText().
毫無疑問,getText()方法的具體實現也是在ApplicationPackageManager類中,我們去看一下:
@Override
public CharSequence getText(String packageName, @StringRes int resid,
ApplicationInfo appInfo) {
ResourceName name = new ResourceName(packageName, resid);
CharSequence text = getCachedString(name);
if (text != null) {
return text;
}
if (appInfo == null) {
try {
appInfo = getApplicationInfo(packageName, sDefaultFlags);
} catch (NameNotFoundException e) {
return null;
}
}
try {
Resources r = getResourcesForApplication(appInfo);
text = r.getText(resid);
putCachedString(name, text);
return text;
} catch (NameNotFoundException e) {
Log.w("PackageManager", "Failure retrieving resources for "
+ appInfo.packageName);
} catch (RuntimeException e) {
// If an exception was thrown, fall through to return
// default icon.
Log.w("PackageManager", "Failure retrieving text 0x"
+ Integer.toHexString(resid) + " in package "
+ packageName, e);
}
return null;
}
我們可以看到,具體的獲取方法是getCachedString(),傳入的引數是一個ResourceName 物件。我們的識別符號labelRes最終傳入了ResourceName類的建構函式裡面。具體的原始碼分析不再講解,這裡我們主要的核心是如何去修改某個App相應的標籤文字。
修改App相應的標籤文字
兩種Hook方式:
第一種:Hook目標類是PackageItemInfo類,目標方法是loadLabel()
XposedHelpers.findAndHookMethod(PackageItemInfo.class, "loadLabel", PackageManager.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//匹配想要修改的App的標籤
if (param.getResult().equals("喜馬拉雅FM")){
param.setResult("測試");
}
}
});
第二種:Hook目標是ApplicationPackageManager類,目標方法是getText()
XposedHelpers.findAndHookMethod("android.app.ApplicationPackageManager",loadPackageParam.classLoader, methodName, String.class,int.class, ApplicationInfo.class,new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//匹配想要修改的App標籤文字識別碼
if ((int)param.args[1]==2131230747){
param.setResult("測試");
}
}
});
兩種方式都可以修改某一條item的標籤文字資訊,具體的使用看業務邏輯。
本文到此結束,需要用到本文,請標明出處!
相關推薦
解決Xposed攔截並修改:獲取手機已安裝應用的App包名和App標籤問題
首先,我們先明白一個問題,如何去遍歷獲取手機已安裝應用的App相應的資訊。 大多數情況下,我們使用PackageManager類提供的getInstalledPackages()介面來獲取手機已安裝應用資訊。 例如博主這裡的程式碼為: PackageManager p
獲取手機已安裝應用的name,bundleIdentitifer
workspace ica bsp form eid def scl nss nsarray 獲取手機已安裝應用的name,bundleIdentitifer Class c =NSClassFromString(@"LSApplicationWorkspace"
Android 獲取所有已安裝應用程式的包名
//因為我的手機是華為手機所以過濾掉了華為,大家可以按需求過濾 public void getAppProcessName(Context context) { //當前應用pid final PackageManager packageManager
手機如何安裝應用刪除不了(app安裝成系統目錄下、更改開機畫面、更改桌面桌布等)
又是週末,休閒娛樂來一發嘛(此處省略了個表情),為什麼要說安裝app成系統檔案去呢,問這個問題的你得去找個女朋友了,我這裡來個標準的答案是讓女友刪不掉你的app啊(至於這個app你想幹嘛就是你的事了)! 為了不讓女朋友反感,簡單粗暴的方法就是每天一張她精心批過的圖片當手機桌
查詢手機已安裝應用大小,適配 android 8
一、查詢手機所有已安裝的應用 可以通過 PackageManager 來獲取,可以使用 PackageManager pm = context.getPackageManager(); List<ApplicationInfo> apps = pm.getInsta
在建立Android專案完成之後修改包名和app名
1.修改包名 applyplugin:'com.android.application' android { compileSdkVersion26 buildToolsVersion"26.0
獲取所有APP包名和啟動Activity名
轉載自:http://my.eoe.cn/870420/archive/1314.html public class MainActivity extends Activity { ArrayList<String> list; private List&
Appium+Python自動化 -獲取 app 包名和 activity
輸入 manage com dong ger 環境變量 自動 相關 ack 方法一: ①手機通過USB連接電腦 ②打開手機上被測app ③在電腦上 dos命令窗口,輸入命令 adb shell dumpsys window w | findstr \/ | f
【舊文章搬運】獲取並修改PEB中的映像路徑,命令列和當前目錄
原文發表於百度空間,2008-7-24 當時對UNICODE_STRING的使用還有點問題,導致最終效果圖中字串被截斷了========================================================================== 先從分析PEB開始吧.感覺分析這個東
Android基礎:獲取手機聯絡人工具類
Bean: public class ContactInfo { public String id; public String name; public String phone; } Co
Android工具類:獲取手機的資料夾及檔案列表
效果圖: package wuwang.tools.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja
Unity&Android之二:獲取手機電量資訊、網路狀況
Unity&Android之二:獲取手機電量資訊、網路狀況 遊戲中經常會在UI顯示電量以及網路狀況 手機電量包括: 1、當前正在充電還是放電 2、當前電量值 網路包括: 1、如果是WIFI,WIFI訊號強度 2、如果是流量,訊號強度等資料
Fiddler攔截並修改移動端請求
由於測試電商平臺APP,需測試購買,但又限於公司一提到錢,就給種不給力,所以想到使用Fiddler攔截訊息,修改一個虛擬商品ID,虛擬商品價格為0.01元,方便以後測試。 1.開啟Fiddler,配置代理 開啟工具欄Tools-Telerik Fiddler Options
Android 在Android手機上獲取其他應用的包名及版本號
string android手機 app名 fin 產品 logo 信息 led 直接 獲取Android手機上其他應用的包名及版本號方法有很多,可以通過AAPT從APK包中直接獲取,也可以通過代碼在手機上獲取。顯然,對於產品或者用戶來說要獲取這些信息,在手機上獲取更為簡便
獲取應用版本號,版本名稱,包名,AppName,圖標,是否是系統應用,獲取手機中所有應用,所有進程
pac version raw 是否 系統 app bsp agen nco PackageManager packageManager = getPackageManager(); PackageInfo packageInfo; = packageManager.get
Android同一套程式碼打多個APP包並能夠在同一個手機上安裝執行
Android同一套程式碼打多個APP包並能夠在同一個手機上安裝執行 Android同一套程式碼打多個APP包並能夠==在同一個手機上安裝執行==,同時==APP名稱、桌面icon圖示也都不同== 給同一套程式碼起不同的多個包名,並把APP名稱和桌面圖示設定為
Appium基礎總結1:獲取包名和Activity
主要通過cmd命令:aapt來獲取包名和Activity 1.安裝android build-tools SDK安裝目錄雙擊Manager.exe,勾選Build-tools 2.PATH配置環境變數 將SDK安裝目錄下的\Android\android-sdk\build-tools\24.0.0;新增到
Android獲取手機裝置識別碼(IMEI)和手機號碼
最近看了下獲取手機裝置ID和手機資訊以及SIM的資訊例子,主要還是借鑑別人的,現在自己寫一下,算是鞏固加深了,也希望能給大家一個參考 必要的條件還是一部真機,SIM卡或者UIM卡。 首先,在AndroidMainfest.xml裡獲取許可權 <uses-permiss
獲取手機內部所有應用的方法比較 PackageInfo、ResolveInfo
首先我們來看一下PackageItemInfo,它是包含了一些資訊的基類,它的直接子類有: ApplicationInfo、 ComponentInfo、InstrumentationInfo、PermissionGroupInfo、PermissionInfo。
android平臺獲取手機IMSI,IMEI ,序列號,和 手機號的方法
/** * 獲取android當前可用記憶體大小 */ private String getAvailMemory() {// 獲取android當前可用記憶體大小 ActivityManager am = (ActivityMa