首页
社区
课程
招聘
[原创]某海外电商app设备指纹收集与加密算法分析
发表于: 5天前 1915

[原创]某海外电商app设备指纹收集与加密算法分析

5天前
1915

最近好像比较流行用ai水文章,我也用ai水几篇。这份是用ai自动分析了某电商的设备指纹函数trace。将3GB的trace文件交给ai自动分析后的结果。

我只使用QTrace对关键的指纹收集函数做trace,然后将trace文件直接交给ai自动分析,其余的不做干预。总共分析了不到20分钟
图片描述
报告包含完整的指纹采集点,源码级实现逻辑,和数据加密方式。

本报告中的结论分为三类:

所谓“源码级等价实现”,指在相同 Android 版本、相同应用 UID、相同私有数据、相同运行时 block list 和相同实时设备状态下,按 trace 观察到的 API、顺序、格式及编码方式重现采集。时间、随机数、内存、电池、网络、sequence 和密文按设计会随会话变化。

本次实际结果:Base64 长 14,500,解码后 10,873 字节。

加密算法链已经可以确定为:随机临时密钥材料 + 32 字节共享秘密派生 + SHA-256/HMAC/HKDF 类 KDF + ChaCha20-Poly1305 风格 AEAD 信封。 最后一层的结构与长度关系完全吻合 12-byte nonce + ciphertext + 16-byte tag;加密数据不是 AES 分组密文(总长不是 16 的倍数)。

注意:该包高度控制流混淆,静态伪代码中的函数名均为本文按行为命名。算法流程和长度关系为高置信度;固定服务端公钥/协议常量仍应从当前版本运行时内存提取,而不能凭空写死。

实际顺序严格为:

每个值先按 application/x-www-form-urlencoded 规则转义(空格变 +,保留字符按 UTF-8 %HH 编码),再以 & 连接。

第一层整体又作为一个字段嵌入第二层。trace 中完整观察到以下字段与顺序:

随后继续追加:

此外还观察到:

这里最重要的是:顺序固定、空字段保留、复杂字段先内部拼接后整体 URL 编码。改变顺序或省略空字段会改变最终密文和服务端解释。

trace 显示明文加密前长度为 0x2A1B = 10779 字节;固定/AAD 头长度为 0x52 = 82 字节。最终长度:

与实际 Base64 解码后的 10,873 字节完全一致。因此布局为:

sub_2D9794(0x2D9794)直接给出组包公式:

sub_2EBF5C(0x2EBF5C)逐字节构造:

对应伪代码:

sub_2EBF5C 的数据流:

sub_150144 / sub_14EAC0 是标准 Base64 编码路径;trace 最终明确按 "encryptedData":" + Base64 + "} 组装。

已由 trace 直接观察并确认:字段名称、字段顺序、大部分字段值、JNI/系统 API、URL 编码后的两层明文、明文长度、82/12/16 字节结构、最终 Base64 和 JSON 包装。

由反汇编与长度关系高置信度识别:X25519/ECDH 类 32-byte 密钥交换、HKDF-SHA256 类派生、ChaCha20-Poly1305 风格 AEAD。

当前证据中不能负责任地写死:两个协议字节、服务端长期公钥、header 两个 32-byte 槽位的官方字段名,以及少数混淆字段(如 p15)的官方业务命名。它们随版本/会话变化,正确实现应从调用参数和运行时产生,而不是复制本次样本值。

每个 pN 在 native 执行前还通过 APK 方法 ri.bg.cb("pN") 查询运行时 block list。调用者如需精确复现字段屏蔽,应在 buildPBlock 前应用同一 block list。

“能编译”不等于“准确”。对 trace 没有执行到 producer 的字段,写一个看似合理的 Android API 会制造错误结论。因此源码把这些字段显式定义成 AppInputs:调用方必须接入原始 Tomu 模块或继续针对相应 producer 补 trace。这样已观察到的采集逻辑可以完全重现,而未知项不会被伪造。

The source was compiled successfully against Android API 37's android.jar.

pBlock is a separate collected block seen in the trace. Its placement inside the product's higher-level request is controlled by the caller. The final native plaintext in this trace is the main query followed by the native fields in appendNativeFields.

A different application cannot completely reproduce Tomu's values merely by calling public Android APIs:

For exact same-device reproduction, execute this code in the original app process/UID and provide the AppInputs from the original app modules. Otherwise, the methods reproduce collection semantics but values legitimately differ.

以下代码不是摘要,而是报告对应的完整可编译采集实现。已使用 Android API 37 的 android.jar 编译验证。

{"encryptedData":"<standard-base64>"}
p1=...&p2=...&p3=...&p4=...&p5=...&p6=...&p7=...&p8=...&p9=...
&p10=...&p11=...&p12=...&p13=...&p14=...&p15=...&p16=...
&p17=...&p18=...&p19=...&p20=...&p21=...
version=35
process_id=8484
availablecapacity=78084268032
input_device=4|sec_touchscreen|0fa9d3d0d40f7f5b316320876742e00ad5eb1ba9|4355;
machine_arch=ARM
fk_result={"vInfo":{"exits":0,"id":""},"antInfo":{"exits":0}}
0c1999=
foreground=true
uuid=5951dc94-f0fc-45ee-a235-9befbeb1f365
secure_lock=0
totalcapacity=117153181696
mediaDrm=0:07c0...a2af1|Google|17.0.0|Widevine CDM;
c86fee=4.61.0
instrumentation=android.app.Instrumentation
u2=<另一个已加密并 Base64 的大字段>
ab9de2=1
060a12=
net_type=WIFI
01bf33=
currentTime=1784689721004
availablememory=3429867520
brightness=44
a5=1
74b954=36
kernelVersion=<Linux /proc/version>
eeba5e=en
02b81e=Asia/Shanghai
dme=1
string form_encode(const vector<pair<string,string>>& fields) {
    string out;
    for (size_t i = 0; i < fields.size(); ++i) {
        if (i) out += '&';
        out += url_encode(fields[i].first);  // 字段名本身通常无需变化
        out += '=';
        out += url_encode_utf8(fields[i].second); // 空格 -> '+',其余按 %HH
    }
    return out;
}

