├── LICENSE ├── README.md ├── Study ├── README.md └── Study │ ├── app_infomation(180).xlsx │ ├── project_lists(1,728).xlsx │ ├── setting_keyword_issues(11,656).xlsx │ ├── specific_APIs_list.xlsx │ ├── study_list(1,074).xlsx │ └── usage_of_settings(180).xlsx └── Tool ├── SetChecker ├── .vscode │ ├── launch.json │ └── settings.json ├── bin │ ├── FileParseJson.class │ ├── FlowAnalysis$1.class │ ├── FlowAnalysis$2.class │ ├── FlowAnalysis$AnalysisMode.class │ ├── FlowAnalysis.class │ ├── GeneratePattern.class │ ├── Main.class │ ├── PatternAnalysis$1.class │ ├── PatternAnalysis$2.class │ ├── PatternAnalysis$AnalysisMode.class │ ├── PatternAnalysis.class │ ├── PatternFlowSet.class │ ├── TestApp$1.class │ ├── TestApp.class │ └── Util.class ├── lib │ ├── AXMLPrinter2.jar │ ├── android-2.3.1.jar │ ├── android-4.1.1.4.jar │ ├── android-support-v4.jar │ ├── cltk.jar │ ├── json-20210307.jar │ ├── soot-infoflow-android-2.9.0.jar │ └── sootclasses-trunk-jar-with-dependencies.jar ├── resource │ ├── allmappings │ ├── allmappings.json │ ├── dangerouslist.txt │ ├── pattern.json │ ├── pattern1.json │ ├── pattern2.json │ └── publishedapimapping.json └── src │ ├── FileParseJson.java │ ├── FlowAnalysis.java │ ├── GeneratePattern.java │ ├── Main.java │ ├── PatternAnalysis.java │ ├── PatternFlowSet.java │ ├── TestApp.java │ └── Util.java └── SetDroid ├── Document ├── Android_logo.jpg ├── Android_robot.png ├── DroidBot_documentation.docx ├── DroidBot_documentation.pdf ├── Heartbeat.mp3 ├── droidbot_utg.png ├── droidmutator_test_report.png ├── intermission.mp3 ├── password.txt ├── sample.3gp ├── sample_3GPP.3gp.zip ├── sample_iPod.m4v ├── sample_mpeg4.mp4 └── sample_sorenson.mov ├── app.py ├── checker.py ├── device.py ├── event.py ├── executor.py ├── injector.py ├── policy.py ├── setdroid.py ├── start.py ├── state.py ├── style.html ├── utils.py └── view.py /README.md: -------------------------------------------------------------------------------- 1 | # SetDroid Prototype 2 | 3 | ## Publication 4 | 5 | [1] "[Understanding and Finding System Setting-Related Defects in Android Apps](https://tingsu.github.io/files/issta21-Setdroid.pdf)" by Jingling Sun, Ting Su, Junxin Li, Zhen Dong, Geguang Pu, Tao Xie and Zhendong Su. *The 30th ACM SIGSOFT International Symposium on Software Testing and Analysis* (ISSTA 2021) 6 | ``` 7 | @inproceedings{SetDroid, 8 | author = {Sun, Jingling and Su, Ting and Li, Junxin and Dong, Zhen and Pu, Geguang and Xie, Tao and Su, Zhendong}, 9 | title = {Understanding and Finding System Setting-Related Defects in Android Apps}, 10 | year = {2021}, 11 | doi = {10.1145/3460319.3464806}, 12 | booktitle = {Proceedings of the 30th ACM SIGSOFT International Symposium on Software Testing and Analysis}, 13 | pages = {204–215}, 14 | numpages = {12}, 15 | keywords = {Testing, Setting, Empirical study, Android}, 16 | location = {Virtual, Denmark}, 17 | series = {ISSTA 2021} 18 | } 19 | ``` 20 | 21 | [2] "[Characterizing and Finding System Setting-Related Defects in Android Apps](https://tingsu.github.io/files/TSE23-SetDroid.pdf)" by Jingling Sun, Ting Su, Kai Liu, Chao Peng, Zhao Zhang, Geguang Pu, Tao Xie, Zhendong Su. *IEEE Transactions on Software Engineering* (TSE 2023) 22 | ``` 23 | @article{SetDroid2, 24 | author = {Jingling Sun and 25 | Ting Su and 26 | Kai Liu and 27 | Chao Peng and 28 | Zhao Zhang and 29 | Geguang Pu and 30 | Tao Xie and 31 | Zhendong Su}, 32 | title = {Characterizing and Finding System Setting-Related Defects in Android 33 | Apps}, 34 | journal = {{IEEE} Trans. Software Eng.}, 35 | volume = {49}, 36 | number = {4}, 37 | pages = {2941--2963}, 38 | year = {2023}, 39 | doi = {10.1109/TSE.2023.3236449} 40 | } 41 | ``` 42 | 43 | *You can find more about our work on testing/analyzing Android apps at this [website](https://mobile-app-analysis.github.io/)*. 44 | 45 | ## Getting Started 46 | 47 | ### Running SetDroid via Virtual Machine 48 | 49 | #### Requirements 50 | 51 | * You need to enable virtualization technology in your computer's BIOS, see [this link](https://stackoverflow.com/questions/35456063/enable-intel-vt-x-intel-virtualization-technology-intel-vt-x) for how to enable virtualization technology in the computer. Some computers have turned on virtualization by default. 52 | * Your computer needs at least 16G of memory, and at least 40G of storage. 53 | * VirtualBox: we built our artifact by using version 6.1.20. 54 | * Download the zip file from [this link](https://1drv.ms/u/s!AinXMMnLw-UDjTNrboNh0fTfss6G?e=v0Va1t), and extract it. 55 | 56 | #### Setting up ([video tutorial](https://1drv.ms/u/s!AinXMMnLw-UDjgCWoQclrXqb6-xE?e=n6eiDy)) 57 | 58 | * Open VirtualBox, click "File", click "Import Appliance", then select the file named "SetDroid.ova" from the extracted contents (this step will take about five to ten minutes to complete). 59 | * After the import is completed, you should see "vm" as one of the listed VMs in your VirtualBox. 60 | * Click "Settings", click "System", click "Processor", and check "Enable Nested VT-x/AMD-V" 61 | * If you want to run the virtual machine more smoothly, you can click "Setting", click "Display", and then increase the value of "Video Memory" according to your situation. 62 | * Run the virtual machine. The username and the password are both "setdroid". 63 | * If you could not run the VM with "Nested VT-x/AMD-V" option enabled in VirtualBox, it may be because that you did not disable the Hyper-V option. You can disable Hyper-V launch temporarily. See [this link](https://forums.virtualbox.org/viewtopic.php?f=1&t=62339) for more information about that. 64 | * If you want to copy & paste from host to "vm", you can open the terminal and execute the following command (you may not need to do so, because the virtual machine already contains all the files needed to run the tool): 65 | ``` 66 | sudo apt-get install virtualbox-guest-additions-iso 67 | sudo mkdir /media/temp 68 | sudo mount /usr/share/virtualbox/VBoxGuestAdditions.iso /media/temp 69 | ``` 70 | 71 | #### Run ([video tutorial](https://1drv.ms/u/s!AinXMMnLw-UDjgCWoQclrXqb6-xE?e=n6eiDy)) 72 | 73 | * Open the terminal and execute the following command: 74 | ``` 75 | /home/setdroid/Android/Sdk/emulator/emulator -avd Android8.0 -read-only -port 5554 & 76 | ``` 77 | * Wait for the first Android emulator to start. After the emulator is successfully started, return to the command-line interface, press enter, and then execute the following command: 78 | ``` 79 | /home/setdroid/Android/Sdk/emulator/emulator -avd Android8.0 -read-only -port 5556 & 80 | ``` 81 | * Wait for the second Android emulator to start. After the emulator is successfully started, return to the command-line interface, press enter, and then execute the following command: 82 | ``` 83 | cd /home/setdroid/SetDroid/Tool 84 | ``` 85 | * Then execute the following command (this step will take about five to ten minutes to complete): 86 | ``` 87 | python3 start.py -app_path /home/setdroid/SetDroid/App/a2dp.Vol.apk -append_device emulator-5554 -append_device emulator-5556 -android_system emulator8 -append_strategy display_immediate_1 -testcase_count 1 -choice 0 -event_num 50 88 | ``` 89 | * At this point, SetDroid will start to run a round of example policy (Oracle checking rule I -immediate -display -1) on the example app ( A2DP Volume), which contains 50 events. 90 | * The target app can be modified by the configuration parameter ```-app_path```. The number of runs can be modified by the configuration parameter ```-testcase_count```. The number of events contained in each test can be modified by the configuration parameter ```-event_num```. Setting change strategy can be changed through the configuration parameter ```-append_strategy```. You can also add more strategies to make them be executed in sequence. 91 | * For example, the following command represents the sequential execution of two strategies (Oracle checking rule I - lazy - permission) and (Oracle checking rule II - language) on Amaze. Each strategy is executed 10 times, and each test contains 100 events (this command will take about one to two hours to complete, and you can interrupt the command through ```Ctrl-C``` at any time): 92 | ``` 93 | python3 start.py -app_path /home/setdroid/SetDroid/App/com.amaze.filemanager.apk -append_device emulator-5554 -append_device emulator-5556 -android_system emulator8 -append_strategy permssion_lazy_1 -append_strategy language -testcase_count 10 -event_num 100 94 | ``` 95 | 96 | ### Building and Running SetDroid From Scratch 97 | 98 | #### Requirements 99 | 100 | - Download all the files from [this link](https://1drv.ms/u/s!AinXMMnLw-UDjTX9NJsPSWALFx5p?e=nwW830) 101 | - Download all the apps from [this link](https://1drv.ms/u/s!AinXMMnLw-UDjWBJXwgywNLad3T9?e=S1mMXt) 102 | - Android SDK: API 26+ 103 | - Python 3.8 104 | - We use some libraries (uiautomator2, androguard, cv2, langid, numpy) provided by python, you can add them as prompted, for example: 105 | ``` 106 | pip3 install langid 107 | ``` 108 | 109 | #### Setting up 110 | 111 | You can create an emulator before running SetDroid. See [this link](https://stackoverflow.com/questions/43275238/how-to-set-system-images-path-when-creating-an-android-avd) for how to create avd using [avdmanager](https://developer.android.com/studio/command-line/avdmanager). 112 | The following sample command will help you create an emulator, which will help you to start using SetDroid quickly: 113 | ``` 114 | sdkmanager "system-images;android-26;google_apis;x86" 115 | avdmanager create avd --force --name Android8.0 --package 'system-images;android-26;google_apis;x86' --abi google_apis/x86 --sdcard 512M --device "pixel_xl" 116 | ``` 117 | Next, you can start two identical emulators and assign their port numbers with the following commands: 118 | ``` 119 | emulator -avd Android8.0 -read-only -port 5554 120 | emulator -avd Android8.0 -read-only -port 5556 121 | ``` 122 | #### Run 123 | If you have downloaded our project and configured the environment, you only need to enter ```download_path/tool``` to execute our sample app with the following command: 124 | ``` 125 | python3 start.py -app_path /home/setdroid/SetDroid/App/a2dp.Vol.apk -append_device emulator-5554 -append_device emulator-5556 -android_system emulator8 -append_strategy display_immediate_1 -testcase_count 1 126 | ``` 127 | SetDroid provides several ways to test android apps by command lines. You need to view configuration help through the following commands and change them. 128 | ``` 129 | python3 start.py --help 130 | ``` 131 | 132 | ## Detailed Description 133 | 134 | ### All Optional Parameters of SetDroid 135 | 136 | * ```-pro_click``` The proportion of click events, which is 45% by default. 137 | * ```-pro_longclick``` The proportion of long-press events, which is 25% by default. 138 | * ```-pro_scroll``` The proportion of scroll events, which is 5% by default. 139 | * ```-pro_home``` The proportion of click home button events, which is 0% by default. 140 | * ```-pro_edit``` The proportion of edit events, which is 15% by default. 141 | * ```-pro_naturalscreen``` The proportion of rotating to natural events, which is 1% by default. 142 | * ```-pro_leftscreen``` The proportion of rotating to left events, which is 8% by default. 143 | * ```-pro_back``` The proportion of click back button events, which is 1% by default. 144 | * ```-pro_splitscreen``` The proportion of split-screen events, which is 0% by default. 145 | * ```-app_path``` the APK address of the app you want to test. 146 | * ```-append_device``` The serial numbers of devices used in the test, which can be obtained by executing "adb devices" in the terminal. 147 | * ```-android_system``` The Android system of the test device, At present, only Android 8.0 system is supported. 148 | * ```-root_path``` The storage path of the output file. 149 | * ```-resource_path``` The path of the resource file that you want to import into the test devices in advance. 150 | * ```-testcase_count``` The number of rounds that you want to test for each strategy. 151 | * ```-event_num``` The number of events in per round of test. 152 | * ```-setting_random_denominator``` Used to adjust the frequency of setting change event insertion. 153 | * ```-append_strategy``` The strategies that you want to use (multiple test strategies can be executed in sequence), currently, the supported strategies are as follows, corresponding to the 14 strategies listed in Table 5 of the paper. 154 | 155 | |Strategy name|Setting|Oracle rule|Injection strategy|Pair of events for setting changes| 156 | |---|---|---|---|---| 157 | |network_immediate_1|Network| I| Immediate |⟨turn on airplane, turn off airplane⟩| 158 | |network_lazy_1 |Network| I |Lazy| ⟨turn on airplane, turn off airplane⟩| 159 | |network_lazy_2 |Network| I |Lazy| ⟨switch to mobile data, switch to Wi-Fi⟩| 160 | |location_lazy_1| Location| I| Lazy| ⟨turn off location, turn on location⟩| 161 | |location_lazy_2| Location| I| Lazy| ⟨switch to "device only", switch to "high accuracy"⟩| 162 | |sound_lazy_1| Sound| I| Lazy| ⟨turn on "do not disturb", turn off "do not disturb"⟩| 163 | |battery_immediate_1| Battery| I| Immediate| ⟨turn on the power saving mode, add the app into the whitelist⟩| 164 | |battery_lazy_1| Battery| I| Lazy| ⟨turn on the power saving mode, turn off the power saving mode⟩| 165 | |display_immediate_1| Display| I| Immediate| ⟨switch to landscape, switch to portrait⟩| 166 | |display_immediate_2| Display| I| Immediate| ⟨turn on multi-window, turn off multi-window⟩| 167 | |permssion_lazy_1| Permission| I| Lazy| ⟨turn off permission, turn on permission⟩| 168 | |developer_lazy_1| Developer| I| Lazy| ⟨turn on "Don’t keep activities", turn off "Don’t keep activities"⟩| 169 | |language| Language| II| -| ⟨change system language, -⟩| 170 | |time| Time| II| -| ⟨change hour format, -⟩| 171 | 172 | ### Description of Output Files ([video tutorial](https://1drv.ms/u/s!AinXMMnLw-UDjgCWoQclrXqb6-xE?e=n6eiDy)) 173 | 174 | * The output path of the tool is in ```/home/setdroid/SetDroid/Root```. 175 | * The result files of each app are classified and stored in ```/home/setdroid/SetDroid/Root```. 176 | * Open the folder of an app, and you will see the result files of each strategy for this app are stored by category. 177 | * Open the folder corresponding to a strategy, and you will see an ```error_realtime.txt``` file, a ```wrong_realtime.txt``` file, and many numbered folders correspond to each round of test results. 178 | * Open a numbered folder, and you can see a ```read_trace.txt``` file, a ```trace.txt``` file, an ```i_trace.html``` file, and a folder named ```screen```. 179 | * Open the ```screen``` folder, and you can see the screenshot of each step and the corresponding interface layout information file. 180 | * Next, I will introduce the content and use of each file. 181 | 182 | #### error_realtime.txt 183 | 184 | This file records the sequences that trigger the setting defects, which start with ```Start::x::run_count::y``` (x means the x-th error and Y means the error was captured during the y-th round of execution), and end with ```End::``` 185 | 186 | #### wrong_realtime.txt 187 | 188 | This file records the sequences that trigger the suspected setting defects. 189 | 190 | #### read_trace.txt 191 | 192 | This file records the execution sequence of SetDroid, which is easy for SetDroid users to read. 193 | 194 | #### trace.txt 195 | 196 | This file records the execution sequence of SetDroid, which can be read and replayed by SetDroid. 197 | 198 | #### i_trace.html 199 | 200 | This file records the sequence of screenshots after each step, which is arranged horizontally. The events executed at each step are marked on the screenshot. After opening the file in the browser, there is a drag bar at the bottom, which can drag horizontally to view the whole sequence. When the error is captured, the screenshot is marked with a red frame. When the two interfaces are different, the screen capture is marked with a yellow frame. 201 | 202 | ### Tool Extension 203 | 204 | If someone wants to extend the artifact, they can modify it in the following position of the tool. 205 | 206 | #### Add settings change strategy 207 | 208 | Add a new setting change function in injector.py, and add calls to it to ```change_setting_before_run``` or ```inject_setting_during_run``` as needed. 209 | 210 | #### Add seed test generation policy 211 | 212 | Add a new exploration class according to ```RandomPolicy``` class in policy.py, and inherit the ```Policy``` class 213 | 214 | #### Add a new check condition 215 | 216 | Add a new check function in check.py and call it in the corresponding position in the ```executor.py``` 217 | 218 | ### Main Maintainers 219 | 220 | * [Jingling Sun](https://jinglingsun.github.io/) 221 | * [Ting Su](http://tingsu.github.io/) 222 | 223 | -------------------------------------------------------------------------------- /Study/README.md: -------------------------------------------------------------------------------- 1 | ## Study Dataset 2 | 3 | ### project_lists.xlsx 4 | 5 | This file contains all the Android projects officially released on Google Play and F-Droid that we obtained through GitHub's REST API. We listed their repo names, the number of closed issues, the number of open issues, and the number of stars on GitHub. We keep the apps with more than 200 issues in this list as study objects, a total of 180 apps. 6 | 7 | ### app_infomation.xlsx (corresponding to Figure 2 in our paper) 8 | 9 | This file contains all the information (star numbers, issue numbers, installations, and categories) of our study objects, corresponding to the data in Figure 2 in the paper. 10 | 11 | ### setting_keyword_issues.xlsx 12 | 13 | This file contains all issues of 180 apps obtained through keyword filtering. We listed their repo name, URL, open time, closed time, and setting keywords mentioned in the issue. 14 | 15 | ### study_list.xlsx (corresponding to Table 3 in our paper) 16 | 17 | This file contains 1,074 setting issues of 180 apps that we obtained through manual inspection. For each issue, we listed its URL, consequence, whether it was closed, whether it was repaired, the length of the reproduce steps reported by the reporter, the setting that caused the issue, and the root cause of the issue. 18 | 19 | ### specific_APIs_list.xlsx 20 | 21 | This file lists the specific APIs that we used when investigating the usage of settings in the apps. We listed their corresponding setting categories, the classes they belong to, and the forms used in the static analysis. 22 | 23 | ### usage_of_settings.xlsx (corresponding to Table 2 in our paper) 24 | 25 | This file lists the APIs related to the setting categories used by each app. For each app, we counted the usage of six settings. The number 1 means that at least one API corresponding to this setting category has been detected in the code of the app. -------------------------------------------------------------------------------- /Study/Study/app_infomation(180).xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Study/Study/app_infomation(180).xlsx -------------------------------------------------------------------------------- /Study/Study/project_lists(1,728).xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Study/Study/project_lists(1,728).xlsx -------------------------------------------------------------------------------- /Study/Study/setting_keyword_issues(11,656).xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Study/Study/setting_keyword_issues(11,656).xlsx -------------------------------------------------------------------------------- /Study/Study/specific_APIs_list.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Study/Study/specific_APIs_list.xlsx -------------------------------------------------------------------------------- /Study/Study/study_list(1,074).xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Study/Study/study_list(1,074).xlsx -------------------------------------------------------------------------------- /Study/Study/usage_of_settings(180).xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Study/Study/usage_of_settings(180).xlsx -------------------------------------------------------------------------------- /Tool/SetChecker/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "java", 9 | "name": "Launch GeneratePattern", 10 | "request": "launch", 11 | "mainClass": "GeneratePattern", 12 | "projectName": "SettingPattern_51ca20fa" 13 | }, 14 | { 15 | "type": "java", 16 | "name": "Launch FileParseJson", 17 | "request": "launch", 18 | "mainClass": "FileParseJson", 19 | "projectName": "SettingPattern_51ca20fa" 20 | }, 21 | { 22 | "type": "java", 23 | "name": "Launch Current File", 24 | "request": "launch", 25 | "mainClass": "${file}" 26 | }, 27 | { 28 | "projectName": "SettingPattern_51ca20fa", 29 | "type": "java", 30 | "name": "Launch TestApp", 31 | "request": "launch", 32 | "mainClass": "TestApp", 33 | "args": [ 34 | "-d", 35 | "../output/sootOutput", 36 | "-process-dir", 37 | "target", 38 | "-w", 39 | "-android-jars", 40 | "android-platforms", 41 | "-i", 42 | "android.media.AudioManager", 43 | "-i", 44 | "android.location.LocationManager", 45 | "-i", 46 | "android.content.Context", 47 | "-i", 48 | "android.media.RingtoneManager", 49 | "-i", 50 | "android.telephony.SmsManager", 51 | "-i", 52 | "android.bluetooth.BluetoothAdapter", 53 | "-i", 54 | "android.webkit.WebViewClient", 55 | "-allow-phantom-refs", 56 | "-soot-class-path", 57 | ".;C:/Program Files/Java/jdk1.8.0_181/jre/lib/rt.jar;C:/Program Files/Java/jdk1.8.0_181/jre/lib/jce.jar;lib/android-support-v4.jar;lib/android-4.1.1.4.jar;lib/android-2.3.1.jar", 58 | "-process-multiple-dex" 59 | ] 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /Tool/SetChecker/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.project.sourcePaths": ["src"], 3 | "java.project.outputPath": "bin", 4 | "java.project.referencedLibraries": [ 5 | "lib/**/*.jar" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /Tool/SetChecker/bin/FileParseJson.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/FileParseJson.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/FlowAnalysis$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/FlowAnalysis$1.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/FlowAnalysis$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/FlowAnalysis$2.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/FlowAnalysis$AnalysisMode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/FlowAnalysis$AnalysisMode.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/FlowAnalysis.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/FlowAnalysis.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/GeneratePattern.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/GeneratePattern.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/Main.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/Main.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/PatternAnalysis$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/PatternAnalysis$1.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/PatternAnalysis$2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/PatternAnalysis$2.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/PatternAnalysis$AnalysisMode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/PatternAnalysis$AnalysisMode.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/PatternAnalysis.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/PatternAnalysis.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/PatternFlowSet.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/PatternFlowSet.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/TestApp$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/TestApp$1.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/TestApp.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/TestApp.class -------------------------------------------------------------------------------- /Tool/SetChecker/bin/Util.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/bin/Util.class -------------------------------------------------------------------------------- /Tool/SetChecker/lib/AXMLPrinter2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/AXMLPrinter2.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/android-2.3.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/android-2.3.1.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/android-4.1.1.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/android-4.1.1.4.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/android-support-v4.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/cltk.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/cltk.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/json-20210307.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/json-20210307.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/soot-infoflow-android-2.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/soot-infoflow-android-2.9.0.jar -------------------------------------------------------------------------------- /Tool/SetChecker/lib/sootclasses-trunk-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetChecker/lib/sootclasses-trunk-jar-with-dependencies.jar -------------------------------------------------------------------------------- /Tool/SetChecker/resource/dangerouslist.txt: -------------------------------------------------------------------------------- 1 | ACCEPT_HANDOVER 2 | ACCESS_BACKGROUND_LOCATION 3 | ACCESS_COARSE_LOCATION 4 | ACCESS_FINE_LOCATION 5 | ACCESS_MEDIA_LOCATION 6 | ACTIVITY_RECOGNITION 7 | ADD_VOICEMAIL 8 | ANSWER_PHONE_CALLS 9 | BLUETOOTH_ADVERTISE 10 | BLUETOOTH_CONNECT 11 | BLUETOOTH_SCAN 12 | BODY_SENSORS 13 | CALL_PHONE 14 | CAMERA 15 | GET_ACCOUNTS 16 | PROCESS_OUTGOING_CALLS 17 | READ_CALENDAR 18 | READ_CALL_LOG 19 | READ_CONTACTS 20 | READ_EXTERNAL_STORAGE 21 | READ_PHONE_NUMBERS 22 | READ_PHONE_STATE 23 | READ_SMS 24 | RECEIVE_MMS 25 | RECEIVE_SMS 26 | RECEIVE_WAP_PUSH 27 | RECORD_AUDIO 28 | SEND_SMS 29 | USE_SIP 30 | UWB_RANGING 31 | WRITE_CALENDAR 32 | WRITE_CALL_LOG 33 | WRITE_CONTACTS 34 | WRITE_EXTERNAL_STORAGE -------------------------------------------------------------------------------- /Tool/SetChecker/resource/pattern.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "pattern": [ 4 | { 5 | "mainapi": [ 6 | { 7 | "classname": "", 8 | "methodname": "checkPermission" 9 | }, 10 | { 11 | "classname": "", 12 | "methodname": "checkSelfPermission" 13 | } 14 | ], 15 | "controlcheckbefore": [ 16 | ], 17 | "controlcheckafter": [ 18 | { 19 | "classname": "", 20 | "methodname": "requestPermission" 21 | }, 22 | { 23 | "classname": "", 24 | "methodname": "shouldShowRequestPermissionRationale" 25 | } 26 | ], 27 | "flowgen": [ 28 | ], 29 | "flowkill": [ 30 | ], 31 | "flowfind": [ 32 | ], 33 | "name":"checkSelfPermission->requestPermissions" 34 | } 35 | 36 | 37 | ] 38 | 39 | } -------------------------------------------------------------------------------- /Tool/SetChecker/resource/pattern1.json: -------------------------------------------------------------------------------- 1 | { 2 | "pattern": [ 3 | { 4 | "mainapi": [ 5 | { 6 | "classname": "android.location.Location", 7 | "methodname": "getLastKnownLocation" 8 | } 9 | ], 10 | "controlcheckbefore": [ 11 | { 12 | "classname": "android.location.LocationManager", 13 | "methodname": "isProviderEnabled" 14 | } 15 | ], 16 | "controlcheckafter": [ 17 | ], 18 | "flowgen": [ 19 | { 20 | "classname": "android.location.LocationManager", 21 | "methodname": "getLastKnownLocation" 22 | } 23 | ], 24 | "flowkill": [ 25 | { 26 | "classname": "", 27 | "methodname":"= null" 28 | }, 29 | { 30 | "classname": "", 31 | "methodname":"requireNonNull" 32 | } 33 | ], 34 | "flowfind": [ 35 | { 36 | "classname": "android.location.Location", 37 | "methodname": "getLatitude" 38 | }, 39 | { 40 | "classname": "android.location.Location", 41 | "methodname": "getLongitude" 42 | } 43 | ], 44 | "name":"isProviderEnabled->getLastKnownLocation->getLatitude" 45 | }, 46 | { 47 | "mainapi": [ 48 | { 49 | "classname": "android.media.AudioManager", 50 | "methodname": "setStreamVolume" 51 | }, 52 | { 53 | "classname": "android.media.AudioManager", 54 | "methodname": "setRingerMode" 55 | } 56 | ], 57 | "controlcheckbefore": [ 58 | { 59 | "classname": "android.permission", 60 | "methodname": "ACCESS_NOTIFICATION_POLICY" 61 | } 62 | ], 63 | "controlcheckafter": [ 64 | ], 65 | "flowgen": [ 66 | ], 67 | "flowkill": [ 68 | ], 69 | "flowfind": [ 70 | ], 71 | "name":"ACCESS_NOTIFICATION_POLICY->AudioManager" 72 | }, 73 | { 74 | "mainapi": [ 75 | ], 76 | "controlcheckbefore": [ 77 | ], 78 | "controlcheckafter": [ 79 | ], 80 | "flowgen": [ 81 | { 82 | "classname": "android.content.pm.PackageManager", 83 | "methodname": "getLaunchIntentForPackage" 84 | } 85 | ], 86 | "flowkill": [ 87 | { 88 | "classname": "", 89 | "methodname":"= null" 90 | }, 91 | { 92 | "classname": "", 93 | "methodname":"requireNonNull" 94 | } 95 | ], 96 | "flowfind": [ 97 | { 98 | "classname": "android.content.Intent", 99 | "methodname": "startActivity" 100 | } 101 | ], 102 | "name":"getLaunchIntentForPackage->startActivity" 103 | }, 104 | { 105 | "mainapi": [ 106 | { 107 | "classname": "android.location.LocationManager", 108 | "methodname": "requestLocationUpdates" 109 | }, 110 | { 111 | "classname": "android.location.Location", 112 | "methodname": "getLastKnownLocation" 113 | } 114 | ], 115 | "controlcheckbefore": [ 116 | { 117 | "classname": "", 118 | "methodname": "ACCESS_COARSE_LOCATION" 119 | }, 120 | { 121 | "classname": "", 122 | "methodname": "ACCESS_FINE_LOCATION" 123 | } 124 | ], 125 | "controlcheckafter": [ 126 | ], 127 | "flowgen": [ 128 | ], 129 | "flowkill": [ 130 | ], 131 | "flowfind": [ 132 | ], 133 | "name":"permission->requestLocationUpdates" 134 | }, 135 | { 136 | "mainapi": [ 137 | ], 138 | "controlcheckbefore": [ 139 | ], 140 | "controlcheckafter": [ 141 | ], 142 | "flowgen": [ 143 | { 144 | "classname": "android.media.RingtoneManager", 145 | "methodname": "getRingtone" 146 | } 147 | ], 148 | "flowkill": [ 149 | { 150 | "classname": "", 151 | "methodname": "null" 152 | } 153 | ], 154 | "flowfind": [ 155 | { 156 | "classname": "", 157 | "methodname": "" 158 | } 159 | ], 160 | "name":"READ_EXTERNAL_STORAGE->getRingtone" 161 | }, 162 | { 163 | "mainapi": [ 164 | { 165 | "classname": "android.telephony.SmsManager", 166 | "methodname": "sendTextMessage" 167 | } 168 | ], 169 | "controlcheckbefore": [ 170 | { 171 | "classname": "", 172 | "methodname": "SEND_SMS" 173 | } 174 | ], 175 | "controlcheckafter": [ 176 | ], 177 | "flowgen": [ 178 | ], 179 | "flowkill": [ 180 | ], 181 | "flowfind": [ 182 | ], 183 | "name":"SEND_SMS->sendTextMessage" 184 | }, 185 | { 186 | "mainapi": [ 187 | { 188 | "classname": "android.bluetooth.BluetoothAdapter", 189 | "methodname": "" 190 | } 191 | ], 192 | "controlcheckbefore": [ 193 | { 194 | "classname": "", 195 | "methodname": "BLUETOOTH" 196 | } 197 | ], 198 | "controlcheckafter": [ 199 | ], 200 | "flowgen": [ 201 | { 202 | "classname": "android.bluetooth.BluetoothAdapter", 203 | "methodname": "getDefaultAdapter" 204 | } 205 | ], 206 | "flowkill": [ 207 | { 208 | "classname": "android.bluetooth.BluetoothAdapter", 209 | "methodname": "isEnabled" 210 | }, 211 | { 212 | "classname": "", 213 | "methodname": "= null" 214 | } 215 | ], 216 | "flowfind": [ 217 | { 218 | "classname": "android.bluetooth.BluetoothAdapter", 219 | "methodname": "getBondedDevices" 220 | }, 221 | { 222 | "classname": "android.bluetooth.BluetoothAdapter", 223 | "methodname": "getRemoteDevice" 224 | } 225 | ], 226 | "name":"BLUETOOTH_LIST_KEY->getDefaultAdapter" 227 | }, 228 | { 229 | "mainapi": [ 230 | ], 231 | "controlcheckbefore": [ 232 | ], 233 | "controlcheckafter": [ 234 | ], 235 | "flowgen": [ 236 | { 237 | "classname": "", 238 | "methodname": "TELEPHONY_SERVICE" 239 | } 240 | ], 241 | "flowkill": [ 242 | { 243 | "classname": "", 244 | "methodname":"= null" 245 | }, 246 | { 247 | "classname": "", 248 | "methodname":"requireNonNull" 249 | } 250 | ], 251 | "flowfind": [ 252 | { 253 | "classname": "android.telephony.TelephonyManager", 254 | "methodname": "getCellLocation" 255 | } 256 | ], 257 | "name":"TELEPHONY_SERVICE->getCellLocation" 258 | }, 259 | { 260 | "mainapi": [ 261 | { 262 | "classname": "android.webkit.WebViewClient", 263 | "methodname": "onReceivedError" 264 | } 265 | ], 266 | "controlcheckbefore": [ 267 | { 268 | "classname": "Build.VERSION", 269 | "methodname": "" 270 | } 271 | ], 272 | "controlcheckafter": [ 273 | ], 274 | "flowgen": [ 275 | ], 276 | "flowkill": [ 277 | ], 278 | "flowfind": [ 279 | ], 280 | "name":"Build.VERSION->onReceivedError" 281 | }, 282 | { 283 | "mainapi": [ 284 | ], 285 | "controlcheckbefore": [ 286 | ], 287 | "controlcheckafter": [ 288 | ], 289 | "flowgen": [ 290 | { 291 | "classname": "android.media.MediaPlayer", 292 | "methodname": "new" 293 | } 294 | ], 295 | "flowkill": [ 296 | { 297 | "classname": "", 298 | "methodname":"= null" 299 | }, 300 | { 301 | "classname": "", 302 | "methodname":"requireNonNull" 303 | } 304 | ], 305 | "flowfind": [ 306 | { 307 | "classname": "android.media.MediaPlayer", 308 | "methodname": "start" 309 | } 310 | ], 311 | "name":"MediaPlayer->start" 312 | } 313 | 314 | ] 315 | 316 | } -------------------------------------------------------------------------------- /Tool/SetChecker/resource/pattern2.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "pattern": [ 4 | { 5 | "mainapi": [ 6 | { 7 | "classname": "", 8 | "methodname": "checkPermission" 9 | }, 10 | { 11 | "classname": "", 12 | "methodname": "checkSelfPermission" 13 | } 14 | ], 15 | "controlcheckbefore": [ 16 | ], 17 | "controlcheckafter": [ 18 | { 19 | "classname": "", 20 | "methodname": "requestPermission" 21 | }, 22 | { 23 | "classname": "", 24 | "methodname": "shouldShowRequestPermissionRationale" 25 | } 26 | ], 27 | "flowgen": [ 28 | ], 29 | "flowkill": [ 30 | ], 31 | "flowfind": [ 32 | ], 33 | "name":"checkSelfPermission->requestPermissions" 34 | }, 35 | { 36 | "mainapi": [ 37 | { 38 | "classname": "android.location.LocationManager", 39 | "methodname": "requestLocationUpdates" 40 | }, 41 | { 42 | "classname": "android.location.LocationManager", 43 | "methodname": "getLastKnownLocation" 44 | } 45 | ], 46 | "controlcheckbefore": [ 47 | { 48 | "classname": "android.location.LocationManager", 49 | "methodname": "isProviderEnabled" 50 | } 51 | ], 52 | "controlcheckafter": [ 53 | ], 54 | "flowgen": [ 55 | ], 56 | "flowkill": [ 57 | ], 58 | "flowfind": [ 59 | ], 60 | "name":"isProviderEnabled->getLastKnownLocation" 61 | }, 62 | { 63 | "mainapi": [ 64 | ], 65 | "controlcheckbefore": [ 66 | ], 67 | "controlcheckafter": [ 68 | ], 69 | "flowgen": [ 70 | { 71 | "classname": "android.content.Context", 72 | "methodname": "new" 73 | } 74 | ], 75 | "flowkill": [ 76 | { 77 | "classname": "", 78 | "methodname": "null" 79 | } 80 | ], 81 | "flowfind": [ 82 | { 83 | "classname": "", 84 | "methodname": "getPackageName" 85 | } 86 | ], 87 | "name":"Context->getPackageName" 88 | }, 89 | { 90 | "mainapi": [ 91 | { 92 | "classname": "java.net.HttpURLConnection", 93 | "methodname": "setRequestMethod" 94 | } 95 | ], 96 | "controlcheckbefore": [ 97 | { 98 | "classname": "android.permission", 99 | "methodname": "ACCESS_NETWORK_STATE" 100 | }, 101 | { 102 | "classname": "android.permission", 103 | "methodname": "INTERNET" 104 | } 105 | ], 106 | "controlcheckafter": [ 107 | ], 108 | "flowgen": [ 109 | ], 110 | "flowkill": [ 111 | ], 112 | "flowfind": [ 113 | ], 114 | "name":"android.permission->setRequestMethod" 115 | }, 116 | { 117 | "mainapi": [ 118 | { 119 | "classname": "java.net.HttpURLConnection", 120 | "methodname": "setRequestMethod" 121 | } 122 | ], 123 | "controlcheckbefore": [ 124 | { 125 | "classname": "ConnectivityManager", 126 | "methodname": "getActiveNetworkInfo" 127 | }, 128 | { 129 | "classname": "ConnectivityManager", 130 | "methodname": "requestNetwork" 131 | }, 132 | { 133 | "classname": "", 134 | "methodname": "isActiveNetworkMetered" 135 | } 136 | ], 137 | "controlcheckafter": [ 138 | ], 139 | "flowgen": [ 140 | ], 141 | "flowkill": [ 142 | ], 143 | "flowfind": [ 144 | ], 145 | "name":"checknetwork->usenetwork" 146 | } 147 | 148 | 149 | ] 150 | 151 | } -------------------------------------------------------------------------------- /Tool/SetChecker/src/FileParseJson.java: -------------------------------------------------------------------------------- 1 | import org.json.JSONArray; 2 | import org.json.JSONException; 3 | import org.json.JSONObject; 4 | 5 | public class FileParseJson { 6 | public static void main(String[] args) { 7 | try { 8 | String JsonContext = new Util().ReadFile("C:\\myWork\\SettingPattern\\target\\apkinfo.json"); 9 | JSONObject rootObject = new JSONObject(JsonContext); // Parse the JSON to a JSONObject 10 | JSONArray patterns = rootObject.getJSONArray("pattern1"); // Get all JSONArray rows 11 | 12 | for(int j=0; j < patterns.length(); j++) { // Iterate each element in the elements array 13 | JSONObject pattern = patterns.getJSONObject(j); // Get the element object 14 | JSONArray mainapis = pattern.getJSONArray("mainapi"); // Get duration sub object 15 | for(int i=0; i < mainapis.length(); i++) { 16 | JSONObject mainapi = mainapis.getJSONObject(i); 17 | System.out.println("Classname: " + mainapi.getString("classname")); 18 | System.out.println("Methodname: " + mainapi.getString("methodname")); 19 | } 20 | 21 | 22 | // JSONObject distance = element.getJSONObject("distance"); // Get distance sub object 23 | 24 | // Print int value 25 | // System.out.println("Distance: " + distance.getInt("value")); // Print int value 26 | } 27 | } catch (JSONException e) { 28 | // JSON Parsing error 29 | e.printStackTrace(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Tool/SetChecker/src/FlowAnalysis.java: -------------------------------------------------------------------------------- 1 | 2 | import soot.Value; 3 | import soot.Unit; 4 | import soot.jimple.*; 5 | import soot.toolkits.graph.DirectedGraph; 6 | import soot.toolkits.scalar.ForwardFlowAnalysis; 7 | import org.json.JSONArray; 8 | import org.json.JSONException; 9 | import org.json.JSONObject; 10 | 11 | /** 12 | * @program: MySootScript 13 | * @description: 14 | * @author: Dr.Navid 15 | * @create: 2021-05-10 15:11 16 | **/ 17 | 18 | 19 | public class FlowAnalysis extends ForwardFlowAnalysis { 20 | 21 | 22 | public enum AnalysisMode {MUST, MAY_P, MAY_O} 23 | 24 | public AnalysisMode analysisMode; 25 | PatternFlowSet newset = new PatternFlowSet(); 26 | JSONArray flowgens ,flowkills; 27 | 28 | 29 | 30 | public FlowAnalysis(DirectedGraph graph, PatternFlowSet newset, JSONArray flowgens, JSONArray flowkills) { 31 | super(graph); 32 | this.analysisMode = AnalysisMode.MUST; 33 | this.newset = newset; 34 | this.flowgens = flowgens; 35 | this.flowkills = flowkills; 36 | 37 | doAnalysis(); 38 | } 39 | 40 | @Override 41 | protected PatternFlowSet entryInitialFlow() { 42 | return this.newset; 43 | } 44 | 45 | @Override 46 | protected void flowThrough(PatternFlowSet inSet, Unit unit, PatternFlowSet outSet) { 47 | inSet.copy(outSet); 48 | kill(inSet, unit, outSet); 49 | gen(inSet, unit, outSet); 50 | } 51 | 52 | @Override 53 | protected void merge(PatternFlowSet inSet1, PatternFlowSet inSet2, PatternFlowSet outSet) { 54 | inSet1.union(inSet2, outSet); 55 | } 56 | 57 | @Override 58 | protected void copy(PatternFlowSet source, PatternFlowSet dest) { 59 | //Copies the current FlowSet into dest. 60 | source.copy(dest); 61 | } 62 | 63 | protected void kill(PatternFlowSet inSet, Unit unit, PatternFlowSet outSet){ 64 | if(unit.toString().contains("requireNonNull")){ 65 | System.out.println(unit); 66 | } 67 | unit.apply(new AbstractStmtSwitch() { 68 | @Override 69 | public void caseAssignStmt(AssignStmt stmt) { 70 | Value rightOp = stmt.getRightOp(); 71 | for(int i=0; i < flowkills.length(); i++) { 72 | if(unit.toString().contains(flowkills.getJSONObject(i).getString("methodname")) 73 | && unit.toString().contains(flowkills.getJSONObject(i).getString("classname"))){ 74 | // System.out.println("stmt:"+stmt.toString()); 75 | for(Value v:inSet){ 76 | if (rightOp.toString().contains(v.toString())){ 77 | // System.out.println("special:"+v.toString()); 78 | outSet.remove(v); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | @Override 85 | public void caseIfStmt(IfStmt stmt) { 86 | Value condition = stmt.getCondition(); 87 | for(int i=0; i < flowkills.length(); i++) { 88 | if(unit.toString().contains(flowkills.getJSONObject(i).getString("methodname")) 89 | && unit.toString().contains(flowkills.getJSONObject(i).getString("classname"))){ 90 | // System.out.println("stmt:"+stmt.toString()); 91 | for(Value v:inSet){ 92 | if (condition.toString().contains(v.toString())){ 93 | // System.out.println("special:"+v.toString()); 94 | outSet.remove(v); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | }); 101 | } 102 | 103 | protected void gen(PatternFlowSet inSet, Unit unit, PatternFlowSet outSet){ 104 | 105 | unit.apply(new AbstractStmtSwitch() { 106 | @Override 107 | public void caseAssignStmt(AssignStmt stmt) { 108 | 109 | Value leftOp = stmt.getLeftOp(); 110 | Value rightOp = stmt.getRightOp(); 111 | for(int i=0; i < flowgens.length(); i++) { 112 | String gen_class = flowgens.getJSONObject(i).getString("classname"); 113 | String gen_method = flowgens.getJSONObject(i).getString("methodname"); 114 | if(rightOp.toString().contains(gen_method) && rightOp.toString().contains(gen_class)){ 115 | // System.out.println(unit.toString()); 116 | outSet.add(leftOp); 117 | } 118 | } 119 | } 120 | @Override 121 | public void caseIdentityStmt(IdentityStmt stmt) { 122 | Value leftOp = stmt.getLeftOp(); 123 | Value rightOp = stmt.getRightOp(); 124 | for(int i=0; i < flowgens.length(); i++) { 125 | String gen_method = flowgens.getJSONObject(i).getString("methodname"); 126 | if(rightOp.toString().contains(gen_method) 127 | && rightOp.toString().contains(flowgens.getJSONObject(i).getString("classname"))){ 128 | System.out.println(unit.toString()); 129 | outSet.add(leftOp); 130 | } 131 | } 132 | } 133 | }); 134 | } 135 | 136 | @Override 137 | protected PatternFlowSet newInitialFlow() { 138 | // TODO Auto-generated method stub 139 | return new PatternFlowSet(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Tool/SetChecker/src/GeneratePattern.java: -------------------------------------------------------------------------------- 1 | 2 | import java.io.*; 3 | import java.util.ArrayList; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONObject; 7 | 8 | 9 | public class GeneratePattern { 10 | 11 | public static void main(String[] args) throws IOException 12 | { 13 | String path="resource/allmappings"; 14 | String writepath="resource/allmappings.json"; 15 | FileInputStream fileInputStream = new FileInputStream("resource/dangerouslist.txt"); 16 | InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); 17 | BufferedReader reader = new BufferedReader(inputStreamReader); 18 | String dangerouString = ""; 19 | ArrayList dangerouList = new ArrayList(); 20 | while((dangerouString=reader.readLine()) != null){ 21 | dangerouList.add(dangerouString); 22 | } 23 | reader.close(); 24 | addpattern(path,writepath,dangerouList); 25 | } 26 | public static void addpattern(String path,String writepath,ArrayList dangerouList) throws IOException 27 | { 28 | JSONArray patterns = new JSONArray(); 29 | FileInputStream fileInputStream = new FileInputStream(path); 30 | InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); 31 | BufferedReader reader = new BufferedReader(inputStreamReader); 32 | String tempString = null; 33 | JSONObject pattern =new JSONObject(); 34 | JSONArray mainapis=new JSONArray(); 35 | JSONArray controlcheckbefores=new JSONArray(); 36 | boolean flag = false; 37 | while((tempString = reader.readLine()) != null){ 38 | if(tempString.startsWith("Permission:")) 39 | { 40 | flag = false; 41 | for (String dangerouString : dangerouList) { 42 | if(tempString.contains(dangerouString)){ 43 | flag = true; 44 | break; 45 | } 46 | } 47 | if(flag==true){ 48 | if(mainapis.length()>0) 49 | { 50 | pattern.put("mainapi", mainapis); 51 | JSONArray controlcheckafter=new JSONArray(); 52 | JSONArray flowgen=new JSONArray(); 53 | JSONArray flowkill=new JSONArray(); 54 | JSONArray flowfind=new JSONArray(); 55 | pattern.put("controlcheckafter", controlcheckafter); 56 | pattern.put("flowgen", flowgen); 57 | pattern.put("flowkill", flowkill); 58 | pattern.put("flowfind", flowfind); 59 | patterns.put(pattern); 60 | } 61 | mainapis=new JSONArray(); 62 | controlcheckbefores=new JSONArray(); 63 | pattern =new JSONObject(); 64 | String permission = tempString.substring(tempString.indexOf("Permission:")+11,tempString.length()); 65 | JSONObject controlcheckbefore=new JSONObject(); 66 | controlcheckbefore.put("methodname",permission); 67 | controlcheckbefore.put("classname",""); 68 | controlcheckbefores.put(controlcheckbefore); 69 | pattern.put("controlcheckbefore", controlcheckbefores); 70 | pattern.put("name", permission); 71 | } 72 | } 73 | else{ 74 | if(flag==true){ 75 | if (tempString.startsWith("<")) 76 | { 77 | String classname = tempString.substring(1,tempString.indexOf(':')); 78 | String methodname = tempString.substring(1,tempString.indexOf('(')); 79 | methodname = methodname.substring(methodname.lastIndexOf(' ')+1,methodname.length()); 80 | JSONObject mainapi=new JSONObject(); 81 | mainapi.put("methodname",methodname); 82 | mainapi.put("classname",classname); 83 | mainapis.put(mainapi); 84 | } 85 | if (tempString.startsWith("End")) 86 | { 87 | System.out.println(); 88 | } 89 | } 90 | } 91 | } 92 | JSONObject all_pattern =new JSONObject(); 93 | all_pattern.put("pattern",patterns); 94 | reader.close(); 95 | FileWriter fw = new FileWriter(writepath); 96 | PrintWriter out = new PrintWriter(fw); 97 | out.write(all_pattern.toString()); 98 | out.println(); 99 | fw.close(); 100 | out.close(); 101 | } 102 | } -------------------------------------------------------------------------------- /Tool/SetChecker/src/Main.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | import java.io.File; 3 | 4 | import soot.Body; 5 | import soot.Scene; 6 | import soot.SootClass; 7 | import soot.Unit; 8 | import soot.toolkits.graph.*; 9 | import soot.jimple.*; 10 | import soot.Value; 11 | import soot.SootMethod; 12 | import soot.util.Chain; 13 | import soot.jimple.JimpleBody; 14 | import soot.jimple.toolkits.callgraph.CallGraph; 15 | import soot.jimple.toolkits.callgraph.Targets; 16 | import soot.toolkits.graph.TrapUnitGraph; 17 | import soot.ValueBox; 18 | import soot.Local; 19 | import soot.MethodOrMethodContext; 20 | 21 | import org.json.JSONArray; 22 | import org.json.JSONObject; 23 | 24 | public class Main { 25 | static List> targetpath = new ArrayList<>(); 26 | static File analysisFile; 27 | static String apkname,apkdir; 28 | static List> allpathtospecial = new ArrayList<>(); 29 | static List firstaddmethod = new ArrayList<>(); //速度优化,反向追溯时,对多个路径经过同一个函数只对第一次经过该函数的前置路径分析 30 | static JSONObject pattern; 31 | static List afterbug = new ArrayList<>(); 32 | 33 | public void findbug(String name, JSONObject p){ 34 | targetpath.clear(); 35 | allpathtospecial.clear(); 36 | firstaddmethod.clear(); 37 | pattern = p; 38 | apkname = name; 39 | Set specialmethods; 40 | JSONArray mainapis = pattern.getJSONArray("mainapi"); 41 | JSONArray controlcheckafters = pattern.getJSONArray("controlcheckafter"); 42 | JSONArray controlcheckbefores = pattern.getJSONArray("controlcheckbefore"); 43 | JSONArray flowgens = pattern.getJSONArray("flowgen"); 44 | JSONArray flowfinds = pattern.getJSONArray("flowfind"); 45 | List warningmethod=new ArrayList<>(); 46 | if (controlcheckbefores.length() >0) 47 | { 48 | specialmethods = getSpecialMethods(mainapis); 49 | for (SootMethod sootMethod :specialmethods){ 50 | List path = new ArrayList<>(); 51 | path.add(sootMethod); 52 | findMCallerPath(sootMethod,path);} 53 | for (List path : allpathtospecial){ 54 | boolean flag = true; 55 | if(path.size()>1){ 56 | flag = analysis(path); 57 | } 58 | if(flag == false && path.size()>1 && !warningmethod.contains(path.get(1))){ 59 | if (flowgens.length()<1){ 60 | System.out.println("bug:"+path); 61 | }else{ 62 | System.out.println("warning:"+path);} 63 | warningmethod.add(path.get(1));} 64 | if(flag == true && path.size()>1){ 65 | System.out.println("pass:"+path);}} 66 | } 67 | if (controlcheckafters.length()>0) 68 | { 69 | CallGraph cg = Scene.v().getCallGraph(); 70 | Chain chain = Scene.v().getApplicationClasses(); 71 | for (SootClass sootClass : chain) { 72 | for (SootMethod method : sootClass.getMethods()) { 73 | if (method.toString().contains(apkname)){ 74 | try{ 75 | JimpleBody body = (JimpleBody)method.retrieveActiveBody(); 76 | for(int i=0; i < mainapis.length(); i++) { 77 | if (body.toString().contains(mainapis.getJSONObject(i).getString("methodname")) 78 | && body.toString().contains(mainapis.getJSONObject(i).getString("classname"))){ 79 | List path = new ArrayList<>(); 80 | path.add(method); 81 | findaftercheck(method,cg,path,controlcheckafters);}} 82 | }catch (Exception e) {} 83 | }}}} 84 | if (flowfinds.length()>0) 85 | { 86 | if (mainapis.length()>0){ 87 | for (SootMethod method : warningmethod){ 88 | findnullexception(method,new PatternFlowSet(),pattern);}} 89 | else{ 90 | Chain chain = Scene.v().getApplicationClasses(); 91 | for (SootClass sootClass : chain) { 92 | for (SootMethod method : sootClass.getMethods()) { 93 | for(int i=0; i < flowgens.length(); i++) { 94 | if (method.toString().contains(apkname) 95 | && !method.toString().contains(flowgens.getJSONObject(i).getString("methodname")) 96 | && !method.toString().contains(flowgens.getJSONObject(i).getString("classname"))){ 97 | try{ 98 | JimpleBody body = (JimpleBody) method.retrieveActiveBody(); 99 | if (body.toString().contains(flowgens.getJSONObject(i).getString("methodname")) 100 | && body.toString().contains(flowgens.getJSONObject(i).getString("classname"))){ 101 | findnullexception(method,new PatternFlowSet(),pattern); 102 | } 103 | }catch (Exception e) {} 104 | }}}}} 105 | 106 | } 107 | } 108 | public static void findaftercheck(SootMethod method,CallGraph cg,List path,JSONArray controlcheckafters) 109 | { 110 | List thispath = new ArrayList<>(); 111 | if (method.toString().contains(apkname)){ 112 | try{ 113 | JimpleBody body = (JimpleBody)method.retrieveActiveBody(); 114 | for(int i=0; i < controlcheckafters.length(); i++) { 115 | if (body.toString().contains(controlcheckafters.getJSONObject(i).getString("methodname")) 116 | && body.toString().contains(controlcheckafters.getJSONObject(i).getString("classname"))){ 117 | System.out.print("pass:"); 118 | for(SootMethod me:path){ 119 | System.out.print(me+"->"); 120 | } 121 | System.out.println("end"); 122 | return; 123 | } 124 | } 125 | Iterator targets = new Targets(cg.edgesOutOf(method)); 126 | if (!targets.hasNext() && !afterbug.contains(path.get(0))) 127 | { 128 | afterbug.add(path.get(0)); 129 | System.out.print("bug:"); 130 | for(SootMethod me:path){ 131 | System.out.print(me+"->"); 132 | } 133 | System.out.println("end"); 134 | return; 135 | } 136 | thispath.clear(); 137 | while(targets.hasNext()){ 138 | SootMethod tgt = (SootMethod) targets.next(); 139 | // System.out.println(method+" may call "+tgt); 140 | if(!path.contains(tgt) && method.toString().contains(apkname)) 141 | { 142 | thispath.addAll(path); 143 | thispath.add(tgt); 144 | findaftercheck(tgt,cg,thispath,controlcheckafters); 145 | thispath.clear(); 146 | } 147 | 148 | } 149 | 150 | }catch (Exception e) {} 151 | 152 | } 153 | 154 | } 155 | 156 | /** 157 | * 输入一个函数,找到该函数是否使用通过设置获取的变量后没有检查是否为非空就调用 158 | * @param method 159 | * @param parameterset 160 | */ 161 | public static void findnullexception(SootMethod method,PatternFlowSet parameterset, JSONObject pattern) 162 | { 163 | JSONArray flowgens = pattern.getJSONArray("flowgen"); 164 | JSONArray flowkills = pattern.getJSONArray("flowkill"); 165 | JSONArray flowfinds = pattern.getJSONArray("flowfind"); 166 | if (method.toString().contains(apkname) || parameterset.size()>0) 167 | { 168 | if(parameterset.size()==0){ 169 | System.out.println("Start=================================================================="); 170 | System.out.println(method.toString()); 171 | if (method.toString().contains("setDataSource")){ 172 | System.out.println(method.toString()); 173 | } 174 | } 175 | boolean findnull = false; 176 | JimpleBody body = new JimpleBody(); 177 | try{ 178 | body = (JimpleBody) method.retrieveActiveBody(); 179 | }catch (Exception e) { 180 | System.out.println("Exception,findnullexception,retrieveActiveBody"); 181 | return; 182 | } 183 | TrapUnitGraph g = new TrapUnitGraph(body); 184 | PatternFlowSet newset = findassign(body, parameterset); 185 | try{ 186 | FlowAnalysis npa = new FlowAnalysis(g,newset,flowgens,flowkills); 187 | }catch (Exception e) { 188 | return; 189 | } 190 | FlowAnalysis npa = new FlowAnalysis(g,newset,flowgens,flowkills); 191 | for(Unit unit :body.getUnits()){ 192 | Stmt stmt = (Stmt)unit; 193 | PatternFlowSet setbe = npa.getFlowBefore(unit); 194 | PatternFlowSet setaf = npa.getFlowAfter(unit); 195 | if(npa.getFlowBefore(unit).size()>0 || npa.getFlowAfter(unit).size()>0){ 196 | System.out.println("----------------"); 197 | System.out.println("method:"+method.toString()); 198 | System.out.println(unit.toString() + "[in]" +npa.getFlowBefore(unit)); 199 | System.out.println(unit.toString() + "[out]" +npa.getFlowAfter(unit));} 200 | if(stmt.containsInvokeExpr() && setbe.size() > 0 ){ 201 | List useboxs = stmt.getUseBoxes(); 202 | PatternFlowSet init = new PatternFlowSet(); 203 | SootMethod inme = stmt.getInvokeExpr().getMethod(); 204 | boolean analysisiflag = false; 205 | for (ValueBox use : useboxs){ 206 | if (use.toString().contains("ImmediateBox") && setbe.contains(use.getValue())){ 207 | init.add(use.getValue()); 208 | analysisiflag = true;}} 209 | if (analysisiflag == true){ 210 | findnullexception(inme,init,pattern);}} 211 | for(int i=0; i < flowfinds.length(); i++) { 212 | if (stmt.toString().contains(flowfinds.getJSONObject(i).getString("methodname")) 213 | && stmt.toString().contains(flowfinds.getJSONObject(i).getString("classname"))){ 214 | List useboxs = stmt.getUseBoxes(); 215 | if (useboxs.size() >0){ 216 | for (ValueBox use : useboxs){ 217 | if (setbe.contains(use.getValue())){ 218 | System.out.println("**********************Bug**********************"); 219 | System.out.println("firstfindnull:"+stmt.toString()); 220 | System.out.println("value:"+use.getValue().toString()); 221 | System.out.println("method:"+method.toString()); 222 | System.out.println("**********************Bug**********************"); 223 | findnull = true; 224 | break;}}} 225 | if (findnull == true){ 226 | break;}}}} 227 | if(parameterset.size()==0){ 228 | System.out.println("End"); 229 | System.out.println();} 230 | } 231 | 232 | } 233 | /** 234 | * 在函数中查找参数列表对应的变量,作为数据流分析的初始 235 | * @param body 236 | * @param initset 237 | * @return 238 | */ 239 | public static PatternFlowSet findassign(JimpleBody body, PatternFlowSet initset){ 240 | PatternFlowSet newset = new PatternFlowSet(); 241 | if (initset.size()>0){ 242 | List locals= body.getParameterLocals(); 243 | for(Local local :locals){ 244 | // System.out.println("local:"+local.toString()); 245 | // System.out.println("localtype:"+local.getType().toString()); 246 | for (Value init:initset){ 247 | if (init.getType().equals(local.getType())){ 248 | // System.out.println("inittype:"+init.getType().toString()); 249 | newset.add(local);}}}} 250 | return newset; 251 | } 252 | 253 | /** 254 | * 查找所有最终会调用specialmethod的方法调用路径 255 | * @param specialmethod 256 | * @param path 257 | */ 258 | public static void findMCallerPath(SootMethod specialmethod, List path) { 259 | JSONArray mainapis = pattern.getJSONArray("mainapi"); 260 | Set methods; 261 | methods = getMethods(); 262 | // System.out.println("Analysis the method [" + specialmethod.toString() + "] "); 263 | List callmethods = new ArrayList<>(); 264 | for(SootMethod m : methods){ 265 | if(m.toString().contains(apkname)){ 266 | try{ 267 | Body body = m.retrieveActiveBody(); 268 | for(Unit unit :body.getUnits()){ 269 | Stmt stmt = (Stmt)unit; 270 | if(stmt.containsInvokeExpr()){ 271 | if(specialmethod != null){ 272 | if (stmt.getInvokeExpr().getMethod().getName().equals(specialmethod.getName()) 273 | && stmt.toString().contains(specialmethod.getDeclaringClass().toString()) 274 | && !callmethods.contains(m) && !path.contains(m) && !firstaddmethod.contains(m)){ //优化,!path.contains(m)使得到环时停下 275 | // System.out.println("add:"+m); 276 | firstaddmethod.add(m); 277 | callmethods.add(m); 278 | } 279 | } 280 | } 281 | } 282 | }catch (Exception e) { 283 | 284 | } 285 | } 286 | } 287 | List path_copy = new ArrayList<>(); 288 | path_copy.addAll(path); 289 | if(callmethods.size() > 0){ 290 | for(SootMethod callmethod : callmethods){ 291 | path_copy.add(callmethod); 292 | findMCallerPath(callmethod,path_copy); 293 | path_copy.clear(); 294 | path_copy.addAll(path);}} 295 | else{ 296 | // analysis(path); 297 | allpathtospecial.add(path_copy);} 298 | } 299 | /** 300 | * 分析一条方法调用路径上,每个方法的控制流图中是否有对设置进行检查的语句,例如A->B->C, 301 | * 则C是使用设置的api,检查在B的控制流图中,包含调用C的语句的路径中,从路径起始到调用C之间是否检查过设置, 302 | * 然后再检查在A的控制流图中,包含调用B的语句的路径中,从路径起始到调用B之间是否检查过设置, 303 | * 如果都没有,则视为找到一个可能的BUG的其中一个条件被满足。 304 | * @param path 305 | */ 306 | public static Boolean analysis(List path){ 307 | // System.out.println("Start"); 308 | // for (SootMethod me : path){ 309 | // System.out.print(me.toString()+"->");} 310 | // System.out.println(); 311 | SootMethod caller = path.get(0); 312 | boolean pathflag= false; 313 | for(int i=1;i heads = unitGraph.getHeads(); 331 | for (Unit now : heads){ 332 | List path = new ArrayList<>(); 333 | path.add(now); 334 | Unit returnunit = forwardfindtarget(now,unitGraph,target,path); 335 | List path2 = new ArrayList<>(); 336 | path2.add(returnunit); 337 | getallpath(returnunit,unitGraph,path2);} 338 | boolean flag = checktargetpath(targetpath); 339 | if(flag==true){ 340 | System.out.println("find beforecheck:"+caller.toString()); 341 | } 342 | targetpath = new ArrayList<>(); 343 | // System.out.println(""); 344 | return flag; 345 | } 346 | /** 347 | * 检查控制流路径中,从起始点到调用函数之间是否检查过设置,返回true(检查过)或false(没有检查过) 348 | * @param targetpath 349 | * @return 350 | */ 351 | private static boolean checktargetpath(List> targetpath) 352 | { 353 | boolean flag = false; 354 | for(List path2 : targetpath){ 355 | // System.out.println("==========="); 356 | for (Unit unit:path2){ 357 | // System.out.println(unit+"->"); 358 | Boolean returncheck = havecheck(unit); 359 | if(returncheck == true){ 360 | flag = true;}} 361 | // System.out.println("end"); 362 | } 363 | return flag; 364 | } 365 | /** 366 | * 在函数的控制流图unitGraph中倒序查找所有到达目标点now的路径,并将找到的路径存入全局列表targetpath中 367 | * @param now 368 | * @param unitGraph 369 | * @param path2 370 | */ 371 | private static void getallpath(Unit now, UnitGraph unitGraph, List path2){ 372 | List unitlist = unitGraph.getPredsOf(now); 373 | 374 | if(unitlist.size()>0){ 375 | for (Unit unit:unitlist){ 376 | if(!path2.contains(unit)){ 377 | path2.add(unit); 378 | getallpath(unit,unitGraph,path2);}}} 379 | else{ 380 | targetpath.add(path2);} 381 | } 382 | /** 383 | * 检查一个语句是否是需要的设置检查语句,可根据pattern进行更改 384 | * @param unit 385 | * @return 386 | */ 387 | private static boolean havecheck(Unit unit){ 388 | JSONArray controlcheckbefores = pattern.getJSONArray("controlcheckbefore"); 389 | for(int i=0; i < controlcheckbefores.length(); i++) { 390 | JSONObject controlcheckbefore = controlcheckbefores.getJSONObject(i); 391 | if (unit!= null && (unit.toString().contains(controlcheckbefore.getString("methodname")) 392 | && unit.toString().contains(controlcheckbefore.getString("classname")))){ 393 | return true;} 394 | } 395 | return false; 396 | } 397 | /** 398 | * 从函数的控制流图中的起始点now开始正序查找目标函数(被调用函数), 399 | * 这一步的主要目的是快速找到目标函数在soot中的数据结构,之后便于使用soot方法调用,之前只知道目标函数的名字 400 | * @param now 401 | * @param unitGraph 402 | * @param target 403 | * @param path 404 | * @return 405 | */ 406 | private static Unit forwardfindtarget(Unit now, UnitGraph unitGraph, SootMethod target, List path){ 407 | List unitlist = unitGraph.getSuccsOf(now); 408 | Unit returnunit = null; 409 | if (unitlist.size()>0){ 410 | for (Unit unit:unitlist){ 411 | if(unit.toString().contains(target.getName())){ 412 | // System.out.println("**get it**"+unit.toString()); 413 | return unit;} 414 | else{ 415 | if (!path.contains(unit)){ 416 | // System.out.println(unit.toString()); 417 | path.add(unit); 418 | Unit getunit = forwardfindtarget(unit,unitGraph,target,path); 419 | if (getunit!=null){ 420 | returnunit = getunit;}}}}} 421 | return returnunit; 422 | } 423 | /** 424 | * 输入应用的所有类列表,返回所有包含某个字段的函数列表 425 | * @param classes 426 | * @param methodName 427 | * @param className 428 | * @return 429 | */ 430 | private static Set getSpecialMethods(JSONArray mainapis) 431 | { 432 | Set methods = new HashSet(); 433 | Set mlist = getMethods(); 434 | for (SootMethod method: mlist){ 435 | for(int j=0; j < mainapis.length(); j++) { 436 | JSONObject mainapi = mainapis.getJSONObject(j); 437 | if(method.getName().contains(mainapi.getString("methodname")) 438 | && method.getDeclaringClass().toString().contains(mainapi.getString("classname"))){ 439 | System.out.println("ORIGIN:"+method.toString()); 440 | methods.add(method);} 441 | if (method.toString().contains(apkname)){ 442 | try{ 443 | JimpleBody body = (JimpleBody)method.retrieveActiveBody(); 444 | for(Unit unit :body.getUnits()){ 445 | Stmt stmt = (Stmt)unit; 446 | if (stmt.containsInvokeExpr() && stmt.getInvokeExpr().toString().contains(mainapi.getString("methodname")) 447 | && stmt.getInvokeExpr().toString().contains(mainapi.getString("classname")) 448 | && !methods.contains(stmt.getInvokeExpr().getMethod())){ 449 | System.out.println("ORIGIN:"+stmt.getInvokeExpr().getMethod().toString()); 450 | methods.add(stmt.getInvokeExpr().getMethod());}} 451 | }catch (Exception e) {} 452 | } 453 | } 454 | } 455 | return methods; 456 | } 457 | /** 458 | * get the list of methods in the app 459 | * @param classes 460 | * @return 461 | */ 462 | private static Set getMethods() 463 | { 464 | Set methods = new HashSet(); 465 | Chain chain = Scene.v().getApplicationClasses(); 466 | for(SootClass sootClass: chain) 467 | { 468 | List mlist = sootClass.getMethods(); 469 | methods.addAll(mlist);} 470 | return methods; 471 | } 472 | static boolean unitBelongList(Unit u, List g) 473 | { 474 | for(Unit i:g){ 475 | if(i.equals(u)) 476 | return true;} 477 | return false; 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /Tool/SetChecker/src/PatternAnalysis.java: -------------------------------------------------------------------------------- 1 | 2 | import soot.Value; 3 | import soot.Unit; 4 | import soot.jimple.*; 5 | import soot.toolkits.graph.DirectedGraph; 6 | import soot.toolkits.scalar.ForwardFlowAnalysis; 7 | import org.json.JSONArray; 8 | import org.json.JSONException; 9 | import org.json.JSONObject; 10 | 11 | /** 12 | * @program: MySootScript 13 | * @description: 14 | * @author: Dr.Navid 15 | * @create: 2021-05-10 15:11 16 | **/ 17 | 18 | 19 | public class PatternAnalysis extends ForwardFlowAnalysis { 20 | 21 | 22 | public enum AnalysisMode {MUST, MAY_P, MAY_O} 23 | 24 | public AnalysisMode analysisMode; 25 | PatternFlowSet newset = new PatternFlowSet(); 26 | JSONArray flowgens ,flowkills; 27 | 28 | 29 | 30 | public PatternAnalysis(DirectedGraph graph, PatternFlowSet newset, JSONArray flowgens, JSONArray flowkills) { 31 | super(graph); 32 | this.analysisMode = AnalysisMode.MUST; 33 | this.newset = newset; 34 | this.flowgens = flowgens; 35 | this.flowkills = flowkills; 36 | 37 | doAnalysis(); 38 | } 39 | 40 | @Override 41 | protected PatternFlowSet entryInitialFlow() { 42 | return this.newset; 43 | } 44 | 45 | @Override 46 | protected void flowThrough(PatternFlowSet inSet, Unit unit, PatternFlowSet outSet) { 47 | inSet.copy(outSet); 48 | kill(inSet, unit, outSet); 49 | gen(inSet, unit, outSet); 50 | } 51 | 52 | @Override 53 | protected void merge(PatternFlowSet inSet1, PatternFlowSet inSet2, PatternFlowSet outSet) { 54 | inSet1.union(inSet2, outSet); 55 | } 56 | 57 | @Override 58 | protected void copy(PatternFlowSet source, PatternFlowSet dest) { 59 | //Copies the current FlowSet into dest. 60 | source.copy(dest); 61 | } 62 | 63 | protected void kill(PatternFlowSet inSet, Unit unit, PatternFlowSet outSet){ 64 | if(unit.toString().contains("requireNonNull")){ 65 | System.out.println(unit); 66 | } 67 | unit.apply(new AbstractStmtSwitch() { 68 | @Override 69 | public void caseAssignStmt(AssignStmt stmt) { 70 | Value rightOp = stmt.getRightOp(); 71 | for(int i=0; i < flowkills.length(); i++) { 72 | if(unit.toString().contains(flowkills.getJSONObject(i).getString("methodname")) 73 | && unit.toString().contains(flowkills.getJSONObject(i).getString("classname"))){ 74 | // System.out.println("stmt:"+stmt.toString()); 75 | for(Value v:inSet){ 76 | if (rightOp.toString().contains(v.toString())){ 77 | // System.out.println("special:"+v.toString()); 78 | outSet.remove(v); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | @Override 85 | public void caseIfStmt(IfStmt stmt) { 86 | Value condition = stmt.getCondition(); 87 | for(int i=0; i < flowkills.length(); i++) { 88 | if(unit.toString().contains(flowkills.getJSONObject(i).getString("methodname")) 89 | && unit.toString().contains(flowkills.getJSONObject(i).getString("classname"))){ 90 | // System.out.println("stmt:"+stmt.toString()); 91 | for(Value v:inSet){ 92 | if (condition.toString().contains(v.toString())){ 93 | // System.out.println("special:"+v.toString()); 94 | outSet.remove(v); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | }); 101 | } 102 | 103 | protected void gen(PatternFlowSet inSet, Unit unit, PatternFlowSet outSet){ 104 | 105 | unit.apply(new AbstractStmtSwitch() { 106 | @Override 107 | public void caseAssignStmt(AssignStmt stmt) { 108 | 109 | Value leftOp = stmt.getLeftOp(); 110 | Value rightOp = stmt.getRightOp(); 111 | for(int i=0; i < flowgens.length(); i++) { 112 | String gen_class = flowgens.getJSONObject(i).getString("classname"); 113 | String gen_method = flowgens.getJSONObject(i).getString("methodname"); 114 | if(rightOp.toString().contains(gen_method) && rightOp.toString().contains(gen_class)){ 115 | // System.out.println(unit.toString()); 116 | outSet.add(leftOp); 117 | } 118 | } 119 | } 120 | @Override 121 | public void caseIdentityStmt(IdentityStmt stmt) { 122 | Value leftOp = stmt.getLeftOp(); 123 | Value rightOp = stmt.getRightOp(); 124 | for(int i=0; i < flowgens.length(); i++) { 125 | String gen_method = flowgens.getJSONObject(i).getString("methodname"); 126 | if(rightOp.toString().contains(gen_method) 127 | && rightOp.toString().contains(flowgens.getJSONObject(i).getString("classname"))){ 128 | System.out.println(unit.toString()); 129 | outSet.add(leftOp); 130 | } 131 | } 132 | } 133 | }); 134 | } 135 | 136 | @Override 137 | protected PatternFlowSet newInitialFlow() { 138 | // TODO Auto-generated method stub 139 | return new PatternFlowSet(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Tool/SetChecker/src/PatternFlowSet.java: -------------------------------------------------------------------------------- 1 | 2 | import soot.toolkits.scalar.AbstractBoundedFlowSet; 3 | import soot.Value; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @program: MySootScript 9 | * @description: 10 | * @author: 0range 11 | * @create: 2021-05-10 18:54 12 | **/ 13 | 14 | 15 | public class PatternFlowSet extends AbstractBoundedFlowSet { 16 | 17 | private Set liveVariableSet = new HashSet<>(); 18 | public PatternFlowSet(){ 19 | super(); 20 | } 21 | 22 | @Override 23 | public PatternFlowSet clone() { 24 | PatternFlowSet myClone = new PatternFlowSet(); 25 | myClone.liveVariableSet.addAll(this.liveVariableSet); 26 | return myClone; 27 | } 28 | 29 | @Override 30 | public boolean isEmpty() { 31 | return liveVariableSet.isEmpty(); 32 | } 33 | 34 | @Override 35 | public int size() { 36 | return liveVariableSet.size(); 37 | } 38 | 39 | @Override 40 | public void add(Value local) { 41 | liveVariableSet.add(local); 42 | } 43 | 44 | @Override 45 | public void remove(Value local) { 46 | if(liveVariableSet.contains(local)) { 47 | liveVariableSet.remove(local); 48 | } 49 | } 50 | 51 | @Override 52 | public boolean contains(Value local) { 53 | return liveVariableSet.contains(local); 54 | } 55 | 56 | @Override 57 | public Iterator iterator() { 58 | return liveVariableSet.iterator(); 59 | } 60 | 61 | @Override 62 | public List toList() { 63 | return new ArrayList<>(liveVariableSet); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tool/SetChecker/src/TestApp.java: -------------------------------------------------------------------------------- 1 | 2 | import java.util.*; 3 | import java.io.*; 4 | 5 | import soot.PackManager; 6 | import soot.SceneTransformer; 7 | import soot.Transform; 8 | import soot.options.Options; 9 | import org.json.JSONArray; 10 | import org.json.JSONObject; 11 | 12 | public class TestApp { 13 | static File analysisFile; 14 | static String apkname,apkdir,filePathIn; 15 | static JSONArray patterns; 16 | 17 | public static void main(String[] args) throws IOException 18 | { 19 | PrintStream stdout = System.out; 20 | String path = args[3]; 21 | File file = new File(path); 22 | File[] fs = file.listFiles(); 23 | for(File f:fs){ 24 | if(!f.isDirectory()){ 25 | System.out.println(f); 26 | startsoot(args,f.toString()); 27 | System.setOut(stdout); 28 | } 29 | } 30 | } 31 | public static void startsoot(String[] args, String apkpath) throws IOException{ 32 | soot.G.reset(); 33 | String[] sootlist=handleargs(args,apkpath); 34 | PrintStream ps = new PrintStream(analysisFile); 35 | System.setOut(ps); 36 | Options.v().set_src_prec(Options.src_prec_apk); 37 | Options.v().set_output_format(Options.output_format_jimple); 38 | Options.v().set_no_writeout_body_releasing(true); 39 | 40 | PackManager.v().getPack("wjtp").add(new Transform("wjtp.myTrans", new SceneTransformer(){ 41 | @Override 42 | protected void internalTransform(String arg0, Map arg1) { 43 | Main flowAnalysis = new Main(); 44 | for(int j=0; j < patterns.length(); j++) { // Iterate each element in the elements array 45 | JSONObject pattern = patterns.getJSONObject(j); 46 | System.out.println("#######################################"); 47 | System.out.println("Name:"+pattern.getString("name")); 48 | flowAnalysis.findbug(apkname,pattern); 49 | System.out.println(); 50 | } 51 | } 52 | })); 53 | 54 | soot.Main.main(sootlist); 55 | } 56 | /** 57 | * 解析输入 58 | * @param args 59 | * @throws IOException 60 | */ 61 | public static String[] handleargs(String[] args,String apkpath) throws IOException{ 62 | String[] sootlist = args; 63 | sootlist[3]=apkpath; 64 | String[] list = sootlist[3].split("\\\\"); 65 | apkname = list[list.length-1].substring(0,list[list.length-1].length()-4); 66 | analysisFile = new File("../output/sootOutput/"+apkname+"\\allmappings.txt"); 67 | apkdir = "../output/sootOutput/"+apkname; 68 | File file=new File("../output/sootOutput/"); 69 | if(!file.exists()){ 70 | file.mkdir();} 71 | sootlist[1] = apkdir; 72 | String JsonContext = new Util().ReadFile("resource/allmappings.json"); 73 | JSONObject rootObject = new JSONObject(JsonContext); // Parse the JSON to a JSONObject 74 | patterns = rootObject.getJSONArray("pattern"); 75 | file=new File(apkdir); 76 | if(!file.exists()){ 77 | file.mkdir();} 78 | return sootlist; 79 | } 80 | 81 | public void addpattern(String path) throws IOException 82 | { 83 | FileInputStream fileInputStream = new FileInputStream(path); 84 | InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); 85 | BufferedReader reader = new BufferedReader(inputStreamReader); 86 | String tempString = null; 87 | JSONObject pattern =new JSONObject(); 88 | JSONArray mainapis=new JSONArray(); 89 | JSONArray controlcheckbefores=new JSONArray(); 90 | while((tempString = reader.readLine()) != null){ 91 | if(tempString.startsWith("Permission:")) 92 | { 93 | if(mainapis.length()>0) 94 | { 95 | pattern.put("mainapi", mainapis); 96 | patterns.put(pattern); 97 | } 98 | mainapis.clear(); 99 | controlcheckbefores.clear(); 100 | pattern.clear(); 101 | String permission = tempString.substring(tempString.indexOf("Permission:"),tempString.length()-1); 102 | JSONObject controlcheckbefore=new JSONObject(); 103 | controlcheckbefore.put("methodname",permission); 104 | controlcheckbefores.put(controlcheckbefore); 105 | pattern.put("controlcheckbefore", controlcheckbefores); 106 | } 107 | else{ 108 | if (tempString.startsWith("<")) 109 | { 110 | String classname = tempString.substring(1,tempString.indexOf(':')); 111 | String methodname = tempString.substring(1,tempString.indexOf('(')); 112 | methodname = methodname.substring(methodname.lastIndexOf(' '),methodname.length()); 113 | JSONObject mainapi=new JSONObject(); 114 | mainapi.put("methodname",methodname); 115 | mainapi.put("classname",classname); 116 | mainapis.put(mainapi); 117 | } 118 | } 119 | } 120 | reader.close(); 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /Tool/SetChecker/src/Util.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.FileInputStream; 3 | import java.io.IOException; 4 | import java.io.InputStreamReader; 5 | 6 | 7 | public class Util { 8 | 9 | public String ReadFile(String Path){ 10 | BufferedReader reader = null; 11 | String laststr = ""; 12 | try{ 13 | FileInputStream fileInputStream = new FileInputStream(Path); 14 | InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8"); 15 | reader = new BufferedReader(inputStreamReader); 16 | String tempString = null; 17 | while((tempString = reader.readLine()) != null){ 18 | laststr += tempString; 19 | } 20 | reader.close(); 21 | }catch(IOException e){ 22 | e.printStackTrace(); 23 | }finally{ 24 | if(reader != null){ 25 | try { 26 | reader.close(); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | } 32 | return laststr; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /Tool/SetDroid/Document/Android_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/Android_logo.jpg -------------------------------------------------------------------------------- /Tool/SetDroid/Document/Android_robot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/Android_robot.png -------------------------------------------------------------------------------- /Tool/SetDroid/Document/DroidBot_documentation.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/DroidBot_documentation.docx -------------------------------------------------------------------------------- /Tool/SetDroid/Document/DroidBot_documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/DroidBot_documentation.pdf -------------------------------------------------------------------------------- /Tool/SetDroid/Document/Heartbeat.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/Heartbeat.mp3 -------------------------------------------------------------------------------- /Tool/SetDroid/Document/droidbot_utg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/droidbot_utg.png -------------------------------------------------------------------------------- /Tool/SetDroid/Document/droidmutator_test_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/droidmutator_test_report.png -------------------------------------------------------------------------------- /Tool/SetDroid/Document/intermission.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/intermission.mp3 -------------------------------------------------------------------------------- /Tool/SetDroid/Document/password.txt: -------------------------------------------------------------------------------- 1 | email: droidbot@gmail.com 2 | password: droidbot_passwd 3 | -------------------------------------------------------------------------------- /Tool/SetDroid/Document/sample.3gp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/sample.3gp -------------------------------------------------------------------------------- /Tool/SetDroid/Document/sample_3GPP.3gp.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/sample_3GPP.3gp.zip -------------------------------------------------------------------------------- /Tool/SetDroid/Document/sample_iPod.m4v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/sample_iPod.m4v -------------------------------------------------------------------------------- /Tool/SetDroid/Document/sample_mpeg4.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/sample_mpeg4.mp4 -------------------------------------------------------------------------------- /Tool/SetDroid/Document/sample_sorenson.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setting-defect-fuzzing/home/6762faebf9af0bd9cac47e6a2327bfabe7c58686/Tool/SetDroid/Document/sample_sorenson.mov -------------------------------------------------------------------------------- /Tool/SetDroid/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import hashlib 4 | """ 5 | Record the information of the app 6 | """ 7 | class App(object): 8 | 9 | def __init__(self, app_path, root_path, app_name): 10 | print("Root path:"+root_path) 11 | assert app_path is not None 12 | self.logger = logging.getLogger(self.__class__.__name__) 13 | self.app_path = app_path 14 | 15 | from androguard.core.bytecodes.apk import APK 16 | self.apk = APK(self.app_path) 17 | self.package_name = self.apk.get_package() 18 | self.main_activity = self.apk.get_main_activity() 19 | self.permissions = self.apk.get_permissions() 20 | self.activities = self.apk.get_activities() 21 | if app_name is not None: 22 | self.app_name = app_name 23 | else: 24 | self.app_name = self.apk.get_app_name() 25 | print("Main activity:"+self.main_activity) 26 | print("Package name:"+self.package_name) 27 | self.output_path=root_path+self.package_name 28 | 29 | def get_package_name(self): 30 | """ 31 | get package name of current app 32 | :return: 33 | """ 34 | return self.package_name 35 | 36 | -------------------------------------------------------------------------------- /Tool/SetDroid/checker.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import subprocess 4 | import time 5 | from device import Device 6 | from app import App 7 | import uiautomator2 as u2 8 | import time 9 | import re 10 | from injector import Injector 11 | from utils import Utils 12 | 13 | class Checker(object): 14 | 15 | def __init__(self,devices,app,strategy_list,emulator_path,android_system,root_path,resource_path,testcase_count,event_num,timeout,setting_random_denominator,rest_interval,choice): 16 | 17 | self.timeout = timeout 18 | self.app = app 19 | self.devices = devices 20 | self.guest_devices=self.devices[1:len(self.devices)] 21 | self.emulator_path = emulator_path 22 | self.android_system = android_system 23 | self.root_path = root_path 24 | self.resource_path = resource_path 25 | self.strategy_list = strategy_list 26 | self.testcase_count = testcase_count 27 | self.event_num = event_num 28 | self.setting_random_denominator = setting_random_denominator 29 | self.rest_interval = rest_interval 30 | self.injector = Injector(devices=devices, 31 | app=app, 32 | strategy_list=strategy_list, 33 | emulator_path=emulator_path, 34 | android_system=android_system, 35 | root_path=root_path, 36 | resource_path=resource_path, 37 | testcase_count=testcase_count, 38 | event_num=event_num, 39 | timeout=timeout, 40 | setting_random_denominator=setting_random_denominator, 41 | rest_interval=rest_interval, 42 | choice=choice) 43 | self.utils = Utils(devices=devices) 44 | 45 | def check_time(self,path): 46 | fw = open(path+"/time_bug.txt",'w',encoding='utf-8') 47 | time_bugs=[] 48 | import os 49 | for root, dirs, files in os.walk(path, topdown=False): 50 | for name in files: 51 | if "5556" in name and "xml" in name: 52 | xml_path=os.path.join(root, name) 53 | f = open(xml_path,'r',encoding='utf-8') 54 | lines=f.readlines() 55 | f.close() 56 | for line in lines: 57 | if "text=\"" in line and self.app.package_name in line: 58 | text = line[line.find("text=\"")+6:len(line)] 59 | text = text[0:text.find("\"")] 60 | pattern = re.search("^(1[0-2]|0?[1-9]|0):([0-5]?[0-9])$", text) 61 | if pattern != None and text not in time_bugs: 62 | time_bugs.append(text) 63 | fw.write(xml_path+'\n') 64 | fw.write(text+'\n\n') 65 | fw.flush() 66 | fw.close() 67 | 68 | 69 | def check_language(self,path): 70 | fw = open(path+"/language_bug.txt",'w',encoding='utf-8') 71 | language_bugs=[] 72 | import os 73 | for root, dirs, files in os.walk(path, topdown=False): 74 | for name in files: 75 | if "5556" in name and "xml" in name: 76 | xml_path=os.path.join(root, name) 77 | f = open(xml_path,'r',encoding='utf-8') 78 | lines=f.readlines() 79 | f.close() 80 | for line in lines: 81 | if "text=\"" in line and self.app.package_name in line: 82 | text = line[line.find("text=\"")+6:len(line)] 83 | text = text[0:text.find("\"")] 84 | import langid 85 | language_classify=langid.classify(text) 86 | if "en" in language_classify[0] and text!="" and text not in language_bugs: 87 | print(text) 88 | language_bugs.append(text) 89 | fw.write(xml_path+'\n') 90 | fw.write(text+'\n\n') 91 | fw.flush() 92 | fw.close() 93 | 94 | 95 | 96 | def check_keyboard(self): 97 | for device in self.devices: 98 | # device.use.set_clipboard('text', 'label') 99 | lines = device.state.lines 100 | if "com.sohu.inputmethod.sogou:id/imeview_keyboard" in lines or "com.baidu.input_huawei" in lines: 101 | print("close_keyboard") 102 | device.close_keyboard() 103 | 104 | def check_samestate(self): 105 | if self.devices[0].state.same_but_not_language(self.devices[1].state): 106 | return True 107 | return False 108 | 109 | def check_foreground(self): 110 | packagelist=[self.app.package_name,"com.google.android.permissioncontroller","com.android.packageinstaller","com.android.permissioncontroller"] 111 | lines = self.devices[0].use.dump_hierarchy() 112 | for package in packagelist: 113 | if package in lines: 114 | return True 115 | return False 116 | 117 | def check_start(self,times,strategy): 118 | try: 119 | if self.app.package_name == "fr.free.nrw.commons" and times==0: 120 | print("Feimao start 0") 121 | time.sleep(self.rest_interval*20) 122 | for device in self.devices: 123 | device.use(scrollable=True).scroll.horiz.toEnd(max_swipes=50) 124 | device.use(text="YES!").wait(timeout=10.0) 125 | device.use(text="YES!",instance=0).click() 126 | device.use(text="Skip").wait(timeout=10.0) 127 | device.use(text="Skip",instance=0).click() 128 | device.use(text="YES").wait(timeout=10.0) 129 | device.use(text="YES",instance=0).click() 130 | print("Feimao start end 0") 131 | else: 132 | print("Other start") 133 | time.sleep(self.rest_interval*2) 134 | except: 135 | print("") 136 | 137 | def check_setting_request(self): 138 | flag=False 139 | for device in self.guest_devices: 140 | if device.strategy == "permssion_lazy_1": 141 | if self.check_permission_request(device): 142 | self.devices[1].permission=True 143 | return True 144 | elif device.strategy == "network_lazy_1": 145 | if self.check_network_request(device): 146 | return True 147 | elif device.strategy == "location_lazy_1": 148 | if self.check_location_request(device): 149 | return True 150 | return flag 151 | 152 | def check_location_request(self,device): 153 | Flag = False 154 | gpslist = ["location","gps","位置","加载","connection"] 155 | requestlist = ["unavailable","try again","开启","检查","失败","重新","重试","not available","not enabled"] 156 | lines2 = device.use.dump_hierarchy() 157 | if (self.containsAny(lines2.lower(), gpslist) and self.containsAny(lines2.lower(), requestlist)): 158 | print("Allow location") 159 | if device.use(text="SETTINGS").count > 0: 160 | device.use(text="SETTINGS").click() 161 | device.use(text="OFF").wait() 162 | device.use(text="OFF").click() 163 | device.use.press("back") 164 | self.devices[1].gps_state = True 165 | else: 166 | self.injector.location_lazy_1() 167 | Flag = True 168 | return Flag 169 | 170 | def check_notification_request(self,device): 171 | Flag = False 172 | notificationlist = ["通知"] 173 | requestlist = ["unavailable","try again","开启"] 174 | lines2 = device.use.dump_hierarchy() 175 | if (self.containsAny(lines2.lower(), notificationlist) and self.containsAny(lines2.lower(), requestlist)): 176 | print("Allow notification") 177 | if device.use(text="忽略").count > 0: 178 | device.use(text="忽略").click() 179 | Flag = True 180 | return Flag 181 | 182 | def check_network_request(self,device): 183 | Flag = False 184 | wifilist = ["wifi","network","网络","加载","connection"] 185 | requestlist = ["unavailable","try again","开启","检查","失败","重新","重试","not available"] 186 | lines2 = device.use.dump_hierarchy() 187 | if (self.containsAny(lines2.lower(), wifilist) and self.containsAny(lines2.lower(), requestlist)): 188 | print("Allow network") 189 | self.injector.network_lazy_1() 190 | for device in self.devices: 191 | # if device.use(scrollable=True,packageName=self.app.package_name).count>0: 192 | # device.use(scrollable=True,packageName=self.app.package_name).scroll.vert.backward(steps=5) 193 | # device.use(scrollable=True,packageName=self.app.package_name).scroll.vert.forward(steps=5) 194 | if device.use(textContains="重试").count>0: 195 | device.use(textContains="重试").click() 196 | time.sleep(self.rest_interval*1) 197 | Flag = True 198 | return Flag 199 | 200 | def check_permission_request(self,device): 201 | Flag = False 202 | while device.use(className="android.widget.Button",packageName="com.google.android.permissioncontroller").count > 0: 203 | print("Allow permission:permissioncontroller") 204 | device.use(className="android.widget.Button",packageName="com.google.android.permissioncontroller").click() 205 | time.sleep(self.rest_interval*1) 206 | Flag = True 207 | while device.use(className="android.widget.Button",packageName="com.android.packageinstaller").count > 0: 208 | print("Allow permission:packageinstaller") 209 | device.use(className="android.widget.Button",packageName="com.android.packageinstaller",instance=1).click() 210 | time.sleep(self.rest_interval*1) 211 | Flag = True 212 | while device.use(className="android.widget.Button",packageName="com.google.android.packageinstaller").count > 0: 213 | print("Allow permission:packageinstaller") 214 | device.use(className="android.widget.Button",packageName="com.google.android.packageinstaller",instance=1).click() 215 | time.sleep(self.rest_interval*1) 216 | Flag = True 217 | 218 | permissionlist = ["权限","permission","authoriz"] 219 | requestlist = ["需要","开启","allow","允许","request","ensure","require","检查"] 220 | speciallist = ["migrate"] 221 | lines2 = device.use.dump_hierarchy() 222 | if device.use(packageName="com.android.settings",text="Permissions").count>0: 223 | device.use(packageName="com.android.settings",text="Permissions").click() 224 | time.sleep(self.rest_interval*1) 225 | while device.use(text="OFF",className="android.widget.Switch").count>0: 226 | device.use(text="OFF",className="android.widget.Switch").click() 227 | while device.use(text="我知道了",resourceId="com.ss.android.ugc.aweme:id/e9y").count>0: 228 | device.use(text="我知道了",resourceId="com.ss.android.ugc.aweme:id/e9y").click() 229 | while device.use(packageName=self.app.package_name).count<1: 230 | device.use.press("back") 231 | time.sleep(self.rest_interval*1) 232 | elif device.use(packageName="com.android.settings",text="APPS").count>0: 233 | device.use(scrollable=True,instance = 0).scroll.to(text=self.app.app_name) 234 | device.use(text=self.app.app_name).click() 235 | device.use(text="Battery").wait(timeout=3.0) 236 | device.use(scrollable=True,instance = 0).scroll.to(text="Permissions") 237 | self.check_permission_request(device) 238 | elif (self.containsAny(lines2.lower(), permissionlist) and self.containsAny(lines2.lower(), requestlist)) or self.containsAny(lines2.lower(), speciallist): 239 | print("Allow permission") 240 | again_flag=1 241 | if device.use(text="允许").count > 0: 242 | device.use(text="允许").click() 243 | again_flag=0 244 | elif device.use(text="设置").count > 0: 245 | device.use(text="设置").click() 246 | again_flag=0 247 | elif device.use(text="去设置").count > 0: 248 | device.use(text="去设置").click() 249 | again_flag=0 250 | elif device.use(text="确定").count > 0: 251 | device.use(text="确定").click() 252 | again_flag=0 253 | elif device.use(text="Settings").count > 0: 254 | device.use(text="Settings").click() 255 | again_flag=0 256 | elif device.use(text="Allow storage permissions in order to fully enjoy WeChat features.").count > 0: 257 | device.use(textContains="Allow").click() 258 | again_flag=0 259 | elif device.use(className="android.widget.Button").count > 0: 260 | device.use(className="android.widget.Button").click() 261 | again_flag=0 262 | if again_flag==0: 263 | time.sleep(self.rest_interval*3) 264 | self.check_permission_request(device) 265 | Flag = True 266 | return Flag 267 | 268 | def containsAny(self,seq, aset): 269 | return True if any(i in seq for i in aset) else False 270 | 271 | def check_loading(self): 272 | wait_time=0 273 | for device in self.devices: 274 | if device.use(className="android.widget.ProgressBar").count>0 and wait_time < self.rest_interval*5: 275 | time.sleep(self.rest_interval*5) 276 | wait_time=wait_time+self.rest_interval*5 277 | print("wait load") 278 | elif wait_time > self.rest_interval*20 or wait_time == self.rest_interval*20: 279 | print("so long wait") 280 | return wait_time 281 | 282 | def check_crash(self): 283 | for device in self.devices: 284 | device.last_crash_logcat = device.crash_logcat 285 | f_crash = open(self.root_path+"/"+device.device_serial+"_logcat.txt",'r',encoding='utf-8') 286 | device.crash_logcat=f_crash.read() 287 | if device.use(text="Close app").count>0: 288 | device.use(text="Close app").click() 289 | if device.crash_logcat!=device.last_crash_logcat: 290 | crash_info = device.crash_logcat[len(device.last_crash_logcat):len(device.crash_logcat)-1]+'\n' 291 | return crash_info 292 | return None 293 | -------------------------------------------------------------------------------- /Tool/SetDroid/device.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import re 4 | import subprocess 5 | import sys 6 | import time 7 | import uiautomator2 as u2 8 | from threading import Thread 9 | 10 | class MyThread(Thread): 11 | def __init__(self, func, args): 12 | super(MyThread, self).__init__() 13 | self.func = func 14 | self.args = args 15 | 16 | def run(self): 17 | self.result = self.func(*self.args) 18 | 19 | def get_result(self): 20 | try: 21 | return self.result 22 | except Exception: 23 | return None 24 | """ 25 | Record the information of the device 26 | """ 27 | class Device(object): 28 | 29 | def __init__(self, device_num=None,device_serial=None, is_emulator=True, rest_interval=None): 30 | self.device_num=device_num 31 | self.device_serial=device_serial 32 | self.is_emulator=is_emulator 33 | self.use=None 34 | self.state=None 35 | self.last_state=None 36 | self.strategy="screen" 37 | self.crash_logcat="" 38 | self.last_crash_logcat="" 39 | self.language="en" 40 | self.rest_interval=rest_interval 41 | self.wifi_state=True 42 | self.gps_state=True 43 | self.sound_state=True 44 | self.battery_state=False 45 | self.game_mode=True 46 | self.blue_light=False 47 | self.notification=True 48 | self.permission=True 49 | self.hourformat="12h" 50 | 51 | def set_strategy(self,strategy): 52 | self.strategy = strategy 53 | self.error_num = 0 54 | self.wrong_num = 0 55 | 56 | def set_thread(self,execute_event,args): 57 | if execute_event is not None: 58 | self.thread = MyThread(execute_event, args) 59 | else: 60 | self.thread = None 61 | 62 | def restart(self,emulator_path,emulator_name): 63 | port = self.device_serial[self.device_serial.find("-")+1:len(self.device_serial)] 64 | print("----"+port) 65 | subprocess.run(["adb","-s",self.device_serial,"emu","kill"], stdout=subprocess.PIPE) 66 | time.sleep(self.rest_interval*20) 67 | os.popen(emulator_path+" -avd "+emulator_name+" -read-only -port "+port) 68 | print("wait-for-device") 69 | subprocess.run(["adb","-s",self.device_serial,"wait-for-device"], stdout=subprocess.PIPE) 70 | print("wait-for-device end") 71 | time.sleep(self.rest_interval*10) 72 | 73 | def make_strategy(self,root_path): 74 | if not os.path.isdir(root_path+"strategy_"+self.strategy+"/"): 75 | os.makedirs(root_path+"strategy_"+self.strategy+"/") 76 | self.f_error = open(root_path+"strategy_"+self.strategy+'/error_realtime.txt','w',encoding='utf-8') 77 | self.f_wrong = open(root_path+"strategy_"+self.strategy+'/wrong_realtime.txt','w',encoding='utf-8') 78 | 79 | def make_strategy_runcount(self,run_count,root_path): 80 | self.path=root_path+"strategy_"+self.strategy+"/"+str(run_count)+"/" 81 | if not os.path.isdir(self.path): 82 | os.makedirs(self.path) 83 | if not os.path.isdir(self.path+"screen/"): 84 | os.makedirs(self.path+"screen/") 85 | self.f_read_trace = open(self.path+'/read_trace.txt','w',encoding='utf-8') 86 | self.f_trace = open(self.path+'/trace.txt','w',encoding='utf-8') 87 | 88 | self.error_event_lists = [] 89 | self.wrong_event_lists = [] 90 | self.wrong_flag = True 91 | 92 | def connect(self): 93 | self.use = u2.connect_usb(self.device_serial) 94 | self.use.implicitly_wait(5.0) 95 | 96 | def install_app(self,app): 97 | print(app) 98 | subprocess.run(["adb","-s",self.device_serial,"install",app], stdout=subprocess.PIPE) 99 | 100 | def initialization(self): 101 | self.use.set_orientation("n") 102 | 103 | def initial_setting(self): 104 | print("initial setting") 105 | 106 | def screenshot_and_getstate(self,path,event_count): 107 | self.screenshot_path = path+str(event_count)+'_'+self.device_serial+'.png' 108 | self.use.screenshot(path+str(event_count)+'_'+self.device_serial+'.png') 109 | xml = self.use.dump_hierarchy() 110 | f = open(path+str(event_count)+'_'+self.device_serial+'.xml','w',encoding='utf-8') 111 | f.write(xml) 112 | f = open(path+str(event_count)+'_'+self.device_serial+'.xml','r',encoding='utf-8') 113 | lines=f.readlines() 114 | f.close() 115 | return lines 116 | 117 | def update_state(self,state): 118 | self.last_state=self.state 119 | self.state=state 120 | 121 | def stop_app(self,app): 122 | self.use.app_stop(app.package_name) 123 | 124 | def clear_app(self,app,is_login_app): 125 | if is_login_app == 0 : 126 | self.use.app_stop(app.package_name) 127 | else: 128 | self.use.app_clear(app.package_name) 129 | 130 | def start_app(self,app): 131 | self.use.app_start(app.package_name) 132 | subprocess.run(["adb","-s",self.device_serial,"shell","am","start","-n",app.package_name+"/"+app.main_activity], stdout=subprocess.PIPE) 133 | # self.use.app_wait(app.package_name, front=True, timeout=2.0) 134 | return True 135 | 136 | def click(self,view,strategy_list): 137 | try: 138 | if self.strategy != "language": 139 | if view.description!="": 140 | self.use(description=view.description,packageName=view.package).click() 141 | return "description" 142 | elif view.text!="": 143 | self.use(text=view.text,packageName=view.package).click() 144 | return "text" 145 | elif view.instance == 0: 146 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).click() 147 | return "classNameresourceId" 148 | else: 149 | self.use.click(view.x ,view.y) 150 | return "xy" 151 | elif view.instance == 0: 152 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).click() 153 | return "classNameresourceId" 154 | else: 155 | self.use.click(view.x ,view.y) 156 | return "xy" 157 | except: 158 | self.use.click(view.x ,view.y) 159 | return "xy" 160 | 161 | def longclick(self,view,strategy_list): 162 | try: 163 | if self.strategy != "language": 164 | if view.description!="": 165 | self.use(description=view.description,packageName=view.package).long_click() 166 | return 167 | elif view.text!="": 168 | self.use(text=view.text,packageName=view.package).long_click() 169 | return 170 | elif view.instance == 0: 171 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).long_click() 172 | else: 173 | self.use.long_click(view.x ,view.y) 174 | elif view.instance == 0: 175 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).long_click() 176 | else: 177 | self.use.long_click(view.x ,view.y) 178 | except: 179 | self.use.long_click(view.x ,view.y) 180 | # print("x:"+str(view.x)+",y:"+str(view.y)) 181 | return 182 | 183 | def edit(self,view,strategy_list,text): 184 | if "language" not in strategy_list: 185 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).set_text(text) 186 | else: 187 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).set_text(text) 188 | 189 | def scroll(self,view,strategy_list): 190 | if view.action == "scroll_backward": 191 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).scroll.vert.backward(steps=100) 192 | elif view.action == "scroll_forward": 193 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).scroll.vert.forward(steps=100) 194 | elif view.action == "scroll_right": 195 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).scroll.horiz.toEnd(max_swipes=10) 196 | elif view.action == "scroll_left": 197 | self.use(className=view.className,resourceId=view.resourceId,packageName=view.package).scroll.horiz.toBeginning(max_swipes=10) 198 | 199 | def close_keyboard(self): 200 | subprocess.run(["adb","-s",self.device_serial,"shell","input","keyevent","111"], stdout=subprocess.PIPE) 201 | 202 | def add_file(self,resource_path,resource,path): 203 | subprocess.run(["adb","-s",self.device_serial,"logcat","-c"], stdout=subprocess.PIPE) 204 | subprocess.run(["adb","-s",self.device_serial,"push",resource_path+"/"+resource,path], stdout=subprocess.PIPE) 205 | 206 | def log_crash(self,path): 207 | os.popen("adb -s "+self.device_serial+" logcat -b crash >"+path) 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /Tool/SetDroid/event.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import hashlib 4 | import time 5 | 6 | class Event(object): 7 | 8 | def __init__(self, view, action, device, event_count): 9 | self.view = view 10 | self.action = action 11 | self.device = device 12 | self.text = "None" 13 | self.count=0 14 | self.event_count=event_count 15 | 16 | def set_device(self,device): 17 | self.device = device 18 | 19 | def set_text(self,text): 20 | self.text = text 21 | 22 | def set_count(self,count): 23 | self.count = count 24 | 25 | def print_event(self): 26 | print("Event start=============================") 27 | print("Event_count:"+str(self.event_count)) 28 | if self.view is not None: 29 | print("View_text:"+self.view.line) 30 | print("Action:"+self.action) 31 | print("Device:"+self.device.device_serial) 32 | if self.text is not None: 33 | print("Text:"+self.text) 34 | print("Event end=============================") 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Tool/SetDroid/executor.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import logging 4 | import subprocess 5 | import time 6 | from device import Device 7 | from app import App 8 | from injector import Injector 9 | from policy import Policy,RandomPolicy 10 | import uiautomator2 as u2 11 | from state import State 12 | from checker import Checker 13 | from event import Event 14 | import random 15 | from view import View 16 | from utils import Utils 17 | 18 | class Executor(object): 19 | 20 | def __init__(self,devices,app,strategy_list,pro_click,pro_longclick,pro_scroll, 21 | pro_home,pro_edit,pro_naturalscreen,pro_leftscreen,pro_back,pro_splitscreen,emulator_path,android_system, 22 | root_path,resource_path,testcase_count,start_testcase_count,event_num,timeout,policy_name, 23 | setting_random_denominator,serial_or_parallel,emulator_name,is_login_app,rest_interval,trace_path,choice): 24 | 25 | self.policy_name=policy_name 26 | self.timeout = timeout 27 | self.pro_click = pro_click 28 | self.pro_longclick = pro_longclick 29 | self.pro_scroll = pro_scroll 30 | self.pro_home = pro_home 31 | self.pro_edit = pro_edit 32 | self.pro_naturalscreen = pro_naturalscreen 33 | self.pro_leftscreen = pro_leftscreen 34 | self.pro_back = pro_back 35 | self.pro_splitscreen = pro_splitscreen 36 | self.app = app 37 | self.devices = devices 38 | self.emulator_path = emulator_path 39 | self.android_system = android_system 40 | self.resource_path = resource_path 41 | self.strategy_list = strategy_list 42 | self.testcase_count = testcase_count 43 | self.start_testcase_count = start_testcase_count 44 | self.event_num = event_num 45 | self.setting_random_denominator = setting_random_denominator 46 | self.root_path = root_path 47 | self.policy = self.get_policy() 48 | self.serial_or_parallel = serial_or_parallel 49 | self.emulator_name = emulator_name 50 | self.is_login_app = is_login_app 51 | self.rest_interval = rest_interval 52 | self.guest_devices=self.devices[1:len(self.devices)] 53 | self.trace_path=trace_path 54 | self.choice=choice 55 | self.deduplicate_list1=[] 56 | self.deduplicate_list2=[] 57 | self.injector = Injector(devices=devices, 58 | app=app, 59 | strategy_list=strategy_list, 60 | emulator_path=emulator_path, 61 | android_system=android_system, 62 | root_path=root_path, 63 | resource_path=resource_path, 64 | testcase_count=testcase_count, 65 | event_num=event_num, 66 | timeout=timeout, 67 | setting_random_denominator=setting_random_denominator, 68 | rest_interval=rest_interval, 69 | choice=choice) 70 | 71 | self.checker = Checker(devices=devices, 72 | app=app, 73 | strategy_list=strategy_list, 74 | emulator_path=emulator_path, 75 | android_system=android_system, 76 | root_path=root_path, 77 | resource_path=resource_path, 78 | testcase_count=testcase_count, 79 | event_num=event_num, 80 | timeout=timeout, 81 | setting_random_denominator=setting_random_denominator, 82 | rest_interval=rest_interval, 83 | choice=self.choice) 84 | 85 | self.utils = Utils(devices=devices) 86 | 87 | def get_policy(self): 88 | if self.policy_name=="random": 89 | print("Policy: Random") 90 | policy = RandomPolicy(self.devices,self.app,self.emulator_path,self.android_system,self.root_path, 91 | self.pro_click,self.pro_longclick,self.pro_scroll,self.pro_edit,self.pro_naturalscreen,self.pro_leftscreen,self.pro_back,self.pro_splitscreen,self.pro_home) 92 | else: 93 | print("No valid input policy specified. Using policy \"none\".") 94 | policy = None 95 | return policy 96 | 97 | def execute_event(self,device,event,num): 98 | try: 99 | have_view_action=["click","longclick","edit","scroll"] 100 | feature="" 101 | if event.action in have_view_action and event.view is not None: 102 | if device.use(resourceId=event.view.resourceId).count==0 or device.use(className=event.view.className).count==0: 103 | lines = device.use.dump_hierarchy() 104 | if (event.view.resourceId != "" and event.view.resourceId not in lines) or (event.view.className != "" and event.view.className not in lines): 105 | return False 106 | 107 | if event.action.startswith("setting_"): 108 | self.injector.replay_setting(event,self.strategy_list) 109 | elif event.action == "check_setting_request": 110 | self.checker.check_setting_request() 111 | elif event.action == "click": 112 | feature=device.click(event.view,self.strategy_list) 113 | elif event.action == "longclick": 114 | device.longclick(event.view,self.strategy_list) 115 | elif event.action == "edit": 116 | device.edit(event.view,self.strategy_list,event.text) 117 | elif event.action == "scroll": 118 | device.scroll(event.view,self.strategy_list) 119 | elif event.action == "back": 120 | device.use.press("back") 121 | elif event.action == "home": 122 | device.use.press("home") 123 | elif event.action == "naturalscreen": 124 | device.use.set_orientation("n") 125 | elif event.action == "leftscreen": 126 | device.use.set_orientation("l") 127 | elif event.action == "start": 128 | device.start_app(self.app) 129 | elif event.action == "stop": 130 | device.stop_app(self.app) 131 | elif event.action == "clear": 132 | device.clear_app(self.app,self.is_login_app) 133 | elif event.action == "restart": 134 | device.restart(self.emulator_path,self.emulator_name) 135 | else: 136 | self.injector.replay_setting(event,self.strategy_list) 137 | 138 | print(device.device_serial+":"+feature+":end execute\n") 139 | time.sleep(self.rest_interval*1) 140 | return True 141 | except Exception as ex: 142 | if num ==0: 143 | print(ex) 144 | return self.execute_event(device,event,1) 145 | else: 146 | print(ex) 147 | return False 148 | 149 | def replay(self,strategy): 150 | 151 | #init 152 | self.injector.init_setting() 153 | action_list = ["click","long_click","edit"] 154 | self.devices[1].set_strategy(strategy) 155 | path = os.path.join(self.root_path, "strategy_"+strategy) 156 | self.error_path = os.path.join(path, "error_replay") 157 | self.utils.create_dir(self.error_path) 158 | self.f_replay_record = open(os.path.join(path, "error_replay.txt"),'w',encoding='utf-8') 159 | self.error_event_lists = [] 160 | if not os.path.exists(os.path.join(path, "error_realtime.txt")): 161 | print("You should run first before replaying!") 162 | return 163 | self.f_replay = open(os.path.join(path, "error_realtime.txt"),'r',encoding='utf-8') 164 | lines = self.f_replay.readlines() 165 | 166 | for line in lines: 167 | 168 | self.error_event_lists.append(line) 169 | if "Start::" in line: 170 | #init dir for each error 171 | print("Start") 172 | record_flag = False 173 | linelist = line.split("::") 174 | self.utils.create_dir(os.path.join(self.error_path, linelist[1])) 175 | self.screen_path = os.path.join(self.error_path, linelist[1], "screen/") 176 | self.utils.create_dir(self.screen_path) 177 | f_read_trace = open(os.path.join(self.error_path, linelist[1], "read_trace.txt"),'w',encoding='utf-8') 178 | print(self.screen_path) 179 | elif "End::" in line: 180 | #end 181 | if record_flag == True: 182 | for theline in self.error_event_lists: 183 | self.f_replay_record.write(theline) 184 | self.f_replay_record.flush() 185 | self.error_event_lists = [] 186 | record_flag = False 187 | f_read_trace.close() 188 | self.utils.generate_html(os.path.join(self.error_path, linelist[1]),os.path.join(self.error_path, linelist[1]),linelist[1]) 189 | elif line.strip() !="": 190 | print("-----------------------"+'\n'+line) 191 | f_read_trace.write(line) 192 | f_read_trace.flush() 193 | #replay each event 194 | crash_info=self.checker.check_crash() 195 | if crash_info is not None: 196 | self.f_replay_record.write(crash_info) 197 | self.f_replay_record.flush() 198 | elementlist = line.split("::") 199 | if len(elementlist)<5: 200 | continue 201 | event = self.get_replay_event(elementlist,line) 202 | event.print_event() 203 | if elementlist[1] == "save_state": 204 | self.save_state(event.device.device_num,self.screen_path,elementlist[0],self.f_replay_record) 205 | else: 206 | if event.action in action_list: 207 | self.utils.draw_event(event) 208 | args=(event.device,event,0) 209 | self.devices[event.device.device_num].set_thread(self.execute_event,args) 210 | if event.device.device_num == 1: 211 | self.utils.start_thread() 212 | for device in self.devices: 213 | if device.thread is not None: 214 | success_flag = device.thread.get_result() 215 | if not success_flag and not self.checkduplicate(): 216 | print("write error") 217 | record_flag=True 218 | self.utils.draw_event(event) 219 | self.utils.draw_error_frame() 220 | device.set_thread(None,None) 221 | time.sleep(self.rest_interval*1) 222 | if "device1" in line and "save_state" in line and self.devices[0].state is not None and self.devices[1].state is not None and not self.devices[0].state.same(self.devices[1].state): 223 | print("different!") 224 | event = Event(None, "wrong", self.devices[1], elementlist[0]) 225 | self.utils.draw_event(event) 226 | if "::start::" in line: 227 | self.checker.check_start(0,strategy) 228 | 229 | def get_replay_event(self,elementlist,line): 230 | view =None 231 | if elementlist[4].strip()!="None": 232 | view=View(elementlist[4],None,[]) 233 | if elementlist[2] == "device0": 234 | event = Event(view, elementlist[1], self.devices[0], elementlist[0]) 235 | elif elementlist[2] == "device1": 236 | event = Event(view, elementlist[1], self.devices[1], elementlist[0]) 237 | else: 238 | print(line+"error") 239 | return event 240 | 241 | def start_app(self,event_count): 242 | for device in self.devices: 243 | args=(self.app,) 244 | device.set_thread(device.start_app,args) 245 | self.utils.start_thread() 246 | 247 | for device in self.guest_devices: 248 | self.utils.write_read_event("::start::all devices::None::None"+'\n',event_count,None,"all devices",device.device_num) 249 | event = Event(None, "start", device, event_count) 250 | self.utils.write_event(event,device.device_num,device.f_trace) 251 | self.utils.draw_event(event) 252 | 253 | def clear_app(self,event_count): 254 | for device in self.devices: 255 | device.clear_app(self.app,self.is_login_app) 256 | device.use.set_orientation("n") 257 | 258 | for device in self.guest_devices: 259 | device.error_event_lists.clear() 260 | device.wrong_event_lists.clear() 261 | device.wrong_flag=True 262 | self.utils.write_read_event("::clear::all devices::None::None"+'\n',event_count,None,"all devices",device.device_num) 263 | event = Event(None, "clear", device, event_count) 264 | self.utils.write_event(event,device.device_num,device.f_trace) 265 | self.utils.draw_event(event) 266 | 267 | def clear_and_restart_app(self,event_count,strategy): 268 | for device in self.devices: 269 | device.clear_app(self.app,self.is_login_app) 270 | device.use.set_orientation("n") 271 | for device in self.guest_devices: 272 | device.error_event_lists.clear() 273 | device.wrong_event_lists.clear() 274 | device.wrong_flag=True 275 | event = Event(None, "naturalscreen", device, event_count) 276 | self.utils.write_event(event,device.device_num,device.f_trace) 277 | event = Event(None, "clear", device, event_count) 278 | event_count = self.write_draw_and_save_all(device,event,event_count) 279 | 280 | if event_count>3: 281 | event=self.injector.change_setting_after_run(event_count,strategy) 282 | if event is not None: 283 | event_count = self.write_draw_and_save_one(event,event_count) 284 | 285 | #check keyboard 286 | self.checker.check_keyboard() 287 | 288 | for device in self.devices: 289 | args=(self.app,) 290 | device.set_thread(device.start_app,args) 291 | self.utils.start_thread() 292 | self.checker.check_start(0,strategy) 293 | 294 | for device in self.guest_devices: 295 | event = Event(None, "start", device, event_count) 296 | event_count = self.write_draw_and_save_all(device,event,event_count) 297 | 298 | event=self.injector.change_setting_before_run(event_count,strategy) 299 | 300 | if event is not None: 301 | event_count = self.write_draw_and_save_one(event,event_count) 302 | return event_count 303 | 304 | def back_to_app(self,event_count,strategy): 305 | for device in self.devices: 306 | device.use.press("back") 307 | print("Back") 308 | time.sleep(self.rest_interval*1) 309 | if not self.checker.check_foreground(): 310 | for device in self.devices: 311 | device.stop_app(self.app) 312 | args=(self.app,) 313 | device.set_thread(device.start_app,args) 314 | self.utils.start_thread() 315 | self.checker.check_start(1,strategy) 316 | 317 | for device in self.guest_devices: 318 | self.utils.write_read_event("::restart::all devices::None::None"+'\n',event_count,None,"all devices",device.device_num) 319 | event = Event(None, "back", device, event_count) 320 | self.utils.write_event(event,device.device_num,device.f_trace) 321 | event = Event(None, "home", device, event_count) 322 | self.utils.write_event(event,device.device_num,device.f_trace) 323 | event = Event(None, "start", device, event_count) 324 | self.utils.write_event(event,device.device_num,device.f_trace) 325 | self.utils.draw_event(event) 326 | else: 327 | for device in self.guest_devices: 328 | self.utils.write_read_event("::back::all devices::None::None"+'\n',event_count,None,"all devices",device.device_num) 329 | event = Event(None, "back", device, event_count) 330 | self.utils.write_event(event,device.device_num,device.f_trace) 331 | self.utils.draw_event(event) 332 | 333 | def save_all_state(self,event_count): 334 | time.sleep(self.rest_interval*1) 335 | for device in self.guest_devices: 336 | self.save_state(0,device.path+"screen/",event_count,device.f_trace) 337 | self.save_state(device.device_num,device.path+"screen/",event_count,device.f_trace) 338 | event = Event(None, "save_state", device, event_count) 339 | event.set_count(device.device_num) 340 | self.utils.write_event(event,device.device_num,device.f_trace) 341 | return event_count+1 342 | 343 | def update_all_state(self,event_count): 344 | event_count=event_count-1 345 | time.sleep(self.rest_interval*1) 346 | for device in self.guest_devices: 347 | self.update_state(0,device.path+"screen/",event_count,device.f_trace) 348 | self.update_state(device.device_num,device.path+"screen/",event_count,device.f_trace) 349 | event_count=event_count+1 350 | 351 | def save_state(self,device_count,path,event_count,f_trace): 352 | #get and save state of all devices 353 | lines = self.devices[device_count].screenshot_and_getstate(path,event_count) 354 | state = State(lines) 355 | self.devices[device_count].update_state(state) 356 | 357 | def update_state(self,device_count,path,event_count,f_trace): 358 | lines = self.devices[device_count].use.dump_hierarchy().splitlines() 359 | state = State(lines) 360 | self.devices[device_count].update_state(state) 361 | if self.devices[device_count].last_state != self.devices[device_count].state: 362 | self.save_state(device_count,path,event_count,f_trace) 363 | 364 | def restart_devices(self,event_count): 365 | print("restart_devices") 366 | for device in self.devices: 367 | if device.is_emulator == 0: 368 | device.restart(self.emulator_path,self.emulator_name) 369 | for device in self.guest_devices: 370 | device.connect() 371 | device.error_event_lists.clear() 372 | device.wrong_event_lists.clear() 373 | device.wrong_flag=True 374 | self.utils.write_read_event("::restart::all devices::None::None"+'\n',event_count,None,"all devices",device.device_num) 375 | event = Event(None, "restart", device, event_count) 376 | self.utils.write_event(event,device.device_num,device.f_trace) 377 | self.utils.draw_event(event) 378 | 379 | def wait_load(self,event_count): 380 | wait_time=self.checker.check_loading() 381 | if wait_time>0: 382 | event_count=event_count-1 383 | event_count=self.save_all_state(event_count) 384 | 385 | def write_draw_and_save_one(self,event,event_count): 386 | self.utils.write_read_event(None,event_count,event,"all device",event.device.device_num) 387 | self.utils.write_one_device_event(event,event.device.device_num,event.device.f_trace) 388 | self.utils.draw_event(event) 389 | event_count=self.save_all_state(event_count) 390 | return event_count 391 | 392 | def write_draw_and_save_all(self,device,event,event_count): 393 | self.utils.write_read_event(None,event_count,event,"all device",event.device.device_num) 394 | self.utils.write_event(event,device.device_num,device.f_trace) 395 | self.utils.draw_event(event) 396 | event_count=self.save_all_state(event_count) 397 | return event_count 398 | 399 | def read_event(self,line,event_count): 400 | eventlist=line.split("::") 401 | action=eventlist[1] 402 | if eventlist[4] != "None\n": 403 | view=View(eventlist[4],None,[]) 404 | event = Event(view, action, self.devices[0], event_count) 405 | else: 406 | event = Event(None, action, self.devices[0], event_count) 407 | return event 408 | 409 | def test(self): 410 | for device in self.guest_devices: 411 | self.checker.check_language(self.root_path+"/strategy_language/") 412 | 413 | def checkduplicate(self): 414 | for screen in self.deduplicate_list1: 415 | if self.devices[0].state.same(screen): 416 | return True 417 | for screen in self.deduplicate_list2: 418 | if self.devices[1].state.same(screen): 419 | return True 420 | self.deduplicate_list1.append(self.devices[0].state) 421 | self.deduplicate_list2.append(self.devices[1].state) 422 | return False 423 | 424 | def start(self,strategy): 425 | #if execute serial, init the strategy of device1, otherwise, init all the guest devices' strategies 426 | if self.serial_or_parallel == 0: 427 | self.devices[0].use.press("home") 428 | self.devices[1].use.press("home") 429 | self.devices[0].set_strategy(strategy) 430 | self.devices[1].set_strategy(strategy) 431 | self.devices[1].make_strategy(self.root_path) 432 | else: 433 | for device in self.guest_devices: 434 | device.set_strategy(self.strategy_list[device.device_num-1]) 435 | device.make_strategy(self.root_path) 436 | 437 | run_count=self.start_testcase_count 438 | while run_count < self.testcase_count: 439 | 440 | #create folder of new run 441 | run_count=run_count+1 442 | for device in self.guest_devices: 443 | device.use.press("back") 444 | device.use.press("back") 445 | device.make_strategy_runcount(run_count,self.root_path) 446 | 447 | #init setting 448 | event_count=0.0 449 | event_count=self.save_all_state(event_count) 450 | self.injector.init_setting() 451 | 452 | #clear and start app 453 | event_count=self.clear_and_restart_app(event_count,strategy) 454 | 455 | while event_count0: 104 | event_view_num = random.randint(0,len(views)-1) 105 | event_view = views[event_view_num] 106 | event = Event(event_view, "click", device,event_count) 107 | else: 108 | # print("re_choice") 109 | event = self.choice_event(device,event_count) 110 | elif event_type0: 121 | event_view_num = random.randint(0,len(views)-1) 122 | event_view = views[event_view_num] 123 | event = Event(event_view, "longclick", device,event_count) 124 | else: 125 | # print("re_choice") 126 | event = self.choice_event(device,event_count) 127 | elif event_type0: 138 | event_view_num = random.randint(0,len(views)-1) 139 | event_view = views[event_view_num] 140 | direction_list = ["backward","forward","right","left"] 141 | direction_num = random.randint(0,len(direction_list)-1) 142 | event = Event(event_view, "scroll_"+direction_list[direction_num], device,event_count) 143 | else: 144 | # print("re_choice") 145 | event = self.choice_event(device,event_count) 146 | elif event_type0: 156 | event_view_num = random.randint(0,len(views)-1) 157 | event_view = views[event_view_num] 158 | event = Event(event_view, "edit", device,event_count) 159 | text = self.random_text() 160 | event.set_text(text) 161 | else: 162 | # print("re_choice") 163 | event = self.choice_event(device,event_count) 164 | elif event_type 0: 155 | self.timer = Timer(self.timeout, self.stop) 156 | self.timer.start() 157 | self.start = time.time() 158 | 159 | #connect device and install app 160 | if self.is_login_app != 0: 161 | for device in self.devices: 162 | device.connect() 163 | device.install_app(self.app.app_path) 164 | else: 165 | for device in self.devices: 166 | device.restart(self.emulator_path,self.emulator_name) 167 | device.connect() 168 | 169 | #add some files to the devices 170 | resourcelist=os.listdir(self.resource_path) 171 | for device in self.devices: 172 | device.log_crash(self.root_path+"/"+device.device_serial+"_logcat.txt") 173 | for resource in resourcelist: 174 | device.add_file(self.resource_path,resource,"/sdcard") 175 | 176 | if self.choice == 0: #run 177 | if self.serial_or_parallel == 0: 178 | for strategy in self.strategy_list: 179 | try: 180 | self.executor.start(strategy) 181 | except: 182 | continue 183 | else: 184 | self.executor.start(0) 185 | utils = Utils(self.devices) 186 | # utils.generate_outline_html(self.app.output_path, self.strategy_list) 187 | elif self.choice == 1: #replay 188 | for strategy in self.strategy_list: 189 | self.executor.replay(strategy) 190 | utils = Utils(self.devices) 191 | # utils.generate_replay_all_html(self.app.output_path, self.strategy_list) 192 | elif self.choice == 2: #test 193 | self.executor.test() 194 | else: #screenshot 195 | for device in self.devices: 196 | device.screenshot_and_getstate(self.root_path,7) 197 | 198 | def resize(self,path): 199 | from cv2 import cv2 200 | image = cv2.imread(path) 201 | (h, w) = image.shape[:2] 202 | if w > 2000: 203 | (cX, cY) = (w // 2, h // 2) 204 | M = cv2.getRotationMatrix2D((cX, cY), -90, 1.0) 205 | import numpy as np 206 | cos = np.abs(M[0, 0]) 207 | sin = np.abs(M[0, 1]) 208 | nW = int((h * sin) + (w * cos)) 209 | nH = int((h * cos) + (w * sin)) 210 | M[0, 2] += (nW / 2) - cX 211 | M[1, 2] += (nH / 2) - cY 212 | image = cv2.warpAffine(image, M, (nW, nH)) 213 | image=cv2.resize(image, (256, 512)) 214 | cv2.imwrite(path, image) 215 | elif w > 512: 216 | image=cv2.resize(image, (256, 512)) 217 | cv2.imwrite(path, image) 218 | 219 | 220 | def stop(self): 221 | for root, dirs, files in os.walk(self.root_path, topdown=False): 222 | for name in files: 223 | if ".png" in name: 224 | image_path=os.path.join(root, name) 225 | self.resize(image_path) 226 | self.enabled = False 227 | end = time.time() 228 | print(end-self.start) 229 | if self.timer and self.timer.isAlive(): 230 | self.timer.cancel() 231 | 232 | 233 | 234 | -------------------------------------------------------------------------------- /Tool/SetDroid/start.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from setdroid import SetDroid 3 | 4 | def parse_args(): 5 | """ 6 | parse command line input 7 | """ 8 | parser = argparse.ArgumentParser(description="Start SetDroid to detect setting issues.", 9 | formatter_class=argparse.RawTextHelpFormatter) 10 | parser.add_argument("-pro_click", action="store", dest="pro_click", required=False, 11 | help="The percentage of click event", default=45) 12 | parser.add_argument("-pro_longclick", action="store", dest="pro_longclick", required=False, 13 | help="The percentage of click event", default=25) 14 | parser.add_argument("-pro_scroll", action="store", dest="pro_scroll", required=False, 15 | help="The percentage of click event", default=5) 16 | parser.add_argument("-pro_home", action="store", dest="pro_home", required=False, 17 | help="The percentage of click event", default=0) 18 | parser.add_argument("-pro_edit", action="store", dest="pro_edit", required=False, 19 | help="The percentage of click event", default=15) 20 | parser.add_argument("-pro_naturalscreen", action="store", dest="pro_naturalscreen", required=False, 21 | help="The percentage of click event", default=1) 22 | parser.add_argument("-pro_leftscreen", action="store", dest="pro_leftscreen", required=False, 23 | help="The percentage of click event", default=80) 24 | parser.add_argument("-pro_back", action="store", dest="pro_back", required=False, 25 | help="The percentage of click event", default=1) 26 | parser.add_argument("-pro_splitscreen", action="store", dest="pro_splitscreen", required=False, 27 | help="The percentage of click event", default=0) 28 | parser.add_argument("-app_path", action="store", dest="app_path", required=True, 29 | help="The path of the application you want to test") 30 | parser.add_argument("-is_emulator", action="store", dest="is_emulator", required=False, default=0, type=int, 31 | help="Whether the devices are emulators") 32 | parser.add_argument('-append_device', action='append', dest="append_device", required=True, 33 | help="Serial of the device") 34 | parser.add_argument('-serial_or_parallel', action='store', dest="serial_or_parallel", required=False, default=0, type=int, 35 | help="0 is serial, 1 is parallel") 36 | parser.add_argument("-append_strategy", action="append", dest="strategy_list", required=False, 37 | help="Selected strategy") 38 | parser.add_argument("-choice", action="store", dest="choice", required=False, default=0, type=int, 39 | help="Run or replay") 40 | parser.add_argument("-emulator_path", action="store", dest="emulator_path", required=False, default="E:\\Sdk\\emulator\\emulator.exe", 41 | help="Emulator path") 42 | parser.add_argument("-android_system", action="store", dest="android_system", required=False, default="emulator8", 43 | help="System of the device") 44 | parser.add_argument("-root_path", action="store", dest="root_path", required=False, default="../Output/", 45 | help="Output path") 46 | parser.add_argument("-emulator_name", action="store", dest="emulator_name", required=False, default="Android8.0", 47 | help="the emulator name") 48 | parser.add_argument("-resource_path", action="store", dest="resource_path", required=False, default="Document/", 49 | help="Resource path") 50 | parser.add_argument("-testcase_count", action="store", dest="testcase_count", required=False, default=10, type=int, 51 | help="How many testcases are generated for each strategy") 52 | parser.add_argument("-start_testcase", action="store", dest="start_testcase_count", required=False, default=0, type=int, 53 | help="start from which testcase") 54 | parser.add_argument("-event_num", action="store", dest="event_num", required=False, default=100, type=int, 55 | help="How many events are in each test case") 56 | parser.add_argument("-timeout", action="store", dest="timeout", required=False, default=-1, type=int, 57 | help="How long to run at most") 58 | parser.add_argument("-policy_name", action="store", dest="policy_name", required=False, default="random", 59 | help="Policy name") 60 | parser.add_argument("-setting_random_denominator", action="store", dest="setting_random_denominator", required=False, default=5, type=int, 61 | help="Setting random denominator") 62 | parser.add_argument("-app_name", action="store", dest="app_name", required=False, 63 | help="App name") 64 | parser.add_argument("-is_login_app", action="store", dest="is_login_app", required=False, default=0, type=int, 65 | help="0 = app needed to login, 1 = app did not need to login") 66 | parser.add_argument("-rest_interval", action="store", dest="rest_interval", required=False, default=1, type=int, 67 | help="time to sleep") 68 | parser.add_argument("-trace_path", action="store", dest="trace_path", required=False, default="../Trace", 69 | help="path of traces") 70 | 71 | options = parser.parse_args() 72 | # print options 73 | return options 74 | 75 | def main(): 76 | opts = parse_args() 77 | import os 78 | if not os.path.exists(opts.app_path): 79 | print("APK does not exist.") 80 | return 81 | 82 | if len(opts.append_device)<2: 83 | print("You need to define at least two devices") 84 | return 85 | 86 | if len(opts.strategy_list)+1!=len(opts.append_device) and opts.serial_or_parallel == 1: 87 | print("You need n+1 devices to execute n strategies") 88 | return 89 | 90 | if len(opts.append_device)>2 and opts.serial_or_parallel ==0: 91 | print("You can only execute serial strategy in 2 device") 92 | return 93 | 94 | setdroid = SetDroid( 95 | pro_click=opts.pro_click, 96 | pro_longclick=opts.pro_longclick, 97 | pro_scroll=opts.pro_scroll, 98 | pro_home=opts.pro_home, 99 | pro_edit=opts.pro_edit, 100 | pro_naturalscreen=opts.pro_naturalscreen, 101 | pro_leftscreen=opts.pro_leftscreen, 102 | pro_back=opts.pro_back, 103 | pro_splitscreen=opts.pro_splitscreen, 104 | app_path=opts.app_path, 105 | is_emulator=opts.is_emulator, 106 | devices_serial=opts.append_device, 107 | choice=opts.choice, 108 | emulator_path=opts.emulator_path, 109 | android_system=opts.android_system, 110 | root_path=opts.root_path, 111 | resource_path=opts.resource_path, 112 | strategy_list=opts.strategy_list, 113 | testcase_count=opts.testcase_count, 114 | start_testcase_count=opts.start_testcase_count, 115 | event_num=opts.event_num, 116 | timeout=opts.timeout, 117 | policy_name=opts.policy_name, 118 | setting_random_denominator=opts.setting_random_denominator, 119 | serial_or_parallel=opts.serial_or_parallel, 120 | app_name=opts.app_name, 121 | emulator_name=opts.emulator_name, 122 | is_login_app=opts.is_login_app, 123 | rest_interval=opts.rest_interval, 124 | trace_path=opts.trace_path 125 | ) 126 | setdroid.start() 127 | setdroid.stop() 128 | 129 | if __name__ == "__main__": 130 | main() -------------------------------------------------------------------------------- /Tool/SetDroid/state.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import hashlib 4 | from view import View 5 | """ 6 | Record the information of the app's state 7 | """ 8 | class State(object): 9 | 10 | def __init__(self, lines): 11 | self.lines=lines 12 | self.classname_list = [] 13 | self.resourceid_list = [] 14 | self.num_list = [] 15 | self.all_views=self.get_view() 16 | self.views=[] 17 | for view in self.all_views: 18 | if view.level == 2: 19 | self.views.append(view) 20 | 21 | def same_but_not_language(self,state): 22 | for view in self.views: 23 | if "com.google.android.inputmethod.latin" not in view.line and "com.android.systemui" not in view.line: #Decrease accuracy 24 | flag = False 25 | for view2 in state.views: 26 | if view.same_but_not_language(view2): 27 | flag=True 28 | break 29 | if flag==False: 30 | return False 31 | return True 32 | 33 | def same(self,state): 34 | for view in self.views: 35 | if "com.google.android.inputmethod.latin" not in view.line and "com.android.systemui" not in view.line: #Decrease accuracy 36 | flag = False 37 | for view2 in state.views: 38 | if view.same(view2): 39 | flag=True 40 | break 41 | if flag==False: 42 | return False 43 | return True 44 | 45 | def get_instance(self,view): 46 | #get_instance 47 | flag = False 48 | i=0 49 | while i < len(self.classname_list): 50 | if self.classname_list[i] == view.className and self.resourceid_list[i] == view.resourceId: 51 | flag = True 52 | self.num_list[i]=self.num_list[i]+1 53 | view.set_instance(self.num_list[i]) 54 | break 55 | i=i+1 56 | if flag == False: 57 | self.classname_list.append(view.className) 58 | self.resourceid_list.append(view.resourceId) 59 | self.num_list.append(0) 60 | view.set_instance(0) 61 | return view 62 | 63 | def get_view(self): 64 | all_views=[] 65 | stack=[] 66 | for line in self.lines: 67 | if '' in line: 68 | if len(stack)==0: 69 | view=View(line,None,[]) 70 | else: 71 | view=View(line,stack[len(stack)-1],[]) 72 | stack[len(stack)-1].add_son(view) 73 | view = self.get_instance(view) 74 | all_views.append(view) 75 | elif '' in line: 83 | view=stack[len(stack)-1] 84 | stack.pop() 85 | view = self.get_instance(view) 86 | all_views.append(view) 87 | if len(stack)>0: 88 | stack[len(stack)-1].add_son(view) 89 | 90 | return all_views 91 | 92 | -------------------------------------------------------------------------------- /Tool/SetDroid/style.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Tool/SetDroid/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import hashlib 4 | from view import View 5 | from event import Event 6 | import re 7 | 8 | class Utils(object): 9 | 10 | def __init__(self, devices): 11 | self.devices=devices 12 | 13 | def write_error(self,fail_device,run_count,event_list,f_write,num): 14 | # print("write_error") 15 | event_count=0 16 | f_write.write("Start::"+str(num+1)+"::run_count::"+str(run_count)+'\n') 17 | for event in event_list: 18 | event_count=event_count+1 19 | if event.view is not None: 20 | f_write.write(str(event.event_count)+"::"+event.action+"::device"+str(event.device.device_num)+"::"+event.text+"::"+event.view.line) 21 | else: 22 | f_write.write(str(event.event_count)+"::"+event.action+"::device"+str(event.device.device_num)+"::"+event.text+"::None::"+'\n') 23 | f_write.flush 24 | f_write.write("End::"+'\n'+'\n') 25 | f_write.flush() 26 | 27 | def write_read_event(self,string,event_count,event,device_string,device_count): 28 | f_read_trace=self.devices[device_count].f_read_trace 29 | if string is not None: 30 | f_read_trace.write(str(event_count)+string) 31 | else: 32 | if event.view is not None: 33 | f_read_trace.write(str(event_count)+"::"+event.action+"::"+device_string+"::"+event.text+"::"+event.view.text+"::"+event.view.description+'\n') 34 | else: 35 | f_read_trace.write(str(event_count)+"::"+event.action+"::"+device_string+"::"+event.text+"::None::None"+'\n') 36 | f_read_trace.flush() 37 | 38 | def write_one_device_event(self,event,device_count,f_trace): 39 | if event.view is not None: 40 | f_trace.write(str(event.event_count)+"::"+event.action+"::device"+str(self.devices[device_count].device_num)+"::"+event.text+"::"+event.view.line) 41 | else: 42 | f_trace.write(str(event.event_count)+"::"+event.action+"::device"+str(self.devices[device_count].device_num)+"::"+event.text+"::None"+'\n') 43 | f_trace.flush() 44 | event.set_device(self.devices[device_count]) 45 | self.devices[device_count].error_event_lists.append(event) 46 | self.devices[device_count].wrong_event_lists.append(event) 47 | 48 | def write_event(self,event,device_count,f_trace): 49 | if event.view is not None: 50 | f_trace.write(str(event.event_count)+"::"+event.action+"::device"+str(self.devices[device_count].device_num)+"::"+event.text+"::"+event.view.line) 51 | f_trace.write(str(event.event_count)+"::"+event.action+"::device"+str(self.devices[0].device_num)+"::"+event.text+"::"+event.view.line) 52 | else: 53 | f_trace.write(str(event.event_count)+"::"+event.action+"::device"+str(self.devices[device_count].device_num)+"::"+event.text+"::None"+'\n') 54 | f_trace.write(str(event.event_count)+"::"+event.action+"::device"+str(self.devices[0].device_num)+"::"+event.text+"::None"+'\n') 55 | f_trace.flush() 56 | event.set_device(self.devices[0]) 57 | self.devices[device_count].error_event_lists.append(event) 58 | self.devices[device_count].wrong_event_lists.append(event) 59 | new_event = Event(event.view, event.action, self.devices[device_count], event.event_count) 60 | self.devices[device_count].error_event_lists.append(new_event) 61 | self.devices[device_count].wrong_event_lists.append(new_event) 62 | 63 | def start_thread(self): 64 | for device in self.devices: 65 | if device.thread is not None: 66 | device.thread.start() 67 | for device in self.devices: 68 | if device.thread is not None: 69 | device.thread.join() 70 | 71 | def create_dir(self,path): 72 | if not os.path.isdir(path): 73 | os.makedirs(path) 74 | 75 | def draw_error_frame(self): 76 | for device in self.devices: 77 | from cv2 import cv2 78 | image = cv2.imread(device.screenshot_path) 79 | cv2.rectangle(image, (1,1), (1430, 2550), (0, 0, 255), 20) 80 | cv2.imwrite(device.screenshot_path, image) 81 | 82 | 83 | def draw_event(self,event): 84 | for device in self.devices: 85 | from cv2 import cv2 86 | image = cv2.imread(device.screenshot_path) 87 | if device.screenshot_path != None and event.view !=None: 88 | if event.action == "click": 89 | cv2.rectangle(image, (int(event.view.xmin), int(event.view.ymin)), (int(event.view.xmax), int(event.view.ymax)), (0, 0, 255), 5) 90 | elif event.action == "longclick": 91 | cv2.rectangle(image, (int(event.view.xmin), int(event.view.ymin)), (int(event.view.xmax), int(event.view.ymax)), (0, 225, 255), 5) 92 | elif event.action == "edit": 93 | cv2.rectangle(image, (int(event.view.xmin), int(event.view.ymin)), (int(event.view.xmax), int(event.view.ymax)), (225, 0, 255), 5) 94 | else: 95 | cv2.rectangle(image, (int(event.view.xmin), int(event.view.ymin)), (int(event.view.xmax), int(event.view.ymax)), (225, 225, 255), 5) 96 | else: 97 | if event.action == "wrong": 98 | cv2.rectangle(image, (0,0), (1430, 2550), (0, 225, 255), 20) 99 | else: 100 | cv2.putText(image,event.action, (100,300), cv2.FONT_HERSHEY_SIMPLEX, 5,(0, 0, 255), 1, cv2.LINE_AA) 101 | # image=cv2.resize(image, (256, 512)) 102 | cv2.imwrite(device.screenshot_path, image) 103 | 104 | """ 105 | generate a html file for each strategy 106 | :return: 107 | """ 108 | def generate_html(self,path,html_path,run_count): 109 | line_list=[] 110 | f_html = open(os.path.join(html_path, str(run_count)+"_trace.html"),'w',encoding='utf-8') 111 | f_style = open("style.html",'r',encoding='utf-8') 112 | f_write_trace = open(os.path.join(path, "read_trace.txt"),'r',encoding='utf-8') 113 | lines_trace=f_write_trace.readlines() 114 | img_list=os.listdir(path+"/screen") 115 | new_str="
    "+'\n' 116 | for img_file in img_list: 117 | if ".png" in img_file and self.devices[0].device_serial in img_file: 118 | num=img_file.find("_") 119 | state_num=img_file[0:num] 120 | device1_img=state_num+"_"+self.devices[1].device_serial+".png" 121 | action=self.find_action_in_file(state_num,lines_trace) 122 | if path == html_path: 123 | line="
  • "+action+"

  • "+'\n' 124 | else: 125 | line="
  • "+action+"

  • "+'\n' 126 | line_list.append((float(state_num),line)) 127 | line_list.sort() 128 | for item in line_list: 129 | new_str = new_str + item[1] 130 | new_str=new_str+"
