我对C有一个非常基本的理解(尽管我一般都理解编程概念)。我有一个创建缓冲区溢出的任务,产生某些东西(比如访问未经授权的区域,免费资金等),而不仅仅是程序崩溃。
我尝试过不同大小的缓冲区并且总是可以“崩溃”程序,但我无法启动任何代码(即/ bin / su)。我接近这个错误吗?
以下是代码:
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <string.h>
#define BUFSIZE 20
int main() {
int month=12;
int day=31;
int year=2016;
int confirm = 0;
double dollars = 5.00;
char *sitenum="97871";
char acctnum[BUFSIZE];
printf("Welcome to the Acme AP-AR System. This is the Accounts Receivable module. \n");
/* Gathering date information */
printf("Please enter the month of transaction as an integer value (2 digits). \n");
printf("For example, July would be 07, December would be 12. Please input the month: ");
for (;;) { /* Start of month input validation loop */
scanf("%d", &month);
if(month>=1 && month<=12) {
printf("Validated. \n");
break;
}
else {
printf("Please enter a value between 1 and 12! \n");
continue;
}
} /* End of month input validation loop */
printf("\nPlease enter the day of transaction as an integer value (2 digits). \n");
printf("For example, the 3rd would be 03, the 25th would be 25. Please input the day: ");
for (;;) { /* Start of day input validation loop */
scanf("%d", &day);
if(day>=1 && day<=31) {
printf("Validated. \n");
break;
}
else {
printf("Please enter a value between 1 and 31! \n");
continue;
}
} /* End of day input validation loop */
/* Gathering sender account number */
printf("\nPlease enter the sender Account Number: ");
scanf("%s", acctnum);
/* Gathering transaction amount */
printf("\nPlease enter the USD amount (including cents) received: $ ");
scanf("%lf", &dollars);
/* Confirming data entry */
printf("\nTransaction information.\n Date: %d-%d-%d \n", month,day,year);
printf("Account: %s-%s \n", sitenum, acctnum);
printf(" Amount: $ %.2lf \n", dollars);
printf("\nProcess transaction information? (Yes=1/No=0) ");
for (;;) { /* Start of confirmation validation loop */
scanf("%d", &confirm);
if(confirm==1) {
printf("Transaction processed. \n");
break;
}
else {
printf("Transaction voided! \n");
break;
}
} /* End of confirmation validation loop */
return (EXIT_SUCCESS);
}
执行时,如果您为月中输入25个字符,程序将一直持续到结束。只有在最后一次输入后才会以堆栈粉碎错误终止。我担心我会尝试做一些无法做到的事情,但谷歌搜索的一天(字面上,过去8小时)并没有产生我能够使用的例子。
有人能把我推向一个不同的方向,这会让我接近我想要实现的目标吗?感谢。
答案 0 :(得分:0)
您需要深入了解目标体系结构(x86,x86-64等)才能实现这一目标。一种典型的方法是小心地构造缓冲区溢出的内容,以便1)包含您希望在输入数据被重新解释为机器指令时运行的代码,以及2)覆盖堆栈帧的返回地址以便它跳转进入你的代码,而不是返回到调用函数。
我不太愿意提供实际执行此操作的代码,但它确实可以做到。
编辑:顺便说一句,我不认为该作业旨在要求实际运行任意代码。我根据你发布的代码进行猜测,你应该只是覆盖部分堆栈,这样你就可以访问不同的&#34; sitenum&#34;。这肯定是可能的,因为sitenum指针将在堆栈中的acctnum之后存储(至少通常)。因此,如果您仔细制作缓冲区溢出,则可以将sitenum指针更改为指向其他位置。例如,(假设sitenum指针紧跟在堆栈中的acctnum之后),您可以在acctnum中输入1个额外字符,并且空终止字符将覆盖sitenum指针的最低有效字节,这很可能指向a不同的位置。在我看来,这是一个糟糕的任务,因为1)堆栈可以根据大量因素进行不同的排列,以及2)大多数现代开发环境将默认添加运行时检查以防止这种情况堆栈损坏。例如,在MS Visual C ++中,您必须不遗余力地禁用基本运行时检查和缓冲区安全检查功能以避免异常。
无论如何,希望有所帮助。
答案 1 :(得分:0)
这是一个简单的例子,它覆盖堆栈上的返回地址以执行另一个函数(然后会立即崩溃)。适用于x86上的Windows VS2015。
#include "stdafx.h"
void hello()
{
printf("hello world!\n");
}
void run(int a)
{
int * ret = &a;
--ret; // stack grows downward on x86
*ret = (int)hello;
}
int main()
{
int a = 42;
run(a);
printf("this won't print\n");
}
答案 2 :(得分:0)
这是另一个简单的示例(VS2015 / x86),它首先保存返回地址,然后在执行hello()之后,将返回地址返回到堆栈上的main()。请注意,它首先使用run()中声明的局部变量启动,而不是作为参数传入的局部变量。它归结为理解返回地址,参数传递的顺序,堆栈的方向以及当前堆栈帧的起始位置。执行后,您可能会收到调试器环境中运行时检查失败的通知,但是您应该看到它打印到控制台:
你好世界 主
#include "stdafx.h"
int saveret;
void hello()
{
int a = 43;
printf("hello world!\n");
// put saved return address to main() back on stack
int * ret = &a;
ret += 4;
*ret = saveret;
}
void run()
{
int a = 42;
int * ret = &a;
ret += 4; // stack grows downward on x86
saveret = (int)*ret;
*ret = (int)hello;
}
int main()
{
run();
printf("main\n");
}