Search code examples
javaandroidandroid-sourcesuandroid-5.1.1-lollipop

AOSP 5.1 - how can I execute my app with real-root rights?


In my AOSP 5.1-build, I am trying to create a MySetupApp.apk-app, that does some setup-actions according to the user.
E.g. among other things (that mostly also require root) the user should be able to select a bootanimation in my MySetupApp.apk. The bootanimation is saved on /sdcard/ and the apk should move it to the correct system-folder /system/media/bootanimation.zip This is working fine on the adb-shell. The following commands deliver the desired result (new bootanimation):

adb remount
adb shell cp /sdcard/bootanimation.zip /system/media/
adb shell chmod 777 /system/media/bootanimation.zip

The app is running as system-user: android:sharedUserId="android.uid.system" I can verify this on the adb-shell:

root@astar-yh:/system/bin # ps
system    9634  170   1242144 35536 ffffffff b6e51bf4 S com.mycompany.MySetupApp

I have added the app in my AOSP-build:

/android/device/softwinner/common/prebuild/apk/Android.mk:

include $(CLEAR_VARS)
LOCAL_MODULE := MySetupApp
LOCAL_MODULE_TAGS := optional
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
include $(BUILD_PREBUILT)

So the app is installed in /system/priv-app and is signed with the platform-keys.

Also I have tweaked /android/system/extras/su/su.c:

myuid = getuid();
// added AID_SYSTEM
if (myuid != AID_ROOT && myuid != AID_SHELL && myuid != AID_SYSTEM) {
   fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
   return 1;
}

// Removed this code-block
/*if(setgid(gid) || setuid(uid)) {
    fprintf(stderr,"su: permission denied\n");
    return 1;
}*/

Here is my Java-Code. The outputs are in comments behind the lines:

public void doRootStuff(){
  try {
    String line;
    Process process = Runtime.getRuntime().exec("su");

    OutputStream stdin = process.getOutputStream();
    InputStream stderr = process.getErrorStream();
    InputStream stdout = process.getInputStream();

    stdin.write(("getenforce\n").getBytes()); // "Permissive"
    stdin.write(("mount -o rw,remount -t ext4 /system\n").getBytes()); //E/[Error]: mount: Operation not permitted
    stdin.write(("ls /data/data/\n").getBytes()); // Works! Shows the directory contents
    stdin.write(("touch /system/media/test\n").getBytes()); // Does nothing - no error but also no file
    stdin.write(("cp -f /sdcard/bootanimation.zip /system/media/bootanimation_test.zip\n").getBytes()); // E/[Error]: cp: /system/media/bootanimation_test.zip: Read-only file system
    stdin.write("exit\n".getBytes());
    stdin.flush();

    stdin.close();
    BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
    while ((line = br.readLine()) != null) {
      Log.d("[Output]", line);
    }
    br.close();
    br = new BufferedReader(new InputStreamReader(stderr));
    while ((line = br.readLine()) != null) {
      Log.e("[Error]", line);
    }
    br.close();
    process.waitFor();
    process.destroy();

  } catch (Exception ex) {
    Log.e(TAG, ex.toString());
    ex.printStackTrace();
  }
}

As suggested here, I have also edited the android_filesystem_config.h, but without any luck

    { 06755, AID_ROOT,      AID_ROOT,      0, "system/priv-app/MySetupApp/*" },

Calling setuid(0); in my app also doesn't work:

com.mycompany.MySetupApp W/art: Failed to find OatDexFile for DexFile /system/priv-app/MySetupApp/MySetupApp.apk ( canonical path /system/priv-app/MySetupApp/MySetupApp.apk) with checksum 0x73b2ee8d in OatFile /data/dalvik-cache/arm/system@priv-app@[email protected]@classes.dex
com.mycompany.MySetupApp W/System.err: android.system.ErrnoException: setuid failed: EPERM (Operation not permitted)

So remounting the system-directory and copying something on it is not possible from the apk!

What can I do, to make this work? What should I change in my app or in the Android-Sources?

Update

I also tried to transfer the functionality into a script setup_device.sh and put it into /system/bin/setup_script.sh. When I try to run this in java:

Process process = Runtime.getRuntime().exec("su");
stdin.write(("./system/bin/setup_device.sh\n").getBytes());

it says:

E/[Error]: sh: <stdin>[2]: ./system/bin/setup_device.sh: Permission denied

Solution

  • I was able to resolve my issue:

    1.) As mentioned in the question I wrote a simple script, that copies the bootanimation from /sdcard/ to /system/media/. Put this script into /system/bin/

    setup_device.sh

    #!/sbin/busybox sh
    
    BUSYBOX="/sbin/busybox"
    
    echo "Remount /system-partition to rw"
    mount -o remount,rw /system
    
    if [ -e /sdcard/bootanimation.zip ] ; then
        echo "copy bootanimation"
        $BUSYBOX cp -fp "/sdcard/bootanimation.zip" "/system/media/bootanimation.zip"
        chmod 777 "/system/media/bootanimation.zip"
        chown 0.0 "/system/media/bootanimation.zip"
        $BUSYBOX rm "/sdcard/bootanimation.zip"
    else
        echo "Bootanimation not in /sdcard/-folder"
    fi
    

    2.) Invoke this script in init.rc and execute it on property-change

    service setup_device /sbin/busybox sh /system/bin/setup_device.sh
        user root
        group root
        disabled
        oneshot
    
    on property:start.setup=1
        start setup_device
    

    3.) In the setup.apk, Just set this property:

    Process proc = Runtime.getRuntime().exec(new String[] { "/system/bin/sh", "-c", "setprop start.setup 1" });
    proc.waitFor();
    

    Right after the app sets this property, the setup_device.sh-script will trigger and execute the code with root-rights! bootanimation.zip will finally copy to /system/media/ and you can see the new bootanmiation as soon as you reboot the device.

    Pitfalls:

    • When you declare the script-name in init.rc, be sure that is does not exceed the character-limit of 16 characters! Also only user alpha-numerical characters and - or _