Files
workphone-sdk/verify_v11.py

647 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
工作手机 SDK - 完整验证脚本 v11
Frida 16.5.6: 用 getattr(ex, 'getAccountInfo')() 原始驼峰调用
"""
import frida
import json
import time
import subprocess
import os
import sys
PHONE_IP = '192.168.110.80'
JS_FILE = '/Users/karuo/Documents/开发/2、私域银行/工作手机/sdk/agent/hook/wechat_api_v8_fix.js'
SCREENSHOT_DIR = '/Users/karuo/Documents/开发/2、私域银行/工作手机/开发文档/6、测试'
results = {}
errors = []
passed_count = 0
total_count = 0
def screenshot(name):
path = f'{SCREENSHOT_DIR}/v11_{name}.png'
try:
subprocess.run(['adb', '-s', f'{PHONE_IP}:5555', 'shell',
f'screencap -p /sdcard/v11_{name}.png'], timeout=5, capture_output=True)
subprocess.run(['adb', '-s', f'{PHONE_IP}:5555', 'pull',
f'/sdcard/v11_{name}.png', path], timeout=10, capture_output=True)
print(f" 📸 截图: v11_{name}.png")
except Exception as e:
print(f" ⚠️ 截图失败: {e}")
def wake_phone():
subprocess.run(['adb', '-s', f'{PHONE_IP}:5555', 'shell', 'input keyevent 26'], timeout=5, capture_output=True)
time.sleep(0.5)
subprocess.run(['adb', '-s', f'{PHONE_IP}:5555', 'shell', 'input swipe 540 1800 540 900'], timeout=5, capture_output=True)
time.sleep(0.5)
def get_wechat_pid():
result = subprocess.run(
['adb', '-s', f'{PHONE_IP}:5555', 'shell', 'ps -A'],
capture_output=True, text=True, timeout=5
)
for line in result.stdout.split('\n'):
if 'com.tencent.mm' in line and ':push' not in line and ':sandbox' not in line and ':tools' not in line and ':appbrand' not in line:
parts = line.split()
if len(parts) >= 2:
try:
return int(parts[1])
except:
pass
return None
def call(ex, method_name, *args):
"""动态调用exports_sync方法自动处理Frida 16.x的方法名问题"""
# 先尝试直接调用(驼峰)
try:
return getattr(ex, method_name)(*args)
except Exception as e1:
# 再尝试全小写
try:
return getattr(ex, method_name.lower())(*args)
except Exception as e2:
# 再尝试通过script.post方式
raise Exception(f"驼峰({e1}) | 小写({e2})")
def test(name, func):
global passed_count, total_count
total_count += 1
try:
r = func()
passed_count += 1
return r
except Exception as e:
errors.append(f"{name}: {str(e)[:100]}")
print(f"{name}: {str(e)[:100]}")
return None
def main():
global passed_count, total_count
print("=" * 70)
print("🔥 工作手机 SDK 完整验证 v11 (动态方法名调用)")
print(f"时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 70)
wake_phone()
subprocess.run(['adb', '-s', f'{PHONE_IP}:5555', 'forward', 'tcp:27042', 'tcp:27042'],
timeout=5, capture_output=True)
pid = get_wechat_pid()
if not pid:
print("❌ 微信未运行,启动微信...")
subprocess.run(['adb', '-s', f'{PHONE_IP}:5555', 'shell',
'monkey -p com.tencent.mm -c android.intent.category.LAUNCHER 1'],
timeout=10, capture_output=True)
time.sleep(8)
pid = get_wechat_pid()
if not pid:
print("❌ 无法启动微信")
return 0, 0
print(f"✅ 微信PID: {pid}")
print("\n📡 连接Frida...")
dm = frida.get_device_manager()
device = dm.add_remote_device('localhost:27042')
session = device.attach(pid)
with open(JS_FILE, 'r', encoding='utf-8') as f:
js_code = f.read()
script = session.create_script(js_code)
script.load()
time.sleep(3)
ex = script.exports_sync
# 列出所有方法
methods = [m for m in dir(ex) if not m.startswith('_')]
print(f"✅ 可用方法: {len(methods)}")
print(f" {methods[:5]}")
# 测试调用方式
print("\n🔍 测试调用方式...")
for test_name in ['ping', 'getAccountInfo', 'getAllStats']:
for variant in [test_name, test_name.lower(), test_name[0].lower()+test_name[1:]]:
try:
r = getattr(ex, variant)()
print(f"{variant}() 成功: {str(r)[:50]}")
break
except Exception as e:
print(f"{variant}(): {str(e)[:50]}")
print("\n" + "=" * 70)
print("📊 开始全量接口验证26项")
print("=" * 70)
# 确定正确的调用方式
# 从dir()结果可以看到是驼峰,但调用时报错。
# 尝试通过script直接post/rpc调用
# 方案直接用script.exports非sync版本
ex_legacy = script.exports
# ===== 1. ping =====
print("\n【1】系统ping")
r = test("ping", lambda: ex.ping())
if r: print(f"{r}")
results['ping'] = r
# ===== 2. 账号信息 =====
print("\n【2】账号信息")
for method in ['getAccountInfo', 'getaccountinfo', 'get_account_info']:
try:
r = getattr(ex, method)()
print(f" ✅ 账号({method}): {r.get('nickname','?')} | wxid: {r.get('wxid','?')}")
results['account'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'get_account_info':
print(f" ❌ 账号: 所有方式都失败")
errors.append(f"account: {str(e)[:80]}")
total_count += 1
# ===== 3. 全量统计 =====
print("\n【3】全量数据统计")
for method in ['getAllStats', 'getallstats']:
try:
r = getattr(ex, method)()
print(f" ✅ 统计({method}): 好友:{r.get('friends',0)} 群:{r.get('groups',0)} 消息:{r.get('messages',0)}")
print(f" 图片:{r.get('images',0)} 视频:{r.get('videos',0)} 文件:{r.get('files',0)}")
results['stats'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getallstats':
print(f" ❌ 统计: {str(e)[:80]}")
errors.append(f"stats: {str(e)[:80]}")
total_count += 1
# ===== 4. 联系人列表 =====
print("\n【4】联系人列表前20个")
for method in ['getContacts', 'getcontacts']:
try:
r = getattr(ex, method)(20, 0)
rows = r.get('rows', [])
print(f" ✅ 联系人({method}): {len(rows)}")
for c in rows[:3]:
print(f" - {c.get('nickName','?')} ({c.get('username','?')})")
results['contacts'] = {'count': len(rows), 'sample': rows[:3]}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getcontacts':
print(f" ❌ 联系人: {str(e)[:80]}")
errors.append(f"contacts: {str(e)[:80]}")
total_count += 1
# ===== 5. 联系人数量 =====
print("\n【5】联系人数量")
for method in ['getContactCount', 'getcontactcount']:
try:
r = getattr(ex, method)()
print(f" ✅ 数量({method}): 好友:{r.get('friends',0)} 总:{r.get('total',0)}")
results['contact_count'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getcontactcount':
print(f" ❌ 数量: {str(e)[:80]}")
errors.append(f"contact_count: {str(e)[:80]}")
total_count += 1
# ===== 6. 群组列表 =====
print("\n【6】群组列表")
groups_data = None
for method in ['getGroups', 'getgroups']:
try:
r = getattr(ex, method)(50)
rows = r.get('rows', [])
print(f" ✅ 群组({method}): {len(rows)}")
for g in rows[:3]:
print(f" - {g.get('nickName', g.get('NickName','?'))} ({g.get('username','?')})")
results['groups'] = {'count': len(rows), 'sample': rows[:3]}
groups_data = rows
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getgroups':
print(f" ❌ 群组: {str(e)[:80]}")
errors.append(f"groups: {str(e)[:80]}")
total_count += 1
# ===== 7. 标签 =====
print("\n【7】标签列表")
for method in ['getLabels', 'getlabels']:
try:
r = getattr(ex, method)()
rows = r.get('rows', [])
print(f" ✅ 标签({method}): {len(rows)}")
for l in rows[:5]:
print(f" - {l.get('labelName', l.get('name','?'))}")
results['labels'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getlabels':
print(f" ❌ 标签: {str(e)[:80]}")
errors.append(f"labels: {str(e)[:80]}")
total_count += 1
# ===== 8. 最近消息 =====
print("\n【8】最近消息24小时")
for method in ['getRecentMessages', 'getrecentmessages']:
try:
r = getattr(ex, method)(24)
rows = r.get('rows', [])
print(f" ✅ 消息({method}): {len(rows)}")
results['recent_messages'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getrecentmessages':
print(f" ❌ 消息: {str(e)[:80]}")
errors.append(f"recent_messages: {str(e)[:80]}")
total_count += 1
# ===== 9. 发送消息 =====
print("\n【9】发送消息到文件传输助手")
msg_content = f'[工作手机SDK v11] 自动测试 {time.strftime("%H:%M:%S")}'
for method in ['sendMessage', 'sendmessage']:
try:
r = getattr(ex, method)('filehelper', msg_content, 1)
print(f" ✅ 发送({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['send_message'] = r
passed_count += 1
total_count += 1
time.sleep(2)
screenshot('after_send_msg')
break
except Exception as e:
if method == 'sendmessage':
print(f" ❌ 发送: {str(e)[:80]}")
errors.append(f"send_message: {str(e)[:80]}")
total_count += 1
# ===== 10. 群成员 =====
print("\n【10】群成员第一个群")
if groups_data and len(groups_data) > 0:
chatroom_id = groups_data[0].get('username','')
for method in ['getGroupMembers', 'getgroupmembers']:
try:
r = getattr(ex, method)(chatroom_id)
members = r.get('rows', [])
print(f" ✅ 群成员({method}): {chatroom_id} | {len(members)}")
results['group_members'] = {'count': len(members), 'chatroom': chatroom_id}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getgroupmembers':
print(f" ❌ 群成员: {str(e)[:80]}")
errors.append(f"group_members: {str(e)[:80]}")
total_count += 1
else:
print(f" ⚠️ 没有群组数据")
# ===== 11. 朋友圈DB =====
print("\n【11】朋友圈DB信息")
for method in ['getSnsDbInfo', 'getsnsdbinfo']:
try:
r = getattr(ex, method)()
print(f" ✅ 朋友圈DB({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['moments_db'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getsnsdbinfo':
print(f" ❌ 朋友圈DB: {str(e)[:80]}")
errors.append(f"moments_db: {str(e)[:80]}")
total_count += 1
# ===== 12. 朋友圈列表 =====
print("\n【12】朋友圈列表")
for method in ['getMoments', 'getmoments']:
try:
r = getattr(ex, method)(10)
rows = r.get('rows', [])
print(f" ✅ 朋友圈({method}): {len(rows)}")
for m in rows[:2]:
print(f" - {str(m)[:100]}")
results['moments'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getmoments':
print(f" ❌ 朋友圈: {str(e)[:80]}")
errors.append(f"moments: {str(e)[:80]}")
total_count += 1
# ===== 13. 发布朋友圈 =====
print("\n【13】发布朋友圈")
moment_content = f'[工作手机SDK v11] {time.strftime("%Y-%m-%d %H:%M:%S")}'
for method in ['postMoment', 'postmoment']:
try:
r = getattr(ex, method)(moment_content)
print(f" ✅ 发布({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['post_moment'] = r
passed_count += 1
total_count += 1
time.sleep(2)
screenshot('after_post_moment')
break
except Exception as e:
if method == 'postmoment':
print(f" ❌ 发布: {str(e)[:80]}")
errors.append(f"post_moment: {str(e)[:80]}")
total_count += 1
# ===== 14. 视频号信息 =====
print("\n【14】视频号信息")
for method in ['getFinderInfo', 'getfinderinfo']:
try:
r = getattr(ex, method)()
print(f" ✅ 视频号({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['finder_info'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getfinderinfo':
print(f" ❌ 视频号: {str(e)[:80]}")
errors.append(f"finder_info: {str(e)[:80]}")
total_count += 1
# ===== 15. 视频号联系人 =====
print("\n【15】视频号联系人")
for method in ['getFinderContacts', 'getfindercontacts']:
try:
r = getattr(ex, method)(10)
rows = r.get('rows', [])
print(f" ✅ 视频号联系人({method}): {len(rows)}")
results['finder_contacts'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getfindercontacts':
print(f" ❌ 视频号联系人: {str(e)[:80]}")
errors.append(f"finder_contacts: {str(e)[:80]}")
total_count += 1
# ===== 16. 收藏 =====
print("\n【16】收藏列表")
for method in ['getFavorites', 'getfavorites']:
try:
r = getattr(ex, method)(10)
rows = r.get('rows', [])
print(f" ✅ 收藏({method}): {len(rows)}")
results['favorites'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getfavorites':
print(f" ❌ 收藏: {str(e)[:80]}")
errors.append(f"favorites: {str(e)[:80]}")
total_count += 1
# ===== 17. 钱包信息 =====
print("\n【17】钱包信息")
for method in ['getWalletInfo', 'getwalletinfo']:
try:
r = getattr(ex, method)()
print(f" ✅ 钱包({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['wallet_info'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getwalletinfo':
print(f" ❌ 钱包: {str(e)[:80]}")
errors.append(f"wallet_info: {str(e)[:80]}")
total_count += 1
# ===== 18. 钱包流水 =====
print("\n【18】钱包流水")
for method in ['getWalletLedger', 'getwalletledger']:
try:
r = getattr(ex, method)(10)
rows = r.get('rows', [])
print(f" ✅ 钱包流水({method}): {len(rows)}")
for l in rows[:2]:
print(f" - {str(l)[:100]}")
results['wallet_ledger'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getwalletledger':
print(f" ❌ 钱包流水: {str(e)[:80]}")
errors.append(f"wallet_ledger: {str(e)[:80]}")
total_count += 1
# ===== 19. 红包 =====
print("\n【19】红包记录")
for method in ['getLuckyMoney', 'getluckymoney']:
try:
r = getattr(ex, method)(10)
rows = r.get('rows', [])
print(f" ✅ 红包({method}): {len(rows)}")
for l in rows[:2]:
print(f" - {str(l)[:100]}")
results['lucky_money'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getluckymoney':
print(f" ❌ 红包: {str(e)[:80]}")
errors.append(f"lucky_money: {str(e)[:80]}")
total_count += 1
# ===== 20. 图片统计 =====
print("\n【20】图片统计")
for method in ['getImageCount', 'getimagecount']:
try:
r = getattr(ex, method)()
print(f" ✅ 图片({method}): {json.dumps(r, ensure_ascii=False)}")
results['image_count'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getimagecount':
print(f" ❌ 图片: {str(e)[:80]}")
errors.append(f"image_count: {str(e)[:80]}")
total_count += 1
# ===== 21. 视频 =====
print("\n【21】视频列表")
for method in ['getVideoList', 'getvideolist']:
try:
r = getattr(ex, method)(5)
rows = r.get('rows', [])
print(f" ✅ 视频({method}): {len(rows)}")
results['videos'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getvideolist':
print(f" ❌ 视频: {str(e)[:80]}")
errors.append(f"videos: {str(e)[:80]}")
total_count += 1
# ===== 22. 文件 =====
print("\n【22】文件列表")
for method in ['getFileList', 'getfilelist']:
try:
r = getattr(ex, method)(5)
rows = r.get('rows', [])
print(f" ✅ 文件({method}): {len(rows)}")
results['files'] = {'count': len(rows)}
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getfilelist':
print(f" ❌ 文件: {str(e)[:80]}")
errors.append(f"files: {str(e)[:80]}")
total_count += 1
# ===== 23. 小程序 =====
print("\n【23】小程序信息")
for method in ['getAppBrandList', 'getappbrandlist']:
try:
r = getattr(ex, method)()
print(f" ✅ 小程序({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['appbrand'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'getappbrandlist':
print(f" ❌ 小程序: {str(e)[:80]}")
errors.append(f"appbrand: {str(e)[:80]}")
total_count += 1
# ===== 24. 搜索 =====
print("\n【24】全局搜索")
for method in ['globalSearch', 'globalsearch']:
try:
r = getattr(ex, method)('游条姐')
print(f" ✅ 搜索({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['search'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'globalsearch':
print(f" ❌ 搜索: {str(e)[:80]}")
errors.append(f"search: {str(e)[:80]}")
total_count += 1
# ===== 25. 原始SQL =====
print("\n【25】原始SQL查询")
for method in ['rawQuery', 'rawquery']:
try:
r = getattr(ex, method)('main', 'SELECT type, COUNT(*) as cnt FROM rcontact GROUP BY type ORDER BY cnt DESC LIMIT 10')
print(f" ✅ SQL({method}): {json.dumps(r, ensure_ascii=False)[:300]}")
results['raw_sql'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'rawquery':
print(f" ❌ SQL: {str(e)[:80]}")
errors.append(f"raw_sql: {str(e)[:80]}")
total_count += 1
# ===== 26. 群公告 =====
print("\n【26】设置群公告测试写入接口")
if groups_data and len(groups_data) > 0:
chatroom_id = groups_data[0].get('username','')
for method in ['setGroupAnnouncement', 'setgroupannouncement']:
try:
r = getattr(ex, method)(chatroom_id, f'[SDK测试公告] {time.strftime("%H:%M:%S")}')
print(f" ✅ 群公告({method}): {json.dumps(r, ensure_ascii=False)[:200]}")
results['group_announcement'] = r
passed_count += 1
total_count += 1
break
except Exception as e:
if method == 'setgroupannouncement':
print(f" ❌ 群公告: {str(e)[:80]}")
errors.append(f"group_announcement: {str(e)[:80]}")
total_count += 1
else:
print(f" ⚠️ 没有群组数据")
# ===== 最终截图 =====
print("\n📸 最终截图...")
screenshot('final')
# ===== 汇总 =====
print("\n" + "=" * 70)
print(f"📊 验证结果: {passed_count}/{total_count} 通过 ({passed_count/total_count*100:.0f}%)")
print("=" * 70)
if errors:
print("\n❌ 失败项:")
for e in errors:
print(f" - {e}")
print("\n✅ 真实数据汇总:")
acc = results.get('account', {})
stats = results.get('stats', {})
cc = results.get('contact_count', {})
print(f" 📱 账号: {acc.get('nickname','?')} | wxid: {acc.get('wxid','?')}")
print(f" 👥 好友: {cc.get('friends', stats.get('friends','?'))} | 总联系人: {cc.get('total','?')}")
print(f" 💬 群组: {results.get('groups',{}).get('count', stats.get('groups','?'))}")
print(f" 🏷️ 标签: {results.get('labels',{}).get('count','?')}")
print(f" 📨 24h消息: {results.get('recent_messages',{}).get('count','?')}")
print(f" 🖼️ 图片: {stats.get('images','?')}")
print(f" 🎬 视频: {stats.get('videos','?')}")
print(f" 📁 文件: {stats.get('files','?')}")
print(f" ⭐ 收藏: {results.get('favorites',{}).get('count','?')}")
print(f" 💰 钱包流水: {results.get('wallet_ledger',{}).get('count','?')}")
print(f" 🧧 红包: {results.get('lucky_money',{}).get('count','?')}")
print(f" 📹 视频号联系人: {results.get('finder_contacts',{}).get('count','?')}")
print(f" 🔍 搜索结果: {results.get('search',{})}")
result_file = f'{SCREENSHOT_DIR}/v11_verify_results.json'
with open(result_file, 'w', encoding='utf-8') as f:
json.dump({
'timestamp': int(time.time()),
'time': time.strftime('%Y-%m-%d %H:%M:%S'),
'passed': passed_count,
'total': total_count,
'pass_rate': f"{passed_count/total_count*100:.0f}%",
'results': results,
'errors': errors
}, f, ensure_ascii=False, indent=2)
print(f"\n💾 结果已保存: {result_file}")
return passed_count, total_count
if __name__ == '__main__':
passed, total = main()
print(f"\n🎯 最终: {passed}/{total} ({passed/total*100:.0f}%)")
sys.exit(0 if passed >= total * 0.8 else 1)