pwn学习笔记(11)--off_by_one

pwn学习笔记(11)–off_by_one

​ 在处理for循环或者while循环的时候,有的可能会遇到如下情况:

#include<stdio.h>

int main(){
    char buf[0x10];

    for (int i = 0 ; i <= 0x10 ; i ++){
        buf[i] = getchar();
    }

    puts(buf);
    
}

​ 多次输入几个a之后,发现了最后输出的时候输出了17个a,我的目的仅仅只是需要16个a,结果输出了17个a,像这种,在写入字符串的时候多写入了一个字节的情况,就是off by one。

​ 在堆中,这种问题尤为严重,可能会导致输入的字符覆盖了heap info的prev_in_use或者其他的数据:

  1. 溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。也可使用 NULL 字节溢出的方法
  2. 溢出字节为 NULL 字节:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。(1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。(2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与prev_size 是否一致。

​ 最新版本代码中,已加入针对 2 中后一种方法的 check ,但是在 2.28 及之前版本并没有该 check 。

/* consolidate backward */
    if (!prev_inuse(p)) {
      prevsize = prev_size (p);
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      /* 后两行代码在最新版本中加入,则 2 的第二种方法无法使用,但是 2.28 及之前都没有问题 */
      if (__glibc_unlikely (chunksize(p) != prevsize))
        malloc_printerr ("corrupted size vs. prev_size while consolidating");
      unlink_chunk (av, p);
    }

​ 还有种情况:

#include<stdio.h>
#include<string.h>

char bss[0x20] = "aaaaaaaaaaaaaaaa";

int main(){
    char buf[0x10];

    if (strlen(bss) == 0x10){
        strcpy(buf,bss);
    }

    puts(buf);
}

​ 这种情况,乍看上去没啥问题,但是,strlen不会计算结尾的\x00,而strcpy在拷贝的时候又会多拷贝一个\x00进去,造成多写入了一个字节。

​ 上一个题:

Asis CTF 2016 b00ks(只看前面off by one的部分)

​ checksec 一下看看:

g01den@MSI:/mnt/c/Users/20820/Downloads$ checksec pwn
[*] '/mnt/c/Users/20820/Downloads/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

​ 激活了PIE,以及题目附件被strip过,抱歉,我一个菜鸡误入了大佬的世界,啥都看不懂,反编译之后看到那么抽象突然想放弃了,不过还是得做。

​ 题目是一个寻常的图书管理,有创建书,删除书,编辑描述内容,输出书籍信息,修改最近访问的作者名,退出。

​ 先不看别的,main没啥用,先看add:

__int64 sub_F55()
{
  int v1; // [rsp+0h] [rbp-20h] BYREF
  int v2; // [rsp+4h] [rbp-1Ch]
  void *v3; // [rsp+8h] [rbp-18h]
  void *ptr; // [rsp+10h] [rbp-10h]
  void *v5; // [rsp+18h] [rbp-8h]

  v1 = 0;
  printf("\nEnter book name size: ");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 )
    goto LABEL_2;
  printf("Enter book name (Max 32 chars): ");
  ptr = malloc(v1);
  if ( !ptr )
  {
    printf("unable to allocate enough space");
    goto LABEL_17;
  }
  if ( (unsigned int)readName(ptr, v1 - 1) )
  {
    printf("fail to read name");
    goto LABEL_17;
  }
  v1 = 0;
  printf("\nEnter book description size: ");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 )
  {
LABEL_2:
    printf("Malformed size");
  }
  else
  {
    v5 = malloc(v1);
    if ( v5 )
    {
      printf("Enter book description: ");
      if ( (unsigned int)readName(v5, v1 - 1) )
      {
        printf("Unable to read description");
      }
      else
      {
        v2 = sub_B24();
        if ( v2 == -1 )
        {
          printf("Library is full");
        }
        else
        {
          v3 = malloc(0x20uLL);
          if ( v3 )
          {
            *((_DWORD *)v3 + 6) = v1;
            *((_QWORD *)off_202010 + v2) = v3;
            *((_QWORD *)v3 + 2) = v5;
            *((_QWORD *)v3 + 1) = ptr;
            *(_DWORD *)v3 = ++unk_202024;
            return 0LL;
          }
          printf("Unable to allocate book struct");
        }
      }
    }
    else
    {
      printf("Fail to allocate memory");
    }
  }
LABEL_17:
  if ( ptr )
    free(ptr);
  if ( v5 )
    free(v5);
  if ( v3 )
    free(v3);
  return 1LL;
}

