From 88beaf8b6fa551d3460d92d21ed9e2fd825e43a5 Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Fri, 22 Dec 2023 14:19:56 +0800 Subject: [PATCH] =?UTF-8?q?`Shamrock`:=20=E6=94=AF=E6=8C=81=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0QQ=E6=94=B6=E8=97=8F=EF=BC=88=E5=9B=BE=E7=89=87?= =?UTF-8?q?=EF=BC=89=20`/fav/add=5Fimage=5Fmsg`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/cpp/shamrock.cpp | 20 ++ .../filemanager/api/IFileManagerUtil.java | 7 + .../fuqiuluo/qqinterface/servlet/QFavSvc.kt | 192 +++++++++++++++++- .../shamrock/remote/action/ActionManager.kt | 2 +- .../remote/action/handlers/FavAddImageMsg.kt | 120 +++++++++++ .../action/handlers/FavAddRichMediaMsg.kt | 2 +- .../shamrock/remote/api/FavouriteWeiyun.kt | 12 +- .../moe/fuqiuluo/shamrock/utils/CryptTools.kt | 19 ++ .../fuqiuluo/shamrock/utils/DeflateTools.kt | 18 ++ .../java/moe/fuqiuluo/shamrock/utils/MD5.kt | 2 + .../shamrock/xposed/actions/HookForDebug.kt | 19 +- 11 files changed, 384 insertions(+), 29 deletions(-) create mode 100644 qqinterface/src/main/java/com/tencent/mobileqq/filemanager/api/IFileManagerUtil.java create mode 100644 xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddImageMsg.kt create mode 100644 xposed/src/main/java/moe/fuqiuluo/shamrock/utils/CryptTools.kt diff --git a/app/src/main/cpp/shamrock.cpp b/app/src/main/cpp/shamrock.cpp index 120f8c4..dd1f11d 100644 --- a/app/src/main/cpp/shamrock.cpp +++ b/app/src/main/cpp/shamrock.cpp @@ -37,6 +37,26 @@ Java_moe_fuqiuluo_shamrock_utils_MD5_genFileMd5Hex(JNIEnv *env, jobject thiz, js return env->NewStringUTF(md5Hex.c_str()); } +extern "C" +JNIEXPORT jbyteArray JNICALL +Java_moe_fuqiuluo_shamrock_utils_MD5_genFileMd5(JNIEnv *env, jobject thiz, jstring file_path) { + auto cPathStr = env->GetStringUTFChars(file_path, nullptr); + std::filesystem::path filePath(cPathStr); + if (!std::filesystem::exists(filePath)) { + jclass exClass = env->FindClass("java/io/FileNotFoundException"); + env->ThrowNew(exClass, "目标文件不存在"); + env->DeleteLocalRef(exClass); + return nullptr; + } + auto file = std::ifstream(filePath.c_str(), std::ios::binary); + MD5 md5; + md5.update(file); + auto md5Bytes = md5.digest(); + auto jByteArray = env->NewByteArray(16); + env->SetByteArrayRegion(jByteArray, 0, 16, reinterpret_cast(md5Bytes)); + return jByteArray; +} + extern "C" JNIEXPORT jstring JNICALL Java_moe_fuqiuluo_shamrock_utils_MD5_getMd5Hex(JNIEnv *env, jobject thiz, jbyteArray bytes) { diff --git a/qqinterface/src/main/java/com/tencent/mobileqq/filemanager/api/IFileManagerUtil.java b/qqinterface/src/main/java/com/tencent/mobileqq/filemanager/api/IFileManagerUtil.java new file mode 100644 index 0000000..f9d63c6 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/mobileqq/filemanager/api/IFileManagerUtil.java @@ -0,0 +1,7 @@ +package com.tencent.mobileqq.filemanager.api; + +import com.tencent.mobileqq.qroute.QRouteApi; + +public interface IFileManagerUtil extends QRouteApi { + byte[] getSHA(String str); +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/QFavSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/QFavSvc.kt index 5456c70..fed2c83 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/QFavSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/QFavSvc.kt @@ -1,5 +1,6 @@ package moe.fuqiuluo.qqinterface.servlet +import android.graphics.BitmapFactory import com.tencent.mobileqq.app.QQAppInterface import com.tencent.mobileqq.transfile.HttpNetReq import com.tencent.mobileqq.transfile.INetEngineListener @@ -8,18 +9,23 @@ import com.tencent.mobileqq.transfile.NetResp import com.tencent.mobileqq.transfile.ServerAddr import com.tencent.mobileqq.transfile.api.IHttpEngineService import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.io.core.BytePacketBuilder +import kotlinx.io.core.readBytes +import kotlinx.io.core.writeFully import moe.fuqiuluo.proto.protobufMapOf import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter -import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY -import moe.fuqiuluo.shamrock.tools.toInnerValuesString +import moe.fuqiuluo.shamrock.tools.hex2ByteArray +import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.utils.DeflateTools +import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import mqq.manager.TicketManager import oicq.wlogin_sdk.request.Ticket import oicq.wlogin_sdk.request.WtTicketPromise import oicq.wlogin_sdk.tools.ErrMsg import java.io.ByteArrayOutputStream +import java.io.File import java.nio.ByteBuffer import kotlin.coroutines.resume @@ -28,12 +34,16 @@ import kotlin.coroutines.resume * QQ收藏相关接口 */ internal object QFavSvc: BaseSvc() { - private val SERVER_LIST = listOf(ServerAddr().also { + private val SERVER_LIST_COLLECTOR = listOf(ServerAddr().also { it.isIpv6 = false it.mIp = "collector.weiyun.com" it.port = 80 }) - private const val VT = 27 + private val SERVER_LIST_PICUP = listOf(ServerAddr().also { + it.isIpv6 = false + it.mIp = "pic.pieceup.qq.com" + it.port = 80 + }) private const val VERSION = 12820 private const val APPID = 30244 private const val SUB_APPID = 538116905 @@ -41,6 +51,108 @@ internal object QFavSvc: BaseSvc() { private const val MINOR_VERSION = 9 private var seq = 1 + suspend fun addImageMsg( + uin: Long, + name: String, + groupId: Long = 0, + groupName: String = "", + picUrl: String, + pid: String, + width: Int, height: Int, + size: Long, + md5: String, + ): Result { + val md5Bytes = md5.hex2ByteArray() + val data = protobufMapOf { + it[1] = mapOf( + 20009 to mapOf( + 1 to mapOf( + 1 to 1, // bid + 2 to 1, // category + 3 to mapOf( // author + 1 to if (groupId == 0L) 1 else 2, // type + 2 to uin, // num_id + 3 to name, // str_id + 4 to groupId, // group_id + 5 to groupName // group_name + ), + 4 to System.currentTimeMillis() - 2000, // create_time + 5 to System.currentTimeMillis() - 1000, // sequence + 7 to """{"recordAudioOnly":false,"audioOnly":false,"fileOnly":false}""", + 9 to 0, // original_app_id + 10 to 0 // custom_group_id + ), + 2 to mapOf( + 1 to "", + 3 to "[图片]", + 4 to mapOf( + 1 to picUrl, + 2 to md5Bytes, + 3 to md5, + 6 to width, + 7 to height, + 8 to size, + 9 to 0, + 11 to pid + ), + 5 to 1 + ), + 3 to mapOf( + 2 to """""", + 4 to mapOf( + 1 to picUrl, + 2 to md5Bytes, + 3 to md5, + 6 to width, + 7 to height, + 8 to size, + 9 to 0, + 11 to pid + ) + ) + ) + ) + }.toByteArray() + return sendWeiyunReq(20009, data) + } + + suspend fun applyUpImageMsg( + uin: Long, + name: String, + groupId: Long = 0, + groupName: String = "", + width: Int, height: Int, + image: File + ): Result { + if (!image.exists()) { + return Result.failure(IllegalArgumentException("image file not exists")) + } + val md5 = MD5.genFileMd5(image.absolutePath) + val data = protobufMapOf { + it[1] = mapOf( + 20010 to mapOf( + 1 to mapOf( + 2 to md5, + 4 to md5.toHexString(), + 10 to mapOf( // author + 1 to if (groupId == 0L) 1 else 2, // type + 2 to uin, // num_id + 3 to name, // str_id + 4 to groupId, // group_id + 5 to groupName // group_name + ), + 6 to width, // width + 7 to height, + 8 to image.length(), + 9 to 1, // type + 11 to "/storage/emulated/0/DCIM/ShamrockUpload.jpeg" // pic_id + ) + ) + ) + }.toByteArray() + return sendWeiyunReq(20010, data) + } + suspend fun addRichMediaMsg( uin: Long, name: String, @@ -111,7 +223,15 @@ internal object QFavSvc: BaseSvc() { return sendWeiyunReq(20009, data) } - suspend fun sendWeiyunReq(cmd: Int, body: ByteArray): Result { + suspend fun sendPicUpBlock( + fileSize: Long, + offset: Long, + block: ByteArray, + blockSize: Long, + sha: ByteArray, + pid: String, + outputStream: ByteArrayOutputStream = ByteArrayOutputStream(), + ): Result { return suspendCancellableCoroutine { val httpNetReq = HttpNetReq() httpNetReq.userData = null @@ -120,19 +240,73 @@ internal object QFavSvc: BaseSvc() { if (netResp.mHttpCode != 200 && netResp.mResult != 0 && netResp.mErrDesc.isNullOrEmpty()) { netResp.mErrDesc = netResp.mRespProperties["User-ErrMsg"] } + netResp.mRespData = outputStream.toByteArray().copyOf() + it.resume(Result.success(netResp)) + } + + override fun onUpdateProgeress(netReq: NetReq, curr: Long, final: Long) {} + } + val vi = (app.getManager(QQAppInterface.TICKET_MANAGER) as TicketManager).getA2(app.currentAccountUin) + //LogCenter.log(pSKey) + httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST + httpNetReq.mSendData = BytePacketBuilder().apply { + writeInt(-1412589450) + writeInt(10000) + writeInt(0) + writeInt(sha.size + 16 + blockSize.toInt()) + writeShort(0) + writeShort(sha.size.toShort()) + writeFully(sha) + writeInt(fileSize.toInt()) + writeInt(offset.toInt()) + writeInt(blockSize.toInt()) + writeFully(block) + }.build().readBytes() + httpNetReq.mOutStream = outputStream + httpNetReq.mStartDownOffset = 0L + httpNetReq.mReqProperties["Shamrock"] = "true" + httpNetReq.mReqProperties["Cookie"] = String.format("uin=%s;vt=%d;vi=%s;pid=%s;appid=%d", app.currentAccountUin, 8, vi, pid, APPID) + httpNetReq.mReqProperties["host"] = "pic.pieceup.qq.com" + httpNetReq.mReqProperties["Range"] = "bytes=0-" + httpNetReq.mReqProperties["Content-Length"] = httpNetReq.mSendData.size.toString() + httpNetReq.mReqProperties["Accept-Encoding"] = "gzip" + httpNetReq.mReqProperties["Content-Encoding"] = "gzip" + httpNetReq.mPrioty = 1 + httpNetReq.mReqUrl = "https://pic.pieceup.qq.com/" + httpNetReq.mServerList = SERVER_LIST_PICUP + val service = AppRuntimeFetcher.appRuntime + .getRuntimeService(IHttpEngineService::class.java, "qqfav") + service.sendReq(httpNetReq) + } + } + + suspend fun sendWeiyunReq( + cmd: Int, + body: ByteArray, + outputStream: ByteArrayOutputStream = ByteArrayOutputStream(), + ): Result { + return suspendCancellableCoroutine { + val httpNetReq = HttpNetReq() + httpNetReq.userData = null + httpNetReq.mCallback = object: INetEngineListener { + override fun onResp(netResp: NetResp) { + if (netResp.mHttpCode != 200 && netResp.mResult != 0 && netResp.mErrDesc.isNullOrEmpty()) { + netResp.mErrDesc = netResp.mRespProperties["User-ErrMsg"] + } + netResp.mRespData = outputStream.toByteArray().copyOf() it.resume(Result.success(netResp)) } override fun onUpdateProgeress(netReq: NetReq, curr: Long, final: Long) {} } val pSKey = getWeiYunPSKey() - LogCenter.log(pSKey) + //LogCenter.log(pSKey) httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST httpNetReq.mSendData = DeflateTools.gzip(packData(packHead(cmd, pSKey), body)) - httpNetReq.mOutStream = ByteArrayOutputStream() + httpNetReq.mOutStream = outputStream httpNetReq.mStartDownOffset = 0L httpNetReq.mReqProperties["Shamrock"] = "true" - httpNetReq.mReqProperties["Cookie"] = String.format("uin=%s;vt=%d;vi=%s;appid=%d", app.currentAccountUin, VT, pSKey, APPID) + httpNetReq.mReqProperties["Cookie"] = String.format("uin=%s;vt=%d;vi=%s;appid=%d", app.currentAccountUin, 27, pSKey, APPID) httpNetReq.mReqProperties["host"] = "collector.weiyun.com" httpNetReq.mReqProperties["Range"] = "bytes=0-" httpNetReq.mReqProperties["Content-Length"] = httpNetReq.mSendData.size.toString() @@ -140,7 +314,7 @@ internal object QFavSvc: BaseSvc() { httpNetReq.mReqProperties["Content-Encoding"] = "gzip" httpNetReq.mPrioty = 1 httpNetReq.mReqUrl = "https://collector.weiyun.com/collector.fcg" - httpNetReq.mServerList = SERVER_LIST + httpNetReq.mServerList = SERVER_LIST_COLLECTOR val service = AppRuntimeFetcher.appRuntime .getRuntimeService(IHttpEngineService::class.java, "qqfav") service.sendReq(httpNetReq) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt index f44a513..15e6766 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/ActionManager.kt @@ -52,7 +52,7 @@ internal object ActionManager { GetWeatherCityCode, GetWeather, // FAV - FavAddRichMediaMsg, + FavAddRichMediaMsg, FavAddImageMsg, // OTHER GetDeviceBattery, DownloadFile diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddImageMsg.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddImageMsg.kt new file mode 100644 index 0000000..6b1cf23 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddImageMsg.kt @@ -0,0 +1,120 @@ +package moe.fuqiuluo.shamrock.remote.action.handlers + +import android.graphics.BitmapFactory +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.discardExact +import kotlinx.serialization.json.JsonElement +import moe.fuqiuluo.proto.ProtoUtils +import moe.fuqiuluo.proto.asUtf8String +import moe.fuqiuluo.qqinterface.servlet.QFavSvc +import moe.fuqiuluo.shamrock.remote.action.ActionSession +import moe.fuqiuluo.shamrock.remote.action.IActionHandler +import moe.fuqiuluo.shamrock.tools.EmptyJsonString +import moe.fuqiuluo.shamrock.utils.CryptTools +import moe.fuqiuluo.shamrock.utils.DeflateTools +import moe.fuqiuluo.shamrock.utils.FileUtils + +internal object FavAddImageMsg: IActionHandler() { + override suspend fun internalHandle(session: ActionSession): String { + val uin = session.getLong("user_id") + val nickName = session.getString("nick") + val groupName = session.getStringOrNull("group_name") ?: "" + val groupId = session.getLongOrNull("group_id") ?: 0L + val file = session.getString("file") + return invoke(uin, nickName, file, groupName, groupId, session.echo) + } + + suspend operator fun invoke( + uin: Long, + nickName: String, + fileText: String, + groupName: String = "", + groupId: Long = 0L, + echo: JsonElement = EmptyJsonString + ): String { + val image = fileText.let { + val md5 = it.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase() + if (md5.length == 32) { + FileUtils.getFile(it) + } else { + FileUtils.parseAndSave(it) + } + } + val options = BitmapFactory.Options() + BitmapFactory.decodeFile(image.absolutePath, options) + QFavSvc.applyUpImageMsg(uin, nickName, + image = image, + groupName = groupName, + groupId = groupId, + width = options.outWidth, + height = options.outHeight + ).onSuccess { + return if (it.mHttpCode == 200 && it.mResult == 0) { + val readPacket = ByteReadPacket(DeflateTools.ungzip(it.mRespData)) + readPacket.discardExact(6) + val allLength = readPacket.readInt() + val dataLength = readPacket.readInt() + val headLength = allLength - dataLength - 16 + //LogCenter.log("上传图片请求成功: ${DeflateTools.ungzip(it.mRespData).toHexString()}") + //LogCenter.log("图片上传响应: allLength=$allLength, dataLength=$dataLength, headLength=$headLength") + readPacket.discardExact(2) + ByteArray(headLength).also { + readPacket.readFully(it, 0, it.size) + } + val data = ByteArray(dataLength).also { + readPacket.readFully(it, 0, it.size) + } + val pb = ProtoUtils.decodeFromByteArray(data) + val resp = pb[2, 20010, 1, 2] + val picUrl = resp[1].asUtf8String + val picId = resp[11].asUtf8String + val md5 = resp[4].asUtf8String + + val sha = CryptTools + .getSHA1("/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQ_Collection/pic/" + md5.uppercase() + "_0") + + image.inputStream().use { + var offset = 0L + val block = ByteArray(131072) + var rest = image.length() + do { + val length = if (rest <= 131072) rest else 131072L + if(it.read(block, 0, length.toInt()) != -1) { + QFavSvc.sendPicUpBlock( + fileSize = image.length(), + offset = offset, + block = block, + blockSize = length, + pid = picId, + sha = sha + ).onFailure { + return error(it.message ?: it.toString(), echo) + } + offset += length + rest -= length + } else { + rest = -1 + } + } while (rest > 0) + } + + QFavSvc.addImageMsg( + uin, nickName, groupId, groupName, picUrl, picId, options.outWidth, options.outHeight, image.length(), md5.uppercase() + ).onFailure { + return error(it.message ?: it.toString(), echo) + } + + ok(picUrl, echo) + } else { + logic(it.mErrDesc, echo) + } + }.onFailure { + return error(it.message ?: it.toString(), echo) + } + return ok("请求已提交", echo) + } + + override fun path(): String = "fav.add_image_msg" + + override val requiredParams: Array = arrayOf("user_id", "nick", "file") +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddRichMediaMsg.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddRichMediaMsg.kt index a927e7e..216ab79 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddRichMediaMsg.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/FavAddRichMediaMsg.kt @@ -10,7 +10,7 @@ internal object FavAddRichMediaMsg: IActionHandler() { override suspend fun internalHandle(session: ActionSession): String { val uin = session.getLong("user_id") val nickName = session.getString("nick") - val groupName = session.getStringOrNull("groupName") ?: "" + val groupName = session.getStringOrNull("group_name") ?: "" val groupId = session.getLongOrNull("group_id") ?: 0L val time = session.getLongOrNull("time") ?: System.currentTimeMillis() val content = session.getString("content") diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/FavouriteWeiyun.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/FavouriteWeiyun.kt index a1b8c93..087a0bc 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/FavouriteWeiyun.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/FavouriteWeiyun.kt @@ -4,6 +4,7 @@ import io.ktor.http.ContentType import io.ktor.server.application.call import io.ktor.server.response.respondText import io.ktor.server.routing.Routing +import moe.fuqiuluo.shamrock.remote.action.handlers.FavAddImageMsg import moe.fuqiuluo.shamrock.remote.action.handlers.FavAddRichMediaMsg import moe.fuqiuluo.shamrock.remote.action.handlers.GetFriendList import moe.fuqiuluo.shamrock.remote.action.handlers.GetFriendSystemMsg @@ -21,8 +22,17 @@ fun Routing.fav() { val nickName = call.fetchOrThrow("nick") val time = call.fetchOrNull("time")?.toLong() ?: System.currentTimeMillis() val content = call.fetchOrThrow("content") - val groupName = call.fetchOrNull("groupName") ?: "" + val groupName = call.fetchOrNull("group_name") ?: "" val groupId = call.fetchOrNull("group_id")?.toLong() ?: 0L call.respondText(FavAddRichMediaMsg(uin, nickName, time, content, groupName, groupId), ContentType.Application.Json) } + + getOrPost("/fav/add_image_msg") { + val uin = call.fetchOrThrow("user_id").toLong() + val nickName = call.fetchOrThrow("nick") + val file = call.fetchOrThrow("file") + val groupName = call.fetchOrNull("groupName") ?: "" + val groupId = call.fetchOrNull("group_id")?.toLong() ?: 0L + call.respondText(FavAddImageMsg(uin, nickName, file, groupName, groupId), ContentType.Application.Json) + } } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/CryptTools.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/CryptTools.kt new file mode 100644 index 0000000..4af37f5 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/CryptTools.kt @@ -0,0 +1,19 @@ +package moe.fuqiuluo.shamrock.utils + +object CryptTools { + + fun getSHA1(string: String): ByteArray { + return getSHA1(string.toByteArray()) + } + + fun getSHA1(bytes: ByteArray): ByteArray { + return getDigest(bytes, "SHA-1") + } + + private fun getDigest(bytes: ByteArray, algorithm: String): ByteArray { + val digest = java.security.MessageDigest.getInstance(algorithm) + digest.update(bytes) + return digest.digest() + } + +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DeflateTools.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DeflateTools.kt index 23d55be..733d06b 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DeflateTools.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DeflateTools.kt @@ -4,9 +4,11 @@ import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.IOException import java.util.zip.Deflater +import java.util.zip.GZIPInputStream import java.util.zip.GZIPOutputStream import java.util.zip.Inflater + object DeflateTools { fun uncompress(inputByte: ByteArray?): ByteArray { var len: Int @@ -79,4 +81,20 @@ object DeflateTools { input.close() } } + + fun ungzip(bytes: ByteArray): ByteArray { + val out = ByteArrayOutputStream() + val `in` = ByteArrayInputStream(bytes) + try { + val ungzip = GZIPInputStream(`in`) + val buffer = ByteArray(256) + var n: Int + while (ungzip.read(buffer).also { n = it } >= 0) { + out.write(buffer, 0, n) + } + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + return out.toByteArray() + } } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/MD5.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/MD5.kt index 0e9cdbe..f633863 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/MD5.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/MD5.kt @@ -4,4 +4,6 @@ object MD5 { external fun getMd5Hex(bytes: ByteArray): String external fun genFileMd5Hex(filePath: String): String + + external fun genFileMd5(filePath: String): ByteArray } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/HookForDebug.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/HookForDebug.kt index a729b85..2758a04 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/HookForDebug.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/xposed/actions/HookForDebug.kt @@ -16,17 +16,6 @@ import mqq.app.MobileQQ internal class HookForDebug: IAction { override fun invoke(ctx: Context) { - // MessageHelper.hookSendMessageOldChannel() - val oldHttpEngineProcessor = QRoute.api(IOldHttpEngineProcessor::class.java) - oldHttpEngineProcessor.javaClass.hookMethod("sendReq").before { - if (it.args[0] is HttpNetReq) { - LogCenter.log("已记录一个IOldHttpEngineProcessor请求") - val req = it.args[0] as HttpNetReq - if (req.mReqUrl.startsWith("https://")) { - req.mReqUrl = req.mReqUrl.replace("https://", "http://") - } - } - } val httpEngineService = AppRuntimeFetcher.appRuntime .getRuntimeService(IHttpEngineService::class.java, "all") httpEngineService.javaClass.hookMethod("sendReq").before { @@ -36,12 +25,8 @@ internal class HookForDebug: IAction { return@before } LogCenter.log("已记录一个IHttpEngineService请求") - if (req.mReqUrl == null || req.mReqUrl.isBlank()) { - val host = req.mReqProperties["host"] ?: "collector.weiyun.com" - req.mReqUrl = "http://$host" - } else if (req.mReqUrl.startsWith("https://")) { - req.mReqUrl = req.mReqUrl.replace("https://", "http://") - } + LogCenter.log("请求地址: ${req.mReqUrl}") + LogCenter.log("请求: ${req.toInnerValuesString(NetReq::class.java)}") } }