Open cpuuntery opened 2 years ago
Does it actually work? android.permission.INTERNET
is not a runtime permission.
@MuntashirAkon
Yes, I tried multiple times with multiple apps, and it works 100% every time.
And even if the file is called runtime-permissions.xml
.
The file includes almost all permissions, if not all permissions. even non runtime-permissions
and even if you don't believe me, you can try yourself using android emulator
Related issue #132
After a bit of research, it turns out while android can parse XML files natively. But It treats XML files as immutable files (Read only) and according to this answer, modifying XML file is really hard so I thought why not regex the XML file.
And yes, I know that some people think regexing in XML file is bad thing
but runtime-permissions.xml
is a really simple XML. there are not 10 nested children or anything like that
and we can exclude any permission we can change using pm revoke
here is a list of the permissions that pm revoke
can not change
android.permission.ACCESS_WIFI_STATE
android.permission.ACCESS_NETWORK_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.REORDER_TASKS
android.permission.DOWNLOAD_WITHOUT_NOTIFICATION
android.permission.FOREGROUND_SERVICE
android.permission.INTERNET
android.permission.MANAGE_ACCOUNTS
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.USE_CREDENTIALS
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
android.permission.AUTHENTICATE_ACCOUNTS
android.permission.READ_PROFILE
here is the regex that can change the permission
(android.package.name*?android.permission.anypermission" granted=)(".*?")
replace
$1true
to enable and $1false
to disable permission
so the plan is to parse runtime-permissions.xml
to get a list of packages and permission and exclude some permission that we can revoke using pm revoke
then regex the file to disable or enable permission
and lastly, i know implementing features is hard and time-consuming for devolopers but i think this feature will set the app apart from every other app that can control permission
Rest assured, if this feature is implemented, the file will be written just like how it is written by the framework. Also, regex will no longer work in the future as Android is migrating to a new binary XML schema for faster parsing of the XML files.
@MuntashirAkon To help the developer, this is my full research on the subject. I have a lot to say, so I will try to write three comments or five comment for easier readability. and not write everything in one comment to prevent it from turning in to a very long wall of text.
In my first comment. I only manipulated the permission on android 12 and did not test on older version of android. In the older version of android. You need to change /data/system/packages.xml, and you do not need to turn the true to false you just delete the line that contain the permission.
The first question I asked myself is how reliable changing packages.xml.
It turns out, changing it does not have effect until system_server restart(pkill system_server
) or android reboot
and the permission gets added to back to packages.xml after the package gets updated
so, after a bit of research, it turns out android cached permissions after parsing AndroidManifest.xml inside the apk.
In fact, this command will cause the cache to be written to packages.xml
dumpsys package write
After knowing, the permission get cached after parsing AndroidManifest.xml. I started asking myself can I stop android from caching the permission so changes to packages.xml take effect immediately. And according to this commit there are some interesting methods like
PackageManager.invalidatePackageInfoCache();
PermissionManager.disablePermissionCache();
PermissionManager.disablePackageNamePermissionCache();
PackageManager.corkPackageInfoCache();
@Override
public void disablePackageCaches() {
// disable all package caches that shouldn't apply within system server
PackageManager.disableApplicationInfoCache();
PackageManager.disablePackageInfoCache();
ApplicationPackageManager.invalidateGetPackagesForUidCache();
ApplicationPackageManager.disableGetPackagesForUidCache();
ApplicationPackageManager.invalidateHasSystemFeatureCache();
PackageManager.corkPackageInfoCache();
}
but some of these methods only exist in android 11+ and even if we can call these methods we still need to change packages.xml
So after giving up on disabling the permission cache, I asked myself can we manipulate/change the permission cache. But to change something you need to understand how it works first so i found a plenty of blogs in Chinese that explain how the caching works. I used Google Translate to read the blogs. blog1 blog2 blog3 blog4 blog5 blog6 blog7 blog8 blog9 blog10 blog11 blog12 blog13 blog14 the blog in english but is it is very old
After learning how permission cache works. How can we change it? Or can we intercept calls to the permission cache midway and change the returned results.
The answer is yes, we can. We just need to hook the PackageManagerService
process and manipulate the cached array of permissions.
And as a proof of my theory I found this thread to change the mapping of android permission to the linux user groups without restarting android or killing the system_server and changing etc/permissions/platform.xml
package kz.virtex.android.sdcardfix;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
public class XMain implements IXposedHookZygoteInit
{
@Override
public void initZygote(StartupParam startupParam) throws Throwable
{
final Class<?> pms = XposedHelpers.findClass("com.android.server.pm.PackageManagerService", null);
XposedHelpers.findAndHookMethod(pms, "readPermission", "org.xmlpull.v1.XmlPullParser", "java.lang.String", new XC_MethodHook()
{
protected void afterHookedMethod(MethodHookParam param) throws Throwable
{
String permission = (String) param.args[1];
if (permission.equals("android.permission.WRITE_EXTERNAL_STORAGE")) {
Class<?> process = XposedHelpers.findClass("android.os.Process", null);
int gid = (Integer) XposedHelpers.callStaticMethod(process, "getGidForName", "media_rw");
Object mSettings = XposedHelpers.getObjectField(param.thisObject, "mSettings");
Object mPermissions = XposedHelpers.getObjectField(mSettings, "mPermissions");
Object bp = XposedHelpers.callMethod(mPermissions, "get", permission);
int[] bp_gids = (int[]) XposedHelpers.getObjectField(bp, "gids");
XposedHelpers.setObjectField(bp, "gids", appendInt(bp_gids, gid));
}
}
});
}
private static int[] appendInt(int[] cur, int val)
{
if (cur == null) {
return new int[]
{ val };
}
final int N = cur.length;
for (int i = 0; i < N; i++) {
if (cur[i] == val) {
return cur;
}
}
int[] ret = new int[N + 1];
System.arraycopy(cur, 0, ret, 0, N);
ret[N] = val;
return ret;
}
}
in the old days there was exposed, but now there are only two options, Zygisk or LSPosed
lastly how can you test Zygisk or LSPosed
well LSPosed require Magisk to be installed and Zygisk comes by default with Magisk
the question now is how to install Magisk
well if you are using android avd you can use this repository https://github.com/newbit1/rootAVD
you alse can run android inside using Docker https://github.com/remote-android/redroid-doc https://github.com/ayasa520/redroid-script
or android x86 for newer version you can use BlissOS these two repos to install Magisk https://github.com/HuskyDG/initrd-magisk https://github.com/shakalaca/MagiskOnEmulator
@MuntashirAkon And to emphasize again, I am not writing multiple comment to bother the developer or anything like that. I wrote multiple comment for easier readability.
And adding process hooking to the app will help with making existing features like changing the SSAID without the need for reboot.
And it will also help in the future if android changed every xml to the new binary xml format. Because we can let android do the heavy lifting, and we can intercept the data midway to read it or change it.
And just to be safe, this is my last comment on this issue unless I hear anything that says otherwise from the developer
I have only skimmed over your comments. So, please correct me if I am wrong but this looks overly complicated. Unlike say SSAID or SAF, the probability of breaking something is quite high here. Permissions are at the heart of Android and its security. Therefore, they have to be handled delicately. I think, an interesting experiment would be to find a way to let the system know that the permissions are in fact run-time permissions, not the usual normal permissions.
@MuntashirAkon
So, please correct me if I am wrong but this looks overly complicated
There are many ways to do the same thing. This is what I was meaning in my comments.
In android, apps can not use permissions even if it is normal (granted at install time) if it is not declared in AndroidManifest.xml
My thought is to use this behaviour.
After AndroidManifest.xml gets parsed, the permissions array is passed(we will intercept the array before it get passed to the next component) to the android components responsible for permission management.
by using this way, we will not change anything (logic or behaviour inside permission management) in android permission management/enforcement.
The only thing that is changed is data that is passed to it
so if the app have
android.permission.RECEIVE_BOOT_COMPLETED
in it's AndroidManifest.xml
we will make android think that the permission does not exist in the manifest.
Android default behaviour is to deny any permissions that are not in AndroidManifest.xml
Permissions are at the heart of Android and its security. Therefore, they have to be handled delicately
While I didn't mean to suggest doing things this way
even if we change the behaviour or logic of permission management, like the link to the POC.
I fail to understand how that will make android less secure. I mean yes it is more aggressive change than what I was thinking of, but it is still a valid option
And this feature request is about revoking permissions, not granting permissions, so I think it will improve security.
@MuntashirAkon
I think, an interesting experiment would be to find a way to let the system know that the permissions are in fact run-time permissions, not the usual normal permissions
Your suggestion is also very valid. And i 100% agree it is valid, but it is not the only one in my opinion.
I think that my first plan (tricking android into thinking the permission does not exist) is the easiest.
But in the end I just want this feature implemented in your great and wonderful app
In android, apps can not use permissions even if it is normal (granted at install time) if it is not declared in
AndroidManifest.xml
My thought is to use this behaviour.
Yes. But the latter approach is universal, applicable to all apps and within the concepts used by Android, and this sort of features already offered by some operating systems (e.g. Graphene OS).
we will not change anything (logic or behaviour inside permission management) in android permission management/enforcement.
It's not that simple. Take the INTERNET permission as an example. An app declaring this permission expects that the permission would be granted by default. If the permission were revoked or were not present in AndroidManifest.xml, the app would crash immediately. To mitigate the issue, additional measures have to be taken to ensure that the such thing would never happen. This requires a lot of modifications to the frameworks via hooks (e.g. creating a fake app op and controlling the permission via app op instead of the permission itself). However, not all permissions cause such issues. But most interested ones (such as the one I've just discussed) do.
@MuntashirAkon
It's not that simple. Take the INTERNET permission as an example. An app declaring this permission expects that the permission would be granted by default. If the permission were revoked or were not present in AndroidManifest.xml, the app would crash immediately.
i fully agree with you that maybe disabling permissions that the app expect will always available will maybe cause an app to crash. So what you are thinking may apply to a permission like
android.permission.VIBRATE
and
android.permission.RECEIVE_BOOT_COMPLETED
But i 100% sure tricking android to believe that if android.permission.INTERNET
does not exist in AndroidManifest.xml
does not cause any app to crash
app without android.permission.INTERNET
permission is the same as app
with android.permission.INTERNET
permission, but Wi-Fi is disabled and airplane mode is on
and if your theory is really correct, then I would have seen app crashes. When I manually modified packages.xml
or runtime-permissions.xml
So I suggest that we trick android into thinking that android.permission.INTERNET
does not exist in AndroidManifest.xml
when dealing with the INTERNET permission
and use you appops
method when dealing with other permissions
i fully agree with you that maybe disabling permissions that the app expect will always available will maybe cause an app to crash. So what you are thinking may apply to a permission like
android.permission.VIBRATE
andandroid.permission.RECEIVE_BOOT_COMPLETED
You can revoke the related app op for the former, and for the latter, you can simply disable the broadcast receivers that receive it.
app without
android.permission.INTERNET
permission is the same as app withandroid.permission.INTERNET
permission, but Wi-Fi is disabled and airplane mode is on
That's not how it works. The network API directly throws a SecurityException when it cannot initiate an Internet connection. Since most apps expect it to be granted beforehand, most apps crash immediately when it attempts to connect to the Internet (see #833).
Yes. But the latter approach is universal, applicable to all apps and within the concepts used by Android, and this sort of features already offered by some operating systems (e.g. Graphene OS).
I think that implementing appops. Will take too much time and is way harder to do than tricking android into thinking that a specific permission does not exist in AndroidManifest.xml
.
And just like I said before it my not work with all the normal permissions, but I am sure it will work with android.permission.INTERNET
permission.
I suggest that you consider using the [tricking android into thinking a permission does not exist in AndroidManifest.xml] with at least just the internet permission.
Don't Let the Perfect Be the Enemy of the Good
The appops method, may be more elegant, but it is harder to implement. My method, maybe less elegant, but is way easier to implement. That means that if only and only if you are willing and interested. My method will take less of your valuable time than the appops method, so I am suggesting my method because I think of you and your valuable time.
@MuntashirAkon
You know what they say, a picture is better than a thousand word
I tried to block internet access using my way of changing packages.xml
and I used version 3.0.0 the same version in issue #833
the app still works after I blocked the internet access
I guess my way is better than GrapheneOS. The only thing that it is lacking that updating the app negate the block
Here are some very interesting logs after I blocked the internet using my way
*APP* UID 10079 ProcessRecord{de8e56c 4000:io.github.muntashirakon.AppManager/u0a79}
user #0 uid=10079 gids={50079, 20079, 9997} ###[[NO group 3003 aka internet]]###
Package [io.github.muntashirakon.AppManager] (da60f24):
userId=10079
pkg=Package{2299a8d io.github.muntashirakon.AppManager}
codePath=/data/app/io.github.muntashirakon.AppManager-QB3YGc-8iuVZ1DW02eibPQ==
versionCode=410 minSdk=21 targetSdk=32
versionName=3.0.0 ###[[same version in issue #833]]###
splits=[base]
apkSigningVersion=2
flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA LARGE_HEAP ]
privateFlags=[ HAS_DOMAIN_URLS PARTIALLY_DIRECT_BOOT_AWARE PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ]
dataDir=/data/user/0/io.github.muntashirakon.AppManager
supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
timeStamp=2023-06-06 12:50:30
firstInstallTime=2023-06-06 12:50:32
lastUpdateTime=2023-06-06 12:50:32
signatures=PackageSignatures{edbf442 [f3d46410]}
installPermissionsFixed=true installStatus=1
pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA LARGE_HEAP ]
install permissions: ###[[NO android.permission.INTERNET]]###
android.permission.GET_APP_OPS_STATS: granted=true
android.permission.ACCESS_NETWORK_STATE: granted=true
android.permission.REQUEST_DELETE_PACKAGES: granted=true
com.android.launcher.permission.INSTALL_SHORTCUT: granted=true
android.permission.DUMP: granted=true
cat /data/system/packages.list
io.github.muntashirakon.AppManager 10079 0 /data/user/0/io.github.muntashirakon.AppManager default:targetSdkVersion=32 none ##[[NO group 3003 aka internet]]##
Here is how I changed packages.xml
sed -i -E '/.*<package name>.*/,/.*<permission that will be removed>.*/ s/.*<permission that will be removed>.*/\n/' /data/system/packages.xml
sed -i -E '/.*io.github.muntashirakon.AppManager.*/,/.*android.permission.INTERNET.*/ s/.*android.permission.INTERNET.*/\n/' /data/system/packages.xml
reboot android after the reboot, the app will lose internet access
@MuntashirAkon
If you have some free time. Can you post an update of your thoughts on this issue.
I tried the sed
command, and it worked, but a reboot is needed after you run the command
following this feature request... adding this and the apk compile/decompile feature would make this app the true ultimate swiss army knife of android app management
Describe the solution you'd like While you can control plenty of permission using Appops. But there are permissions you cannot control using Appops like [android.permission.INTERNET] but according to this post you can control other permissions using the file [runtime-permissions.xml] the file path in android 6 to 9 is
/data/system/users/<User_ID>/runtime-permissions.xml
but in android 12/data/misc_de/0/apexdata/com.android.permission/runtime-permissions.xml
For example, you can change
And android support parsing XML files. And I know I can edit the file manually, but the XML file has too many lines and the copy and paste after the edit is a hassle and a chore for me. I would appreciate it very much if this app will automate that process from me.