string build_p_block() {
    // 必须保持 p1 ... p21 的原始顺序;空值仍输出 key=
    return form_encode({
        {"p1", collect_service_and_virtualization()},
        {"p2", collect_keystore_cert_chain()},
        {"p3", collect_first_boot_state()},
        {"p4", collect_kernel_json()},
        {"p5", ""}, {"p6", ""}, {"p7", ""},
        {"p8", settings_boot_count()},
        {"p9", sim_operator_display()},
        {"p10", android_id_via_content_resolver_call()},
        {"p11", ""},
        {"p12", default_sms_package_record()},
        {"p13", display_record()},
        {"p14", installer_package_or_null()},
        {"p15", installer_environment_code()},
        {"p16", audio_stream_json()},
        {"p17", locale_language_country()},
        {"p18", timezone_id_and_name()},
        {"p19", power_profile_info()},
        {"p20", soc_model()},
        {"p21", settings_ad_aaid()}
    });
}

string build_plaintext() {
    vector<pair<string,string>> f;
    // 按 trace 中固定顺序 push version ... dme
    // 再 push platform_type ... bgid;空值不能省略。
    // 某些复杂子结构先自行用 |、;、, 拼接,再作为一个整体 URL 编码。
    return form_encode(f);
}
82 + 12 + 10779 + 16 = 10873
offset  size                 含义
0       82                   protocol header / AAD
82      12                   AEAD nonce
94      plaintext_length     ciphertext(与明文等长)
94+N    16                   authentication tag
out_len = plaintext_len + 16 + aad_len + 12;
out = new byte[out_len];
AEAD_encrypt(key32, plaintext, plaintext_len,
             aad82, 82, nonce12, ciphertext_and_tag);
memcpy(out, aad82, 82);
memcpy(out + 82, nonce12, 12);
memcpy(out + 94, ciphertext_and_tag, plaintext_len + 16);
0x00  2   协议版本/算法标记(两个固定字节)
0x02 32   临时公钥/公钥材料 A
0x22 32   随机 salt/公钥材料 B
0x42  8   64 位时间/随机数,按固定端序写入
0x4A  8   调用方传入的 64 位请求标识
总计 0x52 (82)
header[0] = PROTOCOL_BYTE_0;
header[1] = PROTOCOL_BYTE_1;
memcpy(header + 2,  ephemeral_public_or_material_A, 32);
memcpy(header + 34, random_salt_or_material_B,       32);
store_u64_protocol_endian(header + 66, generated_u64);
memcpy(header + 74, &request_id_u64, 8);
uint8_t eph_private[32] = secure_random_32();
uint8_t eph_public[32]  = x25519_public(eph_private);
uint8_t shared[32]      = x25519(eph_private, server_public_key);
uint8_t salt[32]        = secure_random_32();
uint8_t key[32]         = hkdf_sha256(shared, salt, /*info=*/empty, 32);
vector<uint8_t> encrypt_envelope(
    bytes plaintext,
    uint64_t request_id,
    bytes32 server_public_key)
{
    bytes32 sk   = secure_random_32();
    bytes32 pub  = x25519_public(sk);
    bytes32 salt = secure_random_32();
    bytes32 ss   = x25519(sk, server_public_key);
    bytes32 key  = hkdf_sha256(ss, salt, {}, 32);

    byte aad[82];
    aad[0] = PROTOCOL_BYTE_0;
    aad[1] = PROTOCOL_BYTE_1;
    memcpy(aad + 2, pub, 32);
    memcpy(aad + 34, salt, 32);
    store_u64_protocol_endian(aad + 66, generated_u64());
    memcpy(aad + 74, &request_id, 8);

    byte nonce[12] = secure_random_12();
    auto [ciphertext, tag16] = chacha20_poly1305_encrypt(
        key, nonce, plaintext, /*AAD=*/aad);

    return concat(aad, nonce, ciphertext, tag16);
}

string final_result =
    "{\"encryptedData\":\"" + base64_standard(envelope) + "\"}";
EnvCollector.AppInputs appInputs = new EnvCollector.Defaults() {
    @Override public String uuid() { return loadTomuPersistedUuid(); }
    @Override public String u2() { return generateU2(); }
    @Override public String cookie() { return loadCookie(); }
    @Override public String p15() { return calculateP15(); }
    @Override public String didInfo() { return buildDidInfo(); }
};

String pBlock = EnvCollector.buildPBlock(context, appInputs);
String main = EnvCollector.buildMainQuery(context, appInputs);
String plaintext = EnvCollector.appendNativeFields(context, main, appInputs);
package tomu.reproduction;

import android.app.ActivityManager;
import android.app.Application;
import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.media.MediaDrm;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.os.LocaleList;
import android.os.Process;
import android.os.StatFs;
import android.provider.Settings;
import android.provider.Telephony;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.InputDevice;
import android.view.WindowManager;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;

/**
 * Source-level equivalent of the collectors observed in tomu_env_9.trace.
 *
 * This class deliberately uses LinkedHashMap and explicit append order. The native
 * implementation does not sort fields. Empty fields are emitted rather than omitted.
 *
 * Android hidden APIs and package visibility affect several results. Running this in
 * another package/UID cannot reproduce Tomu's privileged visibility or private files.
 */
public final class EnvCollector {
    private static final String PREF = "se_config";
    private static final String PREF_DEVICE_ID = "secure_sharedpreference_id";
    private static final String PREF_SEQUENCE = "secure_local_sequence_id";
    private static final UUID WIDEVINE = new UUID(
            0xedef8ba979d64aceL, 0xa3c827dcd51d21edL);

    private EnvCollector() {}

    /** Values owned by other Tomu modules or still opaque in the supplied trace. */
    public interface AppInputs {
        String fkResult();             // sample: {"vInfo":...,"antInfo":...}
        String uuid();                 // persisted Tomu UUID, not a fresh UUID each call
        String u2();                   // separate anti-abuse encrypted token
        String cookie();               // sample is a 16-byte value in standard Base64
        String bgid();
        String uidField();
        String field0c1999();
        String field060a12();
        String field01bf33();
        String fieldA5();
        String field74b954();
        String dme();
        String p3();                   // package-service/first-boot status; sample -4
        String p5();
        String p6();
        String p7();
        String p11();
        String p15();                  // sample 900063915; business derivation remains opaque
        String simOperatorDisplay();   // sample 中國移動香港 China Mobile HK
        String socModelOverride();
        String p19PowerProfile();
        String romStatus();
        String didInfo();
        String sequence();
    }

