14

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

whoopdedoo
  • 2,815
  • 23
  • 46
  • @AdamArold but the linked question doesn't answer this one. The accepted answer just says "No, it's not possible in the same app", but there is no explanation why some apps can do it. – UeliDeSchwert Jul 19 '16 at 13:20
  • Think about this - you have a server that listens for constant communication from your installations. After a certain amount of time, you should be able to flag an installation as invalid if no response has been seen. That doesn't help open a browser on uninstall, though, but it helps track active users – OneCricketeer Jul 19 '16 at 13:37
  • @cricket_007 yes you can track it with push notifications for example like appsflyer tracking system doing. But how you said it's not help you to open browser after uninstall – Alexander Pereverzev Jul 19 '16 at 13:50
  • @AlexanderPereverzev Did you find any workaround? – Rahulrr2602 Mar 25 '18 at 13:10

2 Answers2

7

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

whoopdedoo
  • 2,815
  • 23
  • 46
  • Thanks for investigating the issue further. Just a thought that can we track the opening of the Uninstall Screen? – Rahulrr2602 Mar 27 '18 at 07:34
  • we can hook everything, system calls and app code, but, for what ? u asked how it's done, there is a daemon which is not killed by the uninstall flow because it's somehow not attached to the app process or it's creating some deadlock, it seems they have different code for different ABIs – whoopdedoo Mar 27 '18 at 11:08
  • Using the `NativeHelper ` and using `new NativeHelper().watch()` and `new NativeHelper().watch2` we can track self Uninstallation for ARM ? Also what should be the value of `new File(file, "a2")` should it be something in our apps private directory? Also, will this method consume Battery? – Rahulrr2602 Mar 27 '18 at 11:21
  • 1
    1) only `onU()` invoked on uninstall. 2) yes, it creates files in app's directory and play with the locks & forking processes so one of them will not be killed because it's not in the same group (which you can see with running `ps` inside device's shell, looking on the group column for example `ua_51`). 3) which method ? – whoopdedoo Mar 27 '18 at 11:29
  • Thanks for the clarification. By`method` I mean this setup. As I have to keep the battery consumption very less. – Rahulrr2602 Mar 27 '18 at 11:32
  • 1
    two processes simultaneously monitor the other's lock, and one process can immediately hear the other hanging off, because they are blocking methods there is no power consumption efficiency problem, it's not `while` loop with `usleep` – whoopdedoo Mar 27 '18 at 11:44
  • Thank You very much. I think this is the only answer in the Stackoverflow which states how to solve this issue. Will surely try and update if there is an issue. – Rahulrr2602 Mar 27 '18 at 12:06
  • interesting issue no doubt, glad to help , will update if i'll have new discoveries. – whoopdedoo Mar 27 '18 at 12:09
  • Require one more small help from you. Can you please suggest some Decompiler which is good. – Rahulrr2602 Mar 27 '18 at 20:56
0

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

Kelevandos
  • 7,024
  • 2
  • 29
  • 46
  • 4
    No! It's not truth, your app doesn't receive this intent ) see [this link](https://developer.android.com/reference/android/content/Intent.html#ACTION_PACKAGE_REMOVED) – Alexander Pereverzev Jul 19 '16 at 13:39
  • Hmmmm, I am pretty sure I did it once using this method... Let me do some checking – Kelevandos Jul 19 '16 at 13:44
  • You are right, it does not seem to work. They must therefore use some hack, like an undocumented callback that is invoked on uninstall or another app which listens for the main app uninstall... If I can suggest something - unpack their .apk and check the code. Even if it is obfuscated, you should be able to see String constants, which could give you the idea of how they achieve this behavior :-) – Kelevandos Jul 19 '16 at 13:58
  • I appreciate someone else catching this because it is something I had tried previously to only find out it doesn't work. This is capable of being broadcast for other applications to receive, but not the app being uninstalled. – user376327 Oct 13 '16 at 16:11
  • @Kelevandos There are other apps which achieve this can you please try and find out the hack. – Rahulrr2602 Mar 25 '18 at 13:11
  • @Rahulrr2602 can you provide the package ids for "other apps" ? – whoopdedoo Mar 25 '18 at 19:29
  • @IddoE https://play.google.com/store/apps/details?id=com.qihoo.security this app does. Any idea how it does? – Rahulrr2602 Mar 26 '18 at 11:15
  • @Rahulrr2602 it's the same app mentioned in the main question – whoopdedoo Mar 26 '18 at 11:58
  • @IddoE Yes, that is the only app right now I have been able to find which works. There were some apps previously such Avast Antivirus which use to work on my device but now don't. – Rahulrr2602 Mar 26 '18 at 14:15
  • @Rahulrr2602 okay, if you find more do not hesitate to share here – whoopdedoo Mar 26 '18 at 15:33
  • @IddoE Really Thankful for your efforts. I am not much familiar with Android hence will take some time to understand your answer fully. Will surely share any other app which has this feature. Please try and investigate this topic further. – Rahulrr2602 Mar 26 '18 at 15:38