Inside Geinimi Android Trojan. Chapter Two: How to check remotely the presence of the trojan

January 4, 2011  |  Jaime Blasco

In this chapter, we continue describing some of the trojan components. We realized that the code contains some ServerSocket stuff so let’s take a look at the system.

To obtain more information about a process on android we can upload the lsof tool to a rooted via adb tools. You can download the lsof static file here http://android-group-korea.googlecode.com/files/lsof.arm926ejs.static [no longer available].

To use the Android Debug Bridge (ADB) the device needs to have the debug mode enable:

Settings > Applications > Development > USB Debugging

Then connecting the device to the host computer using a USB cable or using the android emulator to simulate a phone we can use the adb tools. To upload the lsof binary:

jaime:tools jaimeblasco$ ./adb push lsof.static /data/

633 KB/s (3517525 bytes in 5.420s)

I recommend you to upload a busybox binary as well in order to be able to use commands like grep, find… You can obtain a busybox binary here

Then we can obtain a shell on the device running adb shell:

jaime:tools jaimeblasco$ ./adb shell

# id

uid=0(root) gid=0(root)

# cd /data/

# chmod 777 busybox

# chmod 777 lsof.static

Now we can use lsof to list interesting data about our process:

# ps|./busybox grep -i monk

app_32    526   33    109024 19224 ffffffff afd0eb08 S com.dseffects.MonkeyJump2

# ./lsof.static -p 526 2>/dev/null




cts.Monke 526    10032    0u   CHR        1,3           42 /dev/null

cts.Monke 526    10032    1u   CHR        1,3           42 /dev/null

cts.Monke 526    10032    2u   CHR        1,3           42 /dev/null

cts.Monke 526    10032    3w   CHR      10,58           55 /dev/log/main

cts.Monke 526    10032    4w   CHR      10,56           57 /dev/log/radio

cts.Monke 526    10032    5w   CHR      10,57           56 /dev/log/events



cts.Monke 526    10032   10u  unix 0xc9f610c0         2515 socket



cts.Monke 526    10032   20u  unix 0xc9fdb260         2519 socket



cts.Monke 526    10032   26r   REG       31,1  570420  284 /data/app/com.dseffects.MonkeyJump2-1.apk

cts.Monke 526    10032   27r   REG       31,1  570420  284 /data/app/com.dseffects.MonkeyJump2-1.apk

cts.Monke 526    10032   28r   REG       31,1  570420  284 /data/app/com.dseffects.MonkeyJump2-1.apk

cts.Monke 526    10032   29u  IPv4       2534          TCP *:5432 (LISTEN)

I have removed almost all the data, you can access a complete dump here http://alienvault-labs-garage.googlecode.com/svn/trunk/geinimi/lsof_geinimi.txt [no longer available]

As you can see the process has a listening socket on port 5432

Let’s start inspecting the code with the purpose of interacting with the listening socket. Before we begin looking at the code, this document comes handy to understand the Dalvik opcodes.

The thread that starts and manages the socket is defined in ./smali/com/dseffects/MonkeyJump2/jump2/j.smali

As we can see a ServerSocket object is created (from Lcom/dseffects/MonkeyJump2/jump2/e;->c), the accept() method is called and it returns a socket object to access java/io/InputStream and java/io/OutputStream to receive and send data. Let’s see how the ServerSocket is created:



Following the code we can see how the ServerSocket is created listening on port 0x15t 0x38t => 5432.

So, let’s continue analyzing the run() method of the previous j class.



Once the connection is established, the thread reads 256 bytes from the socket and builds a string, if the data received is “hi,are you online?” the program answer with he string “yes,I‘m online!”.

Then the client should send two bytes that the program will use later. Finally it sends 0xa 0x05 and closes the connection.



Based on the two bytes sent, the program does some checks.

It gets the string [const-string v0, “10.7”] from Lcom/dseffects/MonkeyJump2/jump2/e/k;->f() and split the string saving the integers to v0 and v1 respectively.

Then if the first byte we sent (v3) is greater than v1 (7) the program jumps to :cond_0.

Basically the program set a boolean variable on the service object that makes the service not to restart if the service stops. Finally it calls stopSelf() function to stop the service.

Let’s use this little script http://alienvault-labs-garage.googlecode.com/svn/trunk/geinimi/geinimi_comm.py [no longer available] to recreate the communication to restart the service.

jaime:tools jaimeblasco$ python geinimi_comm.py 5432

Received "yes,I'm online!"

Sending 'p'

Received '

Received ''
If we take a look at the android event log using "logcat -b events", we see that a destroy_service request has been sent
I/am_destroy_service(   61): [1140917416,com.dseffects.MonkeyJump2/.jump2.c.AndroidIME,301]

I have written a nmap script to remotely check the presence of the trojan. Download http://alienvault-labs-garage.googlecode.com/svn/trunk/geinimi/geinimi.nse [no longer available]

jaime:nmap-5.35DC1 jaimeblasco$ ./nmap -v --script=geinimi -p5432

Starting Nmap 5.35DC1 ( http://nmap.org ) at 2011-01-04 17:49 CET

NSE: Loaded 1 scripts for scanning.

Discovered open port 5432/tcp on

Completed Connect Scan at 17:49, 0.00s elapsed (1 total ports)

NSE: Script scanning

NSE: Starting runlevel 1 (of 1) scan.

Initiating NSE at 17:49

Completed NSE at 17:49, 5.00s elapsed

Nmap scan report for localhost (

Host is up (0.00013s latency).


5432/tcp open  postgresql

|_geinimi: Geinimi trojan present

My conclusion is that the authors used this service during development but maybe the service was made to detect compromised phones. What do you think?.

On the other hand Tim Strazzere from Lookout, has just told me a better theory: he discovered that the 10.7 string is the SDK version so this is a way to update the trojan version.

Share this with others

Featured resources



2024 Futures Report

Get price Free trial