    public static final class Defaults implements AppInputs {
        public String fkResult() { return "{\"vInfo\":{\"exits\":0,\"id\":\"\"},\"antInfo\":{\"exits\":0}}"; }
        public String uuid() { return ""; }
        public String u2() { return ""; }
        public String cookie() { return ""; }
        public String bgid() { return ""; }
        public String uidField() { return ""; }
        public String field0c1999() { return ""; }
        public String field060a12() { return ""; }
        public String field01bf33() { return ""; }
        public String fieldA5() { return "1"; }
        public String field74b954() { return ""; }
        public String dme() { return "1"; }
        public String p3() { return "-4"; }
        public String p5() { return ""; }
        public String p6() { return ""; }
        public String p7() { return ""; }
        public String p11() { return ""; }
        public String p15() { return ""; }
        public String simOperatorDisplay() { return ""; }
        public String socModelOverride() { return ""; }
        public String p19PowerProfile() { return ""; }
        public String romStatus() { return ""; }
        public String didInfo() { return ""; }
        public String sequence() { return "0"; }
    }

    public static String buildPBlock(Context context, AppInputs in) {
        StringBuilder q = new StringBuilder(8192);
        addRaw(q, "p1", formEncode(collectP1ServiceProbe()));
        addRaw(q, "p2", collectP2KeyStoreChain());
        addRaw(q, "p3", nz(in.p3()));
        addRaw(q, "p4", collectP4KernelJson());
        addRaw(q, "p5", nz(in.p5()));
        addRaw(q, "p6", nz(in.p6()));
        addRaw(q, "p7", nz(in.p7()));
        addRaw(q, "p8", getSetting(context, "boot_count"));
        // Trace shows p9 retained Unicode and spaces; do not encode it here.
        addRaw(q, "p9", nz(in.simOperatorDisplay()));
        addRaw(q, "p10", getAndroidIdViaProviderCall(context));
        addRaw(q, "p11", nz(in.p11()));
        addRaw(q, "p12", collectDefaultSms(context));
        addRaw(q, "p13", formEncode(collectDisplays(context)));
        addRaw(q, "p14", String.valueOf(getInstallerPackage(context)));
        addRaw(q, "p15", nz(in.p15()));
        addRaw(q, "p16", collectAudioJson(context));
        addRaw(q, "p17", collectLocale(context));
        addRaw(q, "p18", collectTimeZone());
        addRaw(q, "p19", nz(in.p19PowerProfile()));
        addRaw(q, "p20", collectSoc(in));
        addRaw(q, "p21", getSetting(context, "ad_aaid"));
        return q.toString();
    }

    /** First query observed as one Java string before native collector fields are added. */
    public static String buildMainQuery(Context c, AppInputs in) {
        LinkedHashMap<String, String> m = new LinkedHashMap<>();
        m.put("version", Integer.toString(Build.VERSION.SDK_INT));
        m.put("process_id", Integer.toString(Process.myPid()));
        m.put("availablecapacity", Long.toString(storageAvailable()));
        m.put("input_device", collectInputDevices());
        m.put("machine_arch", machineArch());
        m.put("fk_result", nz(in.fkResult()));
        m.put("0c1999", nz(in.field0c1999()));
        m.put("foreground", Boolean.toString(isForeground(c)));
        m.put("uuid", nz(in.uuid()));
        m.put("secure_lock", collectSecureLock(c));
        m.put("totalcapacity", Long.toString(storageTotal()));
        m.put("mediaDrm", collectWidevine());
        m.put("c86fee", appVersion(c));
        m.put("instrumentation", instrumentationClass());
        m.put("u2", nz(in.u2()));
        m.put("ab9de2", "1");
        m.put("060a12", nz(in.field060a12()));
        m.put("net_type", networkType(c));
        m.put("01bf33", nz(in.field01bf33()));
        m.put("currentTime", Long.toString(System.currentTimeMillis()));
        m.put("availablememory", Long.toString(availableMemory(c)));
        m.put("brightness", screenBrightness(c));
        m.put("a5", nz(in.fieldA5()));
        m.put("74b954", nz(in.field74b954()));
        m.put("kernelVersion", readFirstLine("/proc/version"));
        m.put("eeba5e", Locale.getDefault().getLanguage());
        m.put("02b81e", TimeZone.getDefault().getID());
        m.put("dme", nz(in.dme()));
        return encodeMap(m);
    }

    /** Native fields appended after buildMainQuery(), in observed order. */
    public static String appendNativeFields(Context c, String main, AppInputs in) {
        StringBuilder q = new StringBuilder(main);
        addEncoded(q, "platform_type", "1");
        addEncoded(q, "sharedpreference_id", sharedPreferenceId(c));
        addEncoded(q, "dpi", Integer.toString(displayMetrics(c).densityDpi));
        DisplayMetrics dm = displayMetrics(c);
        addEncoded(q, "resolution", dm.widthPixels + "x" + dm.heightPixels);
        addEncoded(q, "uid", nz(in.uidField()));
        addEncoded(q, "cookie", nz(in.cookie()));
        addEncoded(q, "cpuinfo", collectCpuInfo());
        addEncoded(q, "build_id", Build.ID);
        addEncoded(q, "fingerprint", Build.FINGERPRINT);
        addEncoded(q, "battery_status", collectBattery(c));
        addEncoded(q, "boot_time", collectBootInfo());
        addEncoded(q, "root", collectRoot());
        addEncoded(q, "rom_status", nz(in.romStatus()));
        PackageInfo pi = ownPackageInfo(c);
        addEncoded(q, "install_time", pi == null ? "" : Long.toString(pi.firstInstallTime));
        addEncoded(q, "app_update_time", pi == null ? "" : Long.toString(pi.lastUpdateTime));
        // Misspelling is protocol-significant.
        addEncoded(q, "input_mathod", Settings.Secure.getString(c.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD));
        addEncoded(q, "ip_list", collectIpList());
        addEncoded(q, "total_memory", Long.toString(totalMemoryBytes()));
        addEncoded(q, "sequence", nz(in.sequence()));
        addEncoded(q, "local_sequence", Integer.toString(nextLocalSequence(c)));
        addEncoded(q, "did_info", nz(in.didInfo()));
        addEncoded(q, "simState", collectSimState(c));
        addEncoded(q, "bgid", nz(in.bgid()));
        return q.toString();
    }

