Skip to content

Commit ae9a1e4

Browse files
committed
Minor fixes
1 parent 8532397 commit ae9a1e4

3 files changed

Lines changed: 1 addition & 276 deletions

File tree

app/src/main/java/com/mopwn/app/ApkDetailsActivity.kt

Lines changed: 0 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -465,97 +465,7 @@ class ApkDetailsActivity : AppCompatActivity() {
465465
}
466466
val auditedLibs = libsToAudit.mapNotNull { pair -> auditNativeLibrary(pair.first, pair.second) }
467467

468-
// Cryptographic Developer Signature and Schemes Auditing
469-
val hasV1 = entriesList.any { it.second.startsWith("META-INF/") && (it.second.endsWith(".SF") || it.second.endsWith(".DSA") || it.second.endsWith(".RSA") || it.second.endsWith(".EC")) }
470-
val baseApkPath = appInfo.sourceDir
471-
val blockResults = parseApkSigningBlock(baseApkPath)
472-
val hasV2 = blockResults.first
473-
val hasV3 = blockResults.second
474-
val hasV31 = blockResults.third
475-
val hasV4 = java.io.File("$baseApkPath.idsig").exists()
476-
477-
var issuerName = "Unknown"
478-
var subjectName = "Unknown"
479-
var sigAlg = "Unknown"
480-
var validityStr = "Unknown"
481-
var sha256Hex = "Unknown"
482-
var sha1Hex = "Unknown"
483-
484-
val warnings = ArrayList<String>()
485-
val bestPractices = ArrayList<String>()
486468

487-
try {
488-
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
489-
val packageInfoCert = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
490-
packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNING_CERTIFICATES.toLong()))
491-
} else {
492-
@Suppress("DEPRECATION")
493-
packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
494-
}
495-
packageInfoCert.signingInfo?.let { sInfo ->
496-
if (sInfo.hasMultipleSigners()) sInfo.apkContentsSigners else sInfo.signingCertificateHistory
497-
}
498-
} else {
499-
@Suppress("DEPRECATION")
500-
val packageInfoCert = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
501-
@Suppress("DEPRECATION")
502-
packageInfoCert.signatures
503-
}
504-
505-
if (!signatures.isNullOrEmpty()) {
506-
val firstSig = signatures[0]
507-
val certFactory = java.security.cert.CertificateFactory.getInstance("X.509")
508-
val cert = certFactory.generateCertificate(java.io.ByteArrayInputStream(firstSig.toByteArray())) as java.security.cert.X509Certificate
509-
510-
issuerName = cert.issuerDN.name
511-
subjectName = cert.subjectDN.name
512-
sigAlg = cert.sigAlgName
513-
514-
val sdf = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", java.util.Locale.getDefault())
515-
validityStr = "From ${sdf.format(cert.notBefore)} to ${sdf.format(cert.notAfter)}"
516-
517-
val md256 = java.security.MessageDigest.getInstance("SHA-256")
518-
sha256Hex = md256.digest(firstSig.toByteArray()).joinToString(":") { "%02X".format(it) }
519-
520-
val md1 = java.security.MessageDigest.getInstance("SHA-1")
521-
sha1Hex = md1.digest(firstSig.toByteArray()).joinToString(":") { "%02X".format(it) }
522-
523-
val sigAlgLower = sigAlg.lowercase(java.util.Locale.getDefault())
524-
if (sigAlgLower.contains("md5") || sigAlgLower.contains("sha1")) {
525-
warnings.add("🔴 CRITICAL: Cryptographically weak signature algorithm ($sigAlg). This makes the signature susceptible to collision and spoofing attacks.")
526-
} else {
527-
bestPractices.add("🟢 COMPLIANT: Secure signature algorithm ($sigAlg).")
528-
}
529-
530-
if (issuerName == subjectName) {
531-
bestPractices.add("🔵 INFO: Certificate is self-signed. This is standard for Android development, but ensure the private key is stored in a secure Keystore/HSM.")
532-
}
533-
}
534-
} catch (e: Exception) {
535-
e.printStackTrace()
536-
}
537-
538-
val targetSdkVersion = appInfo.targetSdkVersion
539-
540-
if (hasV1 && !hasV2 && !hasV3) {
541-
warnings.add("🔴 WARNING: Only V1 (JAR) signature is present. The app is vulnerable to the Janus Vulnerability (CVE-2017-13156) on Android 5.0 to 8.0, allowing attackers to inject malicious DEX classes directly into the APK zip container without invalidating the cryptographic signature.")
542-
} else if (!hasV1 && (hasV2 || hasV3)) {
543-
bestPractices.add("🔵 INFO: JAR signing (V1) is disabled, but whole-file signing (V2/V3) is active. The app will only install on Android 7.0 (API 24) and higher.")
544-
} else {
545-
bestPractices.add("🟢 COMPLIANT: JAR signing (V1) and whole-file signing (V2/V3) are both active. Protected against Janus vulnerability.")
546-
}
547-
548-
if (!hasV2 && !hasV3) {
549-
warnings.add("🔴 WARNING: Missing modern whole-file Signature Schemes (V2/V3). Whole-file signing is mandatory for modern Android releases and guarantees faster on-device verification and ZIP structural integrity protection.")
550-
}
551-
552-
if (targetSdkVersion >= 30) {
553-
if (!hasV4) {
554-
bestPractices.add("🟡 RECOMMENDED: Target SDK is 30+ but APK Signature Scheme v4 is missing. Implementing V4 enables seamless incremental installations (Play Feature Delivery).")
555-
} else {
556-
bestPractices.add("🟢 COMPLIANT: APK Signature Scheme v4 is active for incremental installs.")
557-
}
558-
}
559469

