Skip to content

Commit

Permalink
add api-permission check
Browse files Browse the repository at this point in the history
  • Loading branch information
firmianay committed Nov 14, 2023
1 parent 22dffd8 commit 686af07
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 94 deletions.
75 changes: 27 additions & 48 deletions src/main/kotlin/net/bytedance/security/app/MethodFinder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,8 @@ object MethodFinder {
}

private fun filterByClassName(className: String): Collection<SootClass> {
val results = ArrayList<SootClass>()
//to avoid java.util.ConcurrentModificationException
for (c in PLUtils.classes) {
if (isMatched(className, c.name)) {
results.add(c)
}
}
return results
return PLUtils.classes.filter { isMatched(className, it.name) }
}

/**
Expand All @@ -105,10 +99,9 @@ object MethodFinder {
private fun checkAndParseMethodSigInternal(methodSig: String): Set<SootMethod> {
val matchedMethodSet: MutableSet<SootMethod> = HashSet()
val fd = newFunctionSignature(methodSig)
if (!fd.className.contains("*") && !fd.functionName.contains("*") && !fd.args.contains("*") && !fd.returnType.contains(
"*"
)
) {
if (!fd.className.contains("*") && !fd.functionName.contains("*") &&
!fd.args.contains("*") && !fd.returnType.contains("*"))
{
val sc = Scene.v().getSootClassUnsafe(fd.className, false)
val sm = Scene.v().grabMethod(methodSig)
if (sc != null && sm != null) {
Expand All @@ -122,45 +115,35 @@ object MethodFinder {
}
}
}

return matchedMethodSet
}

val targetClassSet = getClassesByName(fd.className)
val possibleMethodSigSet: MutableSet<SootMethod> = HashSet()
for (sc in targetClassSet) {
var methods: List<SootMethod>
if (sc.name.startsWith(PLUtils.CUSTOM_CLASS)) {
continue
} else {
methods = sc.methods
targetClassSet.filterNot { it.name.startsWith(PLUtils.CUSTOM_CLASS) }.forEach { sc ->
val methods = sc.methods

fun processMethod(method: SootMethod, predicate: (SootMethod) -> Boolean) {
if (predicate(method)) {
matchedMethodSet.add(method)
addMatchedMethodSet(possibleMethodSigSet, method)
}
}

if (fd.functionName.contains("*") || fd.args.contains("*") || fd.returnType.contains("*")) {
if (fd.functionName == "*") {
methods.forEach {
matchedMethodSet.add(it)
addMatchedMethodSet(possibleMethodSigSet, it)
}
} else {
for (sm in methods) {
if (isMatched(fd.functionName, sm.name)) {
matchedMethodSet.add(sm)
addMatchedMethodSet(possibleMethodSigSet, sm)
}
methods.forEach { method ->
if (fd.functionName == "*") {
processMethod(method) { true }
} else {
processMethod(method) { isMatched(fd.functionName, it.name) }
}
}
} else {
for (sm in methods) {
if (fd.subSignature() == sm.subSignature) {
matchedMethodSet.add(sm)
addMatchedMethodSet(possibleMethodSigSet, sm)
}
}
methods.forEach { processMethod(it) { fd.subSignature() == it.subSignature } }
}
}

for (otherSig in possibleMethodSigSet) {
matchedMethodSet.add(otherSig)
}
matchedMethodSet.addAll(possibleMethodSigSet)
Log.logDebug(methodSig + " Parsed " + matchedMethodSet.size)
return matchedMethodSet
}
Expand All @@ -179,6 +162,7 @@ object MethodFinder {
val start = System.currentTimeMillis()
val s = checkAndParseMethodSigInternal(methodSig)
profiler.checkAndParseMethodSigInternalTake(System.currentTimeMillis() - start)

MethodSignatureCache[methodSig] = s
return s
}
Expand Down Expand Up @@ -237,15 +221,10 @@ object MethodFinder {
* pattern matching based on class name
*/
private fun getClassesByName(className: String): Collection<SootClass> {
return if (className == "*") {
PLUtils.classes
} else {
if (className.indexOf("*") >= 0) {
return filterByClassName(className)
} else {
val sc = Scene.v().getSootClassUnsafe(className, false) ?: return setOf()
return setOf(sc)
}
return when {
className == "*" -> PLUtils.classes
className.contains("*") -> filterByClassName(className)
else -> Scene.v().getSootClassUnsafe(className, false)?.let { setOf(it) } ?: setOf()
}
}
}
}
21 changes: 6 additions & 15 deletions src/main/kotlin/net/bytedance/security/app/PreAnalyzeContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -178,26 +178,17 @@ open class PreAnalyzeContext {
}

