Skip to content

Instantly share code, notes, and snippets.

@abearxiong
Last active February 15, 2023 03:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save abearxiong/cbb0967a564ed2df713da17159efe1f3 to your computer and use it in GitHub Desktop.
Save abearxiong/cbb0967a564ed2df713da17159efe1f3 to your computer and use it in GitHub Desktop.
socket learning python
#内置函数
abs()	delattr()	hash()	memoryview()	set()
all()	dict()	help()	min()	setattr()
any()	dir()	hex()	next()	slice()
ascii()	divmod()	id()	object()	sorted()
bin()	enumerate()	input()	oct()	staticmethod()
bool()	eval()	int()	open()	str()
breakpoint()	exec()	isinstance()	ord()	sum()
bytearray()	filter()	issubclass()	pow()	super()
bytes()	float()	iter()	print()	tuple()
callable()	format()	len()	property()	type()
chr()	frozenset()	list()	range()	vars()
classmethod()	getattr()	locals()	repr()	zip()
compile()	globals()	map()	reversed()	__import__()
complex()	hasattr()	max()	round()	 

python 换行写入 \ 或者()

QQ协议首选的传输层是UDP,如果UDP不可登陆,那么会再尝试使用TCP进行传输。UDP使用的端口是8000,TCP使用的端口是443,应用协议基本一样,只是在通过TCP进行传输时,前两个字节为协议内容的长度(包括2个字节)。

QQ协议中每个通信内容都带有一个协议头部 [0x02,版本号,命令字,序号,QQ号码,数据,0x03]

其中标识占一个字节,版本号、命令字和序号都是2个字节,QQ号码有4个字节,接下来是数据部分(加密),最后是一个尾部标识1个字节。

在进行协议还原的时候,最关心的就是协议头部的命令字,需要根据不同的命令字,来进行相应的处理,最终获取密钥解密聊天内容。

QQ协议的所有包体都有一个很明显又固定的特征,那就是它每一个包体的未加密部分基本相同,加密部分也能通过未加密部分找出密钥来解密包体。

包结构类型:

TCPF包我们把它分为5类:

登录请求包(LIP,LogIn Packet),它是由客户端向服务器发出登录请求的数据包。

登录应答包(LRP,Login Reply Packet),它是由服务器响应客户端登录请求的数据包。

注销请求包(LOP,LogOut Packet),它是由客户端向服务器发出注销登录请求的数据包,服务器对这个包不作应答。

客户端其它包(CSP,Client Sent Packet),它是由客户端向服务器发送的其它包。

服务器其它包(SSP,Server Sent Packet),它是由服务器向客户端发送的其它包。

包头:

所有TCPF包的前7个字节是包头,包头可以识别TCPF包的内容。包头的格式为:

第0字节:TCPF包标识:0x02。

第1-2字节:发送者标识。如果是0x01 0x00,表明是由服务器发送。客户端的标识与所使用的使用的QQ版本有关,目前最新版本QQ2003(0808)的标识为0x0A 0x1D。具体的协议的格式与这个字段所标识的客户端版本有关。目前我们以这个最新的0A1D版本来讨论。

第3-4字节:命令编号。具体的命令编号含义在《QQ协议概述》(Protocol Overview.rtf)中有描述。如果这个字段是0x00 0x01,那么这是一个注销请求包。如果这个字段是0x00 0x22,而发送者标识是0x01 0x00,那么这是一个登录应答包。如果这个字段是0x00 0x22,而发送者标识是其它(例如0x0A 0x1D),那么这是一个登录请求包。其它的命令代码表明是其它包,我们通过发送者标识来区分它是CSP还是SSP。

第5-6字节:命令序列号。客户端和服务器都有各自的当前发送序列号。每初始发出一个指令的时候,使用当前的序列号,然后把当前序列号加一,如果超过0xFFFF,就绕回。如果是响应对方发出的命令,则使用这个命令的序列号。例如,客户端当前的序列号为0x1110,它向服务发送一个0x0016命令,它使用0x1110这个序列号,服务器收到以后,返回一个序列号为0x1110的0x0016命令响应。下一次,客户端又发送一个0x0026命令,这一次它使用加一了的序列号0x1111,服务器也响应0x1111序列号的一个0x0026命令响应。如果这是服务器要向客户端发送0x0017命令,它使用它自己的当前序列号,比如说0x2220,客户端收到以后,也响应一个序列号为0x2220的0x0017命令应答。我们可以通过序列号来判断发出的指令是否已经得到了应答,如果没有,可以重发。服务器对收到的命令的序列号顺序没有要求。服务器也不会一定按照发出的顺序给予应答。

包尾:

所有的TCPF包都以0x03作为包尾。在包头和包尾中间的包数据则不同类型的包有所不同。

LIP包:

登录请求包的包数据格式为:

第7-10字节(4 bytes):发出登录请求的QQ号码。这是一个Big Endian(高位在前)的unsigned long型数值。例如:0x01 0x82 0x5D 0x90就是0x01825D90,转换为十进制是25320848,表明发出请求的QQ号是25320848。

第11-26字节(16 bytes):随机密钥。这个密钥由于加密后面的数据。QQ使用TEA算法来加密数据。它使用的是128bit(16 bytes)的密钥。在0A1D版本中,这个密钥已经固定为16个01。

第27-106字节(80 bytes):加密后的登录包数据。

LRP包:

从第7字节开始到包尾前:加密的登录应答包数据。解密的密钥随客户端版本的不同,有不同的可能。在旧有版本中,使用登录包的随机密钥,在后期的版本,使用用户QQ密码的MD5 Digest。在0A1D中,使用QQ密码的MD5 Digest的MD5 Digest(这体现了腾讯有多么的愚昧和无耻,为了改变而改变)。LRP包内数据很重要的是16个字节的Session Key,它用来作为以后通讯的加密密钥。

LOP包:

它的序列号总是0xFFFF。不过,在新的版本中,好象已经没有了这个要求。 第7-10字节(4 bytes):发送注销登录请求的QQ号码。

第11字节到包尾前:加密的注销登录包数据。使用Session Key作为密钥。

CSP包:

第7-10字节(4 bytes):发送请求的QQ号码。

第11字节到包尾前:加密的包数据。使用Session Key作为密钥。

SSP包:

从第7字节开始到包尾前:加密的服务器发送包数据,使用Session Key作为密钥。

