├── .aiexclude
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img
└── sample-app.png
├── native
├── .gitignore
├── Cargo.toml
├── build.sh
└── src
│ ├── common.rs
│ ├── common
│ ├── build.rs
│ ├── files.rs
│ ├── info.rs
│ ├── logging.rs
│ ├── package.rs
│ ├── property.rs
│ ├── system.rs
│ └── util.rs
│ ├── debuggable.rs
│ ├── emulator.rs
│ ├── emulator
│ ├── device.rs
│ ├── general.rs
│ ├── packages.rs
│ ├── properties.rs
│ └── sensors.rs
│ ├── lib.rs
│ ├── root.rs
│ └── root
│ ├── packages.rs
│ ├── properties.rs
│ └── superuser.rs
├── packages
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── AndroidManifest.xml
├── rasp
├── .gitignore
├── build.gradle.kts
├── config
│ └── detekt.yml
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── securevale
│ └── rasp
│ └── android
│ ├── SecureApp.kt
│ ├── api
│ ├── CheckSubscriber.kt
│ ├── SecureAppChecker.kt
│ └── result
│ │ ├── Checks.kt
│ │ └── Result.kt
│ ├── check
│ ├── Check.kt
│ ├── CheckResult.kt
│ ├── ChecksMediator.kt
│ └── Probability.kt
│ ├── debugger
│ ├── DebuggerCheck.kt
│ └── checks
│ │ └── DebuggableChecks.kt
│ ├── emulator
│ ├── EmulatorCheck.kt
│ └── checks
│ │ ├── DeviceChecks.kt
│ │ ├── GeneralChecks.kt
│ │ ├── PackageChecks.kt
│ │ ├── PropertyChecks.kt
│ │ └── SensorChecks.kt
│ ├── root
│ ├── RootCheck.kt
│ └── checks
│ │ ├── AppsChecks.kt
│ │ ├── DeviceChecks.kt
│ │ ├── FileChecks.kt
│ │ └── PropertyChecks.kt
│ └── util
│ ├── Const.kt
│ ├── DeviceInfo.kt
│ ├── SecureAppLogger.kt
│ └── TimeLogger.kt
├── sample-app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── securevale
│ │ └── rasp
│ │ └── android
│ │ └── sample
│ │ ├── AvailableChecksListAdapter.kt
│ │ ├── CheckDetailsActivity.kt
│ │ ├── MainActivity.kt
│ │ ├── SampleApplication.kt
│ │ └── check
│ │ ├── CheckResultAdapter.kt
│ │ ├── DebuggerCheckFragment.kt
│ │ ├── EmulatorCheckFragment.kt
│ │ └── RootCheckFragment.kt
│ ├── jniLibs
│ ├── arm64-v8a
│ │ └── libnative.so
│ ├── armeabi-v7a
│ │ └── libnative.so
│ ├── x86
│ │ └── libnative.so
│ └── x86_64
│ │ └── libnative.so
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ ├── green_check.xml
│ ├── ic_launcher_background.xml
│ └── red_check.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── activity_test.xml
│ ├── fragment_check.xml
│ ├── item_check.xml
│ └── item_check_result.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-mdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.webp
│ └── ic_launcher_round.webp
│ ├── values-night
│ └── themes.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
└── settings.gradle.kts
/.aiexclude:
--------------------------------------------------------------------------------
1 | .gradle
2 | .gradle.kts
3 | .so
4 | .properties
5 | .apk
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | paths-ignore:
8 | - '.gitignore'
9 | - 'README.md'
10 | - 'LICENSE'
11 | - 'CODE_OF_CONDUCT.md'
12 | - 'gradle.properties'
13 | pull_request:
14 | branches:
15 | - master
16 | paths-ignore:
17 | - '.gitignore'
18 | - 'README.md'
19 | - 'LICENSE'
20 | - 'CODE_OF_CONDUCT.md'
21 | - 'gradle.properties'
22 | workflow_dispatch:
23 |
24 | jobs:
25 | lint:
26 | name: Lint
27 | runs-on: ubuntu-latest
28 |
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v3
32 |
33 | - name: Set up JDK 17
34 | uses: actions/setup-java@v3
35 | with:
36 | java-version: '17'
37 | distribution: 'liberica'
38 | cache: gradle
39 |
40 | - name: Grant execute permission for gradlew
41 | run: chmod +x gradlew
42 |
43 | - name: Lint
44 | run: ./gradlew :rasp:lint
45 |
46 | - name: Detekt
47 | run: ./gradlew :rasp:detekt
48 |
49 | rasp_lint:
50 | name: Rasp Lint
51 | runs-on: ubuntu-latest
52 |
53 | steps:
54 | - name: Checkout
55 | uses: actions/checkout@v3
56 |
57 | - name: Set up Rust
58 | uses: actions-rs/toolchain@v1
59 | with:
60 | profile: minimal
61 | toolchain: stable
62 |
63 | - name: Add Clippy
64 | run: rustup component add clippy
65 |
66 | - name: Lint
67 | working-directory: ./native
68 | run: cargo clippy --no-deps -- -D warnings
69 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 | local.keystore
12 |
13 | output-metadata.json
14 | *.apk
15 |
16 | roadmap
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, caste, color, religion, or sexual
10 | identity and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the overall
26 | community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or advances of
31 | any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email address,
35 | without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | conduct@securevale.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series of
86 | actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or permanent
93 | ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within the
113 | community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.1, available at
119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
120 |
121 | Community Impact Guidelines were inspired by
122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
123 |
124 | For answers to common questions about this code of conduct, see the FAQ at
125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
126 | [https://www.contributor-covenant.org/translations][translations].
127 |
128 | [homepage]: https://www.contributor-covenant.org
129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
130 | [Mozilla CoC]: https://github.com/mozilla/diversity
131 | [FAQ]: https://www.contributor-covenant.org/faq
132 | [translations]: https://www.contributor-covenant.org/translations
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
204 |
205 | ## Runtime Library Exception to the Apache 2.0 License: ##
206 |
207 |
208 | As an exception, if you use this Software to compile your source code and
209 | portions of this Software are embedded into the binary product as a result,
210 | you may redistribute such product without providing attribution as would
211 | otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android RASP
2 |
3 | [](https://github.com/securevale/android-rasp/actions/workflows/ci.yml)
4 | [](https://kotlinlang.org/docs/whatsnew19.html)
5 | [](https://developer.android.com/about/versions/nougat)
6 | [](https://developer.android.com/about/versions/15)
7 | [](https://docs.gradle.org/8.11.1/release-notes.html)
8 | [](https://search.maven.org/artifact/com.securevale/rasp-android)
9 |
10 | An open-source RASP (Runtime Application Self-Protection) solution for protecting Android apps
11 | against being run on vulnerable devices.
12 |
13 | > [!NOTE]
14 | > Android RASP is still in development, meaning that some breaking changes are likely to
15 | > be introduced in future releases.
16 | > See [Versioning](#versioning) section for more information.
17 |
18 | ## Motivation
19 |
20 | In the current threat-rich environment, it is crucial to protect the apps against exploitation of a
21 | wide range of vulnerabilities and reverse engineering techniques. Hooking frameworks,
22 | man-in-the-middle attacks, app repackaging, rooted devices, just to name a few common threats
23 | applicable to mobile applications.
24 |
25 | While there are existing solutions for guarding against aforementioned threats, almost all of them
26 | are paid. This library is one attempt to provide a robust solution for "regular" teams and devs that
27 | cannot afford spending hundreds of dollars per month to defend against this kind of threats,
28 | allowing to take control over application execution, security threat detection, and real-time attack
29 | prevention.
30 |
31 | > [!NOTE]
32 | > While adopting this library will shield your app against a number of runtime security threats,
33 | > you need to remember that no security measure can ever guarantee absolute security.
34 | > Any motivated and skilled enough attacker will eventually bypass all security protections.
35 | > For this reason, **always keep your threat models up to date**.
36 |
37 | ## Getting started
38 |
39 | First ensure that you have defined `mavenCentral` in your Gradle configuration.
40 |
41 | ```groovy
42 | repositories {
43 | mavenCentral()
44 | }
45 | ```
46 |
47 | Next, add Android RASP library as a dependency to your project.
48 |
49 | ```groovy
50 | dependencies {
51 | implementation 'com.securevale:rasp-android:{version}'
52 | }
53 | ```
54 |
55 | Before first use, the library needs to be initialised with `init()` method. This method is
56 | expected to be called only once per app's lifecycle, so the best place for doing it is inside
57 | of your app's `Application` class.
58 |
59 | ```kotlin
60 | import com.securevale.rasp.android.SecureApp
61 |
62 | class SampleApplication : Application() {
63 |
64 | override fun onCreate() {
65 | super.onCreate()
66 | SecureApp.init()
67 | }
68 | }
69 | ```
70 |
71 | Then, create a `builder` with the desired configuration options.
72 |
73 | ```kotlin
74 | import com.securevale.rasp.android.emulator.CheckLevel
75 | import com.securevale.rasp.android.api.SecureAppChecker
76 |
77 | val shouldCheckForEmulator = true
78 | val shouldCheckForDebugger = true
79 | val shouldCheckForRoot = true
80 |
81 | val builder = SecureAppChecker.Builder(
82 | this,
83 | checkEmulator = shouldCheckForEmulator,
84 | checkDebugger = shouldCheckForDebugger,
85 | checkRoot = shouldCheckForRoot
86 | )
87 | ```
88 |
89 | Use the `builder` to create the RASP checks and trigger them to obtain the result.
90 |
91 | ```kotlin
92 | import com.securevale.rasp.android.api.result.Result
93 |
94 | val check = builder.build()
95 | val checkResult = check.check()
96 |
97 | when (checkResult) {
98 | is Result.EmulatorFound -> {} // app is most likely running on emulator
99 | is Result.DebuggerEnabled -> {} // app is in debug mode
100 | is Result.Rooted -> {} // app is most likely rooted
101 | is Result.Secure -> {} // OK, no threats detected
102 | }
103 | ```
104 |
105 | You can also perform more granular checks.
106 |
107 | ```kotlin
108 | val check = builder.build()
109 | check.subscribe {
110 | // examine result(s) here
111 | }
112 | ```
113 |
114 | Or even subscribe in order to be notified only when a potential threat is detected.
115 |
116 | ```kotlin
117 | val check = builder.build()
118 | check.subscribeVulnerabilitiesOnly(granular = true) {
119 | // examine result(s) here
120 | }
121 | ```
122 |
123 | You can also choose which checks should be run by passing appropriate list to
124 | the `checkOnlyFor` parameter.
125 |
126 | ```kotlin
127 | import com.securevale.rasp.android.api.result.DebuggerChecks
128 | import com.securevale.rasp.android.api.result.EmulatorChecks
129 | import com.securevale.rasp.android.api.result.RootChecks
130 |
131 | val check = builder.build()
132 | check.subscribeVulnerabilitiesOnly(
133 | granular = true,
134 | checkOnlyFor = arrayOf(
135 | EmulatorChecks.AvdDevice,
136 | EmulatorChecks.AvdHardware,
137 | EmulatorChecks.Genymotion,
138 | EmulatorChecks.Nox,
139 | DebuggerChecks.Debuggable,
140 | DebuggerChecks.DebugField,
141 | RootChecks.SuUser,
142 | RootChecks.TestTags,
143 | RootChecks.RootApps,
144 | RootChecks.RootCloakingApps,
145 | RootChecks.WritablePaths,
146 | RootChecks.SuspiciousProperties
147 | )
148 | ) {
149 | // examine result(s) here
150 | }
151 | ```
152 |
153 | For more information about available configuration options, see
154 | [SecureAppChecker](https://github.com/securevale/android-rasp/blob/master/rasp/src/main/java/com/securevale/rasp/android/api/SecureAppChecker.kt)
155 | class documentation.
156 |
157 | > [!IMPORTANT]
158 | > A skilled attacker might be able to repackage protected app and remove the checks from
159 | > the source code.
160 | > With that said, it is highly recommended to add these checks in multiple places in code, so as to
161 | > maximize the cost and effort required to successfully bypass all the checks.
162 | > Additionally, in order to further impede the malicious actors' life, the library checks are
163 | > written
164 | > in native code (using Rust language) and distributed with library source code as `.so` library files.
165 |
166 | ## Supported Checks
167 |
168 | ### Debugger Detection
169 |
170 | Includes:
171 |
172 | - Several checks for debug flags;
173 | - Check for connected debugger;
174 | - Check for threads waiting for the debugger to be attached.
175 |
176 | ### Emulator Detection
177 |
178 | Includes:
179 |
180 | - Check for "basic" emulator indicators (mostly device build configuration
181 | fields indicating whether particular device is "real" or not). These fields can be easily faked by
182 | the emulator makers or even by the device user (if the device happens to be rooted);
183 | - More advanced checks (such as device's operator name, telephone number, properties etc.).
184 | Recommended when you need to be more certain whether the device is an
185 | emulator. Please note that in order to take full advantage of these checks, you need to add
186 | *android.permission.READ_PHONE_STATE* permission to your application manifest file.
187 |
188 | All implemented checks were tested on various emulators and devices to decrease both false-positives (
189 | when device that is not an emulator is reported as one) and false-negatives. However, as the detection
190 | techniques become more advanced, the emulator detection bypass tools are improving as well. This is a
191 | never ending cat and mouse game, so there is no guarantee that all emulators will be correctly and
192 | accurately reported as such. The library shall be continuously updated with new emulator detection
193 | techniques with the aim of catching the emulators that slip through the existing checks.
194 |
195 | ### Root Detection
196 |
197 | Includes:
198 |
199 | - Checks for superuser indicators;
200 | - Checks for test tags present on device;
201 | - Checks for rooting and root cloaking apps (currently in beta);
202 | - Checks for paths that should not be writable;
203 | - Checks for suspicious properties;
204 |
205 | >[!IMPORTANT]
206 | > Rooting is an ever-evolving domain and so are the root detection techniques. For this reason it is important to understand that the above checks are not exhaustive and
207 | > should be expected to undergo continuous improvement.
208 |
209 | > [!NOTE]
210 | > Although some basics checks for Magisk are already in place, a comprehensive Magisk detection is expected to be implemented in future releases.
211 |
212 | ## ProGuard
213 |
214 | Android RASP ships with its own ProGuard rules, except one caveat regarding the `DebugField`
215 | check. The library relies on `BuildConfig` class which needs to be excluded from obfuscation. In order
216 | for this check to return correct results, add the following line to your ProGuard configuration file:
217 |
218 | ```
219 | -keep class {your_package}.BuildConfig{ *; }
220 | ```
221 |
222 | Alternatively, you can opt out from this check by excluding `DebugField` from an array of checks passed to
223 | `checkOnlyFor` parameter.
224 |
225 |
226 | ## Package visibility changes Android 11+
227 |
228 | Android 11 introduced [changes related to package visibility](https://developer.android.com/about/versions/11/privacy/package-visibility). Android RASP contains several checks which
229 | utilize knowledge about suspicious packages installed on a device. This checks might not work when run on a device running
230 | on Android 11+, it will not crash the app but will not be able to properly check for suspicious packages occurrence thus
231 | reporting device being safe while it might not be.
232 |
233 | The library is checking for [packages defined here](https://github.com/securevale/android-rasp/blob/master/native/src/common/package.rs).
234 |
235 | In order to address this changes and make sure that library is able to check all suspicious packages the additional,
236 | optional library with the manifest was added with manifest which defines all of packages Android RASP is asking as
237 | a queryable.
238 |
239 | ```groovy
240 | dependencies {
241 | implementation 'com.securevale:rasp-packages:{version}'
242 | }
243 | ```
244 |
245 | > [!NOTE]
246 | > You are adding it an your own risk as Google's privacy policies are constantly changing and it's not guaranteed that requesting such a wide list of queryable packages
247 | > won't result in app being pulled off/rejected from the Google Play Store.
248 |
249 | > [!NOTE]
250 | > Although package checks might not work perfectly, Android RASP performs many other security checks.
251 | > So even without using the optional `rasp-packages` library, you can still expect the main library to
252 | > fulfill its security assessment promises.
253 |
254 | ## Sample app
255 |
256 | Sample allows you to test checks. To run it you need to clone the project and build and run "sample-app"
257 | project on device of your choice.
258 |
259 |
260 |
261 | ## Versioning
262 |
263 | This project follows [semantic versioning](https://semver.org/). While still in major version `0`,
264 | source-stability is only guaranteed within minor versions (e.g. between `0.3.0` and `0.3.1`). If you
265 | want to guard against potentially source-breaking package updates, you can specify your package
266 | dependency using exact version as the requirement.
267 |
268 | ## License
269 |
270 | This tool and code is released under Apache License v2.0 with Runtime Library Exception. Please
271 | see [LICENSE](LICENSE) for more information.
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.android.library) apply false
5 | alias(libs.plugins.kotlin.android) apply false
6 | alias(libs.plugins.kotlin.jvm) apply false
7 |
8 | alias(libs.plugins.detekt) apply false
9 | alias(libs.plugins.dokka) apply false
10 | alias(libs.plugins.maven.publish)
11 | }
12 |
13 | val versionCode by extra { 7 }
14 | val versionName by extra { "0.7.0" }
15 | val minSdkVersion by extra { 24 }
16 | val compileSdkVersion by extra { 35 }
17 | val targetSdkVersion by extra { 35 }
18 |
19 | task("clean", type = Delete::class) {
20 | delete(rootProject.buildDir)
21 | }
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | android.nonFinalResIds=false
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | appcompat = "1.7.0"
3 | constraintLayout = "2.2.1"
4 | coreKtx = "1.15.0" # can be upgraded when target SDK 35
5 | leakCanary = "2.9.1"
6 | material = "1.12.0"
7 |
8 | # plugins
9 | androidGradlePlugin = "8.9.0"
10 | androidLibraryPlugin = "8.9.0"
11 | detektPlugin = "1.23.0"
12 | dokkaPlugin = "1.8.20"
13 | kotlinAndroidPlugin = "1.9.0"
14 | kotlinJvmPlugin = "1.9.0"
15 | mavenPublishPlugin = "0.31.0"
16 |
17 | [libraries]
18 | androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
19 | androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintLayout" }
20 | androidx-corektx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
21 |
22 | google-material = { module = "com.google.android.material:material", version.ref = "material" }
23 |
24 | square-leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version.ref = "leakCanary" }
25 |
26 | [plugins]
27 | android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
28 | android-library = { id = "com.android.library", version.ref = "androidLibraryPlugin" }
29 |
30 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinAndroidPlugin" }
31 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinJvmPlugin" }
32 |
33 | detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detektPlugin" }
34 | dokka = { id = "org.jetbrains.dokka", version.ref = "dokkaPlugin" }
35 |
36 | maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublishPlugin" }
37 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/securevale/android-rasp/d74f9689437ecbfd97bb0d151d70ef03ca257c3f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jul 06 17:11:23 CEST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/img/sample-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/securevale/android-rasp/d74f9689437ecbfd97bb0d151d70ef03ca257c3f/img/sample-app.png
--------------------------------------------------------------------------------
/native/.gitignore:
--------------------------------------------------------------------------------
1 | debug/
2 | target/
3 | /.idea
4 |
5 | Cargo.lock
6 | **/*.rs.bk
7 |
8 | work-in-progress
--------------------------------------------------------------------------------
/native/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "native"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | log = "0.4.21"
8 | jni = "0.21.1"
9 | android_logger = "0.15.0"
10 | once_cell = "1.19.0"
11 |
12 | [lib]
13 | name = "native"
14 | crate-type = ["cdylib"]
15 |
16 | [profile.release]
17 | lto = true
18 | strip = true
19 |
20 | #[profile.release-with-debug]
21 | #inherits = "release"
22 | #debug = true
--------------------------------------------------------------------------------
/native/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This script needs to be called from the native folder cuz all paths are assumed to be relative
4 |
5 | set -Eeuo pipefail
6 |
7 | readonly OPTION_DEBUG="-d"
8 | DEBUG=false
9 | BUILD_FOLDER=""
10 |
11 | function create_folders() {
12 | BUILD_FOLDER="$(pwd)/target"
13 |
14 | cd ../sample-app/src/main/jniLibs || exit
15 |
16 | mkdir -p arm64-v8a
17 | mkdir -p armeabi-v7a
18 | mkdir -p x86
19 | mkdir -p x86_64
20 | }
21 |
22 | function create_lib_folders() {
23 |
24 | cd ../rasp/src/main/jniLibs || exit
25 |
26 | mkdir -p arm64-v8a
27 | mkdir -p armeabi-v7a
28 | mkdir -p x86
29 | mkdir -p x86_64
30 | }
31 |
32 | while [[ $# -gt 0 ]]
33 | do
34 | key="$1"
35 |
36 | case $key in
37 | "$OPTION_DEBUG")
38 | DEBUG=true
39 | shift 1
40 | ;;
41 | *)
42 | shift
43 | ;;
44 | esac
45 | done
46 |
47 | if [[ "$DEBUG" == true ]]
48 | then
49 | cargo build --target aarch64-linux-android
50 | cargo build --target armv7-linux-androideabi
51 | cargo build --target i686-linux-android
52 | cargo build --target x86_64-linux-android
53 | create_folders
54 |
55 | cp -fr "$BUILD_FOLDER/aarch64-linux-android/debug/libnative.so" "arm64-v8a/libnative.so"
56 | cp -fr "$BUILD_FOLDER/armv7-linux-androideabi/debug/libnative.so" "armeabi-v7a/libnative.so"
57 | cp -fr "$BUILD_FOLDER/i686-linux-android/debug/libnative.so" "x86/libnative.so"
58 | cp -fr "$BUILD_FOLDER/x86_64-linux-android/debug/libnative.so" "x86_64/libnative.so"
59 |
60 | else
61 | cargo build --target aarch64-linux-android --release
62 | cargo build --target armv7-linux-androideabi --release
63 | cargo build --target i686-linux-android --release
64 | cargo build --target x86_64-linux-android --release
65 |
66 | create_folders
67 |
68 | cp -fr "$BUILD_FOLDER/aarch64-linux-android/release/libnative.so" "arm64-v8a/libnative.so"
69 | cp -fr "$BUILD_FOLDER/armv7-linux-androideabi/release/libnative.so" "armeabi-v7a/libnative.so"
70 | cp -fr "$BUILD_FOLDER/i686-linux-android/release/libnative.so" "x86/libnative.so"
71 | cp -fr "$BUILD_FOLDER/x86_64-linux-android/release/libnative.so" "x86_64/libnative.so"
72 |
73 | cd -
74 | create_lib_folders
75 |
76 | cp -fr "$BUILD_FOLDER/aarch64-linux-android/release/libnative.so" "arm64-v8a/libnative.so"
77 | cp -fr "$BUILD_FOLDER/armv7-linux-androideabi/release/libnative.so" "armeabi-v7a/libnative.so"
78 | cp -fr "$BUILD_FOLDER/i686-linux-android/release/libnative.so" "x86/libnative.so"
79 | cp -fr "$BUILD_FOLDER/x86_64-linux-android/release/libnative.so" "x86_64/libnative.so"
80 |
81 | fi
82 |
--------------------------------------------------------------------------------
/native/src/common.rs:
--------------------------------------------------------------------------------
1 | pub mod build;
2 | pub mod files;
3 | pub mod info;
4 | pub mod logging;
5 | pub mod package;
6 | pub mod property;
7 | pub mod system;
8 | pub mod util;
9 |
--------------------------------------------------------------------------------
/native/src/common/build.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::{JObject, JString};
2 | use jni::JNIEnv;
3 |
4 | pub fn get_build_config_value(env: &mut JNIEnv, key: &str) -> String {
5 | let build_class = env.find_class("android/os/Build").unwrap();
6 |
7 | let field = JObject::try_from(
8 | env.get_static_field(build_class, key, "Ljava/lang/String;")
9 | .unwrap(),
10 | )
11 | .unwrap();
12 |
13 | unsafe {
14 | env.get_string_unchecked(&JString::from(field))
15 | .unwrap()
16 | .to_str()
17 | .unwrap()
18 | .to_string()
19 | }
20 | }
21 |
22 | pub const BOOTLOADER: &str = "BOOTLOADER";
23 | pub const DEVICE: &str = "DEVICE";
24 | pub const MODEL: &str = "MODEL";
25 | pub const BRAND: &str = "BRAND";
26 | pub const BOARD: &str = "BOARD";
27 | pub const MANUFACTURER: &str = "MANUFACTURER";
28 | pub const HARDWARE: &str = "HARDWARE";
29 | pub const DISPLAY: &str = "DISPLAY";
30 | pub const FINGERPRINT: &str = "FINGERPRINT";
31 | pub const PRODUCT: &str = "PRODUCT";
32 | pub const TAGS: &str = "TAGS";
33 | pub const HOST: &str = "HOST";
34 |
35 | pub const TYPE: &str = "TYPE";
36 |
--------------------------------------------------------------------------------
/native/src/common/files.rs:
--------------------------------------------------------------------------------
1 | use log::LevelFilter::Debug;
2 | use std::fs::File;
3 | use std::io;
4 | use std::io::{BufRead, BufReader};
5 | use std::path::Path;
6 |
7 | #[cfg(debug_assertions)]
8 | use crate::common::logging::log_android;
9 |
10 | pub fn has_genymotion_files() -> bool {
11 | check_files_present(&GENYMOTION_FILES)
12 | }
13 |
14 | pub fn has_pipes() -> bool {
15 | check_files_present(&PIPES)
16 | }
17 |
18 | pub fn has_emu_files() -> bool {
19 | check_files_present(&EMU_FILES)
20 | }
21 |
22 | pub fn has_x86_files() -> bool {
23 | check_files_present(&X86_FILES)
24 | }
25 |
26 | pub fn has_andy_files() -> bool {
27 | check_files_present(&ANDY_FILES)
28 | }
29 |
30 | pub fn has_nox_files() -> bool {
31 | check_files_present(&NOX_FILES)
32 | }
33 |
34 | pub fn has_bluestack_files() -> bool {
35 | check_files_present(&BLUE_STACKS_FILES)
36 | }
37 |
38 | pub fn has_phoenix_files() -> bool {
39 | check_files_present(&PHOENIX_FILES)
40 | }
41 |
42 | pub fn has_su_files() -> bool {
43 | check_files_present(&SU_FILES)
44 | }
45 |
46 | pub fn has_dynamic_su_files(dynamic_paths: &[&str]) -> bool {
47 | let mut paths = Vec::new();
48 |
49 | for item in dynamic_paths.iter() {
50 | if !item.ends_with("/") {
51 | paths.push(format!("{}/su", item));
52 | } else {
53 | paths.push(format!("{}su", item));
54 | }
55 | }
56 |
57 | #[cfg(debug_assertions)]
58 | log_android(Debug, format!("SU PATHS: {:?}", paths.as_slice()).as_str());
59 |
60 | paths.iter().any(|file_path| Path::new(&file_path).exists())
61 | }
62 |
63 | pub fn has_dynamic_busybox_files(dynamic_paths: &[&str]) -> bool {
64 | let mut paths = Vec::new();
65 |
66 | for item in dynamic_paths.iter() {
67 | if !item.ends_with("/") {
68 | paths.push(format!("{}/busybox", item));
69 | } else {
70 | paths.push(format!("{}busybox", item));
71 | }
72 | }
73 |
74 | #[cfg(debug_assertions)]
75 | log_android(
76 | Debug,
77 | format!("BUSYBOX PATHS: {:?}", paths.as_slice()).as_str(),
78 | );
79 |
80 | paths.iter().any(|file_path| Path::new(&file_path).exists())
81 | }
82 |
83 | pub fn has_busybox_files() -> bool {
84 | check_files_present(&BUSYBOX_FILES)
85 | }
86 |
87 | fn check_files_present(suspicious_file_paths: &[&str]) -> bool {
88 | suspicious_file_paths
89 | .iter()
90 | .any(|&file_path| Path::new(file_path).exists())
91 | }
92 |
93 | pub fn find_in_file(file_path: &str, search_phrases: &[&str]) -> Result {
94 | let file = File::open(file_path)?;
95 | let reader = BufReader::new(file);
96 |
97 | for line in reader.lines() {
98 | let line = line?;
99 | for phrase in search_phrases.iter() {
100 | if line.contains(phrase) {
101 | return Ok(true);
102 | }
103 | }
104 | }
105 | Ok(false)
106 | }
107 |
108 | /// Files indicate that it is a Genymotion emulator.
109 | const GENYMOTION_FILES: [&str; 3] = [
110 | "/dev/socket/genyd",
111 | "/dev/socket/baseband_genyd",
112 | "/system/bin/genybaseband",
113 | ];
114 |
115 | /// Pipes indicate that it is most likely an emulator.
116 | const PIPES: [&str; 3] = ["/dev/socket/qemud", "/dev/qemu_pipe", "/dev/goldfish_pipe"];
117 |
118 | /// Files indicate that it is most likely an emulator.
119 | const EMU_FILES: [&str; 19] = [
120 | "/system/lib/libc_malloc_debug_qemu.so",
121 | "/system/lib64/libc_malloc_debug_qemu.so",
122 | "/sys/qemu_trace",
123 | "/system/bin/qemu-props",
124 | "/system/bin/qemud",
125 | "/dev/memufp",
126 | "/dev/memuguest",
127 | "/dev/memuuser",
128 | "/system/lib/memuguest.ko",
129 | "/dev/bst_gps",
130 | "/dev/bst_ime",
131 | "/dev/bstgyro",
132 | "/dev/bstmegn",
133 | "/system/lib/hw/gps.ld.so",
134 | "/system/lib/hw/sensors.ld.so",
135 | "/system/lib/libldutils.so",
136 | "/system/bin/ldinit",
137 | "/system/app/LDAppStore/LDAppStore.apk",
138 | "/data/data/com.ldmnq.launcher3/files/launcher.preferences",
139 | ];
140 |
141 | /// Su files (indicates superuser - root privileges)
142 | const SU_FILES: [&str; 15] = [
143 | "/sbin/su",
144 | "su/bin/su",
145 | "/system/bin/su",
146 | "/system/bin/.ext/su",
147 | "/system/xbin/su",
148 | "/data/local/xbin/su",
149 | "/data/local/bin/su",
150 | "/system/sd/xbin/su",
151 | "/system/bin/failsafe/su",
152 | "/system/usr/we-need-root/su",
153 | "/data/local/su",
154 | "/cache/su",
155 | "/dev/su",
156 | "/data/su",
157 | "/system/app/Superuser.apk",
158 | ];
159 |
160 | /// Busybox files TODO check what is busybox
161 | const BUSYBOX_FILES: [&str; 11] = [
162 | "/sbin/busybox",
163 | "su/bin/busybox",
164 | "/system/bin/busybox",
165 | "/system/bin/.ext/busybox",
166 | "/system/xbin/busybox",
167 | "/data/local/xbin/busybox",
168 | "/data/local/bin/busybox",
169 | "/system/sd/xbin/busybox",
170 | "/system/bin/failsafe/busybox",
171 | "/system/usr/we-need-root/busybox",
172 | "/data/local/busybox",
173 | ];
174 |
175 | /// Pipes indicate that it is most likely an emulator.
176 | const X86_FILES: [&str; 10] = [
177 | "ueventd.android_x86.rc",
178 | "x86.prop",
179 | "ueventd.ttVM_x86.rc",
180 | "init.ttVM_x86.rc",
181 | "init.goldfish.rc",
182 | "fstab.ttVM_x86",
183 | "fstab.vbox86",
184 | "init.vbox86.rc",
185 | "ueventd.vbox86.rc",
186 | "ueventd.ranchu.rc",
187 | ];
188 |
189 | /// Pipes indicate that it is an Andy emulator.
190 | const ANDY_FILES: [&str; 2] = ["fstab.andy", "ueventd.andy.rc"];
191 |
192 | /// Pipes indicate that it is a Nox emulator.
193 | const NOX_FILES: [&str; 12] = [
194 | "fstab.nox",
195 | "init.nox.rc",
196 | "ueventd.nox.rc",
197 | "/BigNoxGameHD",
198 | "/YSLauncher",
199 | "/system/bin/nox-prop",
200 | "/system/bin/noxd",
201 | "/system/lib/libnoxd.so",
202 | "/system/lib/libnoxspeedup.so",
203 | "/system/etc/init.nox.sh",
204 | "/system/bin/nox",
205 | "/system/bin/nox-vbox-sf",
206 | ];
207 |
208 | /// Pipes indicate that it is a BlueStacks emulator.
209 | const BLUE_STACKS_FILES: [&str; 33] = [
210 | "/Android/data/com.bluestacks.home",
211 | "/Android/data/com.bluestacks.settings",
212 | "/system/priv-app/com.bluestacks.bstfolder.apk",
213 | "/data/.bluestacks.prop",
214 | "/data/data/com.bluestacks.bstfolder",
215 | "/data/data/com.bluestacks.appmart",
216 | "/data/data/com.bluestacks.home",
217 | "/data/data/com.androVM.vmconfig",
218 | "/data/data/com.bluestacks.accelerometerui",
219 | "/data/data/com.bluestacks.appfinder",
220 | "/data/data/com.bluestacks.appsettings",
221 | "/data/data/com.bluestacks.BstCommandProcessor",
222 | "/data/data/com.bluestacks.help",
223 | "/data/data/com.bluestacks.s2p",
224 | "/data/data/com.bluestacks.searchapp",
225 | "/data/data/com.bluestacks/settings",
226 | "/data/data/com.bluestacks.setup",
227 | "/data/data/com.bluestacks.spotlight",
228 | "/data/data/com.bluestacks.launcher",
229 | "/data/app/com.bluestacks.appmart-1.apk",
230 | "/data/app/com.bluestacks.BstCommandProcessor-1.apk",
231 | "/data/app/com.bluestacks.help-1.apk",
232 | "/data/app/com.bluestacks.home-1.apk",
233 | "/data/app/com.bluestacks.s2p-1.apk",
234 | "/data/app/com.bluestacks.searchapp-1.apk",
235 | "/system/bin/bstfolder",
236 | "/system/bin/bstfolderd",
237 | "/system/bin/bstsyncfs",
238 | "/sys/module/bstsensor",
239 | "/sys/module/bstpgaipc",
240 | "/system/xbin/bstk/su",
241 | "/system/xbin/bstk",
242 | "/mnt/prebundledapps/bluestacks.prop.orig",
243 | ];
244 |
245 | /// Pipes indicate that it is a Phoenix emulator.
246 | const PHOENIX_FILES: [&str; 3] = [
247 | "/system/xbin/phoenix_compat",
248 | "/data/system/phoenixlog.addr",
249 | "/system/phoenixos",
250 | ];
251 |
252 | pub const NON_WRITABLE_PATHS: [&str; 7] = [
253 | "/system",
254 | "/system/bin",
255 | "/system/sbin",
256 | "/system/xbin",
257 | "/vendor/bin",
258 | "/sbin",
259 | "/etc",
260 | ];
261 |
--------------------------------------------------------------------------------
/native/src/common/info.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 |
3 | use jni::objects::{JClass, JObject, JString, JValue};
4 | use jni::sys::jstring;
5 | use jni::JNIEnv;
6 |
7 | use crate::common::build::get_build_config_value;
8 | use crate::common::property::emulator_properties_found;
9 | use crate::{common::build, common::files, common::system};
10 |
11 | // TODO add info about root
12 | #[no_mangle]
13 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_util_DeviceInfoKt_deviceInfo(
14 | mut env: JNIEnv,
15 | _class: JClass,
16 | ) -> jstring {
17 | let result = format!(
18 | "
19 | Bootloader: {}
20 | Device: {}
21 | Model: {}
22 | Product: {}
23 | Manufacturer: {}
24 | Brand: {}
25 | Board: {}
26 | Hardware: {}
27 | Host: {}
28 | Fingerprint: {}
29 | Tags: {}",
30 | get_build_config_value(&mut env, build::BOOTLOADER),
31 | get_build_config_value(&mut env, build::DEVICE),
32 | get_build_config_value(&mut env, build::MODEL),
33 | get_build_config_value(&mut env, build::PRODUCT),
34 | get_build_config_value(&mut env, build::MANUFACTURER),
35 | get_build_config_value(&mut env, build::BRAND),
36 | get_build_config_value(&mut env, build::BOARD),
37 | get_build_config_value(&mut env, build::HARDWARE),
38 | get_build_config_value(&mut env, build::HOST),
39 | get_build_config_value(&mut env, build::FINGERPRINT),
40 | get_build_config_value(&mut env, build::TAGS)
41 | );
42 |
43 | env.new_string(result)
44 | .expect("Unable to collect device info.")
45 | .into_raw()
46 | }
47 |
48 | #[no_mangle]
49 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_util_DeviceInfoKt_extendedDeviceInfo(
50 | mut env: JNIEnv,
51 | _class: JClass,
52 | ) -> jstring {
53 | let result = format!(
54 | "
55 | Nox files: {}
56 | Andy files: {}
57 | Blue files: {}
58 | X86 files: {}
59 | Emulator files: {}
60 | Genymotion files: {}
61 | Emulator Pipes: {}
62 | Qemu Property ro.kernel.qemu: {}
63 | Qemu Properties found: {}
64 | ",
65 | files::has_nox_files(),
66 | files::has_andy_files(),
67 | files::has_bluestack_files(),
68 | files::has_x86_files(),
69 | files::has_emu_files(),
70 | files::has_genymotion_files(),
71 | files::has_pipes(),
72 | system::get_prop(&mut env, &"ro.kernel.qemu".to_string()) == "1",
73 | emulator_properties_found(&mut env),
74 | );
75 |
76 | env.new_string(result)
77 | .expect("Unable to collect device info.")
78 | .into_raw()
79 | }
80 |
81 | #[no_mangle]
82 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_util_DeviceInfoKt_sensorInfo<'a>(
83 | mut env: JNIEnv<'a>,
84 | _class: JClass,
85 | context: JObject<'a>,
86 | ) -> jstring {
87 | let sensor_manager_obj = system::get_system_service(&mut env, context, "sensor").unwrap();
88 |
89 | let sensor_list = JObject::try_from(
90 | env.call_method(
91 | sensor_manager_obj,
92 | "getSensorList",
93 | "(I)Ljava/util/List;",
94 | &[JValue::Int(-1)],
95 | )
96 | .unwrap(),
97 | )
98 | .unwrap();
99 |
100 | let sensor_list = env.get_list(&sensor_list).unwrap();
101 |
102 | let mut iterator = sensor_list.iter(&mut env).unwrap();
103 |
104 | let mut result = String::new();
105 |
106 | while let Ok(sensor) = iterator.next(&mut env) {
107 | match sensor {
108 | Some(sensor) => {
109 | let name_field = JObject::try_from(
110 | env.call_method(sensor, "getName", "()Ljava/lang/String;", &[])
111 | .unwrap(),
112 | )
113 | .unwrap();
114 |
115 | let name_string = &JString::from(name_field);
116 | let name = env.get_string(name_string).unwrap();
117 |
118 | result.push('\n');
119 | result.push_str(name.to_str().unwrap());
120 | }
121 | None => break,
122 | }
123 | }
124 |
125 | env.new_string(result)
126 | .expect("Unable to collect device sensors.")
127 | .into_raw()
128 | }
129 |
--------------------------------------------------------------------------------
/native/src/common/logging.rs:
--------------------------------------------------------------------------------
1 | #[cfg(debug_assertions)]
2 | use android_logger::{Config, FilterBuilder};
3 | #[cfg(debug_assertions)]
4 | use log::LevelFilter;
5 |
6 | #[cfg(debug_assertions)]
7 | use crate::EXTENDED_LOGGING;
8 |
9 | #[cfg(debug_assertions)]
10 | pub fn init_logger() {
11 | let config = Config::default()
12 | .with_max_level(LevelFilter::Trace) // limit log level
13 | .with_tag("rasp-android")
14 | .with_filter(
15 | // configure messages for specific crate
16 | if EXTENDED_LOGGING {
17 | FilterBuilder::new().build()
18 | } else {
19 | FilterBuilder::new()
20 | .parse("debug,hello::crate=error")
21 | .build()
22 | },
23 | );
24 |
25 | android_logger::init_once(config);
26 | log_android(LevelFilter::Debug, "Native logging enabled");
27 | }
28 |
29 | #[cfg(debug_assertions)]
30 | pub fn log_android(level: LevelFilter, message: &str) {
31 | match level {
32 | LevelFilter::Off => {}
33 | LevelFilter::Error => error!("{}", message),
34 | LevelFilter::Warn => warn!("{}", message),
35 | LevelFilter::Info => info!("{}", message),
36 | LevelFilter::Debug => debug!("{}", message),
37 | LevelFilter::Trace => trace!("{}", message),
38 | }
39 | }
40 |
41 | #[cfg(not(debug_assertions))]
42 | pub fn init_logger() {
43 | // do nothing
44 | }
45 |
--------------------------------------------------------------------------------
/native/src/common/package.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::{JObject, JValue};
2 | use jni::JNIEnv;
3 |
4 | use crate::common::util;
5 |
6 | pub fn has_emulator_packages(env: &mut JNIEnv, context: &JObject) -> bool {
7 | is_package_suspicious(env, context, &SUSPICIOUS_EMULATOR_PACKAGES)
8 | }
9 |
10 | pub fn has_root_packages(env: &mut JNIEnv, context: &JObject) -> bool {
11 | is_package_suspicious(env, context, &ROOT_APP_PACKAGES)
12 | }
13 |
14 | pub fn has_root_cloaking_packages(env: &mut JNIEnv, context: &JObject) -> bool {
15 | is_package_suspicious(env, context, &ROOT_CLOAKING_APP_PACKAGES)
16 | }
17 |
18 | fn is_package_suspicious(env: &mut JNIEnv, context: &JObject, suspicious_package: &[&str]) -> bool {
19 | let package_manager = JObject::try_from(
20 | env.call_method(
21 | context,
22 | "getPackageManager",
23 | "()Landroid/content/pm/PackageManager;",
24 | &[],
25 | )
26 | .unwrap(),
27 | )
28 | .unwrap();
29 |
30 | let mut result = false;
31 |
32 | for package in suspicious_package.iter() {
33 | let package_info = env.call_method(
34 | &package_manager,
35 | "getPackageInfo",
36 | "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;",
37 | &[
38 | JValue::Object(&JObject::from(env.new_string(package).unwrap())),
39 | JValue::Int(0),
40 | ],
41 | );
42 |
43 | util::ignore_error(env);
44 |
45 | if package_info.is_ok() {
46 | result = true;
47 | break;
48 | }
49 | }
50 |
51 | result
52 | }
53 |
54 | /**
55 | * Suspicious emulator packages.
56 | */
57 | const SUSPICIOUS_EMULATOR_PACKAGES: [&str; 9] = [
58 | "com.google.android.launcher.layouts.genymotion",
59 | "com.bluestacks",
60 | "com.vphone.launcher",
61 | "com.bluestacks.appmart",
62 | "com.bignox",
63 | "com.bignox.app",
64 | "com.microvirt.tools",
65 | "com.microvirt.download",
66 | "com.mumu.store",
67 | ];
68 |
69 | /**
70 | * Known root app packages.
71 | */
72 | const ROOT_APP_PACKAGES: [&str; 43] = [
73 | "com.noshufou.android.su",
74 | "com.noshufou.android.su.elite",
75 | "com.koushikdutta.superuser",
76 | "com.thirdparty.superuser",
77 | "com.topjohnwu.magisk",
78 | "com.kingo.roo",
79 | "com.zhiqupk.root.global",
80 | "com.smedialink.oneclickroot",
81 | "com.alephzain.framaroo",
82 | "com.yellowes.su",
83 | "com.kingroot.kinguser",
84 | "com.zachspong.temprootremovejb",
85 | "com.ramdroid.appquarantine",
86 | "eu.chainfire.supersu",
87 | "stericson.busybox",
88 | "com.alephzain.framaroot",
89 | "com.kingo.root",
90 | "com.koushikdutta.rommanager",
91 | "com.koushikdutta.rommanager.license",
92 | "com.dimonvideo.luckypatcher",
93 | "com.chelpus.lackypatch",
94 | "com.ramdroid.appquarantinepro",
95 | "com.xmodgame",
96 | "com.cih.game_cih",
97 | "com.charles.lpoqasert",
98 | "catch_.me_.if_.you_.can_",
99 | "org.blackmart.market",
100 | "com.allinone.free",
101 | "com.repodroid.app",
102 | "org.creeplays.hack",
103 | "com.baseappfull.fwd",
104 | "com.zmapp",
105 | "com.dv.marketmod.installer",
106 | "org.mobilism.android",
107 | "com.android.wp.net.log",
108 | "com.android.camera.update",
109 | "cc.madkite.freedom",
110 | "com.solohsu.android.edxp.manager",
111 | "org.meowcat.edxposed.manager",
112 | "com.android.vending.billing.InAppBillingService.COIN",
113 | "com.android.vending.billing.InAppBillingService.LUCK",
114 | "com.chelpus.luckypatcher",
115 | "com.blackmartalpha",
116 | ];
117 |
118 | /**
119 | * Known root cloaking app packages.
120 | */
121 | const ROOT_CLOAKING_APP_PACKAGES: [&str; 8] = [
122 | "com.devadvance.rootcloak",
123 | "com.devadvance.rootcloakplus",
124 | "de.robv.android.xposed.installer",
125 | "com.saurik.substrate",
126 | "com.amphoras.hidemyroot",
127 | "com.amphoras.hidemyrootadfree",
128 | "com.formyhm.hiderootPremium",
129 | "com.formyhm.hideroot",
130 | ];
131 |
--------------------------------------------------------------------------------
/native/src/common/property.rs:
--------------------------------------------------------------------------------
1 | use crate::common::system;
2 | use jni::JNIEnv;
3 | #[cfg(debug_assertions)]
4 | use log::LevelFilter::Debug;
5 |
6 | #[cfg(debug_assertions)]
7 | use crate::common::logging::log_android;
8 |
9 | pub struct Property<'a> {
10 | pub name: &'a str,
11 | pub suspicious_value: Option<&'a str>,
12 | }
13 |
14 | impl Property<'_> {
15 | pub fn looks_suspicious(&self, found_value: Option<&str>) -> bool {
16 | found_value == self.suspicious_value
17 | }
18 | }
19 |
20 | pub fn emulator_properties_found(env: &mut JNIEnv) -> bool {
21 | properties_count(env, &KNOWN_SUSPICIOUS_EMU_PROPERTIES) >= EMULATOR_PROPERTIES_THRESHOLD
22 | }
23 |
24 | pub fn root_properties_found(env: &mut JNIEnv) -> bool {
25 | properties_count(env, &KNOWN_ROOT_PROPERTIES) > 0
26 | }
27 |
28 | fn properties_count(env: &mut JNIEnv, properties: &[Property]) -> u8 {
29 | let mut counter = 0;
30 |
31 | properties.iter().for_each(|property| {
32 | let found_property = system::get_prop(env, &property.name.to_string());
33 | let looks_like_emulator = property.looks_suspicious(if found_property.is_empty() {
34 | None
35 | } else {
36 | Some(found_property.as_str())
37 | });
38 |
39 | if looks_like_emulator {
40 | counter += 1;
41 | }
42 | });
43 |
44 | #[cfg(debug_assertions)]
45 | log_android(Debug, format!("Properties count: {}", counter).as_str());
46 | counter
47 | }
48 |
49 | /**
50 | * The minimum qemu properties threshold which indicates whether device is suspicious or not.
51 | */
52 | const EMULATOR_PROPERTIES_THRESHOLD: u8 = 10;
53 |
54 | /**
55 | * Known qemu properties.
56 | */
57 | pub const KNOWN_SUSPICIOUS_EMU_PROPERTIES: [Property; 20] = [
58 | Property {
59 | name: "init.svc.qemud",
60 | suspicious_value: None,
61 | },
62 | Property {
63 | name: "init.svc.qemu-props",
64 | suspicious_value: None,
65 | },
66 | Property {
67 | name: "qemu.hw.mainkeys",
68 | suspicious_value: None,
69 | },
70 | Property {
71 | name: "qemu.sf.fake_camera",
72 | suspicious_value: None,
73 | },
74 | Property {
75 | name: "qemu.sf.lcd_density",
76 | suspicious_value: None,
77 | },
78 | Property {
79 | name: "ro.bootloader",
80 | suspicious_value: Some("unknown"),
81 | },
82 | Property {
83 | name: "ro.bootmode",
84 | suspicious_value: Some("unknown"),
85 | },
86 | Property {
87 | name: "ro.hardware",
88 | suspicious_value: Some("goldfish"),
89 | },
90 | Property {
91 | name: "ro.hardware",
92 | suspicious_value: Some("ranchu"),
93 | },
94 | Property {
95 | name: "ro.kernel.android.qemud",
96 | suspicious_value: None,
97 | },
98 | Property {
99 | name: "ro.kernel.qemu.gles",
100 | suspicious_value: None,
101 | },
102 | Property {
103 | name: "ro.kernel.qemu",
104 | suspicious_value: Some("1"),
105 | },
106 | Property {
107 | name: "ro.product.device",
108 | suspicious_value: Some("generic"),
109 | },
110 | Property {
111 | name: "ro.product.model",
112 | suspicious_value: Some("sdk"),
113 | },
114 | Property {
115 | name: "ro.product.name",
116 | suspicious_value: Some("sdk"),
117 | },
118 | Property {
119 | name: "ro.serialno",
120 | suspicious_value: None,
121 | },
122 | Property {
123 | name: "ro.secure",
124 | suspicious_value: Some("0"),
125 | },
126 | Property {
127 | name: "ro.product.cpu.abilist",
128 | suspicious_value: Some("x86"),
129 | },
130 | Property {
131 | name: "ro.product.model",
132 | suspicious_value: Some("vmos"),
133 | },
134 | Property {
135 | name: "ro.product.vendor.name",
136 | suspicious_value: Some("vmos"),
137 | },
138 | ];
139 |
140 | /**
141 | * Known root properties.
142 | */
143 | pub const KNOWN_ROOT_PROPERTIES: [Property; 2] = [
144 | Property {
145 | name: "ro.secure",
146 | suspicious_value: Some("0"),
147 | },
148 | Property {
149 | name: "ro.debuggable",
150 | suspicious_value: Some("1"),
151 | },
152 | ];
153 |
--------------------------------------------------------------------------------
/native/src/common/system.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::sync::Mutex;
3 |
4 | use crate::common::util;
5 | use jni::errors::Error;
6 | use jni::objects::{JObject, JString, JValue};
7 | use jni::JNIEnv;
8 | use once_cell::sync::Lazy;
9 |
10 | /**
11 | * Helper class which purpose is to store properties that was accessed alongside with its access status.
12 | * @property failedViaReflection whether accessing property with reflections failed.
13 | * @property failedViaProcessOut whether accessing property with reading getprop process
14 | * output failed.
15 | */
16 | #[allow(dead_code)]
17 | struct PropertiesAccessStatus {
18 | tried_with_reflection: bool,
19 | tried_with_process_out: bool,
20 | value: Option,
21 | }
22 |
23 | /**
24 | * Map of the property names and its accessing status(whether they were attempted to access already or not).
25 | */
26 | static FAILED_ATTEMPTS_MAP: Lazy>> =
27 | Lazy::new(|| {
28 | let map = HashMap::new();
29 | Mutex::new(map)
30 | });
31 |
32 | pub fn get_prop(env: &mut JNIEnv, property_name: &String) -> String {
33 | if let Some(property) = FAILED_ATTEMPTS_MAP
34 | .lock()
35 | .unwrap()
36 | .get(&property_name.clone())
37 | {
38 | if (property.tried_with_process_out) && property.value.is_some() {
39 | return flatten_result(property.value.to_owned());
40 | } else if property.tried_with_process_out && property.value.is_none() {
41 | return "".to_string();
42 | }
43 | }
44 |
45 | let result = try_with_system_out(env, property_name);
46 |
47 | flatten_result(result)
48 | }
49 |
50 | fn flatten_result(result: Option) -> String {
51 | result.unwrap_or_default()
52 | }
53 |
54 | fn try_with_system_out(env: &mut JNIEnv, property_name: &String) -> Option {
55 | let runtime_clz = env.find_class("java/lang/Runtime").unwrap();
56 |
57 | let runtime_obj = JObject::try_from(
58 | env.call_static_method(runtime_clz, "getRuntime", "()Ljava/lang/Runtime;", &[])
59 | .unwrap(),
60 | )
61 | .unwrap();
62 |
63 | let exec = env.call_method(
64 | runtime_obj,
65 | "exec",
66 | "(Ljava/lang/String;)Ljava/lang/Process;",
67 | &[JValue::Object(&JObject::from(
68 | env.new_string(format!("{} {} {}", "getprop", property_name, ""))
69 | .unwrap(),
70 | ))],
71 | );
72 |
73 | if exec.is_err() {
74 | return util::ignore_error_with_default(env, || None::);
75 | }
76 |
77 | let process_obj = JObject::try_from(exec.unwrap()).unwrap();
78 |
79 | // 1
80 | let buff_reader_clz = env.find_class("java/io/BufferedReader").unwrap();
81 |
82 | // 2
83 | let input_stream_reader_clz = env.find_class("java/io/InputStreamReader").unwrap();
84 |
85 | let input_stream_obj = JObject::try_from(
86 | env.call_method(
87 | process_obj,
88 | "getInputStream",
89 | "()Ljava/io/InputStream;",
90 | &[],
91 | )
92 | .unwrap(),
93 | )
94 | .unwrap();
95 |
96 | let args = &[JValue::Object(&input_stream_obj)];
97 |
98 | let input_stream_reader_obj = &env
99 | .new_object(&input_stream_reader_clz, "(Ljava/io/InputStream;)V", args)
100 | .unwrap();
101 |
102 | let reader = env
103 | .new_object(
104 | &buff_reader_clz,
105 | "(Ljava/io/Reader;)V",
106 | &[JValue::Object(input_stream_reader_obj)],
107 | )
108 | .unwrap();
109 |
110 | let line = env.call_method(reader, "readLine", "()Ljava/lang/String;", &[]);
111 |
112 | if line.is_err() {
113 | return util::ignore_error_with_default(env, || None::);
114 | }
115 |
116 | let property = JObject::try_from(line.unwrap()).unwrap();
117 |
118 | let result: String = env
119 | .get_string(&JString::from(property))
120 | .unwrap()
121 | .to_str()
122 | .unwrap()
123 | .to_string();
124 |
125 | let result = match !result.is_empty() {
126 | true => Some(result),
127 | false => None,
128 | };
129 |
130 | update_attempts_map(property_name.clone(), true, false, &result);
131 |
132 | result
133 | }
134 |
135 | fn update_attempts_map(key: String, process: bool, reflection: bool, result: &Option) {
136 | FAILED_ATTEMPTS_MAP.lock().unwrap().insert(
137 | key,
138 | PropertiesAccessStatus {
139 | tried_with_reflection: reflection,
140 | tried_with_process_out: process,
141 | value: result.clone(),
142 | },
143 | );
144 | }
145 |
146 | pub fn get_system_service<'a>(
147 | env: &mut JNIEnv<'a>,
148 | context: JObject<'a>,
149 | service_name: &str,
150 | ) -> Result, Error> {
151 | let system_service = env
152 | .call_method(
153 | context,
154 | "getSystemService",
155 | "(Ljava/lang/String;)Ljava/lang/Object;",
156 | &[JValue::Object(&JObject::from(
157 | env.new_string(service_name).unwrap(),
158 | ))],
159 | )
160 | .unwrap();
161 | JObject::try_from(system_service)
162 | }
163 |
--------------------------------------------------------------------------------
/native/src/common/util.rs:
--------------------------------------------------------------------------------
1 | #![allow(dead_code)]
2 |
3 | use jni::JNIEnv;
4 |
5 | pub fn throw_java(env: &mut JNIEnv, message: &str) {
6 | let _ = env.throw(format!("Securevale exception: {}", message));
7 | }
8 |
9 | pub fn ignore_error(env: &mut JNIEnv) {
10 | if env.exception_check().unwrap() {
11 | let _ = env.exception_clear();
12 | }
13 | }
14 |
15 | pub fn ignore_error_with_default(env: &mut JNIEnv, f: impl FnOnce() -> V) -> V {
16 | ignore_error(env);
17 | f()
18 | }
19 |
--------------------------------------------------------------------------------
/native/src/debuggable.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 |
3 | use crate::common::util;
4 | use jni::objects::{JClass, JObject, JString, JValue};
5 | use jni::sys::jboolean;
6 | use jni::JNIEnv;
7 |
8 | // isDebuggable
9 | #[no_mangle]
10 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_debugger_checks_DebuggableChecks_a<'a>(
11 | mut env: JNIEnv<'a>,
12 | _class: JClass,
13 | context: JObject<'a>,
14 | ) -> jboolean {
15 | let app_info_class = env
16 | .find_class("android/content/pm/ApplicationInfo")
17 | .unwrap();
18 |
19 | let application_info = JObject::try_from(
20 | env.call_method(
21 | context,
22 | "getApplicationInfo",
23 | "()Landroid/content/pm/ApplicationInfo;",
24 | &[],
25 | )
26 | .unwrap(),
27 | )
28 | .unwrap();
29 |
30 | let flags = env.get_field(application_info, "flags", "I").unwrap();
31 |
32 | let app_info_debuggable = env
33 | .get_static_field(app_info_class, "FLAG_DEBUGGABLE", "I")
34 | .unwrap();
35 |
36 | u8::from((flags.i().unwrap() & app_info_debuggable.i().unwrap()) != 0)
37 | }
38 |
39 | // isDebuggerConnected
40 | #[no_mangle]
41 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_debugger_checks_DebuggableChecks_k(
42 | mut env: JNIEnv,
43 | _class: JClass,
44 | ) -> jboolean {
45 | let debug_class = env.find_class("android/os/Debug").unwrap();
46 |
47 | let is_debugger_connected = env
48 | .call_static_method(debug_class, "isDebuggerConnected", "()Z", &[])
49 | .unwrap();
50 |
51 | u8::from(is_debugger_connected.z().unwrap())
52 | }
53 |
54 | // someoneIsWaitingForDebugger
55 | #[no_mangle]
56 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_debugger_checks_DebuggableChecks_d(
57 | mut env: JNIEnv,
58 | _class: JClass,
59 | ) -> jboolean {
60 | let debug_class = env.find_class("android/os/Debug").unwrap();
61 |
62 | let is_debugger_connected = env
63 | .call_static_method(debug_class, "waitingForDebugger", "()Z", &[])
64 | .unwrap();
65 |
66 | u8::from(is_debugger_connected.z().unwrap())
67 | }
68 |
69 | // hasDebugBuildConfig
70 | #[no_mangle]
71 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_debugger_checks_DebuggableChecks_b<'a>(
72 | mut env: JNIEnv<'a>,
73 | _class: JClass,
74 | context: JObject<'a>,
75 | ) -> jboolean {
76 | let clazz = env.find_class("java/lang/Class").unwrap();
77 |
78 | let package = JObject::try_from(
79 | env.call_method(context, "getPackageName", "()Ljava/lang/String;", &[])
80 | .unwrap(),
81 | )
82 | .unwrap();
83 |
84 | let package = &JString::from(package);
85 |
86 | let package_name = env.get_string(package).unwrap();
87 |
88 | let build_config_class = env.call_static_method(
89 | clazz,
90 | "forName",
91 | "(Ljava/lang/String;)Ljava/lang/Class;",
92 | &[JValue::Object(&JObject::from(
93 | env.new_string(package_name.to_str().unwrap().to_owned() + ".BuildConfig")
94 | .unwrap(),
95 | ))],
96 | );
97 |
98 | if build_config_class.is_err() {
99 | return util::ignore_error_with_default(&mut env, || u8::from(false));
100 | }
101 |
102 | let clazz_obj = JObject::try_from(build_config_class.unwrap());
103 |
104 | if clazz_obj.is_err() {
105 | return util::ignore_error_with_default(&mut env, || u8::from(false));
106 | }
107 |
108 | let field = JObject::try_from(
109 | env.call_method(
110 | clazz_obj.unwrap(),
111 | "getField",
112 | "(Ljava/lang/String;)Ljava/lang/reflect/Field;",
113 | &[JValue::Object(&JObject::from(
114 | env.new_string("DEBUG").unwrap(),
115 | ))],
116 | )
117 | .unwrap(),
118 | )
119 | .unwrap();
120 |
121 | let is_debuggable = env
122 | .call_method(
123 | field,
124 | "getBoolean",
125 | "(Ljava/lang/Object;)Z",
126 | &[JValue::Object(&JObject::null())],
127 | )
128 | .unwrap();
129 |
130 | u8::from(is_debuggable.z().unwrap())
131 | }
132 |
--------------------------------------------------------------------------------
/native/src/emulator.rs:
--------------------------------------------------------------------------------
1 | mod device;
2 | mod general;
3 | mod packages;
4 | pub mod properties;
5 | mod sensors;
6 |
--------------------------------------------------------------------------------
/native/src/emulator/device.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::{JClass, JObject, JString};
2 | use jni::sys::jboolean;
3 | use jni::JNIEnv;
4 |
5 | use crate::common::system;
6 |
7 | // isRadioVersionSuspicious
8 | #[no_mangle]
9 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_DeviceChecks_u(
10 | mut env: JNIEnv,
11 | _class: JClass,
12 | ) -> jboolean {
13 | let build_clazz = env.find_class("android/os/Build").unwrap();
14 |
15 | let radio_version = JObject::try_from(
16 | env.call_static_method(build_clazz, "getRadioVersion", "()Ljava/lang/String;", &[])
17 | .unwrap(),
18 | )
19 | .unwrap();
20 |
21 | let radio_version_string = &JString::from(radio_version);
22 |
23 | let binding = env.get_string(radio_version_string).unwrap();
24 |
25 | let radio_version_as_rust_str = binding.to_str().unwrap();
26 |
27 | let result =
28 | radio_version_as_rust_str.is_empty() || radio_version_as_rust_str.trim() == "1.0.0.0";
29 |
30 | u8::from(result)
31 | }
32 |
33 | // isOperatorNameAndroid
34 | #[no_mangle]
35 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_DeviceChecks_c<'a>(
36 | mut env: JNIEnv<'a>,
37 | _class: JClass,
38 | context: JObject<'a>,
39 | ) -> jboolean {
40 | let telephony_manager_obj = system::get_system_service(&mut env, context, "phone").unwrap();
41 |
42 | let network_operator_name = JObject::try_from(
43 | env.call_method(
44 | telephony_manager_obj,
45 | "getNetworkOperatorName",
46 | "()Ljava/lang/String;",
47 | &[],
48 | )
49 | .unwrap(),
50 | )
51 | .unwrap();
52 |
53 | let result = env
54 | .get_string(&JString::from(network_operator_name))
55 | .unwrap()
56 | .to_str()
57 | .unwrap()
58 | .to_lowercase()
59 | == "android";
60 |
61 | u8::from(result)
62 | }
63 |
--------------------------------------------------------------------------------
/native/src/emulator/general.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 |
3 | use jni::objects::JClass;
4 | use jni::sys::jboolean;
5 | use jni::JNIEnv;
6 |
7 | use crate::common::build::get_build_config_value;
8 | use crate::{common, common::build, common::system};
9 |
10 | const AVD_DEVICES: [&str; 4] = ["generic_x86_arm", "generic_x86", "generic", "x86"];
11 |
12 | // isAvdDevice
13 | #[no_mangle]
14 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_z(
15 | mut env: JNIEnv,
16 | _class: JClass,
17 | ) -> jboolean {
18 | let is_avd_device = AVD_DEVICES.contains(&"generic_x86_arm");
19 |
20 | let board = get_build_config_value(&mut env, build::BOARD);
21 |
22 | let result = is_avd_device || board == "goldfish_x86" || board == "unknown";
23 |
24 | u8::from(result)
25 | }
26 |
27 | // isAvdHardware
28 | #[no_mangle]
29 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_w(
30 | mut env: JNIEnv,
31 | _class: JClass,
32 | ) -> jboolean {
33 | let hardware = get_build_config_value(&mut env, build::HARDWARE);
34 |
35 | let result = hardware == "goldfish"
36 | || hardware == "unknown"
37 | || hardware == "ranchu"
38 | || hardware.contains("x86");
39 |
40 | u8::from(result)
41 | }
42 |
43 | // isMemu
44 | #[no_mangle]
45 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_t(
46 | mut env: JNIEnv,
47 | _class: JClass,
48 | ) -> jboolean {
49 | let hardware = get_build_config_value(&mut env, build::HARDWARE);
50 |
51 | u8::from(hardware == "intel")
52 | }
53 |
54 | // isGenymotion
55 | #[no_mangle]
56 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_h(
57 | mut env: JNIEnv,
58 | _class: JClass,
59 | ) -> jboolean {
60 | let hardware = get_build_config_value(&mut env, build::HARDWARE);
61 | let manufacturer = get_build_config_value(&mut env, build::MANUFACTURER);
62 | let product = get_build_config_value(&mut env, build::PRODUCT);
63 |
64 | let hasGenymotionFiles = common::files::has_genymotion_files();
65 |
66 | let result = manufacturer.contains("Genymotion")
67 | || product == "vbox86p"
68 | || hardware == "vbox86"
69 | || hasGenymotionFiles;
70 |
71 | u8::from(result)
72 | }
73 |
74 | // isNox
75 | #[no_mangle]
76 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_s(
77 | mut env: JNIEnv,
78 | _class: JClass,
79 | ) -> jboolean {
80 | let hardware = get_build_config_value(&mut env, build::HARDWARE);
81 | let product = get_build_config_value(&mut env, build::PRODUCT);
82 | let board = get_build_config_value(&mut env, build::BOARD);
83 |
84 | let hasNoxFiles = common::files::has_nox_files();
85 |
86 | let result = hardware.to_lowercase().contains("nox")
87 | || product.to_lowercase().contains("nox")
88 | || board.to_lowercase().contains("nox")
89 | || hasNoxFiles;
90 |
91 | u8::from(result)
92 | }
93 |
94 | // hasSuspiciousFiles
95 | #[no_mangle]
96 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_e(
97 | _env: JNIEnv,
98 | _class: JClass,
99 | ) -> jboolean {
100 | let has_andy_files = common::files::has_andy_files();
101 | let has_x_86_files = common::files::has_x86_files();
102 | let has_emulator_files = common::files::has_emu_files();
103 | let has_phoenix_files = common::files::has_phoenix_files();
104 |
105 | let result = has_andy_files || has_x_86_files || has_emulator_files || has_phoenix_files;
106 |
107 | u8::from(result)
108 | }
109 |
110 | // isBluestacks
111 | #[no_mangle]
112 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_l(
113 | _env: JNIEnv,
114 | _class: JClass,
115 | ) -> jboolean {
116 | u8::from(common::files::has_bluestack_files())
117 | }
118 |
119 | // isFingerprintFromEmulator
120 | #[no_mangle]
121 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_j(
122 | mut env: JNIEnv,
123 | _class: JClass,
124 | ) -> jboolean {
125 | let fingerprint = get_build_config_value(&mut env, build::FINGERPRINT);
126 |
127 | let result = fingerprint.starts_with("generic")
128 | || fingerprint.starts_with("unknown")
129 | || fingerprint.starts_with("google/sdk_gphone_")
130 | || fingerprint.contains("x86")
131 | || fingerprint.contains("debug");
132 |
133 | u8::from(result)
134 | }
135 |
136 | // isGoogleEmulator
137 | #[no_mangle]
138 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_v(
139 | mut env: JNIEnv,
140 | _class: JClass,
141 | ) -> jboolean {
142 | let model = get_build_config_value(&mut env, build::MODEL);
143 | let product = get_build_config_value(&mut env, build::PRODUCT);
144 | let brand = get_build_config_value(&mut env, build::BRAND);
145 | let device = get_build_config_value(&mut env, build::DEVICE);
146 | let tags = get_build_config_value(&mut env, build::TAGS);
147 |
148 | let hasEmulatorPipes = common::files::has_pipes();
149 |
150 | let result = model.contains("Android SDK built for x86")
151 | || model.contains("google_sdk")
152 | || model.to_lowercase().contains("droid4x")
153 | || model.starts_with("sdk_gphone_")
154 | || product == "skd"
155 | || product == "sdk_google"
156 | || product == "google_sdk"
157 | || product == "sdk_x86"
158 | || product == "sdk_gphone64_arm64"
159 | || product == "sdk_gphone_x86"
160 | || product.contains("emulator")
161 | || product.contains("simulator")
162 | || (brand.starts_with("generic") && device.starts_with("generic"))
163 | || tags == "dev-keys"
164 | || system::get_prop(&mut env, &"ro.kernel.qemu".to_string()) == "1"
165 | || hasEmulatorPipes;
166 |
167 | u8::from(result)
168 | }
169 |
170 | // mountsSuspicious
171 | #[no_mangle]
172 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_d(
173 | _env: JNIEnv,
174 | _class: JClass,
175 | ) -> jboolean {
176 | let result = common::files::find_in_file("/proc/mounts", &["vboxsf"]).unwrap_or(false);
177 |
178 | u8::from(result)
179 | }
180 |
181 | // cpuSuspicious
182 | #[no_mangle]
183 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_c(
184 | _env: JNIEnv,
185 | _class: JClass,
186 | ) -> jboolean {
187 | let result =
188 | common::files::find_in_file("/proc/cpuinfo", &["hypervisor", "Goldfish"]).unwrap_or(false);
189 |
190 | u8::from(result)
191 | }
192 |
193 | // modulesSuspicious
194 | #[no_mangle]
195 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_GeneralChecks_u(
196 | _env: JNIEnv,
197 | _class: JClass,
198 | ) -> jboolean {
199 | let result = common::files::find_in_file(
200 | "/proc/cpuinfo",
201 | &[
202 | "vboxsf",
203 | "vboxguest",
204 | "bstcamera",
205 | "bstpgaipc",
206 | "bstaudio",
207 | "bstinput",
208 | "bstvmsg",
209 | ],
210 | )
211 | .unwrap_or(false);
212 |
213 | u8::from(result)
214 | }
215 |
--------------------------------------------------------------------------------
/native/src/emulator/packages.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::{JClass, JObject};
2 | use jni::sys::jboolean;
3 | use jni::JNIEnv;
4 |
5 | use crate::common::package::has_emulator_packages;
6 |
7 | // hasSuspiciousPackages
8 | #[no_mangle]
9 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_PackageChecks_y<'a>(
10 | mut env: JNIEnv<'a>,
11 | _class: JClass,
12 | context: JObject<'a>,
13 | ) -> jboolean {
14 | u8::from(has_emulator_packages(&mut env, &context))
15 | }
16 |
--------------------------------------------------------------------------------
/native/src/emulator/properties.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::JClass;
2 | use jni::sys::jboolean;
3 | use jni::JNIEnv;
4 |
5 | use crate::common::property::emulator_properties_found;
6 |
7 | // hasQemuProperties
8 | #[no_mangle]
9 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_PropertyChecks_l(
10 | mut env: JNIEnv,
11 | _class: JClass,
12 | ) -> jboolean {
13 | u8::from(emulator_properties_found(&mut env))
14 | }
15 |
--------------------------------------------------------------------------------
/native/src/emulator/sensors.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::{JClass, JObject, JString, JValue};
2 | use jni::sys::jboolean;
3 | use jni::JNIEnv;
4 |
5 | use crate::common::system;
6 |
7 | // areSensorsFromEmulator
8 | #[no_mangle]
9 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_emulator_checks_SensorChecks_g<'a>(
10 | mut env: JNIEnv<'a>,
11 | _class: JClass,
12 | context: JObject<'a>,
13 | ) -> jboolean {
14 | let sensor_manager_obj = system::get_system_service(&mut env, context, "sensor").unwrap();
15 |
16 | let sensor_list = JObject::try_from(
17 | env.call_method(
18 | sensor_manager_obj,
19 | "getSensorList",
20 | "(I)Ljava/util/List;",
21 | &[JValue::Int(-1)],
22 | )
23 | .unwrap(),
24 | )
25 | .unwrap();
26 |
27 | let sensor_list = env.get_list(&sensor_list).unwrap();
28 |
29 | let mut iterator = sensor_list.iter(&mut env).unwrap();
30 |
31 | let result = loop {
32 | match iterator.next(&mut env) {
33 | Ok(sensor) => match sensor {
34 | Some(sensor) => {
35 | let name_field = JObject::try_from(
36 | env.call_method(sensor, "getName", "()Ljava/lang/String;", &[])
37 | .unwrap(),
38 | )
39 | .unwrap();
40 |
41 | let name_string = &JString::from(name_field);
42 | let name = env.get_string(name_string).unwrap();
43 |
44 | if name.to_str().unwrap().to_lowercase().contains("goldfish") {
45 | break true;
46 | }
47 | }
48 | None => break false,
49 | },
50 | Err(_) => break false,
51 | }
52 | };
53 |
54 | u8::from(result)
55 | }
56 |
--------------------------------------------------------------------------------
/native/src/lib.rs:
--------------------------------------------------------------------------------
1 | #[cfg(debug_assertions)]
2 | #[macro_use]
3 | extern crate log;
4 | #[cfg(debug_assertions)]
5 | extern crate android_logger;
6 |
7 | use jni::objects::JClass;
8 | use jni::JNIEnv;
9 |
10 | mod debuggable;
11 | mod emulator;
12 |
13 | mod common;
14 | mod root;
15 |
16 | const EXTENDED_LOGGING: bool = false;
17 |
18 | #[allow(clippy::missing_safety_doc)]
19 | #[no_mangle]
20 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_SecureApp_initJni(
21 | _env: JNIEnv,
22 | _class: JClass,
23 | ) {
24 | common::logging::init_logger();
25 | }
26 |
--------------------------------------------------------------------------------
/native/src/root.rs:
--------------------------------------------------------------------------------
1 | mod packages;
2 | mod properties;
3 | mod superuser;
4 |
--------------------------------------------------------------------------------
/native/src/root/packages.rs:
--------------------------------------------------------------------------------
1 | use jni::objects::{JClass, JObject};
2 | use jni::sys::jboolean;
3 | use jni::JNIEnv;
4 |
5 | use crate::common::package::{has_root_cloaking_packages, has_root_packages};
6 |
7 | // hasRootAppPackages
8 | #[no_mangle]
9 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_AppsChecks_r<'a>(
10 | mut env: JNIEnv<'a>,
11 | _class: JClass,
12 | context: JObject<'a>,
13 | ) -> jboolean {
14 | let result = has_root_packages(&mut env, &context);
15 | u8::from(result)
16 | }
17 |
18 | // hasRootCloakingAppPackages
19 | #[no_mangle]
20 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_AppsChecks_k<'a>(
21 | mut env: JNIEnv<'a>,
22 | _class: JClass,
23 | context: JObject<'a>,
24 | ) -> jboolean {
25 | u8::from(has_root_cloaking_packages(&mut env, &context))
26 | }
27 |
--------------------------------------------------------------------------------
/native/src/root/properties.rs:
--------------------------------------------------------------------------------
1 | use crate::common::build::get_build_config_value;
2 | use crate::common::{build, property};
3 | use jni::objects::JClass;
4 | use jni::sys::jboolean;
5 | use jni::JNIEnv;
6 |
7 | // hasRootProperties
8 | #[no_mangle]
9 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_PropertyChecks_y(
10 | mut env: JNIEnv,
11 | _class: JClass,
12 | ) -> jboolean {
13 | u8::from(property::root_properties_found(&mut env))
14 | }
15 |
16 | // isEngBuild
17 | #[no_mangle]
18 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_PropertyChecks_g(
19 | mut env: JNIEnv,
20 | _class: JClass,
21 | ) -> jboolean {
22 | u8::from(get_build_config_value(&mut env, build::TYPE) == "eng")
23 | }
24 |
--------------------------------------------------------------------------------
/native/src/root/superuser.rs:
--------------------------------------------------------------------------------
1 | #![allow(non_snake_case)]
2 |
3 | use jni::objects::{JClass, JObject, JObjectArray, JString, JValue};
4 | use jni::sys::jboolean;
5 | use jni::JNIEnv;
6 | #[cfg(debug_assertions)]
7 | use log::LevelFilter::Debug;
8 |
9 | use crate::common::build::get_build_config_value;
10 | use crate::common::files::{
11 | has_busybox_files, has_dynamic_busybox_files, has_dynamic_su_files, has_su_files,
12 | NON_WRITABLE_PATHS,
13 | };
14 | #[cfg(debug_assertions)]
15 | use crate::common::logging::log_android;
16 | use crate::{common::build, common::util};
17 |
18 | // isSuperUser
19 | #[no_mangle]
20 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_DeviceChecks_p(
21 | mut env: JNIEnv,
22 | _class: JClass,
23 | ) -> jboolean {
24 | let has_su_files = has_su_files();
25 |
26 | let has_busybox_files = has_busybox_files();
27 |
28 | let system_clz = env.find_class("java/lang/System").unwrap();
29 |
30 | let paths_exec = JObject::try_from(
31 | env.call_static_method(
32 | system_clz,
33 | "getenv",
34 | "(Ljava/lang/String;)Ljava/lang/String;",
35 | &[JValue::Object(&JObject::from(
36 | env.new_string("PATH").unwrap(),
37 | ))],
38 | )
39 | .unwrap(),
40 | )
41 | .unwrap();
42 |
43 | let jstring = JString::from(paths_exec);
44 | let binding = env.get_string_unchecked(&jstring).unwrap();
45 |
46 | let paths = binding.to_str().unwrap();
47 |
48 | let hasSuPaths2 = has_dynamic_su_files(paths.split(":").collect::>().as_slice());
49 |
50 | let hasBusyBoxFiles2 =
51 | has_dynamic_busybox_files(paths.split(":").collect::>().as_slice());
52 |
53 | let runtime_clz = env.find_class("java/lang/Runtime").unwrap();
54 |
55 | let runtime_obj = JObject::try_from(
56 | env.call_static_method(runtime_clz, "getRuntime", "()Ljava/lang/Runtime;", &[])
57 | .unwrap(),
58 | )
59 | .unwrap();
60 |
61 | let exec = env.call_method(
62 | runtime_obj,
63 | "exec",
64 | "(Ljava/lang/String;)Ljava/lang/Process;",
65 | &[JValue::Object(&JObject::from(
66 | env.new_string("su").unwrap(),
67 | ))],
68 | );
69 |
70 | let isSu = exec.is_ok();
71 |
72 | if !isSu {
73 | util::ignore_error(&mut env);
74 | }
75 |
76 | let result = hasSuPaths2 || has_su_files || isSu || has_busybox_files || hasBusyBoxFiles2;
77 |
78 | u8::from(result)
79 | }
80 |
81 | // hasTestTags
82 | #[no_mangle]
83 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_DeviceChecks_c(
84 | mut env: JNIEnv,
85 | _class: JClass,
86 | ) -> jboolean {
87 | let tags = get_build_config_value(&mut env, build::TAGS).contains("test-keys");
88 | let fingerprint =
89 | get_build_config_value(&mut env, build::FINGERPRINT).contains("genric.*test-keys");
90 | let display = get_build_config_value(&mut env, build::DISPLAY).contains(".*test-keys");
91 |
92 | u8::from(tags || fingerprint || display)
93 | }
94 |
95 | // hasWritableNonWritablePaths
96 | #[no_mangle]
97 | pub unsafe extern "C" fn Java_com_securevale_rasp_android_root_checks_FileChecks_w(
98 | mut env: JNIEnv,
99 | _class: JClass,
100 | ) -> jboolean {
101 | let runtime_clz = env.find_class("java/lang/Runtime").unwrap();
102 |
103 | let runtime_obj = JObject::try_from(
104 | env.call_static_method(runtime_clz, "getRuntime", "()Ljava/lang/Runtime;", &[])
105 | .unwrap(),
106 | )
107 | .unwrap();
108 |
109 | let exec = env.call_method(
110 | runtime_obj,
111 | "exec",
112 | "(Ljava/lang/String;)Ljava/lang/Process;",
113 | &[JValue::Object(&JObject::from(
114 | env.new_string("mount").unwrap(),
115 | ))],
116 | );
117 |
118 | if exec.is_err() {
119 | #[cfg(debug_assertions)]
120 | log_android(
121 | Debug,
122 | format!("RW paths: {}", "process exec failed").as_str(),
123 | );
124 | return u8::from(false);
125 | }
126 |
127 | let process_obj = JObject::try_from(exec.unwrap()).unwrap();
128 |
129 | let input_stream_obj = JObject::try_from(
130 | env.call_method(
131 | process_obj,
132 | "getInputStream",
133 | "()Ljava/io/InputStream;",
134 | &[],
135 | )
136 | .unwrap(),
137 | )
138 | .unwrap();
139 |
140 | if input_stream_obj.is_null() {
141 | #[cfg(debug_assertions)]
142 | log_android(Debug, format!("RW paths: {}", "null").as_str());
143 | return u8::from(false);
144 | }
145 |
146 | let scanner_clz = env.find_class("java/util/Scanner").unwrap();
147 |
148 | let scanner_obj = &env
149 | .new_object(
150 | &scanner_clz,
151 | "(Ljava/io/InputStream;)V",
152 | &[JValue::Object(&input_stream_obj)],
153 | )
154 | .unwrap();
155 |
156 | let prop = env.call_method(
157 | scanner_obj,
158 | "useDelimiter",
159 | "(Ljava/lang/String;)Ljava/util/Scanner;",
160 | &[JValue::Object(&JObject::from(
161 | env.new_string("\\A").unwrap(),
162 | ))],
163 | );
164 |
165 | let prop_val = env.call_method(
166 | JObject::try_from(prop.unwrap()).unwrap(),
167 | "next",
168 | "()Ljava/lang/String;",
169 | &[],
170 | );
171 |
172 | let mountRead = env.call_method(
173 | JObject::try_from(prop_val.unwrap()).unwrap(),
174 | "split",
175 | "(Ljava/lang/String;)[Ljava/lang/String;",
176 | &[JValue::Object(&JObject::from(
177 | env.new_string("\n").unwrap(),
178 | ))],
179 | );
180 |
181 | let lines = JObjectArray::from(JObject::try_from(mountRead.unwrap()).unwrap());
182 |
183 | if lines.is_null() {
184 | return u8::from(false);
185 | }
186 |
187 | let lines_length = env.get_array_length(&lines).unwrap();
188 |
189 | for n in 0..lines_length {
190 | let line = env.get_object_array_element(&lines, n);
191 |
192 | #[cfg(debug_assertions)]
193 | {
194 | let line_str = &JString::from(env.get_object_array_element(&lines, n).unwrap());
195 |
196 | let line_to_log = env.get_string(line_str).unwrap();
197 |
198 | log_android(
199 | Debug,
200 | format!("LINE: {}", line_to_log.to_str().unwrap()).as_str(),
201 | );
202 | }
203 |
204 | let args_array = env.call_method(
205 | line.unwrap(),
206 | "split",
207 | "(Ljava/lang/String;)[Ljava/lang/String;",
208 | &[JValue::Object(&JObject::from(env.new_string(" ").unwrap()))],
209 | );
210 |
211 | let args = JObjectArray::from(JObject::try_from(args_array.unwrap()).unwrap());
212 |
213 | let args_length = env.get_array_length(&args).unwrap();
214 |
215 | if args_length < 6 {
216 | // Not enough options per line, just skip.
217 | continue;
218 | }
219 |
220 | let mountPointString = &JString::from(env.get_object_array_element(&args, 2).unwrap());
221 |
222 | let mountPoint = env.get_string(mountPointString).unwrap();
223 |
224 | #[cfg(debug_assertions)]
225 | log_android(
226 | Debug,
227 | format!("MOUNT POINT {}", mountPoint.to_str().unwrap()).as_str(),
228 | );
229 |
230 | let mountOptionsString = &JString::from(env.get_object_array_element(&args, 5).unwrap());
231 |
232 | let mountOptions = env.get_string(mountOptionsString).unwrap();
233 |
234 | #[cfg(debug_assertions)]
235 | log_android(
236 | Debug,
237 | format!("MOUNT OPTIONS {}", mountOptions.to_str().unwrap()).as_str(),
238 | );
239 |
240 | for suspicious_path in NON_WRITABLE_PATHS.iter() {
241 | if mountPoint
242 | .to_str()
243 | .unwrap()
244 | .eq_ignore_ascii_case(suspicious_path)
245 | {
246 | let mut correctedMountOptions = mountOptions.to_str().unwrap().replace('(', "");
247 | correctedMountOptions = correctedMountOptions.replace(')', "");
248 |
249 | for option in correctedMountOptions.split(',') {
250 | if option.eq_ignore_ascii_case("rw") {
251 | return u8::from(true);
252 | }
253 | }
254 | }
255 | }
256 | }
257 |
258 | u8::from(false)
259 | }
260 |
--------------------------------------------------------------------------------
/packages/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/packages/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.vanniktech.maven.publish.SonatypeHost
2 |
3 | plugins {
4 | alias(libs.plugins.android.library)
5 | alias(libs.plugins.kotlin.android)
6 | alias(libs.plugins.maven.publish)
7 | }
8 |
9 | android {
10 | namespace = "com.securevale.rasp.android.packages"
11 | compileSdk = rootProject.extra["compileSdkVersion"] as Int
12 |
13 | defaultConfig {
14 | minSdk = rootProject.extra["minSdkVersion"] as Int
15 | targetSdk = rootProject.extra["targetSdkVersion"] as Int
16 | }
17 |
18 | buildTypes {
19 | release {
20 | }
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility = JavaVersion.VERSION_17
25 | targetCompatibility = JavaVersion.VERSION_17
26 | }
27 |
28 | kotlinOptions {
29 | jvmTarget = JavaVersion.VERSION_17.toString()
30 | }
31 | }
32 |
33 | dependencies {
34 | }
35 |
36 | mavenPublishing {
37 |
38 | group = "com.securevale.rasp"
39 | version = rootProject.extra["versionName"] as String
40 | coordinates("com.securevale", "rasp-packages", rootProject.extra["versionName"] as String)
41 |
42 | publishToMavenCentral(SonatypeHost.S01, true)
43 | signAllPublications()
44 |
45 | pom {
46 | name.set("Android RASP Packages")
47 | description.set("Runtime Application Self Protection library for Android with package queries.")
48 | inceptionYear.set("2025")
49 | url.set("https://github.com/securevale/android-rasp")
50 |
51 | licenses {
52 | license {
53 | name.set("The Apache License, Version 2.0")
54 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
55 | distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
56 | }
57 | }
58 |
59 | developers {
60 | developer {
61 | id.set("bmaciejm")
62 | name.set("Bartosz Macięga")
63 | url.set("https://github.com/bmaciejm")
64 | }
65 | }
66 |
67 | scm {
68 | url.set("https://github.com/securevale/android-rasp")
69 | connection.set("scm:git:git://github.com/securevale/android-rasp.git")
70 | developerConnection.set("scm:git:ssh://git@github.com/securevale/android-rasp.git")
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/packages/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/rasp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *.so
3 |
--------------------------------------------------------------------------------
/rasp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.vanniktech.maven.publish.SonatypeHost
2 | import org.jetbrains.dokka.DokkaConfiguration.Visibility
3 | import org.jetbrains.dokka.gradle.DokkaTask
4 |
5 | plugins {
6 | alias(libs.plugins.android.library)
7 | alias(libs.plugins.kotlin.android)
8 | alias(libs.plugins.detekt)
9 | alias(libs.plugins.dokka)
10 | alias(libs.plugins.maven.publish)
11 | }
12 |
13 | android {
14 | compileSdk = rootProject.extra["compileSdkVersion"] as Int
15 |
16 | defaultConfig {
17 | minSdk = rootProject.extra["minSdkVersion"] as Int
18 | targetSdk = rootProject.extra["targetSdkVersion"] as Int
19 |
20 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
21 | consumerProguardFile("consumer-rules.pro")
22 | }
23 |
24 | buildTypes {
25 | getByName("release") {
26 | isMinifyEnabled = false
27 | consumerProguardFiles("consumer-rules.pro")
28 | }
29 |
30 | getByName("debug") {
31 | isMinifyEnabled = false
32 | enableUnitTestCoverage = true
33 | }
34 | }
35 |
36 | compileOptions {
37 | sourceCompatibility = JavaVersion.VERSION_17
38 | targetCompatibility = JavaVersion.VERSION_17
39 | }
40 |
41 | kotlinOptions {
42 | jvmTarget = JavaVersion.VERSION_17.toString()
43 | }
44 |
45 | namespace = "com.securevale.rasp.android"
46 |
47 | lint {
48 | abortOnError = true
49 | warningsAsErrors = true
50 | disable += "GradleDependency"
51 | }
52 |
53 | detekt {
54 | config = files("$projectDir/config/detekt.yml")
55 | }
56 |
57 | testOptions {
58 | unitTests {
59 | isIncludeAndroidResources = true
60 | unitTests.isReturnDefaultValues = true
61 | }
62 | }
63 | }
64 |
65 | dependencies {
66 | implementation(libs.androidx.corektx)
67 | implementation(libs.androidx.appcompat)
68 | implementation(libs.google.material)
69 |
70 | debugImplementation(libs.square.leakcanary)
71 | }
72 |
73 | tasks.dokkaHtml.configure { configureDokka(this, "dokkaHtml") }
74 | tasks.dokkaJavadoc.configure { configureDokka(this, "dokkaJavadoc") }
75 |
76 | fun configureDokka(dokkaTask: DokkaTask, outputDir: String) = dokkaTask.apply {
77 | outputDirectory.set(buildDir.resolve(outputDir))
78 |
79 | dokkaSourceSets {
80 | configureEach {
81 | documentedVisibilities.set(
82 | setOf(
83 | Visibility.PUBLIC,
84 | Visibility.PRIVATE,
85 | Visibility.PROTECTED,
86 | Visibility.INTERNAL,
87 | Visibility.PACKAGE
88 | )
89 | )
90 |
91 | skipEmptyPackages.set(true)
92 | }
93 | }
94 | }
95 |
96 | mavenPublishing {
97 |
98 | group = "com.securevale.rasp"
99 | version = rootProject.extra["versionName"] as String
100 | coordinates("com.securevale", "rasp-android", rootProject.extra["versionName"] as String)
101 |
102 | publishToMavenCentral(SonatypeHost.S01, true)
103 | signAllPublications()
104 |
105 | pom {
106 | name.set("Android RASP")
107 | description.set("Runtime Application Self Protection library for Android.")
108 | inceptionYear.set("2023")
109 | url.set("https://github.com/securevale/android-rasp")
110 |
111 | licenses {
112 | license {
113 | name.set("The Apache License, Version 2.0")
114 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
115 | distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
116 | }
117 | }
118 |
119 | developers {
120 | developer {
121 | id.set("bmaciejm")
122 | name.set("Bartosz Macięga")
123 | url.set("https://github.com/bmaciejm")
124 | }
125 | }
126 |
127 | scm {
128 | url.set("https://github.com/securevale/android-rasp")
129 | connection.set("scm:git:git://github.com/securevale/android-rasp.git")
130 | developerConnection.set("scm:git:ssh://git@github.com/securevale/android-rasp.git")
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/rasp/config/detekt.yml:
--------------------------------------------------------------------------------
1 | build:
2 | maxIssues: 0
3 | excludeCorrectable: false
4 | weights:
5 | # complexity: 2
6 | # LongParameterList: 1
7 | # style: 1
8 | # comments: 1
9 |
10 | config:
11 | validation: true
12 | warningsAsErrors: false
13 | checkExhaustiveness: false
14 | # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
15 | excludes: ''
16 |
17 | processors:
18 | active: true
19 | exclude:
20 | - 'DetektProgressListener'
21 | # - 'KtFileCountProcessor'
22 | # - 'PackageCountProcessor'
23 | # - 'ClassCountProcessor'
24 | # - 'FunctionCountProcessor'
25 | # - 'PropertyCountProcessor'
26 | # - 'ProjectComplexityProcessor'
27 | # - 'ProjectCognitiveComplexityProcessor'
28 | # - 'ProjectLLOCProcessor'
29 | # - 'ProjectCLOCProcessor'
30 | # - 'ProjectLOCProcessor'
31 | # - 'ProjectSLOCProcessor'
32 | # - 'LicenseHeaderLoaderExtension'
33 |
34 | console-reports:
35 | active: true
36 | exclude:
37 | - 'ProjectStatisticsReport'
38 | - 'ComplexityReport'
39 | - 'NotificationReport'
40 | - 'FindingsReport'
41 | - 'FileBasedFindingsReport'
42 | # - 'LiteFindingsReport'
43 |
44 | output-reports:
45 | active: true
46 | exclude:
47 | # - 'TxtOutputReport'
48 | # - 'XmlOutputReport'
49 | # - 'HtmlOutputReport'
50 | # - 'MdOutputReport'
51 | # - 'SarifOutputReport'
52 |
53 | comments:
54 | active: true
55 | AbsentOrWrongFileLicense:
56 | active: false
57 | licenseTemplateFile: 'license.template'
58 | licenseTemplateIsRegex: false
59 | CommentOverPrivateFunction:
60 | active: false
61 | CommentOverPrivateProperty:
62 | active: false
63 | DeprecatedBlockTag:
64 | active: true
65 | EndOfSentenceFormat:
66 | active: false
67 | endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
68 | KDocReferencesNonPublicProperty:
69 | active: false
70 | excludes: [ '**/test/**', '**/androidTest/**' ]
71 | OutdatedDocumentation:
72 | active: true
73 | matchTypeParameters: true
74 | matchDeclarationsOrder: true
75 | allowParamOnConstructorProperties: false
76 | UndocumentedPublicClass:
77 | active: false
78 | excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
79 | searchInNestedClass: true
80 | searchInInnerClass: true
81 | searchInInnerObject: true
82 | searchInInnerInterface: true
83 | searchInProtectedClass: false
84 | UndocumentedPublicFunction:
85 | active: false
86 | excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
87 | searchProtectedFunction: false
88 | UndocumentedPublicProperty:
89 | active: false
90 | excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**' ]
91 | searchProtectedProperty: false
92 |
93 | complexity:
94 | active: true
95 | CognitiveComplexMethod:
96 | active: true
97 | threshold: 15
98 | ComplexCondition:
99 | active: true
100 | threshold: 4
101 | ComplexInterface:
102 | active: true
103 | threshold: 10
104 | includeStaticDeclarations: false
105 | includePrivateDeclarations: false
106 | ignoreOverloaded: false
107 | CyclomaticComplexMethod:
108 | active: true
109 | threshold: 20
110 | ignoreSingleWhenExpression: false
111 | ignoreSimpleWhenEntries: false
112 | ignoreNestingFunctions: false
113 | nestingFunctions:
114 | - 'also'
115 | - 'apply'
116 | - 'forEach'
117 | - 'isNotNull'
118 | - 'ifNull'
119 | - 'let'
120 | - 'run'
121 | - 'use'
122 | - 'with'
123 | LabeledExpression:
124 | active: false
125 | ignoredLabels: [ ]
126 | LargeClass:
127 | active: true
128 | threshold: 600
129 | LongMethod:
130 | active: true
131 | threshold: 60
132 | LongParameterList:
133 | active: true
134 | functionThreshold: 5
135 | constructorThreshold: 6
136 | excludes: [ '**/test/**', '**/androidTest/**' ]
137 | ignoreDefaultParameters: false
138 | ignoreDataClasses: true
139 | ignoreAnnotatedParameter: [ ]
140 | MethodOverloading:
141 | active: false
142 | threshold: 6
143 | NamedArguments:
144 | active: true
145 | threshold: 3
146 | ignoreArgumentsMatchingNames: false
147 | NestedBlockDepth:
148 | active: true
149 | threshold: 4
150 | NestedScopeFunctions:
151 | active: false
152 | threshold: 1
153 | functions:
154 | - 'kotlin.apply'
155 | - 'kotlin.run'
156 | - 'kotlin.with'
157 | - 'kotlin.let'
158 | - 'kotlin.also'
159 | ReplaceSafeCallChainWithRun:
160 | active: true
161 | StringLiteralDuplication:
162 | active: false
163 | excludes: [ '**/test/**', '**/androidTest/**' ]
164 | threshold: 3
165 | ignoreAnnotation: true
166 | excludeStringsWithLessThan5Characters: true
167 | ignoreStringsRegex: '$^'
168 | TooManyFunctions:
169 | active: false
170 | excludes: [ '**/test/**', '**/androidTest/**' ]
171 | thresholdInFiles: 11
172 | thresholdInClasses: 11
173 | thresholdInInterfaces: 11
174 | thresholdInObjects: 11
175 | thresholdInEnums: 11
176 | ignoreDeprecated: false
177 | ignorePrivate: false
178 | ignoreOverridden: false
179 |
180 | coroutines:
181 | active: true
182 | GlobalCoroutineUsage:
183 | active: false
184 | InjectDispatcher:
185 | active: true
186 | dispatcherNames:
187 | - 'IO'
188 | - 'Default'
189 | - 'Unconfined'
190 | RedundantSuspendModifier:
191 | active: true
192 | SleepInsteadOfDelay:
193 | active: true
194 | SuspendFunWithCoroutineScopeReceiver:
195 | active: false
196 | SuspendFunWithFlowReturnType:
197 | active: true
198 |
199 | empty-blocks:
200 | active: true
201 | EmptyCatchBlock:
202 | active: true
203 | allowedExceptionNameRegex: '_|(ignore|expected).*'
204 | EmptyClassBlock:
205 | active: true
206 | EmptyDefaultConstructor:
207 | active: true
208 | EmptyDoWhileBlock:
209 | active: true
210 | EmptyElseBlock:
211 | active: true
212 | EmptyFinallyBlock:
213 | active: true
214 | EmptyForBlock:
215 | active: true
216 | EmptyFunctionBlock:
217 | active: true
218 | ignoreOverridden: false
219 | EmptyIfBlock:
220 | active: true
221 | EmptyInitBlock:
222 | active: true
223 | EmptyKtFile:
224 | active: true
225 | EmptySecondaryConstructor:
226 | active: true
227 | EmptyTryBlock:
228 | active: true
229 | EmptyWhenBlock:
230 | active: true
231 | EmptyWhileBlock:
232 | active: true
233 |
234 | exceptions:
235 | active: true
236 | ExceptionRaisedInUnexpectedLocation:
237 | active: true
238 | methodNames:
239 | - 'equals'
240 | - 'finalize'
241 | - 'hashCode'
242 | - 'toString'
243 | InstanceOfCheckForException:
244 | active: true
245 | excludes: [ '**/test/**', '**/androidTest/**' ]
246 | NotImplementedDeclaration:
247 | active: true
248 | ObjectExtendsThrowable:
249 | active: true
250 | PrintStackTrace:
251 | active: true
252 | RethrowCaughtException:
253 | active: true
254 | ReturnFromFinally:
255 | active: true
256 | ignoreLabeled: false
257 | SwallowedException:
258 | active: true
259 | ignoredExceptionTypes:
260 | - 'InterruptedException'
261 | - 'MalformedURLException'
262 | - 'NumberFormatException'
263 | - 'ParseException'
264 | allowedExceptionNameRegex: '_|(ignore|expected).*'
265 | ThrowingExceptionFromFinally:
266 | active: true
267 | ThrowingExceptionInMain:
268 | active: false
269 | ThrowingExceptionsWithoutMessageOrCause:
270 | active: true
271 | excludes: [ '**/test/**', '**/androidTest/**' ]
272 | exceptions:
273 | - 'ArrayIndexOutOfBoundsException'
274 | - 'Exception'
275 | - 'IllegalArgumentException'
276 | - 'IllegalMonitorStateException'
277 | - 'IllegalStateException'
278 | - 'IndexOutOfBoundsException'
279 | - 'NullPointerException'
280 | - 'RuntimeException'
281 | - 'Throwable'
282 | ThrowingNewInstanceOfSameException:
283 | active: true
284 | TooGenericExceptionCaught:
285 | active: true
286 | excludes: [ '**/test/**', '**/androidTest/**' ]
287 | exceptionNames:
288 | - 'ArrayIndexOutOfBoundsException'
289 | - 'Error'
290 | - 'Exception'
291 | - 'IllegalMonitorStateException'
292 | - 'IndexOutOfBoundsException'
293 | - 'NullPointerException'
294 | - 'RuntimeException'
295 | - 'Throwable'
296 | allowedExceptionNameRegex: '_|(ignore|expected).*'
297 | TooGenericExceptionThrown:
298 | active: true
299 | exceptionNames:
300 | - 'Error'
301 | - 'Exception'
302 | - 'RuntimeException'
303 | - 'Throwable'
304 |
305 | naming:
306 | active: true
307 | BooleanPropertyNaming:
308 | active: false
309 | allowedPattern: '^(is|has|are)'
310 | ignoreOverridden: true
311 | ClassNaming:
312 | active: true
313 | classPattern: '[A-Z][a-zA-Z0-9]*'
314 | ConstructorParameterNaming:
315 | active: true
316 | parameterPattern: '[a-z][A-Za-z0-9]*'
317 | privateParameterPattern: '[a-z][A-Za-z0-9]*'
318 | excludeClassPattern: '$^'
319 | ignoreOverridden: true
320 | EnumNaming:
321 | active: true
322 | enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
323 | ForbiddenClassName:
324 | active: false
325 | forbiddenName: [ ]
326 | FunctionMaxLength:
327 | active: true
328 | excludes: [ '**/test/**', '**/androidTest/**' ]
329 | maximumFunctionNameLength: 30
330 | FunctionMinLength:
331 | active: false
332 | minimumFunctionNameLength: 3
333 | FunctionNaming:
334 | active: true
335 | excludes: [ '**/test/**', '**/androidTest/**' ]
336 | functionPattern: '[a-z][a-zA-Z0-9]*'
337 | excludeClassPattern: '$^'
338 | ignoreOverridden: true
339 | FunctionParameterNaming:
340 | active: true
341 | parameterPattern: '[a-z][A-Za-z0-9]*'
342 | excludeClassPattern: '$^'
343 | ignoreOverridden: true
344 | InvalidPackageDeclaration:
345 | active: true
346 | rootPackage: ''
347 | requireRootInDeclaration: false
348 | LambdaParameterNaming:
349 | active: false
350 | parameterPattern: '[a-z][A-Za-z0-9]*|_'
351 | MatchingDeclarationName:
352 | active: true
353 | mustBeFirst: true
354 | MemberNameEqualsClassName:
355 | active: true
356 | ignoreOverridden: true
357 | NoNameShadowing:
358 | active: true
359 | NonBooleanPropertyPrefixedWithIs:
360 | active: false
361 | ObjectPropertyNaming:
362 | active: true
363 | constantPattern: '[A-Za-z][_A-Za-z0-9]*'
364 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
365 | privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
366 | PackageNaming:
367 | active: true
368 | packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
369 | TopLevelPropertyNaming:
370 | active: true
371 | constantPattern: '[A-Z][_A-Z0-9]*'
372 | propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
373 | privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
374 | VariableMaxLength:
375 | active: true
376 | maximumVariableNameLength: 64
377 | VariableMinLength:
378 | active: false
379 | minimumVariableNameLength: 1
380 | VariableNaming:
381 | active: true
382 | variablePattern: '[a-z][A-Za-z0-9]*'
383 | privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
384 | excludeClassPattern: '$^'
385 | ignoreOverridden: true
386 |
387 | performance:
388 | active: true
389 | ArrayPrimitive:
390 | active: true
391 | CouldBeSequence:
392 | active: true
393 | threshold: 3
394 | ForEachOnRange:
395 | active: true
396 | excludes: [ '**/test/**', '**/androidTest/**' ]
397 | SpreadOperator:
398 | active: true
399 | excludes: [ '**/test/**', '**/androidTest/**' ]
400 | UnnecessaryPartOfBinaryExpression:
401 | active: true
402 | UnnecessaryTemporaryInstantiation:
403 | active: true
404 |
405 | potential-bugs:
406 | active: true
407 | AvoidReferentialEquality:
408 | active: true
409 | forbiddenTypePatterns:
410 | - 'kotlin.String'
411 | CastToNullableType:
412 | active: false
413 | Deprecation:
414 | active: false
415 | DontDowncastCollectionTypes:
416 | active: false
417 | DoubleMutabilityForCollection:
418 | active: true
419 | mutableTypes:
420 | - 'kotlin.collections.MutableList'
421 | - 'kotlin.collections.MutableMap'
422 | - 'kotlin.collections.MutableSet'
423 | - 'java.util.ArrayList'
424 | - 'java.util.LinkedHashSet'
425 | - 'java.util.HashSet'
426 | - 'java.util.LinkedHashMap'
427 | - 'java.util.HashMap'
428 | ElseCaseInsteadOfExhaustiveWhen:
429 | active: true
430 | EqualsAlwaysReturnsTrueOrFalse:
431 | active: true
432 | EqualsWithHashCodeExist:
433 | active: true
434 | ExitOutsideMain:
435 | active: false
436 | ExplicitGarbageCollectionCall:
437 | active: true
438 | HasPlatformType:
439 | active: true
440 | IgnoredReturnValue:
441 | active: true
442 | restrictToConfig: true
443 | returnValueAnnotations:
444 | - '*.CheckResult'
445 | - '*.CheckReturnValue'
446 | ignoreReturnValueAnnotations:
447 | - '*.CanIgnoreReturnValue'
448 | returnValueTypes:
449 | - 'kotlin.sequences.Sequence'
450 | - 'kotlinx.coroutines.flow.*Flow'
451 | - 'java.util.stream.*Stream'
452 | ignoreFunctionCall: [ ]
453 | ImplicitDefaultLocale:
454 | active: true
455 | ImplicitUnitReturnType:
456 | active: false
457 | allowExplicitReturnType: true
458 | InvalidRange:
459 | active: true
460 | IteratorHasNextCallsNextMethod:
461 | active: true
462 | IteratorNotThrowingNoSuchElementException:
463 | active: true
464 | LateinitUsage:
465 | active: false
466 | excludes: [ '**/test/**', '**/androidTest/**' ]
467 | ignoreOnClassesPattern: ''
468 | MapGetWithNotNullAssertionOperator:
469 | active: true
470 | MissingPackageDeclaration:
471 | active: true
472 | excludes: [ '**/*.kts' ]
473 | NullCheckOnMutableProperty:
474 | active: false
475 | NullableToStringCall:
476 | active: true
477 | UnconditionalJumpStatementInLoop:
478 | active: false
479 | UnnecessaryNotNullCheck:
480 | active: false
481 | UnnecessaryNotNullOperator:
482 | active: true
483 | UnnecessarySafeCall:
484 | active: true
485 | UnreachableCatchBlock:
486 | active: true
487 | UnreachableCode:
488 | active: true
489 | UnsafeCallOnNullableType:
490 | active: true
491 | excludes: [ '**/test/**', '**/androidTest/**' ]
492 | UnsafeCast:
493 | active: true
494 | UnusedUnaryOperator:
495 | active: true
496 | UselessPostfixExpression:
497 | active: true
498 | WrongEqualsTypeParameter:
499 | active: true
500 |
501 | style:
502 | active: true
503 | AlsoCouldBeApply:
504 | active: false
505 | CanBeNonNullable:
506 | active: false
507 | CascadingCallWrapping:
508 | active: false
509 | includeElvis: true
510 | ClassOrdering:
511 | active: false
512 | CollapsibleIfStatements:
513 | active: false
514 | DataClassContainsFunctions:
515 | active: false
516 | conversionFunctionPrefix:
517 | - 'to'
518 | DataClassShouldBeImmutable:
519 | active: false
520 | DestructuringDeclarationWithTooManyEntries:
521 | active: true
522 | maxDestructuringEntries: 3
523 | EqualsNullCall:
524 | active: true
525 | EqualsOnSignatureLine:
526 | active: false
527 | ExplicitCollectionElementAccessMethod:
528 | active: false
529 | ExplicitItLambdaParameter:
530 | active: true
531 | ExpressionBodySyntax:
532 | active: false
533 | includeLineWrapping: false
534 | ForbiddenComment:
535 | active: true
536 | values:
537 | - 'FIXME:'
538 | - 'STOPSHIP:'
539 | - 'TODO:'
540 | allowedPatterns: ''
541 | customMessage: ''
542 | ForbiddenImport:
543 | active: false
544 | imports: [ ]
545 | forbiddenPatterns: ''
546 | ForbiddenMethodCall:
547 | active: false
548 | methods:
549 | - reason: 'print does not allow you to configure the output stream. Use a logger instead.'
550 | value: 'kotlin.io.print'
551 | - reason: 'println does not allow you to configure the output stream. Use a logger instead.'
552 | value: 'kotlin.io.println'
553 | ForbiddenSuppress:
554 | active: false
555 | rules: [ ]
556 | ForbiddenVoid:
557 | active: true
558 | ignoreOverridden: false
559 | ignoreUsageInGenerics: false
560 | FunctionOnlyReturningConstant:
561 | active: true
562 | ignoreOverridableFunction: true
563 | ignoreActualFunction: true
564 | excludedFunctions: [ ]
565 | LoopWithTooManyJumpStatements:
566 | active: true
567 | maxJumpCount: 1
568 | MagicNumber:
569 | active: true
570 | excludes: [ '**/test/**', '**/androidTest/**', '**/*.kts', '**/*Check.kt' ]
571 | ignoreNumbers:
572 | - '-1'
573 | - '0'
574 | - '1'
575 | - '2'
576 | ignoreHashCodeFunction: true
577 | ignorePropertyDeclaration: false
578 | ignoreLocalVariableDeclaration: false
579 | ignoreConstantDeclaration: true
580 | ignoreCompanionObjectPropertyDeclaration: true
581 | ignoreAnnotation: false
582 | ignoreNamedArgument: true
583 | ignoreEnums: false
584 | ignoreRanges: false
585 | ignoreExtensionFunctions: true
586 | MandatoryBracesIfStatements:
587 | active: false
588 | MandatoryBracesLoops:
589 | active: false
590 | MaxChainedCallsOnSameLine:
591 | active: false
592 | maxChainedCalls: 5
593 | MaxLineLength:
594 | active: true
595 | maxLineLength: 120
596 | excludePackageStatements: true
597 | excludeImportStatements: true
598 | excludeCommentStatements: true
599 | excludeRawStrings: true
600 | MayBeConst:
601 | active: true
602 | ModifierOrder:
603 | active: true
604 | MultilineLambdaItParameter:
605 | active: false
606 | MultilineRawStringIndentation:
607 | active: false
608 | indentSize: 4
609 | NestedClassesVisibility:
610 | active: true
611 | NewLineAtEndOfFile:
612 | active: true
613 | NoTabs:
614 | active: false
615 | NullableBooleanCheck:
616 | active: false
617 | ObjectLiteralToLambda:
618 | active: true
619 | OptionalAbstractKeyword:
620 | active: true
621 | OptionalUnit:
622 | active: false
623 | OptionalWhenBraces:
624 | active: false
625 | PreferToOverPairSyntax:
626 | active: false
627 | ProtectedMemberInFinalClass:
628 | active: true
629 | RedundantExplicitType:
630 | active: false
631 | RedundantHigherOrderMapUsage:
632 | active: true
633 | RedundantVisibilityModifierRule:
634 | active: false
635 | ReturnCount:
636 | active: true
637 | max: 2
638 | excludedFunctions:
639 | - 'equals'
640 | excludeLabeled: false
641 | excludeReturnFromLambda: true
642 | excludeGuardClauses: false
643 | SafeCast:
644 | active: true
645 | SerialVersionUIDInSerializableClass:
646 | active: true
647 | SpacingBetweenPackageAndImports:
648 | active: true
649 | ThrowsCount:
650 | active: true
651 | max: 2
652 | excludeGuardClauses: false
653 | TrailingWhitespace:
654 | active: false
655 | TrimMultilineRawString:
656 | active: false
657 | UnderscoresInNumericLiterals:
658 | active: false
659 | acceptableLength: 4
660 | allowNonStandardGrouping: false
661 | UnnecessaryAbstractClass:
662 | active: true
663 | UnnecessaryAnnotationUseSiteTarget:
664 | active: false
665 | UnnecessaryApply:
666 | active: true
667 | UnnecessaryBackticks:
668 | active: false
669 | UnnecessaryFilter:
670 | active: true
671 | UnnecessaryInheritance:
672 | active: true
673 | UnnecessaryInnerClass:
674 | active: false
675 | UnnecessaryLet:
676 | active: false
677 | UnnecessaryParentheses:
678 | active: false
679 | allowForUnclearPrecedence: false
680 | UntilInsteadOfRangeTo:
681 | active: false
682 | UnusedImports:
683 | active: true
684 | UnusedPrivateClass:
685 | active: true
686 | UnusedPrivateMember:
687 | active: true
688 | allowedNames: '(_|ignored|expected|serialVersionUID)'
689 | UseAnyOrNoneInsteadOfFind:
690 | active: true
691 | UseArrayLiteralsInAnnotations:
692 | active: true
693 | UseCheckNotNull:
694 | active: true
695 | UseCheckOrError:
696 | active: true
697 | UseDataClass:
698 | active: false
699 | allowVars: false
700 | UseEmptyCounterpart:
701 | active: false
702 | UseIfEmptyOrIfBlank:
703 | active: false
704 | UseIfInsteadOfWhen:
705 | active: false
706 | UseIsNullOrEmpty:
707 | active: true
708 | UseOrEmpty:
709 | active: true
710 | UseRequire:
711 | active: true
712 | UseRequireNotNull:
713 | active: true
714 | UseSumOfInsteadOfFlatMapSize:
715 | active: false
716 | UselessCallOnNotNull:
717 | active: true
718 | UtilityClassWithPublicConstructor:
719 | active: true
720 | VarCouldBeVal:
721 | active: true
722 | ignoreLateinitVar: false
723 | WildcardImport:
724 | active: true
725 | excludes: [ '**/test/**', '**/androidTest/**', '**/DeviceInfo.kt' ]
726 | excludeImports:
727 | - 'java.util.*'
--------------------------------------------------------------------------------
/rasp/consumer-rules.pro:
--------------------------------------------------------------------------------
1 | -keepclasseswithmembernames,includedescriptorclasses class com.securevale.rasp.android.**.*Checks {
2 | native ;
3 | }
4 | -keepclasseswithmembernames,includedescriptorclasses class com.securevale.rasp.android.SecureApp
5 |
--------------------------------------------------------------------------------
/rasp/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/rasp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/SecureApp.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android
2 |
3 | /**
4 | * Library initalisation object.
5 | */
6 | object SecureApp {
7 |
8 | init {
9 | System.loadLibrary("native")
10 | }
11 |
12 | /**
13 | * Method that needs to be called for library initalisation.
14 | * It should be called only once par app's lifecycle.
15 | * Recommended place to call it is app's Application class.
16 | */
17 | fun init() {
18 | initJni()
19 | }
20 |
21 | private external fun initJni()
22 | }
23 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/api/CheckSubscriber.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.api
2 |
3 | import com.securevale.rasp.android.api.result.ExtendedResult
4 |
5 | /**
6 | * The interface for doing more granular checks, it wraps the
7 | * [com.securevale.rasp.android.api.result.CheckType] with the result of the check.
8 | */
9 | fun interface CheckSubscriber {
10 | /**
11 | * Callback function which will be called with the result of the check.
12 | */
13 | fun onCheck(result: ExtendedResult)
14 | }
15 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/api/SecureAppChecker.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.api
2 |
3 | import android.content.Context
4 | import com.securevale.rasp.android.api.result.CheckType
5 | import com.securevale.rasp.android.api.result.Result
6 | import com.securevale.rasp.android.check.CHECK_ALL
7 | import com.securevale.rasp.android.check.ChecksMediator
8 | import com.securevale.rasp.android.util.isDebug
9 |
10 | /**
11 | * Class for checking whether app is secure.
12 | */
13 | class SecureAppChecker private constructor() {
14 |
15 | @PublishedApi
16 | internal lateinit var mediator: ChecksMediator
17 |
18 | /**
19 | * The function for checking whether device is secure.
20 | * It will trigger all checks which were configured using the [Builder].
21 | * @return the [Result] of the check.
22 | */
23 | @Suppress("NOTHING_TO_INLINE")
24 | inline fun check(): Result = mediator.check()
25 |
26 | /**
27 | * The function for checking whether device is secure.
28 | * It will trigger all checks which were passed via [checkOnlyFor] param and returns result(s)
29 | * to the [subscriber] only if the vulnerability will be found.
30 | * @param granular whether it should return result separately for every sub-check or just result of whole check.
31 | * @param checkOnlyFor checks that should be triggered.
32 | * @param subscriber subscriber where the results will be passed on.
33 | */
34 | @Suppress("NOTHING_TO_INLINE")
35 | inline fun subscribeVulnerabilitiesOnly(
36 | granular: Boolean = false,
37 | checkOnlyFor: Array = CHECK_ALL,
38 | subscriber: CheckSubscriber
39 | ) {
40 | if (granular) {
41 | mediator.checkGranular(checkOnlyFor, subscriber, true)
42 | } else {
43 | mediator.checkMerged(checkOnlyFor, subscriber, true)
44 | }
45 | }
46 |
47 | /**
48 | * The function for checking whether device is secure.
49 | * It will trigger all checks which were passed via [checkOnlyFor] param and returns result(s)
50 | * to the [subscriber].
51 | * @param granular whether it should return result separately for every sub-check or just result of whole check.
52 | * @param checkOnlyFor checks that should be triggered.
53 | * @param subscriber the subscriber where the results will be passed on.
54 | */
55 | @Suppress("NOTHING_TO_INLINE")
56 | inline fun subscribe(
57 | granular: Boolean = false,
58 | checkOnlyFor: Array = CHECK_ALL,
59 | subscriber: CheckSubscriber
60 | ) {
61 | if (granular) {
62 | mediator.checkGranular(checkOnlyFor, subscriber, false)
63 | } else {
64 | mediator.checkMerged(checkOnlyFor, subscriber, false)
65 | }
66 | }
67 |
68 | /**
69 | * Builder for [SecureAppChecker].
70 | * @property context the Context used for configuring checks.
71 | * @property checkEmulator whether emulator checks should be triggered.
72 | * @property checkDebugger whether checks for debug should be triggered.
73 | * @property checkRoot whether checks for root should be triggered.
74 | */
75 | class Builder(
76 | private val context: Context,
77 | private val checkEmulator: Boolean = false,
78 | private val checkDebugger: Boolean = false,
79 | private val checkRoot: Boolean = false
80 | ) {
81 |
82 | /**
83 | * Marks whether library will run in debug mode (it shows debug logs from the lib).
84 | */
85 | fun debug(): Builder {
86 | isDebug = true
87 | return this
88 | }
89 |
90 | /**
91 | * Returns [SecureAppChecker] instance.
92 | * @return the configured [SecureAppChecker] instance.
93 | */
94 | fun build() = SecureAppChecker().apply {
95 | mediator = ChecksMediator(context, checkEmulator, checkDebugger, checkRoot)
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/api/result/Checks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.api.result
2 |
3 | /**
4 | * Base interface for check types.
5 | */
6 | interface CheckType
7 |
8 | /**
9 | * The enum containing all debugger checks.
10 | */
11 | enum class DebuggerChecks : CheckType {
12 | DebuggerCheck, Debuggable, DebugField, DebuggerConnected;
13 |
14 | override fun toString(): String = "Debugger: $name"
15 | }
16 |
17 | /**
18 | * The enum containing all emulator checks.
19 | */
20 | enum class EmulatorChecks : CheckType {
21 | EmulatorCheck,
22 | AvdDevice,
23 | AvdHardware,
24 | Genymotion,
25 | Nox,
26 | Memu,
27 | GoogleEmulator,
28 | Fingerprint,
29 | SuspiciousFiles,
30 | Bluestacks,
31 | Sensors,
32 | OperatorName,
33 | RadioVersion,
34 | SuspiciousPackages,
35 | SuspiciousProperties,
36 | Mounts,
37 | CPU,
38 | Modules;
39 |
40 | override fun toString(): String = "Emulator: $name"
41 | }
42 |
43 | /**
44 | * The enum containing all root checks.
45 | */
46 | enum class RootChecks : CheckType {
47 | RootCheck,
48 | SuUser,
49 | TestTags,
50 | RootApps,
51 | EngBuild,
52 | RootCloakingApps,
53 | WritablePaths,
54 | SuspiciousProperties;
55 |
56 | override fun toString(): String = "Root: $name"
57 | }
58 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/api/result/Result.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.api.result
2 |
3 | /**
4 | * The result of the vulnerability check.
5 | */
6 | sealed class Result {
7 | data object EmulatorFound : Result()
8 | data object DebuggerEnabled : Result()
9 | data object Rooted : Result()
10 | data object Secure : Result()
11 | }
12 |
13 | /**
14 | * The extended result class which contains [CheckType] alongside with its result.
15 | */
16 | data class ExtendedResult(
17 | val checkType: CheckType,
18 | val vulnerable: Boolean,
19 | val thresholdExceeded: Boolean
20 | )
21 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/check/Check.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.check
2 |
3 | import com.securevale.rasp.android.api.CheckSubscriber
4 | import com.securevale.rasp.android.api.result.CheckType
5 |
6 | /**
7 | * Base interface for checking.
8 | */
9 | internal interface Check {
10 | fun check(
11 | checksToTrigger: Array = CHECK_ALL,
12 | granular: Boolean = false,
13 | subscriber: CheckSubscriber? = null
14 | ): T
15 | }
16 |
17 | /**
18 | * The default vulnerability check which returns [CheckResult].
19 | */
20 | internal interface DefaultCheck : Check
21 |
22 | /**
23 | * Base class for vulnerability checks.
24 | */
25 | internal abstract class ProbabilityCheck : DefaultCheck {
26 |
27 | /**
28 | * Map of checks that were chosen to be triggered.
29 | */
30 | protected abstract val checksMap: Map WrappedCheckResult>
31 |
32 | /**
33 | * List of checks to be triggered. It contains functions which returns the Int values (check wages)
34 | * when successful.
35 | */
36 | protected var checks: List<() -> WrappedCheckResult> = emptyList()
37 |
38 | /**
39 | * Type this check is assigned to.
40 | */
41 | protected abstract val checkType: String
42 |
43 | /**
44 | * Threshold, if this limit is exceeded, the check will indicate a failure,
45 | * signaling that the app is vulnerable.
46 | */
47 | protected abstract val threshold: Int
48 |
49 | /**
50 | * Function for collecting checks that needs to be called.
51 | * @param checksToTrigger array of checks that needs to be called or [CHECK_ALL] if all checks
52 | * should be called.
53 | */
54 | private fun collectChecks(checksToTrigger: Array): List<() -> WrappedCheckResult> {
55 | val checksToBeCalled = mutableListOf<() -> WrappedCheckResult>()
56 | if (checksToTrigger.contentEquals(CHECK_ALL)) {
57 | checksToBeCalled.addAll(checksMap.values)
58 | return checksToBeCalled
59 | }
60 |
61 | for (check in checksToTrigger) {
62 | checksMap[check]?.let {
63 | checksToBeCalled.add(it)
64 | }
65 | }
66 | return checksToBeCalled
67 | }
68 |
69 | /**
70 | * The check function, it triggers all checks from the [checks].
71 | * @param checksToTrigger the checks that should be triggered.
72 | * @param granular whether it should return results from every [CheckType] as a granular one.
73 | * @param subscriber the subscriber that should be additionally notified about the result(s)
74 | * if [granular] option was chosen.
75 | * @return the result of the check which is [CheckResult.Vulnerable] or [CheckResult.Secure]
76 | * based on the fact whether threshold was matched or exceeded.
77 | */
78 | override fun check(
79 | checksToTrigger: Array,
80 | granular: Boolean,
81 | subscriber: CheckSubscriber?
82 | ): CheckResult {
83 | checks = collectChecks(checksToTrigger)
84 | return if (granular) {
85 | checkNotNull(subscriber) { "Subscriber must be set when want to use granular checks" }
86 | checks.fireGranular(subscriber, threshold)
87 | CheckResult.Ignored
88 | } else {
89 | if (checks.fireChecks(threshold)) CheckResult.Vulnerable else CheckResult.Secure
90 | }
91 | }
92 | }
93 |
94 | /**
95 | * Constant for triggering all checks.
96 | */
97 | val CHECK_ALL = emptyArray()
98 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/check/CheckResult.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.check
2 |
3 | /**
4 | * Result used by [DefaultCheck] (which is implemented by all internal check classes).
5 | */
6 | internal sealed interface CheckResult {
7 | data object Vulnerable : CheckResult
8 | data object Secure : CheckResult
9 | data object Ignored : CheckResult
10 | }
11 |
12 | /**
13 | * Helper function for checking whether vulnerability was found.
14 | */
15 | internal fun CheckResult.vulnerabilityFound() = this is CheckResult.Vulnerable
16 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/check/ChecksMediator.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.check
2 |
3 | import android.content.Context
4 | import com.securevale.rasp.android.api.CheckSubscriber
5 | import com.securevale.rasp.android.api.result.CheckType
6 | import com.securevale.rasp.android.api.result.DebuggerChecks
7 | import com.securevale.rasp.android.api.result.EmulatorChecks
8 | import com.securevale.rasp.android.api.result.ExtendedResult
9 | import com.securevale.rasp.android.api.result.Result
10 | import com.securevale.rasp.android.api.result.RootChecks
11 | import com.securevale.rasp.android.debugger.DebuggerCheck
12 | import com.securevale.rasp.android.emulator.EmulatorCheck
13 | import com.securevale.rasp.android.root.RootCheck
14 |
15 | /**
16 | * Class that acts as a mediator between the public's API of the library and its internals.
17 | * @param context the Context used for checks configuration.
18 | * @param emulator whether emulator checks should be triggered.
19 | * @param debugger whether checks for debug should be triggered.
20 | * @param root whether checks for root should be triggered.
21 | */
22 | @PublishedApi
23 | internal class ChecksMediator(
24 | context: Context,
25 | emulator: Boolean,
26 | debugger: Boolean,
27 | root: Boolean
28 | ) {
29 |
30 | /**
31 | * The emulator check instance.
32 | */
33 | private var emulatorCheck: EmulatorCheck? = null
34 |
35 | /**
36 | * The debugger check instance.
37 | */
38 | private var debugCheck: DebuggerCheck? = null
39 |
40 | /**
41 | * The root check instance.
42 | */
43 | private var rootCheck: RootCheck? = null
44 |
45 | init {
46 | if (emulator) emulatorCheck = EmulatorCheck(context)
47 | if (debugger) debugCheck = DebuggerCheck(context)
48 | if (root) rootCheck = RootCheck(context)
49 | }
50 |
51 | /**
52 | * Function that will trigger granular checks and notify [subscriber] about the result(s).
53 | * @param checkOnlyFor checks that should be triggered.
54 | * @param subscriber subscriber where the results will be passed on.
55 | * @param vulnerabilitiesOnly whether it should notify [subscriber] only if vulnerability is found.
56 | */
57 | fun checkGranular(
58 | checkOnlyFor: Array = CHECK_ALL,
59 | subscriber: CheckSubscriber,
60 | vulnerabilitiesOnly: Boolean
61 | ) {
62 | val internalSubscriber = getSubscriber(vulnerabilitiesOnly, subscriber)
63 | emulatorCheck?.check(checkOnlyFor, true, internalSubscriber)
64 | debugCheck?.check(checkOnlyFor, true, internalSubscriber)
65 | rootCheck?.check(checkOnlyFor, true, internalSubscriber)
66 | }
67 |
68 | /**
69 | * Function that will trigger all checks and notify [subscriber] about the result(s).
70 | * @param checkOnlyFor checks that should be triggered.
71 | * @param subscriber subscriber where the results will be passed on.
72 | * @param vulnerabilitiesOnly whether it should notify [subscriber] only if vulnerability is found.
73 | */
74 | fun checkMerged(
75 | checkOnlyFor: Array = CHECK_ALL,
76 | subscriber: CheckSubscriber,
77 | vulnerabilitiesOnly: Boolean
78 | ) {
79 | val internalSubscriber = getSubscriber(vulnerabilitiesOnly, subscriber)
80 |
81 | val emulator = emulatorCheck?.check(checkOnlyFor)
82 | internalSubscriber.onCheck(
83 | ExtendedResult(
84 | EmulatorChecks.EmulatorCheck,
85 | emulator?.vulnerabilityFound() ?: false,
86 | emulator?.vulnerabilityFound() ?: false
87 | )
88 | )
89 |
90 | val debugger = debugCheck?.check(checkOnlyFor)
91 | internalSubscriber.onCheck(
92 | ExtendedResult(
93 | DebuggerChecks.DebuggerCheck,
94 | debugger?.vulnerabilityFound() ?: false,
95 | debugger?.vulnerabilityFound() ?: false
96 | )
97 | )
98 |
99 | val root = rootCheck?.check(checkOnlyFor)
100 | internalSubscriber.onCheck(
101 | ExtendedResult(
102 | RootChecks.RootCheck,
103 | root?.vulnerabilityFound() ?: false,
104 | root?.vulnerabilityFound() ?: false
105 | )
106 | )
107 | }
108 |
109 | /**
110 | * Function that will trigger all checks and return the result.
111 | * @return the check's [Result].
112 | */
113 | fun check(): Result = when {
114 | emulatorCheck?.check()?.vulnerabilityFound()
115 | ?: false -> Result.EmulatorFound
116 |
117 | debugCheck?.check()?.vulnerabilityFound()
118 | ?: false -> Result.DebuggerEnabled
119 |
120 | rootCheck?.check()?.vulnerabilityFound()
121 | ?: false -> Result.Rooted
122 |
123 | else -> Result.Secure
124 | }
125 |
126 | /**
127 | * Helper function for wrapping [externalSubscriber] based on configuration provided.
128 | * @param vulnerabilitiesOnly whether it should notify subscriber only if vulnerability is found.
129 | * @param externalSubscriber the [CheckSubscriber] to be wrapped.
130 | * @return the wrapped subscriber.
131 | */
132 | private fun getSubscriber(vulnerabilitiesOnly: Boolean, externalSubscriber: CheckSubscriber) =
133 | CheckSubscriber { result ->
134 | if (vulnerabilitiesOnly) {
135 | if (result.vulnerable) externalSubscriber.onCheck(result)
136 | } else {
137 | externalSubscriber.onCheck(result)
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/check/Probability.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.check
2 |
3 | import com.securevale.rasp.android.api.CheckSubscriber
4 | import com.securevale.rasp.android.api.result.CheckType
5 | import com.securevale.rasp.android.api.result.ExtendedResult
6 | import com.securevale.rasp.android.util.SecureAppLogger
7 |
8 | /**
9 | * Helper function for calling vulnerability checks with given threshold.
10 | * @param threshold the threshold which triggers finalization of function when reached.
11 | * @return whether the threshold was reached.
12 | */
13 | fun List<() -> WrappedCheckResult>.fireChecks(threshold: Int): Boolean {
14 | var sum = 0
15 | return !asSequence()
16 | .map { it() }
17 | .filter {
18 | sum += it.first
19 | SecureAppLogger.logDebug("Current threshold is $sum")
20 | sum >= threshold
21 | }
22 | .none()
23 | }
24 |
25 | fun List<() -> WrappedCheckResult>.fireGranular(subscriber: CheckSubscriber, threshold: Int) {
26 | var sum = 0
27 | asSequence()
28 | .map { it() }
29 | .forEach {
30 | sum += it.first
31 | subscriber.onCheck(ExtendedResult(it.second, it.first > 0, sum > threshold))
32 | SecureAppLogger.logDebug("Current threshold is $sum")
33 | }
34 | }
35 |
36 | /**
37 | * Helper function for calling check and returning provided [wage] based on result.
38 | * @param wage the wage which will be returned when check is successful.
39 | * @param checkType the checks that should be triggered.
40 | * @param check the check function which will be called.
41 | * @return [wage] if the [check] is successful or 0 otherwise.
42 | */
43 | fun wrappedCheck(wage: Int, checkType: CheckType, check: () -> Boolean): WrappedCheckResult =
44 | if (check()) wage to checkType else 0 to checkType
45 |
46 | typealias WrappedCheckResult = Pair
47 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/debugger/DebuggerCheck.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.debugger
2 |
3 | import android.content.Context
4 | import com.securevale.rasp.android.api.result.CheckType
5 | import com.securevale.rasp.android.api.result.DebuggerChecks
6 | import com.securevale.rasp.android.api.result.DebuggerChecks.DebugField
7 | import com.securevale.rasp.android.api.result.DebuggerChecks.Debuggable
8 | import com.securevale.rasp.android.api.result.DebuggerChecks.DebuggerConnected
9 | import com.securevale.rasp.android.check.ProbabilityCheck
10 | import com.securevale.rasp.android.check.WrappedCheckResult
11 | import com.securevale.rasp.android.check.wrappedCheck
12 | import com.securevale.rasp.android.debugger.checks.DebuggableChecks.hasDebugBuildConfig
13 | import com.securevale.rasp.android.debugger.checks.DebuggableChecks.isDebuggable
14 | import com.securevale.rasp.android.debugger.checks.DebuggableChecks.isDebuggerConnected
15 | import com.securevale.rasp.android.debugger.checks.DebuggableChecks.someoneIsWaitingForDebugger
16 |
17 | /**
18 | * Debugger detection check.
19 | *
20 | * @property context: This refers to the app's Context and should be initialized using
21 | * the context from the main app module.
22 | *
23 | * Using Context from other modules may lead to incorrect BuildConfig file checks,
24 | * resulting in inaccurate results from this check class.
25 | */
26 | @PublishedApi
27 | internal class DebuggerCheck(private val context: Context) : ProbabilityCheck() {
28 |
29 | /**
30 | * @see [ProbabilityCheck]
31 | */
32 | override val threshold = 3
33 |
34 | override val checksMap: Map WrappedCheckResult> = mapOf(
35 | Debuggable to ::checkDebuggable,
36 | DebugField to ::checkDebugField,
37 | DebuggerConnected to ::checkDebuggerConnected
38 | )
39 |
40 | /**
41 | * @see [ProbabilityCheck]
42 | */
43 | override val checkType: String = DebuggerChecks::class.java.simpleName
44 |
45 | /**
46 | * Checks for debuggable flag.
47 | */
48 | private fun checkDebuggable() = wrappedCheck(3, Debuggable) {
49 | isDebuggable(context)
50 | }
51 |
52 | /**
53 | * Checks whether DEBUG field is present.
54 | */
55 | private fun checkDebugField() = wrappedCheck(3, DebugField) {
56 | hasDebugBuildConfig(context)
57 | }
58 |
59 | /**
60 | * Checks whether debugger is connected.
61 | */
62 | private fun checkDebuggerConnected() = wrappedCheck(3, DebuggerConnected) {
63 | isDebuggerConnected() || someoneIsWaitingForDebugger()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/debugger/checks/DebuggableChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.debugger.checks
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * An object that contains all debugger-related check functions.
7 | */
8 | internal object DebuggableChecks {
9 |
10 | /**
11 | * Checks whether there is debuggable flag present in the app.
12 | * @param context the Context used for check.
13 | * @return whether app is debuggable.
14 | */
15 | @JvmName("a")
16 | external fun isDebuggable(context: Context): Boolean
17 |
18 | /**
19 | * Checks whether there is Debug config field set to true.
20 | * It is CRUCIAL to use the app [context] here in order to receive flag value from the "main" build
21 | * instead of the module's or library (which may return Debug set to true causing this check to mark
22 | * app as debuggable incorrectly).
23 | * @param context the Context used for check.
24 | * @return whether app is debuggable.
25 | */
26 | @JvmName("b")
27 | external fun hasDebugBuildConfig(context: Context): Boolean
28 |
29 | /**
30 | * Checks whether there is debugger connected to the app.
31 | * @return whether app has debugger attached to it.
32 | */
33 | @JvmName("k")
34 | external fun isDebuggerConnected(): Boolean
35 |
36 | /**
37 | * Checks whether there is any thread waiting for attaching the debugger.
38 | * @return whether app is debuggable.
39 | */
40 | @JvmName("d")
41 | external fun someoneIsWaitingForDebugger(): Boolean
42 | }
43 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/emulator/EmulatorCheck.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("WildcardImport")
2 |
3 | package com.securevale.rasp.android.emulator
4 |
5 | import android.content.Context
6 | import com.securevale.rasp.android.api.result.CheckType
7 | import com.securevale.rasp.android.api.result.EmulatorChecks
8 | import com.securevale.rasp.android.api.result.EmulatorChecks.*
9 | import com.securevale.rasp.android.check.ProbabilityCheck
10 | import com.securevale.rasp.android.check.WrappedCheckResult
11 | import com.securevale.rasp.android.check.wrappedCheck
12 | import com.securevale.rasp.android.emulator.checks.DeviceChecks.isOperatorNameAndroid
13 | import com.securevale.rasp.android.emulator.checks.DeviceChecks.isRadioVersionSuspicious
14 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.cpuSuspicious
15 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.hasSuspiciousFiles
16 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isAvdDevice
17 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isAvdHardware
18 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isBluestacks
19 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isFingerprintFromEmulator
20 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isGenymotion
21 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isGoogleEmulator
22 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isMemu
23 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.isNox
24 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.modulesSuspicious
25 | import com.securevale.rasp.android.emulator.checks.GeneralChecks.mountsSuspicious
26 | import com.securevale.rasp.android.emulator.checks.PackageChecks.hasSuspiciousPackages
27 | import com.securevale.rasp.android.emulator.checks.PropertyChecks.hasQemuProperties
28 | import com.securevale.rasp.android.emulator.checks.SensorChecks.areSensorsFromEmulator
29 |
30 | /**
31 | * Emulator detection check.
32 | *
33 | * @property context the app's Context.
34 | */
35 | @PublishedApi
36 | internal class EmulatorCheck(
37 | private val context: Context
38 | ) : ProbabilityCheck() {
39 |
40 | /**
41 | * @see [ProbabilityCheck]
42 | */
43 | override val threshold = 10
44 |
45 | override val checksMap: Map WrappedCheckResult> = mapOf(
46 | AvdDevice to ::checkAvdDevice,
47 | AvdHardware to ::checkAvdHardware,
48 | Genymotion to ::checkGenymotion,
49 | Nox to ::checkNox,
50 | Memu to ::checkMemu,
51 | Fingerprint to ::checkFingerprint,
52 | SuspiciousFiles to ::checkFiles,
53 | Bluestacks to ::checkBluestacks,
54 | GoogleEmulator to ::checkEmulatorGoogle,
55 | Sensors to ::checkSensors,
56 | OperatorName to ::checkOperatorName,
57 | RadioVersion to ::checkRadioVersion,
58 | SuspiciousPackages to ::checkPackages,
59 | SuspiciousProperties to ::checkProperties,
60 | Mounts to ::checkMounts,
61 | CPU to ::checkCPU,
62 | Modules to ::checkModules
63 | )
64 |
65 | /**
66 | * @see [ProbabilityCheck]
67 | */
68 | override val checkType: String = EmulatorChecks::class.java.simpleName
69 |
70 | /**
71 | * Checks whether AVD device indicators were found.
72 | */
73 | private fun checkAvdDevice() = wrappedCheck(2, AvdDevice) { isAvdDevice() }
74 |
75 | /**
76 | * Checks whether AVD hardware indicators were found.
77 | */
78 | private fun checkAvdHardware() = wrappedCheck(2, AvdHardware) { isAvdHardware() }
79 |
80 | /**
81 | * Checks whether device's fingerprint looks suspicious.
82 | */
83 | private fun checkFingerprint() = wrappedCheck(2, Fingerprint) { isFingerprintFromEmulator() }
84 |
85 | /**
86 | * Checks whether Google emulator indicators were found.
87 | */
88 | private fun checkEmulatorGoogle() = wrappedCheck(3, GoogleEmulator) { isGoogleEmulator() }
89 |
90 | /**
91 | * Checks whether Nox emulator indicators were found.
92 | */
93 | private fun checkNox() = wrappedCheck(3, Nox) { isNox() }
94 |
95 | /**
96 | * Checks whether Genymotion emulator indicators were found.
97 | */
98 | private fun checkGenymotion() = wrappedCheck(3, Genymotion) {
99 | isGenymotion()
100 | }
101 |
102 | /**
103 | * Checks whether mounts seems suspicious.
104 | */
105 | private fun checkMounts() = wrappedCheck(3, Mounts) {
106 | mountsSuspicious()
107 | }
108 |
109 | /**
110 | * Checks whether CPU seems suspicious.
111 | */
112 | private fun checkCPU() = wrappedCheck(3, CPU) {
113 | cpuSuspicious()
114 | }
115 |
116 | /**
117 | * Checks whether modules seems suspicious.
118 | */
119 | private fun checkModules() = wrappedCheck(3, Modules) {
120 | modulesSuspicious()
121 | }
122 |
123 | /**
124 | * Checks whether Memu indicators were found.
125 | */
126 | private fun checkMemu() = wrappedCheck(3, Memu) { isMemu() }
127 |
128 | /**
129 | * Checks whether there are any suspicious files were found.
130 | */
131 | private fun checkFiles() = wrappedCheck(5, SuspiciousFiles) { hasSuspiciousFiles() }
132 |
133 |
134 | /**
135 | * Checks whether Bluestacks emulator indicators were found.
136 | */
137 | private fun checkBluestacks() = wrappedCheck(7, Bluestacks) { isBluestacks() }
138 |
139 | /**
140 | * Checks whether any of the sensors looks suspicious.
141 | */
142 | private fun checkSensors() = wrappedCheck(10, Sensors) { areSensorsFromEmulator(context) }
143 |
144 | /**
145 | * Checks whether radio version looks suspicious.
146 | */
147 | private fun checkRadioVersion() = wrappedCheck(10, RadioVersion) { isRadioVersionSuspicious() }
148 |
149 | /**
150 | * Checks whether there are any suspicious emulator packages on the device.
151 | */
152 | private fun checkPackages() =
153 | wrappedCheck(10, SuspiciousPackages) { hasSuspiciousPackages(context) }
154 |
155 | /**
156 | * Section for checks which might return false positives so they cannot indicate whether emulator
157 | * is detected on their own (need to be matched with at least second positive match).
158 | */
159 |
160 | /**
161 | * Checks whether operator name is Android.
162 | */
163 | private fun checkOperatorName() =
164 | wrappedCheck(5, OperatorName) { isOperatorNameAndroid(context) }
165 |
166 | /**
167 | * Checks whether there are any suspicious qemu properties found on a device.
168 | */
169 | private fun checkProperties() = wrappedCheck(3, SuspiciousProperties) { hasQemuProperties() }
170 | }
171 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/emulator/checks/DeviceChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.emulator.checks
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * An object that contains all device-related check functions.
7 | */
8 | internal object DeviceChecks {
9 | /**
10 | * Checks whether operator name looks suspicious.
11 | * @param context the Context used for check.
12 | * @return result of the check.
13 | */
14 | @JvmName("c")
15 | external fun isOperatorNameAndroid(context: Context) : Boolean
16 |
17 | /**
18 | * Checks whether device's radioVersion looks suspicious.
19 | * @return result of the check.
20 | */
21 | @JvmName("u")
22 | external fun isRadioVersionSuspicious() : Boolean
23 | }
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/emulator/checks/GeneralChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.emulator.checks
2 |
3 | /**
4 | * An object that contains all general check functions.
5 | */
6 | internal object GeneralChecks {
7 |
8 | /**
9 | * Checks if there are any indicators that device might be avd one.
10 | * @return result of the check.
11 | */
12 | @JvmName("z")
13 | external fun isAvdDevice(): Boolean
14 |
15 | /**
16 | * Checks if hardware looks like from avd.
17 | * @return result of the check.
18 | */
19 | @JvmName("w")
20 | external fun isAvdHardware(): Boolean
21 |
22 | /**
23 | * Checks if it is memu device.
24 | * @return result of the check.
25 | */
26 | @JvmName("t")
27 | external fun isMemu(): Boolean
28 |
29 | /**
30 | * Checks if it is Genymotion device.
31 | * @return result of the check.
32 | */
33 | @JvmName("h")
34 | external fun isGenymotion(): Boolean
35 |
36 | /**
37 | * Checks if it is Nox device.
38 | * @return result of the check.
39 | */
40 | @JvmName("s")
41 | external fun isNox(): Boolean
42 |
43 | /**
44 | * Checks for suspicious files.
45 | * @return result of the check.
46 | */
47 | @JvmName("e")
48 | external fun hasSuspiciousFiles(): Boolean
49 |
50 |
51 | /**
52 | * Checks if it is Bluestacks device.
53 | * @return result of the check.
54 | */
55 | @JvmName("l")
56 | external fun isBluestacks(): Boolean
57 |
58 | /**
59 | * Checks for suspicious fingerprint build field.
60 | * @return result of the check.
61 | */
62 | @JvmName("j")
63 | external fun isFingerprintFromEmulator(): Boolean
64 |
65 | /**
66 | * Checks for device being Google's emulator.
67 | * @return result of the check.
68 | */
69 | @JvmName("v")
70 | external fun isGoogleEmulator(): Boolean
71 |
72 | /**
73 | * Checks whether mounts seems suspicious.
74 | * @return result of the check.
75 | */
76 | @JvmName("d")
77 | external fun mountsSuspicious(): Boolean
78 |
79 | /**
80 | * Checks whether CPU seems suspicious.
81 | * @return result of the check.
82 | */
83 | @JvmName("c")
84 | external fun cpuSuspicious(): Boolean
85 |
86 | /**
87 | * Checks whether Modules seems suspicious.
88 | * @return result of the check.
89 | */
90 | @JvmName("u")
91 | external fun modulesSuspicious(): Boolean
92 | }
93 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/emulator/checks/PackageChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.emulator.checks
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * An object that contains all package-related check functions.
7 | */
8 | internal object PackageChecks {
9 |
10 | /**
11 | * Checks whether there are any suspicious packages found.
12 | * @param context the app Context used for check.
13 | * @return result of the check.
14 | */
15 | @JvmName("y")
16 | external fun hasSuspiciousPackages(context: Context): Boolean
17 | }
18 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/emulator/checks/PropertyChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.emulator.checks
2 |
3 | /**
4 | * An object that contains all properties-related check functions.
5 | */
6 | internal object PropertyChecks {
7 |
8 | /**
9 | * Checks whether there are any qemu properties found.
10 | * @return result of the check.
11 | */
12 | @JvmName("l")
13 | external fun hasQemuProperties(): Boolean
14 | }
15 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/emulator/checks/SensorChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.emulator.checks
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * An object that contains all sensor-related check functions.
7 | */
8 | internal object SensorChecks {
9 |
10 | /**
11 | * Checks if sensors are from emulator.
12 | * @param context the Context used for check.
13 | * @return whether any of the sensors contains "goldfish" thus indicating emulator.
14 | */
15 | @JvmName("g")
16 | external fun areSensorsFromEmulator(context: Context): Boolean
17 | }
18 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/root/RootCheck.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.root
2 |
3 | import android.content.Context
4 | import com.securevale.rasp.android.api.result.CheckType
5 | import com.securevale.rasp.android.api.result.RootChecks.EngBuild
6 | import com.securevale.rasp.android.api.result.RootChecks.RootApps
7 | import com.securevale.rasp.android.api.result.RootChecks.RootCheck
8 | import com.securevale.rasp.android.api.result.RootChecks.RootCloakingApps
9 | import com.securevale.rasp.android.api.result.RootChecks.SuUser
10 | import com.securevale.rasp.android.api.result.RootChecks.SuspiciousProperties
11 | import com.securevale.rasp.android.api.result.RootChecks.TestTags
12 | import com.securevale.rasp.android.api.result.RootChecks.WritablePaths
13 | import com.securevale.rasp.android.check.ProbabilityCheck
14 | import com.securevale.rasp.android.check.WrappedCheckResult
15 | import com.securevale.rasp.android.check.wrappedCheck
16 | import com.securevale.rasp.android.root.checks.AppsChecks.hasRootAppPackages
17 | import com.securevale.rasp.android.root.checks.AppsChecks.hasRootCloakingAppPackages
18 | import com.securevale.rasp.android.root.checks.DeviceChecks.hasTestTags
19 | import com.securevale.rasp.android.root.checks.DeviceChecks.isSuperUser
20 | import com.securevale.rasp.android.root.checks.FileChecks.hasWritableNonWritablePaths
21 | import com.securevale.rasp.android.root.checks.PropertyChecks.hasRootProperties
22 | import com.securevale.rasp.android.root.checks.PropertyChecks.isEngBuild
23 |
24 | /**
25 | * Root detection check.
26 | *
27 | * @property context the app's Context.
28 | */
29 | @PublishedApi
30 | internal class RootCheck(private val context: Context) : ProbabilityCheck() {
31 |
32 | override val checksMap: Map WrappedCheckResult> = mapOf(
33 | SuUser to ::checkIsSuperUser,
34 | TestTags to ::checkTestTags,
35 | RootApps to ::rootApps,
36 | EngBuild to ::engBuild,
37 | RootCloakingApps to ::rootCloakingApps,
38 | WritablePaths to ::nonWritablePathsAreWritable,
39 | SuspiciousProperties to ::hasSuspiciousProperties
40 | )
41 |
42 | override val threshold: Int = 10
43 |
44 | override val checkType: String = RootCheck::class.java.simpleName
45 |
46 | private fun checkIsSuperUser() = wrappedCheck(10, SuUser) {
47 | isSuperUser(context)
48 | }
49 |
50 | private fun checkTestTags() = wrappedCheck(3, TestTags) {
51 | hasTestTags()
52 | }
53 |
54 | private fun rootApps() = wrappedCheck(10, RootApps) {
55 | hasRootAppPackages(context)
56 | }
57 |
58 | private fun engBuild() = wrappedCheck(10, EngBuild) {
59 | isEngBuild()
60 | }
61 |
62 | private fun rootCloakingApps() = wrappedCheck(7, RootCloakingApps) {
63 | hasRootCloakingAppPackages(context)
64 | }
65 |
66 | private fun nonWritablePathsAreWritable() = wrappedCheck(10, WritablePaths) {
67 | hasWritableNonWritablePaths()
68 | }
69 |
70 | private fun hasSuspiciousProperties() = wrappedCheck(10, SuspiciousProperties) {
71 | hasRootProperties()
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/root/checks/AppsChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.root.checks
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * An object that contains all app-related check functions.
7 | */
8 | object AppsChecks {
9 |
10 | @JvmName("r")
11 | external fun hasRootAppPackages(context: Context): Boolean
12 |
13 | @JvmName("k")
14 | external fun hasRootCloakingAppPackages(context: Context): Boolean
15 | }
16 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/root/checks/DeviceChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.root.checks
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * An object that contains all device-related check functions.
7 | */
8 | internal object DeviceChecks {
9 |
10 | @JvmName("p")
11 | external fun isSuperUser(context: Context): Boolean
12 |
13 | @JvmName("c")
14 | external fun hasTestTags(): Boolean
15 | }
16 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/root/checks/FileChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.root.checks
2 |
3 | /**
4 | * An object that contains all file-related check functions.
5 | */
6 | object FileChecks {
7 |
8 | @JvmName("w")
9 | external fun hasWritableNonWritablePaths(): Boolean
10 | }
11 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/root/checks/PropertyChecks.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.root.checks
2 |
3 | /**
4 | * An object that contains all properties-related check functions.
5 | */
6 | internal object PropertyChecks {
7 |
8 | /**
9 | * Checks whether there are any qemu properties found.
10 | * @return result of the check.
11 | */
12 | @JvmName("y")
13 | external fun hasRootProperties(): Boolean
14 |
15 | /**
16 | * Checks whether is 'eng' build.
17 | */
18 | @JvmName("g")
19 | external fun isEngBuild(): Boolean
20 | }
21 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/util/Const.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.util
2 |
3 | /**
4 | * An empty String.
5 | */
6 | const val EMPTY = ""
7 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/util/DeviceInfo.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.util
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * Helper function for accessing device's Build fields.
7 | */
8 | external fun deviceInfo(): String
9 |
10 | /**
11 | * Helper function for accessing more detailed device's information's.
12 | */
13 | external fun extendedDeviceInfo(): String
14 |
15 | /**
16 | * Helper function for accessing device's sensors list.
17 | */
18 | external fun sensorInfo(context: Context): String
19 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/util/SecureAppLogger.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.util
2 |
3 | import android.util.Log
4 |
5 | /**
6 | * Flag indicates whether library was initialised in debug mode.
7 | */
8 | internal var isDebug = false
9 |
10 | /**
11 | * An object used for logging.
12 | */
13 | object SecureAppLogger {
14 |
15 | /**
16 | * Logs debug.
17 | * @param message message to be logged.
18 | */
19 | fun logDebug(message: String) {
20 | if (isDebug) {
21 | Log.d(TAG, message)
22 | }
23 | }
24 |
25 | /**
26 | * Logs error.
27 | * @param error error message to be logged.
28 | * @param throwable the throwable that might be logged alongside with error message.
29 | */
30 | fun logError(error: String, throwable: Throwable? = null) {
31 | if (isDebug) {
32 | Log.e(TAG, error, throwable)
33 | }
34 | }
35 |
36 | /**
37 | * Logging tag.
38 | */
39 | private const val TAG = "safe-app"
40 | }
41 |
--------------------------------------------------------------------------------
/rasp/src/main/java/com/securevale/rasp/android/util/TimeLogger.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.util
2 |
3 | import android.util.Log
4 |
5 | /**
6 | * Helper function for debugging the elapsed time of function execution.
7 | * @param tag the tag for logging.
8 | * @param func the function to trigger.
9 | * @return the result of [func]
10 | */
11 | fun logTime(tag: String, func: () -> Boolean): Boolean {
12 |
13 | val start = System.currentTimeMillis()
14 |
15 | val result = func.invoke()
16 |
17 | val end = System.currentTimeMillis() - start
18 |
19 | Log.d("Rasp-android-time", "$tag: $end")
20 |
21 | return result
22 | }
23 |
--------------------------------------------------------------------------------
/sample-app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/sample-app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.kotlin.android)
4 | }
5 |
6 | android {
7 | compileSdk = rootProject.extra["compileSdkVersion"] as Int
8 |
9 | defaultConfig {
10 | applicationId = "com.securevale.rasp.android"
11 | minSdk = rootProject.extra["minSdkVersion"] as Int
12 | targetSdk = rootProject.extra["targetSdkVersion"] as Int
13 | versionCode = rootProject.extra["versionCode"] as Int
14 | versionName = rootProject.extra["versionName"] as String
15 |
16 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | getByName("release") {
21 | applicationIdSuffix = ".release"
22 | isMinifyEnabled = true
23 | proguardFiles(
24 | getDefaultProguardFile("proguard-android-optimize.txt"),
25 | "proguard-rules.pro"
26 | )
27 | }
28 | }
29 | compileOptions {
30 | sourceCompatibility = JavaVersion.VERSION_17
31 | targetCompatibility = JavaVersion.VERSION_17
32 | }
33 | kotlinOptions {
34 | jvmTarget = JavaVersion.VERSION_17.toString()
35 | }
36 | namespace = "com.securevale.rasp.android.sample"
37 |
38 | packagingOptions.jniLibs.keepDebugSymbols += "**/*.so"
39 | }
40 |
41 | dependencies {
42 | implementation(project(":rasp"))
43 | implementation(libs.androidx.corektx)
44 | implementation(libs.androidx.appcompat)
45 | implementation(libs.google.material)
46 | implementation(libs.androidx.constraintlayout)
47 | }
48 |
--------------------------------------------------------------------------------
/sample-app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/sample-app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sample-app/src/main/java/com/securevale/rasp/android/sample/AvailableChecksListAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.securevale.rasp.android.sample
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.Button
7 | import androidx.recyclerview.widget.RecyclerView
8 |
9 | class AvailableChecksListAdapter : RecyclerView.Adapter() {
10 |
11 | var items = listOf()
12 |
13 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
14 | ViewHolder(
15 | LayoutInflater.from(parent.context)
16 | .inflate(R.layout.item_check, parent, false)
17 | )
18 |
19 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
20 | holder.bind(items[position])
21 | }
22 |
23 | override fun getItemCount() = items.size
24 |
25 | inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
26 | fun bind(data: Check) = with(itemView) {
27 | findViewById