​ 分析一波,有一些需要记住作用的阿变量名,比如v1:

  • v1可以很明显看出来,v1是存放的是book的name的大小,ptr就是name存放的地址:

  •   v1 = 0;
      printf("\nEnter book name size: ");
      __isoc99_scanf("%d", &v1);
      if ( v1 < 0 )
        goto LABEL_2;
      printf("Enter book name (Max 32 chars): ");
      ptr = malloc(v1);
    
  • 之后重新用了v1来存放book的description的大小v5就是description存放的地址:

  • printf("\nEnter book description size: ");
      __isoc99_scanf("%d", &v1);
      if ( v1 < 0 )
      {
    LABEL_2:
        printf("Malformed size");
      }
      else
      {
        v5 = malloc(v1);
    
  • 之后开了个v3,存放了book的所有信息:

  • v3 = malloc(0x20uLL);
              if ( v3 )
              {
                *((_DWORD *)v3 + 6) = v1;
                *((_QWORD *)off_202010 + v2) = v3;
                *((_QWORD *)v3 + 2) = v5;
                *((_QWORD *)v3 + 1) = ptr;
                *(_DWORD *)v3 = ++unk_202024;
                return 0LL;
              }
    
  • 这里似乎,v3+6的偏移存放的是书的描述的长度,v2是与book的结构体数组头的偏移,*((_QWORD *)off_202010 + v2) = v3;这里是将新分配的结构体地址放入结构体数组指针里,之后将v5,也就是book的description的地址放给v3偏移为2的地方,ptr,也就是name的地址放入v3偏移为2的地方。

​ 这里面还存在一个函数,就是readName:

__int64 __fastcall sub_9F5(_BYTE *a1, int a2)
{
  int i; // [rsp+14h] [rbp-Ch]

  if ( a2 <= 0 )
    return 0LL;
  for ( i = 0; ; ++i )
  {
    if ( (unsigned int)read(0, a1, 1uLL) != 1 )
      return 1LL;
    if ( *a1 == 10 )
      break;
    ++a1;
    if ( i == a2 )
      break;
  }
  *a1 = 0;
  return 0LL;
}

​ 分配两次,代码类似这样:

add(0x20,"book1_name",200,"book1_destruct")
add(0x21000,"book1_name",0x21000,"book1_destruct")

​ 这个函数存在一些问题,a1 是我们想要写入的字符串的起始地址,a2是判定边缘,但是,从0开始,一直到a2为止,很显然多进行了一次读入,因为这里的逻辑是先读入,再判断i与a2是否相等,所以这里就多循环了一次,造成了offbyone,结束循环之后,又将后一位的内存修改成了\x00,因此发生了溢出,例如一个数组是32字节,这个程序调用这个函数的时候,一直都是用的size-1,所以传入的是31,这个程序就刚好做到了让整个数组刚好可以写满,也就是写道buf[31],这里刚好写满,但是,有个关键的问题,最后一个还操作了一下,让*a1=0,这也就导致了buf[32]=0的发生,溢出了一个字节,也就造成了offbyone,或者说off by null。

​ 上gdb看看,先输入32个a作为名字之后,那段内存变成了这样:

pwndbg> search aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Searching for value: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
pwn             0x555555602040 0x6161616161616161 ('aaaaaaaa')
pwndbg> x/30gx 0x555555602040
0x555555602040: 0x6161616161616161      0x6161616161616161
0x555555602050: 0x6161616161616161      0x6161616161616161
0x555555602060: 0x0000000000000000      0x0000000000000000
0x555555602070: 0x0000000000000000      0x0000000000000000
0x555555602080: 0x0000000000000000      0x0000000000000000
0x555555602090: 0x0000000000000000      0x0000000000000000
0x5555556020a0: 0x0000000000000000      0x0000000000000000
0x5555556020b0: 0x0000000000000000      0x0000000000000000
0x5555556020c0: 0x0000000000000000      0x0000000000000000
0x5555556020d0: 0x0000000000000000      0x0000000000000000
0x5555556020e0: 0x0000000000000000      0x0000000000000000
0x5555556020f0: 0x0000000000000000      0x0000000000000000
0x555555602100: 0x0000000000000000      0x0000000000000000
0x555555602110: 0x0000000000000000      0x0000000000000000
0x555555602120: 0x0000000000000000      0x0000000000000000

​ 这里我想着直接通过IDA反编译的来确定这俩BSS段数据的地址的,结果IDA里莫名其妙的,有点怪怪的,这里就直接GDB调算偏移然后算真实地址之类的吧。首先,刚刚那里确定了作者name的那个地址为:0x555555602040,gdb里调的时候查到elf的基地址为:0x555555400000(手动计算出来的),然后算出bss里作者name的偏移为:0x202040,加起来之后和0x555555602040这个地址一样,所以,可以断定,这个地址就是存放作者名字的地方,之后,经过两次申请内存之后,再看看0x555555602040地址的内存(根据结构体指针数组在bss段上,然后暴力经过两次malloc之后查询bss段内容有无变化发现了一些少量变化,由此定位结构体指针数组的地址):

pwndbg> x/30gx 0x555555602040
0x555555602040: 0x6161616161616161      0x6161616161616161
0x555555602050: 0x6161616161616161      0x6161616161616161
0x555555602060: 0x00005555556037a0      0x00005555556037d0
0x555555602070: 0x0000000000000000      0x0000000000000000
0x555555602080: 0x0000000000000000      0x0000000000000000
0x555555602090: 0x0000000000000000      0x0000000000000000
0x5555556020a0: 0x0000000000000000      0x0000000000000000
0x5555556020b0: 0x0000000000000000      0x0000000000000000
0x5555556020c0: 0x0000000000000000      0x0000000000000000
0x5555556020d0: 0x0000000000000000      0x0000000000000000
0x5555556020e0: 0x0000000000000000      0x0000000000000000
0x5555556020f0: 0x0000000000000000      0x0000000000000000
0x555555602100: 0x0000000000000000      0x0000000000000000
0x555555602110: 0x0000000000000000      0x0000000000000000
0x555555602120: 0x0000000000000000      0x0000000000000000

​ 发现0x555555602060这个地址的内容变了,并且,还是某个书的结构体的数据域的地址:

pwndbg> x/30gx 0x00005555556037a0
0x5555556037a0: 0x0000000000000001      0x00005555556036b0
0x5555556037b0: 0x00005555556036d0      0x00000000000000c8
0x5555556037c0: 0x0000000000000000      0x0000000000000031
0x5555556037d0: 0x0000000000000002      0x00007ffff7d66010
0x5555556037e0: 0x00007ffff7d44010      0x0000000000021000
0x5555556037f0: 0x0000000000000000      0x0000000000020811
0x555555603800: 0x0000000000000000      0x0000000000000000
0x555555603810: 0x0000000000000000      0x0000000000000000
0x555555603820: 0x0000000000000000      0x0000000000000000
0x555555603830: 0x0000000000000000      0x0000000000000000
0x555555603840: 0x0000000000000000      0x0000000000000000
0x555555603850: 0x0000000000000000      0x0000000000000000
0x555555603860: 0x0000000000000000      0x0000000000000000
0x555555603870: 0x0000000000000000      0x0000000000000000
0x555555603880: 0x0000000000000000      0x0000000000000000

​ 刚好整个程序存在一个修改作者名字的功能,可以修改作者名字,进行第二次off by null,修改0x00005555556036f0为0x0000555555603600:

pwndbg> x/30gx 0x555555602040
0x555555602040: 0x6262626262626262      0x6262626262626262
0x555555602050: 0x6262626262626262      0x6262626262626262
0x555555602060: 0x0000555555603700      0x00005555556037d0
0x555555602070: 0x0000000000000000      0x0000000000000000
0x555555602080: 0x0000000000000000      0x0000000000000000
0x555555602090: 0x0000000000000000      0x0000000000000000
0x5555556020a0: 0x0000000000000000      0x0000000000000000
0x5555556020b0: 0x0000000000000000      0x0000000000000000
0x5555556020c0: 0x0000000000000000      0x0000000000000000
0x5555556020d0: 0x0000000000000000      0x0000000000000000
0x5555556020e0: 0x0000000000000000      0x0000000000000000
0x5555556020f0: 0x0000000000000000      0x0000000000000000
0x555555602100: 0x0000000000000000      0x0000000000000000
0x555555602110: 0x0000000000000000      0x0000000000000000
0x555555602120: 0x0000000000000000      0x0000000000000000

​ 那么,0x0000555555603600这个地址指向的地方是哪里呢?用heap指令看看:

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555603000
Size: 0x290 (with flag bits: 0x291)

Allocated chunk | PREV_INUSE
Addr: 0x555555603290
Size: 0x410 (with flag bits: 0x411)

Allocated chunk | PREV_INUSE
Addr: 0x5555556036a0
Size: 0x20 (with flag bits: 0x21)

Allocated chunk | PREV_INUSE
Addr: 0x5555556036c0
Size: 0xd0 (with flag bits: 0xd1)

Allocated chunk | PREV_INUSE
Addr: 0x555555603790
Size: 0x30 (with flag bits: 0x31)

Allocated chunk | PREV_INUSE
Addr: 0x5555556037c0
Size: 0x30 (with flag bits: 0x31)

Top chunk | PREV_INUSE
Addr: 0x5555556037f0
Size: 0x20810 (with flag bits: 0x20811)

​ 发现这个地址是在book1_desc的中间:

pwndbg> x/50gx 0x5555556036c0
0x5555556036c0: 0x0000000000000000      0x00000000000000d1
0x5555556036d0: 0x65645f316b6f6f62      0x0000000000006373
0x5555556036e0: 0x0000000000000000      0x0000000000000000
0x5555556036f0: 0x0000000000000000      0x0000000000000000
0x555555603700: 0x0000000000000000      0x0000000000000000			<-------------------
0x555555603710: 0x0000000000000000      0x0000000000000000
0x555555603720: 0x0000000000000000      0x0000000000000000
0x555555603730: 0x0000000000000000      0x0000000000000000
0x555555603740: 0x0000000000000000      0x0000000000000000
0x555555603750: 0x0000000000000000      0x0000000000000000
0x555555603760: 0x0000000000000000      0x0000000000000000
0x555555603770: 0x0000000000000000      0x0000000000000000
0x555555603780: 0x0000000000000000      0x0000000000000000
0x555555603790: 0x0000000000000000      0x0000000000000031
0x5555556037a0: 0x0000000000000001      0x00005555556036b0
0x5555556037b0: 0x00005555556036d0      0x00000000000000c8
0x5555556037c0: 0x0000000000000000      0x0000000000000031
0x5555556037d0: 0x0000000000000002      0x00007ffff7d66010
0x5555556037e0: 0x00007ffff7d44010      0x0000000000021000
0x5555556037f0: 0x0000000000000000      0x0000000000020811
0x555555603800: 0x0000000000000000      0x0000000000000000
0x555555603810: 0x0000000000000000      0x0000000000000000
0x555555603820: 0x0000000000000000      0x0000000000000000
0x555555603830: 0x0000000000000000      0x0000000000000000
0x555555603840: 0x0000000000000000      0x0000000000000000

​ 内存布局大概有了,这里借用某位大佬的图(hollk):

在这里插入图片描述

​ 修改了book1的结构体指针地址之后,因为book1_name这里是可控的,所以可以在指向的那个地址伪造一个fake_chunk,

​ 因为后面确实对我而言有点逆天,所以之后就简述了吧,之后就是伪造chunk泄露libc地址,然后继续伪造fakechunk修改__free_hook为one_gadget,即可拿到shell。

 0x0000000000000000

0x555555603830: 0x0000000000000000 0x0000000000000000
0x555555603840: 0x0000000000000000 0x0000000000000000


​		内存布局大概有了,这里借用某位大佬的图(hollk):

[外链图片转存中...(img-sbaqzceF-1731158930159)]

​		修改了book1的结构体指针地址之后,因为book1_name这里是可控的,所以可以在指向的那个地址伪造一个fake_chunk,

​		因为后面确实对我而言有点逆天,所以之后就简述了吧,之后就是伪造chunk泄露libc地址,然后继续伪造fakechunk修改`__free_hook`为one_gadget,即可拿到shell。



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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Java Web的传智播客crm企业管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【Vue】Vue3.0(十七)Vue 3.0中Pinia的深度使用指南(基于setup语法糖)

上篇文章&#xff1a; 【Vue】Vue3.0&#xff08;十一&#xff09;Vue 3.0 中 computed 计算属性概念、使用及示例 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Vue专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月10日15点23分 文章…

常用的c++新特性-->day03

断言和异常 断言断言的基本使用 静态断言静态断言的基本使用 异常异常基本使用c98异常案例 noexceptnoexcept简单案例 断言 断言的基本使用 #include <iostream> #include <cassert>// >>>>>>>>>>>>>>>> 断言的…

11月7日星期四今日早报简报微语报早读

11月7日星期四&#xff0c;农历十月初七&#xff0c;早报#微语早读。 1、河南&#xff1a;旅行社组织1000人次境外游客在豫住宿2夜以上&#xff0c;可申请激励奖补&#xff1b; 2、主播宣称下播后商品恢复原价构成欺诈&#xff0c;广州市监&#xff1a;罚款5万元&#xff1b;…

数据结构-并查集专题(1)

一、前言 因为要开始准备年底的校赛和明年年初的ACM、蓝桥杯、天梯赛&#xff0c;于是开始按专题梳理一下对应的知识点&#xff0c;先从简单入门又值得记录的内容开始&#xff0c;并查集首当其冲。 二、我的模板 虽然说是借用了jiangly鸽鸽的板子&#xff0c;但是自己也小做…

【dvwa靶场:XSS系列】XSS (Stored)低-中-高级别,通关啦

更改name的文本数量限制大小&#xff0c; 其他我们只在name中进行操作 【除了低级可以在message中进行操作】 一、低级low <script>alert("假客套")</script> 二、中级middle 过滤了小写&#xff0c;咱们可以大写 <Script>alert("假客套…

【spark面试】spark的shuffle过程

概述 所有的shuffle的过程本质上就是一个task将内存中的数据写入磁盘&#xff0c;然后另一个task将磁盘中的数据读入内存的过程。 对于mapreduce来说&#xff0c;我们将内存中的数据写入磁盘成为maptask&#xff0c;将磁盘中的数据读入内存称为reducetask。 而对于spark来说&…

MySQL —— Innodb 索引数据结构

文章目录 不用平衡二叉树或红黑树作为索引B树适合作为索引比B树更适合作为索引的结构——B树总结 MySQL 使用 B树索引数据结构&#xff08;因为默认使用 innodb 存储引擎&#xff09; B树&#xff1a;有序数组 平衡多叉树&#xff1b;B树&#xff1a;有序数组链表 平衡多叉树…

shell中执行hive指令以及hive中执行shell和hdfs指令语法

0. 简介 主要介绍了三种环境命令执行语法&#xff1a; shell中执行hive指令hive中执行shell指令hive中执行hdfs指令 1. shell中执行hive指令 语法&#xff1a;hive [-hiveconf xy]* [<-i filename>]* [<-f filename> | <-e query-string>] [-S] 说明&…

MySQL系列之如何在Linux只安装客户端

导览 前言Q&#xff1a;如何安装一个Linux环境下的MySQL客户端一、准备文件1. 确认Server版本2. 选择Client安装文件 二、下载并安装1. 下载1.1 寻找文件1.2 文件说明 2. 安装2.1 上传至Linux服务器2.2 执行安装 三、连接验证1. 确认远程授权2. 建立远程连接 结语精彩回放 前言…

C++20 概念与约束(3)—— 约束的进阶用法

1、再谈约束主句与从句 上一篇文章中提到过约束可以无限嵌套。末尾也提到不考虑嵌套约束的情况下&#xff0c;模板因为 SFINAE 规则的存在&#xff0c;其中 requires 子句只要存在返回值&#xff0c;只有可能是 true 这一种结果。在非模板中&#xff0c;如果 requires 子句中的…

进程启动时,main 函数是如何被找到的?

Linux中一个进程是如何被启动起来的&#xff1f; 一、进程是怎么启动的&#xff1f;二、进程内存空间分段三、进程的入口函数四、总结 一、进程是怎么启动的&#xff1f; 当一个程序被执行时&#xff0c;怎么看出进程的运行呢&#xff1f;一个进程是怎么启动的&#xff1f;为什…

关于 el-table 的合计行问题

目录 一.自定义合计行 二.合计行不展示&#xff0c;只有缩放/变大窗口或者F12弹出后台时才展示 三.合计行出现了表格滚动条下方 四.合计行整体样式的修改 五.合计行单元格样式修改 1.css 2.jsx方式 六.合计行单元格合并 一.自定义合计行 通过 show-summary 属性开启合计…

十三:java web(5)-- Spring数据持久层

目录 Spring 数据持久层 1. Spring 与 JDBC 1.1 使用 Spring 管理数据库连接 1.1.2 Apache Commons DBCP 基于配置文件xml 使用 1.1.3 Apache Commons DBCP 基于配置类使用 1.1.4 HikariCP 基于配置文件xml 使用 推荐使用 Spring Boot 默认连接池 1.1.5 HikariCP 基于配置…

初学者指南:用例图——开启您的软件工程之旅

目录 背景&#xff1a; 基本组成&#xff1a; 关联&#xff08;Assciation&#xff09;&#xff1a; 包含&#xff08;Include&#xff09;&#xff1a; 扩展&#xff08;Extend&#xff09;&#xff1a; 泛化&#xff08;Inheritance&#xff09;&#xff1a; 完整银行…

基于单片机洗衣机控制器的设计(论文+源码)

1需求分析 在智能洗衣机系统设计中&#xff0c;考虑到洗衣机在实际应用过程中&#xff0c;需要满足用户对于不同衣物清洁、消毒的应用要求&#xff0c;对设计功能进行分析&#xff0c;具体如下&#xff1a; 通过按键实现洗衣机不同工作模式的切换&#xff0c;包括标准模式&am…

qt QFontDialog详解

1、概述 QFontDialog 是 Qt 框架中的一个对话框类&#xff0c;用于选择字体。它提供了一个可视化的界面&#xff0c;允许用户选择所需的字体以及相关的属性&#xff0c;如字体样式、大小、粗细等。用户可以通过对话框中的选项进行选择&#xff0c;并实时预览所选字体的效果。Q…

Python学习从0到1 day27 第三阶段 Spark ③ 数据计算 Ⅱ

目录 一、Filter方法 功能 语法 代码 总结 filter算子 二、distinct方法 功能 语法 代码 总结 distinct算子 三、SortBy方法 功能 语法 代码 总结 sortBy算子 四、数据计算练习 需求&#xff1a; 解答 总结 去重函数&#xff1a; 过滤函数&#xff1a; 转换函数&#xff1a; 排…

今天,智谱「新清影」上线,率先进入有声视频生成时代!还要继续开源宠粉

来&#xff0c;你先把手机音量打开&#xff0c;然后去“听”下面一段视频&#xff1a; 你是不是一脸懵逼&#xff1f;不知道我想表达什么&#xff1f; 视频是AI生成的并不奇怪&#xff0c;但你可能没法相信&#xff0c;这个视频的音效&#xff0c;也是AI生成的。 火车鸣笛 你…

「Mac畅玩鸿蒙与硬件31」UI互动应用篇8 - 自定义评分星级组件

本篇将带你实现一个自定义评分星级组件&#xff0c;用户可以通过点击星星进行评分&#xff0c;并实时显示评分结果。为了让界面更具吸引力&#xff0c;我们还将添加一只小猫图片作为评分的背景装饰。 关键词 UI互动应用评分系统自定义星级组件状态管理用户交互 一、功能说明 …