当前位置: 首页 > news >正文

Windows逆向安全(一)C与汇编的关系

前言

逆向是一种新型的思维模式也是软件开发领域中极为重要的技术,涵盖各种维度去深挖软件架构的本质和操作系统原理,学习逆向后可以在各领域中发挥至关重要的作用,其中包括黑灰色,安全开发,客户端安全,物联网,车联网,游戏安全,红队免杀等行业中绘制出更高的闪光点。

C与汇编的关系

基本语法的学习:

  • 各种进制的转换和原理
  1. 十进制的定义:由十个符号组成,分别是0 1 2 3 4 5 6 7 8 9 逢十进一
  2. 九进制的定义:由九个符号组成,分别是0 1 2 3 4 5 6 7 8 逢九进一
  3. 十六进制的定义:由十六个符号组成,分别是0 1 2 3 4 5 6 7 8 9 A B C D E F
  4. N进制的定义:由N个符号组成 逢N进一

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 数据类型与逻辑运算

在这里插入图片描述

在计算机中,由于硬件的制约,数据是有长度限制的,超过数据宽度的数据会被丢弃

同一个数据,表示无符号数和有符号数则其含义不同

  1. 无符号数:正数
  2. 有符号数:正数、负数

例:

  • 当数据宽度为4时,即数据只能存储4位2进制位0000~1111

无符号数:

  1. 数据:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  2. 十六进制:0 1 2 3 4 5 6 7 8 9 A B C D E F
  3. 二进制:0000000100100011010001010110011110001001101010111100110111101111

有符号数:

正数:

  1. 数据:0 1 2 3 4 5 6 7
  2. 十六进制:0 1 2 3 4 5 6 7
  3. 二进制:00001001000110100010101100111

负数:

  1. 数据:-1 -2 -3 -4 -5 -6 -7 -8
    十六进制:F E D C B A 9 8
    二进制: 11111110110111001011101010011000
  2. 可以发现当数据为1011,把数据看作无符号数时,数据表示为B
  3. 把数据看作有符号数时,数据表示为-5
  4. 无符号数的表示范围为0~2^4-1即0~15\\ 有符号数的表示范围为-23~23-1即-8~7

常见的数据类型(重要)

  • BYTE 字节 8BIT 1字节
  • WORD 字 16BIT 2字节
  • DWORD 双字 32BIT 4字节

常见的运算符类型(重要):

或运算(or |):
两个数只要有一个为1则结果为1

与运算(and &):
两个数都是1结果才为1

异或运算(xor ^):
两个数相同为0, 不同为1

非运算(not !):
两个数取反 1是0, 0是1

CPU如何计算2+3?

X:0010

Y:0011

先异或

在这里插入图片描述
R:0001

异或完以后要判断是否运算结束

将两个数进行与运算 然后左移一位

在这里插入图片描述
0010<<1 ==0100

如果结果全为0,结果则为我们所要的结果

否则,把上面异或得到的值赋值到X

把左移后的结果赋值到Y

X:0001

Y:0100

重复操作

先异或
在这里插入图片描述
R:0101

再将两个数进行与运算 然后左移一位

在这里插入图片描述
左移完结果全是0,结果则为我们所要的

最终结果为0101=5

CPU如何计算2-3?

X:0010

Y:1101

先异或

在这里插入图片描述
R:1111

将两个数进行与运算 然后左移一位

在这里插入图片描述

0000<<1=0000

如果结果全为0,结果则为我们所要的结果

最终结果为1111 = -1

如何取某个值的第N位的数值

与操作

如我们想要查看23h这个十六进制数的第3位则可以进行如下运算:

先将23h转化为二进制:0010 0011

在这里插入图片描述

最简单的加密算法:

通过异或加密数据 再次异或后则解密数据

要加密的数据:2021:0010 0000 0010 0001

密钥:54:0101 0100

在这里插入图片描述
高位:0111 0100 = 74
在这里插入图片描述
低位:0111 0101 = 75

原本的2021加密成了7475

然后再次进行异或操作进行解密:

在这里插入图片描述

高位:0010 0000 = 20

在这里插入图片描述

低位:0010 0100 = 21

解密回了原来的数值2021

  • 通用寄存器和内存读写

32位通用寄存器的指定用途如下:

在这里插入图片描述在这里插入图片描述在这里插入图片描述堆栈相关汇编指令:

在这里插入图片描述MOV指令

MOV 的语法:

MOV  r/m8,r8
MOV  r/m16,r16
MOV  r/m32,r32
MOV  r8,r/m8
MOV  r16,r/m16
MOV  r32,r/m32
MOV r8,  imm8
MOV r16,  imm16
MOV r32,  imm32

MOV 目标操作数,源操作数

作用:拷贝源操作数到目标操作数

源操作数可以是立即数、通用寄存器、段寄存器、或者内存单元
目标操作数可以是通用寄存器、段寄存器或者内存单元
操作数的宽度必须一样
源操作数和目标操作数不能同时为内存单元

ADD指令

ADD 的语法:

ADD r/m8,  imm8
ADD  r/m16,imm16
ADD  r/m32,imm32
ADD r/m16,  imm8
ADD r/m32,  imm8
ADD r/m8,  r8
ADD r/m16,  r16
ADD r/m32,  r32
ADD r8,  r/m8
ADD r16,  r/m16
ADD r32,  r/m32

ADD 目标操作数,源操作数

作用:将源操作数加到目标操作数上

SUB指令

SUB 的语法:

SUB r/m8, imm8
SUB r/m16,imm16
SUB r/m32,imm32
SUB r/m16, imm8
SUB r/m32, imm8
SUB r/m8, r8
SUB r/m16, r16
SUB r/m32, r32
SUB r8, r/m8
SUB r16, r/m16
SUB r32, r/m32

SUB 目标操作数,源操作数

作用:将源操作数减到目标操作数上

AND指令

AND 的语法:

AND r/m8, imm8
AND r/m16,imm16
AND r/m32,imm32
AND r/m16, imm8
AND r/m32, imm8
AND r/m8, r8
AND r/m16, r16
AND r/m32, r32
AND r8, r/m8
AND r16, r/m16
AND r32, r/m32

AND 目标操作数,源操作数

作用:将源操作数与目标操作数与运算后将结果保存到目标操作数中

OR指令

OR 的语法:

OR r/m8, imm8
OR r/m16,imm16
OR r/m32,imm32
OR r/m16, imm8
OR r/m8, r8
OR r/m16, r16
OR r/m32, r32
OR r8, r/m8
OR r16, r/m16
OR r32, r/m32

OR 目标操作数,源操作数

作用:将源操作数与目标操作数或运算后将结果保存到目标操作数中

XOR指令

XOR 的语法:

XOR r/m8, imm8
XOR r/m16,imm16
XOR r/m32,imm32
XOR r/m16, imm8
XOR r/m8, r8
XOR r/m32, r32
XOR r8, r/m8
XOR r16, r/m16
XOR r32, r/m32

XOR 目标操作数,源操作数

作用:将源操作数与目标操作数异或运算后将结果保存到目标操作数中

NOT指令

NOT 的语法:

NOT r/m8NOT r/m16NOT r/m32NOT 操作数

作用:取反

LEA指令

在这里插入图片描述lea:Load Effective Address,即装入有效地址的意思,它的操作数就是地址

lea r32,dword ptr ds:[内存编号(地址)]

将内存地址赋值给32位通用寄存器

