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
import com.tencent.mobileqq.qmmkv.QMMKV
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
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.asString
import moe.fuqiuluo.shamrock.tools.json
import mqq.app.MobileQQ
import kotlin.jvm.internal.Intrinsics
internal sealed class MessageElemConverter: IMessageConvert {
/**
@ -135,14 +138,16 @@ internal sealed class MessageElemConverter: IMessageConvert {
ImageMapping(md5.uppercase(), chatType, image.fileSize)
)
//LogCenter.log(image.toString())
return MessageSegment(
type = "image",
data = hashMapOf(
"file" to md5,
"url" to when(chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(md5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(md5)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(md5)
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(image.originImageUrl, md5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(image.originImageUrl, md5)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(image.originImageUrl, md5)
else -> unknownChatType(chatType)
},
"subType" to image.picSubType,

View File

@ -1,9 +1,13 @@
@file:OptIn(ExperimentalSerializationApi::class)
package moe.fuqiuluo.qqinterface.servlet.transfile
import android.os.Bundle
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.api.IProtoReqManager
import com.tencent.mobileqq.transfile.dns.IpData
import com.tencent.mobileqq.transfile.protohandler.RichProto
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
import kotlinx.coroutines.suspendCancellableCoroutine
@ -19,18 +23,35 @@ import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import mqq.app.MobileQQ
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
import protobuf.oidb.cmd0xfc2.Oidb0xfc2RspBody
import mqq.app.MobileQQ
import tencent.im.cs.cmd0x346.cmd0x346
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
import tencent.im.oidb.cmd0xe37.cmd0xe37
import tencent.im.oidb.oidb_sso
import java.util.ArrayList
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() {
/*@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 {
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray(
Oidb0xfc2ReqBody(
@ -142,19 +163,35 @@ internal object RichProtoSvc: BaseSvc() {
}
fun getGroupPicDownUrl(
originalUrl: String,
md5: 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(
originalUrl: String,
md5: 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 {
return "https://gchat.qpic.cn/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
fun getGuildPicDownUrl(
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(
@ -321,12 +358,4 @@ internal object RichProtoSvc: BaseSvc() {
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.fileName,
when(image.chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(fileMd5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(fileMd5)
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl("", fileMd5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl("", fileMd5)
else -> error("Not supported chat type: ${image.chatType}, convertMsgElementsToMsgSegment::Pic")
}
), echo = echo)

View File

@ -103,7 +103,7 @@ internal object UploadGroupFile : IActionHandler() {
// 根据文件大小调整超时时间
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 contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
suspendCancellableCoroutine<FileTransNotifyInfo?> {

View File

@ -1,12 +1,42 @@
@file:Suppress("UNUSED_VARIABLE", "LocalVariableName")
package moe.fuqiuluo.shamrock.xposed.hooks
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 java.lang.reflect.Modifier
@XposedHook(priority = -1)
@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 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
.getRuntimeService(IHttpEngineService::class.java, "all")
httpEngineService.javaClass.hookMethod("sendReq").before {
@ -19,7 +49,17 @@ internal class HookForDebug: IAction {
LogCenter.log("请求地址: ${req.mReqUrl}")
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())
}
}*/
}
}