Skip to content

Commit

Permalink
Version 1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cioccarellia committed Nov 4, 2018
1 parent 63a4b18 commit 7007192
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 75 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# BillingProtector
BillingProtector is and android robust and small library whose aim is to check the device state & the purchases security.
BillingProtector is and android robust and small library aiming to check the device state & purchases security.
Its main purpose is to block a transition if the application has been modified or patched; it can also be used to prevent applications from being executed on an unprotected/insecure envoronment.

# Setup
Expand All @@ -20,7 +20,8 @@ dependencies {
```

# Usage
BillingProtector has some core functionalities. Let's see them in a list
BillingProtector has different functionalities and can be used for different purposes.


## Initialization
Create an instance of the BillingProtector class passing as argument the context of your current activity/fragment
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:28.0.0'
implementation project(path: ':library')
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ class MainActivity : AppCompatActivity() {
title = "Kotlin activity"
val bp = BillingProtector(baseContext)

val isRootDetected = bp.isRootInstalled
val arePirateAppsInstalled = bp.arePirateAppsInstalled
val pirateList = bp.pirateAppsList
val isRootDetected = bp.isRootInstalled()
val arePirateAppsInstalled = bp.getPirateAppsList()
val pirateList = bp.getPirateAppsList()

mxp.text = "isRootInstalled: $isRootDetected\narePirateAppsInstalled: $arePirateAppsInstalled\npirateAppsList: ${pirateList.map { it.packageName }}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected void onCreate(Bundle savedInstanceState) {
TextView mxp = findViewById(R.id.mxp);
mxp.setText(
"isRootInstalled: " + String.valueOf(bp.isRootInstalled()) +
"\narePirateAppsInstalled: " + bp.getArePirateAppsInstalled() +
"\narePirateAppsInstalled: " + bp.getPirateAppsList() +
"\npirateAppsList: " + bp.getPirateAppsList()
);

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.2.71'
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
Expand Down
5 changes: 0 additions & 5 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {
Expand All @@ -23,13 +21,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,48 +14,58 @@ import com.andreacioccarelli.billingprotector.utils.RootUtils
*/
class BillingProtector(private val context: Context) {

private val pirateApps: List<PirateApp> = createPirateAppsList()
/**
* Returns a boolean that represents the device root state.
* */
fun isRootInstalled() = RootUtils.hasRootAccess()

val isRootInstalled = RootUtils.hasRootAccess()
/**
* Returns a String, representing the root binary path, if present.
* */
fun getRootBinatyPath() = RootUtils.extractPath()

val arePirateAppsInstalled: Boolean
get() {
val appList = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
/**
* Returns a boolean that indicates the presence of pirate apps in the host system
* */
fun arePirateAppsInstalled(): Boolean {
val appList = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

for (app in appList) {
pirateApps.map {
when (it.criteria) {
SelectionCriteria.SLICE -> {
if (it.packageName.contains(app.packageName)) return true
}
for (app in appList) {
createPirateAppsList().map {
when (it.criteria) {
SelectionCriteria.SLICE -> {
if (it.packageName.contains(app.packageName)) return true
}

SelectionCriteria.MATCH -> {
if (it.packageName == app.packageName) return true
}
SelectionCriteria.MATCH -> {
if (it.packageName == app.packageName) return true
}
}
}
return false
}
return false
}

/**
* Returns a list of the installed apps detected as pirate software
* */
fun getPirateAppsList(): List<PirateApp> {
val foundThreats = mutableListOf<PirateApp>()
val appList = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

for (app in appList) {
createPirateAppsList().map {
when (it.criteria) {
SelectionCriteria.SLICE -> {
if (it.packageName.contains(app.packageName)) foundThreats.add(it)
}

val pirateAppsList: List<PirateApp>
get() {
val foundThreats = mutableListOf<PirateApp>()
val appList = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

for (app in appList) {
pirateApps.map {
when (it.criteria) {
SelectionCriteria.SLICE -> {
if (it.packageName.contains(app.packageName)) foundThreats.add(it)
}

SelectionCriteria.MATCH -> {
if (it.packageName == app.packageName) foundThreats.add(it)
}
SelectionCriteria.MATCH -> {
if (it.packageName == app.packageName) foundThreats.add(it)
}
}
}
return foundThreats
}
return foundThreats.toList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@ import android.util.Log


/**
* Created by andrea on 2018/Jul.
* Part of the package com.andreacioccarelli.billingprotector.data
* Class representing a pirate app with built-in string sign check
*/
data class PirateApp(val packageName: String, val encodedPackageName: String, val criteria: SelectionCriteria, val name: String) {
init {
val check = Base64.encodeToString(packageName.toByteArray(), Base64.DEFAULT)

if (check.trim() != encodedPackageName.trim()) {
Log.d("Security error", "pn=$packageName, Check=[$check], encodedPN=[$encodedPackageName]")
throw SecurityException("Package names mismatch")
Log.e("BillingProtector", "Package Name=[$packageName], Sign Check String=[$check], Base64 Encoded Package Name=[$encodedPackageName]")
throw SecurityException("Package names mismatch, apk file damaged or corrupted")
}
}
}

fun createPirateAppsList() = listOf(
internal fun createPirateAppsList() = listOf(
PirateApp("com.chelpus.lackypatch", "Y29tLmNoZWxwdXMubGFja3lwYXRjaA==", SelectionCriteria.MATCH, "Chelpus Lucky Patcher"),
PirateApp("com.dimonvideo.luckypatcher", "Y29tLmRpbW9udmlkZW8ubHVja3lwYXRjaGVy", SelectionCriteria.MATCH, "Lucky Patcher"),
PirateApp("com.forpda.lp", "Y29tLmZvcnBkYS5scA==", SelectionCriteria.MATCH, "4Pda Lucy Patcher"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ package com.andreacioccarelli.billingprotector.data
* Created by andrea on 2018/Jul.
* Part of the package com.andreacioccarelli.billingprotector.data
*/

enum class SelectionCriteria { MATCH, SLICE }
Original file line number Diff line number Diff line change
@@ -1,45 +1,69 @@
package com.andreacioccarelli.billingprotector.utils

import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader

/**
* Created by andrea on 2018/Jul.
* Part of the package com.andreacioccarelli.billingprotector.utils
*/
object RootUtils {
val path: String
get() {
return try {
val process = Runtime.getRuntime().exec("which su")
internal object RootUtils {

val output = StringBuffer()
val buffer = BufferedReader(InputStreamReader(process.inputStream))
/**
* Requests the root binary path by using unix which command, that
* returns the path of a specified executable, in this case, su
*
* This is usually placed in directories like /system/bin,
* /system/xbin, /su, etc..
*
* While checking the path, root access is not requested or logged
*
* @return The path of the binary, or an empty string if
* nothing is found in the host system
* */
internal fun extractPath(): String {
val inspectionCommand = "which su"

var l: String?
return try {
val process = Runtime.getRuntime().exec(inspectionCommand)

do {
l = buffer.readLine()
val outputBuffer = StringBuffer()
val bufferedReader = BufferedReader(InputStreamReader(process.inputStream))

if (l == null)
break
var line: String?

output.append(l)
} while (true)
do {
line = bufferedReader.readLine()

output.toString()
} catch (io: Exception) {
""
}
if (line == null)
break

outputBuffer.append(line)
} while (true)

outputBuffer.toString()
} catch (io: IOException) {
""
} catch (nil: NullPointerException) {
""
}
}

fun hasRootAccess(): Boolean {
val path = path
if (path.isEmpty()) return false
if (path == "") return false
if (!path.contains("/")) return false
if (!path.contains("su")) return false

return true
/**
* Determines wheter or not the device has root access by
* analyzing the path of the su binary.
*
* @return A Boolean representing the device root state
* */
internal fun hasRootAccess(): Boolean {
val path = extractPath()
return when {
path.isEmpty() -> false
!path.contains("/") -> false
!path.contains("su") -> false
else -> true
}
}
}

0 comments on commit 7007192

Please sign in to comment.