☰
Python逆向实战:手机号查QQ号协议分析与TEA加密算法实现
2026/10/7 6:01:22 网站建设 项目流程

1. 这个工具到底在做什么:从手机号到QQ号的查询逻辑拆解

手机号查QQ号这个需求,在数据整理、账号关联分析、老同学找回等场景里一直存在。很多人第一反应是去找现成的在线工具,但用一圈下来会发现:要么收费,要么查不到,要么干脆是钓鱼页面。所以真正靠谱的路子,还是自己动手写一套查询逻辑。

这个项目的核心思路并不复杂:QQ号在注册和使用过程中,会和手机号产生绑定关系。当你在QQ的“找回密码”或“添加好友”流程里输入手机号时,系统会去数据库里匹配这个手机号是否关联了某个QQ账号。如果匹配成功,就会返回一个脱敏后的QQ号(比如只显示前几位或后几位)。我们要做的,就是模拟这个查询请求,把返回的脱敏信息拿到手,再通过多次请求或不同接口的差异来还原出完整的QQ号。

听起来有点像“拼图”:单次请求只能拿到一部分信息,但通过不同入口、不同参数组合,可以把碎片拼成完整号码。这就是为什么很多人在搜“phone2qq”这个词——它指的就是从手机号反查QQ号的整套技术方案。

适合谁来参考这个内容?如果你有Python基础,了解HTTP请求的基本概念,想学习协议分析、加密算法逆向、接口参数构造这些实战技能,那这套东西会非常适合你。它不只是一个“查号工具”,更是一个完整的逆向工程练手项目,涉及TEA加密算法、QQ登录协议、请求签名、频率控制等多个技术点。

注意:本文仅从技术学习角度讨论协议分析和算法实现,所有操作应在合法合规的前提下进行,不得用于侵犯他人隐私或任何违法用途。

2. 技术选型与整体架构设计

2.1 为什么选Python而不是其他语言

做这类协议分析和接口模拟,Python的优势非常明显。首先是requests库发HTTP请求极其方便,几行代码就能完成一个带Cookie、带Header的POST请求。其次是pycryptodome或Cryptodome库对TEA、AES这类加密算法的支持很完善,不用自己从零实现。再者,Python的调试体验好,配合mitmproxy或浏览器开发者工具,可以快速抓包、改包、重放。

如果用Java或Go来做,当然也能实现,但开发效率会低不少。Java的加密库用起来比较繁琐,Go的HTTP库虽然性能好但调试不如Python灵活。对于这种需要反复试错、快速迭代的逆向项目,Python是首选。

2.2 整体架构:三层结构

整个查询工具可以拆成三层:

  • 网络层:负责发送HTTP/HTTPS请求,处理Cookie、Header、代理等。核心是requests.Session(),保持会话状态。
  • 加密层:负责对请求参数进行TEA加密、对响应数据进行解密。核心是TEA算法的Python实现。
  • 业务层:负责构造查询参数、解析返回结果、拼接脱敏QQ号、控制请求频率。

这三层分开写的好处是:加密层可以独立测试,网络层可以替换(比如换成httpx支持异步),业务层可以灵活调整查询策略。

2.3 关键难点:TEA加密算法

QQ的很多接口都用了TEA(Tiny Encryption Algorithm)来加密请求体。TEA是一种分组加密算法,分组长度64位,密钥长度128位,迭代轮数通常为32轮。它的特点是实现简单、速度快,但安全性在现代标准下不算高。

TEA的加密过程大致是这样的:把64位明文分成两个32位部分v0和v1,然后用128位密钥(分成4个32位子密钥k0-k3)进行32轮迭代。每轮的操作包括异或、加法、移位。核心公式是:

sum += delta v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1) v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)

其中delta是一个常数0x9E3779B9,来源于黄金分割率。

解密过程是加密的逆运算,sum从delta * 32开始递减。

在Python里实现TEA,需要注意整数溢出的处理。Python的整数是任意精度的,所以需要手动用& 0xFFFFFFFF来模拟32位无符号整数。

