利用汇编挖掘编程语言的本质

代码的执行效率分析

◼ 思考:if-else和switch,谁的效率高?

int no = 4;
if (no == 1) {
   printf("no is 2");
} else if (no == 2) {
   printf("no is 2");
} else if (no == 3) {
   printf("no is 3");
} else if (no == 4) {
   printf("no is 4");
} else if (no == 5) {
   printf("no is 5");
} else {
   printf("other no");
}
int no = 4;

switch (no) {
case 1:
   printf("no is 1");
   break;
case 2:
   printf("no is 2");
   break;
case 3:
   printf("no is 3");
   break;
case 4:
   printf("no is 4");
   break;
case 5:
   printf("no is 5");
   break;
default:
   printf("other no");
   break;
}
image-20210330105758488
$SG28198 DB 'no is 1', 00H
$SG28201 DB 'no is 2', 00H
$SG28204 DB 'no is 3', 00H
$SG28207 DB 'no is 4', 00H
$SG28210 DB 'no is 5', 00H
$SG28211 DB 'other no', 00H
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage

_no$ = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  push ecx
  mov DWORD PTR _no$[ebp], 4
  cmp DWORD PTR _no$[ebp], 1
  jne SHORT $LN2@main
  push OFFSET $SG28198
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN2@main:
  cmp DWORD PTR _no$[ebp], 2
  jne SHORT $LN4@main
  push OFFSET $SG28201
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN4@main:
  cmp DWORD PTR _no$[ebp], 3
  jne SHORT $LN6@main
  push OFFSET $SG28204
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN6@main:
  cmp DWORD PTR _no$[ebp], 4
  jne SHORT $LN8@main
  push OFFSET $SG28207
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN8@main:
  cmp DWORD PTR _no$[ebp], 5
  jne SHORT $LN10@main
  push OFFSET $SG28210
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN10@main:
  push OFFSET $SG28211
  call _printf
  add esp, 4
$LN1@main:
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
_main ENDP

mov DWORD PTR _no$[ebp], 4 将 4 放入[ebp-4]内存地址,no=4

cmp DWORD PTR _no$[ebp], 1 比较no和1的大小

jne jump not equal,不相等的时候就会跳转 jne SHORT $LN2@main 不相等,就直接跳到 $LN2@main 后面的汇编指令继续执行

$SG28198 DB 'no is 1', 00H
 
 push OFFSET $SG28198
 call _printf

如果no=1 相等的话 就不跳转 执行 push OFFSET $SG28198 call _printf 相当于 printf("no is 1");

jmp == jump,无条件跳转 jmp SHORT $LN1@main 跳到程序结尾

从if-else if - else 汇编指令来分析,把最常命中的条件放前面,减少cmp指令条件的判断

switch

image-20210330112108442
$SG28199 DB 'no is 1', 00H
$SG28201 DB 'no is 2', 00H
$SG28203 DB 'no is 3', 00H
$SG28205 DB 'no is 4', 00H
$SG28207 DB 'no is 5', 00H
$SG28209 DB 'other no', 00H
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage

_no$ = -8 ; size = 4
tv64 = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 8
  mov DWORD PTR _no$[ebp], 4
  mov eax, DWORD PTR _no$[ebp]
  mov DWORD PTR tv64[ebp], eax
  mov ecx, DWORD PTR tv64[ebp]
  sub ecx, 1
  mov DWORD PTR tv64[ebp], ecx
  cmp DWORD PTR tv64[ebp], 4
  ja SHORT $LN9@main
  mov edx, DWORD PTR tv64[ebp]
  jmp DWORD PTR $LN11@main[edx*4]
$LN4@main:
  push OFFSET $SG28199
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN5@main:
  push OFFSET $SG28201
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN6@main:
  push OFFSET $SG28203
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN7@main:
  push OFFSET $SG28205
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN8@main:
  push OFFSET $SG28207
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN9@main:
  push OFFSET $SG28209
  call _printf
  add esp, 4
$LN1@main:
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
  npad 2
$LN11@main:
  DD $LN4@main   //0-3
  DD $LN5@main   //4-7
  DD $LN6@main   //8-11
  DD $LN7@main   //12-15
  DD $LN8@main   //16-19
_main ENDP

内存中的存了代码跳转的地址

image-20210330121507543