/// <summary>
/// 加密解密QQ消息包的工具类.
/// </summary>
public static class QQCrypter
{
private static Random Rnd = new Random();
private static void code(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key)
{
if (outPos > 0)
{
for (int i = 0; i < 8; i++)
{
In[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]);
}
}
uint[] formattedKey = FormatKey(key);
uint y = ConvertByteArrayToUInt(In, outOffset + outPos);
uint z = ConvertByteArrayToUInt(In, outOffset + outPos + 4);
uint sum = 0;
uint delta = 0x9e3779b9;
uint n = 16;
while (n-- > 0)
{
sum += delta;
y += ((z << 4) + formattedKey[0]) ^ (z + sum) ^ ((z >> 5) + formattedKey[1]);
z += ((y << 4) + formattedKey[2]) ^ (y + sum) ^ ((y >> 5) + formattedKey[3]);
}
Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4);
Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 4, 4);
if (inPos > 0)
{
for (int i = 0; i < 8; i++)
{
Out[outOffset + outPos + i] = (byte)(Out[outOffset + outPos + i] ^ In[inOffset + inPos + i - 8]);
}
}
}
private static void decode(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key)
{
if (outPos > 0)
{
for (int i = 0; i < 8; i++)
{
Out[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]);
}
}
else
{
Array.Copy(In, inOffset, Out, outOffset, 8);
}
uint[] formattedKey = FormatKey(key);
uint y = ConvertByteArrayToUInt(Out, outOffset + outPos);
uint z = ConvertByteArrayToUInt(Out, outOffset + outPos + 4);
uint sum = 0xE3779B90;
uint delta = 0x9e3779b9;
uint n = 16;
while (n-- > 0)
{
z -= ((y << 4) + formattedKey[2]) ^ (y + sum) ^ ((y >> 5) + formattedKey[3]);
y -= ((z << 4) + formattedKey[0]) ^ (z + sum) ^ ((z >> 5) + formattedKey[1]);
sum -= delta;
}
Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4);
Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 4, 4);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="In">密文</param>
/// <param name="offset">密文开始的位置</param>
/// <param name="len">密文长度</param>
/// <param name="key">密钥</param>
/// <returns>返回明文</returns>
public static byte[] Decrypt(byte[] In, int offset, int len, byte[] key)
{
// 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况
if ((len % 8 != 0) || (len < 16))
{
return null;
}
byte[] Out = new byte[len];
for (int i = 0; i < len; i += 8)
{
decode(In, offset, i, Out, 0, i, key);
}
for (int i = 8; i < len; i++)
{
Out[i] = (byte)(Out[i] ^ In[offset + i - 8]);
}
int pos = Out[0] & 0x07;
len = len - pos - 10;
byte[] res = new byte[len];
Array.Copy(Out, pos + 3, res, 0, len);
return res;
}
/// <summary>
/// 加密
/// </summary>
/// <param name="In">明文</param>
/// <param name="offset">明文开始的位置</param>
/// <param name="len">明文长度</param>
/// <param name="key">密钥</param>
/// <returns>返回密文</returns>
public static byte[] Encrypt(byte[] In, int offset, int len, byte[] key)
{
// 计算头部填充字节数
int pos = (len + 10) % 8;
if (pos != 0)
{
pos = 8 - pos;
}
byte[] plain = new byte[len + pos + 10];
plain[0] = (byte)((Rnd.Next() & 0xF8) | pos);
for (int i = 1; i < pos + 3; i++)
{
plain[i] = (byte)(Rnd.Next() & 0xFF);
}
Array.Copy(In, 0, plain, pos + 3, len);
for (int i = pos + 3 + len; i < plain.Length; i++)
{
plain[i] = 0x0;
}
// 定义输出流
byte[] outer = new byte[len + pos + 10];
for (int i = 0; i < outer.Length; i += 8)
{
code(plain, 0, i, outer, 0, i, key);
}
return outer;
}
private static uint[] FormatKey(byte[] key)
{
if (key.Length == 0)
{
throw new ArgumentException("Key must be between 1 and 16 characters in length");
}
byte[] refineKey = new byte[16];
if (key.Length < 16)
{
Array.Copy(key, 0, refineKey, 0, key.Length);
for (int k = key.Length; k < 16; k++)
{
refineKey[k] = 0x20;
}
}
else
{
Array.Copy(key, 0, refineKey, 0, 16);
}
uint[] formattedKey = new uint[4];
int j = 0;
for (int i = 0; i < refineKey.Length; i += 4)
{
formattedKey[j++] = ConvertByteArrayToUInt(refineKey, i);
}
return formattedKey;
}
private static byte[] ConvertUIntToByteArray(uint v)
{
byte[] result = new byte[4];
result[0] = (byte)((v >> 24) & 0xFF);
result[1] = (byte)((v >> 16) & 0xFF);
result[2] = (byte)((v >> 8) & 0xFF);
result[3] = (byte)((v >> 0) & 0xFF);
return result;
}
private static uint ConvertByteArrayToUInt(byte[] v, int offset)
{
if (offset + 4 > v.Length)
{
return 0;
}
uint output;
output = (uint)(v[offset] << 24);
output |= (uint)(v[offset + 1] << 16);
output |= (uint)(v[offset + 2] << 8);
output |= (uint)(v[offset + 3] << 0);
return output;
}
}
package qqtea
import (
"bytes"
"encoding/binary"
"errors"
"math/rand"
"time"
)
const (
delta = uint32(0x9E3779B9)
fillnor = 0xF8
)
type teaCipher struct {
keys [4]uint32
value []byte
byte8 [8]byte
ubyte32 [2]uint32
xor [8]byte //xor
fxor [8]byte //first xor
lxor [8]byte //last xor
nxor [8]byte //new xor Decrypt add
balebuff *bytes.Buffer
seedrand *rand.Rand
}
func NewCipher(key []byte) (*teaCipher, error) {
if len(key) != 16 {
return nil, errors.New("invalid key size error")
}
cipher := &teaCipher{
balebuff: bytes.NewBuffer(nil),
}
for i := 0; i < 4; i++ {
cipher.keys[i] = binary.BigEndian.Uint32(key[i*4:])
}
cipher.seedrand = rand.New(rand.NewSource(time.Now().UnixNano()))
return cipher, nil
}
//加密
func (c *teaCipher) Encrypt(value []byte) []byte {
c.balebuff.Reset()
vl := len(value)
filln := (8 - (vl + 2)) % 8
if filln < 0 {
filln += 2 + 8
} else {
filln += 2
}
bindex := filln + 1
if bindex <= 0 {
return nil
}
rands := make([]byte, bindex)
for i := 1; i < bindex; i++ {
rands[i] = byte((c.seedrand.Intn(236) + 1))
}
rands[0] = byte((filln - 2) | fillnor)
c.balebuff.Write(rands)
c.balebuff.Write(value)
c.balebuff.Write([]byte{00, 00, 00, 00, 00, 00, 00})
vl = c.balebuff.Len()
c.value = c.balebuff.Bytes()
c.balebuff.Reset()
for i := 0; i < vl; i += 8 {
c.xor = xor(c.value[i:i+8], c.fxor[0:8])
c.ubyte32[0] = binary.BigEndian.Uint32(c.xor[0:4])
c.ubyte32[1] = binary.BigEndian.Uint32(c.xor[4:8])
c.encipher()
c.fxor = xor(c.byte8[0:8], c.lxor[0:8])
c.balebuff.Write(c.fxor[0:8])
c.lxor = c.xor
}
return c.balebuff.Bytes()
}
//解密
func (c *teaCipher) Decrypt(value []byte) []byte {
vl := len(value)
if vl <= 0 || (vl % 8) != 0 {
return nil
}
c.balebuff.Reset()
c.ubyte32[0] = binary.BigEndian.Uint32(value[0:4])
c.ubyte32[1] = binary.BigEndian.Uint32(value[4:8])
c.decipher()
copy(c.lxor[0:8], value[0:8])
c.fxor = c.byte8
pos := int((c.byte8[0] & 0x7) + 2)
c.balebuff.Write(c.byte8[0:8])
for i := 8; i < vl; i += 8 {
c.xor = xor(value[i:i+8], c.fxor[0:8])
c.ubyte32[0] = binary.BigEndian.Uint32(c.xor[0:4])
c.ubyte32[1] = binary.BigEndian.Uint32(c.xor[4:8])
c.decipher()
c.nxor = xor(c.byte8[0:8], c.lxor[0:8])
c.balebuff.Write(c.nxor[0:8])
c.fxor = xor(c.nxor[0:8], c.lxor[0:8])
copy(c.lxor[0:8], value[i:i+8])
}
pos++
c.value = c.balebuff.Bytes()
nl := c.balebuff.Len()
if pos >= c.balebuff.Len() || (nl-7) <= pos {
return nil
}
return c.value[pos : nl-7]
}
func (c *teaCipher) encipher() {
sum := delta
for i := 0x10; i > 0; i-- {
c.ubyte32[0] += ((c.ubyte32[1] << 4 & 0xFFFFFFF0) + c.keys[0]) ^ (c.ubyte32[1] + sum) ^ ((c.ubyte32[1] >> 5 & 0x07ffffff) + c.keys[1])
c.ubyte32[1] += ((c.ubyte32[0] << 4 & 0xFFFFFFF0) + c.keys[2]) ^ (c.ubyte32[0] + sum) ^ ((c.ubyte32[0] >> 5 & 0x07ffffff) + c.keys[3])
sum += delta
}
binary.BigEndian.PutUint32(c.byte8[0:4], c.ubyte32[0])
binary.BigEndian.PutUint32(c.byte8[4:8], c.ubyte32[1])
}
func (c *teaCipher) decipher() {
sum := delta
sum = (sum << 4) & 0xffffffff
for i := 0x10; i > 0; i-- {
c.ubyte32[1] -= (((c.ubyte32[0] << 4 & 0xFFFFFFF0) + c.keys[2]) ^ (c.ubyte32[0] + sum) ^ ((c.ubyte32[0] >> 5 & 0x07ffffff) + c.keys[3]))
c.ubyte32[1] &= 0xffffffff
c.ubyte32[0] -= (((c.ubyte32[1] << 4 & 0xFFFFFFF0) + c.keys[0]) ^ (c.ubyte32[1] + sum) ^ ((c.ubyte32[1] >> 5 & 0x07ffffff) + c.keys[1]))
c.ubyte32[0] &= 0xffffffff
sum -= delta
}
binary.BigEndian.PutUint32(c.byte8[0:4], c.ubyte32[0])
binary.BigEndian.PutUint32(c.byte8[4:8], c.ubyte32[1])
}
func xor(a, b []byte) (bts [8]byte) {
l := len(a)
for i := 0; i < l; i += 4 {
binary.BigEndian.PutUint32(bts[i:i+4], binary.BigEndian.Uint32(a[i:i+4])^binary.BigEndian.Uint32(b[i:i+4]))
}
return bts
}
/*
package main
import (
"log"
"github.com/sun8911879/qqtea"
)
func main() {
key := []byte{120, 76, 249, 219, 21, 206, 98, 255, 135, 49, 196, 249, 195, 140, 250, 13}
value := []byte{0, 3, 31, 3, 90, 125, 0, 0, 0, 5, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 224, 243, 85, 220, 6, 100, 0, 0, 0, 0, 1, 66, 151, 244, 75, 19, 149, 82, 53, 36, 91, 36, 151, 57, 157, 122, 147, 41, 35, 190, 132, 225, 108, 214, 174, 82, 144, 73, 241, 241, 187, 233, 235, 0, 0, 0, 0, 1, 70, 96, 30, 211, 198, 36, 22, 191, 202, 162, 158, 158, 184, 154, 210, 78, 32, 2, 149, 246, 0, 0, 0, 1, 0, 0}
t,err := qqtea.NewCipher(key)
if err != nil{
log.Panicln(err)
}
result := t.Encrypt(value)
log.Println(result)
result = t.Decrypt(result)
log.Println(result)
}
*/
# -*- coding: utf-8 -*-
"""
Created on Mon May 6 09:48:41 2019
@author: xiong
"""
import socket
HOST = '127.0.0.1' # The server's hostname or IP address
#HOST = socket.gethostname()
PORT = 65432 # The port used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))
# -*- coding: utf-8 -*-
"""
Created on Mon May 6 10:16:33 2019
@author: xiong
"""
import socket
address = ('127.0.0.1', 65432)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg =input("输入数据:")
# msg =bytes(msg,encoding='utf-8')
if not msg:
break
s.sendto(bytes(msg,encoding='utf-8'), address)
s.close()
# -*- coding: utf-8 -*-
"""
Created on Mon May 6 09:45:03 2019
@author: xiong
"""
import socket
HOST = '127.0.0.1' # Standard loopback interface address (localhost)
print(HOST)
PORT = 65432 # Port to listen on (non-privileged ports are > 1023)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
print("get from cliend:",data)
# -*- coding: utf-8 -*-
"""
Created on Mon May 6 10:16:33 2019
@author: xiong
"""
import socket
address = ('127.0.0.1', 65432)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
msg =input("输入数据:")
# msg =bytes(msg,encoding='utf-8')
if not msg:
break
s.sendto(bytes(msg,encoding='utf-8'), address)
s.close()
# -*- coding: utf-8 -*-
"""
Created on Tue May 7 15:41:34 2019 # tea python实现 依据QQCrypter.cs
/**
* 加密解密QQ消息的工具类. QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节
* 我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块
* f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是: crypt = f(plain &circ; preCrypt) &circ;
* prePlain 所以,从crypt得到plain的过程自然是 plain = d(crypt &circ; prePlain) &circ;
* preCrypt 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数
* 填充的字节数与原始明文长度有关,填充的方法是:
*
* <pre>
* <code> *
* ------- 消息填充算法 -----------
* a = (明文长度 + 10) mod 8
* if(a 不等于 0) a = 8 - a;
* b = 随机数 &amp; 0xF8 | a; 这个的作用是把a的值保存了下来
* plain[0] = b; 然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置
* plain[1 至 a+2] = 随机数 &amp; 0xFF; 这里用随机数填充明文的第1到第a+2个字节
* plain[a+3 至 a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文
* plain[a+3+明文长度, 最后] = 0; 在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
* ------- 消息填充算法 ------------ *
* </code>
* </pre>
*/
@author: xiong
"""
import random
#import copy
class Tea:
'''
QQ tea算法 python实现
'''
def __init__(self):
print("Tea")
def code(self, inner, inOffset, inPos, Out, outOffset, outPos, key):
"""method code() 根据已有内容,输入会一直修改,而out每次都是新的"""
if outPos > 0:
inner_start = inner[0:(outOffset + outPos)*2]
inner_body = inner[(outOffset + outPos)*2:(outOffset + outPos)*2+16]
out_body = Out[(outOffset + outPos)*2 -16:(outOffset + outPos)*2]
inner_end = inner[(outOffset + outPos)*2+16:]
inner_center = b''
for i in range(8):
inner_1 = inner_body[i*2:i*2+2]
out_1 = out_body[i*2:i*2+2]
out_value = self.hex_to_int(inner_1)^self.hex_to_int(out_1)
inner_center += self.int_to_hex(out_value)
inner = inner_start + inner_center + inner_end
#print("strat",inner_start)
#print("cent",inner_center)
#print("end",inner_end)
#print(inner)
array = self.format_key(key) # int 4
#print(array)
num = self.convert_bytes2int(inner,(outOffset + outPos)*2)
num2 = self.convert_bytes2int(inner,(outOffset + outPos)*2 + 8)
num3 = 0
num4 = 2654435769
#print("num:",num," num2:",num2," num3:",num3," num4:",num4)
for i in range(16):
num3 += num4
num3 %= 4294967296
num += ((num2 << 4) + array[0]) ^ (num2 + num3) ^ ((num2 >> 5) + array[1])
num %= 4294967296
num2 += ((num << 4) + array[2]) ^ (num + num3) ^ ((num >> 5) + array[3])
num2 %= 4294967296
#print("num:",num," num2:",num2," num3:",num3)
#print("num:",num," num2:",num2," num3:",num3," num4:",num4)
get_num = self.convert_int2bytes(num) + self.convert_int2bytes(num2)
if inPos > 0:
out_1 = get_num
inner_1 = inner[(inOffset + inPos)*2 - 16:(inOffset + inPos)*2]
get_out = b''
#print(get_num,inner_1)
for i in range(8):
inner_2 = inner_1[i*2:i*2+2]
out_2 = out_1[i*2:i*2+2]
out_value = self.hex_to_int(out_2)^self.hex_to_int(inner_2)
get_out += self.int_to_hex(self.hex_to_int(inner_2)^self.hex_to_int(out_2))
get_num = get_out
#print(get_num)
Out += get_num
return inner,Out
def decode(self, inner, inOffset, inPos, Out, outOffset, outPos, key):
if outPos > 0:
inner_body = inner[(outOffset + outPos)*2:(outOffset + outPos)*2+16]
out_body = Out[(outOffset + outPos)*2 -16:(outOffset + outPos)*2]
# print("123",inner_body,out_body)
inner_center = b''
for i in range(8):
inner_1 = inner_body[i*2:i*2+2]
out_1 = out_body[i*2:i*2+2]
out_value = self.hex_to_int(inner_1)^self.hex_to_int(out_1)
inner_center += self.int_to_hex(out_value)
get_num = inner_center
#print("strat",inner_start)
else:
get_num = inner[inPos*2:(inPos*2)+16]
#print(inner)
# print("getnum:",get_num)
array = self.format_key(key) # int 4
#print(array)
num = self.convert_bytes2int(get_num,0)
num2 = self.convert_bytes2int(get_num,8)
num3 = 3816266640
num4 = 2654435769
# print("开始num:",num," num2:",num2," num3:",num3," num4:",num4)
for i in range(16):
num2 -= ((num << 4) + array[2]) ^ (num + num3) ^ ((num >> 5) + array[3])
num2 %= 4294967296
num -= ((num2 << 4) + array[0]) ^ (num2 + num3) ^ ((num2 >> 5) + array[1])
num %= 4294967296
num3 -= num4
num3 %= 4294967296
#print("num:",num," num2:",num2," num3:",num3)
# print("num:",num," num2:",num2," num3:",num3," num4:",num4)
get_num = self.convert_int2bytes(num) + self.convert_int2bytes(num2)
Out += get_num
return inner,Out
def format_key(self, key):
if len(key) == 0:
raise RuntimeError('key(1,16)长度不能为0')
#key = list(key), 只能取到一个,实际是2个字节一组
array = []
if len(key)<16: # 补位暂时不管
for i in range(len(key),16):
key.append(32)
for i in range(0,32,8):
array.append(self.convert_bytes2int(key,i))
return array
def convert_bytes2int(self,v,offset):
if offset + 8 >len(v):
return 0
num = 0
for i in range(4):
hex_bytes = v[offset+i*2:offset+i*2+2]
#print("type:",type(hex_bytes))
num = num | self.hex_to_int(hex_bytes) << (8*(3-i))
return num
def convert_int2bytes(self, num):
bs = b''
for i in range(4):
int_num = num >> (8*(3-i)) & 255
bs += self.int_to_hex(int_num)
return bs
def hex_to_int(self, hex_bytes):
#print("type:",type(hex_bytes),hex_bytes)
b = hex_bytes.decode('utf-8')
#print(hex_bytes,"<-num->", int('0x'+b,16))
return int('0x'+b,16)
def int_to_hex(self, int_num):
if int_num < 16:
return b'0' + hex(int_num)[2:].encode('utf-8')
return hex(int_num)[2:].encode('utf-8')
def encrypt(self, inter, key = b'B3E2EAABC49B6D9F6FDBC4CFB8E55839'):
array2 = b''
array,num = self.filling(inter)
for k in range(0,num,8):
array,array2 = self.code(array,0,k,array2,0,k,key) # *2 是因为python中bytes长度长一倍
return array2
def filling(self, inner):
inLen = int(len(inner)/2)
# 计算头部填充字节数
num = (inLen + 0x0a) % 8
if num != 0:
num = 8 - num
# 计算输出的密文长度 inLen + num + 10
array = b'' + bytes([( 1 & 0xF8) | 1]).hex().encode('utf-8') # random.randint(0,255)
# 这里用随机产生的数填充
for i in range(1, num+3):
array = array + bytes([1 & 0xFF]).hex().encode('utf-8') #random.randint(0,255)
array = array + inner # 加入明文
# 修改明文 num+3+inlen 到数组长度为0 并补位00
for j in range(num+3+inLen,num + inLen +10):
array = array + b'00' # 32
return array,inLen + num + 10
def decrypt(self ,inner ,key = b'B3E2EAABC49B6D9F6FDBC4CFB8E55839'):
inLen = int(len(inner)/2)
tail = True
array = b''
for i in range(inLen):
pos = (inLen - 1 -i)*2
if tail:
if inner[pos: pos+2] == b'03':
tail = False
elif inner[pos: pos+2] == b'00':
pass
else:
array = inner[pos: pos+2] + array
tail = False
else:
array = inner[pos: pos+2] + array
if inLen%8 !=0 or inLen<16:
raise RuntimeError('长度不能小于16')
array2 = b''
for i in range(0,inLen,8):
array,array2 = self.decode(array,0,i,array2,0,i,key)
array2_center = b''
for i in range(8,inLen):
a1 = self.hex_to_int(array2[i*2:i*2+2])
a2 = self.hex_to_int(array[(i-8)*2:(i-8)*2+2])
array2_center += self.int_to_hex(a1^a2)
array2_content = array2[0:16] + array2_center
num = self.hex_to_int(array2_content[0:2])&7
inLen = (inLen - num -10)*2
return array2_content[num*2+6: num*2+6+inLen]
def t(self,val):
val = list(val)
val[1] = 2
if __name__ == "__main__":
tea = Tea()
inner = '0018001600010000045300000001000015857DB8666C000000000309000800013D97B49D0001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA'.lower().encode('utf-8')
key = 'C7FD4ECA0E0D8B2829B5C90CD50DEDB5'.lower().encode('utf-8')
print(inner)
encrypt_result = tea.encrypt(inner,key)
print(encrypt_result)
decrypt_result = tea.decrypt(encrypt_result,key)
print(decrypt_result)
头开始:QQHeaderBasicFamily
02
CMainVer
02 37
CSubVer
02 37 09
(ushort)Command
02 37 09 08 25
Sequence
02 37 09 08 25 36 36
QQ头结尾
02 37 09 08 25 36 36 7D B8 66 6C
<<开始:XxooA
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00
DwClientType
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01
DwPubNo
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01 00 00 68 1C
XxooD:>>
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01 00 00 68 1C 30 00 00 00
0825key
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01 00 00 68 1C 30 00 00 00 32 31 14 F9 81 3C 6D 84 3A B1 BB D9 0F AF 29 D0
0825key
32 31 14 F9 81 3C 6D 84 3A B1 BB D9 0F AF 29 D0
开始<<
wSubVer
00 01
DwSsoVersion
00 01 00 00 04 53
DwServiceId
00 01 00 00 04 53 00 00 00 01
DwClientVer
00 01 00 00 04 53 00 00 00 01 00 00 15 85
QQ
00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C
WRedirectCount
00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00
NullBuf
00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00
Command 头
00 18
Command Body
00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00
TLV0018
00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00
wSubVer
00 01
ip:61.151.180.158
ipcontent
3D 97 B4 9E
DwServerIP
00 01 3D 97 B4 9E
RedirectIP.Count
00 01 3D 97 B4 9E 00
RedirectIP
00 01 3D 97 B4 9E 00
cPingType
00 01 3D 97 B4 9E 00 01
Command 头
03 09
Command Body
03 09 00 08 00 01 3D 97 B4 9E 00 01
TLV0309
00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00 03 09 00 08 00 01 3D 97 B4 9E 00 01
wSubVer
00 02
UshortWrite
00 02 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00
Command 头
00 36
Command Body
00 36 00 12 00 02 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00
TLV0036
00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00 03 09 00 08 00 01 3D 97 B4 9E 00 01 00 36 00 12 00 02 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00
wSubVer
01 02
BufDhPublicKeyLength
01 02 00 19
BufDhPublicKey
01 02 00 19 02 78 28 16 7C 9E F3 B7 5A 7B 5A EF A2 30 10 EC 0C 46 87 70 76 31 A7 88 EA
Command 头
01 14
Command Body
01 14 00 1D 01 02 00 19 02 78 28 16 7C 9E F3 B7 5A 7B 5A EF A2 30 10 EC 0C 46 87 70 76 31 A7 88 EA
TLV0114>>
00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00 03 09 00 08 00 01 3D 97 B4 9E 00 01 00 36 00 12 00 02 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19 02 78 28 16 7C 9E F3 B7 5A 7B 5A EF A2 30 10 EC 0C 46 87 70 76 31 A7 88 EA
BodySteam 需要加密的内容
00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 7D B8 66 6C 00 00 00 00 03 09 00 08 00 01 3D 97 B4 9E 00 01 00 36 00 12 00 02 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19 02 78 28 16 7C 9E F3 B7 5A 7B 5A EF A2 30 10 EC 0C 46 87 70 76 31 A7 88 EA
加密后的内容
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01 00 00 68 1C 30 00 00 00 32 31 14 F9 81 3C 6D 84 3A B1 BB D9 0F AF 29 D0 90 E0 18 48 A4 8C 73 F9 D3 A7 F7 DF A8 4B 32 C0 39 2E 80 C4 9D 79 86 A2 DB EB 08 13 0E B0 1B F5 30 90 BA 48 A3 0C D1 70 20 2D 31 14 22 86 DD 4E 45 50 E1 96 1C 42 2C 2D D8 A5 ED 7C 4E 06 5C 7F 75 44 35 40 D1 B1 B7 7A D2 C2 62 95 43 72 A0 7D E4 2C 3D 43 40 C9 1F 18 38 73 CA 69 E4 B4 9C 8C 65 04 14 E7 F0 BB A6 60
all 如果是tcp需要在包的开头填包长度,然后回到目前pos
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01 00 00 68 1C 30 00 00 00 32 31 14 F9 81 3C 6D 84 3A B1 BB D9 0F AF 29 D0 90 E0 18 48 A4 8C 73 F9 D3 A7 F7 DF A8 4B 32 C0 39 2E 80 C4 9D 79 86 A2 DB EB 08 13 0E B0 1B F5 30 90 BA 48 A3 0C D1 70 20 2D 31 14 22 86 DD 4E 45 50 E1 96 1C 42 2C 2D D8 A5 ED 7C 4E 06 5C 7F 75 44 35 40 D1 B1 B7 7A D2 C2 62 95 43 72 A0 7D E4 2C 3D 43 40 C9 1F 18 38 73 CA 69 E4 B4 9C 8C 65 04 14 E7 F0 BB A6 60 03
结果
02 37 09 08 25 36 36 7D B8 66 6C 03 00 00 00 01 01 01 00 00 68 1C 30 00 00 00 32 31 14 F9 81 3C 6D 84 3A B1 BB D9 0F AF 29 D0 90 E0 18 48 A4 8C 73 F9 D3 A7 F7 DF A8 4B 32 C0 39 2E 80 C4 9D 79 86 A2 DB EB 08 13 0E B0 1B F5 30 90 BA 48 A3 0C D1 70 20 2D 31 14 22 86 DD 4E 45 50 E1 96 1C 42 2C 2D D8 A5 ED 7C 4E 06 5C 7F 75 44 35 40 D1 B1 B7 7A D2 C2 62 95 43 72 A0 7D E4 2C 3D 43 40 C9 1F 18 38 73 CA 69 E4 B4 9C 8C 65 04 14 E7 F0 BB A6 60 03
61.151.180.158:8000
# -*- coding: utf-8 -*-
"""
Created on Tue May 7 18:06:38 2019
tea.py 的测试
@author: xiong
"""
import unittest
import sys
sys.path.append("..")
from src.tea import Tea
tea = Tea()
class TestLogin(unittest.TestCase):
"""Test src/tea.py"""
def test_format_key(self):
"""Test method format_key()"""
#array = [1333436914,2272652757,564238896,517702433]
#self.assertEqual(array, tea.format_key(b"4F7AA1F28775EDD521A19A301EDB8321"))
array2 = [3980167545, 461067553, 3328928192, 3526830759]
self.assertEqual(array2, tea.format_key(b"ED3C89791B7B5521C66B69C0D2372AA7"))
def test_encrypt(self):
"""Test method encrypt()"""
#tea.encrypt()
def test_filling(self):
""""""
inner = '0018001600010000045300000001000015857DB8666C000000000309000800013D97B4AB0001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA'.lower().encode('utf-8')
filling_inner = '010101010018001600010000045300000001000015857DB8666C000000000309000800013D97B4AB0001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA00000000000000'.lower().encode('utf-8')
filling_result = tea.filling(inner)
self.assertEqual(filling_inner,filling_result[0])
inner2 = '0018001600010000045300000001000015857DB8666C000000000309000800013D97B5610001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA'.lower().encode('utf-8')
filling_inner2 = '010101010018001600010000045300000001000015857DB8666C000000000309000800013D97B5610001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA00000000000000'.lower().encode('utf-8')
filling_result2 = tea.filling(inner2)
self.assertEqual(filling_inner2,filling_result2[0])
@unittest.skip(u"强制跳过")
def test_code(self):
"""Test method code(self, inner, inOffset, inPos, Out, outOffset, outPos, key)"""
tin = '010101010018001600010000045300000001000015857DB8666C000000000309000800013D97E2A90001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA00000000000000'.lower().encode('utf-8')
tkey ='A882182C58040751E646866D51928CC3'.lower().encode('utf-8')
ts = tea.code(tin,0,0,b'',0,0,tkey)
self.assertEqual(tin,ts[0])
tValue = 'E0D7699C3630A898'.lower().encode('utf-8')
self.assertEqual(tValue,ts[1])
# print(ts[0])
ts = tea.code(ts[0],0,8,ts[1],0,8,tkey)
tin2 = '0101010100180016E0D6699C3263A8980001000015857DB8666C000000000309000800013D97E2A90001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA00000000000000'.lower().encode('utf-8')
tValue2 = 'E0D7699C3630A898FF5EFDBE7A003748'.lower().encode('utf-8')
self.assertEqual(tin2.decode('utf-8'),ts[0].decode('utf-8'))
self.assertEqual(tValue2,ts[1])
# 循环测试
def test_for_code(self):
"""Test method code() foreach"""
tin = '010101010018001600010000045300000001000015857DB8666C000000000309000800013D97B5610001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA00000000000000'.lower().encode('utf-8')
tkey = 'A882182C58040751E646866D51928CC3'.lower().encode('utf-8')
array2 = b''
for i in range(0,int(len(tin)/2),8):
tin,array2 = tea.code(tin,0,i,array2,0,i,tkey)
#print(i,"tin",tin,"array2:",array2)
tValue = 'E0D7699C3630A898FF5EFDBE7A003748743400C9C63DB85493C6267AC8A67C8777EA7D30A4192FF1AD6F8299F5F7A01C267F78220DD054E48C8B7E39063F0E9BAE40F24577243082D49D725E4FFFA6945AA337A8244285517626A8B43B89700928A83066BF08AC59'.lower().encode('utf-8')
self.assertEqual(tValue,array2)
def test_encrypt(self):
inner = '0018001600010000045300000001000015857DB8666C000000000309000800013D97B5610001003600120002000100000001000000000000000000000114001D01020019027828167C9EF3B75A7B5AEFA23010EC0C4687707631A788EA'.lower().encode('utf-8')
key = 'A882182C58040751E646866D51928CC3'.lower().encode('utf-8')
result = tea.encrypt(inner, key)
should_result = 'E0D7699C3630A898FF5EFDBE7A003748743400C9C63DB85493C6267AC8A67C8777EA7D30A4192FF1AD6F8299F5F7A01C267F78220DD054E48C8B7E39063F0E9BAE40F24577243082D49D725E4FFFA6945AA337A8244285517626A8B43B89700928A83066BF08AC59'.lower().encode('utf-8')
self.assertEqual(should_result,result)
#@unittest.skip(u"强制跳过")
def test_decrypt(self):
inner = 'E0D7699C3630A898FF5EFDBE7A003748743400C9C63DB85493C6267AC8A67C87D9A6DF7EC18DEAE2193318782836D4E54D83629A59FD0308778B4048BEEF54E5FBBDFF7FE09B790F7C72833BA7547FA363B2CE13C073E3009CFAD0B819ACFB1644080396A39EC6EF'.lower().encode('utf-8')
key = 'A882182C58040751E646866D51928CC3'.lower().encode('utf-8')
tValue= b'0018001600010000045300000001000015857db8666c000000000309000800013d97e2a60001003600120002000100000001000000000000000000000114001d01020019027828167c9ef3b75a7b5aefa23010ec0c4687707631a788ea'
result = tea.decrypt(inner,key)
self.assertEqual(tValue,result)
def test_decode(self):
"""Test method code(self, inner, inOffset, inPos, Out, outOffset, outPos, key)"""
tin = 'E0D7699C3630A898FF5EFDBE7A003748743400C9C63DB85493C6267AC8A67C871DD83E661F9B4BA0F29474DC702F7683462D4ABC6E2FDC30CA7F278A863384129CB42AFCC8628AD60811B53D672C3BAA0C015A3709C815050F5D3450DF4AB5672A16DEE056A92B12'.lower().encode('utf-8')
tkey ='A882182C58040751E646866D51928CC3'.lower().encode('utf-8')
ts = tea.decode(tin,0,0,b'',0,0,tkey)
self.assertEqual(tin,ts[0])
tValue = '0101010100180016'.lower().encode('utf-8')
self.assertEqual(tValue,ts[1])
tValue2 = '0101010100180016E0D6699C3263A898'.lower().encode('utf-8')
ts2 = tea.decode(tin,0,8,tValue,0,8,tkey)
self.assertEqual(tValue2,ts2[1])
def test_for_decode(self):
tin = 'E0D7699C3630A898FF5EFDBE7A003748743400C9C63DB85493C6267AC8A67C87D9A6DF7EC18DEAE2193318782836D4E54D83629A59FD0308778B4048BEEF54E5FBBDFF7FE09B790F7C72833BA7547FA363B2CE13C073E3009CFAD0B819ACFB1644080396A39EC6EF'.lower().encode('utf-8')
tkey ='A882182C58040751E646866D51928CC3'.lower().encode('utf-8')
array2 = b''
for i in range(0,int(len(tin)/2),8):
tin,array2 = tea.decode(tin,0,i,array2,0,i,tkey)
#print("循环结果",array2)
array2_content = array2[0:16]
array2_center = b''
for i in range(8,int(len(tin)/2)):
a1 = tea.hex_to_int(array2[i*2:i*2+2])
a2 = tea.hex_to_int(tin[(i-8)*2:(i-8)*2+2])
array2_center += tea.int_to_hex(a1^a2)
array2_content= array2_content + array2_center
#print("修改array2_content",array2_content)
num = tea.hex_to_int(array2_content[0:2])&7
#print("num",num,array2_content[num*2+6:-14])
length = len(tin) - num*2 - 20
#print("num",num,)
tValue= b'0018001600010000045300000001000015857db8666c000000000309000800013d97e2a60001003600120002000100000001000000000000000000000114001d01020019027828167c9ef3b75a7b5aefa23010ec0c4687707631a788ea'
self.assertEqual(tValue,array2_content[num*2+6:num*2+6+length])
def test_convert_int2bytes(self):
n1 = 3772213660
n2 = 909158552
tValue = 'E0D7699C3630A898'.lower().encode('utf-8')
self.assertEqual(tValue,tea.convert_int2bytes(n1) + tea.convert_int2bytes(n2))
if __name__ == '__main__':
unittest.main()
package tea;
import java.io.ByteArrayOutputStream;
import java.util.Random;
/**
* 加密解密QQ消息的工具类. QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节
* 我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块
* f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是: crypt = f(plain &circ; preCrypt) &circ;
* prePlain 所以,从crypt得到plain的过程自然是 plain = d(crypt &circ; prePlain) &circ;
* preCrypt 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数
* 填充的字节数与原始明文长度有关,填充的方法是:
*
* <pre>
* <code>
*
* ------- 消息填充算法 -----------
* a = (明文长度 + 10) mod 8
* if(a 不等于 0) a = 8 - a;
* b = 随机数 &amp; 0xF8 | a; 这个的作用是把a的值保存了下来
* plain[0] = b; 然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置
* plain[1 至 a+2] = 随机数 &amp; 0xFF; 这里用随机数填充明文的第1到第a+2个字节
* plain[a+3 至 a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文
* plain[a+3+明文长度, 最后] = 0; 在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
* ------- 消息填充算法 ------------
*
* </code>
* </pre>
*
* @author luma
* @author notXX
*/
public class Crypter {
// 指向当前的明文块
private byte[] plain;
// 这指向前面一个明文块
private byte[] prePlain;
// 输出的密文或者明文
private byte[] out;
// 当前加密的密文位置和上一次加密的密文块位置,他们相差8
private int crypt, preCrypt;
// 当前处理的加密解密块的位置
private int pos;
// 填充数
private int padding;
// 密钥
private byte[] key;
// 用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的
// 但是最开始的8个字节没有反馈可用,所有需要标明这种情况
private boolean header = true;
// 这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时
// 后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错
private int contextStart;
// 随机数对象
private static Random random = new Random();
// 字节输出流
private ByteArrayOutputStream baos;
/**
* 构造函数
*/
public Crypter() {
baos = new ByteArrayOutputStream(8);
}
/**
* 把字节数组从offset开始的len个字节转换成一个unsigned int, 因为java里面没有unsigned,所以unsigned
* int使用long表示的, 如果len大于8,则认为len等于8。如果len小于8,则高位填0 <br>
* (edited by notxx) 改变了算法, 性能稍微好一点. 在我的机器上测试10000次, 原始算法花费18s, 这个算法花费12s.
*
* @param in
* 字节数组.
* @param offset
* 从哪里开始转换.
* @param len
* 转换长度, 如果len超过8则忽略后面的
* @return
*/
private static long getUnsignedInt(byte[] in, int offset, int len) {
long ret = 0;
int end = 0;
if (len > 8)
end = offset + 8;
else
end = offset + len;
for (int i = offset; i < end; i++) {
ret <<= 8;
ret |= in[i] & 0xff;
}
return (ret & 0xffffffffl) | (ret >>> 32);
}
/**
* 解密
* @param in 密文
* @param offset 密文开始的位置
* @param len 密文长度
* @param k 密钥
* @return 明文
*/
public byte[] decrypt(byte[] in, int offset, int len, byte[] k) {
// 检查密钥
if(k == null)
return null;
crypt = preCrypt = 0;
this.key = k;
int count;
byte[] m = new byte[offset + 8];
// 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况
if((len % 8 != 0) || (len < 16)) return null;
// 得到消息的头部,关键是得到真正明文开始的位置,这个信息存在第一个字节里面,所以其用解密得到的第一个字节与7做与
prePlain = decipher(in, offset);
pos = prePlain[0] & 0x7;
// 得到真正明文的长度
count = len - pos - 10;
// 如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回
if(count < 0) return null;
// 这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时
// 第一个8字节块也没有preCrypt,所有这里建一个全0的
for(int i = offset; i < m.length; i++)
m[i] = 0;
// 通过了上面的代码,密文应该是没有问题了,我们分配输出缓冲区
out = new byte[count];
// 设置preCrypt的位置等于0,注意目前的preCrypt位置是指向m的,因为java没有指针,所以我们在后面要控制当前密文buf的引用
preCrypt = 0;
// 当前的密文位置,为什么是8不是0呢?注意前面我们已经解密了头部信息了,现在当然该8了
crypt = 8;
// 自然这个也是8
contextStart = 8;
// 加1,和加密算法是对应的
pos++;
// 开始跳过头部,如果在这个过程中满了8字节,则解密下一块
// 因为是解密下一块,所以我们有一个语句 m = in,下一块当然有preCrypt了,我们不再用m了
// 但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来
// 所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充
padding = 1;
while(padding <= 2) {
if(pos < 8) {
pos++;
padding++;
}
if(pos == 8) {
m = in;
if(!decrypt8Bytes(in, offset, len)) return null;
}
}
// 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密
// 注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是in了
int i = 0;
while(count != 0) {
if(pos < 8) {
out[i] = (byte)(m[offset + preCrypt + pos] ^ prePlain[pos]);
i++;
count--;
pos++;
}
if(pos == 8) {
m = in;
preCrypt = crypt - 8;
if(!decrypt8Bytes(in, offset, len))
return null;
}
}
// 最后的解密部分,上面一个while已经把明文都解出来了,就剩下尾部的填充了,应该全是0
// 所以这里有检查是否解密了之后是不是0,如果不是的话那肯定出错了,返回null
for(padding = 1; padding < 8; padding++) {
if(pos < 8) {
if((m[offset + preCrypt + pos] ^ prePlain[pos]) != 0)
return null;
pos++;
}
if(pos == 8) {
m = in;
preCrypt = crypt;
if(!decrypt8Bytes(in, offset, len))
return null;
}
}
return out;
}
/**
* @param in
* 需要被解密的密文
* @param inLen
* 密文长度
* @param k
* 密钥
* @return Message 已解密的消息
*/
public byte[] decrypt(byte[] in, byte[] k) {
return decrypt(in, 0, in.length, k);
}
/**
* 加密
* @param in 明文字节数组
* @param offset 开始加密的偏移
* @param len 加密长度
* @param k 密钥
* @return 密文字节数组
*/
public byte[] encrypt(byte[] in, int offset, int len, byte[] k) {
// 检查密钥
if(k == null)
return in;
plain = new byte[8];
prePlain = new byte[8];
pos = 1;
padding = 0;
crypt = preCrypt = 0;
this.key = k;
header = true;
// 计算头部填充字节数
pos = (len + 0x0A) % 8;
if(pos != 0)
pos = 8 - pos;
// 计算输出的密文长度
out = new byte[len + pos + 10];
// 这里的操作把pos存到了plain的第一个字节里面
// 0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置
plain[0] = (byte)((rand() & 0xF8) | pos);
// 这里用随机产生的数填充plain[1]到plain[pos]之间的内容
for(int i = 1; i <= pos; i++)
plain[i] = (byte)(rand() & 0xFF);
pos++;
// 这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块
for(int i = 0; i < 8; i++)
prePlain[i] = 0x0;
// 继续填充2个字节的随机数,这个过程中如果满了8字节就加密之
padding = 1;
while(padding <= 2) {
if(pos < 8) {
plain[pos++] = (byte)(rand() & 0xFF);
padding++;
}
if(pos == 8)
encrypt8Bytes();
}
// 头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完
int i = offset;
while(len > 0) {
if(pos < 8) {
plain[pos++] = in[i++];
len--;
}
if(pos == 8)
encrypt8Bytes();
}
// 最后填上0,以保证是8字节的倍数
padding = 1;
while(padding <= 7) {
if(pos < 8) {
plain[pos++] = 0x0;
padding++;
}
if(pos == 8)
encrypt8Bytes();
}
return out;
}
/**
* @param in
* 需要加密的明文
* @param inLen
* 明文长度
* @param k
* 密钥
* @return Message 密文
*/
public byte[] encrypt(byte[] in, byte[] k) {
return encrypt(in, 0, in.length, k);
}
/**
* 加密一个8字节块
*
* @param in
* 明文字节数组
* @return
* 密文字节数组
*/
private byte[] encipher(byte[] in) {
// 迭代次数,16次
int loop = 0x10;
// 得到明文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
// 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
// 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
long y = getUnsignedInt(in, 0, 4);
long z = getUnsignedInt(in, 4, 4);
long a = getUnsignedInt(key, 0, 4);
long b = getUnsignedInt(key, 4, 4);
long c = getUnsignedInt(key, 8, 4);
long d = getUnsignedInt(key, 12, 4);
// 这是算法的一些控制变量,为什么delta是0x9E3779B9呢?
// 这个数是TEA算法的delta,实际是就是(sqr(5) - 1) * 2^31 (根号5,减1,再乘2的31次方)
long sum = 0;
long delta = 0x9E3779B9;
delta &= 0xFFFFFFFFL;
// 开始迭代了,乱七八糟的,我也看不懂,反正和DES之类的差不多,都是这样倒来倒去
while (loop-- > 0) {
sum += delta;
sum &= 0xFFFFFFFFL;
y += ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b);
y &= 0xFFFFFFFFL;
z += ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d);
z &= 0xFFFFFFFFL;
}
// 最后,我们输出密文,因为我用的long,所以需要强制转换一下变成int
baos.reset();
writeInt((int)y);
writeInt((int)z);
return baos.toByteArray();
}
/**
* 解密从offset开始的8字节密文
*
* @param in
* 密文字节数组
* @param offset
* 密文开始位置
* @return
* 明文
*/
private byte[] decipher(byte[] in, int offset) {
// 迭代次数,16次
int loop = 0x10;
// 得到密文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数
// 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的
// 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与
long y = getUnsignedInt(in, offset, 4);
long z = getUnsignedInt(in, offset + 4, 4);
long a = getUnsignedInt(key, 0, 4);
long b = getUnsignedInt(key, 4, 4);
long c = getUnsignedInt(key, 8, 4);
long d = getUnsignedInt(key, 12, 4);
// 算法的一些控制变量,sum在这里也有数了,这个sum和迭代次数有关系
// 因为delta是这么多,所以sum如果是这么多的话,迭代的时候减减减,减16次,最后
// 得到0。反正这就是为了得到和加密时相反顺序的控制变量,这样才能解密呀~~
long sum = 0xE3779B90;
sum &= 0xFFFFFFFFL;
long delta = 0x9E3779B9;
delta &= 0xFFFFFFFFL;
// 迭代开始了, @_@
while(loop-- > 0) {
z -= ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d);
z &= 0xFFFFFFFFL;
y -= ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b);
y &= 0xFFFFFFFFL;
sum -= delta;
sum &= 0xFFFFFFFFL;
}
baos.reset();
writeInt((int)y);
writeInt((int)z);
return baos.toByteArray();
}
/**
* 写入一个整型到输出流,高字节优先
*
* @param t
*/
private void writeInt(int t) {
baos.write(t >>> 24);
baos.write(t >>> 16);
baos.write(t >>> 8);
baos.write(t);
}
/**
* 解密
*
* @param in
* 密文
* @return
* 明文
*/
private byte[] decipher(byte[] in) {
return decipher(in, 0);
}
/**
* 加密8字节
*/
private void encrypt8Bytes() {
// 这部分完成我上面所说的 plain ^ preCrypt,注意这里判断了是不是第一个8字节块,如果是的话,那个prePlain就当作preCrypt用
for(pos = 0; pos < 8; pos++) {
if(header)
plain[pos] ^= prePlain[pos];
else
plain[pos] ^= out[preCrypt + pos];
}
// 这个完成我上面说的 f(plain ^ preCrypt)
byte[] crypted = encipher(plain);
// 这个没什么,就是拷贝一下,java不像c,所以我只好这么干,c就不用这一步了
System.arraycopy(crypted, 0, out, crypt, 8);
// 这个完成了 f(plain ^ preCrypt) ^ prePlain,ok,下面拷贝一下就行了
for(pos = 0; pos < 8; pos++)
out[crypt + pos] ^= prePlain[pos];
System.arraycopy(plain, 0, prePlain, 0, 8);
// 完成了加密,现在是调整crypt,preCrypt等等东西的时候了
preCrypt = crypt;
crypt += 8;
pos = 0;
header = false;
}
/**
* 解密8个字节
*
* @param in
* 密文字节数组
* @param offset
* 从何处开始解密
* @param len
* 密文的长度
* @return
* true表示解密成功
*/
private boolean decrypt8Bytes(byte[] in , int offset, int len) {
// 这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^ prePlain
for(pos = 0; pos < 8; pos++) {
if(contextStart + pos >= len)
return true;
prePlain[pos] ^= in[offset + crypt + pos];
}
// 好,这里执行到了 d(crypt ^ prePlain)
prePlain = decipher(prePlain);
if(prePlain == null)
return false;
// 解密完成,最后一步好像没做?
// 这里最后一步放到decrypt里面去做了,因为解密的步骤有点不太一样
// 调整这些变量的值先
contextStart += 8;
crypt += 8;
pos = 0;
return true;
}
/**
* 这是个随机因子产生器,用来填充头部的,如果为了调试,可以用一个固定值
* 随机因子可以使相同的明文每次加密出来的密文都不一样
*
* @return
* 随机因子
*/
private int rand() {
return random.nextInt();
}
}
package tea;
import java.util.Arrays;
/**
* @ClassName Main
* @Desctiption 描述
* @Author Abear
* @Date 2019/5/6 11:30
* @Version 1.0
**/
public class Main {
public static void main(String[] args) {
Crypter crypter = new Crypter();
byte[] in = {(byte)0x0E, (byte)0x5B, (byte)0x76, (byte)0xEE, (byte)0x00, (byte)0x02, (byte)0x7D, (byte)0xB8, (byte)0x66, (byte)0x6C, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x53, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x15, (byte)0x85, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x6D, (byte)0x43, (byte)0xEB, (byte)0xD3, (byte)0x1B, (byte)0x9E, (byte)0x35, (byte)0x25, (byte)0x37, (byte)0x61, (byte)0x77, (byte)0x7E, (byte)0xBA, (byte)0xE5, (byte)0xD9, (byte)0xCF, (byte)0x5C, (byte)0xD0, (byte)0x20, (byte)0xB4, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xB6, (byte)0x96, (byte)0x42, (byte)0xB4, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x10, (byte)0x77, (byte)0x98, (byte)0x00, (byte)0x0B, (byte)0xAB, (byte)0x5D, (byte)0x4F, (byte)0x3D, (byte)0x30, (byte)0x50, (byte)0x65, (byte)0x2C, (byte)0x4A, (byte)0x2A, (byte)0xF8, (byte)0x65, (byte)0x4C, (byte)0xB3, (byte)0x6A, (byte)0xC4, (byte)0x84, (byte)0x4C, (byte)0x2B, (byte)0x2E, (byte)0x09, (byte)0x8D, (byte)0x02, (byte)0x04, (byte)0xB3, (byte)0xAD, (byte)0xCA, (byte)0xE9};
byte[] k = {(byte)0xB3,(byte)0xE2,(byte)0xEA,(byte)0xAB,(byte)0xC4,(byte)0x9B,(byte)0x6D,(byte)0x9F,(byte)0x6F,(byte)0xDB,(byte)0xC4,(byte)0xCF,(byte)0xB8,(byte)0xE5,(byte)0x58,(byte)0x39};
byte[] value = crypter.encrypt(in,k);
byte[] ovalue = {(byte)0x5E,(byte)0xBE,(byte)0x47,(byte)0x4A,(byte)0xC5,(byte)0x58,(byte)0x42,(byte)0xAD,(byte)0xEB,(byte)0x96,(byte)0xC3,(byte)0xB7,(byte)0x1C,(byte)0xB2,(byte)0x4D,(byte)0xD5,(byte)0x2E,(byte)0x6A,(byte)0x9B,(byte)0xA6,(byte)0x61,(byte)0x20,(byte)0x74,(byte)0xA8,(byte)0xDB,(byte)0xDB,(byte)0x8A,(byte)0x4B,(byte)0xA4,(byte)0x9E,(byte)0x68,(byte)0xAF,(byte)0x40,(byte)0xB3,(byte)0x2F,(byte)0x84,(byte)0xB8,(byte)0x24,(byte)0xE2,(byte)0x73,(byte)0xAB,(byte)0xC4,(byte)0xED,(byte)0x9A,(byte)0xDD,(byte)0x99,(byte)0xF9,(byte)0x8B,(byte)0xED,(byte)0x86,(byte)0xA3,(byte)0x63,(byte)0x54,(byte)0xCB,(byte)0x37,(byte)0x37,(byte)0xA6,(byte)0x2C,(byte)0xFA,(byte)0x38,(byte)0xAB,(byte)0x6D,(byte)0x79,(byte)0x41,(byte)0x90,(byte)0x56,(byte)0x95,(byte)0x78,(byte)0xA7,(byte)0x3D,(byte)0x77,(byte)0x91,(byte)0xAA,(byte)0x60,(byte)0x41,(byte)0x92,(byte)0x9C,(byte)0x3A,(byte)0xE0,(byte)0xB1,(byte)0xB8,(byte)0x5B,(byte)0x3F,(byte)0xA2,(byte)0x3E,(byte)0x51,(byte)0xF0,(byte)0x73,(byte)0xF4,(byte)0x79,(byte)0x80,(byte)0x94,(byte)0x24,(byte)0xDA,(byte)0x8A,(byte)0x1D,(byte)0xE0,(byte)0x27,(byte)0x27,(byte)0xD3,(byte)0x42,(byte)0xEA,(byte)0xC1,(byte)0x48,(byte)0x27,(byte)0x0F,(byte)0xBE,(byte)0x16,(byte)0xFC,(byte)0x2E,(byte)0x0C,(byte)0x53,(byte)0x19,(byte)0x75,(byte)0xB4,(byte)0xE5,(byte)0x2A,(byte)0x5B,(byte)0x3C,(byte)0xFA};
System.out.println(Arrays.toString(value));
byte[] din = crypter.decrypt(ovalue,k);
System.out.println(Arrays.toString(ovalue));
System.out.println("de:");
System.out.println(Arrays.toString(in));
System.out.println(Arrays.toString(din));
System.out.println(Arrays.toString(crypter.decrypt(ovalue,k)));
String str = new String(in);
//System.out.println(str);
}
}
/*
* [20, -62, -13, 117, 0, 119, 67, 111, -48, -80, 46, -55, 60, -52, 16, -73, -99, 106, -70, -9, -20, 12, -43, -67, -23, -19, -127, 45, -35, -43, -43, -67, -127, -45, -66, 15, 1, -50, 27, 126, -12, -14, 42, -55, -102, 8, 34, 47, -13, 81, 30, 85, 120, -104, 91, -108, 14, -29, -127, -121, 33, -104, 106, 119, 6, 90, 67, 10, -16, 34, 112, 96, -103, 63, 7, 57, 53, 112, -19, 81, 24, -69, -117, 112, -18, -62, 44, 66, 75, -80, 16, 109, -51, -62, 9, 115, -53, 28, -100, -89, 116, -89, -114, 35, -42, -77, 14, 77, 45, -28, 0, 9, -63, 52, 35, -86, -107, -51, -108, 42]
* [94, -66, 71, 74, -59, 88, 66, -83, -21, -106, -61, -73, 28, -78, 77, -43, 46, 106, -101, -90, 97, 32, 116, -88, -37, -37, -118, 75, -92, -98, 104, -81, 64, -77, 47, -124, -72, 36, -30, 115, -85, -60, -19, -102, -35, -103, -7, -117, -19, -122, -93, 99, 84, -53, 55, 55, -90, 44, -6, 56, -85, 109, 121, 65, -112, 86, -107, 120, -89, 61, 119, -111, -86, 96, 65, -110, -100, 58, -32, -79, -72, 91, 63, -94, 62, 81, -16, 115, -12, 121, -128, -108, 36, -38, -118, 29, -32, 39, 39, -45, 66, -22, -63, 72, 39, 15, -66, 22, -4, 46, 12, 83, 25, 117, -76, -27, 42, 91, 60, -6]
* de:
* [14, 91, 118, -18, 0, 2, 125, -72, 102, 108, 0, 0, 4, 83, 0, 0, 0, 1, 0, 0, 21, -123, 0, 0, 0, 109, 67, -21, -45, 27, -98, 53, 37, 55, 97, 119, 126, -70, -27, -39, -49, 92, -48, 32, -76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -74, -106, 66, -76, 0, 0, 0, 0, 0, 0, 0, 5, 0, 16, 119, -104, 0, 11, -85, 93, 79, 61, 48, 80, 101, 44, 74, 42, -8, 101, 76, -77, 106, -60, -124, 76, 43, 46, 9, -115, 2, 4, -77, -83, -54, -23]
* [14, 91, 118, -18, 0, 2, 125, -72, 102, 108, 0, 0, 4, 83, 0, 0, 0, 1, 0, 0, 21, -123, 0, 0, 0, 109, 67, -21, -45, 27, -98, 53, 37, 55, 97, 119, 126, -70, -27, -39, -49, 92, -48, 32, -76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -74, -106, 66, -76, 0, 0, 0, 0, 0, 0, 0, 5, 0, 16, 119, -104, 0, 11, -85, 93, 79, 61, 48, 80, 101, 44, 74, 42, -8, 101, 76, -77, 106, -60, -124, 76, 43, 46, 9, -115, 2, 4, -77, -83, -54, -23]
* [14, 91, 118, -18, 0, 2, 125, -72, 102, 108, 0, 0, 4, 83, 0, 0, 0, 1, 0, 0, 21, -123, 0, 0, 0, 109, 67, -21, -45, 27, -98, 53, 37, 55, 97, 119, 126, -70, -27, -39, -49, 92, -48, 32, -76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -74, -106, 66, -76, 0, 0, 0, 0, 0, 0, 0, 5, 0, 16, 119, -104, 0, 11, -85, 93, 79, 61, 48, 80, 101, 44, 74, 42, -8, 101, 76, -77, 106, -60, -124, 76, 43, 46, 9, -115, 2, 4, -77, -83, -54, -23]
*/
/* OK
* in:
* 0E-5B-76-EE-00-02-7D-B8-66-6C-00-00-04-53-00-00-00-01-00-00-15-85-00-00-00-6D-43-EB-D3-1B-9E-35-25-37-61-77-7E-BA-E5-D9-CF-5C-D0-20-B4-00-00-00-00-00-00-00-00-00-00-00-00-00-B6-96-42-B4-00-00-00-00-00-00-00-05-00-10-77-98-00-0B-AB-5D-4F-3D-30-50-65-2C-4A-2A-F8-65-4C-B3-6A-C4-84-4C-2B-2E-09-8D-02-04-B3-AD-CA-E9
*
* keys:
* B3-E2-EA-AB-C4-9B-6D-9F-6F-DB-C4-CF-B8-E5-58-39
*
* value:
* 5E-BE-47-4A-C5-58-42-AD-EB-96-C3-B7-1C-B2-4D-D5-2E-6A-9B-A6-61-20-74-A8-DB-DB-8A-4B-A4-9E-68-AF-40-B3-2F-84-B8-24-E2-73-AB-C4-ED-9A-DD-99-F9-8B-ED-86-A3-63-54-CB-37-37-A6-2C-FA-38-AB-6D-79-41-90-56-95-78-A7-3D-77-91-AA-60-41-92-9C-3A-E0-B1-B8-5B-3F-A2-3E-51-F0-73-F4-79-80-94-24-DA-8A-1D-E0-27-27-D3-42-EA-C1-48-27-0F-BE-16-FC-2E-0C-53-19-75-B4-E5-2A-5B-3C-FA
*/

