记一次逆向

1.qingyou

内置了vm指令集,难度太高,AI分析了分析一下,放弃了

2.台服

没有成功

思路,跳窗口,但是hook登录抽离了游戏逻辑,不走登录 游戏崩溃

两个so

1.一个运行时解密

没分析

2.一个原SO

动态

这家伙全是花指令

用Frida hook了so

dex解密了

请求逆向

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
微验 API 测试工具 - Python版
"""

import hashlib
import requests
import time
import random
import json

# ============================================================
# RC4 加解密
# ============================================================
def rc4_crypt(key: str, data: bytes) -> bytes:
    """RC4 加解密 (加解密相同)"""
    s = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s[i] + ord(key[i % len(key)])) & 0xFF
        s[i], s[j] = s[j], s[i]
    i = j = 0
    result = bytearray()
    for byte in data:
        i = (i + 1) & 0xFF
        j = (j + s[i]) & 0xFF
        s[i], s[j] = s[j], s[i]
        result.append(byte ^ s[(s[i] + s[j]) & 0xFF])
    return bytes(result)


class WeiYan:
    """微验 API 客户端"""

    def __init__(self, host: str, appid: str, appkey: str, rc4key: str,
                 kami: str = "", imei: str = ""):
        self.host = host
        self.appid = appid
        self.appkey = appkey
        self.rc4key = rc4key
        self.kami = kami
        self.imei = imei

    def _encrypt(self, data: str) -> str:
        """RC4 加密 data,返回 hex 字符串"""
        raw = rc4_crypt(self.rc4key, data.encode())
        return raw.hex()

    def _decrypt(self, data: bytes) -> str:
        """RC4 解密响应"""
        return rc4_crypt(self.rc4key, data).decode(errors='replace')

    def _post(self, path: str, body: str) -> bytes:
        """发送 POST 请求,返回解码后的原始字节"""
        url = f"http://{self.host}/{path}"
        resp = requests.post(url, data=body.encode(),
                             headers={'Content-Type': 'application/x-www-form-urlencoded'},
                             timeout=10)
        text = resp.text.strip()
        # 服务器返回的是 hex 编码文本,先 hex 解码
        try:
            raw = bytes.fromhex(text)
            return raw
        except ValueError:
            # 不是 hex,可能是直接二进制或错误消息
            return resp.content

    # ========== 公开接口 ==========

    def login(self, endpoint: str = "kmlogon",
              kami: str = None, imei: str = None) -> dict:
        """
        登录请求
        endpoint: kmlogon(实际抓到的) / kmlogin(源码模板)
        返回: {raw_hex, raw_len, decrypted, json}
        """
        if kami is None: kami = self.kami
        if imei is None: imei = self.imei

        ts = int(time.time())
        rand_val = f"{ts}{random.randint(1000, 9999)}"

        # 签名: MD5("kami=...&markcode=...&t=...&appkey")
        sign_raw = f"kami={kami}&markcode={imei}&t={ts}&{self.appkey}"
        sign_md5 = hashlib.md5(sign_raw.encode()).hexdigest()

        # 数据拼接
        data_raw = f"kami={kami}&markcode={imei}&t={ts}&sign={sign_md5}&value={rand_val}"
        data_enc = self._encrypt(data_raw)

        body = f"data={data_enc}"
        path = f"api/?id={endpoint}&app={self.appid}"

        print(f"\n{'='*60}")
        print(f"[登录] {self.host}/{path}")
        print(f"[签名原文] {sign_raw}")
        print(f"[MD5签名]  {sign_md5}")
        print(f"[加密前]   {data_raw}")
        print(f"[加密后]   {data_enc[:40]}...")
        print(f"[请求体]   data={data_enc[:40]}...")

        path = f"api/?id={endpoint}&app={self.appid}"
        url = f"http://{self.host}/{path}"
        resp = requests.post(url, data=body.encode(),
                             headers={'Content-Type': 'application/x-www-form-urlencoded'},
                             timeout=10)
        text = resp.text.strip()
        print(f"[HTTP状态] {resp.status_code}")
        print(f"[响应原文] {text[:100]}")

        # hex 解码 → RC4 解密
        raw_bytes = bytes.fromhex(text) if all(c in '0123456789abcdefABCDEF' for c in text) else text.encode()
        print(f"[Hex解码后] {raw_bytes.hex()[:60]}...")
        print(f"[Hex后长度] {len(raw_bytes)} bytes")

        decrypted = self._decrypt(raw_bytes)

        try:
            j = json.loads(decrypted)
            print(f"[解密JSON] {json.dumps(j, indent=2, ensure_ascii=False)}")
        except:
            print(f"[解密文本] {decrypted[:500]}")
            j = None

        return {'de_json': j, 'decrypted': decrypted,
                'raw_hex': raw_bytes.hex(), 'raw_len': len(raw_bytes)}

    def notice(self) -> dict:
        """获取公告 - 请求明文,响应加密"""
        body = f"app={self.appid}"   # 源码中 _ggUrl 是明文!
        url = f"http://{self.host}/api/?id=notice"
        resp = requests.post(url, data=body.encode(),
                             headers={'Content-Type': 'application/x-www-form-urlencoded'},
                             timeout=10)
        text = resp.text.strip()
        print(f"\n[公告] 响应原文: {text[:100]}")

        # hex 解码 → RC4 解密
        if all(c in '0123456789abcdefABCDEF' for c in text):
            raw_bytes = bytes.fromhex(text)
            decrypted = self._decrypt(raw_bytes)
            print(f"[公告] Hex解码: {len(raw_bytes)} bytes")
        else:
            decrypted = self._decrypt(text.encode())

        print(f"[公告] 解密: {decrypted[:500]}")
        try:
            j = json.loads(decrypted)
            print(json.dumps(j, indent=2, ensure_ascii=False))
            return j
        except:
            return {'raw': decrypted}

    def custom(self, path: str, body: str) -> bytes:
        """自定义请求"""
        url = f"http://{self.host}/{path}"
        resp = requests.post(url, data=body.encode(),
                             headers={'Content-Type': 'application/x-www-form-urlencoded'},
                             timeout=10)
        text = resp.text.strip()
        raw_bytes = bytes.fromhex(text) if all(c in '0123456789abcdefABCDEF' for c in text) else text.encode()
        print(f"[自定义] {path}")
        print(f"[响应] {len(raw_bytes)} bytes: {raw_bytes[:200]}")
        return raw_bytes


def analyze_response(response):
    """分析微验响应结构"""
    print("\n" + "="*60)
    print("响应分析:")
    print("="*60)
    
    if 'de_json' in response and response['de_json']:
        j = response['de_json']
        print(f"状态码: {j.get('code', 'N/A')}")
        print(f"时间戳: {j.get('time', 'N/A')}")
        print(f"Check值: {j.get('check', 'N/A')}")
        
        if 'msg' in j:
            msg = j['msg']
            print("\n消息内容:")
            print(f"  ID: {msg.get('id', 'N/A')}")
            print(f"  Token: {msg.get('token', 'N/A')}")
            print(f"  卡密类型: {msg.get('kmtype', 'N/A')}")
            print(f"  Key类型: {msg.get('ktype', 'N/A')}")
            print(f"  VIP: {msg.get('vip', 'N/A')}")
            print(f"  备注: {msg.get('note', 'N/A')}")
    
    print(f"\n响应原始长度: {response.get('raw_len', 'N/A')} bytes")
    
    # 检查 check 字段的格式
    check = j.get('check', '') if j else ''
    if check:
        print(f"\nCheck 字段分析:")
        print(f"  长度: {len(check)} 字符")
        print(f"  类型: {'MD5' if len(check) == 32 else 'SHA1' if len(check) == 40 else '未知'}")
        
        # 尝试分析 check 的生成方式
        if len(check) == 32:
            # 可能是 MD5
            import hashlib
            msg = j.get('msg', {})
            test_strings = [
                f"{APPID}{APPKEY}",
                f"{APPKEY}{APPID}",
                f"{j.get('time', '')}{APPKEY}",
                f"{j.get('time', '')}{APPID}",
                f"{APPID}{j.get('time', '')}{APPKEY}",
                f"{KAMI}{IMEI}{j.get('time', '')}",
                # 包含响应中的字段
                f"{APPKEY}{j.get('time', '')}",
                f"{j.get('time', '')}{APPKEY}{APPID}",
                f"{APPID}{KAMI}{IMEI}",
                f"{KAMI}{APPKEY}",
                f"{IMEI}{APPKEY}",
                # 包含 msg 字段
                f"{msg.get('id', '')}{APPKEY}",
                f"{msg.get('token', '')}{APPKEY}",
                f"{msg.get('id', '')}{msg.get('token', '')}",
                f"{j.get('time', '')}{msg.get('id', '')}{msg.get('token', '')}",
                f"{APPKEY}{j.get('time', '')}{msg.get('id', '')}",
                f"{APPID}{j.get('time', '')}{msg.get('id', '')}{APPKEY}",
                # 带分隔符
                f"{APPID}&{APPKEY}&{j.get('time', '')}",
                f"{j.get('time', '')}|{APPKEY}|{APPID}",
                # 小写
                f"{APPID}{APPKEY}".lower(),
                f"{APPKEY}{j.get('time', '')}".lower(),
            ]
            print("\n  尝试匹配 check 值:")
            for s in test_strings:
                md5 = hashlib.md5(s.encode()).hexdigest()
                if md5 == check:
                    print(f"    ✓ 匹配: {s}")
                else:
                    print(f"    ✗ {s}")
            
            # 尝试组合多个字段
            print("\n  尝试更复杂的组合:")
            complex_tests = [
                # 响应字段组合
                f"{msg.get('id', '')}{msg.get('token', '')}{msg.get('kmtype', '')}{msg.get('ktype', '')}",
                f"{j.get('code', '')}{j.get('time', '')}{msg.get('id', '')}{msg.get('token', '')}",
                f"{KAMI}{IMEI}{msg.get('id', '')}{j.get('time', '')}",
                # 包含所有信息
                f"{APPID}{KAMI}{IMEI}{j.get('time', '')}{APPKEY}",
                f"{KAMI}{IMEI}{APPKEY}{j.get('time', '')}{msg.get('id', '')}",
                # 包含 code 的组合
                f"{j.get('code', '')}{APPKEY}",
                f"{APPKEY}{j.get('code', '')}",
                f"{j.get('code', '')}{j.get('time', '')}{APPKEY}",
                f"{APPKEY}{j.get('code', '')}{j.get('time', '')}",
                f"{APPID}{j.get('code', '')}{APPKEY}",
                f"{j.get('code', '')}{APPID}{APPKEY}",
                f"{msg.get('id', '')}{j.get('code', '')}{APPKEY}",
                f"{j.get('code', '')}{msg.get('id', '')}{msg.get('token', '')}{APPKEY}",
                # 完整响应 JSON 序列化后 MD5
            ]
            for s in complex_tests:
                md5 = hashlib.md5(s.encode()).hexdigest()
                if md5 == check:
                    print(f"    ✓ 匹配: {s}")
                else:
                    print(f"    ✗ {s}")
            
            # 尝试完整JSON的MD5
            print("\n  尝试完整响应JSON的MD5:")
            full_json = json.dumps(j, separators=(',', ':'))
            md5_full = hashlib.md5(full_json.encode()).hexdigest()
            if md5_full == check:
                print(f"    ✓ 匹配: full_json")
            else:
                print(f"    ✗ full_json: {md5_full}")
            
            # 尝试去掉check字段后的JSON MD5
            j_no_check = j.copy()
            if 'check' in j_no_check:
                del j_no_check['check']
            full_json_no_check = json.dumps(j_no_check, separators=(',', ':'))
            md5_no_check = hashlib.md5(full_json_no_check.encode()).hexdigest()
            if md5_no_check == check:
                print(f"    ✓ 匹配: full_json_no_check")
            else:
                print(f"    ✗ full_json_no_check: {md5_no_check}")
            
            # 尝试包含原始响应数据
            if 'decrypted' in response:
                md5_raw = hashlib.md5(response['decrypted'].encode()).hexdigest()
                if md5_raw == check:
                    print(f"    ✓ 匹配: decrypted_response")
                else:
                    print(f"    ✗ decrypted_response: {md5_raw}")

if __name__ == "__main__":
    import sys

    # 默认配置 (从 SO 逆向获取)
    HOST = "wy.llua.cn"
    APPID = " "
    APPKEY = ""    # 填入从 SO 逆出的 AppKey
    RC4KEY = ""    # 填入从 SO 逆出的 RC4 Key
    KAMI = ""      # 填入卡密
    IMEI = ""      # 填入机器码

    wy = WeiYan(HOST, APPID, APPKEY, RC4KEY, KAMI, IMEI)

    if len(sys.argv) > 1:
        cmd = sys.argv[1]
        if cmd == "login":
            ep = sys.argv[2] if len(sys.argv) > 2 else "kmlogon"
            result = wy.login(ep)
            analyze_response(result)
        elif cmd == "notice":
            wy.notice()
        elif cmd == "custom":
            path = sys.argv[2]
            body = sys.argv[3] if len(sys.argv) > 3 else ""
            wy.custom(path, body)
        elif cmd == "analyze":
            # 模拟分析成功响应
            mock_response = {
                'de_json': {
                    'code': 2100,
                    'msg': {
                        'id': 11102437,
                        'kmtype': 'year',
                        'token': '72UZW',
                        'note': '',
                        'ktype': 'code',
                        'vip': 1812197817
                    },
                    'time': 1780663631,
                    'check': 'f200d0dfb83b3e2da14afc39e325ef07'
                },
                'raw_len': 169
            }
            analyze_response(mock_response)
        else:
            print(f"未知命令: {cmd}")
    else:
        # 交互模式
        print("=" * 60)
        print("  微验 API 测试工具 (Python)")
        print("=" * 60)
        print(f"  Host: {HOST}")
        print(f"  AppID: {APPID}")
        print()

        while True:
            try:
                cmd = input(">>> ").strip()
                if not cmd:
                    result = wy.login()
                    analyze_response(result)
                elif cmd == "q":
                    break
                elif cmd == "login":
                    result = wy.login()
                    analyze_response(result)
                elif cmd == "kmlogon":
                    result = wy.login("kmlogon")
                    analyze_response(result)
                elif cmd == "kmlogin":
                    result = wy.login("kmlogin")
                    analyze_response(result)
                elif cmd == "notice":
                    wy.notice()
                elif cmd.startswith("custom "):
                    parts = cmd.split(None, 2)
                    path = parts[1]
                    body = parts[2] if len(parts) > 2 else ""
                    wy.custom(path, body)
                elif cmd == "analyze":
                    mock_response = {
                        'de_json': {
                            'code': 2100,
                            'msg': {
                                'id': 11102437,
                                'kmtype': 'year',
                                'token': '72UZW',
                                'note': '',
                                'ktype': 'code',
                                'vip': 1812197817
                            },
                            'time': 1780663631,
                            'check': 'f200d0dfb83b3e2da14afc39e325ef07'
                        },
                        'raw_len': 169
                    }
                    analyze_response(mock_response)
                elif cmd == "help":
                    print("  login / kmlogon / kmlogin - 登录")
                    print("  notice - 公告")
                    print("  custom <path> [body] - 自定义")
                    print("  analyze - 分析响应结构")
                    print("  q - 退出")
                else:
                    print("  输入 help 查看命令")
            except KeyboardInterrupt:
                break
            except Exception as e:
                print(f"  错误: {e}")

难度SO加密了

appid只能frida修改

(软件有防VPN) 就用的这个软件

问题来了,Code再so里面,并且静态hook不到,frida也没特征地点,于是,只能放弃

但是可以通过so静态找到偏移地点,然后frida进行修改

例如 直接修改appid

获取code

就可以了

相关推荐
凯瑟琳.奥古斯特1 小时前
力扣1002题C++解法详解
开发语言·c++·算法·leetcode·职场和发展
钟灵9211 小时前
C++【模板初阶】
开发语言·c++·笔记·c#
码不停蹄的玄黓1 小时前
旁路缓存(Cache-Aside,CA)
java·开发语言
江屿风1 小时前
【C++笔记】vector流食般投喂
开发语言·c++·笔记
星恒随风1 小时前
Python 基础语法详解(3):顺序语句、条件语句和循环语句一篇讲清楚
开发语言·笔记·python·学习
CHHH_HHH1 小时前
【C++】红黑树:比AVL树更实用的平衡二叉搜索树
开发语言·数据结构·c++·算法·stl
凤头百灵鸟1 小时前
Python语法进阶篇 --- re库、os库、sys库、time库、logging库、random库
python
牛油果子哥q1 小时前
【C++内存对齐与结构体填充】C++内存对齐与结构体填充深度精讲:对齐规则、结构体内存大小计算、填充冗余、笔试真题与工程优化方案
开发语言·c++
ch.ju1 小时前
Java程序设计(第3版)第四章——set-get方法
java·开发语言