    public static String collectP1ServiceProbe() {
        List<String> services = listServicesHidden();
        String[] suspects = {"oppo", "miui", "vivo", "meizu", "redmi", "xiaomi", "huawei",
                "oneplus", "lineage", "samsung", "xposed", ".leui.", "realme", "asus",
                "blackshark", "meitu", "libsu"};
        List<String> hits = new ArrayList<>();
        for (String service : services) {
            String lower = service.toLowerCase(Locale.ROOT);
            for (String suspect : suspects) {
                if (lower.contains(suspect)) { hits.add(service); break; }
            }
        }
        // Exact key spelling "exits" is intentional and observed in trace.
        return "{\"vInfo\":{\"exits\":" + (hits.isEmpty() ? "0" : "1")
                + ",\"id\":\"\"},\"systemService\":\"" + jsonEscape(hits.toString()) + "\"}";
    }

    @SuppressWarnings("unchecked")
    private static List<String> listServicesHidden() {
        try {
            Class<?> sm = Class.forName("android.os.ServiceManager");
            String[] a = (String[]) sm.getDeclaredMethod("listServices").invoke(null);
            List<String> out = new ArrayList<>();
            if (a != null) Collections.addAll(out, a);
            return out;
        } catch (Throwable ignored) { return Collections.emptyList(); }
    }

    public static String collectP2KeyStoreChain() {
        for (String alias : new String[]{"tomu_secure_v2", "tomu_secure"}) {
            try {
                KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
                ks.load(null);
                if (!ks.containsAlias(alias)) continue;
                Certificate[] chain = ks.getCertificateChain(alias);
                if (chain == null) continue;
                StringBuilder out = new StringBuilder();
                for (Certificate cert : chain) {
                    out.append(Base64.encodeToString(cert.getEncoded(), Base64.NO_WRAP)).append(';');
                }
                return formEncode(out.toString());
            } catch (Throwable ignored) {}
        }
        return "";
    }

    public static String collectP4KernelJson() {
        String procVersion = readFirstLine("/proc/version");
        String uname = exec("uname", "-a").trim();
        // The trace encodes the two values, but leaves JSON punctuation unescaped.
        return "{\"1\":\"" + formEncode(procVersion) + "\",\"2\":\"" + formEncode(uname) + "\"}";
    }

    public static String getAndroidIdViaProviderCall(Context c) {
        try {
            ContentResolver r = c.getContentResolver();
            Bundle b = r.call(Uri.parse("content://settings/secure"), "GET_secure", "android_id", null);
            if (b != null) return nz(b.getString("value"));
        } catch (Throwable ignored) {}
        return nz(Settings.Secure.getString(c.getContentResolver(), Settings.Secure.ANDROID_ID));
    }

    public static String collectDefaultSms(Context c) {
        try {
            String pkg = Telephony.Sms.getDefaultSmsPackage(c);
            if (pkg == null) return "";
            PackageManager pm = c.getPackageManager();
            PackageInfo pi;
            if (Build.VERSION.SDK_INT >= 28) {
                pi = pm.getPackageInfo(pkg, PackageManager.GET_SIGNING_CERTIFICATES);
            } else {
                pi = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
            }
            ApplicationInfo ai = pi.applicationInfo;
            String label = ai == null ? "" : ai.loadLabel(pm).toString();
            Signature sig = firstSignature(pi);
            String md5 = sig == null ? "" : md5Upper(sig.toCharsString().getBytes(StandardCharsets.UTF_8));
            long version = Build.VERSION.SDK_INT >= 28 ? pi.getLongVersionCode() : pi.versionCode;
            int flags = ai == null ? 0 : ai.flags;
            return pkg + "|" + label + "|" + md5 + "|" + version + "|" + flags;
        } catch (Throwable ignored) { return ""; }
    }

    private static Signature firstSignature(PackageInfo pi) {
        if (Build.VERSION.SDK_INT >= 28 && pi.signingInfo != null) {
            Signature[] s = pi.signingInfo.hasMultipleSigners()
                    ? pi.signingInfo.getApkContentsSigners()
                    : pi.signingInfo.getSigningCertificateHistory();
            return s == null || s.length == 0 ? null : s[0];
        }
        return pi.signatures == null || pi.signatures.length == 0 ? null : pi.signatures[0];
    }

    public static String collectDisplays(Context c) {
        try {
            DisplayManager dm = (DisplayManager) c.getSystemService(Context.DISPLAY_SERVICE);
            StringBuilder out = new StringBuilder();
            for (Display d : dm.getDisplays()) {
                String owner = null;
                try {
                    Field f = Display.class.getDeclaredField("mDisplayInfo");
                    f.setAccessible(true);
                    Object info = f.get(d);
                    Field ownerField = info.getClass().getDeclaredField("ownerPackageName");
                    ownerField.setAccessible(true);
                    owner = (String) ownerField.get(info);
                } catch (Throwable ignored) {}
                out.append(d.getName()).append('|').append(String.valueOf(owner)).append(';');
            }
            return out.toString();
        } catch (Throwable ignored) { return ""; }
    }

