Shamrock: fix #236

This commit is contained in:
白池 2024-02-17 09:37:01 +08:00
parent 63ce2d40bd
commit 3b210d7ed0
12 changed files with 211 additions and 24 deletions

View File

@ -0,0 +1,29 @@
package com.tencent.mobileqq.perf.block;
import android.os.Bundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import epic.EIPCClient;
import epic.EIPCResult;
import kotlin.Metadata;
import kotlin.jvm.JvmStatic;
public final class BinderMethodProxy {
@NotNull
public static final BinderMethodProxy INSTANCE;
static {
INSTANCE = new BinderMethodProxy();
}
@JvmStatic
public static EIPCResult callServer(@NotNull EIPCClient client, @Nullable String module, @Nullable String action, @Nullable Bundle bundle) {
//MainBlockMethodMonitor.onMethodStart();
//EIPCResult callServer = client.callServer(str, str2, bundle);
//MainBlockMethodMonitor.onMethodEnd();
//return callServer;
return null;
}
}

View File

@ -0,0 +1,13 @@
package com.tencent.mobileqq.qipc;
import epic.EIPCClient;
public class QIPCClientHelper {
public static synchronized QIPCClientHelper getInstance() {
return null;
}
public EIPCClient getClient() {
return null;
}
}

View File

@ -0,0 +1,7 @@
package com.tencent.mobileqq.qmmkv;
public class MMKVOptionEntity {
public String decodeString(String str, String str2) {
return "";
}
}

View File

@ -0,0 +1,9 @@
package com.tencent.mobileqq.qmmkv;
import android.content.Context;
public class QMMKV {
public static MMKVOptionEntity from(Context context, String str) {
return null;
}
}

View File

@ -0,0 +1,40 @@
package com.tencent.mobileqq.transfile.dns;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
public class IpData implements Parcelable {
public int mFailedCount;
public String mIp;
public int mPort;
public int mType;
/**
* Describe the kinds of special objects contained in this Parcelable
* instance's marshaled representation. For example, if the object will
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
* the return value of this method must include the
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
*
* @return a bitmask indicating the set of special object types marshaled
* by this Parcelable object instance.
*/
@Override
public int describeContents() {
return 0;
}
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
}
}

View File

@ -0,0 +1,4 @@
package epic;
public class EIPCClient {
}

View File

@ -0,0 +1,11 @@
package epic;
import android.os.Bundle;
public class EIPCResult {
public Bundle data;
public boolean isSuccess() {
return false;
}
}

View File

