APP安全(一)-防二次打包(C、C++签名校验)

如何防止 Android 应用被二次打包? 其中,my-release-key.keystore 是 keystore 文件名,my_alias 是数字证书别名,RSA 是使用的加密算法,2048 是密钥大小,validity 是证书有效期。在 Android 应用中集成反调试技术,可以在代码中检测是否处于调试状态,如果是,则采取相应的措施,例如退出应用或者强制关闭调试器。注意,某些设备可能存在定制化 ROM,例如小米、华为等,这些设备的系统属性可能与标准 Android 设备不同,需要进行额外的处理。 阅读详情

前言

由于Android系统的开放性,开发出来的APP很容易被逆向,修改代码逻、加入广告、病毒等二次打包后发布,对开发者和用户造成一定的损失。因此我们的APP运行过程中需要进行签名校验,以及使用加解密算法对数据进行处理,从而保证访问服务端的请求是我们信任的APK。签名校验如果放在Java语言层面,很容易被逆向,因此需要在C/C++层进行校验,从而增加破解的难度。

校验逻辑

  • APP启动时候初始化我们所写的动态库,一般在Application的onCrate()方法中初始化
  • 获取APP的签名SHA1值
  • 匹配获取到的值是否与C/C++中写入的值相同
  • APP在加载so文件是,会自动调用JNI_OnLoad方法,因此在该方法中进行判断,如果签名不匹配,返回-1,让App Crash

实现步骤

第一步:NDK环境搭建

NDK环境的搭建可参考谷歌官方文档,已经翻译为中文;

第二步:编写Java层代码

1,工具类

public class SecurityTool {
    static {
        System.loadLibrary("hts-security");
    }
    public static void init(){}
}

2,在Application中初始化

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        SecurityTool.init();
    }
}

SecurityTool的init(){}方法只写了一个空方法,根据虚拟机类的初始化机制,当遇到new,getstatic,putstatic、invokestatic这4条字节码指令时,会执行类的初始化。类的初始化过程中,先执行静态代码块,因此会自动执行我们动态库的OnLoad,完成校验。

第二步:C++代码完成校验

定义校验文件:app-validate.cpp

//
// Created by qj on 2020/1/8.
//

