Featured image of post The Gray Tech Chain Behind Cheap Food-Delivery Coupons: From Packet Capture to Bulk Claiming

The Gray Tech Chain Behind Cheap Food-Delivery Coupons: From Packet Capture to Bulk Claiming

The technical pipeline behind discount coupon reselling on secondhand marketplaces — SSL pinning bypass, API reverse engineering, signature cracking, and automated claiming scripts, unpacked layer by layer.

Search for “Burger King,” “Pizza Hut,” or “McDonald’s” on Xianyu (China’s biggest secondhand marketplace) and you’ll find piles of meal deals well below official prices — a Burger King signature 8-piece combo coupon for ¥39.9, a Pizza Hut two-pizza coupon for ¥26.4. Sellers note “confirm your phone number before ordering,” “14-day validity,” “instant delivery,” and “one order per purchase, repeatable.”

Where do these coupons come from? It’s not a simple question. The answer is a complete technical chain running from network packet capture through API reverse engineering to automated coupon claiming.

This article is for technical learning and security research only. Bulk-claiming and reselling coupons violates platform terms and may breach anti-unfair-competition law. All interfaces and parameters shown here are sanitized; no ready-to-run claiming code is provided.

1. Three Supply Channels

Before the tech, understand the market’s supply structure. A discussion thread on V2EX lays the trade bare:

  • Packet capture + bulk claiming scripts — analyzing a platform app’s coupon API, snapping up hidden coupons, store discounts, and targeted vouchers, then reselling them. This is the mainstream technical route.
  • Leaked corporate/employee benefit coupons — team-building vouchers, employee discount codes, and bank promotional prices collected from individuals and flipped in bulk.
  • Black/gray industry — one reply puts it bluntly: “most of it is money laundering or card fraud,” but that isn’t the whole picture.

For the Burger King and Pizza Hut coupons you see, the first two channels dominate. The tells: a seller based in Zhengzhou, dozens of identical listings, virtual coupons with 14-day validity, and “one per order, repeatable” — the signature of a bulk-claiming script operation, not a human hoarding coupons by hand.

2. The Full Technical Chain

1
2
3
4
5
6
7
8
9
 Intercept HTTPS with a proxy tool (Charles / mitmproxy / Fiddler)
    Obstacle: the app uses SSL Pinning  plain capture shows only ciphertext
 Bypass SSL Pinning with a Frida hook  plaintext requests visible
    Obstacle: No-Proxy detection, VPN detection, native-layer networking
 Analyze the coupon API's URL / parameters / signing / encryption
    Obstacle: dynamic signatures like mtgsig / waimai_sign, encrypted bodies
 Python script calls the API in bulk  timed coupon grabbing
    Obstacle: anti-bot systems blacklist tokens on high-frequency requests
 Scheduled runs on GitHub Actions / cloud functions (at the 11/17/21 drop times)

Every step has a wall. Let’s take them one at a time.

3. Wall One: SSL Pinning

What certificate pinning is

Ordinary apps trust the system’s CA list — install your proxy tool’s self-signed certificate on the device and the app accepts it, letting you decrypt its HTTPS traffic.

But the high-security 1% of apps (banks, major platforms) add certificate pinning: the app hardcodes trust for specific issuers and ignores the system CA list. Install your capture certificate anyway, and the app refuses it; your proxy sees only encrypted garbage.

Notably, Google’s current documentation explicitly advises against SSL pinning — it is largely “security theater” that blocks device owners from controlling their own devices while adding little real protection. Major Chinese delivery and e-commerce apps still use it anyway.

What Frida is

Frida is a cross-platform dynamic instrumentation framework: you write JavaScript that modifies an app’s behavior at runtime — hooking any function, changing return values, logging arguments, disabling features. On Android, a frida-server running on a rooted device lets you control the app from your computer in real time.

The core idea for defeating pinning: find the function that performs certificate verification, hook it, and make it always return “verification passed.”

The generic BoringSSL bypass

The most valuable technique comes from a 52pojie forum post, a universal capture method for ByteDance apps. It targets ByteDance apps (Douyin, Tomato Novel, etc.), but the principle applies to any major app whose network layer uses BoringSSL (Google’s OpenSSL fork).

