Buffer Overflow Lab Smashing the Stack One way of manipulating the stack for uni
ID: 3601706 • Letter: B
Question
Buffer Overflow Lab
Smashing the Stack
One way of manipulating the stack for unintended results is to change the return address on the stack to some pre-set location, possibly containing malicious code. When the procedure exits, it returns to whatever memory address is in the return address portion of the stack. If a malicious user can change the values of the stack to affect this address location, they can change which instruction gets executed next.
As we have previously seen, one way of changing memory locations is through a buffer overflow. Remember that local variables are pushed onto the stack above the return address. If one of the local variables is a string, and the user has the opportunity to input a value for the string that overflows into the return address on the stack, they can effectively change where the procedure returns.
EDIT: The simple machine simulator on the left shows a C-like language and corresponding memory. Memory contains both instructions and variables. This exercise has you identifying which locations are instructions and which are data.
1. Step through the sample program and construct the necessary input string in the procedure to affect where the procedure returns to, by skipping the last output statement. Your output window should say "*** END OF PROGRAM ***", not "*** ILLEGAL INSTRUCTION ***".
Note that only the last three hex digits of the return address are used (there is no typable ASCII character with the hex value 04).
Show the string you used, your reason for using the string, and a screenshot of the code, output, and memory that illustrates the stack and your clobbered return.
Explanation / Answer
void foo(int**matrix,int n,int m);
int main(){
int data[10][5];
// do something on data
foo(data,10,5);
}
Although an int[10][5] can be converted to an (*int)[5], it cannot be converted to int**. Therefore you may need to hard-code the array bound in the function declaration:
// BAD
void foo(int(*matrix)[5],int n,int m);
int main(){
int data[10][5];
// do something on data
foo(data,10,5);
}
To make the function more generic, templates and function overloading should be used:
// GOOD
template<int junk,int rubbish>void foo(int(&matrix)[junk][rubbish],int n,int m);
void foo(int**matrix,int n,int m);
int main(){
int data[10][5];
// do something on data
foo(data,10,5);
}
The reason for having n and m in the first version is mainly for consistency, and also deal with the case that the array allocated is not used completely. It may also be used for checking buffer overflows by comparing n/m with junk/rubbish.
by value[edit]
When we want to write a function which the value of the argument is independent to the passed variable, we use pass-by-value approach.
int add(int num1, int num2)
{
num1 += num2; // change of value of "num1"
return num1;
}
int main()
{
int a = 10, b = 20, ans;
ans = add(a, b);
std::cout << a << " + " << b << " = " << ans << std::endl;
}
Output:
10 + 20 = 30
The above example shows a property of pass-by-value, the arguments are copies of the passed variable and only in the scope of the corresponding function. This means that we have to afford the cost of copying. However, this cost is usually considered only for larger and more complex variables.
In this case, the values of "a" and "b" are copied to "num1" and "num2" on the function "add()". We can see that the value of "num1" is changed in line 3. However, we can also observe that the value of "a" is kept after passed to this function.
Constant Parameters[edit]
The keyword const can also be used as a guarantee that a function will not modify a value that is passed in. This is really only useful for references and pointers (and not things passed by value), though there's nothing syntactically to prevent the use of const for arguments passed by value.
Take for example the following functions:
void foo(const std::string &s)
{
s.append("blah"); // ERROR -- we can't modify the string
std::cout << s.length() << std::endl; // fine
}
void bar(const Widget *w)
{
w->rotate(); // ERROR - rotate wouldn't be const
std::cout << w->name() << std::endl; // fine
}
In the first example we tried to call a non-const method -- append() -- on an argument passed as a const reference, thus breaking our agreement with the caller not to modify it and the compiler will give us an error.
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.