const char HEX_CODE[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
const char *APP_SIGNATURE = "6C97D7FC026DB630523D6ECA15941129F62B0083";
char *appSha1 = NULL;


/**
 *
 * get sha1  by reflect
 *
 */
char* getAppSha1(JNIEnv *env, jobject context_object){
        if(appSha1 == NULL){
            jclass context_class = env->GetObjectClass(context_object);
            jmethodID methodId = env->GetMethodID(context_class, "getPackageManager", "()Landroid/content/pm/PackageManager;");
            jobject package_manager = env->CallObjectMethod(context_object, methodId);
            methodId = env->GetMethodID(context_class, "getPackageName", "()Ljava/lang/String;");
            jstring package_name = (jstring)env->CallObjectMethod(context_object, methodId);
            env->DeleteLocalRef(context_class);
            jclass pack_manager_class = env->GetObjectClass(package_manager);
            methodId = env->GetMethodID(pack_manager_class, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
            env->DeleteLocalRef(pack_manager_class);
            jobject package_info = env->CallObjectMethod(package_manager, methodId, package_name, 0x40);
            env->DeleteLocalRef(package_manager);
            jclass package_info_class = env->GetObjectClass(package_info);
            jfieldID fieldId = env->GetFieldID(package_info_class, "signatures", "[Landroid/content/pm/Signature;");
            env->DeleteLocalRef(package_info_class);
            jobjectArray signature_object_array = (jobjectArray)env->GetObjectField(package_info, fieldId);
            jobject signature_object = env->GetObjectArrayElement(signature_object_array, 0);
            env->DeleteLocalRef(package_info);
            jclass signature_class = env->GetObjectClass(signature_object);
            methodId = env->GetMethodID(signature_class, "toByteArray", "()[B");
            env->DeleteLocalRef(signature_class);
            jbyteArray signature_byte = (jbyteArray) env->CallObjectMethod(signature_object, methodId);
            jclass byte_array_input_class=env->FindClass("java/io/ByteArrayInputStream");
            methodId=env->GetMethodID(byte_array_input_class,"<init>","([B)V");
            jobject byte_array_input=env->NewObject(byte_array_input_class,methodId,signature_byte);
            jclass certificate_factory_class=env->FindClass("java/security/cert/CertificateFactory");
            methodId=env->GetStaticMethodID(certificate_factory_class,"getInstance","(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
            jstring x_509_jstring=env->NewStringUTF("X.509");
            jobject cert_factory=env->CallStaticObjectMethod(certificate_factory_class,methodId,x_509_jstring);
            methodId=env->GetMethodID(certificate_factory_class,"generateCertificate",("(Ljava/io/InputStream;)Ljava/security/cert/Certificate;"));
            jobject x509_cert=env->CallObjectMethod(cert_factory,methodId,byte_array_input);
            env->DeleteLocalRef(certificate_factory_class);
            jclass x509_cert_class=env->GetObjectClass(x509_cert);
            methodId=env->GetMethodID(x509_cert_class,"getEncoded","()[B");
            jbyteArray cert_byte=(jbyteArray)env->CallObjectMethod(x509_cert,methodId);
            env->DeleteLocalRef(x509_cert_class);
            jclass message_digest_class=env->FindClass("java/security/MessageDigest");
            methodId=env->GetStaticMethodID(message_digest_class,"getInstance","(Ljava/lang/String;)Ljava/security/MessageDigest;");
            jstring sha1_jstring=env->NewStringUTF("SHA1");
            jobject sha1_digest=env->CallStaticObjectMethod(message_digest_class,methodId,sha1_jstring);
            methodId=env->GetMethodID(message_digest_class,"digest","([B)[B");
            jbyteArray sha1_byte=(jbyteArray)env->CallObjectMethod(sha1_digest,methodId,cert_byte);
            env->DeleteLocalRef(message_digest_class);
            jsize array_size=env->GetArrayLength(sha1_byte);
            jbyte* sha1 =env->GetByteArrayElements(sha1_byte,NULL);
            char *hex_sha=new char[array_size*2+1];
            for (int i = 0; i <array_size ; ++i) {
                hex_sha[2*i]=HEX_CODE[((unsigned char)sha1[i])/16];
                hex_sha[2*i+1]=HEX_CODE[((unsigned char)sha1[i])%16];
            }
            hex_sha[array_size*2]='\0';
            appSha1 = hex_sha;
        }

        return appSha1;
}


jboolean checkSignature(JNIEnv *env, jobject context){
   const char *appSignatureSha1 = getAppSha1(env,context);
   jstring releaseSignature = env->NewStringUTF(APP_SIGNATURE);
   jstring appSignature = env->NewStringUTF(appSignatureSha1);
   const char *charAppSignature = env->GetStringUTFChars(appSignature, NULL);
   const char *charReleaseSignature = env->GetStringUTFChars(releaseSignature, NULL);
   jboolean result = JNI_FALSE;
    if (charAppSignature != NULL && charReleaseSignature != NULL) {
       if (strcmp(charAppSignature, charReleaseSignature) == 0) {
           result = JNI_TRUE;
       }
    }
    env->ReleaseStringUTFChars(appSignature, charAppSignature);
    env->ReleaseStringUTFChars(releaseSignature, charReleaseSignature);
    return result;
}


/**
 *
 *get Application Context
 *
 */
jobject getApplication(JNIEnv *env){
    jobject application = NULL;
    jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");
    if (activity_thread_clz != NULL) {
        jmethodID currentApplication = env->GetStaticMethodID(
                activity_thread_clz, "currentApplication", "()Landroid/app/Application;");
        if (currentApplication != NULL) {
            application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication);
        }
        env->DeleteLocalRef(activity_thread_clz);
    }
    return application;
}


/**
 *
 *validate signature
 *
 */
jboolean checkSignature(JNIEnv *env){
    jobject appContext = getApplication(env);
    if(appContext != NULL){
        return checkSignature(env,appContext);
    }

    return JNI_FALSE;
}

hts-security.cpp调用校验方法

#include <jni.h>
#include <string>
#include "app-validate.cpp"

extern "C" {

    JNIEXPORT jint JNICALL
    JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env;
        if (vm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
            return -1;
        }
        if (checkSignature(env) != JNI_TRUE) {
            return -1;
        }

        return JNI_VERSION_1_6;

    }
}

注意事项:

const char *APP_SIGNATURE = "6C97D7FC026DB630523D6ECA15941129F62B0083";

该值为apk签名证书SHA1值,Windows环境下debug.keystore一般存储在C:\Users\xx用户xx\.android目录下,进该目录输入:

keytool -list -v -keystore debug.keystore

秘钥库口令一般为:android

完成后可获得证书信息:

密钥库类型: jks
密钥库提供方: SUN

您的密钥库包含 1 个条目

别名: androiddebugkey
创建日期: 2019-1-4
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: C=US, O=Android, CN=Android Debug
发布者: C=US, O=Android, CN=Android Debug
序列号: 1
有效期为 Fri Jan 04 10:14:36 CST 2019 至 Sun Dec 27 10:14:36 CST 2048
证书指纹:
         MD5:  A5:1A:C1:16:03:AA:54:BB:DF:A8:D8:E8:FC:20:93:EB
         SHA1: 6C:97:D7:FC:02:6D:B6:30:52:3D:6E:CA:15:94:11:29:F6:2B:00:83
         SHA256: 21:E5:21:EA:13:9D:06:ED:1B:D7:D5:F8:B7:F3:B1:41:5B:34:B9:73:B5:5D:6E:F1:B8:6B:7F:80:8B:0C:EF:10
签名算法名称: SHA1withRSA
主体公共密钥算法: 1024 位 RSA 密钥
版本: 1


*******************************************
*******************************************

其中SHA1: 6C:97:D7:FC:02:6D:B6:30:52:3D:6E:CA:15:94:11:29:F6:2B:00:83,去掉":"为C++中对比值。

其他证书的SHA1值可参考上述方法。

需要改进的点:

通过反射获取证书能被 hook掉与PMS交互的IPackageManager即可完美破解,没有这块的逆向经验,所以没有试验过。针对这块网上有文章使用 APK安装时的备份,解压APK获取CERT.RSA,然后提取公钥,在进行校验。具体可参考:Android安全机制之NDK实现防钩子签名校验

总结

使用上述方法在C++层面实现了APK签名的校验,但如果仅仅只有这层校验,远远是不够的;因为逆向后删除SecurityTool.init();这行代码,我们所写的动态库就形同虚设,完全没有作用。因此我们我们还在需要在所写的动态库中加入加密算法,和服务交互的数据需要经过我们所写的动态库进行加密,服务端拿到数据后进行解密验证,这样才能保证我们所写的动态库的价值。签名校验完成了第一步,防止二次打包,后续将结合OpenSSL进行加解密,这样保证我们整个APP的安全。

项目github 地址

Android-APK:为何你的应用老是被破解,该如何有效地做签名校验 /** 做普通的签名校验 */ private boolean doNormalSignCheck() { String trueSignMD5 = “d0add9987c7c84aeb7198c3ff26ca152”; String nowSignMD5 = “”; try { // 得到签名的MD5 PackageInfo packageInfo = getPackageManager().getPackageInfo( getPackageName(), PackageManager.GET_SIG 阅读详情

相关推荐

【APP逆向】通过修改smali代码绕过签名校验

目录一、签名校验二、定位入口函数三、定位校验签名函数四、修改smali绕过签名校验五、重新打包/签名/安装检验 一、签名校验 签名验证,就是在APP中写入自己私钥的hash值,和一个获取当前签名中的私钥hash值的函数两个值相一致,那么就说明APP没有被改动允许APP运行。如果两值不一致那么说明APP是被二次打包的,APP就自我销毁进程。 签名验证又可以在两个地方做,一个是在MainActivity.java的OnCreate函数中做,一个是在原生代码文件的JNI_OnLoad函数中做。 在OnCrea

ssrf 2964

Android-安全-签名验证让二次打包变的更难

二次打包的危害性如果你没有对你的应用做任何的安全保障措施,那么你的应用就非常的危险首先了解一下什么是二次打包: 二次打包 通过工具apktool、dex2jar、jd-gui、DDMS、签名工具获取源码,嵌入恶意病毒、广告等行为再利用工具打包、签名,形成二次打包应用。 二次打包的一个小演示这是代码: TextView tv = (TextView) findViewById(R.id.tv

浅浅同学的开发笔记 1万+

动态对抗的艺术:Frida Hook技术在App签名校验绕过中的实战应用

本文深入探讨了Frida Hook技术在动态绕过App签名校验中的实战应用。通过分析签名校验原理,详细介绍了如何利用Frida进行战前侦察、定位关键校验点,并编写Hook脚本实现动态绕过。文章结合CTF赛题实战演练,展示了从静态分析到动态注入的完整流程,并探讨了对抗Native层校验、代码混淆等高级场景,为移动安全研究人员提供了一套行之有效的动态对抗方法论。

weixin_29323493的博客 225

android 加固防止反编译-重新打包

1、需要加密的Apk(源Apk) 2、壳程序Apk(负责解密Apk工作) 3、加密工具(将源Apk进行加密和壳Dex合并成新的Dex) 主要步骤: 我们拿到需要加密的Apk和自己的壳程序Apk,然后用加密算法对源Apk进行加密在将壳Apk进行合并得到新的Dex文件,最后替换壳程序中的dex文件即可,得到新的Apk,那么这个新的Apk我们也叫作脱壳程序Apk.他已经不是一个完

u010921385的博客 8440

Android应用安全加固实战:Dex加密、SO库保护与签名校验全解析

在移动应用开发领域,代码安全与防护是保障知识产权和用户数据安全的核心议题。其基本原理在于通过混淆、加密和完整性验证等技术手段,提升应用对抗逆向工程和恶意篡改的能力。从技术价值看,一套有效的安全方案能显著增加攻击成本,保护核心业务逻辑与算法,尤其对金融、游戏等高价值应用至关重要。典型的应用场景包括防止APK反编译、抵御动态调试以及阻止重打包等。本文聚焦于Android平台,深入探讨了实现全栈防护的三大关键技术:Dex加密通过对字节码进行加密与动态加载,有效对抗静态分析;SO库保护结合代码混淆与反调试,守护Na

weixin_29323273的博客 391

Android应用签名校验原理、绕过思路与安全攻防实战

在移动应用安全领域,数字签名是保障应用完整性与来源可信的核心机制。其原理基于非对称加密技术,开发者使用私钥对应用内容生成哈希摘要并加密,系统则通过公钥验证签名,确保应用在分发与安装过程中未被篡改。这项技术的核心价值在于构建了应用商店生态的安全基石,防止恶意二次打包、保护知识产权,并为应用间安全共享数据提供信任基础。在实际工程中,签名校验贯穿APK打包、安装验证及运行时检查等多个层级,其中V1/V2/V3签名方案的演进,特别是V2签名对APK整体字节的保护,显著提升了防篡改能力。然而,在安全测试与逆向工程场景

weixin_30800807的博客 413

移动安全面试题—APP 安全开发

安全开发: 遵循安全编码规范,及时修复已知漏洞,避免使用不安全的接口或组件。加固应用: 采用代码混淆、资源加密、JNI/NDK 等技术,提高破解难度。完整性检测: 对应用签名、哈希值等进行验证,确保应用未被篡改。环境检测: 检测运行环境,如模拟器、root 状态等,以降低攻击者的调试能力。反调试技术: 通过各种手段阻止或干扰攻击者对应用进行调试。安全通信: 使用 HTTPS、TLS 等安全协议进行网络通信,保护数据传输的安全。

Andy0214的专栏 6243

逆向小红书:从防调试到协议签名的算法攻防实战

本文深入探讨了逆向分析小红书App协议签名算法的实战过程。从绕过防调试与SSL Pinning,到追踪Retrofit/OkHttp3拦截器链,最终聚焦于生成核心签名`X-Sign`的Native层`process`函数。文章揭示了该签名算法的强上下文关联特性,并分享了当直接破解困难时,协议复用、模拟点击等备用思路与实战工具链,为理解现代移动应用安全风控提供了深度视角。

weixin_29234509的博客 1167

Rust NDK 开发#2 - 签名校验

代码:[hijack_rust · Chris/ThinDroid](https://gitee.com/hulytu/thin-droid/tree/master/hijack_rust。来创建一个项目,这个目录可以放在和当前项目目录下,也可以放在其他地方,建议和当前项目放在一起。进行二次打包,都会加上签名校验的操作,这部分代码写在。写的签名校验例子很多,下面分享下关于使用。在开发中,为了防止别人对我们的。这个目录,名字可以取其他的。,把此目录复制到项目中,如。文件,删除原来的代码。

KQe397773106的博客 1094

安卓逆向实战:绕过签名校验的5种常见方法(附代码示例)

本文深入探讨了安卓应用安全中的签名校验机制,并提供了五种实战绕过方法,包括静态代码修补、动态Hook、环境隔离、网络流量篡改和Native层破解。文章结合代码示例,旨在帮助安全研究人员和开发者理解防御原理,提升逆向工程能力与安全防护意识。

weixin_29269441的博客 1352

鸿蒙HAP包安全加固与自动化签名实战指南

本文详细介绍了鸿蒙HAP包安全加固与自动化签名的完整实战流程。针对HAP包结构,重点讲解如何利用Virbox Protector对核心so库进行深度加固,并区分Stage与FA模型进行重新打包。同时,深入解析了DevEco Studio自动化签名配置、证书原理,并提供了构建自动化流水线的方案,帮助开发者高效保障应用安全并提升发布效率。

android的专栏 981

安卓App加固后 如何进行二次签名

 通过对App加固,提升App 安全性,防止Apk(安卓应用软件包)被入侵、篡改、山寨,提升代码的安全性和被破解难度。不过,在对Apk加固后,Apk原有的签名信息会丢失,需要重新签名,否则,无法在安装到手机等安卓设备上。   因此Apk重签名需要和加固前保持一致,否则会触发加固的防二次打包功能,导致Apk无法正常运行。   Apk的签名的作用   软件签名并非安卓系统首创,在诺基亚主导的塞班(Symbian OS)时代就开始运行。通过签名每个程序开发商都拥有合法账号ID。   Apk签名类似在开发者

cover_se的博客 9

Android加固技术:防止二次打包与篡改

Android应用安全问题日益突出,特别是二次打包和代码篡改已成为开发者面临的主要威胁。本文旨在系统介绍Android应用加固技术,帮助开发者构建更安全的应用程序。文章首先介绍基础概念,然后深入技术细节,包括算法原理和实现方法,最后讨论实际应用和未来趋势。二次打包:将合法应用解包后修改内容再重新打包发布的行为代码混淆:通过改变代码结构使其难以理解但功能不变的技术动态加载:在运行时而非编译时加载代码的技术AI驱动的加固技术硬件级安全方案(如TEE)区块链技术应用更智能的动态防护。

移动开发前沿的博客 1228

APP安全(二)-c、c++AES实现加解密

一、前言 APP安全(一)-防二次打包(C、C++签名校验)介绍过签名校验,防止二次打包,还达不到安全的目的,我们需要保证我们所写的代码不能被绕过,还需要服务端配合,完成数据的加解密以及校验,这样能够达到巩固签名校验的目的,同时服务端校验失败后不用返回业务数据以及网络传输的数据被抓包软件抓取后不能随意的破解。在HTTPS中,对传输的数据采用的是AES算法实现,因此,我们在也使用AES算法实现,这样...

mrRuby的博客 901

移动开发技术学习资源D

研发团队在本领域有着多年的研究和经验,有成熟的、实际使用的产品。

Emuelec系统安装全流程:超详细版指南(从零开始)

手把手带你完成emuelec系统的完整安装,从基础准备到系统配置一步不落,适合新手入门。深入解析emuelec在各类设备上的部署技巧,让复古游戏体验更流畅。

weixin_42594419的博客 1970

什么是App加固?

应用程序在运行过程中实时检测是否有调试工具附着在其上,如果检测到调试行为,则采取相应的措施,如终止应用运行、提示用户或进行数据保护等,防止攻击者通过调试工具获取应用内部信息或篡改应用逻辑。:通过对代码进行混淆处理,将代码中的类名、方法名、变量名等标识符替换为无意义的名称,打乱代码结构,使反编译后的代码难以理解,增加逆向工程的难度。通过以上多方面的加固措施,可以显著提高 App 的安全性,保护用户数据和开发者的权益,使 App 能够在安全的环境中运行,为用户提供可靠的服务。

gl123987654的博客 678

FUXA实战:5步搞定基于Web的SCADA系统搭建(含Node.js版本避坑指南)

本文提供了一份基于开源工具FUXA搭建Web版SCADA系统的实战指南。文章重点解析了从环境准备到生产部署的五个核心步骤,并特别强调了Node.js版本选择对西门子S7 PLC通信兼容性的关键影响,帮助开发者避开常见陷阱,快速构建工业监控界面。

weixin_29172963的博客 140

Android so库防客户端破解的解决方案

背景随着移动互联网的发展,移动应用的安全问题越来越突显,特别是涉及到钱相关的产品,前段一段时间,我们的Android客户端产品被人破解了,修改了一些代码,重新打包签名后,就可以免费获得资源,对我们的收入造成了一些影响,移动产品安全性就不得不提重视,安全性这个话题很大,包括客户端、服务端、数据存储、协议等很多方面,这里只是从客户端的角度来讨论一上如何保证客户端产品的安全性,抛砖引玉,也希望大家多提意...

weixin_34417814的博客 4084

TEA-Tiny Encryption算法在FPGA的实现

在FPGA原型中,我们定义了一个TEA_FPGA模块,该模块包含一个输入数据端口(plain)、一个输入密钥端口(key)和一个输出数据端口(cipher)。在模块中,我们通过32轮的循环处理来实现TEA算法的加密过程,并将最终结果存储在cipher变量中。由于FPGA硬件的高速性能,这种基于HDL的TEA算法实现可以在短时间内完成大量的数据加密处理,从而提高加密效率和性能。总之,在FPGA中实现TEA算法可以大大提高加密效率和性能,通过使用硬件描述语言HDL来实现可以更高效、更快速地完成数据加密操作。

BitSlinger的博客 272

OceanSim: 基于Isaac Sim GPU 加速水下机器人感知仿真框架

OceanSim 采用先进的基于物理的渲染技术,对水下视觉和声学传感器进行精确建模。与其他模拟引擎相比,OceanSim 的渲染速度要快得多。作为 NVIDIA Issac Sim 的扩展,OceanSim 采用了多功能设计。通过融入快速发展的 NVIDIA Omniverse 生态系统,我们的模拟器自然而然地在机器人学习和水下机器人技术之间架起了一座桥梁,从而促进了水下应用中体现型人工智能技术的开发和部署。OceanSim 将以开放源码的方式提供,使研究界能够以合作的模式作出贡献。

机器人控制与仿真应用 2050

告别云打包!用Android Studio离线打包uni-app APK的保姆级避坑指南

本文详细介绍了如何使用Android Studio进行uni-app的本地打包,帮助开发者避开云打包的种种限制。从环境配置到签名优化,再到高阶配置和调试技巧,提供了全面的避坑指南,特别适合频繁迭代的敏捷团队。通过本地打包,开发者可以显著提升效率、降低成本并增强安全性。

weixin_33739541的博客 591

从码农到生态建造者:我的一人公司运作方案

华哥·OPC 运营方案 v1.0 - 第一次迭代 🚀,搭建闭环生态系统,赋能 AI 时代的超级个体,更好的做自己,同时照亮别人。

AaronZZH 的博客 994
上一篇: JVM运行时数据区域
下一篇: APP安全(二)-c、c++AES实现加解密
mrRuby
博客等级 码龄11年 20粉丝 63原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值