Principle: BoringSSL registers a verification callback via SSL_CTX_set_custom_verify:

1
2
3
4
5
6
7
8
void SSL_CTX_set_custom_verify(
    SSL_CTX *ctx,
    int mode,
    enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert)
) {
    ctx->verify_mode = mode;
    ctx->custom_verify_callback = callback;
}

The result is an enum:

1
2
3
4
5
enum ssl_verify_result_t {
    ssl_verify_ok,      // 0 — verification passed
    ssl_verify_invalid, // 1 — verification failed
    ssl_verify_retry,   // 2 — retry needed
};

The bypass: hook the callback so it always returns 0 (ssl_verify_ok). Whether or not the certificate is your proxy’s self-signed one, the app believes verification passed, and your tool sees plaintext HTTPS.

The practical script skeleton

A Frida script must solve two problems.

Problem 1: library load timing. Calling Module.getExportByName('libsscronet.so', 'SSL_CTX_set_custom_verify') directly throws unable to find module 'libsscronet.so' — you injected too early, before the library loaded. The fix is to watch android_dlopen_ext (the system’s library loader) and hook only after the target .so arrives:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function onLoad(name, callback) {
    const android_dlopen_ext = Module.findExportByName(
        null, "android_dlopen_ext"
    );
    if (android_dlopen_ext != null) {
        Interceptor.attach(android_dlopen_ext, {
            onEnter: function (args) {
                if (args[0].readCString().indexOf(name) !== -1) {
                    this.hook = true;
                }
            },
            onLeave: function (retval) {
                if (this.hook) {
                    callback();  // library loaded — hook now
                }
            }
        });
    }
}

Problem 2: replacing the callback’s return value. Once you have SSL_CTX_set_custom_verify, hook its third argument (the callback) so it always returns 0:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function main() {
    Java.perform(function () {
        let SSL_CTX_set_custom_verify = Module.getExportByName(
            'libsscronet.so', 'SSL_CTX_set_custom_verify'
        );
        Interceptor.attach(SSL_CTX_set_custom_verify, {
            onEnter: function (args) {
                let callback = args[2];  // arg 3: the callback pointer
                // replace with a function that always returns 0
                Interceptor.replace(callback, new NativeCallback(function () {
                    return 0;  // ssl_verify_ok
                }, 'int', []));
            }
        });
    });
}

Launching it

1
2
3
4
5
# spawn mode (inject at app start — for early-load logic)
frida -U -f com.ss.xxx.aweme -l bytedance_bypass.js

# attach mode (inject into a running app — for later logic)
frida -U -n "Douyin" -l bytedance_bypass.js

-U = USB device, -f = spawn and inject, -l = load script.

On GitHub, CYRUS-STUDIO/frida-ssl-pinning-bypass provides a fully automatic Java-layer + native-layer bypass that isn’t app-specific. Frida CodeShare also has a universal robust bypass you can use directly.

4. Bypassing Pinning Isn’t Enough: Anti-Capture Countermeasures

Defeating SSL pinning is step one. Modern apps stack deeper anti-capture layers.

No Proxy

The app deliberately refuses the system proxy and connects directly:

1
2
3
val client = OkHttpClient.Builder()
    .proxy(Proxy.NO_PROXY)  // disable system proxy
    .build()

Charles in system-proxy mode captures nothing. Bypass: force traffic through a VPN layer — e.g. Drony, an Android proxy client that redirects the app’s traffic to your capture tool at the VPN layer, sidestepping the Java-level Proxy.NO_PROXY setting.

VPN detection

Apps check whether a VPN is active:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fun isVpnActive(context: Context): Boolean {
    val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE)
        as? ConnectivityManager ?: return false
    cm.allNetworks.forEach { network ->
        val caps = cm.getNetworkCapabilities(network)
        if (caps?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) == true)
            return true
    }
    return false
}

On detection, the app refuses requests or degrades service. Bypass: hook this method with Frida to always return false, or use an ADB proxy instead of a VPN.

The ADB proxy route

Skip VPN/V-Proxy entirely and set a proxy at the system level via ADB:

1
2
3
4
5
6
7
8
# set system proxy (hidden from Wi-Fi settings)
adb shell settings put global http_proxy 127.0.0.1:8888

