【Android】逆向开发与反逆向开发入门知识(一)

目录

  • 逆向开发
    • 反编译 & 反混淆
      • 反编译工具
      • 反编译
      • 反混淆
  • 修改预置资源文件
  • 抓包
    • 前期准备
    • 二次打包
    • 重签名
  • 如何预防 App 被逆向开发?
    • 代码混淆
    • 应用加固
    • 防止动态调试
    • Root 检测
    • 二次打包检测

警告:逆向开发相关知识请在法律规定范围内使用,请勿使用相关技术进行违法犯罪行为。

逆向开发

反编译 & 反混淆

反编译 是指将已经编译成机器码或字节码的程序文件,还原成接近源代码的形式的过程。

反编译工具

  • Apktool:是一款用于对第三方、封闭、二进制 Android 应用进行逆向工程的工具。它可以将资源解码为接近原始形式,并在进行一些修改后重建它们;它可以逐步调试 smali 代码。由于具有类似项目的文件结构和一些重复任务(例如构建 apk 等)的自动化,它还可以更轻松地使用应用。

  • dex2jar:此工具可以把 dex 字节码转换为 Java 字节码,当然,也可以将 dex 反汇编为 smali 文件并从 smali 文件组装 dex,使得你可以使用标准的Java反编译器(如 jd-gui)来查看代码。

  • jd-gui:这是一个独立的图形界面程序,用来浏览Java类文件中的源代码。当与 dex2jar 一起使用时,它可以显示反编译后的 Java 代码。

  • jadx:这是一个用于分析、反编译、解密Dalvik字节码和Java字节码、smali 调试器的强大工具,支持将.dex或.jar文件反编译为接近于原始源代码的格式。

  • fernflower:它是 IntelliJ IDEA 中内置的反编译器,也可以作为命令行工具使用,能够生成高质量的 Java 源代码。

反编译

使用 Apktool 进行反编译,使用的指令为:

  • 解码:
apktool d apk所在路径/apk名称

打开 Windows命令行控制台 输入指令按回车即可对 apk 文件进行反编译。

在这里插入图片描述
完成后,会在本地生成一个文件夹,apk的资源就都在里边了。

在这里插入图片描述

反混淆

反混淆是逆向工程的重要手段,可以帮助开发者理解、调试和研究已发布的应用程序。

混淆相关的知识点可以参考本人以往博客文章 👉 【Android】App攻防之代码混淆

PS:混淆是提高 Android 应用安全性和减少 APK 大小的有效手段,但需要谨慎配置,以避免混淆后产生新的问题。

混淆后的 apk 可以通过一系列工具进行反混淆的操作,例如:

  • JEB:是一款功能强大的反编译器,广泛用于逆向工程、安全分析和软件开发领域。JEB支持多种格式的文件和语言,提供了丰富的功能来帮助用户分析和理解二进制代码。

这些工具可以实现反混淆的同时,但是呢部分功能也是需要收费的,也很不巧,反混淆功能就在他们的收费列表之中…

这就难顶了,我以为大家都是为爱发电…

修改预置资源文件

执行上述反编译的操作后,生成的文件内容如下:

在这里插入图片描述
当我们使用 Android Studio 打包项目时,源代码(通常是 Java 或 Kotlin )首先会被编译成 Java 字节码(.class文件)。然后,这些.class文件会被进一步转换为 Dalvik 字节码(专为Android平台设计的一种中间代码形式),并打包到 classes.dex 文件中。如下图的 classes.dex 文件和 classes2.dex 文件。
在这里插入图片描述
扯那么多,那 Dalvik 字节码 和 smali 代码又是什么关系?

smali 是一种类似于汇编语言的文本格式,用于表示 Dalvik 字节码。

这也就解释了为什么反编译后生成的类它变成了 .smali 文件扩展名的文件。

在这里插入图片描述
如果你真的需要仅仅通过一个 .apk 文件就把人家的代码完全理解,那么,smali 将是你必须要跨过去的坎。

但是!!!如果,你只是简单的学习一下人家的部分代码是如何编写的,可以利用某一些工具去简化自己的学习成本,例如:

  • jadx-gui:上面我们有提到过,这个可以将一些 smali 代码转回 Java 代码,使用这个工具,可以最快的获取 apk 文件里面的信息。
  • smali2javasmali2java 是一个专门用于将 smali 代码转换为 Java 代码的工具。它可以直接处理 smali 文件并生成相应的 Java 源代码。

由于 smali 代码是一种低级别的汇编语言,学习起来需要一定的时间,这里就不修改 smali 文件,而是修改打包时没有被进行加密处理的 AndroidManifest.xml 资源文件 。

在这里插入图片描述
修改后点击保存,再给 apk 重签名 + 二次打包就能得到一个修改后的 apk 了。

抓包

Proxyman 是一款高性能的抓包应用程序,使开发人员能够查看来自应用程序和域的 HTTP/HTTPS 请求,包括 iOS 设备、iOS 模拟器Android 设备。

前期准备

  1. 下载 Proxyman

    点击下载 👉 Proxyman

  2. 安装完成后打开 Proxyman ,根据指引安装证书到本地电脑

  3. 找一台 Root 过的 Android 设备,让设备网络与电脑网络处于同一局域网,并能使用电脑 pingAndroid 设备的 IP 地址。

  4. 进入 Android 设备的网络设置,为 以太网WIFI 配置代理信息。配置的代理信息如下:

    Server:192.1.68.1.5
    Port:9090
    Authentication:No

    配置代理需要确保 Android 设备上的 VPN 被关闭,以免代理冲突。

  5. Android 设备上打开浏览器,访问 http://proxy.man/ssl 网址下载 CA 证书。

  6. 打开设置,根据 安全 → 加密与凭证 → 安装证书 → CA 证书 的操作步骤安装刚刚下载的 CA 证书。

  7. 如果你的 Android 系统版本高于 10 ,需要在你的 Android 项目上配置信任 CA 证书,即在 res/xml/network_security_config.xml 加上如下配置:

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config><debug-overrides><trust-anchors><!-- Trust user added CAs while debuggable only --><!-- 信任用户添加的CA证书 --><certificates src="user" /><!-- 信任系统预装的CA证书 --><certificates src="system" /></trust-anchors></debug-overrides><!-- 允许明文流量(即HTTP请求)--><base-config cleartextTrafficPermitted="true"><!-- 配置指定信任的证书来源。--><trust-anchors><!-- 表示信任系统预装的CA证书和用户添加的CA证书 --><certificates src="system" /><!-- 信任系统预装的CA证书和用户添加的CA证书 --><certificates src="user" /></trust-anchors></base-config>
    </network-security-config>
    

    将配置文件的引用添加到 AndroidManifest.xml

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"><applicationandroid:networkSecurityConfig="@xml/network_security_config"></application>
    </manifest>
    
  8. 打开APP
    具体详情可参考 👉 Android Device & Emulator

注意事项:
1、http 的网络请求在任何的 Android 版本上都可以进行抓包,https 的网络请求在Android 10及以上的Android 版本抓包,必须需要在 Android 项目加上一些配置才能抓包。
2、Android 10 及以上的 Android 版本需要在 Android 系统上配置 CA 证书。
3、使用抓包工具进行抓包的电脑要求要和 Android 端的网络处于同一局域网,且使用电脑能够 ping 通。

二次打包

二次打包是指在原始 APK 文件的基础上进行一些修改,然后重新编译生成一个新的 APK 文件。

打包使用的 apktool 指令为:

apktool b apk文件夹所在路径/apk文件夹

在这里插入图片描述
至此完成二次打包的操作。

重签名

由于 APK 的签名机制是与打包过程直接相关的,当你对 APK 进行二次打包时,APK 的内容(如 classes.dex、资源文件等)可能已经发生变化,这会导致原有的签名失效。

重签名需要生成一个签名文件,可以选择使用 Android Studio 生成,或者使用 keytool 命令生成。

以下为 keytool 生成签名的指令:

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000     

以上各个参数的含义如下:

  • genkey:这个参数表示keytool要生成一个新的密钥对和一个自签名的证书。
  • alias android_keystore:这个参数指定了生成的密钥对和证书的别名(alias),此处别名为 android_keystore,这个别名在密钥库中用于唯一标识这个特定的密钥对和证书。
  • keyalg RSA:这个参数指定了用于生成密钥对的算法,即RSA算法。RSA是一种广泛使用的非对称加密算法,它使用一对密钥:一个公钥用于加密数据,另一个私钥用于解密数据。
  • validity 10000:这个参数设置了证书的有效期,以天为单位,此处证书的有效期是 10000 天。
  • keystore android.keystore:这个参数指定了密钥库(keystore)的文件名,即android.keystore。密钥库是一个用于存储密钥对和证书的数据库文件。

确认信息点击回车,就会提示你输入一系列的信息。

在这里插入图片描述
最后面输入字母 Y 确认输入的信息,并生成一个签名。

打包完成后还需要进行签名,麻烦,反编译一个 apk 一堆事…

到这里,逆向开发的入门操作就完成啦,快试试将 apk 安装到你的设备上运行试一试吧!

如何预防 App 被逆向开发?

代码混淆

代码混淆是一种软件保护技术,用于增加逆向工程的难度,使源代码或字节码更难以被理解和修改。在移动应用开发中,特别是 Android 应用,代码混淆是非常重要的一步,可以有效防止应用被反编译和破解。

在 Android 中,除了可以使用 AS 提供的 ProGuardR8 进行混淆,还可以借助一些第三方库实现混淆。例如:

  • DexGuard:ProGuard 的商业版本,提供了更强大的混淆和保护功能。它不仅包括 ProGuard 的所有功能,还增加了更多的安全特性。
  • AndResGuard:一个专门针对 Android 应用资源文件进行混淆和压缩的工具。它可以帮助你减小 APK 的体积,同时提高应用的安全性。

关于 ProGuard 代码混淆的语法可参考 👉 【Android】App攻防之代码混淆

应用加固

应用加固的方式有很多,比如说 360 加固助手、百度应用加固、网易易盾等,360 加固助手每个月都是有免费加固次数使用,本文使用的是 360 加固助手。

点击下载 360 加固助手

使用 360 加固助手加固后的 app 再使用反编译软件打开,app 的源码、资源等信息已经提取不到了。

在这里插入图片描述

防止动态调试

防止动态调试通常指的是防止应用程序在调试模式下运行,或者检测到调试器时采取某些措施禁止调试。

防止动态调试可以在关键逻辑处加入检测代码,如果检测到调试器,则采取相应的措施,如退出应用或显示警告信息。

