通过汇编语言理解栈的过程调用2

接之前的博客,废话不多说,直接上代码分析

C代码

#include <stdio.h>
using namespace std;
void test() {

	printf("\n!!!执行了 test 函数!!!\n\n");

	return;
}

int sumset(int x, int y, int i) {

	int a = x;
	int b = y;
	void* c[1];

	printf("\nsumset(x, y, i) 参数 x 的地址是: %lx \n", (unsigned long long)&x);
	printf("\nsumset(x, y, i) 参数 y 的地址是: %lx \n", (unsigned long long)&y);
	printf("\nsumset(x, y, i) 参数 i 的地址是: %lx \n", (unsigned long long)&i);
	printf("\nsumset 函数局部变量 a 的地址是:  %lx \n", (unsigned long long)&a);
	printf("\nsumset 函数局部变量 b 的地址是:  %lx \n", (unsigned long long)&b);
	printf("\nsumset 函数局部变量 c 的地址是:  %lx \n\n", (unsigned long long)&c);

	for(int k=0; k<=i+1; k++) {
		printf("c[%d] = %16lx \n", k, (unsigned long long)c[k]);
	}
	printf("\nc[%d] 中的原值 %lx 将被赋予新值 %lx \n", i, (unsigned long long)c[i], (unsigned long long)&test);

	c[i] = (void*)&test;
	return a+b;
}


int main() {

	int a;

	printf("\nmain   函数的地址是: %lx \n", (unsigned long long)&main);
	printf("\nsumset 函数的地址是: %lx \n", (unsigned long long)&sumset);
	printf("\ntest   函数的地址是: %lx \n", (unsigned long long)&test);

	while(1) {

		printf("\n输入一个整数(负数将退出程序):");
		scanf("%d", &a);

		if(a < 0) break;

		printf("\n调用函数 sumset(%d, %d, %d) \n", a, a, a);
		sumset(a, a, a);

	}

	return 0;
}

乍一看这段代码可能不知道它是干嘛的,我们可以编译运行它查看结果

我们可以看到各个函数、参数及局部变量的地址,接着输入

直到输入4完了以后,输入5,程序忽然崩溃而直接结算进程了

是输入4的问题还是输入5的问题?我们单独试试输入5

程序成功运行了,不过执行了test函数,然后结束了,这是为什么呢???

这里先告诉大家输入4之后输入任何数程序都会结束,有兴趣的同学可以自己试一试~(其实是懒得截图)

我们通过调试器产生的汇编代码来分析分析这个过程:)