lea是传址,mov是传值,注意区别

  • 堆栈结构

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Windows分配栈时 是从高地址往低地址分配:

  1. MOV EBX,0x13FFDC BASE
  2. MOV EDX,0x13FFDC TOP

栈底和栈顶可以是两个任意的寄存器(Windows采用的是EBP和ESP)

刚开始堆栈为空,栈顶和栈底相同

在这里插入图片描述先将数据压入后再修改栈顶
数据压入

MOV DWORD PTR DS:[EDX-4],0xAAAAAAAA
修改栈顶

SUB EDX,4
先修改栈顶后再将数据压入
修改栈顶

LEA EDX,DWORD PTR DS:[EDX-4] (和上面的SUB一样)
数据压入

MOV DOWRD PTR DS:[EDX],0xAAAAAAAA

在这里插入图片描述
栈顶加偏移读取

MOV ESI,DWORD PTR DS:[EBX-8]
栈底加偏移读取

MOV EDI,DWORD PTR DS:[EDX+4]
在这里插入图片描述
先取出数据再修改栈顶
取出数据

MOV EAX,DOWRD PTR DS:[EDX]
修改栈顶

ADD EDX,4
先修改栈顶再取出数据
修改栈顶

LEA EDX,DWORD PTR DS:[EDX+4]
取出数据

MOV EAX,DOWRD PTR DS:[EDX-4]

在这里插入图片描述在这里插入图片描述

入栈和出栈操作也有对应的指令:

上面我们自己模拟的两个用作栈顶和栈底的寄存器在WINDOWS中分别对应ESP和EBP

并且前面我们自己模拟的入栈和出栈操作也有对应的指令:PUSH 和 POP

就是封装了压入数据和修改栈顶的操作

  • PUSH 和 POP
  • push xxx将 xxx的数据压入堆栈
  • pop xxx将栈顶的数据存储到xxx中

PUSH指令:

PUSH r32

PUSH r16

PUSH m16

PUSH m32

PUSH imm8/imm16/imm32

所有的push都是将esp-4?

压入的数据的数据宽度:

当push的是立即数将esp-4

当push r32如push eax时将esp-4

当push dword ptr ds:[12FFDA]即压入双字内存地址中的数据时将esp-4

当push word ptr ds:[12FFDA]即压入字内存地址中的数据时将esp-2

当push ax,即r16 ,16位通用寄存器时,esp-2

push 不允许压入数据宽度为8的数据 如ah al 和byte ptr ds:[内存编号]

POP指令

POP r32

POP r16

POP m16

POP m32

PUSHAD和POPAD指令

将所有的32位通用寄存器压入堆栈,方便后面随意使用寄存器,用于保护现场

与POPAD对应

PUSHFD和POPFD指令

然后将32位标志寄存器EFLAGS压入堆栈

与POPAD对应

其它相关指令

pusha:将所有的16位通用寄存器压入堆栈

popa:将所有的16位通用寄存器取出堆栈

pushf::将的16位标志寄存器EFLAGS压入堆栈

popf:将16位标志寄存器EFLAGS取出堆栈

栈底和栈顶原理:

  1. 控制栈顶和栈底分别为两个固定的寄存器(EBP 基址指针寄存器 和 ESP 堆栈指针寄存器)
  2. 刚开始堆栈为空,栈顶和栈底相同

标志寄存器:

  1. 进位标志CF(Carry Flag)
  2. 奇偶标志PF(Parity Flag)
  3. 辅助进位标志AF(Auxiliary Carry Flag)
  4. 零标志ZF(Zero Flag)
  5. 符号标志SF(Sign Flag)
  6. 溢出标志OF(Overflow Flag)
  7. 方向标志DF(Direction Flag)

EFLAGS寄存器

在这里插入图片描述

进位标志CF(Carry Flag)

如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0

例子:

MOV AL,0xFFADD AL,1
  • 0x80+0x40

加黑的为最高位

0x80:0 1000 0000

0x40:0 0100 0000

在这里插入图片描述
结果为1100 0000 最高位并没有发生变化,于是CF位为0

  • 0x80-0x40
    在这里插入图片描述
    注意这里借位的位是1000 0000中的加黑部分

而非0 1000 0000这里的最高位

结果为0100 0000 最高位并没有发生变化,于是CF位为0

  • 0x80-0x81

0x80:1000 0000

0x81:1000 0001

在这里插入图片描述
结果为1111 1111= -1,最高位被借位,于是CF位为1

奇偶标志PF(Parity Flag)

奇偶标志PF用于反映运算结果中最低有效字节中“1”的个数的奇偶性

如果“1”的个数为偶数,则PF的值为1,否则其值为0。

指令指令执行后AL的结果PFMOV AL,300111ADD AL,301101ADD AL,210000

例:

MOV AX,803
ADD AX,1

0x803: 0000 1000 0000 0011

执行结果

0x804: 0000 1000 0000 0100 总共2个1 ,PF应为1,但实际运行结果PF为0

因为PF是根据最低有效字节来看,即804后面04的这部分

04: 0000 0100 总共1个1,所以PF为0

辅助进位标志AF(Auxiliary Carry Flag)

在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:

  • 在字操作时,发生低字节向高字节进位或借位时
  • 在字节操作时,发生低4位向高4位进位或借位时

AF与数据宽度相关

32位时 FFFF F FFF

16位时 FF F F

8位时 F F

加黑的字体为AF标志位判断的位置,如果该位置要向前进位则AF为1,否则为0,和CF相似,不过判断的位置不同

32位例:

MOV EAX,55EEFFFFADD EAX,2

16位例:

MOV AX,5EFEADD AX,2

8位例:

MOV AL,4EADD AL,2

零标志ZF(Zero Flag)

零标志ZF用来反映运算结果是否为0

如果运算结果为0,则其值为1,否则其值为0

作用:在判断运算结果是否为0时,可使用此标志位

例子:

XOR EAX,EAX

通过xor将eax清零,会改变zf标志位为1

MOV EAX,0

通过MOV将EAX赋值为0,非运算,不改变zf标志位

符号标志SF(Sign Flag)

符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同

例子:

MOV AL,7F
ADD AL,2

溢出标志OF(Overflow Flag)

溢出标志OF用于反映有符号数加减运算所得结果是否溢出

注意与CF区分!!!

最高位进位与溢出的区别:

进位标志表示无符号数运算结果是否超出范围.

溢出标志表示有符号数运算结果是否超出范围.

溢出主要是给有符号运算使用的,在有符号的运算中,有如下的规律:

  • 正 + 正 = 正 如果结果是负数,则说明有溢出
  • 负 + 负 = 负 如果结果是正数,则说明有溢出
  • 正 + 负 永远都不会有溢出

无符号、有符号都不溢出例

MOV AL,8
ADD AL,8

AL的数据宽度为8,即

无符号数范围为0~FF即0~255

8+8=16在0~255内 不溢出

有符号数的范围为

正数:0~7F 即0~127

负数:80~FF 即 -128~0

8+8=16 在0~127内 两正数相加结果仍为正数,不溢出

无符号溢出、有符号不溢出例

MOV AL,0FF
ADD AL,2

无符号数时

FF+2=255+2=257 在0~255外,溢出

有符号数时

FF+2=-1+2=1

正 + 负 永远都不会有溢出

无符号不溢出、有符号溢出例

MOV AL,7F
ADD AL,2

无符号数时

