Making an Android Spy App! Working with Socket.io and Node.js

Currently, I am working on an android project in which the main task is to fetch user data. In this post, I will tell you about, how to create a node js server and how to transfer data in real-time between two machines using web sockets.

What is a Web-socket?

Wiki says,

Web-socket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.

The Web-socket protocol enables interaction between a web client (such as a browser) and a web server with lower overheads, facilitating real-time data transfer from and to the server.

So what is the use of Web-socket in my project?

The web app sends a command to the android app (bot) to send some data back to it. So I have to communicate with the server, the web app and the bot in real-time, so I opted for web-sockets.

I am using the socket.io library. It has two variants – One for server-side and another for client side. Socket.io-client-java for android app and socket.io for node.js i.e., server side.

What is node.js? What will it do? Is it is necessary?

Node.js creates a server by which we can generate dynamic page content, can create, open, read, write, delete, and close files on the server. Node.js uses JavaScript on the server-side as the language.

In this project, we are using the socket.io library for managing seamlessly communication between client and server. But problem is that server-side library for socket.io is only available in node.js only.

You can create a node.js server just by the code below:-

Now, let’s dig into the android part.

Requirements to start this project:

  • Android Studio – You should be comfortable with android studio
  • Programming language for Android – Java or Kotlin – I’m working with Kotlin
  • Basics of Node.js and Socket.io

Let’s start 🙂

  1. Make a new android studio project.
  2. Create an activity named main activity.
  3. Add
    implementation 'io.socket:socket.io-client:1.0.0'

    to your app level build.gradle dependencies.

  4. Add the following uses-permission tags to your manifest file

Now what we want to do is to fetch user data while the app is running in the background. So we have to create a service which will be running in the background 24×7.  Create a service named CommandService, which extends Service.

Lets, talk about working of MainActivity first. It is simple and straightforward and has to get all uses-permission from the user and then start the service. Once service is started with all uses-permission MainActivity is done. See the source code of MainActivity here.

Now I’m assuming that CommandService(which is an App Service) is started and running with all uses-permissions. Our next task to setup socket.io and accept commands from server via the socket and answer to them by sending required data back to the server.

As you can see that I’m connecting socket.io to the server and sending some basic details about the device back to the server. CommonParams contains all of these details. UID is to define android device uniquely. If you don’t understand this, leave a comment.

The connection is now established and android device is uniquely identified on the server and we can communicate bidirectionally i.e., the server can send data to the android device and vice-versa 🙂. We can send different types of data through the socket, but we only need to send string type data. For sending an image we can convert it into a string by encoding it into base64. JSON is also converted to a string and emitted through the socket. Read more about JSON here.

Note

You have to get uses-permission by yourself.

Your major task is here to run service for almost 100% time or say >80%. Because after some time android stops your service due to either lack of resources or saving battery or some other optimization. I’ll write about it later or you can google it.

 

Now, I am going to show you how to fetch all contacts from the bot.
  • First, we have to tell the bot to fetch user’s contacts, which we can do by sending a keyword to bot from server via socket i.e.,  getContacts.
  • Now we have to fetch all contacts from the android’s on-device storage.
  • After retrieving contacts from Android, we have to send contacts back to the server via the socket.
  • Now we have user’s contacts on the server. Next task is to display them as you want. Isn’t it simple?

When it’s complete, following images show the end result:

