diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/PacketId.java b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/PacketId.java index c0d6ef6a9..72e669d94 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/PacketId.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/PacketId.java @@ -1,8 +1,15 @@ package net.mamoe.mirai.network.packet; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + /** * @author Him188moe @ Mirai Project */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) public @interface PacketId { /** * 获取用于识别的包 ID diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientHeartbeatPacket.java b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientHeartbeatPacket.java index a9c1c5427..6575a22d4 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientHeartbeatPacket.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientHeartbeatPacket.java @@ -1,10 +1,13 @@ package net.mamoe.mirai.network.packet.client; +import net.mamoe.mirai.network.packet.PacketId; + import java.io.IOException; /** * @author Him188moe @ Mirai Project */ +@PacketId(0x0058) public class ClientHeartbeatPacket extends ClientPacket { @Override public void encode() throws IOException { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientLoginPacket.java b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientLoginPacket.java index c15fbfb6b..5a5a2fd00 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientLoginPacket.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientLoginPacket.java @@ -5,7 +5,7 @@ import net.mamoe.mirai.network.packet.PacketId; /** * @author Him188moe @ Mirai Project */ -@PacketId(5) +@PacketId(0x08_25_31_01)// TODO: 2019/8/8 public class ClientLoginPacket extends ClientPacket { @Override public void encode() { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.java b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.java index 9300439dd..dbe38579c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.java @@ -1,6 +1,8 @@ package net.mamoe.mirai.network.packet.client; +import lombok.Getter; import net.mamoe.mirai.network.packet.Packet; +import net.mamoe.mirai.network.packet.PacketId; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -15,14 +17,20 @@ public abstract class ClientPacket extends DataOutputStream implements Packet { super(new ByteArrayOutputStream()); } - private final byte packageId; + @Getter + private final int packageId; { - packageId = 0x0058; - } + var annotation = this.getClass().getAnnotation(PacketId.class); + packageId = annotation.value(); - protected void writeHead() throws IOException { - this.writeByte(0x02); + try { + writeHead(); + writeVersion(); + writePacketId(); + } catch (IOException e) { + throw new RuntimeException(e); + } } public static void main(String[] args) throws IOException { @@ -36,12 +44,41 @@ public abstract class ClientPacket extends DataOutputStream implements Packet { System.out.println(Arrays.toString(((ByteArrayOutputStream) pk.out).toByteArray())); } + protected void writeHead() throws IOException { + this.writeByte(0x02); + } + protected void writeVersion() throws IOException { this.writeByte(0x37_13); } - protected void writePacketId() { + protected void writePacketId() throws IOException { + this.writeByte(this.packageId); + } + protected void writeFixVer() throws IOException { + this.writeByte(0x03); + this.writeByte(0x00); + this.writeByte(0x00); + this.writeByte(0x00); + this.writeByte(0x01); + this.writeByte(0x2E); + this.writeByte(0x01); + this.writeByte(0x00); + this.writeByte(0x00); + this.writeByte(0x68); + this.writeByte(0x52); + this.writeByte(0x00); + this.writeByte(0x00); + this.writeByte(0x00); + this.writeByte(0x00); + } + + protected void write0825Key() throws IOException { + this.writeLong(0xA4_F1_91_88); + this.writeLong(0xC9_82_14_99); + this.writeLong(0x0C_9E_56_55); + this.writeLong(0x91_23_C8_3D); } protected void writeQQ(long qq) throws IOException { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/util/TEAEncryption.java b/mirai-core/src/main/java/net/mamoe/mirai/util/TEAEncryption.java new file mode 100644 index 000000000..b89a14575 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/util/TEAEncryption.java @@ -0,0 +1,10 @@ +package net.mamoe.mirai.util; + +/** + * TEA encryption + * + * @author Him188moe @ Mirai Project + */ +public final class TEAEncryption { + +} diff --git a/mirai-native/pom.xml b/mirai-native/pom.xml new file mode 100644 index 000000000..d708da28a --- /dev/null +++ b/mirai-native/pom.xml @@ -0,0 +1,14 @@ + + + + mirai + net.mamoe + 1.0 + + 4.0.0 + + mirai-native + 1.0 + diff --git a/mirai-native/src/main/c/TeaEncryption.c b/mirai-native/src/main/c/TeaEncryption.c new file mode 100644 index 000000000..84c2f9767 --- /dev/null +++ b/mirai-native/src/main/c/TeaEncryption.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include + +//#define CRYPT_ONE_BYTE + +typedef char int8 ; +typedef unsigned char uint8 ; +typedef short int16 ; +typedef unsigned short uint16 ; +typedef long int32 ; +typedef unsigned long uint32 ; + +typedef struct tagTEACTX +{ + uint8 buf[8] ; + uint8 bufPre[8] ; + const uint8 *pKey ; //指向16字节的key + uint8 *pCrypt ; + uint8 *pCryptPre ; +} TEACTX, *LPTEACTX ; + +uint16 Host2NetShort(uint16 usHost) +{ + const uint16 us = 0x1234 ; + return ((uint8 *)&us)[0] == 0x12 ? usHost : ((usHost>>8) | (usHost<<8)) ; +} + +uint16 Net2HostShort(uint16 usNet) +{ + return Host2NetShort(usNet) ; +} + +uint32 Host2NetLong(uint32 ulHost) +{ + const uint16 us = 0x1234 ; + return ((uint8 *)&us)[0] == 0x12 ? ulHost : (((ulHost>>8) & 0xFF00) | + ((ulHost<<8) & 0xFF0000) | (ulHost<<24) | (ulHost>>24)) ; +} + +uint32 Net2HostLong(uint32 ulHost) +{ + return Host2NetLong(ulHost) ; +} + +//TEA加密。v明文8字节。k密钥16字节。w密文输出8字节。 +void EnCipher(const uint32 *const v, const uint32 *const k, uint32 *const w) +{ + register uint32 + y = Host2NetLong(v[0]), + z = Host2NetLong(v[1]), + a = Host2NetLong(k[0]), + b = Host2NetLong(k[1]), + c = Host2NetLong(k[2]), + d = Host2NetLong(k[3]), + n = 0x10, /* do encrypt 16 (0x10) times */ + sum = 0, + delta = 0x9E3779B9; /* 0x9E3779B9 - 0x100000000 = -0x61C88647 */ + + while (n-- > 0) + { + sum += delta; + y += ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); + z += ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); + } + + w[0] = Net2HostLong(y); + w[1] = Net2HostLong(z); +} + +//TEA解密。v密文8字节。k密钥16字节。w明文输出8字节。 +void DeCipher(const uint32 *const v, const uint32 *const k, uint32 *const w) +{ + register uint32 + y = Host2NetLong(v[0]), + z = Host2NetLong(v[1]), + a = Host2NetLong(k[0]), + b = Host2NetLong(k[1]), + c = Host2NetLong(k[2]), + d = Host2NetLong(k[3]), + n = 0x10, + sum = 0xE3779B90, + /* why this ? must be related with n value*/ + delta = 0x9E3779B9; + + /* sum = delta<<5, in general sum = delta * n */ + while (n-- > 0) + { + z -= ((y << 4) + c) ^ (y + sum) ^ ((y >> 5) + d); + y -= ((z << 4) + a) ^ (z + sum) ^ ((z >> 5) + b); + sum -= delta; + } + + w[0] = Net2HostLong(y); + w[1] = Net2HostLong(z); +} + +uint32 Random(void) +{ + return (uint32)rand(); + //return 0xdead ; +} + +//每次8字节加密 +static void EncryptEach8Bytes(TEACTX *pCtx) +{ +#ifdef CRYPT_ONE_BYTE + uint32 i ; + uint8 *pPlain8, *pPlainPre8, *pCrypt8, *pCryptPre8 ; + pPlain8 = (uint8 *)pCtx->buf ; + pPlainPre8 = (uint8 *)pCtx->bufPre ; + pCrypt8 = (uint8 *)pCtx->pCrypt ; + pCryptPre8 = (uint8 *)pCtx->pCryptPre ; + //本轮明文与上一轮的密文异或 + for(i=0; i<8; i++) + pPlain8[i] ^= pCryptPre8[i] ; + //再对异或后的明文加密 + EnCipher((uint32 *)pPlain8, (uint32 *)pCtx->pKey, (uint32 *)pCrypt8) ; + //将加密后的密文与上一轮的明文(其实是上一轮明文与上上轮密文异或结果)异或 + for(i=0; i<8; i++) + pCrypt8[i] ^= pPlainPre8[i] ; + // + for(i=0; i<8; i++) + pPlainPre8[i] = pPlain8[i] ; +#else + uint32 *pPlain8, *pPlainPre8, *pCrypt8, *pCryptPre8 ; + pPlain8 = (uint32 *)pCtx->buf ; + pPlainPre8 = (uint32 *)pCtx->bufPre ; + pCrypt8 = (uint32 *)pCtx->pCrypt ; + pCryptPre8 = (uint32 *)pCtx->pCryptPre ; + pPlain8[0] ^= pCryptPre8[0] ; + pPlain8[1] ^= pCryptPre8[1] ; + EnCipher(pPlain8, (const uint32 *)pCtx->pKey, pCrypt8) ; + pCrypt8[0] ^= pPlainPre8[0] ; + pCrypt8[1] ^= pPlainPre8[1] ; + pPlainPre8[0] = pPlain8[0] ; + pPlainPre8[1] = pPlain8[1] ; +#endif + pCtx->pCryptPre = pCtx->pCrypt ; + pCtx->pCrypt += 8 ; +} + +//加密。pPlain指向待加密的明文。ulPlainLen明文长度。pKey密钥16字节。 +//pOut指向密文输出缓冲区。pOutLen输入输出参数,指示输出缓冲区长度、密文长度。 +uint32 Encrypt(TEACTX *pCtx, const uint8 *pPlain, uint32 ulPlainLen, + const uint8 *pKey, uint8 *pOut, uint32 *pOutLen) +{ + uint32 ulPos, ulPadding, ulOut ; + const uint8 *p ; + if(pPlain == NULL || ulPlainLen == 0 || pOutLen == NULL) + return 0 ; + //计算需要填充的字节数 + //整个加密流程下来,不管明文长度多少,填充10个字节是固定的, + //然后再根据明文的长度计算还需要填充的字节数。 + ulPos = (8 - ((ulPlainLen + 10) & 0x07)) & 0x07 ; + //计算加密后的长度 + ulOut = 1 + ulPos + 2 + ulPlainLen + 7 ; + if(*pOutLen < ulOut) + { + *pOutLen = ulOut ; + return 0 ; + } + *pOutLen = ulOut ; + memset(pCtx, 0, sizeof(TEACTX)) ; + pCtx->pCrypt = pOut ; + pCtx->pCryptPre = pCtx->bufPre ; + pCtx->pKey = pKey ; + //buf[0]的最低3bit位等于所填充的长度 + pCtx->buf[0] = (uint8)((Random() & 0xF8) | ulPos) ; + //用随机数填充上面计算得到的填充长度(每个字节填充的内容是一样的)。 + //这里填充的起始位置是&buf[1]。 + memset(pCtx->buf+1, (uint8)Random(), ulPos++) ; + //至少再填充两字节 + for(ulPadding=0; ulPadding<2; ulPadding++) + { + if(ulPos == 8) + { + EncryptEach8Bytes(pCtx) ; + ulPos = 0 ; + } + pCtx->buf[ulPos++] = (uint8)Random() ; + } + p = pPlain ; + while(ulPlainLen > 0) + { + if(ulPos == 8) + { + EncryptEach8Bytes(pCtx) ; + ulPos = 0 ; + } + pCtx->buf[ulPos++] = *(p++) ; + ulPlainLen-- ; + } + //末尾再添加7字节0后加密,在解密过程的时候可以用来判断key是否正确。 + for(ulPadding=0; ulPadding<7; ulPadding++) + pCtx->buf[ulPos++] = 0x00 ; + // + EncryptEach8Bytes(pCtx) ; + return ulOut ; +} + +//每次8字节进行解密 +static void DecryptEach8Bytes(TEACTX *pCtx) +{ +#ifdef CRYPT_ONE_BYTE + uint32 i ; + uint8 bufTemp[8] ; + uint8 *pBuf8, *pBufPre8, *pCrypt8, *pCryptPre8 ; + pBuf8 = (uint8 *)pCtx->buf ; + pBufPre8 = (uint8 *)pCtx->bufPre ; + pCrypt8 = (uint8 *)pCtx->pCrypt ; + pCryptPre8 = (uint8 *)pCtx->pCryptPre ; + //当前的密文与前一轮明文(实际是前一轮明文与前前轮密文异或结果)异或 + for(i=0; i<8; i++) + bufTemp[i] = pCrypt8[i] ^ pBufPre8[i] ; + //异或后的结果再解密(解密后得到当前名文与前一轮密文异或的结果,并非真正明文) + DeCipher((uint32 *)bufTemp, (uint32 *)pCtx->pKey, (uint32 *)pBufPre8) ; + //解密后的结果与前一轮的密文异或,得到真正的明文 + for(i=0; i<8; i++) + pBuf8[i] = pBufPre8[i] ^ pCryptPre8[i] ; +#else + uint32 bufTemp[2] ; + uint32 *pBuf8, *pBufPre8, *pCrypt8, *pCryptPre8 ; + pBuf8 = (uint32 *)pCtx->buf ; + pBufPre8 = (uint32 *)pCtx->bufPre ; + pCrypt8 = (uint32 *)pCtx->pCrypt ; + pCryptPre8 = (uint32 *)pCtx->pCryptPre ; + bufTemp[0] = pCrypt8[0] ^ pBufPre8[0] ; + bufTemp[1] = pCrypt8[1] ^ pBufPre8[1] ; + DeCipher(bufTemp, (const uint32 *)pCtx->pKey, pBufPre8) ; + pBuf8[0] = pBufPre8[0] ^ pCryptPre8[0] ; + pBuf8[1] = pBufPre8[1] ^ pCryptPre8[1] ; +#endif + pCtx->pCryptPre = pCtx->pCrypt ; + pCtx->pCrypt += 8 ; +} + +//解密。pCipher指向待解密密文。ulCipherLen密文长度。pKey密钥16字节。 +//pOut指向明文输出缓冲区。pOutLen输入输出参数,指示输出缓冲区长度、明文长度。 +uint32 Decrypt(TEACTX *pCtx, const uint8 *pCipher, uint32 ulCipherLen, + const uint8 *pKey, uint8 *pOut, uint32 *pOutLen) +{ + uint32 ulPos, ulPadding, ulOut, ul ; + // 待解密的数据长度最少16字节,并且长度满足是8的整数倍。 + if(pCipher == NULL || pOutLen == NULL || + ulCipherLen < 16 || (ulCipherLen & 0x07) != 0) + return 0 ; + pCtx->pKey = pKey ; //***2016-06-15 这个忘记加了,补上*** + // 先解密头8字节,以便获取第一轮加密时填充的长度。 + DeCipher((const uint32 *)pCipher, (const uint32 *)pKey, (uint32 *)pCtx->bufPre) ; + for(ul=0; ul<8; ul++) + pCtx->buf[ul] = pCtx->bufPre[ul] ; + ulPos = pCtx->buf[0] & 0x07 ; //第一轮加密时填充的长度 + if(ulPos > 1) + { + for(ulOut=2; ulOut<=ulPos; ulOut++) + { + if(pCtx->buf[1] != pCtx->buf[ulOut]) + { + *pOutLen = 0 ; + return 0 ; //解密失败 + } + } + } + ulOut = ulCipherLen - ulPos - 10 ; + if(ulPos + 10 > ulCipherLen || *pOutLen < ulOut) + return 0 ; + pCtx->pCryptPre = (uint8 *)pCipher ; + pCtx->pCrypt = (uint8 *)pCipher + 8 ; + ulPos++ ; + for(ulPadding=0; ulPadding<2; ulPadding++) + { + if(ulPos == 8) + { + DecryptEach8Bytes(pCtx) ; + ulPos = 0 ; + } + ulPos++ ; + } + // + for(ul=0; ulbuf[ulPos] ; + ulPos++ ; + } + // + for(ulPadding=0; ulPadding<7; ulPadding++) + { + if(ulPos < 8) + { + if(pCtx->buf[ulPos] != 0x00) + { + *pOutLen = 0 ; + return 0 ; + } + } + ulPos++ ; + } + *pOutLen = ulOut ; + return 1 ; +} + +void PrintBuffer(const uint8 *buf, uint32 ulLen) +{ + uint32 i ; + for(i=0; i mirai-core + mirai-native