main函数

   0x000000000040165e <+0>:	push   rbp
   0x000000000040165f <+1>:	mov    rbp,rsp
   0x0000000000401662 <+4>:	sub    rsp,0x30
   0x0000000000401666 <+8>:	call   0x4022a0 <__main>
   0x000000000040166b <+13>:	lea    rax,[rip+0xffffffffffffffec]        # 0x40165e <main()>
   0x0000000000401672 <+20>:	mov    rdx,rax
   0x0000000000401675 <+23>:	lea    rcx,[rip+0x2ad3]        # 0x40414f
   0x000000000040167c <+30>:	call   0x402cc0 <printf>
   0x0000000000401681 <+35>:	lea    rax,[rip+0xfffffffffffffec3]        # 0x40154b <sumset(int, int, int)>
   0x0000000000401688 <+42>:	mov    rdx,rax
   0x000000000040168b <+45>:	lea    rcx,[rip+0x2ad9]        # 0x40416b
   0x0000000000401692 <+52>:	call   0x402cc0 <printf>
   0x0000000000401697 <+57>:	lea    rax,[rip+0xfffffffffffffe92]        # 0x401530 <test()>
   0x000000000040169e <+64>:	mov    rdx,rax
   0x00000000004016a1 <+67>:	lea    rcx,[rip+0x2adf]        # 0x404187
   0x00000000004016a8 <+74>:	call   0x402cc0 <printf>
   0x00000000004016ad <+79>:	lea    rcx,[rip+0x2af4]        # 0x4041a8
   0x00000000004016b4 <+86>:	call   0x402cc0 <printf>
   0x00000000004016b9 <+91>:	lea    rax,[rbp-0x4]
   0x00000000004016bd <+95>:	mov    rdx,rax
   0x00000000004016c0 <+98>:	lea    rcx,[rip+0x2b00]        # 0x4041c7
   0x00000000004016c7 <+105>:	call   0x402cc8 <scanf>
   0x00000000004016cc <+110>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004016cf <+113>:	test   eax,eax
   0x00000000004016d1 <+115>:	jns    0x4016d5 <main()+119>
   0x00000000004016d3 <+117>:	jmp    0x401707 <main()+169>
   0x00000000004016d5 <+119>:	mov    ecx,DWORD PTR [rbp-0x4]
   0x00000000004016d8 <+122>:	mov    edx,DWORD PTR [rbp-0x4]
   0x00000000004016db <+125>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004016de <+128>:	mov    r9d,ecx
   0x00000000004016e1 <+131>:	mov    r8d,edx
   0x00000000004016e4 <+134>:	mov    edx,eax
   0x00000000004016e6 <+136>:	lea    rcx,[rip+0x2ae3]        # 0x4041d0
   0x00000000004016ed <+143>:	call   0x402cc0 <printf>
   0x00000000004016f2 <+148>:	mov    ecx,DWORD PTR [rbp-0x4]
   0x00000000004016f5 <+151>:	mov    edx,DWORD PTR [rbp-0x4]
   0x00000000004016f8 <+154>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004016fb <+157>:	mov    r8d,ecx
   0x00000000004016fe <+160>:	mov    ecx,eax
   0x0000000000401700 <+162>:	call   0x40154b <sumset(int, int, int)>
   0x0000000000401705 <+167>:	jmp    0x4016ad <main()+79>
   0x0000000000401707 <+169>:	mov    eax,0x0
   0x000000000040170c <+174>:	add    rsp,0x30
   0x0000000000401710 <+178>:	pop    rbp
   0x0000000000401711 <+179>:	ret    

 sumset函数

   0x000000000040154b <+0>:	push   rbp
   0x000000000040154c <+1>:	mov    rbp,rsp
   0x000000000040154f <+4>:	sub    rsp,0x40
   0x0000000000401553 <+8>:	mov    DWORD PTR [rbp+0x10],ecx
   0x0000000000401556 <+11>:	mov    DWORD PTR [rbp+0x18],edx
   0x0000000000401559 <+14>:	mov    DWORD PTR [rbp+0x20],r8d
   0x000000000040155d <+18>:	mov    eax,DWORD PTR [rbp+0x10]
   0x0000000000401560 <+21>:	mov    DWORD PTR [rbp-0x8],eax
   0x0000000000401563 <+24>:	mov    eax,DWORD PTR [rbp+0x18]
   0x0000000000401566 <+27>:	mov    DWORD PTR [rbp-0xc],eax
   0x0000000000401569 <+30>:	lea    rax,[rbp+0x10]
   0x000000000040156d <+34>:	mov    rdx,rax
   0x0000000000401570 <+37>:	lea    rcx,[rip+0x2aa9]        # 0x404020
   0x0000000000401577 <+44>:	call   0x402cc0 <printf>
   0x000000000040157c <+49>:	lea    rax,[rbp+0x18]
   0x0000000000401580 <+53>:	mov    rdx,rax
   0x0000000000401583 <+56>:	lea    rcx,[rip+0x2abe]        # 0x404048
   0x000000000040158a <+63>:	call   0x402cc0 <printf>
   0x000000000040158f <+68>:	lea    rax,[rbp+0x20]
   0x0000000000401593 <+72>:	mov    rdx,rax
   0x0000000000401596 <+75>:	lea    rcx,[rip+0x2ad3]        # 0x404070
   0x000000000040159d <+82>:	call   0x402cc0 <printf>
   0x00000000004015a2 <+87>:	lea    rax,[rbp-0x8]
   0x00000000004015a6 <+91>:	mov    rdx,rax
   0x00000000004015a9 <+94>:	lea    rcx,[rip+0x2ae8]        # 0x404098
   0x00000000004015b0 <+101>:	call   0x402cc0 <printf>
   0x00000000004015b5 <+106>:	lea    rax,[rbp-0xc]
   0x00000000004015b9 <+110>:	mov    rdx,rax
   0x00000000004015bc <+113>:	lea    rcx,[rip+0x2afd]        # 0x4040c0
   0x00000000004015c3 <+120>:	call   0x402cc0 <printf>
   0x00000000004015c8 <+125>:	lea    rax,[rbp-0x20]
   0x00000000004015cc <+129>:	mov    rdx,rax
   0x00000000004015cf <+132>:	lea    rcx,[rip+0x2b12]        # 0x4040e8
   0x00000000004015d6 <+139>:	call   0x402cc0 <printf>
   0x00000000004015db <+144>:	mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004015e2 <+151>:	jmp    0x401609 <sumset(int, int, int)+190>
   0x00000000004015e4 <+153>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004015e7 <+156>:	cdqe   
   0x00000000004015e9 <+158>:	mov    rax,QWORD PTR [rbp+rax*8-0x20]
   0x00000000004015ee <+163>:	mov    rdx,rax
   0x00000000004015f1 <+166>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004015f4 <+169>:	mov    r8,rdx
   0x00000000004015f7 <+172>:	mov    edx,eax
   0x00000000004015f9 <+174>:	lea    rcx,[rip+0x2b11]        # 0x404111
   0x0000000000401600 <+181>:	call   0x402cc0 <printf>
   0x0000000000401605 <+186>:	add    DWORD PTR [rbp-0x4],0x1
   0x0000000000401609 <+190>:	mov    eax,DWORD PTR [rbp+0x20]
   0x000000000040160c <+193>:	add    eax,0x1
   0x000000000040160f <+196>:	cmp    eax,DWORD PTR [rbp-0x4]
   0x0000000000401612 <+199>:	jge    0x4015e4 <sumset(int, int, int)+153>
   0x0000000000401614 <+201>:	lea    rdx,[rip+0xffffffffffffff15]   # 0x401530 <test()>
   0x000000000040161b <+208>:	mov    eax,DWORD PTR [rbp+0x20]
   0x000000000040161e <+211>:	cdqe   
   0x0000000000401620 <+213>:	mov    rax,QWORD PTR [rbp+rax*8-0x20]
   0x0000000000401625 <+218>:	mov    rcx,rax
   0x0000000000401628 <+221>:	mov    eax,DWORD PTR [rbp+0x20]
   0x000000000040162b <+224>:	mov    r9,rdx
   0x000000000040162e <+227>:	mov    r8,rcx
   0x0000000000401631 <+230>:	mov    edx,eax
   0x0000000000401633 <+232>:	lea    rcx,[rip+0x2aee]        # 0x404128
   0x000000000040163a <+239>:	call   0x402cc0 <printf>
   0x000000000040163f <+244>:	mov    eax,DWORD PTR [rbp+0x20]
   0x0000000000401642 <+247>:	cdqe   
   0x0000000000401644 <+249>:	lea    rdx,[rip+0xfffffffffffffee5]  # 0x401530 <test()>
   0x000000000040164b <+256>:	mov    QWORD PTR [rbp+rax*8-0x20],rdx
   0x0000000000401650 <+261>:	mov    edx,DWORD PTR [rbp-0x8]
   0x0000000000401653 <+264>:	mov    eax,DWORD PTR [rbp-0xc]
   0x0000000000401656 <+267>:	add    eax,edx
   0x0000000000401658 <+269>:	add    rsp,0x40
   0x000000000040165c <+273>:	pop    rbp
   0x000000000040165d <+274>:	ret 