7F+2=127+2=129 在0~255内 不溢出

有符号数时

7F+2=0x81在80~FF (负数范围)内,两正数相加结果为负数,溢出

无符号、有符号都溢出

MOV AL,0FE
ADD AL,80

无符号数时

FE+2=254+2=256=0x100 在0~255外 溢出

有符号数时

FE+2=0x100在0~FF外,溢出

CPU如何计算OF位

首先引入两个概念:

  • 符号位有进位
  • 最高有效数值位向符号位产生的进位

对于一个有符号数:如0x80和0xC0

符号位有进位

0x80:1 000 0000

0xC0:1 100 0000

最高有效数值位向符号位产生的进位

0x80:1 0 00 0000

0xC0:1 1 00 0000

接下来看一组汇编指令

MOV AL,80
ADD AL,0C0

就是运算0x80+0xc0

0x80:1 0 00 0000

0xC0:1 1 00 0000

符号位1+1有产生进位,于是符号位有进位为1

最高有效数值位向符号位产生的进位0+1没有产生进位,于是最高有效数值位向符号位产生的进位为0

OF = 符号位有进位 xor 最高有效数值位向符号位产生的进位

OF = 1 xor 0 = 1 所以此时OF=1

方向标志DF(Direction Flag)

DF:方向标志位

DF=1时串操作为减地址方式 DF=0为增地址方式

下面的MOVS指令有说明DF的具体应用

相关汇编指令

符号含义r寄存器m内存imm立即数r88位通用寄存器m88位内存imm88位立即数

ADC指令:带进位加法

格式:ADC R/M,R/M/IMM 两边不能同时为内存 数据宽度要一样

例:

MOV AL,1
MOV CL,2
手动修改CF为1
ADC AL,CL

计算结果为4,原本1+2=3,但是现在变成了4,注意与ADD的区别就在于进位

SBB指令:带借位减法

格式:SBB R/M,R/M/IMM 两边不能同时为内存 数据宽度要一样

MOV AL,4
MOV CL,2
手动修改CF为1
SBB AL,CL

计算结果为1,原本4-2=2,但是现在变成了1,注意与SUB的区别就在于进位

XCHG指令:交换数据

格式:XCHG R/M,R/M 两边不能同时为内存 数据宽度要一样

XCHG AL,CL
XCHG DWORD PTR DS:[12FFC4],EAX
XCHG BYTE PTR DS:[12FFC4],AL

例:

MOV AL,1
MOV CL,2
XCHG AL,CL

执行前:AL=1 CL=2

执行后:AL=2 CL=1

MOVS指令:移动数据 内存-内存

BYTE/WORD/DWORD

MOVS指令常用于复制字符串

MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]        简写为:MOVSB
MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]        简写为:MOVSW
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]        简写为:MOVSD

例:

MOV EDI,12FFD8
MOV ESI,12FFD0
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]

执行后,EDI内存里的值被修改为ESI内存里的值,且EDI和ESI各加4

为什么各加4?

和DOWRD数据宽度相关,如果为WORD 则各加2

为什么执行完是加而不是减?

由DF(Direction Flag)方向标志位决定,当DF位为1时为减,当DF位为0时,则为加

STOS指令
将Al/AX/EAX的值存储到[EDI]指定的内存单元,和数据宽度相关

STOS BYTE PTR ES:[EDI]                将AL存储到[EDI]
STOS WORD PTR ES:[EDI]                将AX存储到[EDI]
STOS DWORD PTR ES:[EDI]                将EAX存储到[EDI]

注意这里使用的是ES: 之前写的都是DS:

当后面为[EDI]时要使用ES: 这和后面要学的段寄存器有关,先记住

存储完数据后EDI地址的变化方向也受DF标志控制,1减0增

REP指令

按计数寄存器 (ECX) 中指定的次数重复执行指令

MOV ECX,10
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]        也可以写成REP MOVSD

这里的10为十六进制,也就是0x10=16

代码将会重复执行16次,会不会往同一个地方覆盖?

不会,因为每执行一次EDI和ESI都会变化4,变化方向由DF决定

汇编跳转和比较指令:

  1. JCC指令
  2. JMP指令
  3. CALL指令
  4. RET指令
  5. CMP指令
  6. TEST指令
  7. JCC指令表

JCC指令

cc 代表 condition code(状态码)

Jcc不是单个指令,它只是描述了跳转之前检查条件代码的跳转助记符

例如JNE,在跳转之前检查条件代码

典型的情况是进行比较(设置CC),然后使用跳转助记符之一

CMP EAX,0JNE XXXXX

条件代码也可以用AND、OR、XOR、加法、减法(当然也可以是CMP)等指令来设置

JCC指令用于改变EIP(CPU要读取的指令地址)

JMP指令

JMP指令:修改EIP的值

JMP指令只影响了EIP,不影响堆栈和其它通用寄存器

JMP 寄存器/立即数 相当于 MOV EIP,寄存器/立即数

CALL指令

CALL指令和JMP指令都会修改EIP的值

但CALL指令会将返回地址(CALL指令的下一条指令地址)压入堆栈

因此也会引起esp的变化

RET指令

call调用跳转后执行完相关代码完要返回到call的下一条指令时使用ret指令

ret指令相当于pop eip

比较指令

CMP指令

指令格式:CMP R/M,R/M/IMM

CMP指令只改变标志寄存器的值

该指令是比较两个操作数,实际上,它相当于SUB指令,但是相减的结果并不保存到第一个操作数中

只是根据相减的结果来改变ZF零标志位的,当两个操作数相等的时候,零标志位置1

例:

MOV EAX,100MOV EBX,200CMP EAX,ECXCMP AX,WORD PTR DS:[405000]CMP AL,BYTE PTR DS:[405000]CMP EAX,DWORD PTR DS:[405000]

TEST指令

指令格式:TEST R/M,R/M/IMM

该指令在一定程度上和CMP指令时类似的,两个数值进行与操作,结果不保存,但是会改变相应标志位

与的操作表项如下:

运算

结果

1 and 1

1

1 and 0

0

0 and 1

0

0 and 0

0

可以看到只要有任一操作数为0时,结果就为0

常见用法:用这个指令,可以确定某寄存器是否等于0

只有当eax=0时 eax and eax才会是0

所以

TEST EAX,EAX

观察ZF(零标志位)就可以判断EAX是否为0

堆栈结构图(重点):

  1. 调用CALL又可以分为六个部分:
  2. 提升堆栈
  3. 保护现场
  4. 初始化提升的堆栈
  5. 执行实际内容
  6. 恢复现场
  7. 返回

在这里插入图片描述

提升堆栈
对应语句为

00401040  /> \55            push ebp00401041  |.  8BEC          mov ebp,esp00401043  |.  83EC 40       sub esp,0x40

将堆栈提升了0x40

保护现场
对应语句为

00401046  |.  53            push ebx00401047  |.  56            push esi00401048  |.  57            push edi

将ebx、esi、edi三个通用寄存器保存到堆栈中,前面的push ebp其实也属于保护现场

初始化提升的堆栈

00401049  |.  8D7D C0       lea edi,dword ptr ss:[ebp-0x40]0040104C  |.  B9 10000000   mov ecx,0x1000401051  |.  B8 CCCCCCCC   mov eax,0xCCCCCCCC00401056  |.  F3:AB         rep stos dword ptr es:[edi]

