【计算机系统基础4】程序的机器级表示

news2024/9/21 2:34:36

4.程序的机器级表示

4.1(🏫 CMU补充 )x86-64 Linux 寄存器使用

  • %rax
    • 返回值
    • 调用函数保存
    • 可以通过程序修改
  • rdi,…,%r9
    • 传入参数(arguments)
    • 调用函数保存
    • 可通过程序进行修改
  • %r10%r11
    • 调用函数保存
    • 可通过程序进行修改
  • %rbx%r12%r13%r14
    • 被调用函数保存
    • 可通过程序进行修改
  • %rbp
    • 被调用函数保存
    • 被调用函数必须保存和恢复
    • 可能被用作栈帧的指针
  • rsp
    • 被被调用函数以一种特殊形式保存
    • 在退出过程时恢复到原始值

image-20220731163338484

image-20220731163351647

4.2传送指令

4.2.1mov 指令

  • 一般传送( mov

    • image-20220703142846440

      实现等宽的两个数据之间的传送,源数据和目的数据都是n位。

  • 零扩展传送(movz

    • image-20220704092752997

      把源数据当作是无符号整数,用于实现无符号整数的数据传送。

      源数据复制到目的空间中的低n位部分,在高位部分补充0

  • 符号扩展传送( movs

    • image-20220704094117391

      把源数据看成是带符号整数,用于实现带符号整数的数据传送。

      源数据复制到目的空间的低 n 位部分;高位补充源数据的最高有效位,即源数据的符号位。

  • 指定宽度传送(movswlmovzwl

    bwl :表示数据传送的宽度;b 表示8位,w 表示16位,l 表示32位,q 表示 64位。

    • 例1:movswl -0x16(%ebp), %eax

      **说明:**源数据是16位,目的数据是32位,要做符号扩展。

      表示含义:-0x16(%ebp) 地址开始的16位存储器内容符号扩展到32位后,送到寄存器eax中。

    • 例2:movzwl -0x1 6(%ebp), %eax

      **说明:**源数据是16位,目的数据是32位,要做零扩展。

      **表示含义:**将 -0x16(%ebp) 地址开始的16位存储器内容零扩展到32位以后,送入寄存器eax中。

mov 指令示例代码

主要是写汇编指令对代码实现以下语句:

image-20220704164306207

y = x:等宽传送

q = x:符号扩展传送

z = p:截断,低16位保存

  • 示例代码
#include <stdio.h>
void  main()
{
    short x = 0x8543,y = 1,z = 2;
    int p = 0x12345678,q = 3;
    asm( // -> 代表赋给,根据实际编译情况修改
        "movzwl -0xe(%rbp),%eax\n\t" // x -> eax(零扩展)
        "mov %ax,-0xc(%rbp)\n\t" // ax -> y
        "movswl -0xe(%rbp),%eax\n\t" // x -> eax(符号扩展)
        "mov %eax,-0x4(%rbp)\n\t" // (符号扩展后的)x -> q
        "mov -0x8(%rbp),%eax\n\t"// p -> eax
        "mov %ax,-0xa(%rbp)\n\t" //ax -> z
    );
    printf("x=%d,y=%d,z=%d\n",x,y,z);
    printf("p=%d,q=%d\n",p,q);
    return;
}
  • 调试

    • 查看栈帧范围

      (gdb) i r rsp rbp
      rsp            0x7ffffffeddd0      0x7ffffffeddd0
      rbp            0x7ffffffedde0      0x7ffffffedde0
      
    • 运行到 6 行

      image-20220704165215895

    • 剩下的就是分析赋值以后对应寄存器和栈帧中值的变化。

movlea

lea:Load Effect Address,加载有效地址。

lea指令:地址传送指令。寻址方式计算出来的地址 -> 寄存器

mov指令:image-20220704171146916

( 🏫 CMU补充 )为什么要用 LEA?

  • CPU设计者的预期用途:计算一个指向一个对象的指针

    • 例如,只将一个数组元素传递给另一个函数

      image-20220726163700601

  • 编译器的作者喜欢用它来进行普通的算术

    • 它可以在一条指令中进行复杂的计算

    • 这是x86仅有的三个操作数指令之一

      image-20220726163902850

movlea 示例程序

以视频为例:

image-20220704174842543

汇编前两句:

image-20220706105546261

汇编最后两句:

image-20220706105624306

C语言中的整数之间赋值运算实现

  • 情况一:n = m

    • image-20220706113857592

    编译器直接用 mov 指令,将 X 的机器数赋值给 Y

  • 情况二:n < m

    • image-20220706141913466

    X 定义为无符号整数,编译器用 movz 指令,将 X 的机器数传送给 Y

  • 情况三: n < m

    • image-20220706143528282

    X 定义为带符号整数,则编译器用 movs 指令,将 X 的机器数传送给 Y

  • 情况四:n > m

    • image-20220706145113616

    仅将 X 的低 m 位传送给 Y

C语言中的整数之间赋值运算实现示例代码

#include <stdio.h>

void main()
{
    int ix = -0x25432,iy,iz;
    short sx;
    unsigned uix,uiy,uiz;
    unsigned short usx;
    uix = ix;
    sx = ix;
    usx = ix;
    iy = usx;
    uiy = usx;
    iz = sx;
    uiz = sx;

    printf("整数赋值运算的机器级表示\n");
    printf("ix = %d\n", ix);
    printf("uix = %u\n",uix);
    printf("sx = %d\n",sx);
    printf("usx = %u\n",usx);
    printf("iy = %d\n",iy);
    printf("uiy = %u\n",uiy); 
    printf("iz = %d\n",iz);
    printf("uiz = %u\n",uiz);
    
    return;
}
  • 反编译后调试
    • image-20220706195040221
    • image-20220706195145541
    • image-20220706195216928
    • image-20220706195312364
    • image-20220706195429241

( 🏫 CMU补充 )关于各类寻址方式

  • 简单内存寻址模式

    (R)    ⟺    \iff Mem[Reg[R]]

    • 类似 C 中的指针引用取值
    • 寄存器 R 表示内存地址
    • 例如:movq (%rcx),%rax 就是将 rcx 的地址内容赋值给 rax

    D(R)    ⟺    \iff Mem[Reg[R]+D]

    • 寄存器 R 指定内存区域的开始位置
    • 常量位移 D 指定偏移量
    • 例如:movq 8(%rbp),%rdx就是将 rbp 的地址+ 8 的内容赋值给 rax
  • 完整的内存寻址模式

    D(Rb,Ri,S)    ⟺    \iff Mem[Reg[Rb]+S*Reg[Ri]+D]

    • D: 常数偏移量。1、2或4个字节

    • Rb:基址寄存器(Base register):16个整数寄存器中的任何一个

    • Ri:索引寄存器:任何,%rsp 除外

    • S : 倍数。1,2,4 或者8(为啥这些数? 😕 )

    • 一些具体案例:

      image-20220726161235839

    • 一些形象的举例:

      image-20220726162825844

加减运算指令

对于带符号整数

说明:数据被定义为带符号整数,后续指令可以根据 OF 状态标志位来判断结果是否溢出;

image-20220706203728341

对于无符号整数

说明:数据被定义为无符号整数,后续指令可以根据 CF 状态标志位来判断结果是否有进位或借位。

image-20220706205956822

示例代码

#include<stdio.h>
int addition(int x,int y){
    asm(
        "mov -0x4(%rbp),%eax\n\t" //此处代码要参考 x 这个形参存放的地址
        "add -0x8(%rbp),%eax\n\t"//此处代码要参考 y 这个形参存放的地址
    ); //函数默认返回 eax 
}

int substraction(int x,int y){
    asm(
        "mov -0x4(%rbp),%eax\n\t"//此处代码要参考 x 这个形参存放的地址
        "sub -0x8(%rbp),%eax\n\t"//此处代码要参考 y 这个形参存放的地址
    );//函数默认返回 eax 
}

void main()
{
    int ix = 10,iy = 4,az,sz,z;
    unsigned ux = 10,uy = 4,auz,suz,uz;
    az = addition(ix,iy); auz = addition(ux,uy);
    printf("%d + %d = %d,%u + %u = %u\n",ix,iy,az,ux,uy,auz);
    az = substraction(ix,iy); auz = substraction(ux,uy);
    printf("%d - %d = %d,%u - %u = %u\n",ix,iy,az,ux,uy,auz);
    z = addition(2147483647,1);
    printf("2147483647 + 1:%d, %u\n",z,z);
    uz = substraction(3,4);
    printf("3 - 4:%d,%u\n",uz,uz);
    return;
}
  • 运行结果

    ./addsum 
    10 + 4 = 14,10 + 4 = 14
    10 - 4 = 6,10 - 4 = 6
    2147483647 + 1:-2147483648, 2147483648
    3 - 4:-1,4294967295
    
  • 调试

    • 对于 addition

    执行到 24 行z = addition(2147483647,1);以后,跳转到第 2 行addition,查看赋值到内存区域后,以下是两个加数。

    image-20220707134517331

    执行完 asm 里面的程序以后,查看返回的 eax 寄存器的值,以及 eflags 的情况

    image-20220707145241230

    可以看到 OF 为 1,说明如果是带符号数,则溢出了,结果有误。

    • 对于 substraction

    执行到 26 行 uz = substraction(3,4);以后,跳转到第 9 行,查看赋值到内存区域后,以下是两个加数。

    image-20220707150219002

    执行完 asm 里面的程序以后,查看返回的 eax 寄存器的值,以及 eflags 的情况

    image-20220707150331334

    可以看到 CF 为 1,说明如果是无符号数,则溢出了,结果有误。

cmp 比较指令

  • 假设 A 和 B 是无符号整数:

    image-20220707163609522

  • 假设 A 和 B 是带符号整数:

    image-20220707163652245

  • 示例程序

    image-20220707163758228

( 🏫 CMU补充 )判断条件码 eflags

  • 基本的标志

    • CF 进位标志:Carry Flag (for unsigned)
    • SF 符号标志:Sign Flag (for signed)
    • ZF 零标志:Zero Flag
    • OF 溢出标志:Overflow Flag (for signed)
  • 标志位 置1的情况

    • ZF 置 1

      image-20220726170446142

    • SF 置 1

      image-20220726170510198

    • CF 置 1

      image-20220726170545971

    • OF 置 1

      image-20220726170622346

  • 关于设置标志位

    SetXConditionDescription
    seteZFEqual / Zero
    setne~ZFNot Equal / Not Zero
    setsSFNegative
    setns~SFNonnegative
    setg~(SF^OF)&~ZFGreater (Signed)
    setge~(SF^OF)Greater or Equal (Signed)
    setl(SF^OF)Less (Signed)
    setle`(SF^OF)ZF`
    seta~CF&~ZFAbove (unsigned)
    setbCFBelow (unsigned)

( 🏫 CMU补充 )关于 test 指令

  • test a,b
    • 计算𝑏^𝑎(就像 and 一样)
    • 根据结果设置条件代码(仅限 SFZF ),但不改变 b
  • 最常见的用途: test %rX, %rX ——%rx 和 0 比较
  • 第二种最常见的用途:test %rX, %rY—— 测试在 %rY 的任何一位在 %rX 中也是 1。(反之亦然)

整数乘法指令

C语言中整数乘法的实现

  • 示例代码mulc.c
#include<stdio.h>
void main()
{
    int x = 3,y = 4,z1,z2,z3,z4;
    unsigned ux = 3,uy = 4,uz;
    z1 = x * y;
    uz = ux * uy;
    z2 = x * 3;
    z3 = x * 1024;
    z4 = x*x + 4 *x + 8;
    printf("z1 = %d,z2 = %d,z3 = %d,z4 = %d\n",z1,z2,z3,z4);
    return;
}
  • 编译后反汇编分析

    • 对于带符号数

      image-20220713160429865

      M[R[ebp]-0x28]*R[eax]-> R[eax] 实现两个 32 位整数的乘法运算,虽然乘法电路中产生的结果有 64 位,但指令仅保存低32位到寄存器 eax 中。

    • 对于无符号整数

      image-20220713160552561

      无符号整数的乘法运算用带符号的乘法指令imul实现的原因:仅取乘积的低 32 位作为结果保存给变量 uz ;得到的 uz 的二进制序列,与运用 mul 指令时一致。

    • 变量与常量乘法

      • image-20220713161144557

        整数的乘法运算在电路层中通过加法和移位的迭代运算实现,乘法指令的执行时间远远长于加指令的执行时间,所以遇到变量与常量的乘法运算时,编译器常常不用乘法指令,而是使用加法指令或移位指令实现。

      • image-20220713161255282

    • 多项式(此处和视频教学情况有所不同,笔者是在 64 位 ubuntu 上进行测试)

      image-20220713161407517

整数乘法指令(mulimul

  • 示例代码(mul2.c
#include <stdio.h>

unsigned umul(unsigned x,unsigned y){
    asm(
        "mov -0x4(%rbp),%eax\n\t"//此处要换上具体编译的地址
        "mov -0x8(%rbp),%ecx\n\t"//此处要换上具体编译的地址
        "mul %ecx\n\t"
    );
}

int imul(int x,int y){
    asm(
        "mov -0x4(%rbp),%eax\n\t" //此处要换上具体编译的地址
        "mov -0x8(%rbp),%ecx\n\t"//此处要换上具体编译的地址
        "mul %ecx\n\t"
    );
}

void main()
{
    int x = -1610612735, y = 8;       //x=0xa0000001
    unsigned ux = 2684354561, uy = 8; //ux=0xa0000001
    int z;
    z = imul(x, y);
    printf("%d * %d = %xH = %d\n",x,y,z,z);
    z = umul(ux, uy);
    printf("%u * %u = %xH = %u\n",ux,uy,z,z);
    return;
}
  • 调试

    • imul

      image-20220722103233204

      两个 32 位的有符号数字相乘超出了32位(溢出了),但是输出结果只输出低 32 位。返回结果由 eax 存放

    • umul

      image-20220722104028828

      两个 32 位的无符号数字相乘超出了32位(溢出了),但是输出结果只输出低 32 位。返回结果由 eax 存放。

      注意: 虽然输出结果与无符号数相同,因为二者的低 32 位相同。但是高 32 位不同。

整数乘法的溢出问题

  • 对于带符号整数

    image-20220722105025323

  • 对于无符号整数

    image-20220722105135577

控制转移指令

指令执行顺序: CSEIP 寄存器确定。

EIP寄存器: 程序计数器 PC ,用于存储下一条要执行的指令地址。(64 位系统是 rip)

指令执行转移: 修改 CSEIP ,或仅修改 EIP

转移指令的分类和功能

  • 分类

    • **无条件转移指令 JMP **

      无条件转移到目标地址处执行

    • 条件转移指令

      一种分支转移的情况,eflgas 寄存器中的状态标志位或状态标志位的逻辑运算结果为转移条件,如果满足转移条件,则转移到目标转移地址处执行,如果不满足转移条件,则顺序执行下一条指令。

  • 指令类别

    • 过程调用指令 CALL

      一种无条件转移指令,将控制转移到被调用的子程序执行。

    • 过程返回指令 RET

      一种无条件转移指令,子程序的最后条指令,将控制从子程序返回到主程序继续执行。

    • 中断指令

      调用中断服务程序,使程序的执行从用户态转移到内核态。

相对转移地址的计算

目标转移地址= R[PC] + 偏移量 = 当前转移指令地址 + 转移指令字节数 + 偏移量

  • 示例程序(jmp.c

    #include<stdio.h>
    
    int sum(int a[],int n)
    {
        int i,sum = 0;
        for(i = 0;i < n;i++)
            sum += a[i];
        return sum;
    }
    
    void main()
    {
        int a[4] = {1,2,3,4},n=3,x;
        x = sum(a,n);
        printf("sum=%d\n",x);
    }
    
  • 编译

    gcc -O0 -g -no-pie -fno-pic jmp.c -o jmp2 
    

    -pie : 位置无关可执行程序

    -no-pie : 不采用位置无关可执行程序

    -pic: 位置无关代码,程序可以加载到虚拟空间的任意位置

    -fno-pic : 不采用位置无关的方式编译代码

关于 call

两个功能:

  1. call指令的返回地址入栈
  2. 目标转移地址送入 eip( 64 位系统是 rip

image-20220722192652817

  • 关于 call 8048466 的计算(相对转移的偏移量)

    image-20220722193535374

    上图说明偏移量占 4 个字节,为 7affffffe8 为 执行指令本身,也占了 1 个字节。说明执行这个操作占了 5 个字节。

    image-20220722193909921

    上图说明了 call 8048466 的由来。

关于 jmp

jmp 指令的功能:目标转移地址送入eip(64 位 系统为 rip

  • jmp 指令的目标转移地址是如何计算 ?

    image-20220722195151446

关于 jl

jl 指令的转移条件: SF!=OF and ZF=0

jl 指令的功能: 满足转移条件,将目标转移地址送入eip,否则继续执行后续指令(64 位 系统为 rip

image-20220722200410357

上图,cmp 对比的就是 in,如果满足上述条件,则将 804847c 转移到 rip 寄存器中。

image-20220722200436131

上图,当执行完 cmp 语句以后,发现 eflags 提供的标志位满足条件

image-20220722200659951

上图,完成以后,将 804847c 这个地址转移到 rip 寄存器中。

image-20220722202049975

上图,说明了 084847c 的计算方式

关于 ret

ret 指令的功能 : 返回地址送入eip 返回地址是 call 指令压入栈帧中的 (相当于 pop,执行的是 call 调用之后的语句)

image-20220722204522103

上图,注意观察 eip 这个 80484ec 这个值是 esp 弹出的内容,这个值指向的是主函数调用 call 指令之后的语句。

image-20220722204859472

可以确认这个值指向的是主函数调用 call 指令之后的语句。

栈和过程调用

总体调用关系以及寄存器使用

image-20220723104058479

上图:P 是调用函数,Q 是被调用函数

示例程序

#include <stdio.h>
int swap(int *x,int *y)
{
    int t = *x;
    *x = *y;
    *y = t;
}
void main()
{
    int a = 15,b = 22;
    swap(&a,&b);
    printf("a=%d\tb=%d\n",a,b);
}

step1️⃣:相关寄存器入栈

image-20220723104520934

上图:eax 等 P 保存的寄存器入栈(如果这些寄存器被使用了)。

image-20220723105204989

上图:P 把过程调用的参数值送入栈中。

image-20220725094624033

上图,过程调用的准备工作,示例代码反汇编

image-20220725112909198

上图,step1️⃣ 的栈帧情况

step2️⃣:call 指令

image-20220723105432814

上图:call 指令将 call 指令的下一条指令(也就是返回地址)压入栈中。

image-20220725094659818

上图,call 指令实现 swap 过程调用。示例代码反汇编

image-20220725113025425

上图,step2️⃣ 的栈帧情况,call 指令执行后,将 main 函数返回地址入栈

step3️⃣:设置被调用函数 Q 的寄存器

image-20220723155446411

上图,建立 Q 的当前栈帧,将当前 ebp 内容压入栈中。

image-20220725095307343

上图,保存 edp 。在示例代码反汇编中。

image-20220723162719327

上图,将 esp 寄存器内容传送给 ebpebpesp 指向了统一地址单元,建立了 Q 的栈帧。

image-20220725095400503

上图,建立自己的栈空间。示例代码反汇编。

image-20220723163124714

上图,要访问 P 传递的值,通过 0xc(%ebp)0x8(%ebp) 访问,就是访问实参

image-20220723164057761

上图,ebx 等寄存器如果被 Q 使用,则压入栈中。

image-20220723172431174

上图,保存非静态局部变量后移动 esp

image-20220725113614252

上图:step3️⃣ 的栈帧情况(左),保存旧 ebp 的值,建立新的栈帧。step3️⃣ 的栈帧情况(右),分配栈空间

step4️⃣:执行 Q 的过程体

image-20220725100701656

上图,执行过程反汇编

image-20220725114012548

上图,执行过程栈帧示意图

step5️⃣: 收回栈空间

image-20220724095827056

上图,将寄存器 ebx 等使用过的寄存器出栈,收回栈空间,IA32 中提供 leave 指令回收栈空间

image-20220725100734469

上图,leave 指令回收栈空间。

image-20220725114054452

上图,step5️⃣ 栈帧情况图,指令回收栈空间

step6️⃣: ret

image-20220724161634152

上图,通过 ret 指令返回 P,此时 esp 指向参数 1 的单元。

image-20220725100831002

上图,ret 返回调用者。

image-20220725094723437

上图,过程调用的结束工作。示例代码反汇编。

image-20220725145452565

上图,step6️⃣ 栈帧情况图,返回到主函数的下一条指令。

( 🏫 CMU补充 )画程序栈图补充

image-20220910141643467

image-20220910141808917

上图,设置返回地址

image-20220910141900283

上图,存储 before

image-20220910142047497

上图,存储 after

image-20220910142152450

上图,存储 buf

image-20220910142507305

上图,使用 GDB 调试

image-20220910142553201

上图,调试效果

image-20220910142723413

上图,x86-64 的栈帧结构

( 🏫 CMU补充 )递归函数的调用过程

image-20220731163756867

上图,左边为 C 语言代码,右边为汇编代码

image-20220731163831792

上图,设置 eax 初始值为 0 ,如果 rdi (也就是 x)为 0 ,直接 ret,返回 rax

image-20220731163849119

上图,rbx 为暂存值。

image-20220731163911706

上图,执行 & 操作和逻辑右移操作。

image-20220731163940390

上图,调用 pcount_r 函数

image-20220731164013824

上图,执行求和操作。

image-20220731164051206

上图,在用完暂存变量 rbx,在程序返回前,弹出 rbx

栈与过程调用实验:缓冲区溢出攻击

原理示意图

image-20220725153859093

就是让本应该是 main 函数的下一条指令改为恶意代码首地址。

实施过程

  • 攻击示例代码(a.c)

    #include<stdio.h>
    #include<string.h>
    
    char code[]="0123456789abcdef";
    
    int main()
    {
        char *arg[3];
        arg[0] = "./b";
        arg[1] = code;
        arg[2] = NULL;
        execve(arg[0],arg,NULL);
        return 0;
    }
    
  • 被攻击示例代码(b.c)

    #include<stdio.h>
    #include<string.h>
    void outputs(char *str)
    {
        char buffer[16];
        strcpy(buffer,str);
        printf("%s\n",buffer);
    }
    
    void hacker(void)
    {
        printf("being hacked\n");
    }
    
    int main(int argc,char *argv[])
    {
        outputs(argv[1]);
        printf("yes\n");
        return 0;
    }
    
  • 关闭栈的随机化

    sudo sysctl -w kernel.randomize_va_space=0
    
  • 编译

    gcc -o0  -g -fno-stack-protector -z execstack -no-pie -fno-pic a.c -o a
    gcc -o0  -g -fno-stack-protector -z execstack -no-pie -fno-pic b.c -o b
    

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

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

相关文章

【网络协议详解】——VLAN技术(学习笔记)

目录 &#x1f552; 1. VLAN介绍&#x1f558; 1.1 目标&#x1f558; 1.2 帧格式&#x1f558; 1.3 划分方式&#x1f558; 1.4 链路&#x1f558; 1.5 端口模式&#x1f564; 1.5.1 接入端口&#x1f564; 1.5.2 干道端口&#x1f564; 1.5.3 混合端口&#xff08;仅华为交换…

信息收集-端口

&#xff08;一&#xff09;端口号 端口号&#xff0c;是指在Internet传输控制协议&#xff08;TCP&#xff09;或用户数据报协议&#xff08;UDP&#xff09;中&#xff0c;用于标识具体应用程序与计算机之间通信的端口号码 互联网上有许多使用TCP和UDP协议进行通信的应用程…

【网络协议详解】——STP技术(学习笔记)

目录 &#x1f552; 1. STP技术工作原理&#x1f552; 2. BPDU报文&#x1f558; 2.1 配置BPDU&#x1f558; 2.2 TCN BPDU &#x1f552; 3. 实验&#xff1a;了解STP生成过程 &#x1f552; 1. STP技术工作原理 以太网交换机使用生成树协议STP&#xff08;Spanning Tree Pro…

连续签到积分兑换试用流量主小程序开发

每日签到积分兑换试用流量主小程序开发 打卡兑奖小程序。用户签到活得积分。积分可以兑换商品。观看激励视频广告可以积分翻倍。 用户可以参加试用商品活动参加试用需要提交信息。可以通过分享方式直接获取试用资格。 以下是流量主小程序的功能列表&#xff1a; 广告位管理&a…

JavaWeb——HTTP 协议的基本格式和 fiddler 的用法

目录 一、HTTP定义 二、HTTP协议的工作流程 三、抓包工具Fiddler的用法 1、介绍 2、原理 3、抓包结果 &#xff08;1&#xff09;、HTTP请求 &#xff08;2&#xff09;、HTTP响应 四、HTTP协议的格式 1、HTTP请求 &#xff08;1&#xff09;、请求行 &#xff08;2…

OpenAPI的签名校验

前言 作为一个后端开发&#xff0c;提供API接口或者对接第三方API接口的时候&#xff0c;考虑接口的防刷、重放等安全问题&#xff0c;严格意义上&#xff0c;都需要加上双方约定的签名规则。 大致思路 一般情况下&#xff0c;签名规则没有墨守成规的规定&#xff0c;只要考…

MediaPipe Face Detection可运行在移动设备上的亚毫秒级人脸检测

MediaPipe人脸检测 MediaPipe人脸检测是一种超快速的人脸检测解决方案,具有6个界标和多人脸支持。它基于BlazeFace,BlazeFace是为移动GPU推理量身定制的轻巧且性能良好的面部检测器。检测器的超实时性能使其可应用于需要准确地关注面部区域作为其他任务特定模型: 例如 1、3…

如何有效解决企业文件安全事件频发问题?

企业文件安全是企业必须解决的一个关键问题。随着数字化趋势的不断发展&#xff0c;企业严重依赖于以电子格式存储和访问数据。这种转变使得组织必须实施适当的安全协议&#xff0c;以确保其敏感数据免受未经授权的访问或盗窃。 企业网盘的使用已经在公司中流行起来&#xff0c…

ChatGPT:3. 使用OpenAI创建自己的AI网站:2. 使用 flask web框架快速搭建网站主体

使用OpenAI创建自己的AI网站 如果你还是一个OpenAI的小白&#xff0c;有OpenAI的账号&#xff0c;但想调用OpenAI的API搞一些有意思的事&#xff0c;那么这一系列的教程将仔细的为你讲解如何使用OpenAI的API制作属于自己的AI网站。博主只能利用下班时间更新&#xff0c;进度慢…

mybatis是如何集成到spring的之SqlSessionFactoryBean

文章目录 1 前言1.1 集成spring前使用mybatis的方式1.2 集成mybatis到spring的关键步骤 2 SqlSessionFactoryBean对象分析2.1 buildSqlSessionFactory做了什么事情&#xff1f;2.2 为什么是SqlSessionFactoryBean却可以使用SqlSessionFactory&#xff1f; 3 验证demo4 举一反三…

【QT】自定义工程封装成DLL并如何调用(带ui界面的)

一、动态库的封装 1、首先新建一个Library工程 2、修改类型为共享库&#xff0c;自定义项目名称以及项目路径 3、选择编译器 4、选择动态库所需要的模块 5、自定义类名&#xff0c;点击下一步 6、点击下一步 7、项目总览 8、此时的文件中还没有ui文件&#xff0c;因为要封装带…

南京邮电大学算法与设计实验四:回溯法(最全最新,与题目要求一致)

要求用回溯法求解8-皇后问题&#xff0c;使放置在8*8棋盘上的8个皇后彼此不受攻击&#xff0c;即&#xff1a;任何两个皇后都不在同一行、同一列或同一斜线上。请输出8皇后问题的所有可行解。 用回溯法编写一个递归程序解决如下装载问题&#xff1a;有n个集装箱要装上2艘载重分…

pg事务:隔离级别(2)

事务隔离级别的历史 ANSI SQL-92定义的隔离级别和异常现象确实对数据库行业影响深远&#xff0c;甚至30年后的今天&#xff0c;绝大部分工程师对事务隔离级别的概念还停留在此&#xff0c;甚至很多真实的数据库隔离级别实现也停留在此。但后ANSI92时代对事物隔离有许多讨论甚至…

【5.20】五、安全测试——渗透测试

目录 5.3 渗透测试 5.3.1 什么是渗透测试 5.3.2 渗透测试的流程 5.3 渗透测试 5.3.1 什么是渗透测试 渗透测试是利用模拟黑客攻击的方式&#xff0c;评估计算机网络系统安全性能的一种方法。这个过程是站在攻击者角度对系统的任何弱点、技术缺陷或漏洞进行主动分析&#x…

如何在项目管理中实现任务活动的留痕管理?

项目工作为什么需要留痕呢? 1&#xff0c;记录项目工作&#xff1a;在项目管理工作中常常涉及多部门协作&#xff0c;工作留痕可以帮助我们有效复原已经发生了的工作活动&#xff0c;从而留下印迹供日后查证。 2&#xff0c;支撑工作复盘&#xff1a;在项目工作结束之后&…

SpringBoot程序启动速度提速分析

传统的破程序&#xff08;百万行级一个微服务&#xff09;&#xff0c;在我的P15-gen2代电脑上启动一次需要80秒左右(直接运行三次&#xff0c;取平均值&#xff09;&#xff0c;在其它人电脑上可想而知了。 大概记录几点 1 优化肯定是需要找工具观察的&#xff0c;不观测还…

MyBatis、MyBatis-plus

文章目录 MyBatis一、MyBatis简介1. 什么是MyBatis2. MyBatis开发步骤3. 环境搭建4. MyBatis的映射文件&#xff08;UserMapper)5. 动态sql语句6. MyBatis的增删改查 二、MyBatis核心配置文件&#xff08;sqlMapConfig&#xff09;1. MyBatis常用配置1&#xff09;environments…

使用JAVA代码实现跳动爱心(脱单节程序员必备哦)

520&#xff01;&#xff01;&#xff01;表白日&#xff0c;你脱单了吗&#xff1f;你跟对象彻夜不归了吗&#xff1f; 如果没有说明&#xff0c;你的诚心不够&#xff0c;来给对象一个代码表白吧&#xff01; 话不多说&#xff0c;先上效果图&#xff1a; 实现代码如下&…

kicad学习笔记6:kicad启动及其grid参数设置和修改

1。kicad启动&#xff1a; single_top.cpp 启动函数&#xff1a; 1。 IMPLEMENT_APP( APP_SINGLE_TOP )2。 PGM_SINGLE_TOP::OnPgmInit()3。 PGM_BASE::InitPgm2。kicad参数 grid参数定义&#xff1a; struct GRID_SETTINGS {bool axes_enabled;std::vector<wxString&…

华为OD机试真题 Java 实现【天然蓄水池】【2023Q1 200分】

一、题目描述 公元2919年&#xff0c;人类终于发现了一颗宜居星球——X星。现想在X星一片连绵起伏的山脉间建一个天然蓄水库&#xff0c;如何选取水库边界&#xff0c;使蓄水量最大&#xff1f; 要求&#xff1a; 山脉用正整数数组s表示&#xff0c;每个元素代表山脉的高度。…