i茅台自动申购算法协议分析

Auto.js实现i茅台自动化申购 现在茅台行情十分火热,茅台集团推出了i茅台APP供大家申购。下面介绍使用Auto.js实现自动化申购。 阅读详情
AI摘要

本文针对i茅台APP自动申购进行破解分析。因看到的自动申购文章疑似引流无法白嫖,作者决定自己动手。过程包括解决启动闪退问题,进行抓包分析找到未知参数,对使用百度加固的文件脱壳,分析MT - K、MT - V、MT - Device - ID等,还涉及登录代码、申购参数及其他包的分析。

首发地址:http://zhuoyue360.com/crack/104.html

一、引言

今日看到有人分享了i茅台自动申购的文章。但是它酷似引流文章,全文一张图,呜呜呜。无法白嫖。太可恶了,因此,我来啦~ 我来整一整,我也要抢茅子!

二、开战

开发者详情:贵州茅台酒销售有限公司

版本信息:1.4.5

更新时间:2023/07/25 19:07

(一) 启动闪退

由于我们的环境特别的异常 Root,Xposed,Fridd,抓包的啥都有。

管他啥检测,先上个Shamiko和配置排除列表. 就可以正常打开了~

img

(二) 抓包分析

本次使用的抓包手段是 Postern + Charles的抓包方案。

当我们点击登录以后,应用提示未检测到网络信号。它一定是有抓包检测的。

img

作为资深的脚本小子,我们再次借用大佬们做好的工具JustMePlus,提示验证码过期,我们也抓到了包数据。

img

看一下提交的数据。有几个不知道含义的字段MT-V、MT-Device-ID、MT-Request-ID、MT-R、MT-SN和BS-DVID参数。找到了未知的参数,我们的目标也就明确了。

POST /xhr/front/user/register/login HTTP/1.1
MT-K: 1690425040309
MT-V: 33212ce8f99d64231e221a5ec9y
MT-Token: 
User-Agent: android;29;google;sailfish
MT-Device-ID: clips_Kxx/RnJBdEYlFyZEJRx6QycQI0V2FydBcxd0FXZH
MT-APP-Version: 1.4.5
MT-Request-ID: 3b2b66c3-747d-4ce7-ba28-a3066594247e
MT-Network-Type: WIFI
MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==
MT-Bundle-ID: com.moutai.mall
MT-USER-TAG: 0
MT-SN: clips_ehwpSC0fLBggRnJAdxYgFiAYLxl9Si5PfEl/TC0afkw=
MT-DTIME: Wed Aug 14 09:25:50 GMT+08:00 2019
MT-RS: 1080*1794
MT-Lng: 
MT-Lat: 
BS-DVID: tDkAoOWWVDbDFNobZRf1eMcF364bZwAjmM87iuSKpikmnWOBRbaGo1ZQOYt0lu5NQDNwm1S-hrcW4Falp_9YIdg
MT-DOUBLE: 0
MT-SIM: 0
MT-ACBE: 0
MT-ACB: 0
MT-ACBM: 0
Content-Type: application/json; charset=UTF-8
Content-Length: 67
Host: app.moutai519.com.cn
Connection: Keep-Alive
Accept-Encoding: gzip

{"vCode":"111111","mobile":"13544711111","ydToken":"","ydLogId":""}

(三) 脱壳

看到libbaiduprotect.so文件,它使用了百度加固。接下来,脚本小子继续上!我使用的是FunDex

img

(四) 分析

使用的是Frida16大版本.

1. MT-K、MT-V (非必须)

随便跟踪一下。

img

img

Pair<String, String> b10 = 
  new RandK().b(
    n.n("", qm.b.e(System.currentTimeMillis())), 
    wf.a.c(), 
    ""
  );
一、qm.b.e(System.currentTimeMillis())

它其实就等于 new Long(System.currentTimeMillis())

public final class b {
    public static final Boolean a(boolean z10) {
        return Boolean.valueOf(z10);
    }

    public static final Double b(double d10) {
        return new Double(d10);
    }

    public static final Float c(float f10) {
        return new Float(f10);
    }

    public static final Integer d(int i10) {
        return new Integer(i10);
    }

    public static final Long e(long j10) {
        return new Long(j10);
    }
}
二、wf.a.c()

这里我把关键的函数给提取出来,先从部分一开始分析。因为它已经涉及到SO了。脚本小子继续上,这里使用yang神的HookRegisterNative的脚本。

//   部分一
public static final String DEVICE_ID = "device_id";

static {
    EnumMap<MMKVRecoverStrategic, Integer> enumMap = new EnumMap<>(MMKVRecoverStrategic.class);
    recoverIndex = enumMap;
    enumMap.put((EnumMap<MMKVRecoverStrategic, Integer>) MMKVRecoverStrategic.OnErrorDiscard, (MMKVRecoverStrategic) 0);
    recoverIndex.put((EnumMap<MMKVRecoverStrategic, Integer>) MMKVRecoverStrategic.OnErrorRecover, (MMKVRecoverStrategic) 1);
    System.loadLibrary("c++_shared");
    System.loadLibrary("mmkv");
    rootDir = null;
    mCreators = new HashMap<>();
}