560470
// Update UI on main thread
561471
runOnUiThread {
@@ -567,55 +477,7 @@ class ApkDetailsActivity : AppCompatActivity() {
567477
tvObfuscationScore.text = "$scoreText$techText"
568478
tvObfuscationScore.setTextColor(if (obfuscationScore > 50) android.graphics.Color.parseColor("#FF5722") else if (obfuscationScore > 15) android.graphics.Color.parseColor("#FFEB3B") else android.graphics.Color.WHITE)
569479

570-
// Bind dynamic Signature Schemes UI
571-
val tvSigSchemes = findViewById<TextView>(R.id.tvSigSchemes)
572-
val tvSigIssuer = findViewById<TextView>(R.id.tvSigIssuer)
573-
val tvSigSubject = findViewById<TextView>(R.id.tvSigSubject)
574-
val tvSigAlgorithm = findViewById<TextView>(R.id.tvSigAlgorithm)
575-
val tvSigValidity = findViewById<TextView>(R.id.tvSigValidity)
576-
val tvSigSHA256 = findViewById<TextView>(R.id.tvSigSHA256)
577-
val tvSigSHA1 = findViewById<TextView>(R.id.tvSigSHA1)
578-
val llSigFindings = findViewById<LinearLayout>(R.id.llSigFindings)
579-
580-
llSigFindings.removeAllViews()
581-
582-
val schemesBuilder = StringBuilder("Signature Schemes Used:\n")
583-
schemesBuilder.append(" • V1 (JAR Signing): ").append(if (hasV1) "✓ YES" else "✗ NO").append("\n")
584-
schemesBuilder.append(" • V2 (APK v2): ").append(if (hasV2) "✓ YES" else "✗ NO").append("\n")
585-
schemesBuilder.append(" • V3 (APK v3): ").append(if (hasV3) "✓ YES" else "✗ NO").append("\n")
586-
if (hasV31) {
587-
schemesBuilder.append(" • V3.1 (APK v3.1): ✓ YES\n")
588-
}
589-
schemesBuilder.append(" • V4 (Incremental): ").append(if (hasV4) "✓ YES" else "✗ NO")
590-
591-
tvSigSchemes.text = schemesBuilder.toString()
592-
tvSigIssuer.text = "Issuer: $issuerName"
593-
tvSigSubject.text = "Subject: $subjectName"
594-
tvSigAlgorithm.text = "Signature Algorithm: $sigAlg"
595-
tvSigValidity.text = "Validity: $validityStr"
596-
tvSigSHA256.text = "SHA-256 Hash:\n$sha256Hex"
597-
tvSigSHA1.text = "SHA-1 Hash:\n$sha1Hex"
598-
599-
for (warning in warnings) {
600-
llSigFindings.addView(TextView(this@ApkDetailsActivity).apply {
601-
text = warning
602-
setTextColor(android.graphics.Color.parseColor("#FF5555"))
603-
textSize = 12f
604-
setPadding(0, 4, 0, 4)
605-
})
606-
}
607480

608-
for (bp in bestPractices) {
609-
val isInfo = bp.contains("INFO:")
610-
val isRecommended = bp.contains("RECOMMENDED:")
611-
val colorHex = if (isInfo) "#8888FF" else if (isRecommended) "#FFFF55" else "#55FF55"
612-
llSigFindings.addView(TextView(this@ApkDetailsActivity).apply {
613-
text = bp
614-
setTextColor(android.graphics.Color.parseColor(colorHex))
615-
textSize = 12f
616-
setPadding(0, 4, 0, 4)
617-
})
618-
}
619481

620482
if (auditedLibs.isNotEmpty()) {
621483
findViewById<androidx.cardview.widget.CardView>(R.id.cardNativeLibs).visibility = android.view.View.VISIBLE
@@ -649,100 +511,7 @@ class ApkDetailsActivity : AppCompatActivity() {
649511
return sdf.format(java.util.Date(timeMs))
650512
}
651513

652-
private fun parseApkSigningBlock(apkPath: String): Triple<Boolean, Boolean, Boolean> {
653-
var hasV2 = false
654-
var hasV3 = false
655-
var hasV31 = false
656-
657-
try {
658-
java.io.RandomAccessFile(apkPath, "r").use { file ->
659-
val length = file.length()
660-
if (length < 22) return Triple(false, false, false)
661-
662-
var eocdOffset = -1L
663-
val scanStart = maxOf(0L, length - 1024)
664-
val searchBytes = byteArrayOf(0x50, 0x4b, 0x05, 0x06) // EOCD signature (Little Endian)
665-
666-
val buffer = ByteArray(1024)
667-
file.seek(scanStart)
668-
val bytesRead = file.read(buffer)
669-
670-
for (i in bytesRead - 4 downTo 0) {
671-
if (buffer[i] == searchBytes[0] && buffer[i+1] == searchBytes[1] &&
672-
buffer[i+2] == searchBytes[2] && buffer[i+3] == searchBytes[3]) {
673-
eocdOffset = scanStart + i
674-
break
675-
}
676-
}
677-
678-
if (eocdOffset == -1L) return Triple(false, false, false)
679-
680-
file.seek(eocdOffset + 16)
681-
val cdOffset = readIntLE(file)
682-
683-
if (cdOffset.toLong() >= length || cdOffset < 32) return Triple(false, false, false)
684-
685-
file.seek(cdOffset.toLong() - 16)
686-
val magicBytes = ByteArray(16)
687-
file.readFully(magicBytes)
688-
val magicString = String(magicBytes, Charsets.US_ASCII)
689-
690-
if (magicString == "APK Sig Block 42") {
691-
file.seek(cdOffset.toLong() - 24)
692-
val blockLength = readLongLE(file)
693-
val startOffset = cdOffset.toLong() - (blockLength + 8)
694-
695-
if (startOffset >= 0) {
696-
file.seek(startOffset)
697-
val totalPairsLength = blockLength - 24
698-
var bytesParsed = 0L
699-
700-
while (bytesParsed < totalPairsLength) {
701-
val pairLength = readLongLE(file)
702-
if (pairLength < 4 || pairLength > totalPairsLength - bytesParsed) break
703-
704-
val pairId = readIntLE(file).toLong() and 0xFFFFFFFFL
705-
706-
when (pairId) {
707-
0x7109871aL -> hasV2 = true
708-
0xf05368c0L -> hasV3 = true
709-
0x1b93ad61L -> hasV31 = true
710-
}
711-
712-
file.skipBytes((pairLength - 4).toInt())
713-
bytesParsed += pairLength + 8
714-
}
715-
}
716-
}
717-
}
718-
} catch (e: Exception) {
719-
e.printStackTrace()
720-
}
721-
722-
return Triple(hasV2, hasV3, hasV31)
723-
}
724-
725-
private fun readIntLE(file: java.io.RandomAccessFile): Int {
726-
val b = ByteArray(4)
727-
file.readFully(b)
728-
return (b[0].toInt() and 0xFF) or
729-
((b[1].toInt() and 0xFF) shl 8) or
730-
((b[2].toInt() and 0xFF) shl 16) or
731-
((b[3].toInt() and 0xFF) shl 24)
732-
}
733514

734-
private fun readLongLE(file: java.io.RandomAccessFile): Long {
735-
val b = ByteArray(8)
736-
file.readFully(b)
737-
return (b[0].toLong() and 0xFFL) or
738-
((b[1].toLong() and 0xFFL) shl 8) or
739-
((b[2].toLong() and 0xFFL) shl 16) or
740-
((b[3].toLong() and 0xFFL) shl 24) or
741-
((b[4].toLong() and 0xFFL) shl 32) or
742-
((b[5].toLong() and 0xFFL) shl 40) or
743-
((b[6].toLong() and 0xFFL) shl 48) or
744-
((b[7].toLong() and 0xFFL) shl 56)
745-
}
746515

747516
private fun copyToClipboard(label: String, text: String) {
748517
val clipboard = getSystemService(android.content.Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager

app/src/main/java/com/mopwn/app/ForegroundTrackerService.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,7 @@ class ForegroundTrackerService : Service() {
209209
"com.android.systemui",
210210
"android",
211211
"com.google.android.inputmethod.latin",
212-
"com.google.android.providers.media.module",
213-
"com.topjohnwu.magisk"
212+
"com.google.android.providers.media.module"
214213
)
215214

216215
if (ignorePackages.contains(pkg)) {

app/src/main/res/layout/activity_apk_details.xml

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -155,50 +155,7 @@
155155
</LinearLayout>
156156
</androidx.cardview.widget.CardView>
157157

158-
<!-- Signature & Developer Certificate Audit Card -->
159-
<androidx.cardview.widget.CardView
160-
android:layout_width="match_parent"
161-
android:layout_height="wrap_content"
162-
app:cardBackgroundColor="#CC222222"
163-
app:cardCornerRadius="8dp"
164-
app:cardElevation="4dp"
165-
android:layout_marginBottom="16dp">
166-
167-
<LinearLayout
168-
android:layout_width="match_parent"
169-
android:layout_height="wrap_content"
170-
android:orientation="vertical"
171-
android:padding="16dp">
172-
173-
<TextView
174-
android:layout_width="wrap_content"
175-
android:layout_height="wrap_content"
176-
android:text="Signature &amp; Certificate Audit"
177-
android:textColor="@color/green_primary"
178-
android:textSize="18sp"
179-
android:textStyle="bold"
180-
android:layout_marginBottom="8dp"/>
181158

182-
<!-- Schemes Used -->
183-
<TextView android:id="@+id/tvSigSchemes" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="6dp" android:textIsSelectable="true"/>
184-
185-
<!-- Certificate Metadata -->
186-
<TextView android:id="@+id/tvSigIssuer" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="4dp" android:textIsSelectable="true" android:textSize="13sp"/>
187-
<TextView android:id="@+id/tvSigSubject" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="4dp" android:textIsSelectable="true" android:textSize="13sp"/>
188-
<TextView android:id="@+id/tvSigAlgorithm" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="4dp" android:textIsSelectable="true" android:textSize="13sp"/>
189-
<TextView android:id="@+id/tvSigValidity" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="4dp" android:textIsSelectable="true" android:textSize="13sp"/>
190-
<TextView android:id="@+id/tvSigSHA256" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="4dp" android:textIsSelectable="true" android:textSize="13sp"/>
191-
<TextView android:id="@+id/tvSigSHA1" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="@color/white" android:layout_marginBottom="12dp" android:textIsSelectable="true" android:textSize="13sp"/>
192-
193-
<!-- Best Practice Findings -->
194-
<LinearLayout
195-
android:id="@+id/llSigFindings"
196-
android:layout_width="match_parent"
197-
android:layout_height="wrap_content"
198-
android:orientation="vertical"
199-
android:layout_marginTop="4dp"/>
200-
</LinearLayout>
201-
</androidx.cardview.widget.CardView>
202159

203160
<!-- Binary & Obfuscation Card -->
204161
<androidx.cardview.widget.CardView

0 commit comments

Comments
 (0)