• Sophomores

    https://linux.do/t/topic/2785654

    书接上文

    昨天晚上无意间跑通的, 刚刚在mumu复现了几次, 自己订购了几个玩玩, 目前还在测试存活时间, 不确定是否会封号, 谨慎使用.

    图片

    原理

    Play给grok.ultra下发了几档offer,同一份 ProductDetails 里就有:

    offerId |
    tag |
    价格 |

    heavy-p3m-30-may2026 |
    discount |
    $99.99/月 ×3,然后 $299.99 |

    heavy-p3m-30-churn-mar2026 |
    pay1-get2free |
    $299.99 / 3月 (1付3) |

    空 offerId |
    无 |
    $299.99/月 (CTA默认) |

    Grok点「Upgrade to SuperGrok Heavy」时,默认把空 offerId 的基价 p1m token塞进SKU_OFFER_ID_TOKEN_LIST,所以无 hook 时墙是 Starting today $299.99。

    Frida 在 getBuyIntentExtraParams 对应的混淆方法里把 list 换成 heavy-p3m-30-may2026 的活 offerIdToken,Play 就按 intro 出墙。

    无 token 的官方 self-replace proto 会 unable to change plan,那条是死路。

    环境

    - MuMu Android 12,1080×1920

    - Google 已登录 + Grok 登录

    - FClash tun,GLOBAL 美国节点。

    - gsm.operator.iso-country=us

    - Private DNS = off

    - Grok 可 sideload。必须 pm disable ai.x.grok/com.pairip.licensecheck.LicenseActivity,否则约 5s 被打去 Play「Get this app from Play」

    - 设备 frida-server 与 host Frida 同一大版本 (本文 17.15.5)。MuMu 上常见 /data/local/tmp/fs17

    出口自检 (Play uid 以机器为准,常见 10045):```
    adb shell settings put global private_dns_mode off

    必须 US。不要用 su 0 curl

    adb shell su 0 sh -c 'su 10045 /system/bin/sh -c "curl -s --connect-timeout 8 http://208.95.112.1/json"'
    ``应用 uid 若 curl: (6) Could not resolve host`,目录会永远转圈,没有 SuperGrok/Plus/Heavy 价格 tab。先修 DNS,再谈 hook。

    Hook点

    Grok 1.2.19 混淆后:```
    com.google.android.gms.internal.play_billing.d.d2(
    int, String, String, String, android.os.Bundle)
    ```sku == grok.ultra 时:```
    extra.putStringArrayList("SKU_OFFER_ID_TOKEN_LIST", [introOfferIdToken])
    ``活 token 从 ProductDetails 包装类 mv.s 里读: b() 是 productId,字段 a / f42182a 是原始 JSON,选 offerId=heavy-p3m-30-may2026 的 offerIdToken`。

    禁止在 d.d2 里 Java.choose。 heap walk 拖死 binder,Play 报 Unable to buy item, Error response code: -1。pick 只能放在 CTA 前 (脚本里 delay 10s)。

    避免出现订阅闪退

    sideload Grok disable 掉 PairIP LicenseActivity 之后有两条杀进程,只拦 System.exit 不够:

    • 约 30s: LicenseClient.startPaywallActivity 因 Activity 被 disable 失败,然后 Java System.exit(0)。日志:```

    ai.x.grok: System.exit called, status: 0
    VM exiting with result code 0
    WIN DEATH: .../ProxyBillingActivity
    ```必须同时拦:

    - java.lang.System.exit

    - java.lang.Runtime.exit / halt

    - com.android.billingclient.api.ProxyBillingActivity.finish (只拦这一个。全局拦 Activity.finish 会让第二次 billing 起不来)

    • 更久 (实测约 25 分钟): licensing 服务断开:```

    LicenseClient.onServiceDisconnected
    → retryOrThrow → handleError → startErrorDialogActivity
    ActivityNotFoundException: LicenseActivity
    FATAL EXCEPTION: main
    ``必须 no-op startErrorDialogActivity / startPaywallActivity / handleError,并吞掉未捕获异常里带 LicenseActivity / pairip` 的。03:18 出墙、03:20 还在,是因为当时还没走到这条;保活进程后来就是这样 FATAL 的。

    还有几个死法:

    - Frida 17 的 stdin 一 EOF,session 就没了。挂 hook 的进程 stdin 要保持打开,不要 frida -q,不要脚本跑完就退。

    - CTA 后别 uiautomator dump,会 Purchase cancelled。只 screencap。

    - 别在 d.d2 里 Java.choose。

    - 别 no-op Process.killProcess 当保活,Grok 会坏。

    - Close 是 content-desc,不是 text。按 text=Close 永远点不到。

    步骤```

    0. frida-server,和 host 同版本

    adb shell su 0 sh -c 'setsid /data/local/tmp/fs17 -D >/data/local/tmp/fs17.log 2>&1 < /dev/null &'
    frida --version # 17.15.5

    1. 禁 PairIP + 关 Private DNS

    adb shell su 0 pm disable ai.x.grok/com.pairip.licensecheck.LicenseActivity
    adb shell settings put global package_verifier_enable 0
    adb shell settings put global private_dns_mode off

    2. 起 Grok attach hook(这条 frida 进程别退,stdin 别关)

    adb shell am force-stop ai.x.grok
    adb shell am start -n ai.x.grok/.main.GrokActivity
    PID=$(adb shell pidof ai.x.grok)
    frida -D $SERIAL -p $PID -l inject_p3m99.js

    3. 等日志 READY tap Heavy CTA

    4. 点 Try SuperGrok

    5. 等出 SuperGrok / Plus / Heavy 和美元价格(别在转圈/skeleton 时点)

    6. Heavy → Upgrade to SuperGrok Heavy

    7. 停手。只截图,不 dump,不 BACK,不 Subscribe

    ```1080×1920 备用坐标 (仍以 CTA 前 dump 活节点为准):

    控件 |
    坐标 |

    Try SuperGrok |
    289, 1547 |

    Heavy tab |
    737, 534 |

    Upgrade to SuperGrok Heavy |
    540, 1761 |

    日志要对:```
    ★SWAPPED src=live|fallback heavy-p3m-30-may2026
    block System.exit 0
    ```墙要对: SuperGrok Heavy + Starting today $99.99/month + 约 +3 个月 $299.99/month。没有 Get this app from Play。

    Frida脚本 inject_p3m99.js```
    'use strict';
    function log(m) { send({ t: 'log', m: String(m) }); }

    var WANT = 'heavy-p3m-30-may2026';
    var FALLBACK = 'AXW7mMuPIEozWT2ukTJE+PRd7/l51pBgfomUPEBu0rukdzfryp0nwkafBsOKevhe/JrwIjnJ0MZi0Wjwzb+9D1iC5RpfT3Gcbie/PHXhf6Sg/JoK';
    var liveToken = null;

    function field(obj, names) {
    var cls = obj.getClass();
    while (cls !== null) {
    var flds = cls.getDeclaredFields();
    for (var i = 0; i < flds.length; i++) {
    var n = flds[i].getName();
    for (var j = 0; j < names.length; j++) {
    if (n === names[j]) {
    flds[i].setAccessible(true);
    return flds[i].get(obj);
    }
    }
    }
    cls = cls.getSuperclass();
    }
    return null;
    }

    function pickUltraOffer() {
    var JSONObject = Java.use('org.json.JSONObject');
    var found = null;
    Java.choose('mv.s', {
    onMatch: function (inst) {
    if (found) return;
    var pid = '';
    try { pid = String(inst.b()); } catch (e0) {}
    if (pid !== 'grok.ultra') return;
    var raw = '';
    try { raw = String(field(inst, ['a', 'f42182a'])); } catch (e1) {}
    if (!raw) return;
    try {
    var jo = JSONObject.$new(raw);
    var arr = jo.optJSONArray('subscriptionOfferDetails');
    if (!arr) return;
    for (var i = 0; i < arr.length(); i++) {
    var o = arr.getJSONObject(i);
    var oid = o.optString('offerId', '');
    var tok = o.optString('offerIdToken', '');
    log('[p3m99] ultra offerId=' + oid + ' tokenLen=' + (tok ? tok.length : 0));
    if (oid === WANT && tok) found = tok;
    }
    } catch (e2) {
    log('[p3m99] parse ' + e2);
    }
    },
    onComplete: function () {}
    });
    return found;
    }

    Java.perform(function () {
    log('[p3m99] start ' + WANT);
    try {
    var d = Java.use('com.google.android.gms.internal.play_billing.d');
    var ov = d.d2.overload('int', 'java.lang.String', 'java.lang.String', 'java.lang.String', 'android.os.Bundle');
    ov.implementation = function (api, pkg, sku, type, extra) {
    try {
    var tok = liveToken || FALLBACK;
    if (String(sku) === 'grok.ultra' && extra && tok) {
    var ArrayList = Java.use('java.util.ArrayList');
    var list = ArrayList.$new();
    list.add(tok);
    extra.putStringArrayList('SKU_OFFER_ID_TOKEN_LIST', list);
    log('[p3m99] ★SWAPPED src=' + (liveToken ? 'live' : 'fallback') + ' ' + WANT);
    }
    } catch (eD) {
    log('[p3m99] d2 wrap ' + eD);
    }
    return ov.call(this, api, pkg, sku, type, extra);
    };
    log('[p3m99] hooked d.d2');
    } catch (e) {
    log('[p3m99] d2 fail ' + e);
    }
    try {
    var Sys = Java.use('java.lang.System');
    Sys.exit.implementation = function (code) {
    log('[p3m99] block System.exit ' + code);
    };
    } catch (eS) {}
    try {
    var RT = Java.use('java.lang.Runtime');
    RT.exit.implementation = function (code) { log('[p3m99] block Runtime.exit ' + code); };
    RT.halt.implementation = function (code) { log('[p3m99] block Runtime.halt ' + code); };
    } catch (eR) {}
    try {
    var PBA = Java.use('com.android.billingclient.api.ProxyBillingActivity');
    PBA.finish.overload().implementation = function () {
    log('[p3m99] block ProxyBillingActivity.finish');
    };
    } catch (eP) {}
    try {
    var LC = Java.use('com.pairip.licensecheck.LicenseClient');
    var names = ['startErrorDialogActivity', 'startPaywallActivity', 'handleError'];
    for (var ni = 0; ni < names.length; ni++) {
    (function (name) {
    if (!LC[name]) return;
    var ovs = LC[name].overloads;
    for (var oi = 0; oi < ovs.length; oi++) {
    ovs[oi].implementation = function () {
    log('[p3m99] block LicenseClient.' + name);
    };
    }
    })(names[ni]);
    }
    } catch (eL) {}
    try {
    var Thread = Java.use('java.lang.Thread');
    var Handler = Java.use('java.lang.Thread$UncaughtExceptionHandler');
    var prev = Thread.getDefaultUncaughtExceptionHandler();
    var Wrap = Java.registerClass({
    name: 'x.p3m99.Ueh',
    implements: [Handler],
    methods: {
    uncaughtException: function (t, e) {
    var s = String(e);
    if (s.indexOf('LicenseActivity') !== -1 || s.indexOf('pairip') !== -1) {
    log('[p3m99] swallow ' + s.substring(0, 160));
    return;
    }
    if (prev) prev.uncaughtException(t, e);
    }
    }
    });
    Thread.setDefaultUncaughtExceptionHandler(Wrap.$new());
    } catch (eU) {}
    log('[p3m99] READY tap Heavy CTA');
    });

    setTimeout(function () {
    Java.perform(function () {
    try {
    var t = pickUltraOffer();
    if (t) liveToken = t;
    } catch (e) {}
    });
    }, 10000);
    ``CTA 后不再 dump。父进程 sleep 保 Frida stdin。需要 ANDROID_SERIAL,inject_p3m99.js 和本脚本放同一目录。``
    #!/usr/bin/env python3
    from __future__ import annotations
    import os, re, signal, subprocess, time
    from pathlib import Path

    os.environ.setdefault("ADB_SERVER_SOCKET", "tcp:127.0.0.1:5038")
    ADB = os.environ.get("ADB", "adb")
    HERE = Path(__file__).resolve().parent
    CACHE = Path.home() / ".cache/grok-heavy-offer"
    SCRIPT = HERE / "inject_p3m99.js"
    LICENSE = "ai.x.grok/com.pairip.licensecheck.LicenseActivity"

    def serial() -> str:
    s = os.environ.get("ANDROID_SERIAL") or os.environ.get("MUMU_SERIAL")
    if not s:
    raise SystemExit("ANDROID_SERIAL required")
    return s

    def adb(ser, *a, timeout=20):
    r = subprocess.run([ADB, "-s", ser, *a], capture_output=True, timeout=timeout)
    return (r.stdout or b"").decode("utf-8", "replace")

    def grok_pid(ser):
    return (adb(ser, "shell", "pidof", "ai.x.grok").strip().split() or [""])[0]

    def dump_xml(ser):
    try:
    subprocess.run([ADB, "-s", ser, "shell", "uiautomator", "dump", "/sdcard/_p3m99.xml"], timeout=8, capture_output=True)
    dest = CACHE / "ui.xml"
    subprocess.run([ADB, "-s", ser, "pull", "/sdcard/_p3m99.xml", str(dest)], timeout=8, capture_output=True)
    return dest.read_text(encoding="utf-8", errors="replace") if dest.exists() else ""
    except Exception as e:
    return f"ERR {e}"

    def nodes(xml):
    out = []
    for m in re.finditer(r"<node [^>]*>", xml):
    s = m.group(0)
    def g(k, s=s):
    mm = re.search(k + r'="([^"]*)"', s)
    return mm.group(1) if mm else ""
    b = g("bounds")
    mm = re.match(r"\[(\d+),(\d+)\]\[(\d+),(\d+)\]", b)
    cx = cy = y1 = 0
    if mm:
    x1, y1, x2, y2 = map(int, mm.groups())
    cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
    out.append({"text": g("text"), "cx": cx, "cy": cy, "y1": y1})
    return out

    def find_text(xml, label, y_lt=None, exact=False):
    for n in nodes(xml):
    t = (n["text"] or "").strip()
    if exact and t != label:
    continue
    if not exact and label not in t:
    continue
    if y_lt is not None and n["y1"] >= y_lt:
    continue
    return n
    return None

    def tap(ser, x, y, wait=1.0):
    adb(ser, "shell", "input", "tap", str(x), str(y))
    time.sleep(wait)

    def main():
    signal.signal(signal.SIGHUP, signal.SIG_IGN)
    ser = serial()
    CACHE.mkdir(parents=True, exist_ok=True)
    log_path = CACHE / "stay_wall.log"
    adb(ser, "shell", "su", "0", "pm", "disable", LICENSE)
    adb(ser, "shell", "settings", "put", "global", "package_verifier_enable", "0")
    adb(ser, "shell", "settings", "put", "global", "private_dns_mode", "off")
    adb(ser, "shell", "am", "force-stop", "ai.x.grok")
    time.sleep(0.5)
    adb(ser, "shell", "am", "start", "-n", "ai.x.grok/.main.GrokActivity")
    pid = ""
    for _ in range(24):
    time.sleep(0.35)
    pid = grok_pid(ser)
    if pid:
    break
    if not pid:
    return 2
    logf = open(log_path, "w", buffering=1)
    proc = subprocess.Popen(
    ["frida", "-D", ser, "-p", pid, "-l", str(SCRIPT)],
    stdin=subprocess.PIPE, stdout=logf, stderr=subprocess.STDOUT, start_new_session=True,
    )
    t0 = time.time()
    while time.time() - t0 < 22:
    txt = log_path.read_text(errors="replace")
    if "READY tap Heavy CTA" in txt:
    break
    if proc.poll() is not None:
    return 3
    time.sleep(0.25)
    else:
    return 3
    for i in range(10):
    time.sleep(0.5)
    xml = dump_xml(ser)
    hit = find_text(xml, "Try SuperGrok")
    if hit:
    tap(ser, hit["cx"], hit["cy"], 2.0)
    break
    else:
    tap(ser, 289, 1547, 2.0)
    xml = ""
    for i in range(12):
    time.sleep(1.4)
    xml = dump_xml(ser)
    heavy = find_text(xml, "Heavy", y_lt=900, exact=True)
    plus = find_text(xml, "Plus", exact=True)
    blob = " ".join(n["text"] for n in nodes(xml))
    if heavy and plus and ("$" in blob or "299" in blob):
    break
    else:
    return 5
    heavy = find_text(xml, "Heavy", y_lt=900, exact=True)
    tap(ser, heavy["cx"] if heavy else 737, heavy["cy"] if heavy else 534, 1.8)
    xml = dump_xml(ser)
    cta = None
    for n in nodes(xml):
    if "upgrade to supergrok heavy" in (n["text"] or "").lower():
    cta = n
    break
    tap(ser, cta["cx"] if cta else 540, cta["cy"] if cta else 1761, 0.4)
    time.sleep(3.0)
    subprocess.run([ADB, "-s", ser, "exec-out", "screencap", "-p"], capture_output=True, timeout=20)
    while proc.poll() is None:
    time.sleep(15)
    return proc.returncode or 0

    if __name__ == "__main__":
    raise SystemExit(main())
    ``FClash Android VPN 的 DNS 是 172.19.0.2。生成 yaml 里经常 dns.listen: ""`。再把 OUTPUT 53 REDIRECT 到 7874 且 7874 没人听 = 所有 hostname 失败。

    设备上跑 UDP 转发听 0.0.0.0:7874,上游 8.8.8.8 / 1.1.1.1 / 223.5.5.5。iptables 必须先 RETURN 这几个上游,再 REDIRECT 其余 53,否则转发自己打回环。

    dnsfwd.c (aarch64 无 libc):```
    / UDP DNS 0.0.0.0:7874 -> 8.8.8.8 / 1.1.1.1 / 223.5.5.5 /
    enum {
    SYS_write = 64, SYS_close = 57, SYS_exit = 93, SYS_socket = 198, SYS_bind = 200,
    SYS_setsockopt = 208, SYS_sendto = 206, SYS_recvfrom = 207, SYS_fcntl = 25,
    SYS_clock_nanosleep = 115
    };
    static long sc(long n, long a, long b, long c, long d, long e, long f) {
    register long x8 asm("x8") = n;
    register long x0 asm("x0") = a;
    register long x1 asm("x1") = b;
    register long x2 asm("x2") = c;
    register long x3 asm("x3") = d;
    register long x4 asm("x4") = e;
    register long x5 asm("x5") = f;
    asm volatile("svc #0" : "+r"(x0) : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) : "memory");
    return x0;
    }
    struct sockaddr_in {
    unsigned short sin_family;
    unsigned short sin_port;
    unsigned int sin_addr;
    unsigned char sin_zero[8];
    };
    static unsigned short b2(unsigned short x) { return (unsigned short)((x << 8) | (x >> 8)); }
    static unsigned int ip4(unsigned a, unsigned b, unsigned c, unsigned d) {
    return a | (b << 8) | (c << 16) | (d << 24);
    }
    static void wr(const char *s) {
    long n = 0; while (s[n]) n++;
    sc(SYS_write, 1, (long)s, n, 0, 0, 0);
    }
    static void nap_ms(long ms) {
    struct { long sec; long nsec; } ts;
    ts.sec = ms / 1000;
    ts.nsec = (ms % 1000) * 1000000L;
    sc(SYS_clock_nanosleep, 1, 0, (long)&ts, 0, 0, 0);
    }
    void _start(void) {
    int fd = (int)sc(SYS_socket, 2, 2, 17, 0, 0, 0);
    if (fd < 0) { wr("socket fail\n"); sc(SYS_exit, 1, 0, 0, 0, 0, 0); }
    int one = 1;
    sc(SYS_setsockopt, fd, 1, 2, (long)&one, 4, 0);
    struct sockaddr_in loc; long i;
    for (i = 0; i < (long)sizeof(loc); i++) ((char *)&loc)[i] = 0;
    loc.sin_family = 2; loc.sin_port = b2(7874); loc.sin_addr = 0;
    if (sc(SYS_bind, fd, (long)&loc, 16, 0, 0, 0) < 0) {
    wr("bind 7874 fail\n"); sc(SYS_exit, 2, 0, 0, 0, 0, 0);
    }
    unsigned int ups[3] = { ip4(8,8,8,8), ip4(1,1,1,1), ip4(223,5,5,5) };
    char buf[2048];
    for (;;) {
    struct sockaddr_in cli;
    for (i = 0; i < (long)sizeof(cli); i++) ((char *)&cli)[i] = 0;
    int clen = 16;
    long n = sc(SYS_recvfrom, fd, (long)buf, 2048, 0, (long)&cli, (long)&clen);
    if (n <= 0) continue;
    int ufd = (int)sc(SYS_socket, 2, 2, 17, 0, 0, 0);
    if (ufd < 0) continue;
    sc(SYS_fcntl, ufd, 4, 2048, 0, 0, 0);
    long got = -1; int u;
    for (u = 0; u < 3 && got <= 0; u++) {
    struct sockaddr_in up;
    for (i = 0; i < (long)sizeof(up); i++) ((char *)&up)[i] = 0;
    up.sin_family = 2; up.sin_port = b2(53); up.sin_addr = ups[u];
    sc(SYS_sendto, ufd, (long)buf, n, 0, (long)&up, 16);
    int t;
    for (t = 0; t < 40 && got <= 0; t++) {
    struct sockaddr_in from; int flen = 16;
    got = sc(SYS_recvfrom, ufd, (long)buf, 2048, 0, (long)&from, (long)&flen);
    if (got > 0) break;
    nap_ms(50);
    }
    }
    sc(SYS_close, ufd, 0, 0, 0, 0, 0);
    if (got > 0) sc(SYS_sendto, fd, (long)buf, got, 0, (long)&cli, clen > 0 ? clen : 16);
    }
    }
    ``编译推送:``
    aarch64-elf-gcc -nostdlib -static -Os -fno-builtin -fno-stack-protector -o dnsfwd dnsfwd.c
    adb push dnsfwd /data/local/tmp/dnsfwd
    adb shell su 0 chmod 755 /data/local/tmp/dnsfwd
    adb shell su 0 sh -c 'setsid /data/local/tmp/dnsfwd >/data/local/tmp/dnsfwd.log 2>&1 < /dev/null &'
    ``iptables (设备上 su 0):``
    iptables -t nat -F OUTPUT
    iptables -t nat -A OUTPUT -p udp --dport 53 -d 8.8.8.8 -j RETURN
    iptables -t nat -A OUTPUT -p udp --dport 53 -d 1.1.1.1 -j RETURN
    iptables -t nat -A OUTPUT -p udp --dport 53 -d 223.5.5.5 -j RETURN
    iptables -t nat -A OUTPUT -p tcp --dport 53 -d 8.8.8.8 -j RETURN
    iptables -t nat -A OUTPUT -p tcp --dport 53 -d 1.1.1.1 -j RETURN
    iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 7874
    iptables -t nat -A OUTPUT -p tcp --dport 53 -j REDIRECT --to-ports 7874

    主楼
  • llmw

    看上去操作好像有点复杂呀。目前不是刚需,不弄了。

    #1
  • baoge418

    我确实有点看不懂,头大了,太复杂了

    #2
  • apparition

    看着手里的两个 supergrok

    算了算了,这次先不跟 ^-^

    #3
  • russell-linuxdo

    这个复杂程度高的有点狠了,弄不来,而且这种渠道的应该活不长吧

    #4
  • A2401314

    两位逆向技术大神的研究,强行把已下架的优惠码用android硬塞进去,长知识了!S佬什么时候更新下GA的工作流呀,期待学习

    #5
  • 晦涩男董

    持续关注, 坐等下周, 跪求佬 来个号~

    #6
  • 嘴硬

    佬还是一如既往的权威,刚睡醒又来波大的

    #7
  • QQbot

    这个还能卡一年吗?谷歌play申诉退款容易吗

    #8
  • pangtl

    有点厉害了,老马还是差了一点儿,不如直接放开这个优惠吧,普惠大众

    #9
  • 长夜

    这个本质其实算漏洞攻击,而不算薅羊毛了。做生意或者研究着玩的土豪的可以。自用号还是悠着点,别搞,牢马封得你们飞起来。笑死。


    额外,大佬逆向牛皮(超大声)!~

    #10
  • 铁傀儡

    很详细啊佬,厉害了,就是怕等下秋后算账钱不给退了

    图片

    #11
  • ruyu

    太细了大佬,去公司就让gpt给我搞起来。先冲个99看看老马反应

    #12
  • 轨迹

    佬,这是真牛逼。。。。怎么研究出来的?

    #13
  • ccicc

    太棒了,收藏了准备后面去试一下看看行不行

    #14
  • hui_xu

    google play现在就是畜生,只要过他手的钱,咋都不退

    #15
  • jiejiejing

    佬真是手把手教学了,真是太细节了,佬的逆向太牛了。

    #16
  • Wengfb

    看不懂看不懂,只知道佬的逆向太牛了

    #17
  • sereno

    nb, 菜鸡投来羡慕的目光

    还得是你啊佬

    #18
  • starlight312

    现在还送cursor ultra吗

    #19

发表回复

登录后回复