MangaGreat: Solve JS challenge (#17217)
* MangaGreat: Solve JS challenge * wording
This commit is contained in:
parent
bad7674de5
commit
41fe45de25
|
@ -1,9 +1,126 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.mangagreat
|
package eu.kanade.tachiyomi.extension.en.mangagreat
|
||||||
|
|
||||||
|
import android.util.Base64
|
||||||
|
import app.cash.quickjs.QuickJs
|
||||||
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
import eu.kanade.tachiyomi.multisrc.madara.Madara
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.Cookie
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.io.IOException
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.spec.IvParameterSpec
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
class MangaGreat : Madara("MangaGreat", "https://mangagreat.com", "en") {
|
class MangaGreat : Madara("MangaGreat", "https://mangagreat.com", "en") {
|
||||||
|
override val client: OkHttpClient = super.client.newBuilder()
|
||||||
|
.addInterceptor(::JSChallengeInterceptor)
|
||||||
|
.build()
|
||||||
|
|
||||||
// The website does not flag the content.
|
// The website does not flag the content.
|
||||||
override val filterNonMangaItems = false
|
override val filterNonMangaItems = false
|
||||||
|
|
||||||
|
// /manga/page/1/ redirects to /manga/
|
||||||
|
override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/"
|
||||||
|
|
||||||
|
//
|
||||||
|
// JS Challenge logic start
|
||||||
|
//
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
private fun JSChallengeInterceptor(chain: Interceptor.Chain): Response {
|
||||||
|
val request = chain.request()
|
||||||
|
val response = chain.proceed(request)
|
||||||
|
|
||||||
|
if (response.code != 202) return response
|
||||||
|
val url = request.url
|
||||||
|
|
||||||
|
Cookie.parse(url, getJSChallengeCookie(response))
|
||||||
|
?.let { client.cookieJar.saveFromResponse(url, listOf(it)) }
|
||||||
|
?: throw IOException("Failed JavaScript challenge. Check WebView.")
|
||||||
|
|
||||||
|
return client.newCall(request).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getJSChallengeCookie(response: Response): String {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
|
||||||
|
val jsPatch = ";function atob(a){return base64.atob(a)};document.cookie"
|
||||||
|
val jsPayload = document.select("script")
|
||||||
|
.joinToString("\n") { it.data() }
|
||||||
|
.trimIndent() + jsPatch
|
||||||
|
|
||||||
|
val fauxBase64 = FauxBase64()
|
||||||
|
val fauxLocation = FauxLocation()
|
||||||
|
val fauxDocument = FauxDocument()
|
||||||
|
val slowAES = FauxSlowAES()
|
||||||
|
|
||||||
|
QuickJs.create().use { context ->
|
||||||
|
context.set("base64", FauxBase64Interface::class.java, fauxBase64)
|
||||||
|
context.set("location", FauxLocationInterface::class.java, fauxLocation)
|
||||||
|
context.set("document", FauxDocumentInterface::class.java, fauxDocument)
|
||||||
|
context.set("slowAES", FauxSlowAESInterface::class.java, slowAES)
|
||||||
|
|
||||||
|
return context.evaluate(jsPayload) as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FauxBase64 : FauxBase64Interface {
|
||||||
|
override fun atob(base64: String): String {
|
||||||
|
return String(Base64.decode(base64, Base64.DEFAULT))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FauxLocation : FauxLocationInterface {
|
||||||
|
override var href: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class FauxDocument : FauxDocumentInterface {
|
||||||
|
override var cookie: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class FauxSlowAES : FauxSlowAESInterface {
|
||||||
|
private fun Array<Int>.toByteArray(): ByteArray {
|
||||||
|
return map { it.toByte() }.toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteArray.toTypedArray(): Array<Int> {
|
||||||
|
return map { it.toInt() and 0xFF }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decrypt(cipherIn: Array<Int>, mode: Int, key: Array<Int>, iv: Array<Int>): Array<Int> {
|
||||||
|
val modeStr = when (mode) {
|
||||||
|
0 -> "OFB"
|
||||||
|
1 -> "CFB"
|
||||||
|
else -> "CBC" // 2 = CBC, 3+ = unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
val cipher = Cipher.getInstance("AES/$modeStr/NoPadding")
|
||||||
|
val keyS = SecretKeySpec(key.toByteArray(), "AES")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(iv.toByteArray()))
|
||||||
|
|
||||||
|
return cipher.doFinal(cipherIn.toByteArray()).toTypedArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FauxBase64Interface {
|
||||||
|
fun atob(base64: String): String
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FauxLocationInterface {
|
||||||
|
val href: String
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface FauxDocumentInterface {
|
||||||
|
val cookie: String
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
private interface FauxSlowAESInterface {
|
||||||
|
fun decrypt(cipherIn: Array<Int>, mode: Int, key: Array<Int>, iv: Array<Int>): Array<Int>
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// JS Challenge logic end
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,7 +237,7 @@ class MadaraGenerator : ThemeSourceGenerator {
|
||||||
SingleLang("MangaFoxFull", "https://mangafoxfull.com", "en"),
|
SingleLang("MangaFoxFull", "https://mangafoxfull.com", "en"),
|
||||||
SingleLang("MangaFreak.online", "https://mangafreak.online", "en", className = "MangaFreakOnline"),
|
SingleLang("MangaFreak.online", "https://mangafreak.online", "en", className = "MangaFreakOnline"),
|
||||||
SingleLang("MangaGG", "https://mangagg.com", "en", overrideVersionCode = 2),
|
SingleLang("MangaGG", "https://mangagg.com", "en", overrideVersionCode = 2),
|
||||||
SingleLang("MangaGreat", "https://mangagreat.com", "en", overrideVersionCode = 3),
|
SingleLang("MangaGreat", "https://mangagreat.com", "en", overrideVersionCode = 4),
|
||||||
SingleLang("MangaHub.fr", "https://mangahub.fr", "fr", isNsfw = true, className = "MangaHubFr", pkgName = "mangahubfr"),
|
SingleLang("MangaHub.fr", "https://mangahub.fr", "fr", isNsfw = true, className = "MangaHubFr", pkgName = "mangahubfr"),
|
||||||
SingleLang("MangaHZ", "https://www.mangahz.com", "en", isNsfw = true, overrideVersionCode = 2),
|
SingleLang("MangaHZ", "https://www.mangahz.com", "en", isNsfw = true, overrideVersionCode = 2),
|
||||||
SingleLang("MangaK2", "https://mangak2.com", "en", isNsfw = true),
|
SingleLang("MangaK2", "https://mangak2.com", "en", isNsfw = true),
|
||||||
|
|
Loading…
Reference in New Issue