好了,我们开始这段痛苦的分析过程

   0x000000000040165e <+0>:	push   rbp
   0x000000000040165f <+1>:	mov    rbp,rsp
   0x0000000000401662 <+4>:	sub    rsp,0x30
   0x0000000000401666 <+8>:	call   0x4022a0 <__main>

老样子,main函数调用前先进行初始化,这里顺便介绍下上次没有介绍的call指令

CALL(LCALL)指令执行时,进行两步操作:

(1)将程序下一条指令的位置的IP压入堆栈中;

(2)转移到调用的子程序。

   0x000000000040166b <+13>:	lea    rax,[rip+0xffffffffffffffec]        # 0x40165e <main()>
   0x0000000000401672 <+20>:	mov    rdx,rax
   0x0000000000401675 <+23>:	lea    rcx,[rip+0x2ad3]        # 0x40414f
   0x000000000040167c <+30>:	call   0x402cc0 <printf>

这次也说一下lea指令,lea指令的全称为Load effect address——取有效地址

我们可以在第一条指令的末尾看到main函数的地址 0x40165e,和前面程序运行结果一致。

这4条指令表示的是代码中main函数中的第一个printf

   0x0000000000401681 <+35>:	lea    rax,[rip+0xfffffffffffffec3]        # 0x40154b <sumset(int, int, int)>
   0x0000000000401688 <+42>:	mov    rdx,rax
   0x000000000040168b <+45>:	lea    rcx,[rip+0x2ad9]        # 0x40416b
   0x0000000000401692 <+52>:	call   0x402cc0 <printf>