# USB port forwarding (device 8888 → computer over USB)
adb reverse tcp:8888 tcp:8888

# clear the proxy
adb shell settings put global http_proxy :0

Because traffic is diverted at the system layer, app-level VPN/proxy checks never see it.

5. Wall Two: Reverse-Engineering Request Signatures

Captured plaintext requests still can’t be replayed for bulk claiming — major platforms sign every request.

Meituan’s double-signature scheme

Meituan’s delivery API carries two critical crypto parameters (see this analysis):

  • waimai_sign: a digital signature over URL + request body + timestamp
  • mtgsig: a dynamic token, time-limited and unique, generated in real time by client-side JS/native code

Missing or wrong parameters yield 403:

1
{"code": 403, "message": "Forbidden", "data": null}

A typical request looks like:

1
2
3
4
5
6
headers = {
    "mtgsig": "v1.a1MjAxMjAyM...",  # dynamic encrypted token
    "User-Agent": "MeituanGroup/7.70.5",
    "Host": "wmapi.meituan.com",
    "Content-Type": "application/json"
}

Reversing mtgsig

mtgsig’s generation logic lives in obfuscated client JS. A cnblogs post on token/mtgsig analysis for a Meituan mini-program walks through the method:

  1. Locate the crypto entry: set breakpoints on SSL_CTX_set_custom_verify or the crypto functions in Chrome DevTools, trigger a coupon claim, and read the call stack.
  2. Identify the obfuscation pattern: Meituan uses “n-function substitution” — constant values replaced by n(number) calls. A Babel AST traversal restores them in bulk:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
traverse(ast, {
    CallExpression(path) {
        let { callee, arguments } = path.node;
        if (arguments.length !== 1) return;
        if (!types.isIdentifier(callee, { name: "n" })) return;
        if (!types.isNumericLiteral(arguments[0])) return;
        let value = eval(path.toString());
        path.replaceWith(types.valueToNode(value));
    },
});
  1. Find the generation core: search for w1.3 and trace upward to require JSguard and require /rohr.js — the two mtgsig generation entry points.
  2. Extract the code: pull the crypto functions out and run them locally under Node.js to produce valid mtgsig values.

Calling the crypto function directly with Frida

If the JS obfuscation is too heavy to extract, there’s another move: call the app’s internal crypto function via Frida. Instead of restoring the algorithm, you invoke the app’s own native signing routine at runtime and let it compute the signature for you. It sidesteps all obfuscation — you don’t need to understand the algorithm, only the call entry:

1
App's internal crypto function(plaintext params) → [Frida hook call] → mtgsig

It’s the “laziest yet most effective” method in reverse engineering: don’t break the algorithm, let the runtime compute for you.

6. Wall Three: Anti-Bot and Risk Control

With the API and signatures in hand, you still can’t hammer blindly — platforms run behavioral risk control.

Meituan’s openresty anti-bot

Meituan’s backend uses openresty (Nginx + Lua) to flag high-frequency requesters and blacklist their tokens. The README of jiuzhi-1/meituan-shenquan on GitHub warns explicitly:

Don’t keep the script running constantly. Stick to the default schedule of three daily windows, or Meituan’s backend nginx plugin openresty will identify you as a crawler and blacklist your token.

Meituan’s daily coupon drops happen at 11:00, 17:00, and 21:00 Beijing time, so the script runs only at those three moments.

Device fingerprinting

Requests carry device parameters (uuid, model, IMEI hash, etc.) that build a unique fingerprint; one fingerprint making rapid requests gets flagged. Bypass: rotate fingerprint parameters, or distribute across multiple devices/accounts.

Behavioral pattern analysis

Servers analyze request frequency and ordering. Normal behavior: open app → browse → tap claim. Script behavior: call the API directly at high frequency. Bypass: scripts mimic the browse path — request pages first, then claim, with random delays.

7. Automated Deployment: GitHub Actions

The claiming script doesn’t even need a server — GitHub Actions runs scheduled jobs for free.