private MMKV(long j10) {
    this.nativeHandle = j10;
}


private static native long getMMKVWithID(String str, int i10, String str2);

public static MMKV mmkvWithID(String str, int i10) {
    if (rootDir != null) {
        return new MMKV(getMMKVWithID(str, i10, null));
    }
    throw new IllegalStateException("You should Call MMKV.initialize() first.");
}


public static MMKV d(Context context, String str, boolean z10) {
    if (TextUtils.isEmpty(str)) {
        return null;
    }
    MMKV mmkv = a.get(str);
    if (mmkv == null && z10) {
        MMKV mmkvWithID = MMKV.mmkvWithID(str, 2);
        a.put(str, mmkvWithID);
        return mmkvWithID;
    }
    return mmkv;
}

public static String e(Context context, String str, String str2, String str3) {
    MMKV d10 = d(context, str, true);
    return d10 != null ? d10.getString(str2, str3) : str3;
}

public static Application a() {
    if (a == null) {
        a = a.e();
    }
    return a;
}


public static String k(String str, String str2, String str3) {
    return sj.a.e(b.a(), str, str2, str3);
}

public static String e(String str, String str2) {
    return k("YanXuan", str, str2);
}

public static String j() {
    return jk.a.e(PushConstants.DEVICE_ID, null);
}


public static synchronized String c() {
        synchronized (a.class) {
            if (TextUtils.isEmpty(a)) {
                String j10 = j();
                a = j10;
                if (!TextUtils.isEmpty(j10)) {
                    return a;
                }
                String a10 = a();
                a = a10;
                p(a10);
            }
            return a;
        }
    }

开始Hook看看函数的注册地址,再去用IDA分析,必须要偷懒~

frida -U -f com.moutai.mall -l .\hook_registerNative.js

很遗憾,但是又是在意料之中的事,应用存在Frida检测。看着日志信息,该反调试应该是由百度加固提供的.所以去看看它的so

