mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 13:12:17 +08:00
Shamrock
: 临时修补rkey缺失导致的qqnt图片无法获取 #236
This commit is contained in:
parent
62385d6f62
commit
2c8094c8c8
@ -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<ServerAddress> getBigDataIpList(boolean z, @Nullable IpType ipType);
|
||||
|
||||
@Nullable
|
||||
BigDataTicket getBigDataTicket();
|
||||
|
||||
//@Nullable
|
||||
//ArrayList<ServerAddress> getIpDirectList(@Nullable String str, @Nullable IpType ipType);
|
||||
|
||||
void pullConfigIfNeed();
|
||||
}
|
@ -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();
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.tencent.libra.request;
|
||||
|
||||
public class Option {
|
||||
}
|
@ -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();
|
||||
}
|
@ -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) {
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 + ",}";
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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<Int, JsonElement>()
|
||||
TicketSvc.SigType.ALL_TICKET.forEach {
|
||||
ticketMap[it] = getTicket(uin, it, true)
|
||||
}
|
||||
|
||||
respond(true, Status.Ok, data = ticketMap)
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
@ -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")
|
||||
|
Loading…
x
Reference in New Issue
Block a user