The meituan-shenquan workflow:

  1. Fork the project to your GitHub account
  2. Add three secrets under Settings → Secrets: MTTOKEN (Meituan web token), PUSHPLUSTOKEN (notifications), SERVERKEY (notifications)
  3. Configure cron schedules in .github/workflows/action.yml (default 11/17/21)
  4. The Action fires on schedule; claimed coupons are pushed to WeChat via pushPlus/ServerChan

The token is captured from Meituan’s web version — log in via browser and pull the token field from cookies in DevTools. Tokens expire; the script includes expiry alerts that tell you when to refresh.

Ele.me works similarly (see wss1029681084/Autosign): capture cookies from the h5.ele.me H5 page (must include the userid field), then a Python script grabs ¥10 no-threshold red packets on a 10/14/17 schedule.

8. The Complete Toolchain

Capture tools (traffic interception):

ToolCharacteristics
CharlesFriendly GUI, mature SSL Proxying, most popular on Mac
mitmproxyCLI + scriptable; rewrite and map local mutate requests; open source
FiddlerWindows veteran; enterprise edition has strong protocol analysis

SSL pinning bypass (decrypting HTTPS):

SolutionApplies to
CYRUS-STUDIO/frida-ssl-pinning-bypassFully automatic Java + native universal bypass
52pojie ByteDance universal methodHooks BoringSSL SSL_CTX_set_custom_verify
httptoolkit tutorialGround-up Frida walkthrough
Bilibili BV1ZT421k7xw2024 video course on Frida reversing and capture

API reverse engineering (breaking signatures):

ResourceContent
Meituan waimai_sign/mtgsig reversalDouble-signature scheme + parameter structure
Meituan mini-program token/mtgsig analysisAST deobfuscation + code extraction
iOS Meituan reverse engineeringiOS-side API analysis

Automated claiming projects (GitHub source):

ProjectPlatformCharacteristics
jiuzhi-1/meituan-shenquanMeituanMost complete docs; GitHub Actions deployment; token expiry alerts
wss1029681084/AutosignEle.meH5 cookie capture; 10/14/17 scheduled grabs
chenbool/python-seleniumMultiMeituan/Ele.me/OFO/QQ Zone check-in collection

Forums (finding the latest tutorials):

  • 52pojie.cn — China’s top reverse-engineering forum; search for “packet capture,” “coupon claiming,” “SSL pinning”
  • Kanxue bbs.kanxue.com — deeper native-layer analysis
  • V2EX — not a tech-security forum, but invaluable on how the gray trade actually operates

Every link in this chain carries explicit legal risk:

  • Capture + SSL pinning bypass: analyzing your own traffic on your own device is legitimate security research; using it for bulk claiming violates platform terms
  • API reversal + bulk claiming: may constitute unfair competition or unlawful intrusion under computer-information-system protection regulations
  • Reselling: bulk claiming and reselling is unambiguous gray-industry behavior; at scale it can become illegal business operation
  • Frida injection into others’ devices: on someone else’s phone, this can constitute illegal intrusion into a computer information system

In the V2EX thread, someone says plainly that “most of it is money laundering or card fraud” — the upstream of this chain genuinely includes stolen-card laundering, not just technical claiming. Looking at a cheap coupon, you cannot tell whether it was farmed by scripts or washed through by fraud.

The techniques themselves are neutral — capture, reversing, and automation are foundational security-research tools. Combined into bulk claiming and reselling, “research” becomes “gray industry.”

10. If Your Goal Is Something Else

If your aim isn’t reselling but understanding this stack for security research or your own automation, the highest-value learning path is:

  1. Learn Frida first — it’s the fulcrum of the whole chain. The Bilibili course (BV1ZT421k7xw) takes you from Python basics to practical hooks in about a week.
  2. Then AST deobfuscation — the obfuscation pattern in Meituan’s mtgsig is industry-standard; Babel-traverse bulk restoration is the core skill.
  3. Finally API automation — read meituan-shenquan’s source to understand token acquisition, scheduling, and anti-bot evasion as engineering.

Combined, these three skills reach far beyond coupons — they are the universal substrate of all app automation (check-ins, flash sales, data collection). Security researchers, crawler engineers, and automation test engineers all use the same toolchain.


All interfaces, parameters, and code examples in this article are sanitized and provided for security research and study only. Ready-to-run claiming code is outside this article’s scope.