[RegisterNatives] java_class: com.example.vmp.Hook.LHook name: getDexSizeForDexFile sig: (Ljava/lang/Object;)[I fnPtr: 0x72955fa5b0  fnOffset: 0x72955fa5b0 libLVmp.so!0x355b0  callee: 0x72955fb694 libLVmp.so!0x36694
[RegisterNatives] method_count: 0x1
[RegisterNatives] java_class: com.sagittarius.v6.D name: n0 sig: (I)I fnPtr: 0x728aecec64  fnOffset: 0x728aecec64 libbaiduprotect.so!0x32c64  callee: 0x728aeceae4 libbaiduprotect.so!0x32ae4
[RegisterNatives] method_count: 0x3
[RegisterNatives] java_class: com.sagittarius.v6.A name: n1 sig: (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V fnPtr: 0x728aeb00f4  fnOffset: 0x728aeb00f4 libbaiduprotect.so!0x140f4 
 callee: 0x728aeb0090 libbaiduprotect.so!0x14090
[RegisterNatives] java_class: com.sagittarius.v6.A name: n2 sig: (Landroid/content/Context;)V fnPtr: 0x728aeb02b4  fnOffset: 0x728aeb02b4 libbaiduprotect.so!0x142b4  callee: 0x728aeb0090 libbaiduprotect.so!0x14090
[RegisterNatives] java_class: com.sagittarius.v6.A name: n3 sig: (Landroid/content/Context;)V fnPtr: 0x728aeb039c  fnOffset: 0x728aeb039c libbaiduprotect.so!0x1439c  callee: 0x728aeb0090 libbaiduprotect.so!0x14090
Process crashed: Bad access due to protection failure

***
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/sailfish/sailfish:10/QP1A.190711.020/5800535:user/release-keys'
Revision: '0'
ABI: 'arm64'
Timestamp: 2023-07-27 10:58:52+0800
pid: 19932, tid: 19974, name: com.moutai.mall  >>> com.moutai.mall <<<
uid: 10234
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x73883f4b20
Abort message: 'state=545'
    x0  0000007388402a68  x1  0000000000001000  x2  0000000000000001  x3  0000000000000000
    x4  00000072f11f4120  x5  00000072f11f4140  x6  0000007388548280  x7  00000073885482c0
    x8  0000000000000000  x9  0000000000000002  x10 0000000000000001  x11 0000000000000000
    x12 0000007388548240  x13 0000007388548200  x14 0000000000000000  x15 00000072f11acd40
    x16 0000007386ac6888  x17 0000007386ab969c  x18 0000000000000077  x19 0000000000000000
    x20 000000000000000c  x21 000000738994f000  x22 0000000000001000  x23 0000007386ac82e0
    x24 0000007388402a68  x25 00000073883f4b20  x26 000000738994f118  x27 0000007386ac8000
    x28 0000000000000018  x29 0000007289bea7b0
    sp  0000007289bea750  lr  0000007386abb5a8  pc  00000073883f4b20

backtrace:
      #00 pc 0000000000001b20  [anon:thread signal stack]
      #01 pc 00000000000e65a4  /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (offset 0xe6000) (__cxa_finalize+212) (BuildId: 5812256023147338b8a9538321d4c456)
      #02 pc 00000000000e1ea8  /apex/com.android.runtime/lib64/bionic/libc.so!libc.so (offset 0xe1000) (exit+24) (BuildId: 5812256023147338b8a9538321d4c456)
      #03 pc 0000000000000008  <anonymous:72fb904000>
***

当我们使用IDA打开以后,发现它非常的苦恼,没法F5,那 我们还咋分析!这回没法借助脚本了。问了个好朋友,好朋友喊我DumpSo,我使用了FunELF工具,这效果也是嗷嗷好!

img

img

还记得以前在霜哥课程里面学的frida反调试,把检测线程直接干掉,这个也嗷嗷好用。在这里反调试我选择了霜哥的方法。

frida -U -f com.moutai.mall -l .\hook_anti-thread.js

干掉线程的脚本

function replace_thread() {
  var pthread_create_addr = Module.findExportByName(null, "pthread_create");
  var pthread_create = new NativeFunction(pthread_create_addr, "int", ["pointer", "pointer", "pointer", "pointer"]);
  Interceptor.replace(pthread_create_addr, new NativeCallback((parg0, parg1, parg2, parg3) => {
    var so_name = Process.findModuleByAddress(parg2).name;
    var so_base = Module.getBaseAddress(so_name);
    var offset = (parg2 - so_base);
    var PC = 0;
    console.log("normal find thread func offset", so_name, parg2,offset, offset.toString(16));
    // if ((so_name.indexOf("libmsaoaidsec.so") > -1)) {
    //     if (offset === 123) {
    //     } else if (offset === 95736) {
    //     } else if (offset === 93488) {
    //     }
    //     else {
    //         console.log("normal find thread func offset", so_name, offset, offset.toString(16));
    //         PC = pthread_create(parg0, parg1, parg2, parg3);
    //     }
    // } else {
    //     PC = pthread_create(parg0, parg1, parg2, parg3);
    // }

    if ((so_name.indexOf("libbaiduprotect.so") > -1)) {
      if (offset === 178752) {
      } 
      else {
        console.log("normal find thread func offset", so_name, offset, offset.toString(16));
        PC = pthread_create(parg0, parg1, parg2, parg3);
      }
    } else {
      PC = pthread_create(parg0, parg1, parg2, parg3);
    }
    return PC;
  }, "int", ["pointer", "pointer", "pointer", "pointer"]));
}

setImmediate(replace_thread)

于是乎,我们就把i茅台的反调试给过掉了,虽然不知道怎么检测了啥。脚本小子,不需要知道那么多! 继续RUN,RUN,RUN

我们继续回到wf.a.c

2. MT-Device-ID

let b = Java.use("ic.b");
b["a"].implementation = function (str) {
    console.log(`b.a is called: str=${str}`);
    let result = this["a"](str);
    console.log(`b.a result=${result}`);
    return result;
};
b.a is called: str=d127e6d9172a92a949532d2b6edd8b4e
b.a result=clips_LB0vGH1LLxYnECJDekgpECQdKBspTX8dK04qTnYUIEU=

再把堆栈打出来,发现d127e6d9172a92a949532d2b6edd8b4e是来自于wf.a.c(),这个方法在MT-V、MT-K中也用到了。这里就不多说了.

public static String j() {
    return jk.a.e(PushConstants.DEVICE_ID, null);
}


public static String a() {
        String d10 = d();
        String e10 = e();
        String i10 = i();
        String b10 = b();
        if (!TextUtils.isEmpty(d10) && n(d10) && !TextUtils.isEmpty(e10) && m(e10)) {
            d10 = d10 + "@" + e10;
        } else if (!TextUtils.isEmpty(e10) && m(e10)) {
            d10 = e10;
        } else if (TextUtils.isEmpty(d10) || !n(d10)) {
            if (TextUtils.isEmpty(i10) || !o(i10)) {
                d10 = !TextUtils.isEmpty(b10) ? b10 : "";
            } else {
                d10 = i10;
            }
        }
        if (TextUtils.isEmpty(d10)) {
            try {
                d10 = UUID.randomUUID().toString();
            } catch (Exception unused) {
            }
        }
        String a10 = b.a(d10, "UTF-8");
        return !TextUtils.isEmpty(a10) ? a10.trim() : d10;
    }


public static synchronized String c() {
    synchronized (a.class) {
        if (TextUtils.isEmpty(a)) {
            String j10 = j();
            a = j10;
            if (!TextUtils.isEmpty(j10)) {
                return a;
            }
            String a10 = a();
            a = a10;
            p(a10);
        }
        return a;
    }
}


wf.a.c()
public class a {
    public static String a(byte[] bArr) {
        return Base64.encodeToString(bArr, 2);
    }

    public static String b(String str) {
        try {
            return a(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e10) {
            e10.printStackTrace();
            return null;
        }
    }
}

public static String a(String str) {  // str = deviceId_md5
    if (TextUtils.isBlank(str)) {
        return str;
    }
    String b10 = a.b(b(str, 72));
    return "clips_" + b10;
}

public static String b(String str, int i10) {
    if (TextUtils.isBlank(str)) {
        return str;
    }
    StringBuilder sb2 = new StringBuilder();
    for (char c10 : str.toCharArray()) {
        i10 ^= c10;
        sb2.append((char) i10);
    }
    return sb2.toString();
}

总的来说逻辑如下:

  1. deviceID = 获取设备号或deviceId
  2. deviceId_md5 = MD5(deviceID)
  3. mt_device_id = base64(xor(deviceId_md5))
import base64
import hashlib
import uuid


def getDeviceId():
    # UUID
    return uuid.uuid4()

def md5(deviceId):
    if not str:
        return None
    if not deviceId:
        str2 = "UTF-8"
    try:
        message_digest = hashlib.md5()
        message_digest.update(str.encode(deviceId))
        digest = message_digest.digest()
        string_buffer = ""
        for b10 in digest:
            string_buffer += format(b10, 'x')
        return string_buffer
    except Exception as e:
        return None
def getMTDeviceId(deviceId):
    ret = []
    i10 = 72
    for char in deviceId:
        i10 ^= ord(char)
        ret.append(chr(i10))
    ret = ''.join(ret)
    return base64.b64encode(ret.encode()).decode()

if __name__ == '__main__':
    deviceidMd5 = md5('d38d477f-9804-4667-822f-750d15e10915')
    mt_device_id = getMTDeviceId(deviceidMd5)
    print(mt_device_id)

在发送验证码的时候,有一个参数md5。这个就比较简单了。

str = 手机号;
String l10 = j10.l("2af72f100c356273d46284f6fd1dfc08" + str + currentTimeMillis);

然后其他就没什么难得东西了。

{'code': 2000}
验证码:271744
{'code': 2000, 'data': {'userId': 1127142557, 'userName': '用户1127142557', 'mobile': '135****9983', 'verifyStatus': 0, 'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtdCIsImV4cCI6MTY5MzAzNzU4MSwidXNlcklkIjoxMTI3MTQyNTU3LCJkZXZpY2VJZCI6ImNsaXBzX0xCb2pFSE5ISlJZaUczNVBmVVVrRVNaRkp4RW5GbmRESWhCeEZDWVMiLCJpYXQiOjE2OTA0NDU1ODF9.V_hJSKm3D6I56-6u9TkON4ZzFK6vkzey4wo9miSOAb4', 'userTag': 0, 'cookie': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtdCIsImV4cCI6MTY5MzAzNzU4MSwidXNlcklkIjoxMTI3MTQyNTU3LCJkZXZpY2VJZCI6ImNsaXBzX0xCb2pFSE5ISlJZaUczNVBmVVVrRVNaRkp4RW5GbmRESWhCeEZDWVMiLCJpYXQiOjE2OTA0NDU1ODF9.fRFdAM_cVjsTSRtLIP5tl9zHLxs_-sW2L_9wJkHeZQQ'}}

(五) 登录代码

import base64
import hashlib
import time
import uuid

import requests


def getDeviceId():
    # UUID
    return str(uuid.uuid4())


def md5(deviceId):
    if not str:
        return None
    if not deviceId:
        str2 = "UTF-8"
    try:
        message_digest = hashlib.md5()
        message_digest.update(str.encode(deviceId))
        digest = message_digest.digest()
        string_buffer = ""
        for b10 in digest:
            string_buffer += format(b10, 'x')
        return string_buffer
    except Exception as e:
        return None


def getMTDeviceId(deviceId):
    ret = []
    i10 = 72
    for char in deviceId:
        i10 ^= ord(char)
        ret.append(chr(i10))
    ret = ''.join(ret)
    return 'clips_' + base64.b64encode(ret.encode()).decode()

def sendCode(phone,mt_device_id):
    timestamp =str(int(time.time() * 1000))
    url = 'https://app.moutai519.com.cn/xhr/front/user/register/vcode'
    md5_str = '2af72f100c356273d46284f6fd1dfc08' + phone + timestamp
    md5_str = hashlib.md5(md5_str.encode()).hexdigest()
    data = {
        "md5":md5_str,
        "mobile":phone,
        "timestamp":timestamp
    }
    headers = {
        "MT-Token": "",
        "User-Agent": "android;29;google;sailfish",
        "MT-Device-ID": mt_device_id,
        "MT-APP-Version": "1.4.5",
        "MT-Request-ID": str(uuid.uuid4()),
        "MT-Network-Type": "WIFI",
        "MT-Bundle-ID": "com.moutai.mall",
        "MT-USER-TAG": "0",
        "MT-RS": "1080*1794",
        "Content-Type": "application/json; charset=UTF-8",
        "Host": "app.moutai519.com.cn"
    }
    return requests.post(url=url, json=data, headers=headers).json()


def login(phone,code,mt_device_id):
    url = 'https://app.moutai519.com.cn/xhr/front/user/register/login'

    data = {
        "vCode":code,
        "mobile":phone,
        "ydToken":"",
        "ydLogId":""
    }
    headers = {
        "MT-Token": "",
        "User-Agent": "android;29;google;sailfish",
        "MT-Device-ID": mt_device_id,
        "MT-APP-Version": "1.4.5",
        "MT-Request-ID": str(uuid.uuid4()),
        "MT-Network-Type": "WIFI",
        "MT-Bundle-ID": "com.moutai.mall",
        "MT-USER-TAG": "0",
        "MT-RS": "1080*1794",
        "Content-Type": "application/json; charset=UTF-8",
        "Host": "app.moutai519.com.cn"
    }
    return requests.post(url=url, json=data, headers=headers).json()
def go():
    deviceId = getDeviceId()
    deviceidMd5 = md5(deviceId)
    mt_device_id = getMTDeviceId(deviceidMd5)
    print(mt_device_id)
    phone = "135********"
    data = sendCode(phone,mt_device_id)
    print(data)
    if data['code'] != 2000:
        print('异常')
        return
    code = input('验证码:')
    print(login(phone, code, mt_device_id))
if __name__ == '__main__':
    go()
请输入手机号:13544779983
{'code': 2000}
验证码:271744
{'code': 2000, 'data': {'userId': 123123, 'userName': '123123', 'mobile': '135********', 'verifyStatus': 0, 'token': '****.****.V_hJSKm3D6I56-****', 'userTag': 0, 'cookie': '***.eyJpc3MiOiJtdCIsImV**.*-*'}}

(六) 申购分析(actParam)

在申购得时候,发现一个关键参数也要搞.

{
	"sessionId": 700,
	"shopId": "144440300003",
	"itemInfoList": [{
		"itemId": "10213",
		"count": 1
	}],
	"actParam": "oUxr9vtC5s6wgqbkfHZkFAudq+iTYalbyD2cl2/oYIq44OE879P2dJcuXYNHvwht79rANbLKxdxL\n3hg7WmB2upL/xILVsuZ5TMg1A3Lma5dcdficheXbgZc2QmuIs0kJcVaseBdhtrvFiMrfEHCO0g==\n"
}

此处应该上RPC.

img

(七) 其他包

实名包

POST /xhr/front/user/realNameAuth HTTP/1.1
MT-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtdCIsImV4cCI6MTY5MzAzNzcyNCwidXNlcklkIjoxMTI3MTQyNTU3LCJkZXZpY2VJZCI6ImNsaXBzX0xCc3RUSGdlZTBvdlRpeE5LUjBzVFM4YUtSOTdHSDVKY0VRaVFIRkMiLCJpYXQiOjE2OTA0NDU3MjR9.NvaXbgWMOUEqtkyq15zDP8SQqKaGnQxUgoYMdl_aQkk
User-Agent: android;29;google;sailfish
MT-Device-ID: clips_LBstTHgee0ovTixNKR0sTS8aKR97GH5JcEQiQHFC
MT-APP-Version: 1.4.5
MT-Request-ID: e24eb535-daa3-45fd-a7f4-9f5e833d559d
MT-Network-Type: WIFI
MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==
MT-Bundle-ID: com.moutai.mall
MT-USER-TAG: 0
MT-SN: clips_ehwpSC0fLBggRnJAdxYgFiAYLxl9Si5PfEl/TC0afkw=
MT-DTIME: Wed Aug 14 09:25:50 GMT+08:00 2019
MT-RS: 1080*1794
MT-Lng: 
MT-Lat: 
BS-DVID: hWr62Q591-OLfaOq1ge1xkVW4OZfwq8jzbHpK2tx-B0Mhg0ZB8SVmhl0MzjFvXjHo7cnRUKQz5p9ChG1rFMxykw
MT-DOUBLE: 0
MT-SIM: 0
MT-ACBE: 0
MT-ACB: 0
MT-ACBM: 0
Content-Type: application/json; charset=UTF-8
Content-Length: 65
Host: app.moutai519.com.cn
Connection: Keep-Alive
Accept-Encoding: gzip

{"cardType":0,"idCardName":"ddd","idCardNo":"440582199811054333"}
curl -H "MT-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtdCIsImV4cCI6MTY5MzAzNzcyNCwidXNlcklkIjoxMTI3MTQyNTU3LCJkZXZpY2VJZCI6ImNsaXBzX0xCc3RUSGdlZTBvdlRpeE5LUjBzVFM4YUtSOTdHSDVKY0VRaVFIRkMiLCJpYXQiOjE2OTA0NDU3MjR9.NvaXbgWMOUEqtkyq15zDP8SQqKaGnQxUgoYMdl_aQkk" -H "User-Agent: android;29;google;sailfish" -H "MT-Device-ID: clips_LBstTHgee0ovTixNKR0sTS8aKR97GH5JcEQiQHFC" -H "MT-APP-Version: 1.4.5" -H "MT-Request-ID: e24eb535-daa3-45fd-a7f4-9f5e833d559d" -H "MT-Network-Type: WIFI" -H "MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==" -H "MT-Bundle-ID: com.moutai.mall" -H "MT-USER-TAG: 0" -H "MT-SN: clips_ehwpSC0fLBggRnJAdxYgFiAYLxl9Si5PfEl/TC0afkw=" -H "MT-DTIME: Wed Aug 14 09:25:50 GMT+08:00 2019" -H "MT-RS: 1080*1794" -H "MT-Lng: " -H "MT-Lat: " -H "BS-DVID: hWr62Q591-OLfaOq1ge1xkVW4OZfwq8jzbHpK2tx-B0Mhg0ZB8SVmhl0MzjFvXjHo7cnRUKQz5p9ChG1rFMxykw" -H "MT-DOUBLE: 0" -H "MT-SIM: 0" -H "MT-ACBE: 0" -H "MT-ACB: 0" -H "MT-ACBM: 0" -H "Content-Type: application/json; charset=UTF-8" -H "Host: app.moutai519.com.cn" --data-binary "{\"cardType\":0,\"idCardName\":\"ddd\",\"idCardNo\":\"440582199811054333\"}" --compressed "https://app.moutai519.com.cn/xhr/front/user/realNameAuth"
在多台终端设备的i茅台应用中,实现同时自动化预约茅台酒的解决方案 通过录屏回放,何如在多台手机上,实现自动在i茅台App中预约茅台酒。做到降低预约成本,提高预约效率。 阅读详情

相关推荐

Windows 下自动预约申购 i茅台

保存之后用户列表没有自动更新,我们需要点击【搜索】按钮查看信息。我们在填写预约信息时,需要填写自己所在的省份和城市名称。我们先来刷新一下商店列表,然后过滤自己所在的城市,主要是拿到省份和城市全称,在填写账号信息时用到。添加好账号之后,我们需要查看每天是否自动进行预约了,软件作者也提供了一个查看日志的功能。手动点击刷新一下商品列表,我们在预约时需要用到【预约商品code】这个信息。首先我们先进入【预约项目】菜单界面,先刷新查看一下我们可以预约的商品。我们需要先完善账号信息,点击【修改】链接。

yuxuan6699的博客 4267

WPF 实现 Windows下自动预约申购 i茅台

前言飞天茅台一直都很难能够以原价买到,各大电商每个工作日都会放出少量的商品,但一直都很难抢到。茅台官方推出了i茅台APP,每天可以通过预约的方式进行申购,当天18点公布预约结果。虽然几率也不太大,但也不失为另一个买到飞天茅台的方式,而且不用自己去抢。给大家介绍一款能自动预约的程序,作者介绍的部署过程不够详细,感兴趣的同学可以去官网看看。分享一个自动预约抢茅子的工具。准备工作需安装.NET 6 依赖...

weixin_68436326的博客 283

SAP 分期付款,付款条件的要件定义方法

以上内容做好后,可以在F-02或者F-60里看设置后的结果。找到相关路径选择【维护支付条件】点击进入后,选择【新条目】再设置一个总的支付规则,注意这里【付款条款】一定要选择。例:默认付款条件SP00,改成刚定义好的0028即可。将刚才基础创建的付款条件,以以下方式录入,注意。028A- 30天到期,过账日期基础。028B- 60天到期,过账日期基础。028C- 90天到期,过账日期基础。目的:新建支付条款(本次以。的选择,然后保存后,完成。,【基准日期缺省值】选择。基础创建好了以后,选择【

晴天的博客分享技术知识 2936

告别手动抢茅台!3分钟部署i茅台自动预约系统终极指南

还在为每天9点准时守在手机前抢购茅台而烦恼吗?手动预约不仅耗时费力,成功率还低得让人崩溃。现在,有了i茅台自动预约系统,你可以彻底解放双手,让科技为你代劳!这个基于Java+Vue开发的智能预约平台,通过自动化技术让你的茅台预约成功率飙升数倍,支持多账号管理、智能门店选择、定时预约和实时推送等核心功能。 ## 🎯 传统抢购的三大痛点与智能解决方案 ### 痛点分析:为什么你总是抢不到茅台?

gitblog_00498的博客 87

ANSYS ACT插件开发实战指南:从IronPython脚本到XML界面设计

本文提供了一份详尽的ANSYS ACT插件开发实战指南,重点讲解如何利用IronPython脚本实现仿真自动化,并通过XML设计用户友好的向导界面。内容涵盖从环境搭建、脚本编写(如自动创建材料)、界面控件绑定到调试打包的全流程,旨在帮助仿真工程师和团队负责人将重复性工作流程标准化,显著提升ANSYS软件的使用效率。

weixin_29310605的博客 453

WPS JS宏智能识别代码段并设置样式(完整可用版)

之前,我们已经经历了多次迭代,解决了许多问题,比如识别代码块、处理多行导入、调整样式等。现在他们需要一份全面的文档来解释整个代码的结构和工作原理。(5)智能行分析(含URL和注解检测优化)function analyzeLineEnhanced(rawText,trimmedText,codeSymbols,codePatterns, singleLinePatterns)该解决方案在保持高性能的同时,提供了良好的可扩展性,能够适应不同组织的代码规范需求,是WPS生态中专业文档处理的创新实践。

搏博的专栏 1826

i茅台自动预约系统campus-imaotai:5分钟部署完成每日9点定时申购

campus-imaotai 是一个 i茅台自动预约系统。它把 i茅台账号托管在服务器上,每天 9 点按账号各自的时间自动提交预约,晚间 18:05 自动查询结果,并可把成败推送到手机。适合想自己部署、并愿意动手维护接口的用户。 ## 🚀 快速上手:从零到跑起来 ### 环境要求 | 组件 | 版本 | 用途 | | --- | --- | --- | | Docker + Compose

gitblog_01021的博客 876

电机控制——PMSM无感FOC控制(一)FOC入门

FOC 是一种电机控制技术,全称为 Field Oriented Control(磁场定向控制),也称作矢量控制。该技术可以提高电机的效率、控制稳定性和精度,广泛应用于电机驱动系统中,是目前无刷直流电机(BLDC)、永磁同步电机(PMSM)、感应电机的高效控制的最优方法之一。本节主要讲了FOC的大致流程,带大家先了解FOC到底是个什么东西在做什么,各个模块具体的原理会放到后面的章节详细讲解。FOC其实就是通过控制,相电流按照正弦变化,从而产生出旋转的磁场,控制电机转子转动。

zhuxinmingde的博客 2629

使用python实现i茅台自动预约

获得后端打包文件exe和前端打包文件dist就可以直接部署在服务器上面了,前端可以直接使用nginx部署,后端简单点就执行运行就可以了。这个应用程序可以帮助你进行茅台自动化配置。打包后,得到前端打包文件dist。直接运行 imtApi.py。需要切换到vue目录。

codel 2530

使用 C# 实现大华监控摄像头的控制与数据处理

本文介绍了使用C#调用大华SDK(DHNetSDK.dll)开发监控系统的方法。主要内容包括:开发准备、DLL引用设置、SDK初始化与设备登录、视频预览窗口嵌入、云台控制、抓图录像等核心功能实现。通过P/Invoke技术封装SDK接口,开发者可以快速构建具有视频监控、设备控制功能的C#应用程序。文中还提供了代码示例、常见问题解决方案,以及视频帧智能分析的扩展建议,为开发大华监控系统的二次应用提供了完整的技术指南。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1280

【报错解决】OpenClaw 报错 FetchError: request to [https://registry.npmjs.org/](https://registry.npmjs.or

在国产基础软件与云原生生态逐步成熟的背景下,越来越多的开发者开始尝试将 Web 前端、AI 推理、系统服务等组件部署到国产操作系统与国产云环境中。我所在的项目正是这样一个场景: 我们基于 OpenClaw 构建一套云端 AI 管理平台,用于统一管理模型服务、前端控制台与后台 API。

博客 1753

i某台app自动预约分析与实现

在“i茅台”App中可以预约茅台酒,每天早上9:00-10:00可以在线预约茅台酒,每天手动预约过于繁琐,且容易忘记,中奖概率低,为了提高中奖概率无非就是用大量的手机号去登录APP每天定时预约。

weixin_47481826的博客 2万+

基于YOLOv8的视频监控违规停车智能识别系统与应用研究

通过这个毕设,你已经从数据准备、模型训练到界面开发,完整走了一遍深度学习项目的流程。把多个停车场的监控数据整合起来,做一个区域级的停车管理平台;加入车牌识别功能,直接定位到违规车辆的车主;试试把YOLO模型再优化一下,让它在手机端也能运行,实现移动巡检。相信我,按照这个教程一步步做,你的毕设不仅能顺利完成,还能做出亮点,让答辩老师眼前一亮。要是过程中遇到什么问题,随时可以再琢磨琢磨,或者找我聊聊,咱们一起把这个毕设做成精品。

LXY_LOVE36的博客 118

Python自动化篇 | 实现自动化抢茅台超详细过程!

最近,酱香拿铁的上市,国内再次掀起了「 抢茅台」的浪潮,每天早上 9-10 时可以通过 App 进行申购,晚上 18 点后会公布当天的申购结果虽然中签率很低,但是和打卡一样,没有任何成本,只需要随手点一下,就有一定的几率会中签,带来一定的收益本篇文章将通过下面几点聊聊自动化抢茅台、查询申购结果的完整详细流程自动化申购自动化查询申购结果参数化及配置定时任务总结一下。

优快云_430422的博客 6065

RT-DETR训练前的准备,将数据集划分成训练集、测试集验证集(附完整脚本及使用说明)

在计算机视觉领域,当我们着手训练一个模型时,。其中,将数据集划分为和更是一项基础性且关键的操作。这篇博客分析了这一划分背后的原因、其重要意义以及如何通过代码实现,帮助大家更好地理解和运用这一技巧,。

Limiiiing的博客 909

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

老 Mac 停止官方支持?OpenCore Legacy Patcher 免费装新版 macOS 的完整教程

OpenCore Legacy Patcher(下文简称 OCLP)是一个免费开源的 Python 工具:它用 OpenCore 接管旧 Mac 的引导,再用 Root Patch 给硬件逐项打补丁,让苹果已停止支持的 Intel 老机型继续安装 macOS 11 直到最新大版本。本文按「先核对资格 → 备齐环境与备份 → 走完三个执行阶段 → 卡住了查速查表」的顺序给出可照做的流程,机型、容量、

gitblog_00417的博客 202

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

i茅台智能预约系统:多用户自动申购的终极解决方案

还在为每天手动预约茅台而烦恼吗?想要同时管理多个账号却苦于操作繁琐?i茅台智能预约系统正是为你量身打造的自动化工具,通过智能算法和多账号协同管理,让茅台预约变得前所未有的简单高效。 ## 🌟 为什么选择这套系统? 想象一下:每天早上醒来,系统已经自动为你完成了所有账号的预约操作,而你只需查看结果报告。这正是i茅台智能预约系统带来的革命性体验! ### 智能化多账号管理 系统支持同时管理多个

gitblog_00611的博客 1014

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

i茅台自动预约系统:Docker 三步跑通每日申购

早上九点整,你手指悬在手机屏幕上方,页面卡了半秒,再点进去只剩"今日名额已约完"。你刷新、退出、重进,手心冒汗,一天的心情全毁在这几秒延迟上。campus-imaotai 是一个 i茅台自动预约系统:你把账号交给它,它每天替你登录、挑门店、在九点档自动提交申购,并推送当天结果。 ## 它到底能干什么 ### 批量托管多个 i 茅台账号 在后台录入手机号,系统自动发验证码完成登录,把 toke

gitblog_00194的博客 1030

从签名算法到 Docker 一键部署:campus-imaotai 如何自动化 i茅台 每日申购

campus-imaotai 是一套面向 i茅台 App 的自动申购系统:它把"发验证码—登录—选门店—提交申购—查结果"整条链路做成服务端定时任务,用 MD5 签名与 AES 加密模拟移动端接口调用,配合 MySQL、Redis、Nginx 与 Vue 管理台,一条 Docker Compose 命令即可完成部署。项目作者声明本项目不提供成品、所复现的算法已被淘汰,代码定位是学习研究。 ##

gitblog_00076的博客 872

i茅台智能预约系统完整部署指南:从零搭建自动化申购平台

i茅台智能预约系统是一款基于Spring Boot和Vue.js技术栈开发的茅台申购自动化解决方案。该系统通过智能算法和多账号管理机制,帮助用户实现i茅台APP的自动预约功能,大幅提升预约成功率。本指南将详细介绍如何从零开始部署和使用这套系统。 ## 系统架构与核心特性 ### 技术架构解析 i茅台智能预约系统采用前后端分离的现代化架构设计: **后端服务层**: - 基于Spring B

gitblog_00309的博客 362

i茅台自动预约系统:每日 9 点批量申购的 Docker 部署方案

campus-imaotai 是一个基于 Java + Vue 的 i茅台自动预约系统:它把每天 9 点的申购,从"手动登录、选门店、点提交"变成一条定时任务批量完成,并通过 Docker 一键部署。适合想集中管理多个申购账号、又不想在 9 点守着手机的人。 ## 9 点前那几分钟,手动操作差在哪一步 早上 9:00 整,申购通道开放。手动操作要经历:打开 App → 输验证码登录 → 选商品

gitblog_00458的博客 712

Ruffle Flash 模拟器完整指南:如何用 Rust 让老 .swf 文件重新播放

Ruffle 是一个用 Rust 编写的 Flash Player 模拟器,目标是让 Adobe 在 2020 年停产 Flash 之后无人照看的 .swf 文件,在今天的电脑和浏览器里照常播放。本文从"它正在跑"的画面倒着讲:先看清画面背后发生了什么,再说明它替谁解决了什么问题、能力边界画在哪里、仓库里 30 多个 crate 如何分工,最后给出编译命令和一套由 4949 个测试用例支撑的验证手

gitblog_00578的博客 598

Cryptid新Stakes难度阶梯详解:10个从Pink到Ascendant的终极挑战攻略

Cryptid 是一款面向 Balatro(小丑牌)的非平衡(unbalanced)Mod,除了大量新增小额外,它最硬核的功能莫过于在原版 Gold 注(Gold Stake)之后一口气追加的 **24 个全新 Stakes 难度模式**——从 Pink 一路攀升到 Ascendant。本文带你快速看懂这条"地狱难度阶梯"的完整机制,并给出 10 条实战攻略,助你从新手一路爬到飞升。 [![Cr

gitblog_01077的博客 729

Edge TTS 完整指南:无需密钥与费用,用 3 行 Python 产出自然文本转语音

Edge TTS 让你直接在 Python 里调用 Microsoft Edge 的在线语音合成(TTS,即文本转语音)服务,不用安装 Edge 浏览器,不用 Windows,也不用申请 API 密钥。装好之后几行代码就能拿到自然度很高的 MP3 音频,还可以顺带生成 SRT 字幕(视频编辑里常见的字幕文件格式)。下面从安装、首句合成、挑音色到字幕导出,一次讲清。 ## 传统语音合成,是不是总要

gitblog_00070的博客 691

终极指南:如何快速搭建Campus-iMaoTai茅台预约系统,实现自动化申购

Campus-iMaoTai茅台预约系统是一款功能强大的自动化申购平台,能够帮助用户实现i茅台app的每日自动预约,支持docker一键部署,让茅台申购变得轻松简单。 [![茅台预约系统背景图](https://raw.gitcode.com/GitHub_Trending/ca/campus-imaotai/raw/ff23e422111f202042a4819e26cbffde232f517

gitblog_00750的博客 840
上一篇: AST入门与实战(一):基于babel库的js反混淆通用模板
下一篇: Dex文件混淆(一):BlackObfuscator
Qiled
博客等级 码龄11年 1053粉丝 53原创
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值