rovo89 / XposedBridge

The Java part of the Xposed framework.
3.91k stars 1.1k forks source link

XSharedPreferences with MODE_PRIVATE #169

Closed daimoniuma closed 7 years ago

daimoniuma commented 7 years ago

I have some standard preferences which was saved in MODE_PRIVATE.

And I failed to get it in initZygote.

public static XSharedPreferences key_prefs;

 @Override
    public void initZygote(StartupParam startupParam) throws Throwable {
        key_prefs = new XSharedPreferences("my.package.name", "Key");
 }

How exactly I need to access my own settings? I need to keep this prefs in private.

aviraxp commented 7 years ago

Maybe you can store your settings in /data/system. As for sharedpreferences, try use

key_prefs.makeWorldReadable

But that may only works with SELinux permissive.

daimoniuma commented 7 years ago

I tried to makeWorldReadable. No reaction. My SELinux in normal mode from vendor (enforcing). Anyway my module should be universal.

Hmm... Could I set readable permissions in handleLoadPackage and return standard permissions after reading?

aviraxp commented 7 years ago

My thought: Place your file in /data/system and set the PRIVATE permission. It could work because /data/system is the place that system_server (zygote) place things. Also it has the right SELinux context.

If you want to use handleLoadPackage(), that might be hard because the process you hook may not have the permission to handle the file. Also you can try other methods, such as content provider. (Note that you cannot use it in initZygote()).

daimoniuma commented 7 years ago

I can't. It should be used in normal way (without Xposed) by my app.

If you want to use handleLoadPackage(), that might be hard because the process you hook may not have the permission to handle the file.

As I suggested: set needed permissions before reading, then return it back. Chmod, maybe. So I need to use libsuperuser, I guess. Is it possible, what do you think?

aviraxp commented 7 years ago

Oh, seems you have root access. If you do then, Zygote will asks for root permission I guess, which is kind of confusing, but should work.

daimoniuma commented 7 years ago

I'm noob here. Could you show example? Do I need to chmod directories or file only? Which params for operation?

aviraxp commented 7 years ago

I think chmod the file is enough. chmod 664 will make it world readable, and chmod 660 turn it back.

daimoniuma commented 7 years ago

Something creepy is going on here.

public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        if (!lpparam.packageName.equals("some.app"))
            return;

        String set_path = "/data/data/my.app/shared_prefs/Settings.xml";
        Runtime.getRuntime().exec(new String[] {"su", "-c", "chmod 664 " + set_path});

        String key_path = "/data/data/my.app/shared_prefs/Key.xml";
        Runtime.getRuntime().exec(new String[] {"su", "-c", "chmod 664 " + key_path});

        set_prefs = new XSharedPreferences("my.app", "Settings");
        key_prefs = new XSharedPreferences("my.app", "Key");

This code totally freezes my system.

daimoniuma commented 7 years ago

All as you described: Zygote is calling SuperSU and changing permissions to readable (which is finally works for me). But for some reason system is freezing. I tried many times. Hooked app is starting at boot completed. So system every time is stuck here.

aviraxp commented 7 years ago

How about this? https://github.com/apsun/RemotePreferences

daimoniuma commented 7 years ago

I successfully made it working by placing su requests under findAndHookMethod (su is called by hooked app now). Also I got unexpected issue that this Runtime.getRuntime().exec(new String[] {"su", "-c", "chmod 664 " + set_path}); have delay (few milliseconds). So XSharedPreferences tried to get forbidden files. Replaced with that:

Process process = Runtime.getRuntime().exec("su"); //Generic SU Command
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("chmod 644 " + XposedExtension.set_path + "\n");
os.writeBytes("chmod 644 " + XposedExtension.key_path + "\n");
os.writeBytes("exit\n");
os.flush();
os.close();
try {
process.waitFor();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

Also I got your ContextHelper which you suggested in the question beside. Thanks!

rovo89 commented 7 years ago

The reason why su blocks the execution is because it needs to ask the user for confirmation that the command shall be executed. So it sends an intent to the root app (whichever you're using), which will ask the user and will then send feedback to the binary. But in Zygote, no apps are running, so it waits infinitly. Some root apps have a setting to allow root commands without confirmation during boot.

daimoniuma commented 7 years ago

But I tried to ask for su in handleLoadPackage, not in initZygote, so there should be apps. I also allowed su during boot in SuperSU from Chainfire.

C3C0 commented 7 years ago

Why not use a bound service, e.g. in combination with Messenger?

daimoniuma commented 7 years ago

Because I don't know how to. If you will show me example of this idea, I'll be able to reproduce it.

C3C0 commented 7 years ago

Then it's a good time to start looking at documentation :) https://developer.android.com/guide/components/bound-services.html#Messenger

daimoniuma commented 7 years ago

It's a terrible time to do such things. 😿

kslakhani commented 5 years ago

How about this? https://github.com/apsun/RemotePreferences

You made my day mann, Thanks

yingshaoxo commented 4 years ago

https://github.com/XF-zhjnc/CloudMusicXposed/blob/7cd8feb789ce3ea79bb2b744cc9b8092f4a2b130/app/src/main/java/cn/com/zhjnc/cloudmusicxposed/ui/SettingsFragment.java#L26

It can be as simple as:

  1. in your activity, do this every time after configuring commitment:

    fun setWorldReadable() {
        val dataDir = File(this.getApplicationInfo().dataDir)
        val prefsDir = File(dataDir, "shared_prefs")
        val prefsFile = File(prefsDir, "main" + ".xml")
        if (prefsFile.exists()) {
            //Toast.makeText(this, prefsFile.path.toString(), Toast.LENGTH_LONG).show()
            for (file in arrayOf<File>(dataDir, prefsDir, prefsFile)) {
                file.setReadable(true, false)
                file.setExecutable(true, false)
            }
        }
    }
        global_switch.setOnCheckedChangeListener { _, isChecked ->
            with (sharedPref.edit()) {
                this.putBoolean("switch", isChecked)
                this.commit()
            }
            setWorldReadable()
        }
  2. in your xposed java class:

        XSharedPreferences pref = new XSharedPreferences(BuildConfig.APPLICATION_ID.toString(), "main");

That's it! You don't have to use pref.makeWorldReadable() or pref.reload...