@@ -32,7 +32,7 @@ import com.dergoogler.mmrl.webui.R
3232import com.dergoogler.mmrl.webui.__webui__adapters__
3333import com.dergoogler.mmrl.webui.activity.WXActivity
3434import com.dergoogler.mmrl.webui.interfaces.WXInterface
35- import com.dergoogler.mmrl.webui.view.WebUIView
35+ import com.dergoogler.mmrl.webui.util.WXUPublicKey
3636import com.squareup.moshi.Json
3737import com.squareup.moshi.JsonClass
3838import dalvik.system.BaseDexClassLoader
@@ -42,8 +42,10 @@ import kotlinx.coroutines.flow.StateFlow
4242import org.jf.dexlib2.dexbacked.DexBackedDexFile
4343import org.jf.dexlib2.iface.instruction.ReferenceInstruction
4444import java.io.InputStream
45- import java.lang.System.console
4645import java.nio.ByteBuffer
46+ import java.nio.ByteOrder
47+ import java.security.PublicKey
48+ import java.security.Signature
4749import java.util.concurrent.ConcurrentHashMap
4850import java.util.regex.Pattern
4951
@@ -194,25 +196,33 @@ open class WebUIConfigBaseLoader() {
194196 ): BaseDexClassLoader ? {
195197 val file = SuFile (modId.webrootDir, dexPath)
196198
197- if (! file.isFile || file.extension != " dex" ) {
198- Log .e(TAG , " Provided path is not a valid .dex file: ${file.path} " )
199+ if (! file.isFile || ! file.extension.equals( " dex" , ignoreCase = true ) ) {
200+ Log .e(TAG , " Invalid .dex file: ${file.path} " )
199201 return null
200202 }
201203
202- // Using InMemoryDexClassLoader is efficient if DEX files are not excessively large.
203- val dexFileBytes = file.readBytes()
204- val str = SuFileInputStream (file).use { it.buffered() }
205- if (isBlocked(str)) {
206- return null
204+ val dexFile = loadSignedDex(file, WXUPublicKey )
205+
206+ if (! dexFile.official) {
207+ SuFileInputStream (file).use { stream ->
208+ if (isBlocked(stream.buffered())) {
209+ Log .w(TAG , " Blocked dex loading: ${file.path} " )
210+ return null
211+ }
212+ }
207213 }
208214
209- return InMemoryDexClassLoader (ByteBuffer .wrap(dexFileBytes), context.classLoader)
215+ return InMemoryDexClassLoader (
216+ ByteBuffer .wrap(dexFile.dexBytes),
217+ context.classLoader
218+ )
210219 }
211220
212221 @Throws(Exception ::class )
213- fun isBlocked (stream : InputStream ): Boolean {
222+ private fun isBlocked (stream : InputStream ): Boolean {
214223 val blockedPackages = listOf (
215- " (Lcom/dergoogler/mmrl/platform/)?(.+)?/?KsuNative"
224+ " (Lcom/dergoogler/mmrl/platform/)?(.+)?/?KsuNative" ,
225+ " Lcom/dergoogler/mmrl/webui/util/WXUPublicKeyKt"
216226 )
217227
218228 val dexFile = DexBackedDexFile .fromInputStream(null , stream)
@@ -237,6 +247,71 @@ open class WebUIConfigBaseLoader() {
237247 return false
238248 }
239249
250+ private data class VerifiedDex (
251+ val dexBytes : ByteArray ,
252+ val official : Boolean ,
253+ ) {
254+ override fun equals (other : Any? ): Boolean {
255+ if (this == = other) return true
256+ if (javaClass != other?.javaClass) return false
257+
258+ other as VerifiedDex
259+
260+ if (official != other.official) return false
261+ if (! dexBytes.contentEquals(other.dexBytes)) return false
262+
263+ return true
264+ }
265+
266+ override fun hashCode (): Int {
267+ var result = official.hashCode()
268+ result = 31 * result + dexBytes.contentHashCode()
269+ return result
270+ }
271+ }
272+
273+ private fun loadSignedDex (file : SuFile , publicKey : PublicKey ): VerifiedDex {
274+ val allBytes = file.readBytes()
275+ val totalSize = allBytes.size
276+
277+ if (totalSize < 8 ) { // must fit signature size + some bytes
278+ Log .e(TAG , " File too small to contain signature: ${file.path} " )
279+ return VerifiedDex (allBytes, false )
280+ }
281+
282+ return try {
283+ val sigSizeBytes = allBytes.copyOfRange(totalSize - 4 , totalSize)
284+ val sigSize = ByteBuffer .wrap(sigSizeBytes)
285+ .order(ByteOrder .BIG_ENDIAN )
286+ .int
287+
288+ if (sigSize <= 0 || sigSize > totalSize - 4 ) {
289+ Log .e(TAG , " Invalid signature size=$sigSize for ${file.path} " )
290+ return VerifiedDex (allBytes, false )
291+ }
292+
293+ val sigStart = totalSize - 4 - sigSize
294+ val sigBytes = allBytes.copyOfRange(sigStart, totalSize - 4 )
295+ val dexBytes = allBytes.copyOfRange(0 , sigStart)
296+
297+ val signature = Signature .getInstance(" SHA256withRSA" )
298+ signature.initVerify(publicKey)
299+ signature.update(dexBytes)
300+
301+ val isOfficial = try {
302+ signature.verify(sigBytes)
303+ } catch (e: Exception ) {
304+ Log .e(TAG , " Error verifying signature" , e)
305+ false
306+ }
307+
308+ VerifiedDex (dexBytes, isOfficial)
309+ } catch (e: Exception ) {
310+ Log .e(TAG , " Unable to verify signature for ${file.path} " , e)
311+ VerifiedDex (allBytes, false )
312+ }
313+ }
314+
240315 /* *
241316 * Creates a ClassLoader for a class within an installed APK.
242317 */
0 commit comments