switch核心汇编分析

  mov DWORD PTR _no$[ebp], 4        //no = 4
  mov eax, DWORD PTR _no$[ebp]      //eax = 4
  mov DWORD PTR tv64[ebp], eax      //tv64 = 4 tv64临时变量
  mov ecx, DWORD PTR tv64[ebp]      //ecx = 4
  sub ecx, 1                        //ecx = 3   
  mov DWORD PTR tv64[ebp], ecx      //tv64 = 3
  cmp DWORD PTR tv64[ebp], 4        //比较tv64和4的大小
  ja SHORT $LN9@main                //直接跳转到$LN9@main 
  mov edx, DWORD PTR tv64[ebp]      //edx = 3
  jmp DWORD PTR $LN11@main[edx*4]   //无条件跳转到 $LN11@main 3*4=12

开辟了20个字节的存储空间,作为jmp比较跳转地址的保存空间,在x86环境下,一个指针的地址占四个字节,所以是45=20个字节的地址,case在这段代码的汇编,用了空间换时间的优化思想*。

情景分析-1-将条件减少一点

switch条件比较少的情况下,没有生成跳转位置表

◼ 编译器并没有做任何优化,if-else跟switch效率差不多

int main(){
    int no = 4;

switch (no) {
case 1:
   printf("no is 1");
   break;
case 2:
   printf("no is 2");
   break;
default:
   printf("other no");
   break;
}

}

//assembly 
_no$ = -8 ; size = 4
tv64 = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 8
  mov DWORD PTR _no$[ebp], 4
  mov eax, DWORD PTR _no$[ebp]
  mov DWORD PTR tv64[ebp], eax
  cmp DWORD PTR tv64[ebp], 1    //和1比较
  je SHORT $LN4@main            //je 相等就跳转
  cmp DWORD PTR tv64[ebp], 2    //和2比较
  je SHORT $LN5@main
  jmp SHORT $LN6@main           //jmp 无条件跳转到$LN6@main other
$LN4@main:
  push OFFSET $SG28199
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN5@main:
  push OFFSET $SG28201
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN6@main:
  push OFFSET $SG28203
  call _printf
  add esp, 4
$LN1@main:
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
_main ENDP

情景分析-2- case的值连续

image-20210330120327282

◼情景分析-3

case的值不连续

// 条件比较多
void test3() {
   int no = 8;

   switch (no) {
   case 1:
      printf("no is 1");
      break;
   case 3:
      printf("no is 3");
      break;
   case 5:
      printf("no is 5");
      break;
   case 6:
      printf("no is 6");
      break;
   default:
      printf("other no");
      break;
   }

   int age = 4;
}
$SG28200 DB 'no is 1', 00H
$SG28202 DB 'no is 3', 00H
$SG28204 DB 'no is 5', 00H
$SG28206 DB 'no is 6', 00H
$SG28208 DB 'other no', 00H
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage

_age$ = -12 ; size = 4
_no$ = -8 ; size = 4
tv64 = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 12 ; 0000000cH
  mov DWORD PTR _no$[ebp], 8
  mov eax, DWORD PTR _no$[ebp]
  mov DWORD PTR tv64[ebp], eax
  mov ecx, DWORD PTR tv64[ebp]
  sub ecx, 1                   //-1
  mov DWORD PTR tv64[ebp], ecx
  cmp DWORD PTR tv64[ebp], 5
  ja SHORT $LN8@main
  mov edx, DWORD PTR tv64[ebp]
  jmp DWORD PTR $LN10@main[edx*4]
$LN4@main:
  push OFFSET $SG28200
  call _printf
  add esp, 4
  jmp SHORT $LN2@main
$LN5@main:
  push OFFSET $SG28202
  call _printf
  add esp, 4
  jmp SHORT $LN2@main
$LN6@main:
  push OFFSET $SG28204
  call _printf
  add esp, 4
  jmp SHORT $LN2@main
$LN7@main:
  push OFFSET $SG28206
  call _printf
  add esp, 4
  jmp SHORT $LN2@main
$LN8@main:
  push OFFSET $SG28208
  call _printf
  add esp, 4
$LN2@main:
  mov DWORD PTR _age$[ebp], 4
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
  npad 2
$LN10@main:
  DD $LN4@main  //case 1
  DD $LN8@main  //case 2
  DD $LN5@main  //case 3
  DD $LN8@main  //case 4
  DD $LN6@main  //case 5
  DD $LN7@main  //case 6
_main ENDP

jmp DWORD PTR $LN10@main[edx*4]

$LN10@main:
  DD $LN4@main  //case 1
  DD $LN8@main  //case 2
  DD $LN5@main  //case 3
  DD $LN8@main  //case 4
  DD $LN6@main  //case 5
  DD $LN7@main  //case 6

跳转表里面加了case2 和case4的地址,填入的other的地址,同样开辟了内存空间存储了跳转表来计算case情况的跳转地址!