@ -1,5 +1,6 @@
package moe.fuqiuluo.qqinterface.servlet.msg.convert package moe.fuqiuluo.qqinterface.servlet.msg.convert
import com.tencent.mobileqq.qmmkv.QMMKV
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
@ -13,6 +14,8 @@ import moe.fuqiuluo.shamrock.helper.db.MessageDB
import moe.fuqiuluo.shamrock.tools.asJsonObject import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.json import moe.fuqiuluo.shamrock.tools.json
import mqq.app.MobileQQ
import kotlin.jvm.internal.Intrinsics
internal sealed class MessageElemConverter: IMessageConvert { internal sealed class MessageElemConverter: IMessageConvert {
/** /**
@ -135,14 +138,16 @@ internal sealed class MessageElemConverter: IMessageConvert {
ImageMapping(md5.uppercase(), chatType, image.fileSize) ImageMapping(md5.uppercase(), chatType, image.fileSize)
) )
//LogCenter.log(image.toString())
return MessageSegment( return MessageSegment(
type = "image", type = "image",
data = hashMapOf( data = hashMapOf(
"file" to md5, "file" to md5,
"url" to when(chatType) { "url" to when(chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(md5) MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(image.originImageUrl, md5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(md5) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(image.originImageUrl, md5)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(md5) MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(image.originImageUrl, md5)
else -> unknownChatType(chatType) else -> unknownChatType(chatType)
}, },
"subType" to image.picSubType, "subType" to image.picSubType,

View File

@ -1,9 +1,13 @@
@file:OptIn(ExperimentalSerializationApi::class) @file:OptIn(ExperimentalSerializationApi::class)
package moe.fuqiuluo.qqinterface.servlet.transfile package moe.fuqiuluo.qqinterface.servlet.transfile
import android.os.Bundle
import com.tencent.mobileqq.pb.ByteStringMicro import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.mobileqq.perf.block.BinderMethodProxy
import com.tencent.mobileqq.qipc.QIPCClientHelper
import com.tencent.mobileqq.transfile.FileMsg import com.tencent.mobileqq.transfile.FileMsg
import com.tencent.mobileqq.transfile.api.IProtoReqManager import com.tencent.mobileqq.transfile.api.IProtoReqManager
import com.tencent.mobileqq.transfile.dns.IpData
import com.tencent.mobileqq.transfile.protohandler.RichProto import com.tencent.mobileqq.transfile.protohandler.RichProto
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
@ -19,18 +23,35 @@ import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import mqq.app.MobileQQ
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
import protobuf.oidb.cmd0xfc2.Oidb0xfc2RspBody import protobuf.oidb.cmd0xfc2.Oidb0xfc2RspBody
import mqq.app.MobileQQ
import tencent.im.cs.cmd0x346.cmd0x346 import tencent.im.cs.cmd0x346.cmd0x346
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6 import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
import tencent.im.oidb.cmd0xe37.cmd0xe37 import tencent.im.oidb.cmd0xe37.cmd0xe37
import tencent.im.oidb.oidb_sso import tencent.im.oidb.oidb_sso
import java.util.ArrayList
import kotlin.coroutines.resume import kotlin.coroutines.resume
private const val GPRO_PIC = "gchat.qpic.cn"
private const val GPRO_PIC_NT = "multimedia.nt.qq.com.cn"
private const val C2C_PIC = "c2cpicdw.qpic.cn"
internal object RichProtoSvc: BaseSvc() { internal object RichProtoSvc: BaseSvc() {
/*@Deprecated("Use RichProtoSvc.getQQDns instead", ReplaceWith("getQQDns(domain)"))
fun getQQDns(domain: String) {
val bundle = Bundle()
bundle.putString("domain", "xxx")
bundle.putInt("businessType", 1)
val result = BinderMethodProxy
.callServer(QIPCClientHelper.getInstance().client, "InnerDnsModule", "reqDomain2IpList", bundle)
if (result.isSuccess) {
val ipList: ArrayList<IpData> = result.data.getParcelableArrayList("ip")!!
}
}*/
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String { suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray( val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray(
Oidb0xfc2ReqBody( Oidb0xfc2ReqBody(
@ -142,19 +163,35 @@ internal object RichProtoSvc: BaseSvc() {
} }
fun getGroupPicDownUrl( fun getGroupPicDownUrl(
originalUrl: String,
md5: String md5: String
): String { ): String {
return "http://gchat.qpic.cn/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2" val domain = if (originalUrl.contains("rkey=")) GPRO_PIC_NT else GPRO_PIC
if (originalUrl.isNotEmpty()) {
return "https://$domain$originalUrl"
}
return "https://$domain/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2"
} }
fun getC2CPicDownUrl( fun getC2CPicDownUrl(
originalUrl: String,
md5: String md5: String
): String { ): String {
return "https://c2cpicdw.qpic.cn/offpic_new/0/123-0-${md5.uppercase()}/0?term=2" if (originalUrl.isNotEmpty()) {
return "https://$C2C_PIC$originalUrl"
}
return "https://$C2C_PIC/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
} }
fun getGuildPicDownUrl(md5: String): String { fun getGuildPicDownUrl(
return "https://gchat.qpic.cn/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2" originalUrl: String,
md5: String
): String {
val domain = if (originalUrl.contains("rkey=")) GPRO_PIC_NT else GPRO_PIC
if (originalUrl.isNotEmpty()) {
return "https://$domain$originalUrl"
}
return "https://$domain/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
} }
suspend fun getC2CVideoDownUrl( suspend fun getC2CVideoDownUrl(
@ -321,12 +358,4 @@ internal object RichProtoSvc: BaseSvc() {
RichProtoProc.procRichProtoReq(richProtoReq) RichProtoProc.procRichProtoReq(richProtoReq)
} }
} }
suspend fun getGuildPttDownUrl(
peerId: String,
md5Hex: String,
fileUUId: String
): String {
return "unsupported"
}
} }

View File

@ -35,8 +35,8 @@ internal object GetImage: IActionHandler() {
image.size, image.size,
image.fileName, image.fileName,
when(image.chatType) { when(image.chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(fileMd5) MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl("", fileMd5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(fileMd5) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl("", fileMd5)
else -> error("Not supported chat type: ${image.chatType}, convertMsgElementsToMsgSegment::Pic") else -> error("Not supported chat type: ${image.chatType}, convertMsgElementsToMsgSegment::Pic")
} }
), echo = echo) ), echo = echo)

View File

@ -103,7 +103,7 @@ internal object UploadGroupFile : IActionHandler() {
// 根据文件大小调整超时时间 // 根据文件大小调整超时时间
val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP) val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP)
val info = (withTimeoutOrNull((srcFile.length() / (300 * 1024)) * 1000 + 5000) { val info = (withTimeoutOrNull((srcFile.length() / (125 * 1024)) * 1000 + 5000) {
val msgService = QRoute.api(IMsgService::class.java) val msgService = QRoute.api(IMsgService::class.java)
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId) val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
suspendCancellableCoroutine<FileTransNotifyInfo?> { suspendCancellableCoroutine<FileTransNotifyInfo?> {

View File

@ -1,12 +1,42 @@
@file:Suppress("UNUSED_VARIABLE", "LocalVariableName")
package moe.fuqiuluo.shamrock.xposed.hooks package moe.fuqiuluo.shamrock.xposed.hooks
import android.content.Context import android.content.Context
import com.tencent.mobileqq.perf.block.BinderMethodProxy
import com.tencent.mobileqq.qmmkv.MMKVOptionEntity
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.xposed.loader.LuoClassloader
import moe.fuqiuluo.symbols.Process
import moe.fuqiuluo.symbols.XposedHook import moe.fuqiuluo.symbols.XposedHook
import java.lang.reflect.Modifier
@XposedHook(priority = -1) @XposedHook(priority = -1, process = Process.ALL)
internal class HookForDebug: IAction { internal class HookForDebug: IAction {
override fun invoke(ctx: Context) { override fun invoke(ctx: Context) {
/* /*val NtDnsManager = LuoClassloader.load("com.tencent.qqnt.dns.NtDnsManager")!!
val NtDnsInternal = NtDnsManager.declaredMethods.first {
!Modifier.isStatic(it.modifiers) && it.parameterCount == 0
}.returnType
XposedBridge.hookMethod(NtDnsInternal.declaredMethods.first {
it.parameterCount == 2
&& it.parameterTypes[0] == String::class.java
&& it.parameterTypes[1] == Int::class.java
&& it.returnType == ArrayList::class.java
}, beforeHook {
val domain = it.args[0] as String
val type = it.args[1] as Int
LogCenter.log("NtDnsManager: reqDomain2IpList($domain, $type)")
LogCenter.log(Exception().stackTraceToString())
})*/
}
}
/*
val httpEngineService = AppRuntimeFetcher.appRuntime val httpEngineService = AppRuntimeFetcher.appRuntime
.getRuntimeService(IHttpEngineService::class.java, "all") .getRuntimeService(IHttpEngineService::class.java, "all")
httpEngineService.javaClass.hookMethod("sendReq").before { httpEngineService.javaClass.hookMethod("sendReq").before {
@ -19,7 +49,17 @@ internal class HookForDebug: IAction {
LogCenter.log("请求地址: ${req.mReqUrl}") LogCenter.log("请求地址: ${req.mReqUrl}")
LogCenter.log("请求: ${req.toInnerValuesString(NetReq::class.java)}") LogCenter.log("请求: ${req.toInnerValuesString(NetReq::class.java)}")
} }
}
BinderMethodProxy::class.java.hookMethod("callServer").before {
val action = it.args[2] as String
if (action == "reqDomain2IpList") {
LogCenter.log(Exception().stackTraceToString())
}
}
EIPCClient::class.java.hookMethod("callServer").before {
val module = it.args[0] as String
val action = it.args[1] as String
if (action == "reqDomain2IpList" || module.contains("dns", ignoreCase = true)) {
LogCenter.log(Exception().stackTraceToString())
}
}*/ }*/
}
}