栈帧应用
程序员文章站
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
例如:给定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;
}
上一篇: android 获取视频某一时间的帧,并获取bitmap
下一篇: pwn调整栈帧的技巧