Alexander Pereverzev
Alexander Pereverzev

Reputation: 235

How some apps track their own uninstall on android

I found that 360 security app after uninstall open their page in browser.

They can do it on all android versions (4., 5. and 6.*) and I don't understand how.

Maybe someone have any ideas? I know about same questions here and here and others but they still have no answers.

It's not a bug with inotify because its works only on < 4.4.2 android, there is no other processes which listen for same bug in new way, I checked.

They had some magic in their lib eternity.so

Upvotes: 14

Views: 1503

Answers (2)

James W.
James W.

Reputation: 3055

Let me try to clarify.

Before Android 4.4 we could use inotify, which provides a mechanism for monitoring filesystem events, we create a daemon to monitor if a file we created in our app's directory is been deleted or our main app directory is deleted, which should happen when user uninstall the app, the JNI code will look something like this:

// initializes a new inotify instance and returns a file descriptor
fd = inotify_init();
// watch directory delete/create events 
inotify_add_watch(fd, DIRECTORY, IN_DELETE | IN_CREATE);
__android_log_print(ANDROID_LOG_INFO, "TAG", "Watching [%s]", DIRECTORY);
// TODO: implement checkIfDeleted
if(checkIfDeleted()) {
    // execute intent to open url
    system("/system/bin/am start --user 0 -a android.intent.action.VIEW -d https://www...");

This no longer works because uninstall also kill group processes, attaching the relevant code from current source code

ActivityManagerService invoke kill

ProcessRecord kill group

Take a look at git log

I need more time to investigate on unrooted device, 360security pack the app with specific architecture (a.k.a. ABI) and probably per API to reduce APK size, unfortunately apkmirror(.com) has only ARM available for download, I prefer to read x86, might edit this answer in the near future.

So far it seems the native code is creating files and playing with the locks to detect when the process is dead after the uninstall and then use JNI interface to invoke the callback.

To simplify, it seems it locks itself, and then join the synchronization module notify_and_waitfor.

You can see a native code example for Android 5.0 here

NativeHelper source code (decompiled):

All method with keyword native are implemented inside eternity binary

package com.qihoo.eternity;

import com.qihoo.eternity.b;

public class NativeHelper {
    static {
        try {
            System.loadLibrary((String)"eternity");
        }
        catch (Exception exception) {}
    }

    public native void look(String var1, String var2, String var3, String var4, String var5);

    public void onU() {
        b.a().g();
    }

    public native void pass(String var1, String var2);

    public void peerDead() {
        b.a().f();
    }

    public native void watch(String var1, String var2, String var3, String var4);

    public native void watch2(String var1, String var2, String var3, String var4, String var5);
}

App references for NativeHelper:

com/qihoo/eternity/b.java:203:
    new NativeHelper().look(b.this.h.getPackageName(), b.b((b)b.this).f, string2, b.b(b.this.i), string3);
com/qihoo/eternity/b.java:224:
    new NativeHelper().watch(new File(file, "a1").getAbsolutePath(), new File(file, "a2").getAbsolutePath(), new File(file, "a3").getAbsolutePath(), new File(file, "a4").getAbsolutePath());
com/qihoo/eternity/b.java:264:
    new NativeHelper().watch(new File(file, "a2").getAbsolutePath(), new File(file, "a1").getAbsolutePath(), new File(file, "a4").getAbsolutePath(), new File(file, "a3").getAbsolutePath());
com/qihoo/eternity/b.java:518:
    new NativeHelper().pass(this.a, this.b);
com/qihoo/eternity/b.java:563:
    new NativeHelper().watch2(new File(file, "b1").getAbsolutePath(), new File(file, "b2").getAbsolutePath(), new File(file, "b3").getAbsolutePath(), new File(file, "b4").getAbsolutePath(), b.this.h.getDir("lib", 0).getAbsolutePath());
com/qihoo/eternity/b.java:588:
    new NativeHelper().watch2(new File(file, "b2").getAbsolutePath(), new File(file, "b1").getAbsolutePath(), new File(file, "b4").getAbsolutePath(), new File(file, "b3").getAbsolutePath(), b.this.h.getDir("lib", 0).getAbsolutePath());

One solution will be to include the shared object eternity.so in your JNI folder and implement NativeHelper.onU method :)

Upvotes: 8

Kelevandos
Kelevandos

Reputation: 7082

The app can specify a BroadcastReceiver with action:

"android.intent.action.PACKAGE_REMOVED" 

It will be called each time a package is removed, even if it is the app's own package. Then, in the Receiver, the app can check which package exactly was removed and react accordingly.

Please mind that different versions of the system may treat this differently, giving the Receiver varying amounts of time before the app's process is shut down. The performed action should therefore be quick and aimed on an external target, like sending a ACTION_VIEW Intent with a webpage url inside that you mentioned :-)

Upvotes: 0

Related Questions