CSAPP - 反汇编 strings_not_equal

CSAPP - 反汇编 strings_not_equal

CSAPP bomlab1 中涉及到的 strings_not_equal 函数, 虽然可以从函数名字猜出函数含义,但我想根据汇编代码反推出对应的C代码,而不是根据函数名字猜测。

相比于专门学习 CTF 的选手, 本篇的废话很多,是完全不熟悉汇编的视角出发。

一点经验:

  • 逐句翻译汇编,写出对应的C代码
  • 写出C代码的时候,增加注释,把寄存器 和 变量名字 绑定起来, 好处是下次看到 callee-saved 寄存器时,直接对应到C代码

文章目录

    • CSAPP - 反汇编 strings_not_equal
      • 概况 - 涉及的函数调用
      • 完整的反汇编代码
      • 偏移地址 0, 2, 3 和偏移地址 101, 102, 103 的汇编解读
      • 偏移地址 4, 7
      • 偏移地址 94, 99 - 函数返回值类型
      • 偏移地址 10, 15, 18, 21, 26, 31, 34, 99 - 调用 string_length
      • 偏移地址 36, 39, 41, 80, 85, 99
      • 偏移地址 43, 46, 58, 62
      • 偏移地址 66, 69, 71, 50, 53, 56, 94, 99
      • 偏移地址 69, 71, 73, 78, 99
      • 50, 53, 56, 58, 62
      • 43, 46, 48, 87, 92, 99
      • 整理代码

概况 - 涉及的函数调用

int main()
{phase_1()- read_line()- strings_not_equal()- string_length()
}

完整的反汇编代码

前文已对 string_length() 做了反汇编(汇编代码到C代码的人工翻译和整理), 本篇对 strings_not_equal() 做反汇编, 它的完整汇编代码是:

(gdb) disassemble strings_not_equal 
Dump of assembler code for function strings_not_equal:0x0000000000401338 <+0>:     push   r120x000000000040133a <+2>:     push   rbp0x000000000040133b <+3>:     push   rbx0x000000000040133c <+4>:     mov    rbx,rdi0x000000000040133f <+7>:     mov    rbp,rsi0x0000000000401342 <+10>:    call   0x40131b <string_length>0x0000000000401347 <+15>:    mov    r12d,eax0x000000000040134a <+18>:    mov    rdi,rbp0x000000000040134d <+21>:    call   0x40131b <string_length>0x0000000000401352 <+26>:    mov    edx,0x10x0000000000401357 <+31>:    cmp    r12d,eax0x000000000040135a <+34>:    jne    0x40139b <strings_not_equal+99>0x000000000040135c <+36>:    movzx  eax,BYTE PTR [rbx]0x000000000040135f <+39>:    test   al,al0x0000000000401361 <+41>:    je     0x401388 <strings_not_equal+80>0x0000000000401363 <+43>:    cmp    al,BYTE PTR [rbp+0x0]0x0000000000401366 <+46>:    je     0x401372 <strings_not_equal+58>0x0000000000401368 <+48>:    jmp    0x40138f <strings_not_equal+87>0x000000000040136a <+50>:    cmp    al,BYTE PTR [rbp+0x0]0x000000000040136d <+53>:    nop    DWORD PTR [rax]0x0000000000401370 <+56>:    jne    0x401396 <strings_not_equal+94>0x0000000000401372 <+58>:    add    rbx,0x10x0000000000401376 <+62>:    add    rbp,0x10x000000000040137a <+66>:    movzx  eax,BYTE PTR [rbx]0x000000000040137d <+69>:    test   al,al0x000000000040137f <+71>:    jne    0x40136a <strings_not_equal+50>0x0000000000401381 <+73>:    mov    edx,0x00x0000000000401386 <+78>:    jmp    0x40139b <strings_not_equal+99>0x0000000000401388 <+80>:    mov    edx,0x00x000000000040138d <+85>:    jmp    0x40139b <strings_not_equal+99>0x000000000040138f <+87>:    mov    edx,0x10x0000000000401394 <+92>:    jmp    0x40139b <strings_not_equal+99>0x0000000000401396 <+94>:    mov    edx,0x10x000000000040139b <+99>:    mov    eax,edx0x000000000040139d <+101>:   pop    rbx0x000000000040139e <+102>:   pop    rbp0x000000000040139f <+103>:   pop    r120x00000000004013a1 <+105>:   ret    
End of assembler dump.