    public static String collectAudioJson(Context c) {
        AudioManager a = (AudioManager) c.getSystemService(Context.AUDIO_SERVICE);
        LinkedHashMap<String,Integer> streams = new LinkedHashMap<>();
        streams.put("voiceCall", AudioManager.STREAM_VOICE_CALL);
        streams.put("system", AudioManager.STREAM_SYSTEM);
        streams.put("ring", AudioManager.STREAM_RING);
        streams.put("music", AudioManager.STREAM_MUSIC);
        streams.put("alarm", AudioManager.STREAM_ALARM);
        streams.put("notification", AudioManager.STREAM_NOTIFICATION);
        StringBuilder s = new StringBuilder("{");
        for (Map.Entry<String,Integer> e : streams.entrySet()) {
            if (s.length() > 1) s.append(',');
            int min = Build.VERSION.SDK_INT >= 28 ? a.getStreamMinVolume(e.getValue()) : 0;
            s.append('"').append(e.getKey()).append("\":{\"min\":").append(min)
                    .append(",\"max\":").append(a.getStreamMaxVolume(e.getValue()))
                    .append(",\"cur\":").append(a.getStreamVolume(e.getValue())).append('}');
        }
        return s.append('}').toString();
    }

    public static String collectLocale(Context c) {
        Locale locale;
        if (Build.VERSION.SDK_INT >= 24) {
            LocaleList l = c.getResources().getConfiguration().getLocales();
            locale = l.isEmpty() ? Locale.getDefault() : l.get(0);
        } else locale = c.getResources().getConfiguration().locale;
        String country = locale.getCountry();
        return locale.getLanguage() + (country.isEmpty() ? "" : "_" + country);
    }

    public static String collectTimeZone() {
        TimeZone z = TimeZone.getDefault();
        return z.getID() + " " + z.getDisplayName();
    }

    public static String collectSoc(AppInputs in) {
        String override = nz(in.socModelOverride());
        if (!override.isEmpty()) return override;
        if (Build.VERSION.SDK_INT >= 31 && Build.SOC_MODEL != null) return Build.SOC_MODEL;
        String[] props = {"ro.soc.model", "ro.board.platform", "ro.hardware"};
        for (String p : props) { String v = systemProperty(p); if (!v.isEmpty()) return v; }
        return Build.HARDWARE;
    }

    public static String collectInputDevices() {
        StringBuilder out = new StringBuilder();
        for (int id : InputDevice.getDeviceIds()) {
            InputDevice d = InputDevice.getDevice(id);
            if (d == null) continue;
            out.append(id).append('|').append(d.getName()).append('|')
                    .append(d.getDescriptor()).append('|')
                    .append(d.getVendorId()).append(';');
        }
        return out.toString();
    }

    public static boolean isForeground(Context c) {
        ActivityManager am = (ActivityManager)c.getSystemService(Context.ACTIVITY_SERVICE);
        ActivityManager.RunningAppProcessInfo me = new ActivityManager.RunningAppProcessInfo();
        ActivityManager.getMyMemoryState(me);
        return me.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
                || me.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
    }

    public static String collectSecureLock(Context c) {
        try {
            KeyguardManager k = (KeyguardManager)c.getSystemService(Context.KEYGUARD_SERVICE);
            return k.isDeviceSecure() ? "1" : "0";
        } catch (Throwable ignored) { return "0"; }
    }

    public static String collectWidevine() {
        MediaDrm drm = null;
        try {
            drm = new MediaDrm(WIDEVINE);
            String id = sha256Lower(drm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID));
            return "0:" + id + "|" + drm.getPropertyString(MediaDrm.PROPERTY_VENDOR)
                    + "|" + drm.getPropertyString(MediaDrm.PROPERTY_VERSION)
                    + "|" + drm.getPropertyString(MediaDrm.PROPERTY_DESCRIPTION) + ";";
        } catch (Throwable ignored) { return ""; }
        finally { if (drm != null) try { drm.close(); } catch (Throwable ignored) {} }
    }

    public static String instrumentationClass() {
        try {
            Class<?> at = Class.forName("android.app.ActivityThread");
            Method current = at.getDeclaredMethod("currentActivityThread");
            current.setAccessible(true);
            Object thread = current.invoke(null);
            Field f = at.getDeclaredField("mInstrumentation");
            f.setAccessible(true);
            Object inst = f.get(thread);
            return inst == null ? "" : inst.getClass().getName();
        } catch (Throwable ignored) { return ""; }
    }

    public static String collectBattery(Context c) {
        Intent i = c.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        if (i == null || i.getExtras() == null) return "";
        // Bundle keySet iteration order is deliberately retained: trace used keySet().iterator().
        StringBuilder out = new StringBuilder();
        Bundle b = i.getExtras();
        for (String key : b.keySet()) {
            Object value = b.get(key);
            out.append(key).append(',').append(String.valueOf(value)).append(',');
        }
        return out.toString();
    }

    public static String collectIpList() {
        StringBuilder out = new StringBuilder();
        try {
            Enumeration<NetworkInterface> es = NetworkInterface.getNetworkInterfaces();
            while (es.hasMoreElements()) {
                NetworkInterface ni = es.nextElement();
                if (!ni.isUp()) continue;
                Enumeration<InetAddress> as = ni.getInetAddresses();
                while (as.hasMoreElements()) {
                    InetAddress a = as.nextElement();
                    if (!a.isLoopbackAddress()) out.append(a.getHostAddress()).append(';');
                }
            }
        } catch (Throwable ignored) {}
        return out.toString();
    }

    public static String collectRoot() {
        String[] paths = {"/product/bin/su", "/sbin/su", "/system/bin/su", "/system/sbin/su",
                "/system/xbin/su", "/vendor/bin/su", "/data/misc/riru"};
        for (String p : paths) if (new File(p).exists()) return "1";
        return "0";
    }

    public static String collectCpuInfo() {
        String cpu = readAll("/proc/cpuinfo");
        for (String line : cpu.split("\\n")) {
            String l = line.toLowerCase(Locale.ROOT);
            if (l.startsWith("hardware") || l.startsWith("model name")) {
                int p = line.indexOf(':');
                if (p >= 0) return line.substring(p + 1).trim();
            }
        }
        return Build.HARDWARE;
    }

    public static String collectBootInfo() {
        return exec("cat", "/proc/sys/kernel/random/boot_id").trim();
    }

    public static long totalMemoryBytes() {
        try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/meminfo")))) {
            String line;
            while ((line = r.readLine()) != null) {
                if (line.startsWith("MemTotal:")) {
                    String digits = line.replaceAll("[^0-9]", "");
                    return Long.parseLong(digits) * 1024L;
                }
            }
        } catch (Throwable ignored) {}
        return 0;
    }

    public static int nextLocalSequence(Context c) {
        SharedPreferences p = c.getSharedPreferences(PREF, Context.MODE_PRIVATE);
        int current = p.getInt(PREF_SEQUENCE, 0);
        p.edit().putInt(PREF_SEQUENCE, current + 1).apply();
        return current;
    }

    public static String sharedPreferenceId(Context c) {
        return c.getSharedPreferences(PREF, Context.MODE_PRIVATE).getString(PREF_DEVICE_ID, "");
    }

    public static String collectSimState(Context c) {
        try {
            Object tm = c.getSystemService(Context.TELEPHONY_SERVICE);
            Method m = tm.getClass().getMethod("getSimState");
            return Integer.toString((Integer)m.invoke(tm));
        } catch (Throwable ignored) { return "0"; }
    }

    private static long storageAvailable() {
        try { return new StatFs(Environment.getDataDirectory().getPath()).getAvailableBytes(); }
        catch (Throwable ignored) { return 0; }
    }

    private static long storageTotal() {
        try { return new StatFs(Environment.getDataDirectory().getPath()).getTotalBytes(); }
        catch (Throwable ignored) { return 0; }
    }

    private static long availableMemory(Context c) {
        ActivityManager.MemoryInfo m = new ActivityManager.MemoryInfo();
        ((ActivityManager)c.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(m);
        return m.availMem;
    }

    private static DisplayMetrics displayMetrics(Context c) {
        DisplayMetrics dm = new DisplayMetrics();
        WindowManager wm = (WindowManager)c.getSystemService(Context.WINDOW_SERVICE);
        wm.getDefaultDisplay().getRealMetrics(dm);
        return dm;
    }

    private static String screenBrightness(Context c) {
        try { return Integer.toString(Settings.System.getInt(c.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS)); }
        catch (Throwable ignored) { return ""; }
    }

    private static String machineArch() {
        String abi = Build.SUPPORTED_ABIS.length == 0 ? "" : Build.SUPPORTED_ABIS[0].toLowerCase(Locale.ROOT);
        if (abi.contains("arm")) return "ARM";
        if (abi.contains("x86")) return "X86";
        return abi.toUpperCase(Locale.ROOT);
    }

    private static String networkType(Context c) {
        try {
            ConnectivityManager cm = (ConnectivityManager)c.getSystemService(Context.CONNECTIVITY_SERVICE);
            Network n = cm.getActiveNetwork();
            NetworkCapabilities nc = cm.getNetworkCapabilities(n);
            if (nc == null) return "NONE";
            if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) return "WIFI";
            if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) return "MOBILE";
            if (nc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) return "ETHERNET";
        } catch (Throwable ignored) {}
        return "UNKNOWN";
    }

    private static String appVersion(Context c) {
        PackageInfo p = ownPackageInfo(c);
        return p == null ? "" : nz(p.versionName);
    }

    private static PackageInfo ownPackageInfo(Context c) {
        try { return c.getPackageManager().getPackageInfo(c.getPackageName(), 0); }
        catch (Throwable ignored) { return null; }
    }

    private static String getInstallerPackage(Context c) {
        try {
            if (Build.VERSION.SDK_INT >= 30) {
                InstallSourceInfo s = c.getPackageManager().getInstallSourceInfo(c.getPackageName());
                return s.getInstallingPackageName();
            }
            return c.getPackageManager().getInstallerPackageName(c.getPackageName());
        } catch (Throwable ignored) { return null; }
    }

    private static String getSetting(Context c, String key) {
        String v = null;
        try { v = Settings.Global.getString(c.getContentResolver(), key); } catch (Throwable ignored) {}
        if (v == null) try { v = Settings.Secure.getString(c.getContentResolver(), key); } catch (Throwable ignored) {}
        return nz(v);
    }

    private static String systemProperty(String key) {
        try {
            Class<?> c = Class.forName("android.os.SystemProperties");
            return nz((String)c.getDeclaredMethod("get", String.class).invoke(null, key));
        } catch (Throwable ignored) { return ""; }
    }

    private static String encodeMap(LinkedHashMap<String,String> map) {
        StringBuilder q = new StringBuilder();
        for (Map.Entry<String,String> e : map.entrySet()) addEncoded(q, e.getKey(), e.getValue());
        return q.toString();
    }

    private static void addEncoded(StringBuilder q, String key, String value) {
        if (q.length() != 0) q.append('&');
        q.append(key).append('=').append(formEncode(nz(value)));
    }

    private static void addRaw(StringBuilder q, String key, String alreadyEncodedOrRawValue) {
        if (q.length() != 0) q.append('&');
        q.append(key).append('=').append(nz(alreadyEncodedOrRawValue));
    }

    /** java.net.URLEncoder semantics observed in trace: UTF-8 and spaces represented by '+'. */
    public static String formEncode(String s) {
        try { return URLEncoder.encode(nz(s), "UTF-8"); }
        catch (Throwable ignored) { return nz(s); }
    }

    private static String md5Upper(byte[] bytes) throws Exception {
        return hex(MessageDigest.getInstance("MD5").digest(bytes), true);
    }

    private static String sha256Lower(byte[] bytes) throws Exception {
        return hex(MessageDigest.getInstance("SHA-256").digest(bytes), false);
    }

    private static String hex(byte[] bytes, boolean upper) {
        char[] alphabet = (upper ? "0123456789ABCDEF" : "0123456789abcdef").toCharArray();
        char[] out = new char[bytes.length * 2];
        for (int i=0;i<bytes.length;i++) { out[i*2]=alphabet[(bytes[i]>>>4)&15]; out[i*2+1]=alphabet[bytes[i]&15]; }
        return new String(out);
    }

    private static String readFirstLine(String path) {
        try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(path)))) {
            return nz(r.readLine());
        } catch (Throwable ignored) { return ""; }
    }

    private static String readAll(String path) {
        try (InputStream in = new FileInputStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            byte[] b = new byte[4096]; int n;
            while ((n=in.read(b))>=0) out.write(b,0,n);
            return out.toString("UTF-8");
        } catch (Throwable ignored) { return ""; }
    }

    private static String exec(String... argv) {
        try {
            java.lang.Process p = new ProcessBuilder(argv).redirectErrorStream(true).start();
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            try (InputStream in = p.getInputStream()) { byte[] b=new byte[1024]; int n; while((n=in.read(b))>=0) out.write(b,0,n); }
            p.waitFor();
            return out.toString("UTF-8");
        } catch (Throwable ignored) { return ""; }
    }

    private static String jsonEscape(String s) {
        return nz(s).replace("\\", "\\\\").replace("\"", "\\\"");
    }

    private static String nz(String s) { return s == null ? "" : s; }
}
EnvCollector.AppInputs inputs = new EnvCollector.Defaults() {
    @Override public String uuid() {
        return loadOriginalTomuUuid();
    }

    @Override public String u2() {
        return generateOriginalU2Token();
    }

    @Override public String cookie() {
        return loadOriginalCookie();
    }

    @Override public String p15() {
        return calculateOriginalP15();
    }

    @Override public String didInfo() {
        return buildOriginalDidInfo();
    }
};