python 转换

1. md5使用(一点)

import hashlib # 第一步导入hashlib
md5 = hashlib.md5() # 获取md5
md = md5.update(b'content') # bytes的数据更新; type(md)的类型的hashlib
md_str = md5.hexdigest() # 获取hash的值,得到hex 16进制内容 type(md_str) 类型为 str

2. bytes 和 hex 和 字符串转换

o_str = "123456" # 原字符串
bytes_str = bytes(o_str,"utf-8") # 转换为  type = bytes 
bytes_str = o_str.encode("utf-8") # 同上  type = bytes 
o_str = bytes_str.decode("utf-8") # decode可以省utf-8; 因为默认是;bytes()或str()转换不能省略 
o_str = str(byte_str, "utf-8") # 同上
# 如果 str(byte_str) 会是b'123456',而不是'123456'

hex_str = "0xAD4"  # type = str
hex_int = int(hex_str, 16) # type = int
new_int = hex_int + 0x200 # type = int
print hex(new_int)

int_value = 123  # type = int
'{:016x}'.format(int_value) # 进行对int_value 格式化 0是不足补0 16是16位长度,x是16进制

bytes_str = bytes.fromhex(hex_str) # 从字符串hex转bytes  type = bytes 
hex_str  = bytes_str.hex() # type = str
# -*- coding: utf-8 -*-
"""
Created on Mon May 6 16:31:34 2019
@author: xiong
"""
import hashlib
if __name__ =="__main__":
u = 2109236844
md5 = hashlib.md5()
md5.update(b'pwd')
m1 = md5.hexdigest()
print("第一次hash结果:",md5.hexdigest())
merge = bytes(m1,'utf-8') + bytes('{:016x}'.format(u),'utf-8')
merge_str = merge.decode('utf-8')
print("得到merge的bytes:",merge,"得到的字符串:",merge_str)
merge_str_bytes = bytes.fromhex(merge_str)
md52 =hashlib.md5()
md52.update(merge_str_bytes)
print(md52.hexdigest())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment