diff --git a/README.md b/README.md index f3b46fc..102e937 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ QwQ 是基于 LSPosed 实现的类似于QAuxiliary的通用性QQ增强LSPosed模 > 如有违反法律,请联系删除。 > 请勿在任何平台宣传,宣扬,转发本项目,请勿恶意修改企业安装包造成相关企业产生损失,如有违背,必将追责到底。 > +> 本项目在多处拥有唯一的特征,如若今后在任何闭源模块发现,将会给予警告及公示! +> 本项目代码仅提供学习与交流,不提供私有化,闭源分发! +> > [Discord社区](https://discord.gg/MKR2wz863h) ## 兼容说明 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 94ba236..417f49a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -186,6 +186,7 @@ fun getVersionName(): String { } dependencies { + implementation("androidx.exifinterface:exifinterface:1.3.7") compileOnly("de.robv.android.xposed:api:82") compileOnly(project(":qqinterface")) // oicq common interface implementation(project(":processor")) // pre build diff --git a/app/src/main/assets/random_face.gif b/app/src/main/assets/random_face.gif new file mode 100644 index 0000000..072efee Binary files /dev/null and b/app/src/main/assets/random_face.gif differ diff --git a/app/src/main/java/moe/fuqiuluo/xposed/loader/LuoClassloader.kt b/app/src/main/java/moe/fuqiuluo/xposed/loader/LuoClassloader.kt index 367738d..375c505 100644 --- a/app/src/main/java/moe/fuqiuluo/xposed/loader/LuoClassloader.kt +++ b/app/src/main/java/moe/fuqiuluo/xposed/loader/LuoClassloader.kt @@ -4,6 +4,8 @@ object LuoClassloader: ClassLoader() { lateinit var hostClassLoader: ClassLoader lateinit var ctxClassLoader: ClassLoader + internal val moduleLoader: ClassLoader = LuoClassloader::class.java.classLoader!! + fun load(name: String): Class<*>? { return kotlin.runCatching { loadClass(name) diff --git a/app/src/main/java/moe/qwq/miko/internals/entries/EcTextElem.kt b/app/src/main/java/moe/qwq/miko/internals/entries/EcTextElem.kt new file mode 100644 index 0000000..8503c8c --- /dev/null +++ b/app/src/main/java/moe/qwq/miko/internals/entries/EcTextElem.kt @@ -0,0 +1,73 @@ +package moe.qwq.miko.internals.entries + +import com.tencent.qqnt.kernel.nativeinterface.LinkInfo +import com.tencent.qqnt.kernel.nativeinterface.MsgConstant +import com.tencent.qqnt.kernel.nativeinterface.MsgElement +import com.tencent.qqnt.kernel.nativeinterface.TextElement +import kotlinx.serialization.Serializable + +@Serializable +class EcTextElem( + var atChannelId: Long? = null, + var atNtUid: String? = null, + var atRoleColor: Int? = null, + var atRoleId: Long? = null, + var atRoleName: String? = null, + var atTinyId: Long = 0, + var atType: Int = 0, + var atUid: Long = 0, + var content: String? = null, + var linkInfo: EcLinkInfo? = null, + var needNotify: Int? = null, + var subElementType: Int? = null +) { + companion object { + fun from(text: TextElement): EcTextElem { + return EcTextElem( + atChannelId = text.atChannelId, + atNtUid = text.atNtUid, + atRoleColor = text.atRoleColor, + atRoleId = text.atRoleId, + atRoleName = text.atRoleName, + atTinyId = text.atTinyId, + atType = text.atType, + atUid = text.atUid, + content = text.content, + linkInfo = text.linkInfo?.let { EcLinkInfo(it.icon, it.tencentDocType, it.title) }, + needNotify = text.needNotify, + subElementType = text.subElementType + ) + } + } + + fun toTextElement(): TextElement { + return TextElement().apply { + atChannelId = this@EcTextElem.atChannelId + atNtUid = this@EcTextElem.atNtUid + atRoleColor = this@EcTextElem.atRoleColor + atRoleId = this@EcTextElem.atRoleId + atRoleName = this@EcTextElem.atRoleName + atTinyId = this@EcTextElem.atTinyId + atType = this@EcTextElem.atType + atUid = this@EcTextElem.atUid + content = this@EcTextElem.content + linkInfo = this@EcTextElem.linkInfo?.let { LinkInfo(it.title, it.icon, it.tencentDocType) } + needNotify = this@EcTextElem.needNotify + subElementType = this@EcTextElem.subElementType + } + } + + fun toMsgElement(): MsgElement { + return MsgElement().apply { + elementType = MsgConstant.KELEMTYPETEXT + textElement = toTextElement() + } + } +} + +@Serializable +class EcLinkInfo( + var icon: String? = null, + var tencentDocType: Int? = null, + var title: String? = null, +) \ No newline at end of file diff --git a/app/src/main/java/moe/qwq/miko/internals/helper/MessageCrypt.kt b/app/src/main/java/moe/qwq/miko/internals/helper/MessageCrypt.kt new file mode 100644 index 0000000..d023595 --- /dev/null +++ b/app/src/main/java/moe/qwq/miko/internals/helper/MessageCrypt.kt @@ -0,0 +1,176 @@ +@file:OptIn(ExperimentalSerializationApi::class) + +package moe.qwq.miko.internals.helper + +import android.graphics.BitmapFactory +import com.tencent.qqnt.kernel.nativeinterface.MsgConstant +import com.tencent.qqnt.kernel.nativeinterface.MsgElement +import com.tencent.qqnt.kernel.nativeinterface.PicElement +import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil +import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo +import kotlinx.io.core.BytePacketBuilder +import kotlinx.io.core.readBytes +import kotlinx.io.core.use +import kotlinx.io.core.writeFully +import kotlinx.io.streams.inputStream +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf +import moe.fuqiuluo.xposed.loader.LuoClassloader +import moe.qwq.miko.internals.entries.EcTextElem +import moe.qwq.miko.internals.setting.QwQSetting +import moe.qwq.miko.tools.FileUtils +import java.security.MessageDigest +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import androidx.exifinterface.media.ExifInterface +import de.robv.android.xposed.XposedBridge +import kotlinx.io.core.ByteReadPacket +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.decodeFromByteArray + +object MessageCrypt { + private val IV_AES by lazy { "5201314fuqiuluo!".toByteArray() } + private val randomPicDir by lazy { + QwQSetting.dataDir.resolve("randomFaces").also { + if (it.isFile) it.delete() + if (!it.exists()) { + it.mkdirs() + val randomFaceFile = it.resolve("base.gif") + randomFaceFile.outputStream().use { + LuoClassloader.moduleLoader.getResourceAsStream("assets/random_face.gif").use { origin -> + origin.copyTo(it) + } + } + } + } + } + private val tmpPicDir by lazy { + QwQSetting.dataDir.resolve("tmp").also { + if (it.isFile) it.delete() + if (!it.exists()) it.mkdirs() + } + } + + fun decrypt(data: ByteArray, keyStr: String): Result> { + val aesKey = md5(keyStr) + val decrypt = aesDecrypt(data, aesKey) + val reader = ByteReadPacket(decrypt) + val result = ArrayList() + repeat(reader.readInt()) { + when (val elementType = reader.readInt()) { + MsgConstant.KELEMTYPETEXT -> { + val text = ProtoBuf.decodeFromByteArray(reader.readBytes(reader.readInt())) + result.add(text.toMsgElement()) + } + else -> return Result.failure(RuntimeException("Unsupported msg type: $elementType")) + } + } + return Result.success(result) + } + + fun encrypt(msgs: ArrayList, uin: String, keyStr: String): Result { + // 加上uin作为Hash判断,防止别人转发加密后的消息后被解密解密 + // 这种转发的加密消息不该被解密! + val keyHash = (keyStr + uin).hashCode() + val msgBuilder = BytePacketBuilder() + msgBuilder.writeInt(msgs.size) + msgs.forEach { msg -> + msgBuilder.writeInt(msg.elementType) + when (msg.elementType) { + MsgConstant.KELEMTYPETEXT -> { + val encrypt = ProtoBuf.encodeToByteArray(EcTextElem.from(msg.textElement)) + msgBuilder.writeInt(encrypt.size) + msgBuilder.writeFully(encrypt) + } + + // TODO support more msg + else -> return Result.failure(RuntimeException("Unsupported msg type: ${msg.elementType}")) + } + } + + val data = msgBuilder.build().readBytes() + val aesKey = md5(keyStr) + + val builder = BytePacketBuilder() + val encrypt = aesEncrypt(data, aesKey) + builder.writeFully(encrypt) + builder.writeInt(encrypt.size) + builder.writeInt(keyHash) + builder.writeInt(0x114514) + + val tmpFile = tmpPicDir.resolve("${System.currentTimeMillis()}.tmp") + tmpFile.outputStream().use { out -> + (randomPicDir + .listFiles { f -> f.isFile } + ?.random() ?: return Result.failure(RuntimeException("No random face found"))).inputStream().use { + it.copyTo(out) + } + builder.build().inputStream().use { it.copyTo(out) } + } + + val elem = MsgElement() + elem.elementType = MsgConstant.KELEMTYPEPIC + val pic = PicElement() + pic.md5HexStr = QQNTWrapperUtil.CppProxy.genFileMd5Hex(tmpFile.absolutePath) + + val msgService = NTServiceFetcher.kernelService.msgService!! + val originalPath = msgService.getRichMediaFilePathForMobileQQSend(RichMediaFilePathInfo(2, 0, pic.md5HexStr, tmpFile.name, 1, 0, null, "", true)) + + //XposedBridge.log("${pic.md5HexStr} encrypt: $originalPath") + + if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(originalPath) != tmpFile.length()) { + val thumbPath = msgService.getRichMediaFilePathForMobileQQSend(RichMediaFilePathInfo(2, 0, pic.md5HexStr, tmpFile.name, 2, 720, null, "", true)) + QQNTWrapperUtil.CppProxy.copyFile(tmpFile.absolutePath, originalPath) + QQNTWrapperUtil.CppProxy.copyFile(tmpFile.absolutePath, thumbPath) + } + + val options = BitmapFactory.Options() + options.inJustDecodeBounds = true + BitmapFactory.decodeFile(originalPath, options) + val exifInterface = ExifInterface(originalPath!!) + val orientation = exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_UNDEFINED + ) + if (orientation != ExifInterface.ORIENTATION_ROTATE_90 && orientation != ExifInterface.ORIENTATION_ROTATE_270) { + pic.picWidth = options.outWidth + pic.picHeight = options.outHeight + } else { + pic.picWidth = options.outHeight + pic.picHeight = options.outWidth + } + pic.sourcePath = originalPath + pic.fileSize = QQNTWrapperUtil.CppProxy.getFileSize(originalPath) + pic.original = true + pic.picType = FileUtils.getPicType(tmpFile) + pic.picSubType = 0 + pic.isFlashPic = false + + elem.picElement = pic + tmpFile.delete() + return Result.success(elem) + } + + private fun md5(str: String): ByteArray { + val md = MessageDigest.getInstance("MD5") + val bytes = md.digest(str.toByteArray()) + return bytes + } + + private fun aesEncrypt(data: ByteArray, key: ByteArray): ByteArray { + val mAlgorithmParameterSpec = IvParameterSpec(IV_AES) + val mSecretKeySpec = SecretKeySpec(key, "AES") + val mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec, mAlgorithmParameterSpec) + return mCipher.doFinal(data) + } + + private fun aesDecrypt(data: ByteArray, key: ByteArray): ByteArray { + val mAlgorithmParameterSpec = IvParameterSpec(IV_AES) + val mSecretKeySpec = SecretKeySpec(key, "AES") + val mCipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, mAlgorithmParameterSpec) + return mCipher.doFinal(data) + } +} \ No newline at end of file diff --git a/app/src/main/java/moe/qwq/miko/internals/helper/NTServiceFetcher.kt b/app/src/main/java/moe/qwq/miko/internals/helper/NTServiceFetcher.kt index 00ce0d5..06508c5 100644 --- a/app/src/main/java/moe/qwq/miko/internals/helper/NTServiceFetcher.kt +++ b/app/src/main/java/moe/qwq/miko/internals/helper/NTServiceFetcher.kt @@ -5,6 +5,7 @@ package moe.qwq.miko.internals.helper import com.google.protobuf.UnknownFieldSet import com.tencent.qqnt.kernel.api.IKernelService import com.tencent.qqnt.kernel.api.impl.MsgService +import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import de.robv.android.xposed.XposedBridge import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromByteArray @@ -12,10 +13,12 @@ import kotlinx.serialization.protobuf.ProtoBuf import moe.fuqiuluo.entries.MessagePush import moe.qwq.miko.ext.hookMethod import moe.qwq.miko.internals.AioListener +import moe.qwq.miko.internals.hooks.MessageHook internal object NTServiceFetcher { private lateinit var iKernelService: IKernelService private var curKernelHash = 0 + private var isMsgListenerHookLoaded = false fun onFetch(service: IKernelService) { val msgService = service.msgService ?: return @@ -33,6 +36,23 @@ internal object NTServiceFetcher { private fun initNTKernel(msgService: MsgService) { XposedBridge.log("[QwQ] Init NT Kernel.") + + msgService.javaClass.hookMethod("addMsgListener").before { + val listener = it.args[0] + if (isMsgListenerHookLoaded) return@before + listener.javaClass.hookMethod("onRecvMsg").before { + val msgs = it.args[0] as ArrayList + msgs.forEach { msg -> + MessageHook.tryHandleMessageDecrypt(msg) + } + } + + listener.javaClass.hookMethod("onAddSendMsg").before { + val record = it.args[0] as MsgRecord + MessageHook.tryHandleMessageDecrypt(record) + } + } + kernelService.wrapperSession.javaClass.hookMethod("onMsfPush").before { runCatching { val cmd = it.args[0] as String diff --git a/app/src/main/java/moe/qwq/miko/internals/hooks/MessageHook.kt b/app/src/main/java/moe/qwq/miko/internals/hooks/MessageHook.kt index 13a2f26..d08f485 100644 --- a/app/src/main/java/moe/qwq/miko/internals/hooks/MessageHook.kt +++ b/app/src/main/java/moe/qwq/miko/internals/hooks/MessageHook.kt @@ -5,6 +5,7 @@ import com.tencent.mobileqq.qroute.QRoute import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgRecord +import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo import com.tencent.qqnt.kernel.nativeinterface.TextElement import com.tencent.qqnt.msg.api.IMsgService import de.robv.android.xposed.XC_MethodHook @@ -12,10 +13,49 @@ import de.robv.android.xposed.XposedBridge import moe.fuqiuluo.processor.HookAction import moe.qwq.miko.actions.ActionProcess import moe.qwq.miko.actions.IAction +import moe.qwq.miko.internals.helper.MessageCrypt +import moe.qwq.miko.internals.helper.NTServiceFetcher +import moe.qwq.miko.internals.helper.msgService import moe.qwq.miko.internals.setting.QwQSetting +import moe.qwq.miko.tools.PlatformTools +import mqq.app.MobileQQ +import java.io.File +import java.io.RandomAccessFile @HookAction("发送消息预劫持") class MessageHook: IAction { + companion object { + fun tryHandleMessageDecrypt(record: MsgRecord) { + val encrypt by QwQSetting.getSetting(QwQSetting.MESSAGE_ENCRYPT) + if (encrypt.isBlank()) return + if (record.elements.size != 1 || record.elements.first().elementType != MsgConstant.KELEMTYPEPIC) return + + val pic = record.elements.first().picElement + val msgService = NTServiceFetcher.kernelService.msgService!! + val originalPath = msgService.getRichMediaFilePathForMobileQQSend( + RichMediaFilePathInfo(2, 0, pic.md5HexStr, "", 1, 0, null, "", true) + ) ?: return + val originalFile = RandomAccessFile(originalPath, "r") + val length = originalFile.length() + originalFile.seek(length - 12) + val dataSize = originalFile.readInt() + val hash = originalFile.readInt() + val magic = originalFile.readInt() + if (magic == 0x114514 && hash == (encrypt + record.senderUin).hashCode()) { + val data = ByteArray(dataSize) + originalFile.seek(length - 12 - dataSize) + originalFile.read(data) + originalFile.close() + MessageCrypt.decrypt(data, encrypt).onSuccess { + record.elements.clear() + record.elements.addAll(it) + }.onFailure { + XposedBridge.log("消息解密失败: ${it.stackTraceToString()}") + } + } + } + } + private fun handleMessageBody(msgs: ArrayList) { if (msgs.isActionMsg()) return val tail by QwQSetting.getSetting(name) @@ -37,7 +77,12 @@ class MessageHook: IAction { } private fun handleMessageEncrypt(msgs: ArrayList, encryptKey: String) { - + MessageCrypt.encrypt(msgs, PlatformTools.app.currentAccountUin, encryptKey).onFailure { + XposedBridge.log("[QwQ] 消息加密失败: ${it.stackTraceToString()}") + }.onSuccess { + msgs.clear() + msgs.add(it) + } } override fun onRun(ctx: Context) { diff --git a/app/src/main/java/moe/qwq/miko/internals/setting/QwQSetting.kt b/app/src/main/java/moe/qwq/miko/internals/setting/QwQSetting.kt index d0091f4..c0f2209 100644 --- a/app/src/main/java/moe/qwq/miko/internals/setting/QwQSetting.kt +++ b/app/src/main/java/moe/qwq/miko/internals/setting/QwQSetting.kt @@ -30,6 +30,7 @@ object QwQSetting { .parentFile!!.resolve("Tencent/QwQ").also { it.mkdirs() } + private val config: MMKV get() = MMKVTools.mmkvWithId("qwq") val settingMap = hashMapOf>( INTERCEPT_RECALL to Setting(INTERCEPT_RECALL, SettingType.BOOLEAN), diff --git a/app/src/main/java/moe/qwq/miko/tools/FileUtils.kt b/app/src/main/java/moe/qwq/miko/tools/FileUtils.kt new file mode 100644 index 0000000..e640df9 --- /dev/null +++ b/app/src/main/java/moe/qwq/miko/tools/FileUtils.kt @@ -0,0 +1,38 @@ +package moe.qwq.miko.tools + +import java.io.File + +object FileUtils { + private val PicIdMap = hashMapOf( + "jpg" to 1000, + "bmp" to 1005, + "gif" to 2000, + "png" to 1001, + "webp" to 1002, + "sharpp" to 1004, + "apng" to 2001, + ) + + fun getFileType(file: File): String { + val bytes = ByteArray(2) + file.inputStream().use { + it.read(bytes) + } + return when ("${bytes[0].toUByte()}${bytes[1].toUByte()}".toInt()) { + 6677 -> "bmp" + 7173 -> "gif" + 7784 -> "midi" + 7790 -> "exe" + 8075 -> "zip" + 8273 -> "webp" + 8297 -> "rar" + 13780 -> "png" + 255216 -> "jpg" + else -> "jpg" + } + } + + fun getPicType(file: File): Int { + return PicIdMap[getFileType(file)] ?: 1000 + } +} \ No newline at end of file diff --git a/app/src/main/java/moe/qwq/miko/tools/PlatformTools.kt b/app/src/main/java/moe/qwq/miko/tools/PlatformTools.kt index fbbc1d5..026a190 100644 --- a/app/src/main/java/moe/qwq/miko/tools/PlatformTools.kt +++ b/app/src/main/java/moe/qwq/miko/tools/PlatformTools.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.pm.PackageInfo import android.os.Process import android.provider.Settings +import com.tencent.common.app.AppInterface import moe.fuqiuluo.maple.Maple import moe.qwq.miko.tools.PlatformTools.QQ_9_0_70_VER import moe.qwq.miko.tools.PlatformTools.getQQVersionCode @@ -17,6 +18,13 @@ val maple by lazy { } internal object PlatformTools { + val app by lazy { + (if (isMqqPackage()) + MobileQQ.getMobileQQ().waitAppRuntime() + else + MobileQQ.getMobileQQ().waitAppRuntime(null)) as AppInterface + } + const val QQ_9_0_8_VER = 5540 const val QQ_9_0_65_VER = 6566 const val QQ_9_0_70_VER = 6700 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 6d85859..9da85e1 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 @@ -2,6 +2,7 @@ import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener; +import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,15 +15,13 @@ public void addMsgListener(IKernelMsgListener listener) { //public void addLocalJsonGrayTipMsg(@NotNull Contact contact, @NotNull JsonGrayElement json, boolean needStore, boolean needRecentContact, @Nullable IAddJsonGrayTipMsgCallback iAddJsonGrayTipMsgCallback) { //} - -/* public String getRichMediaFilePathForGuild(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) { - return null; - } - @Nullable public String getRichMediaFilePathForMobileQQSend(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) { return null; } +/* public String getRichMediaFilePathForGuild(@NotNull RichMediaFilePathInfo richMediaFilePathInfo) { + return null; + } public void prepareTempChat(TempChatPrepareInfo tempChatPrepareInfo, IOperateCallback cb) { diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/MsgRecord.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/MsgRecord.java index cf248a1..558adc4 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/MsgRecord.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/MsgRecord.java @@ -1,68 +1,431 @@ package com.tencent.qqnt.kernel.nativeinterface; import java.util.ArrayList; +import java.util.HashMap; public class MsgRecord { - //public AnonymousExtInfo anonymousExtInfo; - public int atType; - public int avatarFlag; - public String avatarMeta; - public String avatarPendant; - public int categoryManage; - public String channelId; - public String channelName; - public int chatType; - //public GuildClientIdentity clientIdentityInfo; - public long clientSeq; - public long cntSeq; - public long commentCnt; - public int directMsgFlag; - //public ArrayList directMsgMembers; - public boolean editable; - public ArrayList elements; - //public ArrayList emojiLikesList; - public byte[] extInfoForUI; - public String feedId; - public Integer fileGroupSize; - //public FoldingInfo foldingInfo; - //public FreqLimitInfo freqLimitInfo; - public long fromAppid; - //public FromRoleInfo fromChannelRoleInfo; - //public FromRoleInfo fromGuildRoleInfo; - public long fromUid; - public byte[] generalFlags; - public long guildCode; - public String guildId; - public String guildName; - public boolean isImportMsg; - public boolean isOnlineMsg; - //public FromRoleInfo levelRoleInfo; - //public HashMap msgAttrs; - public byte[] msgEventInfo; - public long msgId; - public byte[] msgMeta; - public long msgRandom; - public long msgSeq; - public long msgTime; - public int msgType; - //public MultiTransInfo multiTransInfo; - public int nameType; - public String peerName; - public String peerUid; - public long peerUin; - //public GuildMedal personalMedal; - public long recallTime; - public ArrayList records; - public long roleId; - public int roleType; - public String sendMemberName; - public String sendNickName; - public String sendRemarkName; - public int sendStatus; - public int sendType; - public String senderUid; - public long senderUin; - public int sourceType; - public int subMsgType; - public long timeStamp; + //AnonymousExtInfo anonymousExtInfo; + int atType; + int avatarFlag; + String avatarMeta; + String avatarPendant; + int categoryManage; + String channelId; + String channelName; + int chatType; + //GProClientIdentity clientIdentityInfo; + long clientSeq; + long cntSeq; + long commentCnt; + int directMsgFlag; + //ArrayList directMsgMembers; + boolean editable; + ArrayList elements; + //ArrayList emojiLikesList; + byte[] extInfoForUI; + String feedId; + Integer fileGroupSize; + //FoldingInfo foldingInfo; + //FreqLimitInfo freqLimitInfo; + long fromAppid; + //FromRoleInfo fromChannelRoleInfo; + //FromRoleInfo fromGuildRoleInfo; + long fromUid; + byte[] generalFlags; + long guildCode; + String guildId; + String guildName; + boolean isImportMsg; + boolean isOnlineMsg; + //FromRoleInfo levelRoleInfo; + //HashMap msgAttrs; + byte[] msgEventInfo; + long msgId; + byte[] msgMeta; + long msgRandom; + long msgSeq; + long msgTime; + int msgType; + //MultiTransInfo multiTransInfo; + int nameType; + String peerName; + String peerUid; + long peerUin; + //GProMedal personalMedal; + long recallTime; + ArrayList records; + long roleId; + int roleType; + String sendMemberName; + String sendNickName; + String sendRemarkName; + int sendStatus; + int sendType; + String senderUid; + long senderUin; + int subMsgType; + long timeStamp; + +/* public MsgRecord() { + this.senderUid = ""; + this.peerUid = ""; + this.channelId = ""; + this.guildId = ""; + this.msgMeta = new byte[0]; + this.sendRemarkName = ""; + this.sendMemberName = ""; + this.sendNickName = ""; + this.guildName = ""; + this.channelName = ""; + this.elements = new ArrayList<>(); + this.records = new ArrayList<>(); + this.emojiLikesList = new ArrayList<>(); + this.directMsgMembers = new ArrayList<>(); + this.peerName = ""; + this.avatarMeta = ""; + this.avatarPendant = ""; + this.feedId = ""; + this.fromChannelRoleInfo = new FromRoleInfo(); + this.fromGuildRoleInfo = new FromRoleInfo(); + this.levelRoleInfo = new FromRoleInfo(); + this.generalFlags = new byte[0]; + this.msgAttrs = new HashMap<>(); + } + + public AnonymousExtInfo getAnonymousExtInfo() { + return this.anonymousExtInfo; + }*/ + + public int getAtType() { + return this.atType; + } + + public int getAvatarFlag() { + return this.avatarFlag; + } + + public String getAvatarMeta() { + return this.avatarMeta; + } + + public String getAvatarPendant() { + return this.avatarPendant; + } + + public int getCategoryManage() { + return this.categoryManage; + } + + public String getChannelId() { + return this.channelId; + } + + public String getChannelName() { + return this.channelName; + } + + public int getChatType() { + return this.chatType; + } + +/* public GProClientIdentity getClientIdentityInfo() { + return this.clientIdentityInfo; + }*/ + + public long getClientSeq() { + return this.clientSeq; + } + + public long getCntSeq() { + return this.cntSeq; + } + + public long getCommentCnt() { + return this.commentCnt; + } + + public int getDirectMsgFlag() { + return this.directMsgFlag; + } + +/* public ArrayList getDirectMsgMembers() { + return this.directMsgMembers; + }*/ + + public boolean getEditable() { + return this.editable; + } + + public ArrayList getElements() { + return this.elements; + } + +/* public ArrayList getEmojiLikesList() { + return this.emojiLikesList; + }*/ + + public byte[] getExtInfoForUI() { + return this.extInfoForUI; + } + + public String getFeedId() { + return this.feedId; + } + + public Integer getFileGroupSize() { + return this.fileGroupSize; + } +/* + public FoldingInfo getFoldingInfo() { + return this.foldingInfo; + } + + public FreqLimitInfo getFreqLimitInfo() { + return this.freqLimitInfo; + }*/ + + public long getFromAppid() { + return this.fromAppid; + } + +/* public FromRoleInfo getFromChannelRoleInfo() { + return this.fromChannelRoleInfo; + } + + public FromRoleInfo getFromGuildRoleInfo() { + return this.fromGuildRoleInfo; + }*/ + + public long getFromUid() { + return this.fromUid; + } + + public byte[] getGeneralFlags() { + return this.generalFlags; + } + + public long getGuildCode() { + return this.guildCode; + } + + public String getGuildId() { + return this.guildId; + } + + public String getGuildName() { + return this.guildName; + } + + public boolean getIsImportMsg() { + return this.isImportMsg; + } + + public boolean getIsOnlineMsg() { + return this.isOnlineMsg; + } + +/* public FromRoleInfo getLevelRoleInfo() { + return this.levelRoleInfo; + } + + public HashMap getMsgAttrs() { + return this.msgAttrs; + }*/ + + public byte[] getMsgEventInfo() { + return this.msgEventInfo; + } + + public long getMsgId() { + return this.msgId; + } + + public byte[] getMsgMeta() { + return this.msgMeta; + } + + public long getMsgRandom() { + return this.msgRandom; + } + + public long getMsgSeq() { + return this.msgSeq; + } + + public long getMsgTime() { + return this.msgTime; + } + + public int getMsgType() { + return this.msgType; + } +/* + + public MultiTransInfo getMultiTransInfo() { + return this.multiTransInfo; + } +*/ + + public int getNameType() { + return this.nameType; + } + + public String getPeerName() { + return this.peerName; + } + + public String getPeerUid() { + return this.peerUid; + } + + public long getPeerUin() { + return this.peerUin; + } + +/* public GProMedal getPersonalMedal() { + return this.personalMedal; + }*/ + + public long getRecallTime() { + return this.recallTime; + } + + public ArrayList getRecords() { + return this.records; + } + + public long getRoleId() { + return this.roleId; + } + + public int getRoleType() { + return this.roleType; + } + + public String getSendMemberName() { + return this.sendMemberName; + } + + public String getSendNickName() { + return this.sendNickName; + } + + public String getSendRemarkName() { + return this.sendRemarkName; + } + + public int getSendStatus() { + return this.sendStatus; + } + + public int getSendType() { + return this.sendType; + } + + public String getSenderUid() { + return this.senderUid; + } + + public long getSenderUin() { + return this.senderUin; + } + + public int getSubMsgType() { + return this.subMsgType; + } + + public long getTimeStamp() { + return this.timeStamp; + } + +/* public String toString() { + return "MsgRecord{msgId=" + this.msgId + ",msgRandom=" + this.msgRandom + ",msgSeq=" + this.msgSeq + ",cntSeq=" + this.cntSeq + ",chatType=" + this.chatType + ",msgType=" + this.msgType + ",subMsgType=" + this.subMsgType + ",sendType=" + this.sendType + ",senderUid=" + this.senderUid + ",peerUid=" + this.peerUid + ",channelId=" + this.channelId + ",guildId=" + this.guildId + ",guildCode=" + this.guildCode + ",fromUid=" + this.fromUid + ",fromAppid=" + this.fromAppid + ",msgTime=" + this.msgTime + ",msgMeta=" + this.msgMeta + ",sendStatus=" + this.sendStatus + ",sendRemarkName=" + this.sendRemarkName + ",sendMemberName=" + this.sendMemberName + ",sendNickName=" + this.sendNickName + ",guildName=" + this.guildName + ",channelName=" + this.channelName + ",elements=" + this.elements + ",records=" + this.records + ",emojiLikesList=" + this.emojiLikesList + ",commentCnt=" + this.commentCnt + ",directMsgFlag=" + this.directMsgFlag + ",directMsgMembers=" + this.directMsgMembers + ",peerName=" + this.peerName + ",freqLimitInfo=" + this.freqLimitInfo + ",editable=" + this.editable + ",avatarMeta=" + this.avatarMeta + ",avatarPendant=" + this.avatarPendant + ",feedId=" + this.feedId + ",roleId=" + this.roleId + ",timeStamp=" + this.timeStamp + ",clientIdentityInfo=" + this.clientIdentityInfo + ",isImportMsg=" + this.isImportMsg + ",atType=" + this.atType + ",roleType=" + this.roleType + ",fromChannelRoleInfo=" + this.fromChannelRoleInfo + ",fromGuildRoleInfo=" + this.fromGuildRoleInfo + ",levelRoleInfo=" + this.levelRoleInfo + ",recallTime=" + this.recallTime + ",isOnlineMsg=" + this.isOnlineMsg + ",generalFlags=" + this.generalFlags + ",clientSeq=" + this.clientSeq + ",fileGroupSize=" + this.fileGroupSize + ",foldingInfo=" + this.foldingInfo + ",multiTransInfo=" + this.multiTransInfo + ",senderUin=" + this.senderUin + ",peerUin=" + this.peerUin + ",msgAttrs=" + this.msgAttrs + ",anonymousExtInfo=" + this.anonymousExtInfo + ",nameType=" + this.nameType + ",avatarFlag=" + this.avatarFlag + ",extInfoForUI=" + this.extInfoForUI + ",personalMedal=" + this.personalMedal + ",categoryManage=" + this.categoryManage + ",msgEventInfo=" + this.msgEventInfo + ",}"; + } + + public MsgRecord(long j2, long j3, long j4, long j5, int i2, int i3, int i4, int i5, String str, String str2, String str3, String str4, long j6, long j7, long j8, long j9, byte[] bArr, int i6, String str5, String str6, String str7, String str8, String str9, ArrayList arrayList, ArrayList arrayList2, ArrayList arrayList3, long j10, int i7, ArrayList arrayList4, String str10, FreqLimitInfo freqLimitInfo, boolean z, String str11, String str12, String str13, long j11, long j12, GProClientIdentity gProClientIdentity, boolean z2, int i8, int i9, FromRoleInfo fromRoleInfo, FromRoleInfo fromRoleInfo2, FromRoleInfo fromRoleInfo3, long j13, boolean z3, byte[] bArr2, long j14, Integer num, FoldingInfo foldingInfo, MultiTransInfo multiTransInfo, long j15, long j16, HashMap hashMap, AnonymousExtInfo anonymousExtInfo, int i10, int i11, byte[] bArr3, GProMedal gProMedal, int i12, byte[] bArr4) { + this.senderUid = ""; + this.peerUid = ""; + this.channelId = ""; + this.guildId = ""; + this.msgMeta = new byte[0]; + this.sendRemarkName = ""; + this.sendMemberName = ""; + this.sendNickName = ""; + this.guildName = ""; + this.channelName = ""; + this.elements = new ArrayList<>(); + this.records = new ArrayList<>(); + this.emojiLikesList = new ArrayList<>(); + this.directMsgMembers = new ArrayList<>(); + this.peerName = ""; + this.avatarMeta = ""; + this.avatarPendant = ""; + this.feedId = ""; + this.fromChannelRoleInfo = new FromRoleInfo(); + this.fromGuildRoleInfo = new FromRoleInfo(); + this.levelRoleInfo = new FromRoleInfo(); + this.generalFlags = new byte[0]; + this.msgAttrs = new HashMap<>(); + this.msgId = j2; + this.msgRandom = j3; + this.msgSeq = j4; + this.cntSeq = j5; + this.chatType = i2; + this.msgType = i3; + this.subMsgType = i4; + this.sendType = i5; + this.senderUid = str; + this.peerUid = str2; + this.channelId = str3; + this.guildId = str4; + this.guildCode = j6; + this.fromUid = j7; + this.fromAppid = j8; + this.msgTime = j9; + this.msgMeta = bArr; + this.sendStatus = i6; + this.sendRemarkName = str5; + this.sendMemberName = str6; + this.sendNickName = str7; + this.guildName = str8; + this.channelName = str9; + this.elements = arrayList; + this.records = arrayList2; + this.emojiLikesList = arrayList3; + this.commentCnt = j10; + this.directMsgFlag = i7; + this.directMsgMembers = arrayList4; + this.peerName = str10; + this.freqLimitInfo = freqLimitInfo; + this.editable = z; + this.avatarMeta = str11; + this.avatarPendant = str12; + this.feedId = str13; + this.roleId = j11; + this.timeStamp = j12; + this.clientIdentityInfo = gProClientIdentity; + this.isImportMsg = z2; + this.atType = i8; + this.roleType = i9; + this.fromChannelRoleInfo = fromRoleInfo; + this.fromGuildRoleInfo = fromRoleInfo2; + this.levelRoleInfo = fromRoleInfo3; + this.recallTime = j13; + this.isOnlineMsg = z3; + this.generalFlags = bArr2; + this.clientSeq = j14; + this.fileGroupSize = num; + this.foldingInfo = foldingInfo; + this.multiTransInfo = multiTransInfo; + this.senderUin = j15; + this.peerUin = j16; + this.msgAttrs = hashMap; + this.anonymousExtInfo = anonymousExtInfo; + this.nameType = i10; + this.avatarFlag = i11; + this.extInfoForUI = bArr3; + this.personalMedal = gProMedal; + this.categoryManage = i12; + this.msgEventInfo = bArr4; + }*/ } diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/QQNTWrapperUtil.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/QQNTWrapperUtil.java new file mode 100644 index 0000000..f4880d1 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/QQNTWrapperUtil.java @@ -0,0 +1,56 @@ +package com.tencent.qqnt.kernel.nativeinterface; + +import java.util.ArrayList; +import java.util.HashMap; + +public interface QQNTWrapperUtil { + final class CppProxy implements QQNTWrapperUtil { + //public static native ArrayList DecoderRecentInfo(byte[] bArr); + + //public static native ThumbResult calcThumbSize(int i2, int i3, ThumbOpt thumbOpt); + + public static native boolean copyFile(String str, String str2); + + //public static native UnregisterRes decodeOffLine(byte[] bArr); + + public static native void emptyWorkingSet(int i2); + + //public static native byte[] encodeOffLine(UnregisterInfo unregisterInfo); + + public static native boolean fileIsExist(String str); + + public static native String fullWordToHalfWord(String str); + + public static native byte[] genFileMd5Buf(String str); + + public static native String genFileMd5Hex(String str); + + public static native HashMap genFileShaAndMd5Hex(String str, int i2); + + public static native byte[] genFileShaBuf(String str); + + public static native String genFileShaHex(String str); + + public static native long getFileSize(String str); + + public static native String getNTUserDataInfoConfig(); + + //public static native OidbRspInfo getOidbRspInfo(byte[] bArr); + + public static native ArrayList> getPinyin(String str, boolean z); + + public static native String getSoBuildInfo(); + + public static native byte[] getSsoBufferOfOidbReq(int i2, int i3, byte[] bArr); + + public static native String getSsoCmdOfOidbReq(int i2, int i3); + + public static native boolean makeDirByPath(String str); + + public static native boolean matchInPinyin(ArrayList> arrayList, String str); + + private native void nativeDestroy(long j2); + + public static native int runProcess(String str, boolean z); + } +} diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/RichMediaFilePathInfo.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/RichMediaFilePathInfo.java new file mode 100644 index 0000000..7e66de7 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/RichMediaFilePathInfo.java @@ -0,0 +1,70 @@ +package com.tencent.qqnt.kernel.nativeinterface; + +public final class RichMediaFilePathInfo { + int downloadType; + int elementSubType; + int elementType; + String fileName; + String fileUuid; + byte[] importRichMediaContext; + String md5HexStr; + boolean needCreate; + int thumbSize; + + public RichMediaFilePathInfo() { + this.md5HexStr = ""; + this.fileName = ""; + this.fileUuid = ""; + } + + public int getDownloadType() { + return this.downloadType; + } + + public int getElementSubType() { + return this.elementSubType; + } + + public int getElementType() { + return this.elementType; + } + + public String getFileName() { + return this.fileName; + } + + public String getFileUuid() { + return this.fileUuid; + } + + public byte[] getImportRichMediaContext() { + return this.importRichMediaContext; + } + + public String getMd5HexStr() { + return this.md5HexStr; + } + + public boolean getNeedCreate() { + return this.needCreate; + } + + public int getThumbSize() { + return this.thumbSize; + } + + public RichMediaFilePathInfo(int elementType, int elementSubType, String md5Hex, String fileName, int downloadType, int thumbSiz, byte[] importRichMediaContext, String uuid, boolean needCreate) { + this.md5HexStr = ""; + this.fileName = ""; + this.fileUuid = ""; + this.elementType = elementType; + this.elementSubType = elementSubType; + this.md5HexStr = md5Hex; + this.fileName = fileName; + this.downloadType = downloadType; + this.thumbSize = thumbSiz; + this.importRichMediaContext = importRichMediaContext; + this.fileUuid = uuid; + this.needCreate = needCreate; + } +} \ No newline at end of file