fun findInstantCallSiteWithSubclass(className: String): Set<CallSite> {
val s = HashSet<CallSite>()
for (sc in PLUtils.classes) {
if (sc.name == className || sc.hasSuperclass() && className == sc.superclass.name) {
s.addAll(findInstantCallSite(sc))
}
}
return s
return PLUtils.classes.filter { it.name == className
|| (it.hasSuperclass() && className == it.superclass.name) }
.flatMap { findInstantCallSite(it) }.toSet()
}


/**
* field load callsites
*/
fun findFieldCallSite(field: String): Set<CallSite> {
val fields = MethodFinder.checkAndParseFieldSignature(field)
val results = HashSet<CallSite>()
for (f in fields) {
results.addAll(findFieldCallSite(f))
}
return results
return MethodFinder.checkAndParseFieldSignature(field)
.flatMap { findFieldCallSite(it) }.toSet()
}

/**
Expand All @@ -206,4 +197,4 @@ open class PreAnalyzeContext {
fun findFieldCallSite(field: SootField): Set<CallSite> {
return this.loadFieldRefs[field] ?: setOf()
}
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/net/bytedance/security/app/RuleData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ data class RuleData(

val ManifestCheckMode: Boolean? = null,
val APIMode: Boolean? = null,
val APIPermission: String? = null,

val InternalMediaLocation: Boolean? = null,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ object StaticAnalyzeMain {
@Throws(Exception::class)
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("Usage: java -jar appshark.jar config.json5")
println("Usage: java -jar appshark.jar config.json5")
return
}
val configPath = args[0]
Expand All @@ -98,6 +98,7 @@ fun main(args: Array<String>) {
EngineConfig.libraryConfig.setPackage(it)
}
}
Log.logDebug(Json.encodeToString(argumentConfig))
runBlocking { StaticAnalyzeMain.startAnalyze(argumentConfig) }
} catch (e: Exception) {
e.printStackTrace()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ import kotlin.system.exitProcess
@Serializable
class Results {
var AppInfo: AppInfo? = null
var ManifestRisk: ManifestRisk? = null
var ManifestRisk = ManifestRisk()
var SecurityInfo: MutableMap<String, MutableMap<String, SecurityRiskItem>> = HashMap()
var ComplianceInfo: MutableMap<String, MutableMap<String, SecurityRiskItem>> = HashMap()
var DeepLinkInfo: MutableMap<String, MutableSet<String>>? = null
var HTTP_API: MutableList<HttpAPI>? = null
var JsBridgeInfo: MutableList<JsBridgeAPI>? = null
var BasicInfo: BasicInfo? = null
var UsePermissions: Set<String>? = null
var UsePermissions: MutableMap<String, String> = mutableMapOf()
var DefinePermissions: Map<String, String>? = null
var Profile: String? = null
}
Expand All @@ -58,18 +58,17 @@ object OutputSecResults {
private var BasicInfo = BasicInfo()
private var DeepLinkInfo: MutableMap<String, MutableSet<String>> = HashMap()
var AppInfo = AppInfo()
var ManifestRisk = ManifestRisk()
var APIList: MutableList<HttpAPI> = ArrayList()
var JsBridgeList: MutableList<JsBridgeAPI> = ArrayList()
var JSList: MutableList<String> = ArrayList()
private var vulnerabilityItems = ArrayList<VulnerabilityItem>()
var ApiPermissionMapping: MutableMap<String, Set<String>> = mutableMapOf()

fun init() {
AppInfo.appsharkTakeTime = profiler.totalRange.takes
AppInfo.classCount = profiler.ProcessMethodStatistics.availableClasses
AppInfo.methodCount = profiler.ProcessMethodStatistics.availableMethods
Results.AppInfo = AppInfo
Results.ManifestRisk = ManifestRisk
Results.DeepLinkInfo = DeepLinkInfo
Results.HTTP_API = APIList
Results.JsBridgeInfo = JsBridgeList
Expand Down Expand Up @@ -99,13 +98,19 @@ object OutputSecResults {
}

private fun insertMani() {
ManifestRisk.debuggable = AndroidUtils.debuggable
ManifestRisk.allowBackup = AndroidUtils.allowBackup
ManifestRisk.usesCleartextTraffic = AndroidUtils.usesCleartextTraffic
Results.ManifestRisk.debuggable = AndroidUtils.debuggable
Results.ManifestRisk.allowBackup = AndroidUtils.allowBackup
Results.ManifestRisk.usesCleartextTraffic = AndroidUtils.usesCleartextTraffic
}

private fun insertPerm() {
Results.UsePermissions = AndroidUtils.usePermissionSet
AndroidUtils.usePermissionSet.forEach {
Results.UsePermissions[it] = when {
this.ApiPermissionMapping.contains(it) && this.ApiPermissionMapping[it]!!.isNotEmpty() -> "used"
this.ApiPermissionMapping.contains(it) -> "unused"
else -> "unknown"
}
}
Results.DefinePermissions = AndroidUtils.permissionMap
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,37 +38,50 @@ class ApiModeProcessor(val ctx: PreAnalyzeContext) : IRuleProcessor {
if (rule !is ApiModeRule) {
return
}
val sinkObject = rule.sink
for (sinkRuleSig in sinkObject.keys) {
if (sinkRuleSig.isMethodSignature()) {
val methodSigSet = MethodFinder.checkAndParseMethodSig(sinkRuleSig)
methodSigSet.forEach {
val callMap = ctx.findInvokeCallSite(it)
apiModeToHtml(rule, callMap)
}

} else if (sinkRuleSig.isFieldSignature()) {
val callMap = ctx.findFieldCallSite(
sinkRuleSig
)
apiModeToHtml(rule, callMap)
} else {
val matchedClasses = PLUtils.findMatchedChildClasses(setOf(sinkRuleSig))
val sinkClass = Scene.v().getSootClassUnsafe(sinkRuleSig, false)
if (sinkClass != null) {
matchedClasses.add(sinkClass)
if (rule.apiPermission.isNotEmpty()) {
val callSet = mutableSetOf<String>()

rule.sink.keys.forEach { sinkRuleSig ->
if (sinkRuleSig.isMethodSignature()) {
val methodSigSet = MethodFinder.checkAndParseMethodSig(sinkRuleSig)
callSet.addAll(methodSigSet.map { it.signature })
} else if (sinkRuleSig.isFieldSignature()) {
val fieldSigSet = MethodFinder.checkAndParseFieldSignature(sinkRuleSig)
callSet.addAll(fieldSigSet.map { it.signature })
}
for (sc in matchedClasses) {
val callMap = ctx.findInstantCallSite(sc.name)
apiModeToHtml(rule, callMap)
}
apiPermissionToResults(rule.apiPermission, callSet)

} else {
rule.sink.keys.forEach { sinkRuleSig ->
val callMap = when {
sinkRuleSig.isMethodSignature() -> {
val methodSigSet = MethodFinder.checkAndParseMethodSig(sinkRuleSig)
methodSigSet.map { ctx.findInvokeCallSite(it) }
}

sinkRuleSig.isFieldSignature() -> listOf(ctx.findFieldCallSite(sinkRuleSig))

else -> {
val matchedClasses = PLUtils.findMatchedChildClasses(setOf(sinkRuleSig))
Scene.v().getSootClassUnsafe(sinkRuleSig, false)?.let { matchedClasses.add(it) }
matchedClasses.map { ctx.findInstantCallSite(it.name) }
}
}
callMap.forEach { apiModeToHtml(rule, it) }
}
}
}

private suspend fun apiModeToHtml(rule: IRule, callMap: Set<CallSite>) {
for (site in callMap) {
APIModeHtmlWriter(OutputSecResults, rule, site.method, site.stmt).addVulnerabilityAndSaveResultToOutput()
APIModeHtmlWriter(OutputSecResults, rule, site.method, site.stmt)
.addVulnerabilityAndSaveResultToOutput()
}
}

private fun apiPermissionToResults(apiPermission: String, callSet: Set<String>) {
OutputSecResults.ApiPermissionMapping[apiPermission] = callSet
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import net.bytedance.security.app.SinkBody
class ApiModeRule(name: String, ruleData: RuleData) : AbstractRule(name, ruleData) {
override val mode: String = "APIMode"
val sink: Map<String, SinkBody>
val apiPermission: String

init {
sink = ruleData.sink!!
apiPermission = ruleData.APIPermission ?: ""
}
}

0 comments on commit 686af07

Please sign in to comment.