由于汇编较长,并且在执行时跳来跳去,像是乱划线,因此按照汇编代码相对于函数起始地址的偏移量(例如 +8) 作为序号进行解释。一共105个序号,其中很多相邻行可以合并起来看。

偏移地址 0, 2, 3 和偏移地址 101, 102, 103 的汇编解读

   0x0000000000401338 <+0>:     push   r120x000000000040133a <+2>:     push   rbp0x000000000040133b <+3>:     push   rbx...0x000000000040139d <+101>:   pop    rbx0x000000000040139e <+102>:   pop    rbp0x000000000040139f <+103>:   pop    r12

+0: push r12: 把 r12 寄存器内容压栈。对应的是 +103pop 103, 是从栈上恢复 r12 寄存器。
为啥要压栈和恢复?因为 r12 寄存器要被当前函数 strings_not_equal 使用, 而调用规约规定, callee 要在函数结束时恢复 r12, 这样的话,前面一个调用 r12 的函数(frame)能继续用 r12。

+2: push rbp: 将基指针寄存器(Base Pointer,rbp)的内容压入栈中。rbp 通常用于标记当前栈帧的开始,它在函数调用过程中用于定位局部变量和函数参数。
显然, 先压入 r12, 再压入 rbp 是比较合理的。
对应的是 +102pop rbp, 弹出 rbp。

+3 push rbx: 将 rbx 寄存器的内容压入栈中。rbx 是另一个被调用者保存寄存器,它在函数执行过程中用于保存一个临时值,或者作为一个指向数据的指针。
对应的是 +101pop rbx, 恢复 rbx 寄存器。

callee-saved寄存器

在 x86_64 架构中,使用的是 System V AMD64 ABI 调用约定(这是大多数 Unix-like 系统,包括 Linux 和 macOS,所采用的)。根据这个调用约定,以下是被调用者保存(callee-saved)寄存器的列表:

RBX - 基址寄存器(Base register)
RBP - 基指针寄存器(Base pointer register)
R12 - 第12个通用寄存器
R13 - 第13个通用寄存器
R14 - 第14个通用寄存器
R15 - 第15个通用寄存器
RSP - 栈指针寄存器(Stack pointer register),虽然通常不直接保存和恢复,但必须在函数调用结束时保持一致。
被调用者保存的寄存器是指在函数调用过程中,如果一个函数需要修改这些寄存器,它必须在函数返回前将它们恢复到原始值。这意味着调用者可以期望这些寄存器在函数调用后保持不变。

caller-saved寄存器

除此之外,还有一些寄存器是调用者保存(caller-saved)的,也被称作易失性(volatile)寄存器,包括:

  • RAX - 累加器寄存器(Accumulator):用于整数运算和返回值。
  • RCX - 计数寄存器(Counter):用于字符串操作和循环计数。
  • RDX - 数据寄存器(Data register):用于整数运算和输入/输出操作。
  • RSI - 源索引寄存器(Source Index):在字符串和数组操作中用作源地址指针。
  • RDI - 目的索引寄存器(Destination Index):在字符串和数组操作中用作目的地址指针。
  • R8 - 第8个通用寄存器:用于整数运算和传递函数参数。
  • R9 - 第9个通用寄存器:同样用于整数运算和传递函数参数。
  • R10 - 第10个通用寄存器:通常用于整数运算。
  • R11 - 第11个通用寄存器:通常用于整数运算。
  • XMM0-XMM15(浮点寄存器(用于浮点数和 SIMD 运算):
    • XMM0-XMM7 - 用于浮点运算和 SIMD 运算的寄存器,同时也用于传递函数参数和返回值。
    • XMM8-XMM15 - 在某些系统上,这些额外的寄存器用于相同的目的,但主要是在函数调用中作为易失性寄存器使用。
      这些寄存器可以在函数调用中被自由修改,不需要保存和恢复它们的原始值。如果调用者希望在函数调用后使用这些寄存器的值,它必须在调用之前自己保存它们。

偏移地址 4, 7

   0x000000000040133c <+4>:     mov    rbx,rdi0x000000000040133f <+7>:     mov    rbp,rsi

前面提到 rbx 和 rbp 都是 callee-saved 寄存器, 已经在使用前保存原有值、 在函数结束时恢复原有值。
现在开始使用 rbx 和 rbp:
mov rbx, rdi: rdi 寄存器存放了函数 strings_not_equal() 第一个参数。这句汇编是意思是把第一个参数放到 rbx 寄存器里。
mov rbp, rsi: 类似上面这句, 是把第二个参数放到 rbp 寄存器里。

尝试写一下对应的 C 代码:

return_type strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;const char* p2 = s2;
}

偏移地址 94, 99 - 函数返回值类型

   0x0000000000401396 <+94>:    mov    edx,0x10x000000000040139b <+99>:    mov    eax,edx

在汇编代码 ret 之前紧邻的代码中, 如果是往 eax 寄存器里写东西, 那说明函数返回值类型是 int。 eax 是用于 int 类型返回值的函数的寄存器。

mov edx, 0x1: 把数字1写入到 rdx 寄存器。前面提到过, rdx 是数据寄存器(Data register),用于整数运算和输入/输出操作。
mov eax, edx: 把 edx 寄存器内容写入到 eax 寄存器。

通过 edx 寄存器传递感觉是多余的,但由于不知道 CSAPP bomb 可执行文件的编译参数中,是否开启过优化选项, 因此不能确定能否直接干掉。

对应的 C 代码,更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;const char* p2 = s2;return 1;
}

偏移地址 10, 15, 18, 21, 26, 31, 34, 99 - 调用 string_length

   0x0000000000401342 <+10>:    call   0x40131b <string_length>0x0000000000401347 <+15>:    mov    r12d,eax0x000000000040134a <+18>:    mov    rdi,rbp0x000000000040134d <+21>:    call   0x40131b <string_length>0x0000000000401352 <+26>:    mov    edx,0x10x0000000000401357 <+31>:    cmp    r12d,eax0x000000000040135a <+34>:    jne    0x40139b <strings_not_equal+99>0x000000000040139b <+99>:    mov    eax,edx

call 0x40131b <string_length>: 调用 C 语言函数 string_length. 前文分析过它, 返回值类型是 int, 也就是说返回值放在 eax 寄存器中。
mov r12d,eax: 前面提到过, r12 是 callee-saved register, 用来存放数据。这里是说,把调用 string_length(s1) 的结果,存放到 r12d。 此时 C 代码大概这样:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;    // p1: rbxconst char* p2 = s2;    // p2: rbpint len1 = string_length(p1); // len1: r12dreturn 1;
}

mov rdi, rbp: 把 rbp 寄存器里的内容放到 rdi 寄存器, 也就是说接下来调用 string_length() 函数时,传入的第一个参数是 rbp 里的内容。 rbp 里现在是啥?

0x000000000040133f <+7>: mov rbp,rsi

没错,在偏移地址7处, 是把 strings_not_equal() 第二个参数放到了 rbp 里, 现在作为 string_length 的第一个参数。紧接着的汇编代码是 call 0x40131b <string_length>. 于是 C 代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);   // len2: eaxreturn 1;
}

mov edx,0x1: 往寄存器 edx 里写入1。
cmp r12d,eax: 比较 r12d 和 eax 两个寄存器的值。其中 eax 寄存器存放的是,最近一次函数调用(返回值是整数)的结果,也就是 string_length(s2)的结果; r12d 存放的是是调用 string_length(s1) 的结果, 是在偏移地址15 时存入的。 因此,现在这句汇编执行的是,s1 和 s2 两个字符串长度的比较。
jne 0x40139b <strings_not_equal+99>: 如果比较的 s1 和 s2 的长度不相等, 那么跳转到偏移地址为99的汇编代码继续执行。 偏移地址99处的代码有分析过, 是把 edx 寄存器的值放入 eax, 然后函数返回。而现在 edx 里存放的是1(偏移地址26时写入的)。因此C代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);   // len2: eaxif (len1 != len2){return 1;}
}