和前面分析的类似,这里取了sumset函数得到有效地址,表示的是main函数中第二个printf

   0x0000000000401697 <+57>:	lea    rax,[rip+0xfffffffffffffe92]        # 0x401530 <test()>
   0x000000000040169e <+64>:	mov    rdx,rax
   0x00000000004016a1 <+67>:	lea    rcx,[rip+0x2adf]        # 0x404187
   0x00000000004016a8 <+74>:	call   0x402cc0 <printf>

main函数中第3个printf

   0x00000000004016ad <+79>:	lea    rcx,[rip+0x2af4]        # 0x4041a8
   0x00000000004016b4 <+86>:	call   0x402cc0 <printf>

while循环中的第一个printf

   0x00000000004016b9 <+91>:	lea    rax,[rbp-0x4]
   0x00000000004016bd <+95>:	mov    rdx,rax
   0x00000000004016c0 <+98>:	lea    rcx,[rip+0x2b00]        # 0x4041c7
   0x00000000004016c7 <+105>:	call   0x402cc8 <scanf>

这里有点奇怪,前面并没有看到rbp-0x4位置的压栈,不过main函数中代码中只有一个int a;不难看出这里是rbp-0x4是a的地址

整段就是scanf语句,输入a的值

   0x00000000004016cc <+110>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004016cf <+113>:	test   eax,eax
   0x00000000004016d1 <+115>:	jns    0x4016d5 <main()+119>
   0x00000000004016d3 <+117>:	jmp    0x401707 <main()+169>

将DWORD PTR [rbp-0x4]中的值赋给eax寄存器,即eax=a;

test指令的作用的对2个操作数进行AND操作,并根据运算结果设置相关的标志位。

jns则是如果不为负数,就跳转

这两条语句常常用于if指令中,这里jns的作用是如果a≥0则跳转到0x4016d5的地址上,如果a<0,则执行jmp指令,跳转到0x401707的地址上,查看这两个地址可知,0x4016d5地址上的是下一步操作,0x401707地址已经跳出了循环。

   0x00000000004016d5 <+119>:	mov    ecx,DWORD PTR [rbp-0x4]
   0x00000000004016d8 <+122>:	mov    edx,DWORD PTR [rbp-0x4]
   0x00000000004016db <+125>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004016de <+128>:	mov    r9d,ecx
   0x00000000004016e1 <+131>:	mov    r8d,edx
   0x00000000004016e4 <+134>:	mov    edx,eax
   0x00000000004016e6 <+136>:	lea    rcx,[rip+0x2ae3]        # 0x4041d0
   0x00000000004016ed <+143>:	call   0x402cc0 <printf>

这一大段都是printf,不多作分析

   0x00000000004016f2 <+148>:	mov    ecx,DWORD PTR [rbp-0x4]
   0x00000000004016f5 <+151>:	mov    edx,DWORD PTR [rbp-0x4]
   0x00000000004016f8 <+154>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004016fb <+157>:	mov    r8d,ecx
   0x00000000004016fe <+160>:	mov    ecx,eax
   0x0000000000401700 <+162>:	call   0x40154b <sumset(int, int, int)>

终于到子函数了,将DWORD PTR [rbp-0x4]的值赋给3个寄存器,再进行寄存器之间的传值,执行call指令,进入sumset函数,并留下返回地址。

   0x000000000040154b <+0>:	push   rbp
   0x000000000040154c <+1>:	mov    rbp,rsp
   0x000000000040154f <+4>:	sub    rsp,0x40

又是初始化,不过,记住这个0x40,后面有用!

   0x0000000000401553 <+8>:	mov    DWORD PTR [rbp+0x10],ecx
   0x0000000000401556 <+11>:	mov    DWORD PTR [rbp+0x18],edx
   0x0000000000401559 <+14>:	mov    DWORD PTR [rbp+0x20],r8d

x,y,i参数的压栈,即形参的实例化,压栈位置不在sumset开辟的空间中,而在main函数的栈帧中

其中i的地址最高,x的地址最低,可见传参顺序为从右到左

   0x000000000040155d <+18>:	mov    eax,DWORD PTR [rbp+0x10]
   0x0000000000401560 <+21>:	mov    DWORD PTR [rbp-0x8],eax
   0x0000000000401563 <+24>:	mov    eax,DWORD PTR [rbp+0x18]
   0x0000000000401566 <+27>:	mov    DWORD PTR [rbp-0xc],eax

前2句指令是将,DWORD PTR [rbp+0x10]的值赋给eax,再进行压栈,即局部变量的创建,对应代码int a = x;

34句同理,对应代码int b = y;
 

   0x0000000000401569 <+30>:	lea    rax,[rbp+0x10]
   0x000000000040156d <+34>:	mov    rdx,rax
   0x0000000000401570 <+37>:	lea    rcx,[rip+0x2aa9]        # 0x404020
   0x0000000000401577 <+44>:	call   0x402cc0 <printf>
   0x000000000040157c <+49>:	lea    rax,[rbp+0x18]
   0x0000000000401580 <+53>:	mov    rdx,rax
   0x0000000000401583 <+56>:	lea    rcx,[rip+0x2abe]        # 0x404048
   0x000000000040158a <+63>:	call   0x402cc0 <printf>
   0x000000000040158f <+68>:	lea    rax,[rbp+0x20]
   0x0000000000401593 <+72>:	mov    rdx,rax
   0x0000000000401596 <+75>:	lea    rcx,[rip+0x2ad3]        # 0x404070
   0x000000000040159d <+82>:	call   0x402cc0 <printf>
   0x00000000004015a2 <+87>:	lea    rax,[rbp-0x8]
   0x00000000004015a6 <+91>:	mov    rdx,rax
   0x00000000004015a9 <+94>:	lea    rcx,[rip+0x2ae8]        # 0x404098
   0x00000000004015b0 <+101>:	call   0x402cc0 <printf>
   0x00000000004015b5 <+106>:	lea    rax,[rbp-0xc]
   0x00000000004015b9 <+110>:	mov    rdx,rax
   0x00000000004015bc <+113>:	lea    rcx,[rip+0x2afd]        # 0x4040c0
   0x00000000004015c3 <+120>:	call   0x402cc0 <printf>
   0x00000000004015c8 <+125>:	lea    rax,[rbp-0x20]
   0x00000000004015cc <+129>:	mov    rdx,rax
   0x00000000004015cf <+132>:	lea    rcx,[rip+0x2b12]        # 0x4040e8
   0x00000000004015d6 <+139>:	call   0x402cc0 <printf>

6个printf,不讲了

   0x00000000004015db <+144>:	mov    DWORD PTR [rbp-0x4],0x0
   0x00000000004015e2 <+151>:	jmp    0x401609 <sumset(int, int, int)+190>

同样是局部变量的创建,这里是int k = 0;同时跳转到0x401609

   0x0000000000401609 <+190>:	mov    eax,DWORD PTR [rbp+0x20]
   0x000000000040160c <+193>:	add    eax,0x1
   0x000000000040160f <+196>:	cmp    eax,DWORD PTR [rbp-0x4]
   0x0000000000401612 <+199>:	jge    0x4015e4 <sumset(int, int, int)+153>

我们来到401609地址,前两句指令,将DWORD PTR [rbp+0x20]的值赋给寄存器eax,再将eax加1,rbp+0x20对应的是形参i

所以这两句是for循环中的 i+1;

接下去的2句,比较eax和DWORD PTR [rbp-0x4]的值,如果DWORD PTR [rbp-0x4]小于或等于eax的值,进行跳转,否则继续下一条指令,即跳出for循环,这里表示的是如果k≤i+1,则继续for循环,否则跳出

   0x00000000004015e4 <+153>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004015e7 <+156>:	cdqe   
   0x00000000004015e9 <+158>:	mov    rax,QWORD PTR [rbp+rax*8-0x20]
   0x00000000004015ee <+163>:	mov    rdx,rax
   0x00000000004015f1 <+166>:	mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004015f4 <+169>:	mov    r8,rdx
   0x00000000004015f7 <+172>:	mov    edx,eax
   0x00000000004015f9 <+174>:	lea    rcx,[rip+0x2b11]        # 0x404111
   0x0000000000401600 <+181>:	call   0x402cc0 <printf>
   0x0000000000401605 <+186>:	add    DWORD PTR [rbp-0x4],0x1

跳转来到0x4015e4,执行for函数中的printf,接着k++,继续循环判断

   0x0000000000401614 <+201>:	lea    rdx,[rip+0xffffffffffffff15]   # 0x401530 <test()>
   0x000000000040161b <+208>:	mov    eax,DWORD PTR [rbp+0x20]
   0x000000000040161e <+211>:	cdqe   
   0x0000000000401620 <+213>:	mov    rax,QWORD PTR [rbp+rax*8-0x20]
   0x0000000000401625 <+218>:	mov    rcx,rax
   0x0000000000401628 <+221>:	mov    eax,DWORD PTR [rbp+0x20]
   0x000000000040162b <+224>:	mov    r9,rdx
   0x000000000040162e <+227>:	mov    r8,rcx
   0x0000000000401631 <+230>:	mov    edx,eax
   0x0000000000401633 <+232>:	lea    rcx,[rip+0x2aee]        # 0x404128
   0x000000000040163a <+239>:	call   0x402cc0 <printf>

最后一个printf
 

   0x000000000040163f <+244>:	mov    eax,DWORD PTR [rbp+0x20]
   0x0000000000401642 <+247>:	cdqe   
   0x0000000000401644 <+249>:	lea    rdx,[rip+0xfffffffffffffee5]  # 0x401530 <test()>
   0x000000000040164b <+256>:	mov    QWORD PTR [rbp+rax*8-0x20],rdx
   0x0000000000401650 <+261>:	mov    edx,DWORD PTR [rbp-0x8]
   0x0000000000401653 <+264>:	mov    eax,DWORD PTR [rbp-0xc]
   0x0000000000401656 <+267>:	add    eax,edx
   0x0000000000401658 <+269>:	add    rsp,0x40
   0x000000000040165c <+273>:	pop    rbp
   0x000000000040165d <+274>:	ret 

第一条指令,取出DWORD PTR [rbp+0x20]中的值放入寄存器eax中,即eax=i;

接着,lea指令,取test的地址放入rdx寄存器中

QWORD PTR [rbp+rax*8-0x20]对应的是c[i],即将test的地址赋给c[i];

最后几句是取出a和b的值,相加并返回。

 

好了,我们整个过程就先分析到这里,但是,我们仍然没有解决前面的2个问题,为什么输入4后再输入会导致程序停止,为什么输入5会调用test函数

先说明一下void* 指针是8个字节,unsigned long long也是8个字节,观察sumset的汇编代码,没有看到c的压栈过程,但是,有一段QWORD PTR [rbp+rax*8-0x20],0x20即十进制的32,rbp-0x20即开辟了32个字节的空间,虽然在sumset函数中,       void* c[1]只定义了一位的数组,但是实际开辟的空间却足够放32/8=4个,所以在产生越界数组c[1]-c[3]时没有发生任何问题,程序仍正常进行。

但是,当达到c[4]时,没有剩余空间给他了,那怎么办呢,根据上面的程序,我们可以得到c[4] = 62fe50,这是什么地址,在调试器中查看得到,这是main函数的基址寄存器rbp的地址,之后test函数的地址赋值给c[4],即改变main函数基址寄存器的地址为test函数的地址,sumset函数调用结束返回,重新开始while循环,当运行到scanf时,遇到了指令

   0x00000000004016b9 <+91>:	lea    rax,[rbp-0x4]

此时,rbp地址被改变了,rbp-0x4是一个不属于这个程序的存储地址,被操作系统捕获到,然后中止了程序运行。

接着是输入5,查看可得c[5] =  401705,汇编代码中,401705对应的指令如下

   0x0000000000401705 <+167>:	jmp    0x4016ad <main()+79>

这是while循环的跳转指令,也是sumset函数的返回地址,test函数的地址赋值给c[5],即将返回地址修改为test函数的地址,在sumset函数执行完直接返回到test函数,但test函数不是被正常调用的,没有进行初始化等操作,所以test函数的return会返回到不属于这个程序的存储地址去,从而被操作系统捕获到,然后中止了程序运行。

 

void* c[1]开辟的是32位的空间,那void* c[2],void* c[3]呢?修改代码查看调试器得当void* c[i],i的值为1-4时,开辟的空间都为32,当i的值为5-8时,开辟的空间都为64(总开辟空间也随之增加为0x60,即96),可以存放8个;所以当代码为void* c[5]时,输入8和9才能导致返回地址改变
 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值