" 131 | old_str="
    " 132 | 133 | import re 134 | for line in f_style: 135 | f_html.write(re.sub(old_str,new_str,line)) 136 | 137 | def is_number(self,str): 138 | try: 139 | if str=='NaN': 140 | return False 141 | float(str) 142 | return True 143 | except ValueError: 144 | return False 145 | 146 | def find_action_in_file(self,state_num,lines): 147 | for line in lines: 148 | line_num=line[0:line.find("::")] 149 | if self.is_number(line_num) and self.is_number(state_num): 150 | if float(state_num)+1.0==float(line_num): 151 | line=line[line.find("::")+2:len(line)] 152 | action = line[0:line.find("::")] 153 | return state_num+"::"+action 154 | return state_num 155 | 156 | def print_dividing_line(self,success,event_count): 157 | if success == False: 158 | print(str(event_count)+"fail device_0-------------------") 159 | else: 160 | print(str(event_count)+"---------------------------------") 161 | 162 | def generate_outline_html(self, output_path, strategy_list): 163 | outline_path = output_path + '/all_run_bugs.html' 164 | f_outline_html = open(outline_path, 'w+', encoding="UTF-8") 165 | 166 | insert_lines = '\n' \ 167 | '\n' \ 168 | '\n' \ 169 | 'All bug report\n' \ 170 | '\n' \ 171 | '\n' 172 | 173 | tip = "
    \n" \ 174 | "

    " \ 175 | "This is a bug page that runs the SetDroid tool" \ 176 | "

    \n" \ 177 | "

    All the bug connections are listed below\n" \ 178 | "

    \n" \ 179 | "

    \n" \ 180 | "-----------------------------------------------------------------------------------" \ 181 | "

    \n" \ 182 | "

    " \ 183 | "X-Picture:Y means image index(Y) in the test trace(X) of a policy" \ 184 | "

    \n" \ 185 | "

    " \ 186 | "You can click on it to jump to another detaied page" \ 187 | "

    \n" \ 188 | "

    \n" 189 | insert_lines = insert_lines + tip 190 | bug_index = 1 191 | for strategy in strategy_list: 192 | insert_lines = insert_lines + '
    ' 193 | directory_num_list = [] 194 | bug_event_num_list = [] 195 | bug_file_name = output_path + "/strategy_" + strategy + "/error_realtime.txt" 196 | lines = open(bug_file_name, 'r', encoding="UTF-8").readlines() 197 | step = 2 198 | lines__ = [lines[i:i + step] for i in range(0, len(lines), step)] 199 | temp = 0 200 | for line in lines__: 201 | find_str1 = "run_count" 202 | find_str2 = "End" 203 | if len(line) > 1: 204 | if line[0].find(find_str1) > -1: 205 | directory_num_list.append(line[0][line[0].find(find_str1) + 11:]) 206 | elif line[1].find(find_str1) > -1: 207 | directory_num_list.append(line[1][line[1].find(find_str1) + 11:]) 208 | elif line[0].find(find_str2) > -1: 209 | bug_event_num_list.append(temp) 210 | elif line[1].find(find_str2) > -1: 211 | bug_event_num_list.append(line[0][0:line[0].find("::")]) 212 | temp = line[1][0:4] 213 | else: 214 | if line[0].find(find_str1) > -1: 215 | directory_num_list.append(line[0][line[0].find(find_str1) + 11:]) 216 | elif line[0].find(find_str2) > -1: 217 | bug_event_num_list.append(temp) 218 | 219 | if len(directory_num_list) == 0: 220 | continue 221 | 222 | insert_lines = insert_lines + '

    Policy: '+ strategy + '

    ' +'\n' 223 | 224 | for directory_num, bug_event_num in zip( directory_num_list,bug_event_num_list): 225 | directory_num = directory_num.strip('\n') 226 | insert_lines = insert_lines + 'Bug' + str(bug_index) + ': '\ 227 | '' \ 229 | + directory_num.replace('\n', '') + '-Picture:' + bug_event_num +'
    \n' 230 | bug_index = bug_index + 1 231 | insert_lines = insert_lines + '
    \n' \ 232 | '\n' \ 233 | '\n' 234 | f_outline_html.write(''.join(insert_lines)) 235 | f_outline_html.close() 236 | 237 | def generate_replay_all_html(self, output_path, strategy_list): 238 | outline_path = output_path + '/all_replay_bugs.html' 239 | f_outline_html = open(outline_path, 'w+', encoding="UTF-8") 240 | insert_lines = '\n' \ 241 | '\n' \ 242 | '\n' \ 243 | 'All bug report\n' \ 244 | '\n' \ 245 | '\n' 246 | 247 | tip = "
    \n" \ 248 | "

    " \ 249 | "This is a bug page that runs the SetDroid tool" \ 250 | "

    \n" \ 251 | "

    All the bug connections are listed below\n" \ 252 | "

    \n" \ 253 | "

    \n" \ 254 | "-----------------------------------------------------------------------------------" \ 255 | "

    \n" \ 256 | "

    " \ 257 | "X_trace.html is a bug's hayperlink, you can click on it" \ 258 | "

    \n" \ 259 | "

    " \ 260 | "And then slide to the last few images to check the bug" \ 261 | "

    \n" \ 262 | "

    \n" 263 | insert_lines = insert_lines + tip + '
    \n' 264 | bug_index = 1 265 | for strategy in strategy_list: 266 | 267 | replay_base_path = output_path + "/strategy_" + strategy + "/error_replay/" 268 | files = os.listdir(replay_base_path) 269 | temp_insert_lines = "" 270 | have_html = False 271 | for file in files: 272 | replay_path = replay_base_path + file 273 | html_files = os.listdir(replay_path) 274 | have_html = False 275 | 276 | for html in html_files: 277 | if html.find('html') > -1: 278 | have_html = True 279 | temp_insert_lines = temp_insert_lines + 'Bug' + str(bug_index) + ': '\ 280 | '' \ 282 | + html.replace('\n', '') + '
    \n' 283 | if have_html: 284 | insert_lines = insert_lines + '

    Policy: ' + strategy + '

    \n' 285 | insert_lines = insert_lines + temp_insert_lines 286 | have_html = False 287 | insert_lines = insert_lines + '
    \n' \ 288 | '\n' \ 289 | '\n' 290 | f_outline_html.write(''.join(insert_lines)) 291 | f_outline_html.close() 292 | 293 | 294 | -------------------------------------------------------------------------------- /Tool/SetDroid/view.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import hashlib 4 | 5 | class View(object): 6 | 7 | def __init__(self, line, father, sons): 8 | self.level=line.find('