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哈希。有些接口还会加入时间戳和随机数来防止重放。
构造签名的步骤通常是:
- 把所有请求参数(除了
sign本身)按key的字典序排序。 - 拼接成
key1=value1&key2=value2的形式。 - 在末尾加上一个固定的
appkey或secret。 - 计算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 mitmproxy4.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}×tamp={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,我们可以通过以下策略尝试还原:
- 多接口对比:如果另一个接口返回
123***6789,那么中间缺失的部分就是5,完整QQ号是12345789。 - 长度推断:QQ号通常是5-11位。如果脱敏后显示
1234***789,总长度是10位,那么中间缺失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是频率超限。排查步骤:
- 检查
sign的计算逻辑,确认参数排序和appkey是否正确。 - 检查
timestamp是否和服务器时间同步,偏差不要超过5分钟。 - 检查
User-Agent和Referer是否和抓包时一致。 - 如果确认参数没问题,那就是频率问题,降低请求频率或换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数据库,方便后续分析。
- 研究其他平台的类似接口,比如手机号查微博、查支付宝等(同样要注意法律边界)。
我个人在实际操作中的体会是:协议分析最耗时的部分不是写代码,而是抓包和调试。一个参数不对,可能就要花几个小时去排查。所以耐心和细致比技术本身更重要。另外,一定要做好日志记录,不然出了问题连从哪查起都不知道。