def tea_encrypt(v, k): v0, v1 = v[0], v[1] sum_val = 0 delta = 0x9E3779B9 k0, k1, k2, k3 = k[0], k[1], k[2], k[3] for _ in range(32): sum_val = (sum_val + delta) & 0xFFFFFFFF v0 = (v0 + (((v1 << 4) + k0) ^ (v1 + sum_val) ^ ((v1 >> 5) + k1))) & 0xFFFFFFFF v1 = (v1 + (((v0 << 4) + k2) ^ (v0 + sum_val) ^ ((v0 >> 5) + k3))) & 0xFFFFFFFF return [v0, v1]

这段代码看起来简单,但有几个坑:第一,sum_val每轮都要加delta并取模;第二,v0和v1的更新顺序不能错,必须先更新v0再用新的v0更新v1;第三,移位操作要用>>和<<,不能用>>>(Python没有无符号右移)。

2.4 请求签名的构造

除了TEA加密,QQ的接口通常还需要一个签名参数,比如sign或sig。这个签名一般是对请求参数按字典序排序后拼接,再进行MD5或SHA1哈希。有些接口还会加入时间戳和随机数来防止重放。

构造签名的步骤通常是:

  1. 把所有请求参数(除了sign本身)按key的字典序排序。
  2. 拼接成key1=value1&key2=value2的形式。
  3. 在末尾加上一个固定的appkey或secret。
  4. 计算MD5,取十六进制字符串。

这个逻辑不复杂,但关键是找到正确的appkey。不同的QQ接口用的appkey可能不同,需要通过抓包和对比来确认。

3. 核心细节解析与实操要点

3.1 抓包:第一步永远是抓包

不管你要分析哪个接口,第一步都是抓包。用mitmproxy或者浏览器的开发者工具,把手机QQ或QQ安全中心的请求抓下来。重点看这几个东西:

  • 请求URL和Method
  • 请求Header里的User-Agent、Cookie、Referer
  • 请求Body里的参数名和值
  • 响应Body的结构

抓包的时候要注意:QQ的接口很多是HTTPS的,需要配置证书才能解密。mitmproxy的证书安装比较简单,按照官方文档操作就行。如果是手机端抓包,需要把手机WiFi的代理指向电脑,然后安装mitmproxy的CA证书。

抓到的请求里,有些参数是明文,有些是加密的。明文的参数比如uin、phone、timestamp,加密的参数比如data、encrypt。你需要区分哪些是业务参数,哪些是加密后的数据。

3.2 定位加密入口

抓到包之后,下一步是找到加密的逻辑。如果是Web端的接口,可以直接在浏览器开发者工具的Sources面板里搜索关键词,比如encrypt、tea、sign。如果是App端的接口,就需要反编译APK,用jadx或apktool把DEX文件转成Java代码,然后搜索加密相关的类。

搜索的关键词包括:TEA、encrypt、decrypt、sign、getSign、QQEncrypt。找到加密类之后,重点看它的密钥是怎么来的。有些密钥是硬编码在代码里的,有些是从其他接口动态获取的。

实操心得:反编译的时候,不要一上来就看混淆后的代码,先找字符串常量。密钥、appkey、salt这些通常以字符串形式存在,搜索字符串比看逻辑快得多。

3.3 参数构造的细节

构造查询请求的时候,有几个参数特别关键:

  • 手机号:需要是带国家码的格式,比如86+手机号。有些接口要求不带+号,有些要求带。
  • 时间戳:通常是毫秒级Unix时间戳,需要和服务器时间同步。如果时间偏差太大,请求会被拒绝。
  • 随机数:有些接口要求一个随机字符串,长度和字符集都有要求。
  • 版本号:QQ的接口通常需要指定版本号,比如QQVersion或clientVersion。版本号不对,返回的数据格式可能不同。

这些参数的具体值,需要通过多次抓包对比来确定。比如你抓了两次请求,发现只有timestamp和nonce不同,那这两个就是动态参数。

3.4 响应解析与脱敏还原

查询接口返回的数据通常是JSON格式,里面包含一个脱敏的QQ号,比如1234***789。要还原完整QQ号,有几种思路:

第一种是利用不同接口的脱敏规则差异。比如接口A显示前4位和后3位,接口B显示前3位和后4位,两个一对比,中间缺失的部分就能推断出来。

第二种是利用QQ的“找回密码”流程。在找回密码时,系统会要求你验证手机号,验证成功后可能会显示完整的QQ号或者部分更多位。

第三种是通过多次请求,每次请求稍微不同的参数(比如不同的来源标识),观察返回的脱敏结果是否有变化。