fun checkForDebugger() {if (isAppInDebugMode()) {// 采取相应措施Toast.makeText(this,"该应用禁止动态调试",Toast.LENGTH_LONG).show()// 可以选择退出应用System.exit(0)}
}fun isAppInDebugMode(): Boolean {return BuildConfig.DEBUG || Debug.isDebuggerConnected()
}

PS:防止动态调试建议和代码混淆、应用加固一起使用,否则形同虚设。

Root 检测

Root 检测是指在 Android 设备上检查是否已经获取了 root 权限的过程。Root 权限允许用户对设备进行底层操作,包括修改系统文件、访问受保护的数据等。

Root 检测通常用于确定设备的安全状态,防止被不怀好意的用户破解,apk 若是被安装到一台拥有 Root 权限的 Android 设备上,那么将可能被不怀好意的用户使用以下技术对 apk 进行破解等操作。

  • Hook:指在程序运行时动态修改或插入代码,以改变程序原有的行为。

那么,如何进行 Root 检测呢?

  • 检查已知的Root管理应用
    许多Root管理应用(如SuperSU、Magisk等)会在设备上留下特定的文件或目录。可以通过检查这些文件或目录的存在来判断设备是否已被Root。
    public boolean checkForRootManagementApps() {String[] paths = {"/system/app/Superuser.apk","/system/bin/su","/system/xbin/su","/sbin/su","/system/sd/xbin/su","/system/bin/failsafe/su","/data/local/xbin/su","/data/local/bin/su","/system/sbin/su","/usr/bin/su","/usr/sbin/su","/system/extras/su","/system/usr/we-need-root/su"};for (String path : paths) {if (new File(path).exists()) {return true;}}return false;
    }
    
  • 检查系统属性
    某些Root工具会修改系统属性,可以通过检查这些属性来判断设备是否已被Root。
    public boolean checkForDangerousProps() {String[] props = {"ro.debuggable","ro.secure","ro.build.tags"};for (String prop : props) {try {String value = SystemProperties.get(prop);if ("1".equals(value) || "test-keys".equals(value)) {return true;}} catch (Exception e) {// Ignore}}return false;
    }
    
  • 检查是否存在su命令
    可以通过尝试执行su命令来判断设备是否已被Root。
    public boolean checkForSuBinary() {Process process = null;try {process = Runtime.getRuntime().exec(new String[]{"/system/xbin/which", "su"});BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));if (in.readLine() != null) {return true;}return false;} catch (Throwable t) {return false;} finally {if (process != null) {process.destroy();}}
    }
    

二次打包检测

二次打包检测是指检测应用是否被非法重新打包或篡改。通常有以下几种方式验证应用是否被进行了二次打包。

  • 签名验证
    通过验证应用的签名是否与预期的签名一致,可以判断应用是否被重新打包。

    public boolean isSignatureValid(Context context) {try {PackageManager pm = context.getPackageManager();PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);for (Signature signature : packageInfo.signatures) {MessageDigest md = MessageDigest.getInstance("SHA");md.update(signature.toByteArray());String currentSignature = Base64.encodeToString(md.digest(), Base64.DEFAULT);// 预期的签名值String expectedSignature = "YOUR_EXPECTED_SIGNATURE_HERE";if (currentSignature.equals(expectedSignature)) {return true;}}} catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {e.printStackTrace();}return false;
    }
    
  • 应用完整性校验
    通过校验应用的文件完整性,可以判断应用是否被篡改。可以使用MD5或SHA-256等哈希算法来生成文件的校验值,并与预存的校验值进行比较。

    public boolean isAppIntegrityValid(Context context) {try {ApplicationInfo appInfo = context.getApplicationInfo();String apkPath = appInfo.sourceDir;// 计算当前APK的MD5值String currentMd5 = getMD5(apkPath);// 预期的MD5值String expectedMd5 = "YOUR_EXPECTED_MD5_HERE";return currentMd5.equals(expectedMd5);} catch (Exception e) {e.printStackTrace();}return false;
    }private String getMD5(String filePath) throws Exception {FileInputStream fis = new FileInputStream(filePath);MessageDigest md = MessageDigest.getInstance("MD5");byte[] dataBytes = new byte[1024];int nread = 0;while ((nread = fis.read(dataBytes)) != -1) {md.update(dataBytes, 0, nread);}fis.close();byte[] mdbytes = md.digest();// convert the byte to hex formatStringBuilder sb = new StringBuilder();for (byte b : mdbytes) {sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));}return sb.toString();
    }
    

报错集合

  1. 执行 apktool b reverse_development-release 指令进行二次打包时,提示 C:\Users\binjx\Desktop\反编译资源\reverse_development-release\res: error: failed to open directory: 系统找不到指定的 文件

    解决方案:将文件所在路径包含的中文名称改为英文名称。

编写文章期间产生的一些疑问

  • 使用Java、kotlin、compose进行编码的类文件反编译有什么区别吗?
    • Java 反编译后的代码与原始代码相似度较高,易于理解和阅读。
    • Kotlin 反编译后的代码可能因编译器优化而与原始代码有较大差异,但仍然可读。
    • Compose 反编译后的代码因声明式UI和编译器优化而复杂,较难理解。
  • apk 经过反编译后得到的代码是否可以在 Android Studio 上运行?
    可以的。但是过程较为复杂,非专业人员需要耗费的时间过多,期间可能会遇到一些问题,例如:缺少部分资源文件、代码注释丢失、混淆后的代码难以理解等问题。

本文暂时欠缺部分逆向开发知识点如下:

  • 动态调试
  • Xposed

参考文章

1、当 App 有了系统权限,真的可以为所欲为?
2、Androidmanifest文件加固和对抗
3、Android 逆向入门保姆级教程
4、Android 逆向之 Xposed 开发
5、Android逆向之旅—反编译利器Apktool使用教程(Apktool的安装使用)
6、动态调试 & Log插桩

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/59695.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为Mate 70临近上市:代理IP与抢购攻略

随着科技的飞速发展&#xff0c;智能手机已经成为我们日常生活中不可或缺的一部分。而在众多智能手机品牌中&#xff0c;华为一直以其卓越的技术和创新力引领着行业的发展。近日&#xff0c;华为Mate 70系列手机的发布会正式定档在11月26日&#xff0c;这一消息引发了众多科技爱…

【Linux之权限】理论篇

前言 Linux的权限是我们学习Linux初期非常重要的基础知识&#xff0c;接下来我将通过一个系列【Linux之权限】&#xff0c;共三篇文章&#xff0c;对此进行较为全面和详细的解说。 sudo 情况&#xff1a;如果我们不是超级管理员&#xff0c;但是想执行一个权限级别比较高的指…

[C++] 智能指针

文章目录 智能指针的使用原因及场景分析为什么需要智能指针&#xff1f;异常抛出导致的资源泄漏问题分析 智能指针与RAIIC常用智能指针 使用智能指针优化代码优化后的代码优化点分析 析构函数中的异常问题解决方法 RAII 和智能指针的设计思路详解什么是 RAII&#xff1f;RAII 的…

spark性能优化调优指导性文件

1.让我们看一下前面的核心参数设置&#xff1a; num-executors10||20&#xff0c;executor-cores1||2&#xff0c;executor-memory10||20&#xff0c;driver-memory20&#xff0c;spark.default.parallelism64 假设我们的火花队列资源如下&#xff1a; 内存1T&#xff0c;内…

视频流媒体播放器EasyPlayer.js RTSP播放器视频颜色变灰色/渲染发绿的原因分析

EasyPlayer.js RTSP播放器属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0…

零售项目管理的核心问题:如何让协作更高效?

在零售行业&#xff0c;团队协作的效率直接影响到市场反应速度和客户满意度。商品的上下架、库存管理、促销活动的策划与执行、跨部门的沟通与协作……每一个环节都需要精准的协调。而在这些纷繁复杂的任务中&#xff0c;项目管理软件正成为零售行业的关键工具&#xff0c;帮助…

用appinventor制作艾宾浩斯遗忘曲线app

&#xff08;呕心沥血 仅供参考&#xff09; 测试效果演示 用appinventor制作课本记背应用程序&#xff08;基于遗忘曲线设计&#xff09; 目录 效果演示 项目重难点 总体设计 系统功能模块设计 总体结构如下图所示&#xff1a; 功能模块 详细设计与实现 登录界面 界…

【设计模式】行为型模式(三):责任链模式、状态模式

行为型模式&#xff08;三&#xff09;&#xff1a;责任链模式、状态模式 5.责任链模式&#xff08;Chain of Responsibility&#xff09;5.1 通俗易懂的解释5.2 具体步骤5.3 代码示例5.3.1 处理者接口5.3.2 具体处理者5.3.3 测试类5.3.4 输出 5.4 总结 6.状态模式&#xff08;…

CC3学习记录

&#x1f338; CC3 之前学习到的cc1和cc6都是通过Runtime进行命令执行的&#xff0c;如果Runtime被加入黑名单的话&#xff0c;整个链子也就失效了。而cc3则是通过动态类加载机制进行任意代码执行的。 &#x1f338; 版本限制 JDK版本&#xff1a;8u65 Commons-Collections…

flutter字体大小切换案例 小字体,标准字体,大字体,超大字体案例

flutter字体大小切换案例 小字体&#xff0c;标准字体&#xff0c;大字体&#xff0c;超大字体案例 Android iOS设备带有选择记录 我的flutter项目版本 environment: sdk: ‘>3.4.4 <4.0.0’ 图片案例 pubspec.yaml 添加依赖 # 屏幕尺寸适配 https://github.com/OpenF…

设计模式(四)装饰器模式与命令模式

一、装饰器模式 1、意图 动态增加功能&#xff0c;相比于继承更加灵活 2、类图 Component(VisualComponent)&#xff1a;定义一个对象接口&#xff0c;可以给这些对象动态地添加职责。ConcreteComponent(TextView)&#xff1a;定义一个对象&#xff0c;可以给这个对象添加一…

django入门【05】模型介绍(二)——字段选项

文章目录 1、null 和 blank示例说明⭐ null 和 blank 结合使用的几种情况总结&#xff1a; 2、choices**choices 在 Django 中有以下几种形式&#xff1a;**&#xff08;1&#xff09; **简单的列表或元组形式**&#xff08;2&#xff09; **字典映射形式**&#xff08;3&#…

C++清除所有输出【DEV-C++】所有编辑器通用 | 算法基础NO.1

各位小伙伴们&#xff0c;上一期的保留小数位数教学够用一辈子&#xff0c;有不错的点赞量&#xff0c;可我连一个粉丝铁粉都没有&#xff0c;你愿意做我的第一个铁粉吗&#xff1f;OK废话不多说&#xff0c;开始&#xff01; 温故与知心 可能你也学过&#xff0c;且是工作者…

【提高篇】3.3 GPIO(三,工作模式详解 上)

目录 一,工作模式介绍 二,输入浮空 2.1 输入浮空简介 2.2 输入浮空特点 2.3 按键检测示例 2.4 高阻态 三,输入上拉 3.1 输入上拉简介 3.2 输入上拉的特点 3.3 按键检测示例 四,输入下拉 4.1 输入下拉简介 4.2 输入下拉特点 4.3 按键检测示例 一,工作模式介绍…

Thinkphp6视图介绍

一.MVC MVC 软件系统分为三个基本部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controller&#xff09; ThinkPHP6 是一个典型的 MVC 架构 控制器—控制器&#xff0c;用于将用户请求转发给相应的Model进行处理&a…

C++(Qt)软件调试---内存泄漏分析工具MTuner (25)

C(Qt)软件调试—内存泄漏分析工具MTuner &#xff08;25&#xff09; 文章目录 C(Qt)软件调试---内存泄漏分析工具MTuner &#xff08;25&#xff09;[toc]1、概述&#x1f41c;2、下载MTuner&#x1fab2;3、使用MTuner分析qt程序内存泄漏&#x1f9a7;4、相关地址&#x1f41…

QT_CONFIG宏使用

时常在Qt代码中看到QT_CONFIG宏&#xff0c;之前以为和#define、DEFINES 差不多&#xff0c;看了定义才发现不是那么回事&#xff0c;定义如下&#xff1a; 看注释就知道了QT_CONFIG宏&#xff0c;其实是&#xff1a;实现了一个在编译时期安全检查&#xff0c;检查指定的Qt特性…

Redis下载历史版本

Linux版本&#xff1a; https://download.redis.io/releases/ Windows版本&#xff1a; https://github.com/tporadowski/redis/releases Linux Redis对应gcc版本

8 软件项目管理

软件项目管理 1、软件项目管理概念1.1 软件项目管理内容1.2 软件项目管理的4P要素人员产品过程项目 2、软件项目度量2.1 软件项目度量定义及度量方法2.2 面对规模的度量2.3 面对功能的度量UFC相关的五类组件14个复杂性调节因素 F i F_i Fi​一个功能点开发代码行数 2.4 软件估算…

游戏引擎学习第12天

视频参考:https://www.bilibili.com/video/BV1yom9YnEWY 这节没讲什么东西&#xff0c;主要是改了一下音频的代码 后面有介绍一些alloc 和malloc,VirtualAlloc 的东西 _alloca 函数&#xff08;或 alloca&#xff09;分配的是栈内存&#xff0c;它的特点是&#xff1a; 生命周…