欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

栈帧应用

程序员文章站 2022-07-07 12:10:12
...

1、不通过调用函数来执行函数;//通过修改函数完成后返回其调用函数的断点地址

#include<stdio.h>
#include<windows.h>

void bug()
{
	printf("helloBug\n");
	system("pause");
}

int my_add(int a, int b)
{
	int z = 0;
	//分析栈帧可知返回main函数的地址在第一个临时变量的下一个地址(栈自上而下)
	//函数调用参数列表自右而左的实例化
	//所以此时返回main函数的地址在a的下一个地址
	int *p = &a;
	p--;//指针变量减1,实质是减去所指向变量类型的大小4
	//修改返回地址
	*p = (int)bug;
	z = a + b;
	printf("my_add run!\n");
	return z;
}

int main()
{
	int a = 0XAAAAAAAA;
	int b = 0xFFFFFFFF;
	int add = 0;

	printf("main run!\n");
	add = my_add(a,b);
	printf("函数调完返回!\n");

	printf("add = %d\n",add);
	system("pause");
	return 0;
}

运行结果:可以看出并没调用的bug函数也被执行了
栈帧应用
但是!!!也出现了此错误

栈帧应用

原因:没回到main函数这个调用其他的函数的函数

优化:在bug函数调完后返回到main函数的断点地址处(即为调函数的下一条指令的地址)
#include<stdio.h>
#include<windows.h>

int *mainBreakPoint;

void bug()
{
	int b;//声明在此函数的栈帧中,分析栈帧可知,&b+8即为存储返回地址的地址
	int *p = &b;
	p+=2;
	*p = mainBreakPoint;
	printf("helloBug\n");
	system("pause");
}

int my_add(int a, int b)
{
	int z = 0;
	//分析栈帧可知返回main函数的地址在第一个临时变量的下一个地址(栈自上而下)
	//函数调用参数列表自右而左的实例化
	//所以此时返回main函数的地址在a的下一个地址
	int *p = &a;
	p--;//指针变量减1,实质是减去所指向变量类型的大小4
	//在修改返回地址之前,先将之前的地址保存
	mainBreakPoint = *p;
	//修改返回地址
	*p = (int)bug;
	z = a + b;
	printf("my_add run!\n");
	return z;
}

int main()
{
	int a = 0XAAAAAAAA;
	int b = 0xFFFFFFFF;
	int add = 0;

	printf("main run!\n");
	add = my_add(a,b);
	printf("函数调完返回!\n");

	printf("add = %d\n",add);
	
	system("pause");
	return 0;
}

运行结果:可以看出在bug函数调完后又回到了main函数执行
栈帧应用
但是!!!也出现了此错误。
栈帧应用
原因:函数正常调用是通过call指令来调用的,
而call指令的作用就是将当前执行指令的下一条指令的地址压栈,再修改eip的值;
此bug函数没有通过call来调用而是直接修改返回地址的值;
但是在每个函数的最后一条指令ret指令的作用是将出栈并保存至eip;
所以:结论就是,bug函数只有ret没有call(即为没有push却有pop):导致栈帧结构错误

优化:将栈顶指针下移4;

栈帧应用

运行结果:无错。

栈帧应用
2实现一个函数,可以左旋字符串中的k个字符。
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
3判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:给定s1 = AABCD和s2 = BCDAA,返回1,给定s1=abcd和s2=ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<windows.h>

#pragma warning(disable:4996)

//左旋字符串中的k个字符
//此处注意左旋的k肯定是小于len的,所以:k = len%k;
//方法1
char *leftRotate(char *str, int len, int k)
{
	assert(str);
	assert(len > 0);

	//左旋次数
	k = len % k; 
	for (int i = 0; i < k; i++){
		char first = str[0]; //保存第一个
		int j = 0;
		for (; j < len - 1; j++){//再将后面的都前移1
			str[j] = str[j + 1];
		}
		str[j] = first;//将保存的第一个放在最后面
	}
	return str;
}

//逆置  
void reverse(char* pre, char* aft)
{
	assert(pre);
	assert(aft);

	while (pre < aft){
		*pre ^= *aft;
		*aft ^= *pre;
		*pre ^= *aft;
		pre++, aft--;
	}
}

//方法2:先局部逆置再整体逆置
char *leftRotate2(char *str, int len, int k)
{
	assert(str);
	assert(len > 0);

	k = len % k;
	reverse(str, str + k - 1);//根据k将字符串分为两部分;先将前一部分逆置
	reverse(str + k, str + len - 1);//再将后一部分逆置
	reverse(str, str + len - 1);//最后再将整体逆置
	return str;
}

//方法3:穷举
//abcdefabcdef:左旋3只需要从开始处获取len的字符即可得到结果
char *leftRotate3(char *str, int len, int k)
{
	assert(str);
	assert(len > 0);

	k = len % k;
	char *strres;
	strres = (char *)malloc(sizeof(char) * len * 2 + 1);
	if (!strres){//动态申请失败
		return;
	}
	strcpy(strres, str);
	strcat(strres, str);
	//memmove(str, strres + k, len);
	strncpy(str, strres + k, len);

	free(strres);//记得释放空间

	return str;
}

//判断一个字符串是否为另外一个字符串旋转之后的字符串
//右旋r = len - 左旋l
//方法1
int judge(char *s1, char *s2, int len)
{
	for (int i = 0; i < len; i++){//0:就代表s1字符串本身
		if (!strcmp(s2, leftRotate(s1, len, i))){//i:代表s1左旋个数
			return 1;
		}
	}
	return 0;
}

//方法2:根据上题方法3可知:构造出s1的双重字符串,判断s2是否为其子串即可
int judge2(char *s1, char *s2, int len)
{
	assert(s1);
	assert(s2);
	assert(len > 0);

	char *strres;
	strres = (char *)malloc(sizeof(char) * len * 2 + 1);
	if (!strres){//动态申请失败
		return;
	}
	strcpy(strres, s1);
	strcat(strres, s1);
	if (strstr(strres, s2)){//返回子串首次出现的首地址(找到)或NULL(未找到)
		return 1;
	}
	else{
		return 0;
	}
}

int main()
{
	//char str[] = "abcdef";
	//int len = strlen(str);
	//int k = 3;
	////printf("%s\n",leftRotate(str, len, k));//真正的左旋字符数为k%len
	////printf("%s\n", leftRotate2(str, len, k));
	//printf("%s\n", leftRotate3(str, len, k));

	char *s1 = "AABAD";
	char *s2 = "ADAAB";
	int len = strlen(s1);
	//printf("%d\n", judge(s1, s2, len));
	printf("%d\n", judge2(s1, s2, len));

	system("pause");
	return 0;
}

相关标签: 栈帧