偏移地址 36, 39, 41, 80, 85, 99

   0x000000000040135c <+36>:    movzx  eax,BYTE PTR [rbx]0x000000000040135f <+39>:    test   al,al0x0000000000401361 <+41>:    je     0x401388 <strings_not_equal+80>0x0000000000401388 <+80>:    mov    edx,0x00x000000000040138d <+85>:    jmp    0x40139b <strings_not_equal+99>0x000000000040139b <+99>:    mov    eax,edx

movzx eax,BYTE PTR [rbx]: 把寄存器 rbx 里存放的值对应的内存地址处的值, 放到 eax 寄存器里, 并且注意 movzx 和 mov 有区别, movzx 是 move with zero extending 的意思,高位填充0。也就是说被放到 eax 的 [rbx], 是一个宽度小于 int 的内容. 对应的C代码:

char c1 = *(rbx);
// 也就是:
char c1 = *s1;

完整的 C 代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);   //if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eax
}

test al,al: test 指令执行的是 AND 操作。 test al, al 意思是 al 和自己做 AND 操作。 test 指令不存储结果, 只修改 ZF, SF, PF。显然,如果 al 本身是0,那么 ZF 将等于1.
je 0x401388 <strings_not_equal+80>: 如果 ZF 为1, 则跳转到偏移地址80的地方继续执行。 也就是说, 如果 al(eax的低8位,也就是刚刚C代码中的 c1)为0,那么就跳转到偏移地址80地方执行。 对应的 C 代码:

char c1 = *p1;
if (c1 == 0)
{goto offset80;
}

地址偏移80,85, 99处的汇编:
mov edx, 0x0: 往 edx 寄存器写入0。
jmp 0x40139b <strings_not_equal+99>: 无条件跳转到 offset99。
mov eax, edx: 把 edx 寄存器的值拷贝到 eax,也就是说 edx 里刚刚写入的0,是函数的返回值。

完整的C代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}
}

偏移地址 43, 46, 58, 62

是在偏移地址41的地方, ZF 不为0的情况下,继续执行的代码。也就是 char c1 = *s1 后, c1 != '\0' 情况下,继续执行的代码。

   0x0000000000401363 <+43>:    cmp    al,BYTE PTR [rbp+0x0]0x0000000000401366 <+46>:    je     0x401372 <strings_not_equal+58>0x0000000000401372 <+58>:    add    rbx,0x10x0000000000401376 <+62>:    add    rbp,0x1

cmp al, BYTE PTR [rbp+0x0]: 比较两个值,第一个值是 al 寄存器, 也就是 C 代码中我们自行定义的 c1 变量; 第二个是 BYTE PTR [rbp+0x0], 也就是 rbp 寄存器中存储的值对应的内存地址处存放的值。

cmp 指令,可以理解为两个操作数做减法,如果结果等于0,那么ZF就更新为1,否则ZF更新为0.

je 0x401372 <strings_not_equal+58>: 如果 ZF 为1(也就是cmp比较的两个操作数相等),那么跳转到偏移地址为58的地方。对应的C代码:

if (al == *rbp)
{goto label58
}

也就是:

if (c1 == *p2)
{goto label58
}

偏移地址为58的地方:
add rbx, 0x1: rbx 寄存器加1. 也就是 rbx 更新为 rbx + 1。 对应的C代码:

p1++;

偏移地址为62的地方:
add rbp, 0x1: 同上, rbp++:

p2++;

完整的C代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}if (c1 == *p2){p1++;p2++;}
}

偏移地址 66, 69, 71, 50, 53, 56, 94, 99

   0x000000000040137a <+66>:    movzx  eax,BYTE PTR [rbx]0x000000000040137d <+69>:    test   al,al0x000000000040137f <+71>:    jne    0x40136a <strings_not_equal+50>0x000000000040136a <+50>:    cmp    al,BYTE PTR [rbp+0x0]0x000000000040136d <+53>:    nop    DWORD PTR [rax]0x0000000000401370 <+56>:    jne    0x401396 <strings_not_equal+94>0x0000000000401396 <+94>:    mov    edx,0x10x000000000040139b <+99>:    mov    eax,edx

movzx eax, BYTE PTR [rbx]: 把 rbx 寄存器里存放的值对应的内存地址处, 存放到 eax 寄存器。感觉这里是把 eax 寄存器当作临时变量用了。实际上偏移地址36和66的汇编代码一样:

   0x000000000040135c <+36>:    movzx  eax,BYTE PTR [rbx]0x000000000040137a <+66>:    movzx  eax,BYTE PTR [rbx]

含义自然也是一样的:char c1 = *s1. 只不过此时的 s1 已经是原始输入的 s1 再加 1 了。

test al, al: 让 AL 和 AL 做 AND 操作, 如果结果为1, 则ZF为0. 如果 AND 结果为0, 则 ZF 为1.

jne 0x40136a <strings_not_equal+50>: 检查 ZF, 如果ZF为1(也就是AL为0, 对应到C代码就是 c1 为 0),则跳转到偏移地址为50的地方继续执行。

对应的 C 代码

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}if (c1 == *p2){p1++;p2++;}c1 = *p1;if (c1 != '\0'){if (c1 != *p2){return 1;}}
}

偏移地址 69, 71, 73, 78, 99

   0x000000000040137d <+69>:    test   al,al0x000000000040137f <+71>:    jne    0x40136a <strings_not_equal+50>0x0000000000401381 <+73>:    mov    edx,0x00x0000000000401386 <+78>:    jmp    0x40139b <strings_not_equal+99>0x000000000040139b <+99>:    mov    eax,edx
if (c1 == '\0')
{return 0;
}

完整代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}if (c1 == *p2){p1++;p2++;}c1 = *p1;if (c1 != '\0'){if (c1 != *p2){return 1;}}else{return 0;}
}

50, 53, 56, 58, 62

   0x000000000040136a <+50>:    cmp    al,BYTE PTR [rbp+0x0]0x000000000040136d <+53>:    nop    DWORD PTR [rax]0x0000000000401370 <+56>:    jne    0x401396 <strings_not_equal+94>0x0000000000401372 <+58>:    add    rbx,0x10x0000000000401376 <+62>:    add    rbp,0x1
int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}if (c1 == *p2){
goto label58;}
label58:p1++;p2++;c1 = *p1;if (c1 != '\0'){if (c1 != *p2){return 1;}else{goto label58;}}else{return 0;}
}

43, 46, 48, 87, 92, 99

   0x0000000000401363 <+43>:    cmp    al,BYTE PTR [rbp+0x0]0x0000000000401366 <+46>:    je     0x401372 <strings_not_equal+58>0x0000000000401368 <+48>:    jmp    0x40138f <strings_not_equal+87>0x000000000040138f <+87>:    mov    edx,0x10x0000000000401394 <+92>:    jmp    0x40139b <strings_not_equal+99>0x000000000040139b <+99>:    mov    eax,edx
if (c1 != *p2)
{return 1;
}

完整的C代码更新为:

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}if (c1 == *p2){
goto label58;}else{return 1;}
label58:p1++;p2++;c1 = *p1;if (c1 != '\0'){if (c1 != *p2){return 1;}else{goto label58;}}else{return 0;}
}

整理代码

int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{return 0;}if (c1 != *p2){return 1;}
label58:p1++;p2++;c1 = *p1;if (c1 == '\0'){return 0;}if (c1 != '\0'){if (c1 != *p2){return 1;}else{goto label58;}}
}
int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;            // p1: rbxconst char* p2 = s2;            // p2: rbpint len1 = string_length(p1);   // len1: r12dint len2 = string_length(p2);if (len1 != len2){return 1;}int ret = 0;for (; ;){char c1 = *p1;                  // c1: eaxif (c1 == '\0')                 // s1[0] 为 '\0', 而 s1, s2 长度相等, 说明 s2[0] 也是 '\0'{ret = 0;break;}if (c1 != *p2){ret = 1;break;}p1++;p2++;}return ret;
}

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

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

相关文章

怎么把PDF转成图片格式?分享一个PDF转图片的方法

在数字时代&#xff0c;PDF文件已经成为我们日常工作中不可或缺的一部分。然而&#xff0c;有时候我们需要将PDF文件中的内容转换为图片格式&#xff0c;这涉及到将PDF转图片的需求。PDF转图片的需求来源于不同的应用场景。在某些情况下&#xff0c;我们需要将PDF文件中的内容以…

openssl3.2 - 官方demo学习 - cipher - aesccm.c

文章目录 openssl3.2 - 官方demo学习 - cipher - aesccm.c概述笔记END openssl3.2 - 官方demo学习 - cipher - aesccm.c 概述 aesccm.c 是 AES-192-CCM 的加解密应用例子, 用的EVP接口. 看到不仅仅要用到key, iv, data, 在此之前还要设置 nonce, tag, 认证数据. 为啥需要设置…

TensorRT量化

系列文章目录 第一章 YOLOv5模型训练集标注、训练流程 第二章 YOLOv5模型转ONNX,ONNX转TensorRT Engine 第三章 TensorRT量化 文章目录 系列文章目录前言一、量化二、量化在TensorRT中的实现三、预处理&#xff08;Preprocess)和后处理(Postprocess)总结 前言 学习笔记–恩培…

linuxshell日常脚本命令(1)

Linux 清理make、configure生成的文件&#xff08;灵感来自于quilt安装&#xff09; make clean #make clean 可以清除make失败的内容Linux 清理make、configure生成的文件 make clean #清除上一次make命令生成的文件 make distclean #清除上一次make以及configure命令生成的…

【MATLAB】 多元变分模态分解MVMD信号分解算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~ 1 基本定义 多元变分模态分解&#xff08;MVMD&#xff09;是一种信号分解方法&#xff0c;可以自适应地实现信号的频域剖分及各分量的有效分离。 MVMD算法的具体步骤如下&#xff1a; 假设原始信号S被分解为K个分量μ…

代码随想录 Leetcode160. 相交链表

题目&#xff1a; 代码(首刷看解析 2024年1月13日&#xff09;&#xff1a; class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *A headA, *B headB;while (A ! B) {A A ! nullptr ? A->next : headB;B B ! nullpt…

【力扣·每日一题】2182.构造限制重复的字符串(模拟 贪心 优先队列 C++ Go)

题目链接 题意 给你一个字符串 s 和一个整数 repeatLimit &#xff0c;用 s 中的字符构造一个新字符串 repeatLimitedString &#xff0c;使任何字母 连续 出现的次数都不超过 repeatLimit 次。你不必使用 s 中的全部字符。 返回 字典序最大的 repeatLimitedString 。 如果…

[易语言]易语言部署yolox的onnx模型

【官方框架地址】 https://github.com/Megvii-BaseDetection/YOLOX 【算法介绍】 YOLOX是YOLO系列目标检测算法的进一步演变和优化。它由Megvii Technology的研究团队开发&#xff0c;是一个高性能、可扩展的对象检测器。YOLOX在保留快速处理速度的同时&#xff0c;通过引入一…

textarea文本框根据输入内容自动适应高度

第一种&#xff1a; <el-input auto-completeoff typetextarea :autosize"{minRows:3,maxRows:10}" class"no-scroll"> </el-input> /* 页面的样式表 */ .no-scroll textarea {overflow: hidden; /* 禁用滚动条 */resize: none; /* 禁止用户…

NetApp E系列(E-Series)OEM产品介绍以及如何收集日志和保存配置信息

NetApp E系列是NetApp收购LSI存储后建立的一条新的产品线&#xff0c;由于LSI存储的历史悠久&#xff0c;所以这条产品线给NetApp带来了很多的OEM产品&#xff0c;可以说E系列是世界上OEM给最多公司的存储产品线也不为过&#xff0c;因为最早LSI的产品销售测率就是OEM&#xff…

2024.1.9 Spark SQL day06 homework,数据清洗