注意:脱敏还原的难度取决于接口的设计。有些接口只返回“该手机号已绑定QQ”,不返回任何QQ号信息,这种情况下就无法还原。

3.5 频率控制与异常处理

QQ的接口有严格的频率限制。如果短时间内发送大量请求,会被封IP或封账号。所以必须做频率控制:

  • 每次请求之间间隔至少2-3秒。
  • 单个IP每小时请求不超过50次。
  • 如果返回403或429,立即停止并切换IP。

异常处理方面,要捕获requests.exceptions.RequestException,对超时、连接错误、SSL错误分别处理。如果连续失败3次,就暂停一段时间再试。

4. 完整实操流程:从零搭建查询工具

4.1 环境准备与依赖安装

先确保Python环境就绪。推荐用Python 3.8以上版本,太老的版本对requests和pycryptodome的支持不好。

pip install requests pycryptodome

如果要用异步请求提高效率,可以装httpx:

pip install httpx

开发工具方面,VSCode配Python插件就够用了。如果需要抓包,装mitmproxy:

pip install mitmproxy

4.2 TEA加密模块的完整实现

前面给了加密的核心逻辑,这里补全解密和填充处理。

import struct def tea_decrypt(v, k): v0, v1 = v[0], v[1] delta = 0x9E3779B9 sum_val = (delta * 32) & 0xFFFFFFFF k0, k1, k2, k3 = k[0], k[1], k[2], k[3] for _ in range(32): v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + sum_val) ^ ((v0 >> 5) + k3))) & 0xFFFFFFFF v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + sum_val) ^ ((v1 >> 5) + k1))) & 0xFFFFFFFF sum_val = (sum_val - delta) & 0xFFFFFFFF return [v0, v1] def pad(data): pad_len = 8 - (len(data) % 8) return data + bytes([pad_len] * pad_len) def unpad(data): pad_len = data[-1] return data[:-pad_len] def encrypt_tea(plaintext, key): plaintext = pad(plaintext) key_bytes = key if isinstance(key, bytes) else key.encode() k = struct.unpack('>4I', key_bytes[:16]) result = b'' for i in range(0, len(plaintext), 8): v = struct.unpack('>2I', plaintext[i:i+8]) v = tea_encrypt(v, k) result += struct.pack('>2I', v[0], v[1]) return result def decrypt_tea(ciphertext, key): key_bytes = key if isinstance(key, bytes) else key.encode() k = struct.unpack('>4I', key_bytes[:16]) result = b'' for i in range(0, len(ciphertext), 8): v = struct.unpack('>2I', ciphertext[i:i+8]) v = tea_decrypt(v, k) result += struct.pack('>2I', v[0], v[1]) return unpad(result)

这段代码里,struct.unpack('>4I', ...)的>表示大端序,4I表示4个无符号32位整数。QQ的TEA实现通常用大端序,但有些接口用小端序,需要根据实际情况调整。

4.3 请求构造与发送

下面是一个完整的请求构造示例。假设我们已经通过抓包确定了接口地址、参数格式和加密方式。

import requests import time import hashlib import random import string class QQPhoneQuery: def __init__(self): self.session = requests.Session() self.session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36', 'Content-Type': 'application/x-www-form-urlencoded', 'Referer': 'https://aq.qq.com/', }) self.tea_key = b'0123456789abcdef' # 示例密钥,实际需要从抓包或反编译获取 def generate_nonce(self, length=16): chars = string.ascii_letters + string.digits return ''.join(random.choice(chars) for _ in range(length)) def build_sign(self, params, appkey): sorted_keys = sorted(params.keys()) sign_str = '&'.join(f'{k}={params[k]}' for k in sorted_keys if k != 'sign') sign_str += appkey return hashlib.md5(sign_str.encode()).hexdigest() def query(self, phone): timestamp = int(time.time() * 1000) nonce = self.generate_nonce() params = { 'phone': phone, 'timestamp': timestamp, 'nonce': nonce, 'clientVersion': '8.9.0', } # 构造签名 appkey = 'your_appkey_here' params['sign'] = self.build_sign(params, appkey) # 加密敏感参数 plaintext = f'phone={phone}&timestamp={timestamp}'.encode() encrypted = encrypt_tea(plaintext, self.tea_key) params['data'] = encrypted.hex() # 发送请求 try: resp = self.session.post( 'https://example.qq.com/api/query', data=params, timeout=10 ) resp.raise_for_status() return self.parse_response(resp.json()) except requests.exceptions.RequestException as e: print(f'请求失败: {e}') return None def parse_response(self, data): if data.get('code') != 0: print(f'接口返回错误: {data.get("msg")}') return None encrypted_result = bytes.fromhex(data['data']) decrypted = decrypt_tea(encrypted_result, self.tea_key) return decrypted.decode()