这里将我们提升的堆栈中的内容全部初始化为CCCCCCCC

为什么是初始化为CC?防止缓冲溢出

CC的硬编码对应的指令为int 3,即断点

这么做有什么好处呢?当程序执行超过缓冲区时,遇到int 3就会自动停下来

执行实际的内容
对应语句为

00401058  |.  8B45 08       mov eax,dword ptr ss:[ebp+0x8]0040105B  |.  0345 0C       add eax,dword ptr ss:[ebp+0xC]

就是将前面压入的参数2和1进行相加得到3

恢复现场
对应语句为

0040105E  |.  5F            pop edi                                  ;  HelloWor.004011710040105F  |.  5E            pop esi                                  ;  HelloWor.0040117100401060  |.  5B            pop ebx                                  ;  HelloWor.0040117100401061  |.  8BE5          mov esp,ebp00401063  |.  5D            pop ebp                                  ;  HelloWor.00401171

与前面保护现场相对应

返回
对应语句为

00401064  \.  C3            retn

CALL返回后
对应语句为

00401171  |.  83C4 08       add esp,0x8

作用为平衡堆栈

逆推C语言代码

根据我们前面的分析,我们不难发现这其实就是个简单的加法函数

int add(int x,int y){x=x+y;        //这里的x和y分别对应压入的参数return x;        //对应RETN 默认采用eax作为返回值的传递载体}
  • 反汇编分析C语言:

函数内部

接着进到函数的内部

有了之前画堆栈图的经验,我们不难看出,尽管我们的函数是个空函数,但其汇编代码依然完成了以下流程:

  1. 提升堆栈
  2. 保护现场
  3. 初始化提升的堆栈
  4. 恢复现场
  5. 返回

提升堆栈

00401010   push        ebp
00401011   mov         ebp,esp
00401013   sub         esp,40h

保护现场

00401016   push        ebx
00401017   push        esi
00401018   push        edi

PS:前面的push ebp也是保护现场
初始化提升的堆栈

00401019   lea         edi,[ebp-40h]
0040101C   mov         ecx,10h
00401021   mov         eax,0CCCCCCCCh
00401026   rep stos    dword ptr [edi]

恢复现场

00401028   pop         edi
00401029   pop         esi
0040102A   pop         ebx
0040102B   mov         esp,ebp
0040102D   pop         ebp
//空函数        
void function(){}int main(int argc, char* argv[])
{//调用空函数function();return 0;
}
  • C语言内联汇编和调用协定
int __declspec (naked) Plus(int x,int y){__asm{//保留调用前堆栈push ebp//提升堆栈mov ebp,espsub esp,0x40//保护现场push ebxpush esipush edi//初始化提升的堆栈,填充缓冲区mov eax,0xCCCCCCCCmov ecx,0x10lea edi,dword ptr ds:[ebp-0x40]rep stosd//函数核心功能//取出参数mov eax,dword ptr ds:[ebp+8]//参数相加add eax,dword ptr ds:[ebp+0xC]//恢复现场pop edipop esipop ebx//降低堆栈mov esp,ebppop ebp                //返回ret }        
}void __declspec (naked) function(){__asm{ret}
}
  • 汇编寻找C程序入口
  1. mainCRTStartup 和 wmainCRTStartup 是控制台环境下多字节编码和Unicode 编码的启动函数
  2. 而WinMainCRTStartup 和wWinMainCRTStartup 是windows 环境下多字节编码和Unicode 编码的启动函数

mainCRTStartup做了哪些事:

在这里插入图片描述

  1. 前面我们已经知道了mainCRTStartup也就是程序入口,那么如何通过mainCRTStartup来找到main函数入口
  2. 根据函数的参数来进行判断
  3. main函数貌似只有两个参数,但实际上main函数一共有三个参数,只不过一般第三个参数我们并没有用到,于是在使用main函数时并没有加上,完整的main函数原型如下:
int main(int argc,char *argv[],char *envp[]){}

这里的argv和envp对应mainCRTStartup里_setargv()和_setenvp()

main函数的三个参数:

在这里插入图片描述

  • 汇编C语言基本类型
#include "stdafx.h"
int main(int argc, char* argv[])
{char a=0xFF;short b=0xFF;int c=0xFF;long d=0xFF;return 0;
}
  • 汇编 全局和局部 变量

在这里插入图片描述
全局变量

  1. MOV 寄存器,byte/word/dword ptr ds:[0x12345678]
  2. 上面的0x12345678是固定的地址,每次程序启动都不变,通过寄存器的宽度,或者byte/word/dword 来判断全局变量的宽度
  3. 全局变量就是所谓的基址

局部变量

  1. 局部变量在程序编译完成后并没有分配固定的地址
  2. 在所属的方法没有被调用时,局部变量并不会分配内存地址,只有当所属的程序被调用了,才会在堆栈中分配内存
  3. 当局部变量所属的方法执行完毕后,局部变量所占用的内存将变成垃圾数据.局部变量消失
  4. 局部变量只能在函数内部使用,函数A无法使用函数B的局部变量
  5. 局部变量的反汇编识别

[ebp-4]

[ebp-8]

[ebp-0xC]

例子:

#include "stdafx.h"
//全局变量
int global=0x610;
int main(int argc, char* argv[])
{//局部变量int temp=0x160;global=global+temp;return 0;
}

汇编C语言类型转换

类型转换相关汇编指令

  1. MOVSX

  2. 先符号扩展,再传送

  3. MOV AL,0FF
    MOVSX CX,AL
    MOV AL,80
    MOVSX CX,AL

  4. MOVZX

  5. 先零扩展,再传送

  6. MOV AL,0FF
    MOVZX CX,AL
    MOV AL,80
    MOVSX CX,AL

#include "stdafx.h"
int main(int argc, char* argv[])
{unsigned char i=0xFF;printf("%d\n",i);int j=i+1;i=i+1;printf("%d\n",i);printf("%d\n",j);return 0;
}
  • 汇编嵌套if else
#include "stdafx.h"int result=0;
int getMax(int i,int j,int k){if(i>j){if(i>k){return i;}else{return k;}}else{if(j>k){return j;}else{return k;}}
}int main(int argc, char* argv[])
{result=getMax(1,2,3);printf("%d\n",result);result=getMax(1,3,2);printf("%d\n",result);result=getMax(2,1,3);printf("%d\n",result);result=getMax(2,3,1);printf("%d\n",result);result=getMax(3,1,2);printf("%d\n",result);result=getMax(3,2,1);printf("%d\n",result);return 0;
}
#include "stdafx.h"
int getMax2(int i,int j){if(i>j){return i;}else{return j;}
}
int main(int argc, char* argv[])
{getMax2(1,2);return 0;
}

汇编比较三种循环
1.

#include "stdafx.h"
int loop1(){int i=0,j=0;for(i=0;i<10;i++){j=j+i;}return j;
}int loop2(){int i=0,j=0;while(i<10){j=j+i;i=i+1;}return j;
}
int loop3(){int i=0,j=0;do {j=j+i;i=i+1;} while(i<10);return j;
}int main(int argc, char* argv[])
{int result=0;result=loop1();printf("%d\n",result);result=loop2();printf("%d\n",result);result=loop3();printf("%d\n",result);return 0;
}

汇编一维数组

  1. 数组的寻址
    数组的存储并不复杂,接下来看看如何来找到数组的某个成员
#include "stdafx.h"
void  function(){int x=1;int y=2;int r=0;int arr[5]={1,2,3,4,5};r=arr[1];r=arr[x];r=arr[x+y];r=arr[x*2+y];
}
int main(int argc, char* argv[])
{function();return 0;
}
#include "stdafx.h"
void  function(){int arr[5]={1,2,3,4,5};
}
int main(int argc, char* argv[])
{function();return 0;
}

数组越界的应用:

#include "stdafx.h"
void  function(){int arr[5]={1,2,3,4,5};arr[6]=0x12345678;}
int main(int argc, char* argv[])
{function();return 0;
}
  • 汇编二维数组位移 乘法

在这里插入图片描述位移

前面在寻址的过程中分别用到了乘法,当乘数为2的n次方时,可以直接使用左移来实现,无需imul指令

汇编中有常用的两种位移指令:shl和shr

使用方法并没有太大的区别,这里就拿shl指令作为例子

shl指令:

SHL是一个汇编指令,作用是逻辑左移指令,将目的操作数顺序左移1位或CL寄存器中指定的位数。左移一位时,操作数的最高位移入进位标志位CF,最低位补零。

运算例子:

在这里插入图片描述

乘法
imul指令:

imul指令使用起来和div指令有些类似

IMUL(有符号数乘法)指令执行有符号整数乘法

x86 指令集支持三种格式的 IMUL 指令:单操作数、双操作数和三操作数。单操作数格式中,乘数和被乘数大小相同,而乘积的大小是它们的两倍

这里限于篇幅,仅介绍上面使用到的三操作数,其余部分可以参考:汇编语言IMUL指令:汇编语言IMUL指令:有符号数乘法

例子

        int i=1;i=i*5;i=i*6;i=i*7;
 		int i=1;i=i*4;i=i*8;i=i*16;
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int a=arr[2][3];
int i=1,j=2;
int b=arr[i][j];
int c=arr[i+j][i*2];
int arr[3][4]={{1,2,0,0},{5,6,7,0},{9,0,0,0}};
int arr[3][4]={{1,2},{5,6,7},{9}};
  • 汇编 结构体和内存对齐
#include "stdafx.h"
#include <string.h>
struct Player{float hp;                //人物血量float mp;                //人物魔力值int money;                //人物金钱int atk;                //人物攻击力char name[10];        //人物昵称float x;                //人物x坐标float y;                //人物y坐标
};
int main(int argc, char* argv[])
{Player player;player.hp=100;player.mp=50;player.money=1000;player.atk=10;        strcpy(player.name,"lyl610abc");player.x=600;player.y=100;return 0;
}
#include "stdafx.h"
#include <string.h>
struct Player{float hp;                //人物血量float mp;                //人物魔量int money;                //人物金钱int atk;                //人物攻击力char name[10];        //人物昵称float x;                //人物x坐标float y;                //人物y坐标
};Player retStruct(){Player player;return player;
}int main(int argc, char* argv[])
{Player player;player=retStruct();return 0;
}

内存对齐

1.内存对齐也称作字节对齐
2.前面或多或少都有提到过内存对齐,但没有具体展开,现在来谈谈内存对齐
3.为什么要内存对齐

性能原因:

寻址时提高效率,采用了以空间换时间的思想
当寻址的内存的单位和本机宽度一致时,寻址的效率最高

举个例子:

在32位的机器上,一次读32位(4字节)的内存 效率最高
在64位的机器上,一次读64位(8字节)的内存 效率最高
  • 汇编switch比较if else

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "stdafx.h"void MySwitch(int x){        switch(x) {case 1:printf("num is 1\n");break;case 2:printf("num is 2\n");break;case 3:printf("num is 3\n");break;default:printf("no cases match\n");break;}
}int main(int argc, char* argv[])
{MySwitch(2);return 0;
}

switch(表达式)中,表达式应该为整数类型:char short int long,其它类型诸如:float、double等类型均不可以

switch搭配case使用,case里如果没有添加break语句则会继续向下执行下面的case

default语句可以没有,如果所有case都不匹配会默认执行default语句

  • 汇编角度看C指针模型
void function(){                char* a;a=(char*) 610;int** b;b=(int**) 610;
}
void function(){                int a;a=610;a=(int)610;
}
void function(){                char* a;short* b;int* c;a=(char*)  1;b= (short*) 2;c=(int*)  3;
}
#include "stdafx.h"void function(){                char* a;short* b;int* c;a=(char*)  1;b= (short*) 2;c=(int*)  3;a++;b++;c++;printf("a:%d\t b:%d\tc:%d\n",a,b,c);}int main(int argc, char* argv[])
{function();return 0;
}

(4):编译器原理

编译一个程序一般由预处理器、解释器、汇编器、链接器、加载器等组成,进而将一个高级语言编译成机器可以执行的二进制语言。

  • 预处理器

主要进行宏处理,文件包含,语言拓展等。

  • 解释器

将一个高级语言转化成一个机器语言,解释器从输入中读取一句,将其变成中间代码,执行它,再读取下一句,如果有错误发生,则解释器停止执行并且报告错误。

  • 汇编器

将汇编语言转化成机器语言,汇编器的输出文件叫做目标文件,是一系列机器指令的集合,并且将它们存放到内存中去。

  • 链接器

其作用是将一些目标文件连接起来形成一个可执行文件,所有这些文件可能有不同的汇编器汇编的,链接器的作用是寻找定位程序的参考模块,决定代码被加载到内存位置。

  • 加载器

加载器是操作系统的一部分,负责加载可执行文件并且执行它们,它计算程序的大小,为其创造内存空间,初始化多个寄存器来进行执行初始化。

在这里插入图片描述

  1. 几个基本概念

编译:编译器对源文件的编译过程,就是将源文件中的文本形式代码翻译为机器语言形式的目标文件的过程,此过程中会有一系列语法检查、指令优化等,生成目标(OBJ)文件。

编译单元:每一个CPP文件就是一个编译单元,每个单元之间是互相独立且不可知的。

目标文件:编译步骤产生的文件,包含了编译单元内所有代码和数据,以二进制形式存在。请注意三个关键表:未解决符号表、导出符号表、地址重定向表。

链接:当编译器将工程中所有CPP文件以分离形式编译成各个对应目标(OBJ)文件之后,再由链接器进行链接生成一个EXE或者DLL文件。

  1. 一个例子

用一个例子来理解下编译器与链接器的工作:

file1.cpp

int gVal = 123;void func1()
{++gVal;
}

这个文件编译出来的目标文件file1.obj 就会有一个段来包含上面的数据和函数,内容大致如下(只是示意,并非完全一样):

偏移量    内容    长度0x0000   gVal    40x0004   func1  ??

这里的??表示长度未知,实际目标文件的各个数据可能不是连续的,也不一定从0x0000开始。比如这里的func1可能是这样:

0x0004 inc DWORD PTR[0x0000]0x00?? ret

这里把++gVal翻译为inc语句,也就是把本单元的0x0000地址的一个DWORD(4字节)进行加一。

file2.cpp

extern int gVal;void func2()
{++gVal;
}

对应的file2.obj文件内容应该是:

偏移量    内容    长度0x0000   func2  ??

可以看到这里并没有gVal,原因是extern关键字声明这是个外部引用符号,已经在别的单元里面定义了。由于单元之间是隔离的,所以这里的func2代码就没有办法填写地址,大致是这样:

0x0004 inc DWORD PTR[????]0x00?? ret

这个???就表示当前无法拿到有效地址,需要链接器来完成,如何告诉链接器?需要一个未解决符号表(unresolved symbol table),同样提供gVal符号的目标文件也要提供一个导出符号表(export symbol table)来告知链接器可以提供的符号内容。

这两个表之间依靠符号来进行关联,在C/C++中每一个变量和函数都有自己的符号名,函数名略复杂(依据编译器不同而有差异),假设这里函数func1的符号为_func1,那么file1.obj文件的导出符号为:

导出符号      地址gVal             0x0000_func1         0x0004

而对应的未解决符号表则为空,因为没有依赖其他单元的内容。另一个file2.cpp文件的导出符号表为:

导出符号      地址_func2         0x0000

而对应的未解决符号表为(下表的含义是说0x0001位置有一个地址不明,符号叫gVal):

未决符号      地址gVal             0x0001

链接器会针对未解决符号表中的在所有导出符号表中进行匹配,如果找到则匹配填写进来,如果找不到就会报链接错误。但这里可以发现一个问题,就是gVal的符号地址是0x0000,如果直接将解析的地址替换未解决符表中的???就会生成一个冲突的地址0x0000,与本单元的地址重复了。为了解决这个问题,还需要针对每一个编译单元引入的一个地址偏移。例如file1.obj的地址从0x00001000开始,file2.obj的地址从0x00002000开始,这样符号地址叠加后就不会重复了。记录这样的地址偏移量的表称之为地址重定向表(address redirect table)。

  1. 链接器工作顺序

当链接器进行链接的时候,首先决定各个目标文件在最终可执行文件里的位置。然后访问所有目标文件的地址重定义表,对其中记录的地址进行重定向(加上一个偏移量,即该编译单元在可执行文件上的起始地址)。然后遍历所有目标文件的未解决符号表,并且在所有的导出符号表里查找匹配的符号,并在未解决符号表中所记录的位置上填写实现地址。最后把所有的目标文件的内容写在各自的位置上,再作一些另的工作,就生成一个可执行文件。

说明:实现链接的时候会更加复杂,一般实现的目标文件都会把数据,代码分成好向个区,重定向按区进行,但原理都是一样的。

extern:这就是告诉编译器,这个变量或函数在别的编译单元里定义了,也就是要把这个符号放到未解决符号表里面去(外部链接)。

static:如果该关键字位于全局函数或者变量的声明前面,表明该编译单元不导出这个函数或变量,因些这个符号不能在别的编译单元中使用(内部链接)。如果是static局部变量,则该变量的存储方式和全局变量一样,但是仍然不导出符号。

默认链接属性:对于函数和变量,默认链接是外部链接,对于const变量,默认内部链接。

外部链接:外部链接的符号在整个程序范围内都是可以使用的,这就要求其他编译单元不能导出相同的符号(不然就会报duplicated external symbols)。

内部链接:内部链接的符号不能在别的编译单元中使用。但不同的编译单元可以拥有同样的名称的符号

相关文章:

Windows逆向安全(一)C与汇编的关系

前言 逆向是一种新型的思维模式也是软件开发领域中极为重要的技术&#xff0c;涵盖各种维度去深挖软件架构的本质和操作系统原理&#xff0c;学习逆向后可以在各领域中发挥至关重要的作用&#xff0c;其中包括黑灰色&#xff0c;安全开发&#xff0c;客户端安全&#xff0c;物…...

Lazada、Allegro、速卖通测评自养号技术(方法解析)

无论是亚马逊、拼多多Temu、shopee、Lazada、wish、速卖通、煤炉、敦煌、雅虎、eBay、TikTok、Newegg、乐天、美客多、阿里国际、沃尔玛、OZON、Joom、Facebook、Coupang、独立站、Cdiscount、Kaufland、DARTY、Allegro、MANO等平台测评自养号对于卖家来说算是一种低成本、高回…...

Vue3的composition API—setup函数, ref函数,reactive函数

1、Setup 函数 1.setup 是vue3中的一个配置项 2、setup是所有组件所需要的数据和方法都需要配置到setup中的 3、setup两种返回值&#xff1a; 若返回一个对象 若返回一个渲染函数 mian.js文件 注意&#xff1a;尽量不与Vue2混用 setup中无法访问vue2中的配置 不能是async函数…...

国外seo比较好的优化方法有哪些?

随着互联网的不断发展&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;变得越来越重要。 对于国外市场&#xff0c;Google搜索引擎是最为重要的搜索引擎之一&#xff0c; 因此在优化国外网站时&#xff0c;需要将Google SEO优化作为首要任务。 关键词研究和优化 在进行…...

【JavaEE进阶】——第一节.Maven国内源配置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 操作步骤 1.打开项目配置界面&#xff08;当前项目配置&#xff09; 2.检查并配置国内源 3.再次打开项目配置界面&#xff08;新项目配置&#xff09; 4…...

dockerFile编写

dockerFile编写 语法参数 # DockerFile常用指令 USER # 指定运行的用户&#xff0c;一般不用配置 FROM # 拉取基础镜像&#xff0c;一切从这里开始构建 ARG # 构建参数&#xff0c;只能在dockerFile中使用, # eg: JAR_FILEtarget/springboot-mongo-0.0.1-SNAPSHOT.jar MAI…...

jenkins扩展你的流水线

文章目录一、概述二、可信库和不可信库可信库不可信库三、内部库与外部库内部库SSH访问HTTP 访问外部库配置一个外部库四、在流水线脚本中使用库从源码版本控制中自动下载库加载库到脚本中Library 注解库步骤库指令五、Jenkins 项目中的库范围六、共享库代码的结构src示例一&am…...

Golang模糊测试入门

本教程介绍了 Go 中模糊测试的基础知识。通过模糊测试,随机数据会针对您的测试运行,以试图找到漏洞或导致崩溃的输入。可以通过模糊测试发现的一些漏洞示例包括 SQL 注入、缓冲区溢出、拒绝服务和跨站点脚本攻击。 在本教程中,您将为一个简单的函数编写模糊测试,运行 go 命…...

ARM uboot 的移植4 -从 uboot 官方标准uboot开始移植

一、添加DDR初始化1 1、分析下一步的移植路线 (1) cpu_init_crit 函数成功初始化串口、时钟后&#xff0c;转入 _main 函数&#xff0c;函数在 arch/arm/lib/crt0.S 文件中。 (2) 在 crt0.S 中首先设置栈&#xff0c;将 sp 指向 DDR 中的栈地址&#xff1b; #if defined(CONF…...

不用索引怎么优化百亿数据? | MySQL性能优化篇

文章目录数据库调优一、数据库调优原理1.1 为什么要进行MySQL数据库调优&#xff1f;1.2 什么影响数据库性能&#xff1f;1.3 数据库调优到底调什么&#xff1f;二、数据库压力测试2.1 什么是压测&#xff1f;2.2 JMeter简介2.3 驱动下载2.4 测试过程三、连接池3.1 压力测试连接…...

JavaScript(WebAPI)

目录 1.什么是Web API? 2.DOM和DOM树 3.获取元素 4.事件 5.操作元素 获取/修改元素内容 1.innerText 2. innerHTML 获取/修改元素属性 获取/修改表单元素属性 获取/修改样式属性 1.修改内联样式 2.修改元素应用的CSS类名 6.操作节点 新增节点 删除节点 7.案例…...

idea集成GitHub

设置 GitHub 账号绑定账号有两种方式&#xff1a;1. 通过授权登录2.如果上述登录不成功&#xff0c;用Token口令的方式登录&#xff0c;口令在github账号哪里生成&#xff0c;点击settings --->Developer settings --->pwrsonal access tokens ----> 复制口令到idea 口…...

软考高级信息系统项目管理师系列之四十一:项目组合管理

软考高级信息系统项目管理师系列之四十一:项目组合管理 一、项目组合管理内容二、项目组合管理基础概述1.项目组合定义及相关知识图2.项目组合、项目集和项目之间的关系3.项目组合模块具备的特征三、项目组合管理1.项目组合管理定义及要求2.不同级别管理之间的特性及关联3.项目…...

Spring——Spring整合Mybatis(XML和注解两种方式)

框架整合spring的目的:把该框架常用的工具对象交给spring管理&#xff0c;要用时从IOC容器中取mybatis对象。 在spring中应该管理的对象是sqlsessionfactory对象&#xff0c;工厂只允许被创建一次&#xff0c;所以需要创建一个工具类&#xff0c;把创建工厂的代码放在里面&…...

【专项训练】布隆过滤器和LRU缓存

布隆过滤器:与哈希表类似 哈希表是一个没有误差的数据结构! 有哈希函数得到index,会把要存的整个元素放在哈希表里面 有多少元素,每个元素有多大,所有的这些元素需要占的内存空间,在哈希表中都要找相应的内存大小给存起来 事实上,我们并不需要存所有的元素本身,而是只…...

从一道面试题看 TCP 的吞吐极限

分享一个 TCP 面试题&#xff1a;单条 TCP 流如何打满香港到旧金山的 320Gbps 专线&#xff1f;(补充&#xff0c;写成 400Gbps 更具迷惑性&#xff0c;但预测大多数人都会跑偏&#xff0c;320Gbps 也就白给了) 这个题目是上周帮一个朋友想的&#xff0c;建议他别问三次握手&a…...

rsync 的用法

rsync 介绍下 用法 rsync是一个常用的数据同步工具&#xff0c;它能够在本地和远程系统之间同步文件和目录。以下是rsync的基本用法&#xff1a; 同步本地文件夹&#xff1a; bash Copy code rsync -av /path/to/source /path/to/destination其中&#xff0c;-a表示归档模式&…...

【LeetCode每日一题:[面试题 17.05] 字母与数字-前缀和+Hash表】

题目描述 给定一个放有字母和数字的数组&#xff0c;找到最长的子数组&#xff0c;且包含的字母和数字的个数相同。 返回该子数组&#xff0c;若存在多个最长子数组&#xff0c;返回左端点下标值最小的子数组。若不存在这样的数组&#xff0c;返回一个空数组。 示例 1: 输入…...

华为OD机试题 - 简易压缩算法(JavaScript)| 机考必刷

更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:简易压缩算法题目输入输出示例一输入输出说明示例二输入输出说明…...

Kubenates中的日志收集方案ELK(下)

1、rpm安装Logstash wget https://artifacts.elastic.co/downloads/logstash/logstash-6.8.7.rpm yum install -y logstash-6.8.7.rpm2、创建syslog配置 input {beats{port> 5044 } }output {elasticsearch {hosts > ["http://localhost:9200"]index …...

LeetCode - 42 接雨水

目录 题目来源 题目描述 示例 提示 题目解析 算法源码 题目来源 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例1 输入&…...

python --生成时间序列,作为横轴的标签。时间跨越2008-2022年,生成每年的6-10月的第一天作为时间序列

python 生成制定的时间序列作为绘图时x轴的标签 问题需求 在绘图时&#xff0c;需要对于x轴的标签进行专门的设置&#xff0c;整体时间跨越2008年-2022年&#xff0c;将每年的6-10月的第一天生成一条时间序列&#xff0c;绘制成图。 解决思路 对于时间序列的生成&#xff0…...

【Unity VR开发】结合VRTK4.0:创建一个按钮(Togglr Button)

语录&#xff1a; 有人感激过你的善良吗&#xff0c;貌似他们只会得寸进尺。 前言&#xff1a; Toggle按钮是提供简单空间 UI 选项的另一种方式&#xff0c;在该选项中&#xff0c;按钮将保持其状态&#xff0c;直到再次单击它。这允许按钮处于激活状态或停用状态的情况&#…...

lottie-miniprogram在taro+vue的小程序中怎么使用

把一个json的动图展示在页面上。使用的是插件lottie-miniprogramhttps://blog.csdn.net/qq_33769914/article/details/128705922之前介绍过。但是发现使用在taro使用的时候他会报错。那可能是因为我们 wx.createSelectorQuery().select(#canvas).node(res > {console.log(re…...

C++回顾(二十二)—— stack容器 与 queue容器

22.1 stack容器 &#xff08;1&#xff09; stack容器简介 stack是堆栈容器&#xff0c;是一种“先进后出”的容器。stack是简单地装饰deque容器而成为另外的一种容器。添加头文件&#xff1a;#include <stack> &#xff08;2&#xff09;stack对象的默认构造 stack…...

逻辑优化基础-disjoint support decomposition

先遣兵 在了解 disjoint support decomposition 之前&#xff0c;先学习两个基本的概念。 disjoint 数学含义上的两个集合交集&#xff0c;所谓非相交&#xff0c;即交集为空集。 A∩BC⊘A \cap B C \oslash A∩BC⊘ support 逻辑综合中的 supportsupportsupport 概念是…...

保姆级使用PyTorch训练与评估自己的DaViT网络教程

文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址&#xff1a;https://github.com/Fafa-DL/Awesome-Backbones 操作教程&#xff1a;https://www.bilibili.co…...

Java8新特性:Stream流处理使用总结

一. 概述 Stream流是Java8推出的、批量处理数据集合的新特性&#xff0c;在java.util.stream包下。结合着Java8同期推出的另一项新技术&#xff1a;行为参数化&#xff08;包括函数式接口、Lambda表达式、方法引用等&#xff09;&#xff0c;Java语言吸收了函数式编程的语法特…...

Java基准测试工具JMH高级使用

去年&#xff0c;我们写过一篇关于JMH的入门使用的文章&#xff1a;Java基准测试工具JMH使用&#xff0c;今天我们再来聊一下关于JMH的高阶使用。主要我们会围绕着以下几点来讲&#xff1a; 对称并发测试非对称并发测试阻塞并发测试Map并发测试 关键词 State 在很多时候我们…...

问心 | 再看token、session和cookie

什么是cookie HTTP Cookie&#xff08;也叫 Web Cookie或浏览器 Cookie&#xff09;是服务器发送到用户浏览器并保存在本地的一小块数据&#xff0c;它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。 什么是session Session 代表着服务器和客户端一次会话…...

Ubuntu 安装 CUDA and Cudnn

文章目录0 查看 nvidia驱动版本1 下载Cuda2 下载cudnn参考&#xff1a;0 查看 nvidia驱动版本 nvidia-smi1 下载Cuda 安装之前先安装 gcc g gdb 官方&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive&#xff0c;与驱动版本进行对应&#xff0c;我这里是12.0…...

【漏洞复现】Grafana任意文件读取(CVE-2021-43798)

docker环境搭建 #进入环境 cd vulhub/grafana/CVE-2021-43798#启动环境&#xff0c;这个过程可能会有点慢&#xff0c;保持网络通畅 docker-compose up -d#查看环境 docker-compose ps直接访问虚拟机 IP地址:3000 目录遍历原理 目录遍历原理&#xff1a;攻击者可以通过将包含…...

磨金石教育摄影技能干货分享|春之旅拍

春天来一次短暂的旅行&#xff0c;你会选择哪里呢&#xff1f;春天的照片又该如何拍呢&#xff1f;看看下面的照片&#xff0c;或许能给你答案。照片的构图很巧妙&#xff0c;画面被分成两部分&#xff0c;一半湖泊&#xff0c;一半绿色树林。分开这些的是一条斜向的公路&#…...

中断以及 PIC可编程中断控制器

1 中断分为同步中断&#xff08;中断&#xff09;和异步中断&#xff08;异常&#xff09; 1.1 中断和异常的不同 中断由IO设备和定时器产生&#xff0c;用户的一次按键会引起中断。异步。 异常一般由程序错误产生或者由内核必须处理的异常条件产生。同步。缺页异常&#xff…...

SecureCRT 安装并绑定ENSP设备终端

软件下载链接链接&#xff1a;https://pan.baidu.com/s/1WFxmQgaO9bIiUTwBLSR4OA?pwd2023 提取码&#xff1a;2023 CRT安装&#xff1a;软件可以从上面链接进行下载&#xff0c;下载完成后解压如下&#xff1a;首先双击运行scrt-x64.8.5.4 软件&#xff0c;进行安装点击NEXT选…...

ESP32设备驱动-TCS3200颜色传感器驱动

TCS3200颜色传感器驱动 1、TCS3200介绍 TCS3200 和 TCS3210 可编程彩色光频率转换器在单个单片 CMOS 集成电路上结合了可配置的硅光电二极管和电流频率转换器。 输出是方波(50% 占空比),其频率与光强度(辐照度)成正比。 满量程输出频率可以通过两个控制输入引脚按三个预…...

< JavaScript小技巧:Array构造函数妙用 >

文章目录&#x1f449; Array构造函数 - 基本概念&#x1f449; Array函数技巧用法1. Array.of()2. Array.from()3. Array.reduce()4. (Array | String).includes()5. Array.at()6. Array.flat()7. Array.findIndex()&#x1f4c3; 参考文献往期内容 &#x1f4a8;今天这篇文章…...

【17】组合逻辑 - VL17/VL19/VL20 用3-8译码器 或 4选1多路选择器 实现逻辑函数

VL17 用3-8译码器实现全减器 【本题我的也是绝境】 因为把握到了题目的本质要求【用3-8译码器】来实现全减器。 其实我对全减器也是不大清楚,但是仿照对全加器的理解,全减器就是低位不够减来自低位的借位 和 本单元位不够减向后面一位索要的借位。如此而已,也没有很难理解…...

2023年全国最新二级建造师精选真题及答案19

百分百题库提供二级建造师考试试题、二建考试预测题、二级建造师考试真题、二建证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 37.下列纠纷中&#xff0c;属于劳动争议范围的有()。 A.因劳动保护发生的纠纷 B.家庭与家政…...

Java中的 this 和 super

1 this 关键字 1.1 this 访问本类属性 this 代表对当前对象的一个引用 所谓当前对象&#xff0c;指的是调用当前类中方法或属性的那个对象this只能在方法内部使用&#xff0c;表示对“调用方法的那个对象”的引用this.属性名&#xff0c;表示本对象自己的属性 当对象的属性和…...

ESP32设备驱动-红外寻迹传感器驱动

红外寻迹传感器驱动 1、红外寻迹传感器介绍 红外寻迹传感器具有一对红外线发射管与接收管,发射管发射出一定频率的红外线,当检测方向遇到障碍物(反射面)时,红外线反射回来被接收管接收,经过比较器电路处理之后,输出接口会输出一个数字信号(低电平或高电平,取决于电路…...

初识BFC

初识BFC 先说如何开启BFC&#xff1a; 1.设置display属性&#xff1a;inline-block&#xff0c;flex&#xff0c;grid 2.设置定位属性&#xff1a;absolute&#xff0c;fixed 3.设置overflow属性&#xff1a;hidden&#xff0c;auto&#xff0c;scroll 4.设置浮动&#xf…...

随想录二刷Day17——二叉树

文章目录二叉树9. 二叉树的最大深度10. 二叉树的最小深度11. 完全二叉树的节点个数12. 平衡二叉树二叉树 9. 二叉树的最大深度 104. 二叉树的最大深度 思路1&#xff1a; 递归找左右子树的最大深度&#xff0c;选择最深的 1&#xff08;即加上当前层&#xff09;。 class So…...

Weblogic管理控制台未授权远程命令执行漏洞复现(cve-2020-14882/cve-2020-14883)

目录漏洞描述影响版本漏洞复现权限绕过漏洞远程命令执行声明&#xff1a;本文仅供学习参考&#xff0c;其中涉及的一切资源均来源于网络&#xff0c;请勿用于任何非法行为&#xff0c;否则您将自行承担相应后果&#xff0c;本人不承担任何法律及连带责任。 漏洞描述 Weblogic…...

STM32F103CubeMX定时器

前言定时器作为最重要的内容之一&#xff0c;是每一位嵌入式软件工程师必备的能力。STM32F103的定时器是非常强大的。1&#xff0c;他可以用于精准定时&#xff0c;当成延时函数来使用。不过个人不建议这么使用&#xff0c;因为定时器很强大&#xff0c;这么搞太浪费了。如果想…...

多态且原理

多态 文章目录多态多态的定义和条件协变&#xff08;父类和子类的返回值类型不同&#xff09;函数隐藏和虚函数重写的比较析构函数的重写关键字final和override抽象类多态的原理单继承和多继承的虚函数表单继承下的虚函数表多继承下的虚函数表多态的定义和条件 定义&#xff1…...

动态库(二) 创建动态库

文章目录创建动态库设计动态库ABI兼容动态符号的可见性示例控制符号可见性通过-fvisibility通过strip工具删除指定符号创建动态库 在Linux中创建动态库通过如下过程完成&#xff1a; gcc -fPIC -c first.c second.c gcc -shared first.o second.o -o libdynamiclib.so 按照Li…...

opencv加水印

本文介绍opencv给图片加水印的方法。 目录1、添加水印1.1、铺满1.2、在指定区域添加1.3、一比一铺满1、添加水印 添加水印的原理是调低两张图片的透明度&#xff0c;然后叠加起来。公式如下&#xff1a; dst src1 * opacity src2 * (1 - opacity) gamma; opacity是透明度&a…...

Flume基操

Flume概述 Flume 定义 Flume 是 Cloudera 提供的一个高可用的&#xff0c;高可靠的&#xff0c;分布式的海量日志采集、聚合和传输的系统。Flume 基于流式架构&#xff0c;灵活简单。 Flume最主要的作用就是&#xff0c;实时读取服务器本地磁盘的数据&#xff0c;将数据写入到…...

图文详解红黑树,还有谁不会?

前言在MySQL中&#xff0c;无论是Innodb还是MyIsam&#xff0c;都使用了B树作索引结构(这里不考虑hash等其他索引)。本文将从最普通的二叉查找树开始&#xff0c;逐步说明各种树解决的问题以及面临的新问题&#xff0c;从而说明MySQL为什么选择B树作为索引结构。目录一、二叉查…...