diff --git a/qqinterface/src/main/java/com/tencent/guild/api/transfile/IGuildTransFileApi.java b/qqinterface/src/main/java/com/tencent/guild/api/transfile/IGuildTransFileApi.java new file mode 100644 index 0000000..8883363 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/guild/api/transfile/IGuildTransFileApi.java @@ -0,0 +1,19 @@ +package com.tencent.guild.api.transfile; + +import androidx.annotation.Nullable; + +import com.tencent.mobileqq.qroute.QRouteApi; +import com.tencent.qqnt.kernel.nativeinterface.BigDataTicket; + +public interface IGuildTransFileApi extends QRouteApi { + //@Nullable + //ArrayList getBigDataIpList(boolean z, @Nullable IpType ipType); + + @Nullable + BigDataTicket getBigDataTicket(); + + //@Nullable + //ArrayList getIpDirectList(@Nullable String str, @Nullable IpType ipType); + + void pullConfigIfNeed(); +} \ No newline at end of file diff --git a/qqinterface/src/main/java/com/tencent/libra/download/ILibraDownloader.java b/qqinterface/src/main/java/com/tencent/libra/download/ILibraDownloader.java new file mode 100644 index 0000000..baa8f9f --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/libra/download/ILibraDownloader.java @@ -0,0 +1,26 @@ +package com.tencent.libra.download; + +import androidx.annotation.NonNull; + +import com.tencent.libra.request.Option; + +public interface ILibraDownloader { + class PicDownLoadListener { + Option mOption; + + public PicDownLoadListener(@NonNull Option option) { + this.mOption = option; + } + + public void onResult(boolean success, int code) { + } + } + + boolean canDownload(Option option); + + void cancel(Option option); + + void downLoad(Option option, PicDownLoadListener picDownLoadListener); + + boolean needDownloadOnWorkThread(); +} diff --git a/qqinterface/src/main/java/com/tencent/libra/request/Option.java b/qqinterface/src/main/java/com/tencent/libra/request/Option.java new file mode 100644 index 0000000..65f084a --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/libra/request/Option.java @@ -0,0 +1,4 @@ +package com.tencent.libra.request; + +public class Option { +} diff --git a/qqinterface/src/main/java/com/tencent/qqnt/aio/api/IAIOPicDownloaderProvider.java b/qqinterface/src/main/java/com/tencent/qqnt/aio/api/IAIOPicDownloaderProvider.java new file mode 100644 index 0000000..44a7248 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/qqnt/aio/api/IAIOPicDownloaderProvider.java @@ -0,0 +1,11 @@ +package com.tencent.qqnt.aio.api; + +import com.tencent.libra.download.ILibraDownloader; +import com.tencent.mobileqq.qroute.QRouteApi; + +import org.jetbrains.annotations.NotNull; + +public interface IAIOPicDownloaderProvider extends QRouteApi { + @NotNull + ILibraDownloader provideDownloader(); +} \ No newline at end of file diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/api/impl/MsgService.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/api/impl/MsgService.java index f6c1817..3fc02e1 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/api/impl/MsgService.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/api/impl/MsgService.java @@ -3,6 +3,7 @@ package com.tencent.qqnt.kernel.api.impl; import com.tencent.qqnt.kernel.nativeinterface.IGetTempChatInfoCallback; import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener; import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback; +import com.tencent.qqnt.kernel.nativeinterface.RichMediaElementGetReq; import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo; import com.tencent.qqnt.kernel.nativeinterface.TempChatPrepareInfo; @@ -10,6 +11,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class MsgService { + void getRichMediaElement(@NotNull RichMediaElementGetReq req) { + + } + public void addMsgListener(IKernelMsgListener listener) { } diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/BigDataTicket.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/BigDataTicket.java new file mode 100644 index 0000000..4cbcda8 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/BigDataTicket.java @@ -0,0 +1,30 @@ +package com.tencent.qqnt.kernel.nativeinterface; + +public final class BigDataTicket { + public String sessionKey; + public String sessionSig; + + public BigDataTicket() { + this.sessionSig = ""; + this.sessionKey = ""; + } + + public String getSessionKey() { + return this.sessionKey; + } + + public String getSessionSig() { + return this.sessionSig; + } + + public String toString() { + return "BigDataTicket{sessionSig=" + this.sessionSig + ",sessionKey=" + this.sessionKey + ",}"; + } + + public BigDataTicket(String str, String str2) { + this.sessionSig = ""; + this.sessionKey = ""; + this.sessionSig = str; + this.sessionKey = str2; + } +} \ No newline at end of file diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/PicElement.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/PicElement.java index c8ca2bc..71b2d0f 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/PicElement.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/PicElement.java @@ -264,6 +264,10 @@ public final class PicElement implements IKernelModel { this.transferStatus = num; } + public int getStoreID() { + return 0; + } + public String toString() { return "PicElement{picSubType=" + this.picSubType + ",fileName=" + this.fileName + ",fileSize=" + this.fileSize + ",picWidth=" + this.picWidth + ",picHeight=" + this.picHeight + ",original=" + this.original + ",md5HexStr=" + this.md5HexStr + ",sourcePath=" + this.sourcePath + ",thumbPath=" + this.thumbPath + ",transferStatus=" + this.transferStatus + ",progress=" + this.progress + ",picType=" + this.picType + ",invalidState=" + this.invalidState + ",fileUuid=" + this.fileUuid + ",fileSubId=" + this.fileSubId + ",thumbFileSize=" + this.thumbFileSize + ",fileBizId=" + this.fileBizId + ",downloadIndex=" + this.downloadIndex + ",summary=" + this.summary + ",emojiFrom=" + this.emojiFrom + ",emojiWebUrl=" + this.emojiWebUrl + ",emojiAd=" + this.emojiAd + ",emojiMall=" + this.emojiMall + ",emojiZplan=" + this.emojiZplan + ",originImageMd5=" + this.originImageMd5 + ",originImageUrl=" + this.originImageUrl + ",importRichMediaContext=" + this.importRichMediaContext + ",isFlashPic=" + this.isFlashPic + ",}"; } diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/RichMediaElementGetReq.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/RichMediaElementGetReq.java new file mode 100644 index 0000000..af71a78 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/RichMediaElementGetReq.java @@ -0,0 +1,118 @@ +package com.tencent.qqnt.kernel.nativeinterface; + +public final class RichMediaElementGetReq implements IKernelModel { + public int chatType; + public int downSourceType; + public int downloadType; + public long elementId; + public long fileModelId; + public String filePath; + public long msgId; + public String peerUid; + public int thumbSize; + public int triggerType; + + public RichMediaElementGetReq() { + this.peerUid = ""; + this.filePath = ""; + } + + public int getChatType() { + return this.chatType; + } + + public int getDownSourceType() { + return this.downSourceType; + } + + public int getDownloadType() { + return this.downloadType; + } + + public long getElementId() { + return this.elementId; + } + + public long getFileModelId() { + return this.fileModelId; + } + + public String getFilePath() { + return this.filePath; + } + + public long getMsgId() { + return this.msgId; + } + + public String getPeerUid() { + return this.peerUid; + } + + public int getThumbSize() { + return this.thumbSize; + } + + public int getTriggerType() { + return this.triggerType; + } + + public void setChatType(int i2) { + this.chatType = i2; + } + + public void setDownSourceType(int i2) { + this.downSourceType = i2; + } + + public void setDownloadType(int i2) { + this.downloadType = i2; + } + + public void setElementId(long j2) { + this.elementId = j2; + } + + public void setFileModelId(long j2) { + this.fileModelId = j2; + } + + public void setFilePath(String str) { + this.filePath = str; + } + + public void setMsgId(long j2) { + this.msgId = j2; + } + + public void setPeerUid(String str) { + this.peerUid = str; + } + + public void setThumbSize(int i2) { + this.thumbSize = i2; + } + + public void setTriggerType(int i2) { + this.triggerType = i2; + } + + public String toString() { + return "RichMediaElementGetReq{msgId=" + this.msgId + ",peerUid=" + this.peerUid + ",chatType=" + this.chatType + ",elementId=" + this.elementId + ",downloadType=" + this.downloadType + ",thumbSize=" + this.thumbSize + ",filePath=" + this.filePath + ",fileModelId=" + this.fileModelId + ",downSourceType=" + this.downSourceType + ",triggerType=" + this.triggerType + ",}"; + } + + public RichMediaElementGetReq(long j2, String str, int i2, long j3, int i3, int i4, String str2, long j4, int i5, int i6) { + this.peerUid = ""; + this.filePath = ""; + this.msgId = j2; + this.peerUid = str; + this.chatType = i2; + this.elementId = j3; + this.downloadType = i3; + this.thumbSize = i4; + this.filePath = str2; + this.fileModelId = j4; + this.downSourceType = i5; + this.triggerType = i6; + } +} diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/TicketSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/TicketSvc.kt index 2b3551b..26e4564 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/TicketSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/TicketSvc.kt @@ -13,8 +13,11 @@ import tencent.im.oidb.oidb_sso internal object TicketSvc: BaseSvc() { object SigType { - const val WLOGIN_A2 = 64 const val WLOGIN_A5 = 2 + const val WLOGIN_RESERVED = 16 + const val WLOGIN_STWEB = 32 // TLV 103 + const val WLOGIN_A2 = 64 + const val WLOGIN_ST = 128 const val WLOGIN_AQSIG = 2097152 const val WLOGIN_D2 = 262144 const val WLOGIN_DA2 = 33554432 @@ -26,14 +29,17 @@ internal object TicketSvc: BaseSvc() { const val WLOGIN_PSKEY = 1048576 const val WLOGIN_PT4Token = 134217728 const val WLOGIN_QRPUSH = 67108864 - const val WLOGIN_RESERVED = 16 const val WLOGIN_SID = 524288 const val WLOGIN_SIG64 = 8192 const val WLOGIN_SKEY = 4096 - const val WLOGIN_ST = 128 - const val WLOGIN_STWEB = 32 // TLV 103 const val WLOGIN_TOKEN = 32768 const val WLOGIN_VKEY = 131072 + + val ALL_TICKET = arrayOf( + WLOGIN_A5, WLOGIN_RESERVED, WLOGIN_STWEB, WLOGIN_A2, WLOGIN_ST, WLOGIN_AQSIG, WLOGIN_D2, WLOGIN_DA2, + WLOGIN_LHSIG, WLOGIN_LSKEY, WLOGIN_OPENKEY, WLOGIN_PAYTOKEN, WLOGIN_PF, WLOGIN_PSKEY, WLOGIN_PT4Token, + WLOGIN_QRPUSH, WLOGIN_SID, WLOGIN_SIG64, WLOGIN_SKEY, WLOGIN_TOKEN, WLOGIN_VKEY + ) } fun getUin(): String { diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt index 368a5dc..7c8c109 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt @@ -141,8 +141,7 @@ internal sealed class MessageElemConverter: IMessageConvert { //LogCenter.log(image.toString()) val originalUrl = image.originImageUrl ?: "" - - LogCenter.log("receive image: $image", Level.DEBUG) + //LogCenter.log({ "receive image: $image" }, Level.DEBUG) return MessageSegment( type = "image", diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt index 98232fc..79fa641 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt @@ -164,10 +164,13 @@ internal object RichProtoSvc: BaseSvc() { fun getGroupPicDownUrl( originalUrl: String, - md5: String + md5: String, ): String { - val domain = if (originalUrl.contains("rkey=")) GPRO_PIC_NT else GPRO_PIC + val domain = if (originalUrl.startsWith("/download")) GPRO_PIC_NT else GPRO_PIC if (originalUrl.isNotEmpty()) { + if (!originalUrl.contains("rkey=")) { + return "https://$domain$originalUrl&rkey=CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64" + } return "https://$domain$originalUrl" } return "https://$domain/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2" @@ -187,7 +190,9 @@ internal object RichProtoSvc: BaseSvc() { originalUrl: String, md5: String ): String { - val domain = if (originalUrl.contains("rkey=")) GPRO_PIC_NT else GPRO_PIC + val domain = if (originalUrl.startsWith("/download") || + originalUrl.contains("rkey=")) GPRO_PIC_NT + else GPRO_PIC if (originalUrl.isNotEmpty()) { return "https://$domain$originalUrl" } diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetCookies.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetCookies.kt index 56f6adb..1f4f1b0 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetCookies.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/GetCookies.kt @@ -1,9 +1,12 @@ package moe.fuqiuluo.shamrock.remote.action.handlers +import com.tencent.guild.api.transfile.IGuildTransFileApi +import com.tencent.mobileqq.qroute.QRoute import kotlinx.serialization.json.JsonElement import moe.fuqiuluo.qqinterface.servlet.TicketSvc import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.IActionHandler +import moe.fuqiuluo.shamrock.remote.service.data.BigDataTicket import moe.fuqiuluo.shamrock.remote.service.data.Credentials import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.symbols.OneBotHandler @@ -17,10 +20,20 @@ internal object GetCookies: IActionHandler() { } operator fun invoke(echo: JsonElement = EmptyJsonString): String { - return ok(Credentials(cookie = TicketSvc.getCookie()), echo) + return ok(Credentials( + cookie = TicketSvc.getCookie(), + bigDataTicket = QRoute.api(IGuildTransFileApi::class.java).bigDataTicket?.let { + BigDataTicket(it.sessionKey, it.sessionSig) + } + ), echo) } suspend operator fun invoke(domain: String, echo: JsonElement = EmptyJsonString): String { - return ok(Credentials(cookie = TicketSvc.getCookie(domain)), echo) + return ok(Credentials( + cookie = TicketSvc.getCookie(domain), + bigDataTicket = QRoute.api(IGuildTransFileApi::class.java).bigDataTicket?.let { + BigDataTicket(it.sessionKey, it.sessionSig) + } + ), echo) } } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/TicketAction.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/TicketAction.kt index e14b229..f082d0f 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/TicketAction.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/TicketAction.kt @@ -5,7 +5,9 @@ import moe.fuqiuluo.qqinterface.servlet.TicketSvc import io.ktor.server.application.call import io.ktor.server.response.respondText import io.ktor.server.routing.Routing +import kotlinx.serialization.json.JsonElement import moe.fuqiuluo.shamrock.remote.action.handlers.* +import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig import moe.fuqiuluo.shamrock.remote.structures.Status import moe.fuqiuluo.shamrock.tools.* @@ -44,20 +46,36 @@ fun Routing.ticketActions() { } } + fun getTicket(uin: String, id: Int, debug: Boolean = false) = TicketSvc.getTicket(uin, id)?.let { + mutableMapOf( + "sig" to (it._sig?.toHexString() ?: "null"), + "key" to (it._sig_key?.toHexString() ?: "null") + ).also { map -> + if (debug) + map["content"] = ((it._sig?.decodeToString() ?: "") + ":" + (it._sig_key?.decodeToString() ?: "null")) + }.json.asJsonObject + } ?: EmptyJsonObject + getOrPost("/get_ticket") { val uin = fetchOrThrow("uin") val ticket = when(val id = fetchOrThrow("id").toInt()) { 32 -> TicketSvc.getStWeb(uin) else -> { - respond(true, Status.Ok, data = TicketSvc.getTicket(uin, id)?.let { - mapOf( - "sig" to (it._sig?.toHexString() ?: "null"), - "key" to (it._sig_key?.toHexString() ?: "null") - ).json.asJsonObject - } ?: EmptyJsonObject) + respond(true, Status.Ok, data = getTicket(uin, id)) return@getOrPost } } respond(true, Status.Ok, data = ticket) } + + if (ShamrockConfig.isDev()) getOrPost("/get_all_ticket") { + val uin = fetchOrThrow("uin") + + val ticketMap = mutableMapOf() + TicketSvc.SigType.ALL_TICKET.forEach { + ticketMap[it] = getTicket(uin, it, true) + } + + respond(true, Status.Ok, data = ticketMap) + } } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/TicketData.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/TicketData.kt index 1f1d3c5..b0d69c7 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/TicketData.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/data/TicketData.kt @@ -6,5 +6,12 @@ import kotlinx.serialization.Serializable @Serializable internal data class Credentials( @SerialName("token") val bkn: String = "", - @SerialName("cookies") val cookie: String = "" + @SerialName("cookies") val cookie: String = "", + @SerialName("bigdata_ticket") val bigDataTicket: BigDataTicket? = null +) + +@Serializable +data class BigDataTicket( + var key: String? = null, + var sig: String? = null ) \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/hooks/HookForDebug.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/hooks/HookForDebug.kt index 576324f..24e5536 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/hooks/HookForDebug.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/hooks/HookForDebug.kt @@ -5,11 +5,14 @@ package moe.fuqiuluo.shamrock.xposed.hooks import android.content.Context import com.tencent.mobileqq.perf.block.BinderMethodProxy import com.tencent.mobileqq.qmmkv.MMKVOptionEntity +import com.tencent.mobileqq.qroute.QRoute +import com.tencent.qqnt.aio.api.IAIOPicDownloaderProvider import de.robv.android.xposed.XposedBridge import epic.EIPCClient import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.tools.beforeHook import moe.fuqiuluo.shamrock.tools.hookMethod +import moe.fuqiuluo.shamrock.tools.toInnerValuesString import moe.fuqiuluo.shamrock.xposed.loader.LuoClassloader import moe.fuqiuluo.symbols.Process import moe.fuqiuluo.symbols.XposedHook @@ -18,7 +21,15 @@ import java.lang.reflect.Modifier @XposedHook(priority = -1, process = Process.ALL) internal class HookForDebug: IAction { override fun invoke(ctx: Context) { - /*val NtDnsManager = LuoClassloader.load("com.tencent.qqnt.dns.NtDnsManager")!! + //val LibraDownloader = QRoute.api(IAIOPicDownloaderProvider::class.java).provideDownloader().javaClass + //LibraDownloader.hookMethod("downLoad").before { + // val option = it.args[0] + // LogCenter.log("LibraDownloader.downLoad(${option.toInnerValuesString()})") + //} + } +} + +/*val NtDnsManager = LuoClassloader.load("com.tencent.qqnt.dns.NtDnsManager")!! val NtDnsInternal = NtDnsManager.declaredMethods.first { !Modifier.isStatic(it.modifiers) && it.parameterCount == 0 }.returnType @@ -33,9 +44,6 @@ internal class HookForDebug: IAction { LogCenter.log("NtDnsManager: reqDomain2IpList($domain, $type)") LogCenter.log(Exception().stackTraceToString()) })*/ - } -} - /* val httpEngineService = AppRuntimeFetcher.appRuntime .getRuntimeService(IHttpEngineService::class.java, "all")