1. 程式人生 > >解決Xposed攔截並修改:獲取手機已安裝應用的App包名和App標籤問題

解決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攔截修改獲取手機安裝應用AppApp標籤問題

首先,我們先明白一個問題,如何去遍歷獲取手機已安裝應用的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