First Step – Send Command to Bot from Server
socket.emit('commands', {commands: [{command: 'getContacts'}]);

Run above line of code from the node.js server. Where socket is a connection between server and bot. And we are emitting an event named commands and sending a JSON object with the command name.

Second Step – Fetch all contacts from Android database

Make a new class named GetContactsTask.kt and add below code in it.

If you have read the above code then you would have realized that both second and third steps are covered in it. Function getContacts() is retrieving all the contacts and converting them to a JSON string. After that function uploadData() uploads the contacts to Node.js server.

Receive data on Node.js server

socket.on('usrData', function (data) {
        console.log(data);
 });

Similarly, we can get all other user data like – SMS, call-logs, location, images, videos, etc.

Fourth Step – Displaying data either on web-app or mobile-app

Node.js server has the user data now. We just have to display it somewhere, but I am not going to that part right now. Maybe, I’ll write about it later 😜. Keep watching this space.

You can see the complete project on GitHub(and clone it 😜):

  • You will find Android app code here,
  • and server-side Node.js and JavaScript web-app here.

If you’ve queries/suggestion, feel free to leave a comment below.

4 thoughts on “Making an Android Spy App! Working with Socket.io and Node.js

  • August 11, 2018 at 8:48 am
    Permalink

    if android stops the services due to any reasons, how can we establish the connection again?
    Or will we have to make user install the app again?
    Because, keeping this service running all the time is not really possible.

    Also, any thoughts on getting access to the internal storage?

    i have been working/experimenting on trying to get access to all kind of files on storage and uploading them on a cloud storage (just like how google photos work) and periodically sync it like every 24 hours.
    This way practically we have everything there is on the mobile without actually needing a live backdoor which keeps dying after some point.
    Let me know what do you think.

    Reply
    • September 1, 2018 at 12:26 pm
      Permalink

      Happy to hear from you Zyglrox

      You are right, we cannot keep our service running 24×7, but we can try our best. So there are some methods which can help in running service continuosly:
      1. Return START_STICKY from your service onStartCommand method, obviously you do know that. By doing this Android restart your service if service was closed due to lack of resources.
      2. Using the Alarm Manager class you can trigger an alarm after an interval to start your service. I’ll send you another mail explaining how to do that.
      3. Once, I tested how WhatsApp keeps its service running, so I turn off my internet and then force closed WhatsApp and waited for service to be restart. But service didn’t start. But when I turn on the internet service started. Then I realize that server (GCM) is communicating with the app. So you can restart your service by using Firebase. You can integrate firebase notification in your app and then send notification from firebase. This will start firebase service in your app and in that service you can restart your original service even if your app is forced closed. Method 1 and 2 will not work if your app is closed but this will. I’ll send you my code in leading mail(s). Really its very nice to hear from you. Happy to help! 🙂

      For uploading photos you can listen to content resolver :-
      **contentResolver.registerContentObserver(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, true,
      ContentObserver
      )

      private var ContentObserver: ContentObserver = object : ContentObserver(Handler()) {
      override fun onChange(selfChange: Boolean) {
      //This function is called when there is change in content i.e., insertion, deletion, modification …
      }
      }**

      Reply
  • August 18, 2018 at 5:19 am
    Permalink

    Hello,

    I really appreciate your work, I try to export the application with android studio but I have an error message. Can not start compilation: the output path is not specified for “Android_Spy_App-master” module.
    Specify the output path in the Project Structure dialog. I would like to know if it is possible to have unique environmental listening device.

    Thank you for your help

    Reply
  • August 18, 2018 at 8:11 am
    Permalink

    Hello again,

    I fix this problem but now I have this error message: Android Source Generator: Error: Can not find bundle for base name messages.AndroidJpsBundle, locale en_US
    java.util.MissingResourceException: Can not find bundle for base name messages.AndroidJpsBundle, locale en_US
    at java.util.ResourceBundle.throwMissingResourceException (ResourceBundle.java:1564)
    at java.util.ResourceBundle.getBundleImpl (ResourceBundle.java:1387)
    at java.util.ResourceBundle.getBundle (ResourceBundle.java:773)
    at org.jetbrains.jps.android.AndroidJpsBundle.getBundle (AndroidJpsBundle.java:22)
    at org.jetbrains.jps.android.AndroidJpsBundle.message (AndroidJpsBundle.java:32)
    at org.jetbrains.jps.android.AndroidSourceGeneratingBuilder.computeModuleDatas (AndroidSourceGeneratingBuilder.java:1276)
    at org.jetbrains.jps.android.AndroidSourceGeneratingBuilder.doBuild (AndroidSourceGeneratingBuilder.java:130)
    at org.jetbrains.jps.android.AndroidSourceGeneratingBuilder.build (AndroidSourceGeneratingBuilder.java:114)
    at org.jetbrains.jps.incremental.IncProjectBuilder.runModuleLevelBuilders (IncProjectBuilder.java:1246)
    at org.jetbrains.jps.incremental.IncProjectBuilder.runBuildersForChunk (IncProjectBuilder.java:923)
    at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk (IncProjectBuilder.java:995)
    at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunkIfAffected (IncProjectBuilder.java:886)
    at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunks (IncProjectBuilder.java:719)
    at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild (IncProjectBuilder.java:371)
    at org.jetbrains.jps.incremental.IncProjectBuilder.build (IncProjectBuilder.java:178)
    at org.jetbrains.jps.cmdline.BuildRunner.runBuild (BuildRunner.java:138)
    at org.jetbrains.jps.cmdline.BuildSession.runBuild (BuildSession.java:308)
    at org.jetbrains.jps.cmdline.BuildSession.run (BuildSession.java:138)
    at org.jetbrains.jps.cmdline.BuildMain $ MyMessageHandler.lambda $ channelRead0 $ 0 (BuildMain.java:235)
    at org.jetbrains.jps.service.impl.SharedThreadPoolImpl.lambda $ executeOnPooledThread $ 0 (SharedThreadPoolImpl.java:42)
    at java.util.concurrent.Executors $ RunnableAdapter.call (Executors.java:511)
    at java.util.concurrent.FutureTask.run (FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor $ Worker.run (ThreadPoolExecutor.java:617)
    at java.lang.Thread.run (Thread.java:745)

    thank you very much

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *