asm:常用语法

news2024/10/6 6:05:44

常用语法

  • 1、循环
    • 1.1、使用条件跳转指令实现循环
    • 1.2、使用LOOP指令实现循环
  • 2、字符串
    • 2.1、指定字符串的长度
    • 2.2、字符串指令
    • 2.3、重复前缀
  • 3、数组
  • 4、递归
  • 5、宏
  • 6、文件操作
  • 7、内存管理

1、循环

1.1、使用条件跳转指令实现循环

汇编语言中实现循环通常需要使用跳转指令和条件判断指令。下面是一个示例代码,用于实现一个简单的循环结构:

MOV CX, 10     ; 将计数器初始值10存储到CX寄存器中
L1:            ; 标记循环开始处
<LOOP-BODY>    ; 循环体中的指令
DEC CX         ; 将计数器值减1
JNZ L1         ; 如果计数器不为0,则跳转到循环开始处继续执行

这段代码的具体含义如下:

  1. 首先使用MOV指令将计数器的初值10(或者其它合适的值)存储到CX寄存器中。

    MOV CX, 10
    
  2. 然后定义一个名为L1的标号,表示循环的开始位置。

    L1:
    
  3. 接着执行循环体(LOOP-BODY)中的指令,这些指令可以根据具体需求进行编写。

    <LOOP-BODY>
    
  4. 使用DEC指令将计数器CX寄存器中的值减1。

    DEC CX
    
  5. 然后使用JNZ指令(Jump if Not Zero)判断计数器的值是否为0,如果不为0则跳转到L1标号处继续执行循环体内的指令;否则结束循环。

    JNZ L1
    

在这个示例代码中,循环的次数由CX寄存器中的值控制,每次循环体执行结束后,计数器的值会减1。当计数器的值为0时,程序会跳转到循环结束处,结束循环。

1.2、使用LOOP指令实现循环

LOOP是x86汇编中的一个指令,用于循环执行某段代码块。它的语法格式如下:

LOOP destination

其中destination表示循环体的结束位置,可以是任何有效的内存地址或标号(label)。执行完这条指令后,CPU会将CX寄存器中的值减1,并判断CX寄存器中的值是否为0。如果不为0,则跳转到destination指向的内存地址处执行指令,否则继续往下执行。

LOOP指令的执行流程如下:

  1. 首先将CX寄存器中的值减1。

  2. 判断CX寄存器中的值是否为0,如果不为0,则跳转到destination指向的位置处执行指令。

  3. 如果CX寄存器中的值为0,则不跳转,直接继续往下执行。

需要注意的是,LOOP指令只能使用CX寄存器作为计数器,因此在使用LOOP指令前需要将CX寄存器初始化为循环次数。另外,由于LOOP指令只能处理8位或16位有符号整数的计数器,因此在循环次数较大时可能会出现计算错误的情况。

下面是一个简单的示例程序,演示了如何使用LOOP指令:

MOV CX, 10     ; 将计数器初值10存储到CX寄存器中
L1:            ; 标记循环开始处
<LOOP-BODY>    ; 循环体中的指令
LOOP L1        ; 继续执行循环,直到CX寄存器中的值为0

在这个示例中,首先使用MOV指令将计数器的初值10存储到CX寄存器中。然后定义一个名为L1的标号,表示循环的开始位置。接着执行循环体(LOOP-BODY)中的指令,这些指令可以根据具体需求进行编写。最后使用LOOP指令对CX先进行减1的操作,然后指示CPU在CX寄存器中的值不为0时跳转回L1标号处执行循环体,直到CX寄存器中的值为0时结束循环。

2、字符串

2.1、指定字符串的长度

我们可以使用表示位置计数器当前值的 $location 计数器符号来显式存储字符串长度。

在以下实例中:

msg  db  'Hello, world!',0xa ;字符
len  equ  $ - msg            ;字符长度

2.2、字符串指令

每个字符串指令可能需要一个源操作数,一个目标操作数或两者。对于 32 位段,字符串指令使用 ESI 和 EDI 寄存器分别指向源和目标操作数。但是,对于 16 位段,SI 和 DI 寄存器分别用于指向源和目标。

有 5 种用于处理字符串的基本指令

MOVS - 该指令将从ESI寄存器指向的源地址复制到EDI寄存器指向的目标地址

LODS - 该指令将数据从ESI寄存器指向的源地址读取到AL/AX/EAX寄存器中,并递增或递减ESI寄存器的值。

STOS - 该指令将AL/AX/EAX寄存器的值写入EDI寄存器指向的目标地址,并递增或递减EDI寄存器的值。

CMPS - 该指令将ESI寄存器指向的源地址处的一个字节与EDI寄存器指向的目标地址处的一个字节进行比较,并递增或递减ESI和EDI寄存器的值

SCAS -该指令将AL/AX/EAX寄存器的值与与EDI寄存器指向的目标地址处的一个字节进行比较,并递增或递减EDI寄存器的值。

下面我们分别看五个简短的示例:

; movs
section .data
src_buf db 1,2,3,4,5
dst_buf times 5 db 0    ; 初始化为5个0

section .text
global _start

_start:
mov esi, src_buf ; 设置源地址
mov edi, dst_buf ; 设置目标地址
mov ecx, 5       ; 设置要传送的字节数

rep movsb        ; 在两个缓冲区之间传送所有字节
***
section .data
buf db 1,2,3,4,5

section .text
global _start

_start:
mov esi, buf ; 设置源地址
mov ecx, 5   ; 设置要读取的字节数

xor eax, eax ; 清零累加器eax

loop_start:
lodsb        ; 读取一个字节到al寄存器中,并递增esi寄存器的值
add eax, al  ; 将al寄存器中的值加到eax寄存器中
loop loop_start

mov ebx, eax ; 将结果保存到ebx寄存器中

mov eax, 1   ; 设置系统调用号为exit
xor ecx, ecx ; 设置退出码为0
int 0x80     ; 调用系统调用
section .data
buf db 1,2,3,4,5

section .text
global _start

_start:
mov edi, buf ; 设置目标地址
mov ecx, 5   ; 设置要写入的字节数
xor al, al   ; 设置要写入的字节值为0

rep stosb    ; 将5个字节设置为0

mov eax, 1   ; 设置系统调用号为exit
xor ebx, ebx ; 设置退出码为0
int 0x80     ; 调用系统调用
section .data
buf1 db 1,2,3,4,5
buf2 db 1,2,3,4,6

section .text
global _start

_start:
mov esi, buf1 ; 设置源地址1
mov edi, buf2 ; 设置源地址2
mov ecx, 5   ; 设置要比较的字节数

repe cmpsb   ; 比较两个缓冲区中的5个字节是否相等

jz success   ; 如果两个缓冲区中的字节都相等,则跳转到success标签
***
section .data
buf db 1,2,3,4,5

section .text
global _start

_start:
mov edi, buf ; 设置目标地址
mov ecx, 5   ; 设置要查找的字节数
mov al, 4    ; 设置要查找的字节值为4

repe scasb   ; 在缓冲区中查找字节值为4的字节

jnz not_found   ; 如果没找到,则跳转到not_found标签
***

2.3、重复前缀

  • REP是一个通用前缀,它可以与大多数指令一起使用。它的作用是根据指令类型重复执行指令,直到ECX计数器变为0。例如,REP MOVSB指令可以用于在两个存储器之间传输字符串,每执行一次MOVSB指令,ESI和EDI寄存器的值都会增加或减少,ECX计数器的值也会递减,直到ECX计数器为0。

  • REPE前缀表示“repeat while equal”,它通常与字符串比较指令(如CMPSB)一起使用,用于比较两个字符串,如果相等则继续执行,直到ECX计数器为0,或者找到不同处,即ZF标志位被清除。例如,REPE CMPSB指令可以用于比较两个字符串,每执行一次CMPSB指令,ESI和EDI寄存器的值都会增加或减少,ECX计数器的值也会递减,直到ECX计数器为0,或者字符串中出现不匹配的字符,即ZF标志位被清除。

  • REPNE前缀表示“repeat while not equal”,它与REPE类似,但是用于比较两个字符串时,如果不相等则继续执行,直到ECX计数器为0,或者找到相同处,即ZF标志位被设置。例如,REPNE CMPSB指令可以用于比较两个字符串,每执行一次CMPSB指令,ESI和EDI寄存器的值都会增加或减少,ECX计数器的值也会递减,直到ECX计数器为0,或者字符串中出现匹配的字符,即ZF标志位被设置。

  • REPF前缀表示“repeat while equal, forward”,它通常与字符串搜索指令(如SCASB)一起使用,用于搜索字符串中是否存在一个给定字符,如果找到了,则继续执行,直到ECX计数器为0,或者搜索完整个字符串,即ZF标志位被清除。例如,REPF SCASB指令可以用于搜索字符串中是否存在某个字符,每执行一次SCASB指令,EDI寄存器的值都会增加或减少,ECX计数器的值也会递减,直到ECX计数器为0,或者找到了给定字符,即ZF标志位被清除。

  • REPNF前缀表示“repeat while not equal, forward”,它与REPF类似,但是用于搜索字符串中是否存在一个给定字符时,如果没找到,则继续执行,直到ECX计数器为0,或者搜索完整个字符串,即ZF标志位被设置。例如,REPNF SCASB指令可以用于搜索字符串中是否存在某个字符,每执行一次SCASB指令,EDI寄存器的值都会增加或减少,ECX计数器的值也会递减,直到ECX计数器为0,或者没找到给定字符,即ZF标志位被设置。

3、数组

数组是啥不用我说了吧。

section .data
array db 1, 2, 3, 4, 5 ; 定义一个包含5个字节的数组
section .data
    array:                  ; 数组名
        times 5 db 0   ; 声明5个字节的数组,初始化为0;

4、递归

递归也不用介绍了,直接看个例子:

;程序展示了如何在汇编语言中实现阶乘 n。为了保持程序简单,我们将计算阶乘 3。

section .text
   global _start         ;必须声明才能使用 gcc
_start:                  ;告诉链接器入口点
   mov bx, 3             ;用于计算阶乘 3
   call  proc_fact
   add   ax, 30h
   mov  [fact], ax
   mov    edx,len        ;消息长度
   mov    ecx,msg        ;消息
   mov    ebx,1          ;文件描述 (stdout)
   mov    eax,4          ;系统调用号 (sys_write)
   int    0x80           ;调用内核
   mov   edx,1           ;消息长度
   mov    ecx,fact       ;消息
   mov    ebx,1          ;文件描述 (stdout)
   mov    eax,4          ;系统调用号 (sys_write)
   int    0x80           ;调用内核
   mov    eax,1          ;系统调用号 (sys_exit)
   int    0x80           ;调用内核
proc_fact:
   cmp   bl, 1
   jg    do_calculation
   mov   ax, 1
   ret
do_calculation:
   dec   bl
   call  proc_fact
   inc   bl
   mul   bl        ;ax = al * bl
   ret
section .data
msg db 'Factorial 3 is:',0xa
len equ $ - msg
section .bss
fact resb 1

5、宏

编写宏是确保用汇编语言进行模块化编程的另一种方法。宏是一系列指令,由名称指定,可以在程序中的任何位置使用。

在 NASM 中,宏是用 %macro 和 %endmacro 指令定义的。宏以 %macro 指令开始,以 %endmacro 指令结束。

宏的语法如下:

%macro macro_name  number_of_params
<macro body>
%endmacro

其中,number_of_params 指定数字参数,macro_name 指定宏的名称。

通过使用宏名称和必要的参数来调用宏。当您需要在程序中多次使用某个指令序列时,可以将这些指令放在宏中并使用它,而不是一直编写指令。

例如,程序的一个常见需求是在屏幕上写入字符串。要显示字符串,您需要以下指令序列:

movedx,len  ;message length
movecx,msg  ;message to write
movebx,1    ;file descriptor (stdout)
moveax,4    ;system call number (sys_write)
int0x80     ;call kernel

在上面显示字符串的实例中,INT 80H 函数调用使用了寄存器 EAX、EBX、ECX 和 EDX。因此,每次需要在屏幕上显示时,都需要将这些寄存器保存在堆栈上,调用 INT 80H,然后从堆栈中恢复寄存器的原始值。因此,编写两个宏来保存和恢复数据可能很有用。

我们注意到,一些指令,如 IMUL、IDIV、INT 等,需要将一些信息存储在某些特定寄存器中,甚至在某些特定的寄存器中返回值。如果程序已经使用这些寄存器来保存重要数据,那么这些寄存器中的现有数据应该保存在堆栈中,并在指令执行后恢复。

6、文件操作

文件处理系统调用
下表简要介绍了与文件处理相关的系统调用:

%eax名称%ebx%ecx%edx备注
2sys_forkstruct pt_regs--
3sys_read文件描述符(unsigned int)缓冲区的地址(char)要读取的字节数(size_t)
4sys_write文件描述符(unsigned int)(缓冲区的地址)const char要写入的字节数(size_t)
5sys_open文件名(const char)文件访问模式(int)文件权限(int)
6sys_close文件描述符(unsigned int)--
8sys_creat文件名(const char)文件权限(int)-
19sys_lseek文件描述符(unsigned int)偏移值(off_t)偏移量的参考位置(unsigned int)参考位置可以是:文件开头 :0;当前位置:1;文件结尾:2。

以上所有的系统调用如果出现错误,系统调用将返回 EAX 寄存器中的错误代码。

我们来看两个示例:

;sys_fork
***
_start:
    ; 调用sys_fork系统调用创建子进程
    mov eax, 0x02 ; sys_fork的系统调用号
    int 0x80 ; 触发系统调用

    ; 检查返回值
    test eax, eax ; 如果eax为0,此条命令的执行结果为0,存储在标志寄存器中
    jz child_process ; jz(jump if zero),当标志寄存器中的值为0时,则跳转到child_process继续执行。
    ***
child_process:    
***
section .data
filename db 'example.txt', 0
mode rdwr ; 以读写方式打开文件

section .bss
fd resd 1 ; 用于存储文件描述符

section .text
global _start

_start:
    ; 调用sys_open函数打开文件
    mov eax, 0x05 ; sys_open系统调用号
    mov ebx, filename ; 文件名的地址
    mov ecx, mode ; 打开文件的模式
    int 0x80 ; 触发系统调用
    mov [fd], eax ; 将返回值(文件描述符)存储在fd变量中

    ; 如果文件描述符小于0,则表示打开文件失败
    cmp eax, 0 ; 比较eax与0的大小
    jl open_failed ; 如果小于0,则跳转到open_failed

    ; 在此处对文件进行操作
    ; ...

    ; 关闭文件
    mov eax, 0x06 ; sys_close系统调用号
    mov ebx, [fd] ; 文件描述符
    int 0x80 ; 触发系统调用

    ; 程序退出
    mov eax, 0x01 ; sys_exit系统调用号
    xor ebx, ebx ; 返回值为0
    int 0x80 ; 触发系统调用

open_failed:
    ; 文件打开失败的处理
    ; ...

7、内存管理

说内存管理之前我们先来看一个系统调用,sys_brk。
sys_brk()是一个操作系统中的内核函数,用于改变进程的堆空间大小。堆是进程运行时动态分配内存空间的区域,通过sys_brk()函数可以在堆上分配或释放指定大小的内存空间。

在Linux系统中,使用malloc()、calloc()等函数来进行内存动态分配时,都会使用sys_brk()函数来实现。sys_brk系统调用详见博主的另一篇博文。

section .text
   global _start         ;必须声明才能使用 gcc
_start:                  ;告诉链接器入口点
   mov  eax, 45          ;sys_brk
   xor  ebx, ebx
   int  80h              ; 这一步执行的是sys_brk(0),即获取到当前的堆的尾地址,保存在eax中。
   add  eax, 16384       ; eax += 16384,即堆增长16KB。
   mov  ebx, eax
   mov  eax, 45          ;sys_brk
   int  80h
   cmp  eax, 0
   jl   exit             ;exit, if error
   mov  edi, eax         ;EDI = highest available address
   sub  edi, 4           ;pointing to the last DWORD
   mov  ecx, 4096        ;number of DWORDs allocated
   xor  eax, eax         ;clear eax
   std                   ;backward
   rep  stosd            ;repete for entire allocated area
   cld                   ;put DF flag to normal state
   mov  eax, 4
   mov  ebx, 1
   mov  ecx, msg
   mov  edx, len
   int  80h              ;打印消息
exit:
   mov  eax, 1
   xor  ebx, ebx
   int  80h
section .data
msg     db      "Allocated 16 kb of memory!", 10
len     equ     $ - msg

执行结果如下:
在这里插入图片描述

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

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

相关文章

k8s概念-DaemonSet

回到目录 参考链接https://v1-23.docs.kubernetes.io/zh/docs/concepts/workloads/controllers/daemonset/ DaemonSet 确保全部&#xff08;或者某些&#xff09;节点上运行一个 Pod 的副本 当节点加入到K8S集群中&#xff0c;pod会被&#xff08;DaemonSet&#xff09;调度到…

昆腾存储XSAN架构迁移误格式化系统的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 昆腾存储&#xff0c; 操作系统&#xff1a;MAC OS&#xff0c; 存放的数据类型&#xff1a;MXF、MOV等格式视频文件。 存储中有9个数据卷&#xff0c;其中包括1个META信息卷&#xff0c;8个DATA信息卷。 服务器故障&#xff1a; 由于业务需求…

《入门级-Cocos2d 4.0塔防游戏开发》---实战

第一课&#xff1a;coco2dx4.0开发环境搭建 在uos专业版本搭建cocos2dx4.0开发环境 ---》 点击学习 第二课&#xff1a;游戏加载界面开发 介绍coco2dx4.0的场景添加. ---》 点击学习 第三课&#xff1a;欢迎界面开发&#xff08;一&#xff09; 开发欢迎界面的精灵添加和位…

git 生成change-id的解决方法

解决问题 1&#xff0c;在提交代码时的信息会要求添加change-id的要求&#xff0c;但对于默认的git来说&#xff0c;是不会自动生成change-id的 2&#xff0c;当git push的时候&#xff0c;报错 remote: ERROR: commit 7c30eda: missing Change-Id in message footer 解决办…

Boost开发指南-3.7intrusive_ptr

intrusive_ptr intrusive_ptr也是一种引用计数型智能指针&#xff0c;但与之前介绍的 scoped_ptr&#xff0c;shared_ptr 不同&#xff0c;需要额外增加一些的代码才能使用。它的名字可能会给人造成误解&#xff0c;实际上它并不一定要修改代理对象的内部数据。 如果现存代码…

一个月,英语能提高多少?附资料分享

学习语言是一个漫长的过程&#xff0c;但对于我这样一个英语小白&#xff0c;利用业余时间&#xff0c;一个月的学习&#xff0c;还是能看到不少的进步。除了听、说、读、写等能力方面有提升之外&#xff0c;最大的收获就是培养了英语学习和运用的兴趣。这样的结果对我来说&…

《Python深度学习-Keras》精华笔记1:深度学习数学基础及张量

公众号&#xff1a;尤而小屋作者&#xff1a;Peter编辑&#xff1a;Peter 持续更新《Python深度学习》一书的精华内容&#xff0c;仅作为学习笔记分享。 本文是第一篇&#xff1a;深度学习中的数学基础和张量操作 In [1]: import pandas as pd import numpy as npimport tens…

【linux--->传输层协议】

文章目录 [TOC](文章目录) 一、端口号1.端口号划分范围2.常用知名端口号 二、网络命令1.netstat 命令2.pidof 命令 三、UDP协议1.格式2.协议的分离和合并3.特点4.缓冲区 四、TCP协议1.格式2.4位的数据偏移3.确认应答机制4.序号与确认序号5.16位窗口6.标志位7.超时重传8.三次握手…

腾讯云TencentOS Server镜像系统常见问题解答

腾讯云TencentOS Server镜像是腾讯云推出的Linux操作系统&#xff0c;完全兼容CentOS生态和操作方式&#xff0c;TencentOS Server操作系统为云上运行的应用程序提供稳定、安全和高性能的执行环境&#xff0c;TencentOS可以运行在腾讯云CVM全规格实例上&#xff0c;包括黑石物理…

2023-07-31力扣每日一题

链接&#xff1a; 143. 重排链表 题意&#xff1a; 将链表L0 → L1 → … → Ln - 1 → Ln变成L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 解&#xff1a; 线性表法还是好写的 这边搞一下翻转法&#xff0c;快慢指针求翻转点&#xff08;翻转后面一半然后双指针合并…

【运维】linkis1.3.2版本保姆级安装

文章目录 一.建议配置1 软硬件环境建议配置1.1 Linux 操作系统版本要求1.2 服务器建议配置1.3 软件要求1.4 客户端 Web 浏览器要求2.3 LDAP设置&#xff08;暂时不需要&#xff09; 二. 下载三. 安装部署linkis server端1. 首次安装准备事项1.1 Linux服务器1.2 添加部署用户 2.…

《入门级-Cocos2dx4.0 塔防游戏开发》---第四课:欢迎界面开发(二)

目录 一、开发环境介绍 二、开发内容 2.1. logo变化动画 2.2. logo帧动画 2.3 start按钮移动动画 2.4 Credite移动动画 三、显示效果 四、知识总结 1. 移动动画 2. 变大变小动画 3. 延时效果 4. 动画组 一、开发环境介绍 操作系统&#xff1a;UOS1060专业版本。 c…

Java常用API:Math、Syetem、Runtime、BigDecimal

Math类 //目标:了解下Nath类提供的常见方法。 // 1、public static int abs(int a):取绝对值&#xff08;拿到的结果一定是正数&#xff09; //public static double abs(double a) system.out.println(Math.abs(-12)); // 12 system.out.println(Math.abs(123));// 123 system…

upload-labs靶场全通关

upload-labs靶场全通关 pass-1pass-2pass-3pass-4pass-5pass-6pass-7pass-8pass-9pass-10pass-11pass-12pass-13pass-14pass-15pass-16pass-17pass-18pass-19pass-20 pass-1 首先我们新建一个php的webshell文件 在这里我写了一个rabb1t.php的shell文件 内容为这个样子 然后我…

【LeetCode经典算法】链表反转

题目 题目&#xff1a;给定单链表头节点&#xff0c;将单链表的链接顺序反转过来 例&#xff1a; 输入&#xff1a;1->2->3->4->5 输出&#xff1a;5->4->3->2->1 要求&#xff1a;按照两种方式实现 解决办法 方式一&#xff1a; 思路 单链表的结…

DH算法、DHE算法、ECDHE算法演进

ECDHE 算法解决了 RSA 算法不具备前向安全的性质 和 DH 算法效率低下的问题。 ECDHE 算法具有前向安全。所以被广泛使用。 由什么演变而来 DH 算法 -- > DHE 算法 -- > ECDHE 算法 DH 算法是非对称加密算法&#xff0c;该算法的核心数学思想是离散对数。 核心数学思…

wms-3代货架标签(电池版本)接口文档

一、查询标签信息接口 接口类型&#xff1a;POST, 参数格式&#xff1a;json array 链接形式&#xff1a; http://localhost/wms/associate/getTagsMsg 代码形式&#xff1a; { url : http://localhost/wms/associate/getTagsMsg, requestMethed : GET, requestParamet…

亚马逊买家账号ip关联怎么处理

对于亚马逊买家账号&#xff0c;同样需要注意IP关联问题。在亚马逊的眼中&#xff0c;如果多个买家账号共享相同的IP地址&#xff0c;可能会被视为潜在的操纵、违规或滥用行为。这种情况可能导致账号受到限制或处罚。 处理亚马逊买家账号IP关联问题&#xff0c;建议采取以下步骤…

有哪些很奇PA,但又比较少人知道的病毒?

世界上有哪些很奇PA&#xff08;或者说很搞笑&#xff09;&#xff0c;但又比较少人知道的病毒&#xff1f; ①温州皮鞋厂病毒 中毒迹象&#xff1a;播放出我们熟悉的《温州皮鞋厂倒闭了》bgm&#xff0c;并出现“捍卫版权&#xff0c;盗&#xff08;到&#xff09;此为止”…

DAY15_VUEElement综合案例

目录 1 VUE1.1 概述1.1.1 Vue js文件下载 1.2 快速入门1.3 Vue 指令1.3.1 v-bind & v-model 指令1.3.2 v-on 指令1.3.3 条件判断指令1.3.4 v-for 指令 1.4 生命周期1.5 案例1.5.1 需求1.5.2 查询所有功能1.5.3 添加功能 2 Element2.0 element-ui js和css和字体图标下载2.1 …