ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python逆向实战:手机号查QQ号协议分析与TEA加密算法实现

Python逆向实战:手机号查QQ号协议分析与TEA加密算法实现 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的很多接口都用了TEATiny 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的字典序排序。拼接成key1value1key2value2的形式。在末尾加上一个固定的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如果要用异步请求提高效率可以装httpxpip install httpx开发工具方面VSCode配Python插件就够用了。如果需要抓包装mitmproxypip 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:i8]) 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:i8]) 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 b0123456789abcdef # 示例密钥实际需要从抓包或反编译获取 def generate_nonce(self, length16): 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 fphone{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, dataparams, timeout10 ) 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, rate0.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被封检查网络、换IP5.5 独家避坑技巧技巧一用真实设备抓包。模拟器的请求特征和真机不同容易被风控识别。如果条件允许用真机抓包把真机的User-Agent、DeviceID等参数复制到代码里。技巧二保持会话。QQ的接口通常需要先登录获取Cookie然后用这个Cookie去查询。不要每次请求都重新登录保持requests.Session()的会话状态。技巧三观察响应时间。如果响应时间突然变长可能是被限速了。这时候主动降速比等到被封再处理要好。技巧四日志记录。每次请求都把URL、参数、响应状态、响应时间记下来。出问题的时候翻日志比重新抓包快得多。技巧五不要贪多。一次查一个手机号查到了就停。批量查询很容易触发风控而且从道德和法律角度也不合适。6. 协议分析中的加密算法扩展6.1 TEA之外QQ还用哪些加密除了TEAQQ的接口还可能用到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数据库方便后续分析。研究其他平台的类似接口比如手机号查微博、查支付宝等同样要注意法律边界。我个人在实际操作中的体会是协议分析最耗时的部分不是写代码而是抓包和调试。一个参数不对可能就要花几个小时去排查。所以耐心和细致比技术本身更重要。另外一定要做好日志记录不然出了问题连从哪查起都不知道。
返回列表