String pBlock = EnvCollector.buildPBlock(context, inputs);
String javaMainQuery = EnvCollector.buildMainQuery(context, inputs);
String nativePlaintext = EnvCollector.appendNativeFields(
        context, javaMainQuery, inputs);

// pBlock 由更高层调用方放入对应业务参数;nativePlaintext 是本 trace
// 中进入最终 native 加密信封的数据主体。

注意:该包高度控制流混淆,静态伪代码中的函数名均为本文按行为命名。算法流程和长度关系为高置信度;固定服务端公钥/协议常量仍应从当前版本运行时内存提取,而不能凭空写死。

参数 本次值/含义 具体采集方法
p1 {"vInfo":{"exits":0,"id":""},"systemService":"[]"} android.os.ServiceManager.listServices();结合厂商/虚拟化关键字检查。trace 中枚举了 284 个 service,并逐个与 oppo/miui/vivo/.../xposed/libsu 等比较。
p2 tomu_secure_v2 Android Keystore 证书链,4 张证书以 ; 拼接 KeyStore.getInstance("AndroidKeyStore") → load(null) → containsAlias("tomu_secure_v2") / 回退 tomu_secure → getCertificateChain() → 每张 X509Certificate.getEncoded() → Base64。
p3 -4 通过 ServiceManager.getService("package") 及 IPackageManager.Stub.TRANSACTION_isFirstBoot 等系统状态检测所得状态码。
p4 JSON,键 1 为 /proc/version,键 2 为 uname -a 读取内核版本;trace 明确执行 uname -a。
p5 空 本次未取得。
p6 空 本次未取得。
p7 空 本次未取得。
p8 429 Settings.Global/Secure.getString(contentResolver, "boot_count")。
p9 中國移動香港 China Mobile HK SIM/运营商信息。
p10 3240a81b31837bae ContentResolver.call(content://settings/secure, "GET_secure", "android_id", null),再从 Bundle 取 value。
p11 空 本次未取得。
p12 `包名 标签
p13 `Built-in Screen null;`
p14 null 当前包名及 PackageManager.getInstallerPackageName();本次安装来源为空。
p15 900063915 安装来源/包环境派生的整数标识;trace 能确认其位置和值,当前证据不足以给该整数命名为某个公开 Android 字段。
p16 六类音量 JSON AudioManager.getStreamMinVolume/getStreamMaxVolume/getStreamVolume,包含 voiceCall/system/ring/music/alarm/notification。
p17 en_CA Resources → Configuration → LocaleList → 首个 Locale 的 language/country。
p18 Asia/Shanghai GMT+08:00 TimeZone.getDefault().getID() 和 getDisplayName()。
p19 空 尝试反射 com.android.internal.os.PowerProfile.sPowerItemMap,本次无值。
p20 SM7250 SoC/硬件平台信息。
p21 空 查询 Settings 的 ad_aaid,本次为空。
字段 实际内容/采集方式
platform_type 1
sharedpreference_id SharedPreferences("se_config").getString("secure_sharedpreference_id", "");本次 1cda03acb0484d718ac373ad6cd91475。
dpi DisplayMetrics.densityDpi;本次 440。
resolution widthPixels + "x" + heightPixels;本次 1080x2340。
uid 当前 UID/派生值;本次为空。
cookie 本次 Cm3AFGpE44VWp0sh8sa0Ag==,24 字符标准 Base64,解码 16 字节。
root 检查 /product/bin/su、/sbin/su、/system/bin/su、/system/sbin/su、/system/xbin/su、/vendor/bin/su;并检查 /data/misc/riru。
fingerprint Build.FINGERPRINT;本次 google/redfin/redfin:14/UP1A.../release-keys。
build_id UP1A.231105.001.B2。
rom_status ROM/启动状态。
cpuinfo CPU 硬件信息;本次可见 Qualcomm Technologies, Inc LITO。
input_mathod Settings.Secure["default_input_method"],本次 Google LatinIME。
battery_status 注册 ACTION_BATTERY_CHANGED,遍历 extras 全部键值;含 technology、health、status、plugged、present、charge_counter、level、scale、temperature、voltage、cycle count 等。
install_time PackageInfo.firstInstallTime。
app_update_time PackageInfo.lastUpdateTime。
boot_time /proc/sys/kernel/random/boot_id 及系统时间/文件时间组合。
ip_list 枚举 NetworkInterface,仅取 up 的接口,再枚举非 loopback 地址;本次包括 rmnet_data0/dummy0/wlan0 的 IPv6 和 wlan0 IPv4。
total_memory /proc/meminfo;本次 7823970304。
sequence 本次 0。
local_sequence SharedPreferences("se_config")["secure_local_sequence_id"],取出后递增并 apply();本次送出 627。
did_info 多个时间、文件状态、boot/device ID 等以 ; 拼接并 URL 编码。
simState SIM 状态;本次 0。
bgid 设备/业务标识,本次为空。
地址 本文命名 作用
0x2EAB50 top-level encrypt 调用密钥协商、AEAD、Base64。
0x2EBF5C build crypto context/header 生成临时密钥、共享秘密、salt、82-byte header。
0x2EC39C derive key 调用 HMAC/HKDF 路径生成 32-byte key。
0xEABBC secure random 生成随机字节。
0xE0544 public-key derivation 32-byte 私钥 → 32-byte 公钥材料。
0xE0D40 shared-secret 32-byte × 32-byte → 32-byte 共享秘密。
0xEA4AC / 0xEA7D4 HMAC/HKDF SHA-256/HMAC 状态与 32-byte 输出。
0x2D9794 AEAD envelope 12-byte nonce、16-byte tag、AAD 前缀组包。
0xD9E38 AEAD core wrapper 对明文执行认证加密。
0x150144 / 0x14EAC0 Base64 二进制信封编码为标准 Base64。
字段 Java 等价实现
p1 collectP1ServiceProbe():反射 ServiceManager.listServices(),按 trace 中关键字扫描,生成带 exits 拼写的 JSON。
p2 collectP2KeyStoreChain():读取 AndroidKeyStore 的 tomu_secure_v2,回退 tomu_secure,证书 DER 标准 Base64 后用 ; 连接。
p3 AppInputs.p3():trace 只覆盖结果 -4,未覆盖其完整 Binder producer,不伪造。
p4 collectP4KernelJson():/proc/version 与 uname -a。
p5/p6/p7 AppInputs;样本为空。
p8 getSetting(...,"boot_count")。
p9 AppInputs.simOperatorDisplay();样本为运营商显示文本,但 trace 未覆盖其上游 Telephony 调用。
p10 getAndroidIdViaProviderCall(),与 trace 的 content://settings/secure / GET_secure / android_id / value 一致。
p11 AppInputs.p11();样本为空。
p12 collectDefaultSms();默认短信包、label、MD5(Signature.toCharsString UTF-8) 大写十六进制、version、flags。
p13 collectDisplays();DisplayManager、名称、反射 mDisplayInfo.ownerPackageName。
p14 getInstallerPackage()。
p15 AppInputs.p15();样本值 900063915,但 producer 未在 trace 覆盖。
p16 collectAudioJson();六个 AudioManager stream 的 min/max/current。
p17 collectLocale()。
p18 collectTimeZone()。
p19 AppInputs.p19PowerProfile();trace 尝试 hidden PowerProfile.sPowerItemMap,样本为空。
p20 collectSoc();优先 Build.SOC_MODEL,回退系统属性。
p21 getSetting(...,"ad_aaid")。
字段 Java 等价实现/来源
version Build.VERSION.SDK_INT
process_id Process.myPid()
availablecapacity StatFs(data).getAvailableBytes()
input_device collectInputDevices()
machine_arch Build.SUPPORTED_ABIS[0] 归一为 ARM/X86
fk_result AppInputs.fkResult()
0c1999 AppInputs.field0c1999()
foreground ActivityManager.getMyMemoryState()
uuid AppInputs.uuid(),必须是 Tomu 持久化 UUID,不是临时随机 UUID
secure_lock KeyguardManager.isDeviceSecure()
totalcapacity StatFs(data).getTotalBytes()
mediaDrm collectWidevine():Widevine deviceUniqueId SHA-256 小写 hex、vendor/version/description
c86fee 当前 app versionName
instrumentation ActivityThread.mInstrumentation.getClass().getName()
u2 AppInputs.u2(),独立加密 token producer 不在本 trace
ab9de2 常量 1
060a12 AppInputs.field060a12()
net_type ConnectivityManager / NetworkCapabilities
01bf33 AppInputs.field01bf33()
currentTime System.currentTimeMillis()
availablememory ActivityManager.MemoryInfo.availMem
brightness Settings.System.SCREEN_BRIGHTNESS
a5 AppInputs.fieldA5()
74b954 AppInputs.field74b954()
kernelVersion /proc/version
eeba5e Locale.getDefault().getLanguage()
02b81e TimeZone.getDefault().getID()
dme AppInputs.dme()

传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

收藏
点赞 ・64
打赏
分享
最新回复 (28)
雪    币: 4801
活跃值: (8277)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2 楼
感谢分享!
5天前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3 楼
6666
4天前
0
雪    币: 778
活跃值: (3717)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4 楼
666
4天前
0
雪    币: 102
活跃值: (4171)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5 楼
我去,乐哥出品,前排占位。
4天前
0
雪    币: 3712
活跃值: (2716)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6 楼
1
4天前
0
雪    币: 0
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7 楼
1
4天前
0
雪    币: 9389
活跃值: (6293)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8 楼
666
4天前
0
雪    币: 158
活跃值: (5856)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9 楼
感谢分享。
4天前
0
雪    币: 1901
活跃值: (6003)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
10 楼
1123
4天前
0
雪    币: 3262
活跃值: (6390)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11 楼
666
4天前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12 楼
太强了
4天前
0
雪    币: 1349
活跃值: (1880)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13 楼
666
4天前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14 楼
tql
4天前
0
雪    币: 1200
活跃值: (755)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15 楼
666
4天前
0
雪    币: 95
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16 楼
777
4天前
0
雪    币: 323
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17 楼
6666
4天前
0
雪    币: 7779
活跃值: (25275)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
18 楼
看看
4天前
0
雪    币: 1688
活跃值: (1618)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
19 楼
6666
3天前
0
雪    币: 530
活跃值: (2655)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20 楼
666
3天前
0
雪    币: 405
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21 楼
6666
3天前
0
雪    币: 206
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22 楼
谢谢分享,学习一下
3天前
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23 楼
666
2天前
0
雪    币: 208
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24 楼
666
23小时前
0
雪    币: 1043
活跃值: (6574)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
25 楼
大佬。。这3g文件怎么分析,,我用ai工具大文件分析好像并不是很理想,有什么要处理的,还是用agent配合?
22小时前
0
游客
登录 | 注册 方可回帖
返回