case的值差距较大

int main(){
    int no = 100;

    switch (no) {
    case 1:
        printf("no is 1");
        break;
    case 3:
        printf("no is 3");
        break;
    case 8:
        printf("no is 8");
        break;
    case 12:
        printf("no is 100");
        break;
    default:
        printf("other no");
        break;
    }

    int age = 4;

}
_age$ = -12 ; size = 4
_no$ = -8 ; size = 4
tv64 = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 12 ; 0000000cH
  mov DWORD PTR _no$[ebp], 4
  mov eax, DWORD PTR _no$[ebp]
  mov DWORD PTR tv64[ebp], eax
  mov ecx, DWORD PTR tv64[ebp]
  sub ecx, 1                                    //ecx = 3
  mov DWORD PTR tv64[ebp], ecx                  //tv64 = 3
  cmp DWORD PTR tv64[ebp], 11 ; 0000000bH       //3>11 ?
  ja SHORT $LN8@main                            //不大于11
  mov edx, DWORD PTR tv64[ebp]                  //edx = 3
  movzx eax, BYTE PTR $LN10@main[edx]           //eax = 4
  jmp DWORD PTR $LN11@main[eax*4]               //跳转到$LN11@main[4] -->$LN8@main  即other
$LN4@main:
  push OFFSET $SG28200
  call _printf
  add esp, 4
  jmp SHORT $LN2@main
  .......
$LN8@main:
  push OFFSET $SG28208                          //$SG28208 DB 'other no', 00H
  call _printf
  add esp, 4
$LN11@main:
  DD $LN4@main      //0
  DD $LN5@main      //1
  DD $LN6@main      //2
  DD $LN7@main      //3
  DD $LN8@main      //4
$LN10@main:
  DB 0
  DB 4
  DB 1
  DB 4
  DB 4
  DB 4
  DB 4
  DB 2
  DB 4
  DB 4
  DB 4
  DB 3
_main ENDP

这种case语句,开辟了两张表,一张case语句代码执行地址的表,一共5个地址4*5=20个字节,又开辟了一个12个byte的内存地址表,存储了0-11这些情况需要计算内存地址偏移地址长度的个数

image-20210330130908339

case的值差距非常大

退化为if方式

int main(){
    
    int no = 100;

    switch (no) {
    case 1:
        printf("no is 1");
        break;
    case 3:
        printf("no is 3");
        break;
    case 8:
        printf("no is 8");
        break;
    case 10000:
        printf("no is 100");
        break;
    default:
        printf("other no");
        break;
    }


}


_main PROC
  push ebp
  mov ebp, esp
  sub esp, 8
  mov DWORD PTR _no$[ebp], 100 ; 00000064H
  mov eax, DWORD PTR _no$[ebp]
  mov DWORD PTR tv64[ebp], eax
  cmp DWORD PTR tv64[ebp], 8
  jg SHORT $LN10@main               //是否大于8
  cmp DWORD PTR tv64[ebp], 8
  je SHORT $LN6@main                //==8
  cmp DWORD PTR tv64[ebp], 1
  je SHORT $LN4@main                //==1
  cmp DWORD PTR tv64[ebp], 3
  je SHORT $LN5@main                //==3
  jmp SHORT $LN8@main
$LN10@main:
  cmp DWORD PTR tv64[ebp], 10000 ; 00002710H
  je SHORT $LN7@main                //==10000
  jmp SHORT $LN8@main               //跳转到other
$LN4@main:
  push OFFSET $SG28199
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN5@main:
  push OFFSET $SG28201
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN6@main:
  push OFFSET $SG28203
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN7@main:
  push OFFSET $SG28205
  call _printf
  add esp, 4
  jmp SHORT $LN1@main
$LN8@main:
  push OFFSET $SG28207
  call _printf
  add esp, 4
$LN1@main:
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
_main ENDP

总结

◼ 对比if-else,编译器会对switch作一定的优化,提高执行效率


a++和++a

◼ 在C++中,++a是可以被赋值的,a++不可以被赋值

void test6() {
   int a = 5;
   int b = a++ + 2;

   printf("a == %d\n", a);
   printf("b == %d\n", b);
}

a == 6
b == 7

_b$ = -8 ; size = 4
_a$ = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 8
  mov DWORD PTR _a$[ebp], 5         //a=5
  mov eax, DWORD PTR _a$[ebp]       //eax=5
  add eax, 2                        //eax=7
  mov DWORD PTR _b$[ebp], eax       //b = 7
  mov ecx, DWORD PTR _a$[ebp]       //ecx = 5
  add ecx, 1                        //exc = 6
  mov DWORD PTR _a$[ebp], ecx       //a= ecx = 6
  mov edx, DWORD PTR _a$[ebp]
  push edx
  push OFFSET $SG28197
  call _printf
  add esp, 8
  mov eax, DWORD PTR _b$[ebp]
  push eax
  push OFFSET $SG28198
  call _printf
  add esp, 8
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
_main ENDP
int main(){
    
   int a = 5;
   int b = ++a + 2;
}

_b$ = -8 ; size = 4
_a$ = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 8
  mov DWORD PTR _a$[ebp], 5         //a=5
  mov eax, DWORD PTR _a$[ebp]       //eax = a = 5
  add eax, 1                        //eax = 6
  mov DWORD PTR _a$[ebp], eax       //a = eax = 6
  mov ecx, DWORD PTR _a$[ebp]       //exc = eax = 6
  add ecx, 2                        //ecx = (6+2) = 8
  mov DWORD PTR _b$[ebp], ecx       //b = ecx = 8
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
_main ENDP

a == 6

b == 8

a++ = 7 is not assignable

a++Expression is not assignable

构造函数

◼ 构造函数(也叫构造器),在对象创建的时候自动调用,一般用于完成对象的初始化工作

◼ 一个广为流传的、很多教程\书籍都推崇的错误结论:

默认情况下,编译器会为每一个类生成空的无参的构造函数

正确理解:==在某些特定的情况下,编译器才会为类生成空的无参的构造函数==,比如

✓ 成员变量在声明的同时进行了初始化

✓ 包含了对象类型的成员,且这个成员有构造函数

✓ 父类有构造函数

✓ ......等等

class Car {
public:
    int m_price;

    Car() {
        printf("Car()----");
    }
};

int main(){
    
  Car *car = new Car();
  car->m_price = 100;
}
_car$ = -28 ; size = 4
$T2 = -24 ; size = 4
tv75 = -20 ; size = 4
$T3 = -16 ; size = 4
__$EHRec$ = -12 ; size = 12
_main PROC
  push ebp
  mov ebp, esp
  push -1
  push __ehhandler$_main
  mov eax, DWORD PTR fs:0
  push eax
  sub esp, 16 ; 00000010H
  mov eax, DWORD PTR ___security_cookie
  xor eax, ebp
  push eax
  lea eax, DWORD PTR __$EHRec$[ebp]
  mov DWORD PTR fs:0, eax
  push 4
  call void * operator new(unsigned int) ; operator new
  add esp, 4
  mov DWORD PTR $T3[ebp], eax
  mov DWORD PTR __$EHRec$[ebp+8], 0
  cmp DWORD PTR $T3[ebp], 0
  je SHORT $LN3@main
  mov ecx, DWORD PTR $T3[ebp]
  call Car::Car(void) ; Car::Car
  mov DWORD PTR tv75[ebp], eax
  jmp SHORT $LN4@main
$LN3@main:
  mov DWORD PTR tv75[ebp], 0
$LN4@main:
  mov eax, DWORD PTR tv75[ebp]
  mov DWORD PTR $T2[ebp], eax
  mov DWORD PTR __$EHRec$[ebp+8], -1
  mov ecx, DWORD PTR $T2[ebp]
  mov DWORD PTR _car$[ebp], ecx
  mov edx, DWORD PTR _car$[ebp]
  mov DWORD PTR [edx], 100 ; 00000064H
  xor eax, eax
  mov ecx, DWORD PTR __$EHRec$[ebp]
  mov DWORD PTR fs:0, ecx
  pop ecx
  mov esp, ebp
  pop ebp
  ret 0
__unwindfunclet$_main$0:
  push 4
  mov eax, DWORD PTR $T3[ebp]
  push eax
  call void operator delete(void *,unsigned int) ; operator delete
  add esp, 8
  ret 0
__ehhandler$_main:
  mov edx, DWORD PTR [esp+8]
  lea eax, DWORD PTR [edx+12]
  mov ecx, DWORD PTR [edx-20]
  xor ecx, eax
  call @__security_check_cookie@4
  mov eax, OFFSET __ehfuncinfo$_main
  jmp ___CxxFrameHandler3
_main ENDP
image-20210330142207900

call Car::Car(void) ; Car::Car car类已经定义了默认的构造函数的情况下 new的情况下,主动调用call函数执行默认构造函数,并且汇编生成了car的汇编代码

去掉默认构造函数,没有实现的情况下呢?接着分析情况。

class Car {
public:
    int m_price ;
};