目录 一. Spark SQL中数据清洗的API有哪些&#xff0c;各自作用是什么&#xff1f; 二. 设置Spark SQL的shuffle分区数的方式有哪几种 三. 数据写出到数据库需要注意什么? 四. Spark程序运行集群分类 一. Spark SQL中数据清洗的API有哪些&#xff0c;各自作用是什么&#x…

JQuery过滤选择器-如何让某个元素换颜色(俩种方式)

目录 一、过滤选择器&#xff1a;eq二、过滤选择器 : lt 前言 : 在做项目时经常会遇到列表或者选择某个元素 一、过滤选择器&#xff1a;eq :eq (index)匹配一个给定索引值的元素 $("ul li:eq(0)").css("color","red");二、过滤选择器 : lt …

2024-01-11 部署Stable Diffusion遇挫记

点击 <C 语言编程核心突破> 快速C语言入门 部署Stable Diffusion遇挫记 前言一、一如既往的GitHub部署二、使用的感受总结 create by Stable Diffusion; prompt: fire water llama 前言 要解决问题: 由于近期的努力, 已经实现语音转文字模型, 通用chat迷你大模型的本地…

怎么把workspace的数据导入到simulink进行FFT分析?

怎么把数据导入到simulink在这篇博客已经阐述了&#xff0c;那么如何把数据导入到simulink还能进行FFT分析呢&#xff1f; 首先我们看simulink的FFT分析界面&#xff0c;&#xff08;前置步骤&#xff1a;导入powergui模块&#xff0c;双击powergui模块&#xff0c;Tool选项卡…

使用curl发送时间参数

# 获取当前日期 current_date$(date %Y-%m-%d)# 获取前一天的0点和23:59:59的时间&#xff0c;并格式化为yyyy-MM-dd 24hh:mm:ss begin_time$(date -d "yesterday 00:00:00" %Y-%m-%d\ %H:%M:%S) end_time$(date -d "yesterday 23:59:59" %Y-%m-%d\ %H:%M:…

发动机装备3d虚拟在线云展馆360度展示每处细节

在当今数字化的时代&#xff0c;消费者对于线上购物的需求与期待日益增长。尤其在购车这一大宗消费行为上&#xff0c;消费者不再满足于传统的图片与文字介绍。为了满足这一市场需求&#xff0c;我们引入了3D线上展示技术。 3D汽车模型实景互动展示是一种通过先进的三维建模技术…

【密码学】python密码学库pycryptodome

记录了一本几乎是10年前的书&#xff08;python绝技–用python成为顶级黑客&#xff09;中过时的内容 p20 UNIX口令破解机 里面提到了python标准库中自带的crypt库&#xff0c;经验证Python 3.12.1中并没有这个自带的库&#xff0c;密码学相关的库目前&#xff08;2024.1.12&a…

生成函数——裴蜀定理

有三种数量无限的砝码和一个天平&#xff0c;天平的一端有一个质量为 m 的物品&#xff0c;问能否通过放置砝码使得天平平衡&#xff1f; 输入 第一行包含一个整数 T (1 ≤ T ≤ 1e5)&#xff0c;表示测试用例的组数。 每组测试用例的第一行包含四个整数 a,b,c,m (1 ≤ a,b,c,…

R语言【paleobioDB】——pbdb_occurrences():从PBDB获取多个化石记录号的基本信息

Package paleobioDB version 0.7.0 paleobioDB 包在2020年已经停止更新&#xff0c;该包依赖PBDB v1 API。 可以选择在Index of /src/contrib/Archive/paleobioDB (r-project.org)下载安装包后&#xff0c;执行本地安装。 Usage pbdb_occurrences(...) Arguments 参数【...】…

一杯干红葡萄酒的酿造

一杯干红葡萄酒的酿造 一、什么是干红葡萄酒&#xff1f; 干红葡萄酒是指葡萄酒在酿造后&#xff0c;酿酒原料(葡萄汁)中的糖分完全转化成酒精&#xff0c;残糖量小于或等于4.00/L的红葡萄酒。 干红葡萄酒按颜色分可以分为 1&#xff0c;白葡萄酒:选择用白葡萄或浅色果皮的酿…