Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 602
Alexander Pereverzev Avatar asked Jul 19 '16 13:07

Alexander Pereverzev


People also ask

Is it possible to detect Android app uninstall?

How are App Uninstalls detected? You can detect app uninstalls by sending 'silent' push notifications. Silent push notifications are those notifications that aren't rendered on the user's device. You could send a silent push notification daily to all the devices with your app to track uninstalls.

Can you track app uninstall?

Detection of app uninstalls can be made by sending silent push notifications. Notifications that are not created on the devices of the app users are called silent push notifications. You can keep track of app uninstalls by sending silent push notifications to all devices where your app is installed.

Do apps know when you uninstall?

No. However, you can gauge your active user base by releasing an update and seeing how many people download it. Graphing it over time is useful data. Like Facebook, Twitter or Instagram too.

How do you make sure an app is completely uninstalled?

Do it correctly. Sometimes, when an app has outlived its usefulness, you'll want to uninstall it to make way for new ones. Uninstalling apps is easy enough: simply go to the Apps list, find the app, and hit the Uninstall button.


1 Answers

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 :)

like image 173
whoopdedoo Avatar answered Sep 27 '22 18:09

whoopdedoo