int main(){
    
  Car *car = new Car();
  printf("car=%d",car->m_price);
  car->m_price = 100;
}
image-20210330142653826

从汇编代码编译看到,没有为car类生成任何汇编代码,直接mov DWORD PTR _car$[ebp], eax给了car变量一个指针内存地址。

_car$ = -12 ; size = 4
tv70 = -8 ; size = 4
$T1 = -4 ; size = 4
_main PROC
  push ebp
  mov ebp, esp
  sub esp, 12 ; 0000000cH
  push 4
  call void * operator new(unsigned int) ; operator new
  add esp, 4
  mov DWORD PTR $T1[ebp], eax
  cmp DWORD PTR $T1[ebp], 0
  je SHORT $LN3@main
  xor eax, eax
  mov ecx, DWORD PTR $T1[ebp]
  mov DWORD PTR [ecx], eax
  mov edx, DWORD PTR $T1[ebp]
  mov DWORD PTR tv70[ebp], edx
  jmp SHORT $LN4@main
$LN3@main:
  mov DWORD PTR tv70[ebp], 0
$LN4@main:
  mov eax, DWORD PTR tv70[ebp]
  mov DWORD PTR _car$[ebp], eax
  mov ecx, DWORD PTR _car$[ebp]
  mov edx, DWORD PTR [ecx]
  push edx
  push OFFSET $SG28210
  call _printf
  add esp, 8
  mov eax, DWORD PTR _car$[ebp]
  mov DWORD PTR [eax], 100 ; 00000064H
  xor eax, eax
  mov esp, ebp
  pop ebp
  ret 0
_main ENDP

成员变量在声明的同时进行了初始化

class Car {
public:
    int m_price = 5 ;
};

int main(){
    
  Car *car = new Car();
  printf("car=%d",car->m_price);
  car->m_price = 100;
}
image-20210330143428531

包含了对象类型的成员,且这个成员有构造函数

class Car {
public:
    int m_price;

    Car() {
        m_price = 5;
    }
};
class Person {
    Car car;
};

int main(){

    Person *p = new Person();
}
image-20210330144438294

我们再来看一下,成员没有构造函数的情况

image-20210330144928060
image-20210330145146327

父类有构造函数

image-20210330150025836
image-20210330145707866

函数的内存布局

EBP:基址指针寄存器,指向栈帧的底部,基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。

EIP:指令寄存器,存储的是 CPU 下次要执行的指令的地址,ARM 下为 PC,寄存器为 R15。

image-20210330154204432
image-20210330160220265
void test1(int v1, int v2) {
    int a = v1 + 1;
    int b = v2 + 2;

//  test2(3);
}
int main(){
    
    test1(1,2);
}
_b$ = -8 ; size = 4
_a$ = -4 ; size = 4
_v1$ = 8 ; size = 4
_v2$ = 12 ; size = 4
void test1(int,int) PROC ; test1
  push ebp
  mov ebp, esp
  sub esp, 8
  mov eax, DWORD PTR _v1$[ebp]
  add eax, 1
  mov DWORD PTR _a$[ebp], eax
  mov ecx, DWORD PTR _v2$[ebp]
  add ecx, 2
  mov DWORD PTR _b$[ebp], ecx
  mov esp, ebp
  pop ebp
  ret 0
void test1(int,int) ENDP ; test1

_main PROC
  push ebp
  mov ebp, esp
  push 2
  push 1
  call void test1(int,int) ; test1
  add esp, 8
  xor eax, eax
  pop ebp
  ret 0
_main ENDP
image-20210330164237412
image-20210330164836916
  • push 2
push 2
  • push 1
push 1
  • call void test1(int,int)
image-20210330165155087
  • push ebp
image-20210330165458038
  • mov ebp, esp
image-20210330165758340
  • sub esp, 8
image-20210330170106338
  • mov eax, DWORD PTR _v1$[ebp]
image-20210330170405070

eax=1

  • add eax, 1 eax=2
  • mov DWORD PTR _a$[ebp], eax
image-20210330170637097
  • mov ecx, DWORD PTR _v2$[ebp]
image-20210330170804461
  • add ecx, 2 ecx=2+2 =4
  • mov DWORD PTR _b$[ebp], ecx
image-20210330170933930
  • mov esp, ebp
image-20210330171147234
  • pop ebp
image-20210330171447008
  • ret 0
image-20210330171814967

函数从ret返回后 ESP EBP 和调用前,指向的栈地址完全恢复 EIP回到了调用test函数前的下一条地址,完成函数栈平衡和调用堆栈恢复

image-20210330172033328
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容