mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-30 19:00:33 +08:00
Slider captcha solver
This commit is contained in:
parent
817f3b02a1
commit
905823b94b
@ -53,19 +53,21 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
is Captcha -> when (response) {
|
||||
is Captcha.Picture -> {
|
||||
bot.logger.info("需要图片验证码")
|
||||
var result = bot.configuration.loginSolver.onSolvePicCaptcha(bot, response.data)
|
||||
if (result === null || result.length != 4) {
|
||||
if (result == null || result.length != 4) {
|
||||
//refresh captcha
|
||||
result = "ABCD"
|
||||
}
|
||||
bot.logger.info("提交验证码")
|
||||
response = LoginPacket.SubCommand2(bot.client, response.sign, result).sendAndExpect()
|
||||
response = LoginPacket.SubCommand2.SubmitPictureCaptcha(bot.client, response.sign, result).sendAndExpect()
|
||||
continue@mainloop
|
||||
}
|
||||
is Captcha.Slider -> {
|
||||
bot.logger.info("需要滑动验证码")
|
||||
TODO("滑动验证码")
|
||||
var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url)
|
||||
if (ticket == null) {
|
||||
ticket = ""
|
||||
}
|
||||
response = LoginPacket.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
|
||||
continue@mainloop
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,52 +264,6 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
|
||||
}
|
||||
|
||||
internal inline fun BytePacketBuilder.writeSmsSsoPacket(
|
||||
client: QQAndroidClient,
|
||||
subAppId: Long,
|
||||
commandName: String,
|
||||
extraData: ByteReadPacket = BRP_STUB,
|
||||
sequenceId: Int,
|
||||
body: BytePacketBuilder.() -> Unit
|
||||
) {
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }) {
|
||||
writeInt(sequenceId)
|
||||
writeInt(subAppId.toInt())
|
||||
writeInt(subAppId.toInt())
|
||||
writeHex("00 00 00 00 00 00 00 00 00 00 01 00")
|
||||
if (extraData === BRP_STUB) {
|
||||
writeInt(0x04)
|
||||
} else {
|
||||
writeInt((extraData.remaining + 4).toInt())
|
||||
writePacket(extraData)
|
||||
}
|
||||
commandName.let {
|
||||
writeInt(it.length + 4)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
|
||||
writeInt(4 + 4)
|
||||
writeInt(45112203) // 02 B0 5B 8B
|
||||
|
||||
client.device.imei.let {
|
||||
writeInt(it.length + 4)
|
||||
writeStringUtf8(it)
|
||||
}
|
||||
|
||||
writeInt(4)
|
||||
|
||||
client.ksid.let {
|
||||
writeShort((it.size + 2).toShort())
|
||||
writeFully(it)
|
||||
}
|
||||
|
||||
writeInt(4)
|
||||
}
|
||||
|
||||
// body
|
||||
writeIntLVPacket(lengthOffset = { it + 4 }, builder = body)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes a request packet
|
||||
|
@ -566,6 +566,15 @@ fun BytePacketBuilder.t188(
|
||||
} shouldEqualsTo 16
|
||||
}
|
||||
|
||||
fun BytePacketBuilder.t193(
|
||||
ticket: String
|
||||
) {
|
||||
writeShort(0x193)
|
||||
writeShortLVPacket {
|
||||
writeFully(ticket.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
fun BytePacketBuilder.t194(
|
||||
imsiMd5: ByteArray
|
||||
) {
|
||||
|
@ -23,12 +23,30 @@ import net.mamoe.mirai.utils.io.discardExact
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wtlogin.login") {
|
||||
|
||||
/**
|
||||
* 提交验证码
|
||||
*/
|
||||
object SubCommand2 {
|
||||
private const val appId = 16L
|
||||
private const val subAppId = 537062845L
|
||||
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
operator fun invoke(
|
||||
fun SubmitSliderCaptcha(
|
||||
client: QQAndroidClient,
|
||||
ticket: String
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeShort(2) // subCommand
|
||||
writeShort(4) // count of TLVs
|
||||
t193(ticket)
|
||||
t8(2052)
|
||||
t104(client.t104)
|
||||
t116(150470524, 66560)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun SubmitPictureCaptcha(
|
||||
client: QQAndroidClient,
|
||||
captchaSign: ByteArray,
|
||||
captchaAnswer: String
|
||||
@ -36,7 +54,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeShort(2) // subCommand
|
||||
writeShort(4) // count of TLVs, probably ignored by server?
|
||||
writeShort(4) // count of TLVs
|
||||
t2(captchaAnswer, captchaSign, 0)
|
||||
t8(2052)
|
||||
t104(client.t104)
|
||||
@ -69,6 +87,9 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交 SMS
|
||||
*/
|
||||
object SubCommand7 {
|
||||
private const val appId = 16L
|
||||
private const val subAppId = 537062845L
|
||||
@ -79,7 +100,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
t402: ByteArray,
|
||||
phoneNumber: String
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
writeSmsSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId, unknownHex = "01 00 00 00 00 00 00 00 00 00 01 00") {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeShort(8) // subCommand
|
||||
writeShort(6) // count of TLVs, probably ignored by server?TODO
|
||||
@ -96,6 +117,9 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 密码登录
|
||||
*/
|
||||
object SubCommand9 {
|
||||
private const val appId = 16L
|
||||
private const val subAppId = 537062845L
|
||||
@ -257,8 +281,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
sealed class Captcha : LoginPacketResponse() {
|
||||
|
||||
class Slider(
|
||||
val data: IoBuffer,
|
||||
val sign: ByteArray
|
||||
val url: String
|
||||
) : Captcha() {
|
||||
override fun toString(): String {
|
||||
return "LoginPacketResponse.Captcha.Slider"
|
||||
@ -340,10 +363,12 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
@UseExperimental(MiraiDebugAPI::class)
|
||||
private fun onSolveLoginCaptcha(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Captcha {
|
||||
// val ret = tlvMap[0x104]?.let { println(it.toUHexString()) }
|
||||
println()
|
||||
val question = tlvMap[0x165] ?: error("CAPTCHA QUESTION UNKNOWN")
|
||||
when (question[18].toInt()) {
|
||||
0x36 -> {
|
||||
tlvMap[0x192]?.let {
|
||||
bot.client.t104 = tlvMap.getOrFail(0x104)
|
||||
return LoginPacketResponse.Captcha.Slider(it.encodeToString())
|
||||
}
|
||||
tlvMap[0x165]?.let { question ->
|
||||
if (question[18].toInt() == 0x36) {
|
||||
//图片验证
|
||||
DebugLogger.debug("是一个图片验证码")
|
||||
bot.client.t104 = tlvMap.getOrFail(0x104)
|
||||
@ -356,8 +381,10 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
|
||||
sign = sign
|
||||
)
|
||||
}
|
||||
else -> error("UNKNOWN CAPTCHA QUESTION: $question")
|
||||
else error("UNKNOWN CAPTCHA QUESTION: $question")
|
||||
}
|
||||
|
||||
error("UNKNOWN CAPTCHA")
|
||||
}
|
||||
|
||||
@UseExperimental(MiraiDebugAPI::class)
|
||||
|
@ -11,7 +11,7 @@ actual var defaultLoginSolver: LoginSolver = object : LoginSolver() {
|
||||
error("should be implemented manually by you")
|
||||
}
|
||||
|
||||
override suspend fun onSolveSliderCaptcha(bot: Bot, data: IoBuffer): String? {
|
||||
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
|
||||
error("should be implemented manually by you")
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import kotlin.jvm.JvmStatic
|
||||
abstract class LoginSolver {
|
||||
abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String?
|
||||
|
||||
abstract suspend fun onSolveSliderCaptcha(bot: Bot, data: IoBuffer): String?
|
||||
abstract suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String?
|
||||
|
||||
abstract suspend fun onGetPhoneNumber(): String
|
||||
|
||||
|
@ -34,38 +34,41 @@ class DefaultLoginSolver : LoginSolver() {
|
||||
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
|
||||
withContext(Dispatchers.IO) {
|
||||
tempFile.createNewFile()
|
||||
MiraiLogger.info("需要验证码登录, 验证码为 4 字母")
|
||||
bot.logger.info("需要图片验证码登录, 验证码为 4 字母")
|
||||
try {
|
||||
tempFile.writeChannel().use { writeFully(data) }
|
||||
MiraiLogger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
|
||||
bot.logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
|
||||
} catch (e: Exception) {
|
||||
MiraiLogger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
|
||||
bot.logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
|
||||
}
|
||||
|
||||
tempFile.inputStream().use {
|
||||
val img = ImageIO.read(it)
|
||||
if (img == null) {
|
||||
MiraiLogger.info("无法创建字符图片. 请查看文件")
|
||||
bot.logger.info("无法创建字符图片. 请查看文件")
|
||||
} else {
|
||||
MiraiLogger.info(img.createCharImg())
|
||||
bot.logger.info(img.createCharImg())
|
||||
}
|
||||
}
|
||||
}
|
||||
MiraiLogger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
|
||||
bot.logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
|
||||
return readLine()?.takeUnless { it.isEmpty() || it.length != 4 }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun onSolveSliderCaptcha(bot: Bot, data: IoBuffer): String? {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
|
||||
bot.logger.info("需要滑动验证码")
|
||||
bot.logger.info("请在任意浏览器中打开以下链接并完成验证码. ")
|
||||
bot.logger.info(url)
|
||||
return readLine()
|
||||
}
|
||||
|
||||
override suspend fun onGetPhoneNumber(): String {
|
||||
loginSolverLock.withLock {
|
||||
while (true){
|
||||
while (true) {
|
||||
MiraiLogger.info("请输入你的手机号码")
|
||||
val var0 = readLine()
|
||||
if(var0!==null && var0.length > 10){
|
||||
if (var0 !== null && var0.length > 10) {
|
||||
return var0;
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ class MiraiService : Service() {
|
||||
return mCaptchaDeferred.await()
|
||||
}
|
||||
|
||||
override suspend fun onSolveSliderCaptcha(bot: Bot, data: IoBuffer): String? {
|
||||
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user