android oat如何提取dex文件字节码,Android: 使用oatdump反编译oat文件

网上经常看到有通过apktool将apk中的dex反编译成smali格式的文件,以便分析功能实现与破—解,确没怎么看到oat文件反通过oatdump反编译的,所以就写了一篇这样的文档。声明一下oat文件也是可以反编译的。本来想着通过oatdump处理之后,既可以看到dex代码又可以看到相应的汇编实现。反编译之后发现其实不然,难道是因为系统是eng版本的原因,有机会再验证看看。

先看一下这两个代码片段

1. JNI代码: jni/com_android_hello_HelloActivity.cpp

#include

#include

extern "C" void Java_com_android_hello_HelloActivity_sayHello(JNIEnv *, jobject)

{

__builtin_trap();

}

使APP出现native crash

2. Java代码: src/com/android/hello/HelloActivity.java

public class HelloActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

sayHello();

}

static {

System.loadLibrary("hello-jni");

}

private static native void sayHello();

}

加载JNI库libhello-jni.so, 并且在app launch时调用JNI方法sayHello()。

APK安装时会执行dex2oat命令,生成oat文件:

03-11 23:06:38.748 11835 11902 D PackageManager: Renaming /data/app/vmdl111617587.tmp to /data/app/com.android.hello-1

03-11 23:06:38.774 11835 11902 I PackageManager: Running dexopt on: /data/app/com.android.hello-1/base.apk pkg=com.android.hello isa=arm vmSafeMode=false

03-11 23:06:38.800 18221 18221 I dex2oat : /system/bin/dex2oat --zip-fd=6 --zip-location=/data/app/com.android.hello-1/base.apk --oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@com.android.hello-1@base.apk@classes.dex --instruction-set=arm --instruction-set-features=div --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=interpret-only --swap-fd=8

03-11 23:06:38.818 18221 18221 I dex2oat : Decided to run without swap.

03-11 23:06:38.838 18221 18221 I dex2oat : dex2oat took 38.211ms (threads: 4) arena alloc=0B java alloc=7KB native alloc=75KB free=32KB

oat文件是什么:

$ adb pull /data/dalvik-cache/arm/data@app@com.android.hello-1@base.apk@classes.dex

1236 KB/s (12720 bytes in 0.010s)

$ file data\@app\@com.android.hello-1\@base.apk\@classes.dex

data@app@com.android.hello-1@base.apk@classes.dex: ELF 32-bit LSB shared object, ARM, version 1 (GNU/Linux), dynamically linked, stripped

是一个ELF可执行文件。执行readelf/arm-linux-androideabi-readelf命令看看:

$ arm-linux-androideabi-readelf -a data\@app\@com.android.hello-1\@base.apk\@classes.dex

ELF Header:

Magic: 7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00

Class: ELF32

Data: 2's complement, little endian

Version: 1 (current)

OS/ABI: UNIX - GNU

ABI Version: 0

Type: DYN (Shared object file)

Machine: ARM

Version: 0x1

Entry point address: 0x0

Start of program headers: 52 (bytes into file)

Start of section headers: 12400 (bytes into file)

Flags: 0x5000000, Version5 EABI

Size of this header: 52 (bytes)

Size of program headers: 32 (bytes)

Number of program headers: 5

Size of section headers: 40 (bytes)

Number of section headers: 8

Section header string table index: 7

Section Headers:

[Nr] Name Type Addr Off Size ES Flg Lk Inf Al

[ 0] NULL 00000000 000000 000000 00 0 0 0

[ 1] .dynsym DYNSYM 000000d4 0000d4 000040 10 A 2 0 4

[ 2] .dynstr STRTAB 00000114 000114 00004f 01 A 0 0 1

[ 3] .hash HASH 00000164 000164 000020 04 A 1 0 4

[ 4] .rodata PROGBITS 00001000 001000 001000 00 A 0 0 4096

[ 5] .text PROGBITS 00002000 002000 000094 00 AX 0 0 4096

[ 6] .dynamic DYNAMIC 00003000 003000 000038 08 A 1 0 4096

[ 7] .shstrtab STRTAB 00000000 003038 000038 01 0 0 1

Key to Flags:

W (write), A (alloc), X (execute), M (merge), S (strings)

I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)

O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

Program Headers:

Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align

PHDR 0x000034 0x00000034 0x00000034 0x000a0 0x000a0 R 0x4

LOAD 0x000000 0x00000000 0x00000000 0x02000 0x02000 R 0x1000

LOAD 0x002000 0x00002000 0x00002000 0x00094 0x00094 R E 0x1000

LOAD 0x003000 0x00003000 0x00003000 0x00038 0x00038 RW 0x1000

DYNAMIC 0x003000 0x00003000 0x00003000 0x00038 0x00038 RW 0x1000

Section to Segment mapping:

Segment Sections...

00

01 .dynsym .dynstr .hash .rodata

02 .text

03 .dynamic

04 .dynamic

Dynamic section at offset 0x3000 contains 7 entries:

Tag Type Name/Value

0x00000004 (HASH) 0x164

0x00000005 (STRTAB) 0x114

0x00000006 (SYMTAB) 0xd4

0x0000000b (SYMENT) 16 (bytes)

0x0000000a (STRSZ) 79 (bytes)

0x0000000e (SONAME) Library soname: [data@app@com.android.hello-1@base.apk@classes.dex]

0x00000000 (NULL) 0x0

There are no relocations in this file.

There are no unwind sections in this file.

Symbol table '.dynsym' contains 4 entries:

Num: Value Size Type Bind Vis Ndx Name

0: 00000000 0 NOTYPE LOCAL DEFAULT UND

1: 00001000 4096 OBJECT GLOBAL DEFAULT 4 oatdata

2: 00002000 148 OBJECT GLOBAL DEFAULT 5 oatexec

3: 00002090 4 OBJECT GLOBAL DEFAULT 5 oatlastword

Histogram for bucket list length (total of 2 buckets):

Length Number % of total Coverage

0 0 ( 0.0%)

1 1 ( 50.0%) 33.3%

2 1 ( 50.0%) 100.0%

No version information found in this file.

能使用objdump/arm-linux-androideabi-objdum反编译吗:

$ arm-linux-androideabi-objdump -d data\@app\@com.android.hello-1\@base.apk\@classes.dex

data@app@com.android.hello-1@base.apk@classes.dex: file format elf32-littlearm

Disassembly of section .text:

00002000 :

...

200c:30 00 00 00 e0 4d 00 00 00 00 00 00 78 00 00 00 0....M......x...

201c:2d e9 e0 4d ad f2 14 0d 00 90 4f f0 01 0c cd f8 -..M......O.....

202c:08 c0 d9 f8 c8 c0 cd f8 04 c0 0d f2 04 0c c9 f8 ................

203c:c8 c0 d0 f8 08 c0 cd f8 0c c0 c9 f8 8c d0 4f f0 ..............O.

204c:00 0c c9 f8 90 c0 48 46 d0 f8 94 c1 e0 47 04 90 ......HF.....G..

205c:0d f2 0c 01 d9 f8 98 00 dd f8 00 c0 dc f8 28 c0 ..............(.

206c:e0 47 04 98 49 46 d1 f8 9c c1 e0 47 d9 f8 7c c0 .G..IF.....G..|.

207c:bc f1 00 0f 03 d1 0d f2 14 0d bd e8 e0 8d 60 46 ..............`F

208c:d9 f8 34 c2 ..4.

00002090 :

2090:e0 47 00 be .G..

没什么内容。

最后,我们用oatdump命令查看一下:

$ oatdump --oat-file=data\@app\@com.android.hello-1\@base.apk\@classes.dex

MAGIC:

oat

045

CHECKSUM:

0x864eeade

INSTRUCTION SET:

Thumb2

INSTRUCTION SET FEATURES:

div

DEX FILE COUNT:

1

EXECUTABLE OFFSET:

0x00001000

INTERPRETER TO INTERPRETER BRIDGE OFFSET:

0x00000000

INTERPRETER TO COMPILED CODE BRIDGE OFFSET:

0x00000000

JNI DLSYM LOOKUP OFFSET:

0x00000000

PORTABLE IMT CONFLICT TRAMPOLINE OFFSET:

0x00000000

PORTABLE RESOLUTION TRAMPOLINE OFFSET:

0x00000000

PORTABLE TO INTERPRETER BRIDGE OFFSET:

0x00000000

QUICK GENERIC JNI TRAMPOLINE OFFSET:

0x00000000

QUICK IMT CONFLICT TRAMPOLINE OFFSET:

0x00000000

QUICK RESOLUTION TRAMPOLINE OFFSET:

0x00000000

QUICK TO INTERPRETER BRIDGE OFFSET:

0x00000000

IMAGE PATCH DELTA:

0 (0x00000000)

IMAGE FILE LOCATION OAT CHECKSUM:

0x8223fe58

IMAGE FILE LOCATION OAT BEGIN:

0x70dd0000

KEY VALUE STORE:

dex2oat-cmdline = --zip-fd=6 --zip-location=/data/app/com.android.hello-1/base.apk --oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@com.android.hello-1@base.apk@classes.dex --instruction-set=arm --instruction-set-features=div --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=interpret-only --swap-fd=8

dex2oat-host = Arm

image-location = /data/dalvik-cache/arm/system@framework@boot.art

pic = false

SIZE:

4244

OatDexFile:

location: /data/app/com.android.hello-1/base.apk

checksum: 0x2e3d006c

0: Lcom/android/hello/HelloActivity; (offset=0x00000578) (type_idx=2) (StatusVerified) (OatClassSomeCompiled)

0: void com.android.hello.HelloActivity.() (dex_method_idx=2)

DEX CODE:

0x0000: const-string v0, "hello-jni" // string@10

0x0002: invoke-static {v0}, void java.lang.System.loadLibrary(java.lang.String) // method@6

0x0005: return-void

OatMethodOffsets (offset=0x00000000)

code_offset: 0x00000000

gc_map: (offset=0x00000000)

OatQuickMethodHeader (offset=0x00000000)

mapping_table: (offset=0x00000000)

vmap_table: (offset=0x00000000)

QuickMethodFrameInfo

frame_size_in_bytes: 0

core_spill_mask: 0x00000000

fp_spill_mask: 0x00000000

CODE: (code_offset=0x00000000 size_offset=0x00000000 size=0)

NO CODE!

1: void com.android.hello.HelloActivity.() (dex_method_idx=3)

DEX CODE:

0x0000: invoke-direct {v0}, void android.app.Activity.() // method@0

0x0003: return-void

OatMethodOffsets (offset=0x00000000)

code_offset: 0x00000000

gc_map: (offset=0x00000000)

OatQuickMethodHeader (offset=0x00000000)

mapping_table: (offset=0x00000000)

vmap_table: (offset=0x00000000)

QuickMethodFrameInfo

frame_size_in_bytes: 0

core_spill_mask: 0x00000000

fp_spill_mask: 0x00000000

CODE: (code_offset=0x00000000 size_offset=0x00000000 size=0)

NO CODE!

2: void com.android.hello.HelloActivity.sayHello() (dex_method_idx=5)

DEX CODE:

OatMethodOffsets (offset=0x00000584)

code_offset: 0x0000101d

gc_map: (offset=0x00000000)

OatQuickMethodHeader (offset=0x00001000)

mapping_table: (offset=0x00000000)

vmap_table: (offset=0x00000000)

QuickMethodFrameInfo

frame_size_in_bytes: 48

core_spill_mask: 0x00004de0 (r5, r6, r7, r8, r10, r11, r14)

fp_spill_mask: 0x00000000

CODE: (code_offset=0x0000101d size_offset=0x00001018 size=120)...

0x0000101c: e92d4de0push {r5, r6, r7, r8, r10, r11, lr}

0x00001020: f2ad0d14subw sp, sp, #20

0x00001024: 9000 str r0, [sp, #0]

0x00001026: f04f0c01mov.w r12, #1

0x0000102a: f8cdc008str.w r12, [sp, #8]

0x0000102e: f8d9c0c8ldr.w r12, [r9, #200] ; top_handle_scope

0x00001032: f8cdc004str.w r12, [sp, #4]

0x00001036: f20d0c04addw r12, sp, #4

0x0000103a: f8c9c0c8str.w r12, [r9, #200]

0x0000103e: f8d0c008ldr.w r12, [r0, #8]

0x00001042: f8cdc00cstr.w r12, [sp, #12]

0x00001046: f8c9d08cstr.w sp, [r9, #140]

0x0000104a: f04f0c00mov.w r12, #0

0x0000104e: f8c9c090str.w r12, [r9, #144]

0x00001052: 4648 mov r0, r9

0x00001054: f8d0c194ldr.w r12, [r0, #404]

0x00001058: 47e0 blx r12

0x0000105a: 9004 str r0, [sp, #16]

0x0000105c: f20d010caddw r1, sp, #12

0x00001060: f8d90098ldr.w r0, [r9, #152] ; jni_env

0x00001064: f8ddc000ldr.w r12, [sp, #0]

0x00001068: f8dcc028ldr.w r12, [r12, #40]

0x0000106c: 47e0 blx r12

0x0000106e: 9804 ldr r0, [sp, #16]

0x00001070: 4649 mov r1, r9

0x00001072: f8d1c19cldr.w r12, [r1, #412]

0x00001076: 47e0 blx r12

0x00001078: f8d9c07cldr.w r12, [r9, #124] ; exception

0x0000107c: f1bc0f00cmp.w r12, #0

0x00001080: d103 bne +6 (0x0000108a)

0x00001082: f20d0d14addw sp, sp, #20

0x00001086: e8bd8de0pop {r5, r6, r7, r8, r10, r11, pc}

0x0000108a: 4660 mov r0, r12

0x0000108c: f8d9c234ldr.w r12, [r9, #564] ; pDeliverException

0x00001090: 47e0 blx r12

0x00001092: be00

3: void com.android.hello.HelloActivity.onCreate(android.os.Bundle) (dex_method_idx=4)

DEX CODE:

0x0000: invoke-super {v0, v1}, void android.app.Activity.onCreate(android.os.Bundle) // method@1

0x0003: invoke-static {}, void com.android.hello.HelloActivity.sayHello() // method@5

0x0006: return-void

OatMethodOffsets (offset=0x00000000)

code_offset: 0x00000000

gc_map: (offset=0x00000000)

OatQuickMethodHeader (offset=0x00000000)

mapping_table: (offset=0x00000000)

vmap_table: (offset=0x00000000)

QuickMethodFrameInfo

frame_size_in_bytes: 0

core_spill_mask: 0x00000000

fp_spill_mask: 0x00000000

CODE: (code_offset=0x00000000 size_offset=0x00000000 size=0)

NO CODE!

看来oat文件中既包含dex代码,又包启汇编代码,不知道是不是因为我手机是eng版本的。相关系统属性如下(Nexus4 android-5.1.1_r17(lmy48t):

[dalvik.vm.dex2oat-Xms]: [64m]

[dalvik.vm.dex2oat-Xmx]: [512m]

[dalvik.vm.dex2oat-filter]: [interpret-only]

[dalvik.vm.dexopt-flags]: [m=y]

[dalvik.vm.image-dex2oat-Xms]: [64m]

[dalvik.vm.image-dex2oat-Xmx]: [64m]

[dalvik.vm.image-dex2oat-filter]: [verify-none]

相关的参考文档,ART and Dalvik: https://source.android.com/devices/tech/dalvik/index.html

1. Bytecode Format: https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

2. .Dex Format: https://source.android.com/devices/tech/dalvik/dex-format.html

3. Instruction Formats: https://source.android.com/devices/tech/dalvik/instruction-formats.html

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

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

相关文章

mybatis依赖_Spring Boot2 系列教程(二十一)整合 MyBatis

前面两篇文章和读者聊了 Spring Boot 中最简单的数据持久化方案 JdbcTemplate,JdbcTemplate 虽然简单,但是用的并不多,因为它没有 MyBatis 方便,在 SpringSpringMVC 中整合 MyBatis 步骤还是有点复杂的,要配置多个 Bea…

android获取图片格式,Android得到图片的真实格式——从本地文件或者网络文件流...

ImageFormatFeatures支持从InputStream或者File解析四种格式:jpg 、 png 、 webp 、 gif从文件本身解析格式,而不是从扩展名获取FormatHelper.getFormat(InputStream inputStream)FormatHelper.getFormat(File file)UsageStep 1Step 2解析格式String For…

python中setup函数的用法_python学习之setUp函数和tearDown函数

1,setUp():就是在一个类中最先被调用的函数,每次执行一个函数都要先执行这个函数,有几个函数就被调用几次,与放的位置无关,随便放到哪里都会先执行这个函数 2,tearDown():就是在一个类中最后被调用的函数,每个函数执行之后都会执行一次,与放的位置无关,随便放到哪里…

pagehelper的使用_SpringBoot项目中,如何更规范的使用PageHelper分页?

SpringBoot项目中&#xff0c;如何更规范的使用PageHelper分页&#xff0c;拉勾IT课小编为大家分解一. 开发准备1. 开发工具• IntelliJ IDEA 2020.2.32. 开发环境• Red Hat Open JDK 8u256• Apache Maven 3.6.33. 开发依赖SpringBoot<dependency><groupId>org.s…

2-路插入排序c语言算法,浅谈2路插入排序算法及其简单实现

2路插入排序算法是在直接插入排序算法的基础上增加了一个辅助数组&#xff0c;其目的是减少排序过程中的移动次数&#xff0c;需要增加n个记录的辅助空间。难点可能在于对取余的考虑吧&#xff0c;可以把辅助数组看成一个环状空间&#xff0c;这样就能更好的理解辅助空间中最大…

mysql安装被打断_Mysql的安装/性能优化/安全加固

aa安装&#xff1a;增加一个登录用户和群组#groupaddmysql#useradd -r -g mysql mysql解压缩Mysql数据包#tar-zxvf mysql-5.6.13.tar.gz进入Mysql解压缩目录#cd mysql-5.6.13配置安装mysql#cmake.#make &&make install建立配置文件#cp./support-files/my-medium.cnf/et…

python自动输入账号密码_Python如何基于selenium实现自动登录博客园

这篇文章主要介绍了Python如何基于selenium实现自动登录博客园,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需要做的准备&#xff1a; 本文章是使用Chrome&#xff0c;所以需要Chormedriver.exe&#xff0c;…

安卓软件错误log_Android编程实现捕获程序异常退出时的错误log信息功能详解

本文实例讲述了Android编程实现捕获程序异常退出时的错误log信息功能。分享给大家供大家参考&#xff0c;具体如下&#xff1a;很多时候我们程序无缘无故的就挂掉了&#xff0c;让我们一头雾水&#xff0c;如果刚好我们在调试&#xff0c;那我们可以通过错误log来查看是什么原因…

android 模拟长按菜单键_如何采用PLC梯形图实现单键启动程序

“PLC是一种专门为在工业环境下应用而设计的数字运算操作的电子装置。它采用可以编制程序的存储器&#xff0c;用来在其内部存储执行逻辑运算、顺序运算、计时、计数和算术运算等操作的指令&#xff0c;并能通过数字式或模拟式的输入和输出&#xff0c;控制各种类型的机械或生产…

android 日期对话框,Android日期选择器对话框DatePickerDialog使用详解

调用Android原生日期选择器对话框就是DatePickerDialog&#xff0c;具体内容如下在Android4.4系统上效果如图&#xff1a;在Android5.0以上效果如图&#xff1a;1、Activity的onCreate方法中获取当时的年&#xff0c;月&#xff0c;日Calendar ca Calendar.getInstance();mYea…

c#endread怎么打印出来_NetworkStream.EndRead(IAsyncResult) 方法 (System.Net.Sockets) | Microsoft Docs...

处理异步读取的结束。Handles the end of an asynchronous read.public:override int EndRead(IAsyncResult ^ asyncResult);public override int EndRead (IAsyncResult asyncResult);override this.EndRead : IAsyncResult -> intPublic Overrides Function EndRead (asyn…

wp自定义帖子没标签_ofollow标签的作用有重大变化

nofollow标签的历史经典的nofollow标签作用和使用方法以前的帖子写过&#xff0c;详情读者可以参考以前帖子。nofollow标签&#xff08;准确说是属性&#xff0c;不过约定俗成&#xff0c;还是叫标签吧&#xff09;是Google和Yahoo等搜索引擎2005年推出的&#xff0c;目的是告诉…

android电视视频播放器,智能电视如何播放本地视频?当贝市场分享几款播放器...

原标题&#xff1a;智能电视如何播放本地视频&#xff1f;当贝市场分享几款播放器对视频清晰度要求更高的用户普遍会自己下载视频&#xff0c;然后通过本地播放的方式观影&#xff0c;那么&#xff0c;下面就给大家介绍几款智能电视的本地视频播放软件&#xff0c;包你好用。当…

c 复杂的前置后置面试题_你被哪些C语言面试题坑过?

最近在《深入理解计算机系统》上看到一道题&#xff0c;分享一下&#xff1a;假设我们在对有符号值使用补码运算的32位机器人运行代码。对于有符号值使用的是算术右移&#xff0c;而对于无符号值使用的是逻辑右移。变量的声明和初始化如下&#xff1a;int x foo(); //任意值in…

python 三引号_Python 简明教程 --- 4,Python 变量与基本数据类型

微信公众号&#xff1a;码农充电站pro 个人主页&#xff1a;https://codeshellme.github.io任何一个人都会写出能够让机器理解的代码&#xff0c;只有好的程序员才能写出人类可以理解的代码。 —— Martin Fowler 1&#xff0c;什么是变量计算机的本质是处理数据&#xff0c;数…

鸿蒙系统手机9月11日,鸿蒙系统9月11日,将有望正式成为国际第三大手机操作生态系统...

原标题&#xff1a;鸿蒙系统9月11日&#xff0c;将有望正式成为国际第三大手机操作生态系统众所周知&#xff0c;当时华为鸿蒙系统还处于1.0版本的时候&#xff0c;这项技术就已经被运用到了荣耀智能屏上&#xff0c;目前该系统也已经过渡到了华为的手表上&#xff0c;经过这一…

lisp 所在图幅号计算_地图标准分幅与编号计算(二)新图幅号

新图幅号1991年制订了新的《国家基本比例尺地形图分幅和编号》(GB/T 13989-92 )的国家标准&#xff0c;并给出了不同标准比例尺地形图的编给规范及图式。新测和更新的地图&#xff0c;照此标准进行分幅和编号。我国基本比例尺的地形图包括1:5000、1:1万、1:2.5万、1:5万、1:10万…

高德地图画带箭头的线_现代汽车把艺术展览搬到线上,邀您逛全景获奖展

Hyundai Blue Prize 2019获奖展“游戏社会&#xff1a;狼、猞猁和蚁群”(Play societies&#xff1a;wolves, lynx and ants)线上展览正式上线。《游戏社会: 狼、猞猁和蚁群》&#xff0c;以 “信息高速公路上的荒原狼”、“猞猁安全岛”和“蚁群游戏厅” 三段隐喻文本展开展览…

python实训名片管理程序_python3学生名片管理v2.0版

python学生名片管理vv2.0是在1.0的基础上增加部分功能&#xff0c;实现将数据存入文件保存&#xff0c;以便于程序停止后还能再次取到数据。具体实现请看如下部分&#xff1a;card_main.pyimport sysfrom One.card_func2 import *def head():print(* * 30)print(学生信息管理系…

ssm 项目cannot resolve package_前端工程化之创建项目

前言在我们团队&#xff0c;刚开始创建项目&#xff0c;是直接使用框架的 cli 进行创建项目&#xff0c;并修改相关配置。随着项目的增多&#xff0c;沉淀了两套模板&#xff0c;平台端及移动端。后来&#xff0c;我们自己写了一个简单的 cli&#xff0c;并提供了 create 及 li…