这个示例里,appkey和tea_key都是占位符,实际值需要你自己去抓包和反编译获取。query方法里做了签名、加密、发送、解析的完整流程。

4.4 脱敏QQ号的拼接策略

假设接口返回的脱敏QQ号是1234***789,我们可以通过以下策略尝试还原:

  1. 多接口对比:如果另一个接口返回123***6789,那么中间缺失的部分就是5,完整QQ号是12345789。
  2. 长度推断:QQ号通常是5-11位。如果脱敏后显示1234***789,总长度是10位,那么中间缺失3位。
  3. 字典攻击:如果缺失位数不多(比如2-3位),可以穷举所有可能,逐个尝试登录或查询接口验证。

实操心得:脱敏还原的成功率取决于接口设计。有些接口的脱敏规则是固定的,比如只显示前3后2,那中间缺失的位数可能很多,穷举成本很高。这时候可以考虑结合其他信息源,比如QQ昵称、头像、地区等,缩小范围。

4.5 频率控制与IP轮换

频率控制是保证工具稳定运行的关键。下面是一个简单的令牌桶实现:

import time class RateLimiter: def __init__(self, rate=0.5): self.rate = rate # 每秒允许的请求数 self.last_request = 0 def wait(self): now = time.time() elapsed = now - self.last_request if elapsed < self.rate: time.sleep(self.rate - elapsed) self.last_request = time.time()

用的时候,每次请求前调用limiter.wait(),就能保证请求间隔不低于设定值。

IP轮换方面,如果你有多个出口IP,可以在请求失败时切换。但要注意,频繁切换IP也可能触发风控。建议单个IP用到被限制再换,不要每次请求都换。

5. 常见问题与排查技巧实录

5.1 请求返回403或429怎么办

403通常是签名错误或参数缺失,429是频率超限。排查步骤:

  1. 检查sign的计算逻辑,确认参数排序和appkey是否正确。
  2. 检查timestamp是否和服务器时间同步,偏差不要超过5分钟。
  3. 检查User-Agent和Referer是否和抓包时一致。
  4. 如果确认参数没问题,那就是频率问题,降低请求频率或换IP。

5.2 TEA解密结果乱码

解密出来是乱码,通常有几个原因:

  • 密钥不对。确认密钥的字节序和长度是否正确。
  • 加密模式不对。QQ的TEA通常是ECB模式,但有些接口用CBC,需要加IV。
  • 填充方式不对。有些接口用PKCS7填充,有些用零填充。
  • 字节序不对。试试把>改成<,或者手动调整字节顺序。

5.3 脱敏QQ号无法还原

如果接口只返回“已绑定”或“未绑定”,不返回任何QQ号信息,那就无法还原。这种情况下,可以尝试其他接口,比如“找回密码”流程中的接口,可能会返回更多信息。

另外,有些接口的脱敏规则是动态的,比如根据请求来源不同,显示不同的位数。多试几个来源标识,可能会有意外收获。

5.4 常见问题速查表

问题现象可能原因解决方法
403 Forbidden签名错误、参数缺失检查sign计算、补全参数
429 Too Many Requests频率超限降低频率、换IP
解密乱码密钥/模式/填充错误逐一排查,对比抓包数据
返回“系统繁忙”风控拦截暂停一段时间,换设备指纹
脱敏无法还原接口设计限制换接口、结合其他信息源
连接超时网络问题或IP被封检查网络、换IP

5.5 独家避坑技巧

技巧一:用真实设备抓包。模拟器的请求特征和真机不同,容易被风控识别。如果条件允许,用真机抓包,把真机的User-Agent、DeviceID等参数复制到代码里。

技巧二:保持会话。QQ的接口通常需要先登录获取Cookie,然后用这个Cookie去查询。不要每次请求都重新登录,保持requests.Session()的会话状态。

技巧三:观察响应时间。如果响应时间突然变长,可能是被限速了。这时候主动降速,比等到被封再处理要好。

技巧四:日志记录。每次请求都把URL、参数、响应状态、响应时间记下来。出问题的时候,翻日志比重新抓包快得多。

技巧五:不要贪多。一次查一个手机号,查到了就停。批量查询很容易触发风控,而且从道德和法律角度也不合适。

6. 协议分析中的加密算法扩展

6.1 TEA之外:QQ还用哪些加密

除了TEA,QQ的接口还可能用到:

  • MD5:用于签名和密码哈希。
  • SHA1/SHA256:用于更安全的签名。
  • AES:部分新接口用AES替代TEA。
  • RSA:用于密钥交换和敏感数据加密。
  • Base64:用于编码二进制数据。

这些算法的Python实现都很成熟,hashlib、pycryptodome、base64库都能直接用。关键是要确定每个接口用的是哪种算法,以及密钥和IV是什么。

6.2 如何快速识别加密算法

拿到一段密文,怎么判断它用的是什么算法?几个经验:

  • 密文长度是8的倍数,可能是TEA或DES。
  • 密文长度是16的倍数,可能是AES或SM4。
  • 密文是十六进制字符串,长度固定,可能是哈希值。
  • 密文包含=或==结尾,可能是Base64编码。

结合抓包时的请求参数名,比如data、encrypt、cipher,可以进一步缩小范围。

6.3 密钥的获取途径

密钥通常有几个来源:

  • 硬编码:直接写在代码里,反编译就能看到。
  • 动态获取:通过一个单独的接口获取,需要先调用这个接口。
  • 派生:从其他信息(如设备ID、时间戳)派生出来。
  • 协商:通过RSA等算法协商得到。

硬编码的密钥最好找,动态获取的密钥需要多抓几个包,找到获取密钥的接口。派生和协商的密钥最难搞,需要深入分析算法逻辑。

7. 法律与道德边界:必须说清楚的事

这套技术本身是中性的,但用途有边界。以下几点必须明确:

  • 不得用于侵犯他人隐私。查询别人的手机号关联的QQ号,如果没有得到对方授权,就是侵犯隐私。
  • 不得用于骚扰、诈骗等违法活动。这是底线,没有任何商量余地。
  • 不得批量查询。批量查询不仅容易被风控,而且可能触犯法律。
  • 仅用于技术学习。如果你对协议分析、加密算法感兴趣,可以用自己的手机号做测试,或者搭建模拟环境来练习。

提示:技术能力越强,责任越大。学会这些技能之后,应该用在正道上,比如安全研究、数据保护、系统加固等。

8. 从查询工具延伸出的技能树

做完这个项目,你实际上掌握了一套完整的逆向工程技能:

  • 抓包分析:用mitmproxy、Charles、浏览器开发者工具抓取和分析HTTP/HTTPS请求。
  • 加密算法:TEA、AES、RSA、MD5、SHA系列的理解和Python实现。
  • 协议逆向:从抓包数据反推接口参数、签名逻辑、加密方式。
  • 请求模拟:用requests或httpx构造和发送请求,处理Cookie、Header、代理。
  • 频率控制:令牌桶、漏桶算法的实现,IP轮换策略。
  • 异常处理:网络异常、接口异常、风控异常的分类处理。

这些技能可以迁移到很多其他场景:爬虫开发、API对接、安全测试、数据采集等。所以这个项目的价值不只是一个“查QQ号工具”,而是一个综合性的实战练手项目。

如果你已经跑通了整个流程,下一步可以尝试:

  • 把同步请求改成异步,用httpx或aiohttp提高效率。
  • 加一个简单的GUI,用tkinter或PyQt,方便非技术用户使用。
  • 把查询结果存到SQLite数据库,方便后续分析。
  • 研究其他平台的类似接口,比如手机号查微博、查支付宝等(同样要注意法律边界)。

我个人在实际操作中的体会是:协议分析最耗时的部分不是写代码,而是抓包和调试。一个参数不对,可能就要花几个小时去排查。所以耐心和细致比技术本身更重要。另外,一定要做好日志记录,不然出了问题连从哪查起都不知道。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询