My question is why when i set a breakpoint in main() with GDB i receive the error
<0xffffffffffffe550: Cannot access memory at address 0xffffffffffffe550>
I wanted to set a breakpoint in main() so i could examine the memory in the stack. My disassembled code is this:
0x00000000004008e8 <+0>: push %rbp
0x00000000004008e9 <+1>: mov %rsp,%rbp
0x00000000004008ec <+4>: add $0xffffffffffffff80,%rsp
0x00000000004008f0 <+8>: mov %edi,-0x74(%rbp)
0x00000000004008f3 <+11>: mov %rsi,-0x80(%rbp)
=> 0x00000000004008f7 <+15>: movl $0x1,-0x4(%rbp)
0x00000000004008fe <+22>: cmpl $0x1,-0x74(%rbp)
0x0000000000400902 <+26>: jle 0x400920 <main+56>
0x0000000000400904 <+28>: mov -0x80(%rbp),%rax
0x0000000000400908 <+32>: add $0x8,%rax
0x000000000040090c <+36>: mov (%rax),%rdx
0x000000000040090f <+39>: lea -0x70(%rbp),%rax
0x0000000000400913 <+43>: mov %rdx,%rsi
0x0000000000400916 <+46>: mov %rax,%rdi
0x0000000000400919 <+49>: callq 0x400670 <strcpy#plt>
0x000000000040091e <+54>: jmp 0x400924 <main+60>
0x0000000000400920 <+56>: movb $0x0,-0x70(%rbp)
0x0000000000400924 <+60>: callq 0x4006a0 <getuid#plt>
0x0000000000400929 <+65>: mov %eax,-0x8(%rbp)
0x000000000040092c <+68>: mov $0x0,%esi
0x0000000000400931 <+73>: mov $0x400c6e,%edi
0x0000000000400936 <+78>: mov $0x0,%eax
0x000000000040093b <+83>: callq 0x400720 <open#plt>
0x0000000000400940 <+88>: mov %eax,-0xc(%rbp)
0x0000000000400943 <+91>: cmpl $0xffffffff,-0xc(%rbp)
0x0000000000400947 <+95>: jne 0x40096b <main+131>
0x0000000000400949 <+97>: mov $0x400c80,%edi
0x000000000040094e <+102>: callq 0x400856 <fatal>
0x0000000000400953 <+107>: jmp 0x40096b <main+131>
0x0000000000400955 <+109>: lea -0x70(%rbp),%rdx
0x0000000000400959 <+113>: mov -0x8(%rbp),%ecx
0x000000000040095c <+116>: mov -0xc(%rbp),%eax
0x000000000040095f <+119>: mov %ecx,%esi
0x0000000000400961 <+121>: mov %eax,%edi
0x0000000000400963 <+123>: callq 0x40098c <print_notes>
0x0000000000400968 <+128>: mov %eax,-0x4(%rbp)
0x000000000040096b <+131>: cmpl $0x0,-0x4(%rbp)
0x000000000040096f <+135>: jne 0x400955 <main+109>
0x0000000000400971 <+137>: mov $0x400cb0,%edi
0x0000000000400976 <+142>: callq 0x400680 <puts#plt>
0x000000000040097b <+147>: mov -0xc(%rbp),%eax
0x000000000040097e <+150>: mov %eax,%edi
0x0000000000400980 <+152>: callq 0x4006e0 <close#plt>
0x0000000000400985 <+157>: mov $0x0,%eax
0x000000000040098a <+162>: leaveq
0x000000000040098b <+163>: retq
And the code until my main is this:
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "usefullfuncs.h"
#define FILENAME "/var/notes"
int print_notes(int, int, char *);
int find_user_note(int,int);
int search_note(char *, char *);
int main(int argc, char *argv[]){
int userid,printing=1,fd;
char searchstring[100];
if(argc>1)
strcpy(searchstring,argv[1]);
else
searchstring[0] = 0;
userid = getuid();
fd = open(FILENAME,O_RDONLY);
if(fd == -1)
fatal("in main() while opening file for reading");
while(printing)
printing = print_notes(fd,userid,searchstring);
printf("-------[ end of note data ]-------\n");
close(fd);
}
int print_notes(int fd,int uid,char *searchstring){
int note_lenght;
char note_buffer[100];
note_lenght = find_user_note(fd,uid);
if(note_lenght == -1)
return 0;
read(fd,note_buffer,note_lenght);
note_buffer[note_lenght] = 0;
if(search_note(note_buffer,searchstring))
printf(note_buffer);
return 1;
}
int find_user_note(int fd,int user_uid){
int note_uid=-1;
unsigned char byte;
int lenght;
while(note_uid != user_uid){
if(read(fd,¬e_uid,4)!=4)
return -1;
if(read(fd,&byte,1)!=1)
return -1;
byte = lenght = 0;
while(byte != '\n'){
if(read(fd,&byte,1)!=1)
return -1;
lenght++;
}
}
lseek(fd,lenght*-1,SEEK_CUR);
printf("[DEBUG] found a %d byte note for user id %d\n",lenght,note_uid);
return lenght;
}
int search_note(char *note, char *keyword){
int i,keyword_lenght,match=0;
keyword_lenght = strlen(keyword);
if(keyword_lenght == 0)
return 1;
for(i=0;i < strlen(note);i++){
if(note[i] == keyword[match])
match++;
else{
if(note[i] == keyword[0])
match = 1;
else
match = 0;
}
if(match == keyword_lenght)
return 1;
}
return 0;
}
Thanks in advance.
(gdb) x/24s $esp
0xffffffffffffe550: <error: Cannot access memory at address 0xffffffffffffe550>
On an x86-64 target, $rsp should be used. Using $esp will lead to incorrect results.
$esp is taken from the bottom 32 bits of the 64-bit $rsp register, and gdb treats it as type int32_t. $rsp in your example was probably 0x7fffffffe550. Gdb's x command, which wants to use a 64-bit address, will take the bottom 32 bits of $rsp, 0xffffe550, and sign-extend that to 0xffffffffffffe550. That's almost certainly an invalid address; typical user-space addresses on Linux don't go above 0x7ffffffff000 or so.
Try x/24s $rsp. If you're trying to follow exercises out of an old book, you may be able to duplicate their 32-bit examples by giving gcc the -m32 option, if it supports it. Then you can use $esp.
Related
So a little background. I am a beginner with c and assembly code, we have an "bomb" assignment (written in c)which calls methods that require certain passwords, but the code is not visible and I need to determine the correct password by looking at the assembly code.
The code indicates the password for this method is 6 numbers, which is passed as "input" to method phase 2 (I am trying to avoid triggering ).
I am having trouble understanding what is going on here so if anyone can help me translate this into C code, or if i need to look in any particular registers/locations it would help greatly. There are 4 more phases which are each supposed to be more complex so I want to get a good understanding in how to approach reading these.
Also if anyone has a good resource (like a printable table) with assembly code keywords that would be helpful too, and also if there are any differences between 32-bit and 64-bit registers i need to worry about other than the register names..
(gdb) disas
Dump of assembler code for function phase_2:
0x0000000000400f49 <+0>: push %rbp
0x0000000000400f4a <+1>: push %rbx
0x0000000000400f4b <+2>: sub $0x28,%rsp
0x0000000000400f4f <+6>: mov %fs:0x28,%rax
0x0000000000400f58 <+15>: mov %rax,0x18(%rsp)
0x0000000000400f5d <+20>: xor %eax,%eax
0x0000000000400f5f <+22>: mov %rsp,%rsi
0x0000000000400f62 <+25>: callq 0x401708 <read_six_numbers>
0x0000000000400f67 <+30>: cmpl $0x0,(%rsp)
0x0000000000400f6b <+34>: jne 0x400f74 <phase_2+43>
0x0000000000400f6d <+36>: cmpl $0x1,0x4(%rsp)
0x0000000000400f72 <+41>: je 0x400f79 <phase_2+48>
0x0000000000400f74 <+43>: callq 0x4016d2 <explode_bomb>
0x0000000000400f79 <+48>: mov %rsp,%rbx
0x0000000000400f7c <+51>: lea 0x10(%rsp),%rbp
0x0000000000400f81 <+56>: mov 0x4(%rbx),%eax
0x0000000000400f84 <+59>: add (%rbx),%eax
0x0000000000400f86 <+61>: cmp %eax,0x8(%rbx)
0x0000000000400f89 <+64>: je 0x400f90 <phase_2+71>
=> 0x0000000000400f8b <+66>: callq 0x4016d2 <explode_bomb>
0x0000000000400f90 <+71>: add $0x4,%rbx
0x0000000000400f94 <+75>: cmp %rbp,%rbx
0x0000000000400f97 <+78>: jne 0x400f81 <phase_2+56>
0x0000000000400f99 <+80>: mov 0x18(%rsp),%rax
0x0000000000400f9e <+85>: xor %fs:0x28,%rax
0x0000000000400fa7 <+94>: je 0x400fae <phase_2+101>
0x0000000000400fa9 <+96>: callq 0x400b90 <__stack_chk_fail#plt>
0x0000000000400fae <+101>: add $0x28,%rsp
0x0000000000400fb2 <+105>: pop %rbx
0x0000000000400fb3 <+106>: pop %rbp
0x0000000000400fb4 <+107>: retq
End of assembler dump.
Your assembly is equivalent to this, see phase_2 function
#include <stdio.h>
__attribute__((noinline)) void read_six_numbers(void *xxx, int *num)
{
num[0] = 0;
num[1] = 1;
num[2] = 1;
num[3] = 2;
num[4] = 3;
num[5] = 5;
}
__attribute__((noinline)) void explode_bomb()
{
printf("explode_bomb.\n");
}
void phase_2(void *xxx)
{
int num[6];
int i;
read_six_numbers(xxx, num);
if (num[0] != 0 || num[1] != 1)
explode_bomb();
for (i = 0; i < 4; i++) {
if (num[i] + num[i + 1] == num[i + 2])
continue;
explode_bomb();
}
}
int main()
{
phase_2(NULL);
return 0;
}
Consider the code:
#include <stdio.h>
#include <stdlib.h>
int update (int *arr, int size);
#define SIZE 10
int main() { // <---------------------- Breakpoint 1
int x[SIZE];
// Initialize array
for (int c = 0 ; c < SIZE ; c++) {
x[c] = c * 2;
}
// Do some random updates to an array
update((int*) &x, SIZE);
// Print the elements
for (int c = 0 ; c < SIZE ; c++) {
printf("%d\n", x[c]);
}
return EXIT_SUCCESS;
} // <----------------------Breakpoint 2
int update (int *arr, int size) {
for (int i = 0 ; i < size ; i++) {
arr[i] += i;
update(arr+i, size-1);
}
return 1;
}
Result of running info frame at breakpoint 1:
Stack level 0, frame at 0x7ffc176b2610:
rip = 0x56434b0d76b8 in main (array.c:12); saved rip = 0x7f8190fb92b1
source language c.
Arglist at 0x7ffc176b2600, args:
Locals at 0x7ffc176b2600, Previous frame's sp is 0x7ffc176b2610
Saved registers:
rbp at 0x7ffc176b2600, rip at 0x7ffc176b2608
Result of running info frame at breakpoint 2:
Stack level 0, frame at 0x7ffc176b2610:
rip = 0x56434b0d771a in main (array.c:24); saved rip = 0x2d28490fd6501
source language c.
Arglist at 0x7ffc176b2600, args:
Locals at 0x7ffc176b2600, Previous frame's sp is 0x7ffc176b2610
Saved registers:
rbp at 0x7ffc176b2600, rip at 0x7ffc176b2608
We see that main()'s saved return address rip at 0x7ffc176b2608 is mutated from 0x7f8190fb92b1 to 0x2d28490fd6501 between the two breakpoints.
However, setting a watchpoint on the address of rip with watch * 0x7ffc176b2608 and running the executable anew does not pause execution between the breakpoints as expected.
How can this be?
-----------EDIT-----------
Output of disassemble /s main:
Dump of assembler code for function main:
array.c:
8 int main() {
0x000056434b0d76b0 <+0>: push rbp
0x000056434b0d76b1 <+1>: mov rbp,rsp
0x000056434b0d76b4 <+4>: sub rsp,0x30
9 int x[SIZE];
10
11 // Initialize array
12 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d76b8 <+8>: mov DWORD PTR [rbp-0x4],0x0
0x000056434b0d76bf <+15>: jmp 0x56434b0d76d4 <main+36>
13 x[c] = c * 2;
0x000056434b0d76c1 <+17>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d76c4 <+20>: lea edx,[rax+rax*1]
0x000056434b0d76c7 <+23>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d76ca <+26>: cdqe
0x000056434b0d76cc <+28>: mov DWORD PTR [rbp+rax*4-0x30],edx
12 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d76d0 <+32>: add DWORD PTR [rbp-0x4],0x1
0x000056434b0d76d4 <+36>: cmp DWORD PTR [rbp-0x4],0x9
0x000056434b0d76d8 <+40>: jle 0x56434b0d76c1 <main+17>
14 }
15
16 // Do some random updates to an array
17 update((int*) &x, SIZE);
0x000056434b0d76da <+42>: lea rax,[rbp-0x30]
0x000056434b0d76de <+46>: mov esi,0xa
0x000056434b0d76e3 <+51>: mov rdi,rax
0x000056434b0d76e6 <+54>: call 0x56434b0d7721 <update>
18
19 // Print the elements
20 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d76eb <+59>: mov DWORD PTR [rbp-0x8],0x0
0x000056434b0d76f2 <+66>: jmp 0x56434b0d7714 <main+100>
21 printf("%d\n", x[c]);
0x000056434b0d76f4 <+68>: mov eax,DWORD PTR [rbp-0x8]
0x000056434b0d76f7 <+71>: cdqe
0x000056434b0d76f9 <+73>: mov eax,DWORD PTR [rbp+rax*4-0x30]
0x000056434b0d76fd <+77>: mov esi,eax
0x000056434b0d76ff <+79>: lea rdi,[rip+0x12e] # 0x56434b0d7834
0x000056434b0d7706 <+86>: mov eax,0x0
0x000056434b0d770b <+91>: call 0x56434b0d7560 <printf#plt>
20 for (int c = 0 ; c < SIZE ; c++) {
0x000056434b0d7710 <+96>: add DWORD PTR [rbp-0x8],0x1
0x000056434b0d7714 <+100>: cmp DWORD PTR [rbp-0x8],0x9
0x000056434b0d7718 <+104>: jle 0x56434b0d76f4 <main+68>
22 }
23
24 return EXIT_SUCCESS;
=> 0x000056434b0d771a <+106>: mov eax,0x0
25 }
0x000056434b0d771f <+111>: leave
0x000056434b0d7720 <+112>: ret
End of assembler dump.
Output of disassemble /s update:
Dump of assembler code for function update:
array.c:
27 int update (int *arr, int size) {
0x000056434b0d7721 <+0>: push rbp
0x000056434b0d7722 <+1>: mov rbp,rsp
0x000056434b0d7725 <+4>: sub rsp,0x20
0x000056434b0d7729 <+8>: mov QWORD PTR [rbp-0x18],rdi
0x000056434b0d772d <+12>: mov DWORD PTR [rbp-0x1c],esi
28 for (int i = 0 ; i < size ; i++) {
0x000056434b0d7730 <+15>: mov DWORD PTR [rbp-0x4],0x0
0x000056434b0d7737 <+22>: jmp 0x56434b0d7793 <update+114>
29 arr[i] += i;
0x000056434b0d7739 <+24>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d773c <+27>: cdqe
0x000056434b0d773e <+29>: lea rdx,[rax*4+0x0]
0x000056434b0d7746 <+37>: mov rax,QWORD PTR [rbp-0x18]
0x000056434b0d774a <+41>: add rax,rdx
0x000056434b0d774d <+44>: mov edx,DWORD PTR [rbp-0x4]
0x000056434b0d7750 <+47>: movsxd rdx,edx
0x000056434b0d7753 <+50>: lea rcx,[rdx*4+0x0]
0x000056434b0d775b <+58>: mov rdx,QWORD PTR [rbp-0x18]
0x000056434b0d775f <+62>: add rdx,rcx
0x000056434b0d7762 <+65>: mov ecx,DWORD PTR [rdx]
0x000056434b0d7764 <+67>: mov edx,DWORD PTR [rbp-0x4]
0x000056434b0d7767 <+70>: add edx,ecx
0x000056434b0d7769 <+72>: mov DWORD PTR [rax],edx
30 update(arr+i, size-1);
0x000056434b0d776b <+74>: mov eax,DWORD PTR [rbp-0x1c]
0x000056434b0d776e <+77>: lea edx,[rax-0x1]
0x000056434b0d7771 <+80>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d7774 <+83>: cdqe
0x000056434b0d7776 <+85>: lea rcx,[rax*4+0x0]
0x000056434b0d777e <+93>: mov rax,QWORD PTR [rbp-0x18]
0x000056434b0d7782 <+97>: add rax,rcx
0x000056434b0d7785 <+100>: mov esi,edx
0x000056434b0d7787 <+102>: mov rdi,rax
0x000056434b0d778a <+105>: call 0x56434b0d7721 <update>
28 for (int i = 0 ; i < size ; i++) {
0x000056434b0d778f <+110>: add DWORD PTR [rbp-0x4],0x1
0x000056434b0d7793 <+114>: mov eax,DWORD PTR [rbp-0x4]
0x000056434b0d7796 <+117>: cmp eax,DWORD PTR [rbp-0x1c]
0x000056434b0d7799 <+120>: jl 0x56434b0d7739 <update+24>
31 }
32 return 1;
0x000056434b0d779b <+122>: mov eax,0x1
33 }
0x000056434b0d77a0 <+127>: leave
0x000056434b0d77a1 <+128>: ret
End of assembler dump.
Contents of ~/.gdbinit
# Security
set auto-load safe-path /
# Misc
set disassembly-flavor intel
set disable-randomization off
set pagination off
set follow-fork-mode child
# History
set history filename ~/.gdbhistory
set history save
set history expansion
disp/10i $pc
handle SIGXCPU SIG33 SIG35 SIGPWR nostop noprint
set tui enable
It is likely that this line in your .gdbinit is the source of your troubles:
set disable-randomization off
By default, GDB disables address space layout randomization (ASLR). That means that the binary under GDB starts at exactly the same address, with exactly the same stack pointer every time it runs. This is on by default precisely so you can set a watchpoints and breakpoints on a given address, and have it fire on each run.
By setting disable-randomization off, you are asking GDB to run your binary the same way it would run outside of GDB, i.e. with ASLR enabled. Now the location of stack variables (and globals for a PIE binary that you have) will change from run to run, and setting a watchpoint on a given stack address will only work randomly and rarely.
You can confirm that that is the cause by issuing info frame and run several times. You'll observe that the location where registers are saved changes between runs.
TL;DR: Don't put settings you don't completely understand into your .gdbinit.
the problem with the code is the clobbering of the stack.
The comment by #Mark Plotnick clarifies the problem and includes a suggestion on how to fix it.
Entire code is:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
char *secret = "1234";
void go_shell()
{
char *shell = "/bin/sh";
char *cmd[] = { "/bin/sh", 0};
printf("WOuld you like to play a game?\n");
setreuid(0, 0);
execve(shell, cmd, 0);
}
int authorize()
{
char password[64];
printf("Enter password: ");
gets(password);
if (!strcmp(password, secret))
return 1;
else
return 0;
}
int main()
{
if (authorize()) {
printf("Login successfully\n");
go_shell();
} else {
printf("Incorrect password\n");
}
return 0;
}
I want to see the $ebp when authorize() is just called.
Compiled with
gcc -Wall -ggdb <source_code> -o <exe>
Then I started gdb
gdb <exe>
(gdb) disass authorize
Dump of assembler code for function authorize:
0x000000000040076d <+0>: push %rbp
0x000000000040076e <+1>: mov %rsp,%rbp
0x0000000000400771 <+4>: sub $0x50,%rsp
0x0000000000400775 <+8>: mov %fs:0x28,%rax
0x000000000040077e <+17>: mov %rax,-0x8(%rbp)
0x0000000000400782 <+21>: xor %eax,%eax
0x0000000000400784 <+23>: mov $0x4008d8,%edi
0x0000000000400789 <+28>: mov $0x0,%eax
0x000000000040078e <+33>: callq 0x400590 <printf#plt>
0x0000000000400793 <+38>: lea -0x50(%rbp),%rax
0x0000000000400797 <+42>: mov %rax,%rdi
0x000000000040079a <+45>: mov $0x0,%eax
0x000000000040079f <+50>: callq 0x4005d0 <gets#plt>
0x00000000004007a4 <+55>: mov 0x2008bd(%rip),%rdx # 0x601068 <secret>
0x00000000004007ab <+62>: lea -0x50(%rbp),%rax
0x00000000004007af <+66>: mov %rdx,%rsi
0x00000000004007b2 <+69>: mov %rax,%rdi
0x00000000004007b5 <+72>: callq 0x4005c0 <strcmp#plt>
0x00000000004007ba <+77>: test %eax,%eax
0x00000000004007bc <+79>: jne 0x4007c5 <authorize+88>
0x00000000004007be <+81>: mov $0x1,%eax
0x00000000004007c3 <+86>: jmp 0x4007ca <authorize+93>
0x00000000004007c5 <+88>: mov $0x0,%eax
0x00000000004007ca <+93>: mov -0x8(%rbp),%rcx
0x00000000004007ce <+97>: xor %fs:0x28,%rcx
0x00000000004007d7 <+106>: je 0x4007de <authorize+113>
0x00000000004007d9 <+108>: callq 0x400580 <__stack_chk_fail#plt>
0x00000000004007de <+113>: leaveq
0x00000000004007df <+114>: retq
End of assembler dump.
(gdb) br *authorize+33
Breakpoint 1 at 0x40078e: file simple_login.c, line 19.
(gdb) run
Starting program: ./a.out
Breakpoint 1, 0x000000000040078e in authorize () at simple_login.c:19
19 printf("Enter password: ");
(gdb) where
#0 0x000000000040078e in authorize () at simple_login.c:19
#1 0x00000000004007ee in main () at simple_login.c:29
(gdb) x/2x $ebp
0xffffffffffffe4a0: Cannot access memory at address 0xffffffffffffe4a0
On ubuntu64 bit, Linux version 4.8.0-44-generic (buildd#xxxx) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) #47~16.04.1-Ubuntu SMP
Thanks in advance.
Dump of assembler code for function authorize:
0x000000000040076d <+0>: push %rbp
This is a 64-bit build. You should use x/2x $rbp instead.
I'm performing a relative jump in some asm code in C. I've got the jump working as intended, but it goes back to right after the jump happens and finished running the code.
#include <stdio.h>
void function() {
asm("jmp .+0x31"); //this happens to be where I want to jump to from the function call
}
void main(int argc, char* argv[]) {
int x;
x = 0;
function();
x = 1;
x = x + 1;
printf("%d\n", x);
}
0x000000000040053f <+0>: push %rbp
0x0000000000400540 <+1>: mov %rsp,%rbp
0x0000000000400543 <+4>: sub $0x20,%rsp
0x0000000000400547 <+8>: mov %edi,-0x14(%rbp)
0x000000000040054a <+11>: mov %rsi,-0x20(%rbp)
0x000000000040054e <+15>: movl $0x0,-0x4(%rbp)
0x0000000000400555 <+22>: mov $0x0,%eax
0x000000000040055a <+27>: callq 0x400536 <function>
0x000000000040055f <+32>: movl $0x1,-0x4(%rbp)
0x0000000000400566 <+39>: addl $0x1,-0x4(%rbp)
0x000000000040056a <+43>: mov -0x4(%rbp),%eax
0x000000000040056d <+46>: mov %eax,%esi
0x000000000040056f <+48>: mov $0x400620,%edi
0x0000000000400574 <+53>: mov $0x0,%eax
0x0000000000400579 <+58>: callq 0x400410 <printf#plt>
0x000000000040057e <+63>: nop
0x000000000040057f <+64>: leaveq
0x0000000000400580 <+65>: retq
Following the call of function(), it prints a 0 as intended, but it then goes back through the code after function() is called and prints 2 as well. Am I missing how jmp works? Is there a way to exit via asm code?
The goal of this is to skip
x = 1;
x = x + 1;
and just print 0, then exit the file.
Since function doesn't execute a ret instruction, its return address is still there on the stack. That is, when main executes its ret, the return address it uses is in fact an address left by function, and lands back at x = 1; line.
To achieve your goal, function before doing a jump must adjust the stack pointer as if it has never been called.
That said, don't try this at home.
I am trying to modify the following C program so that the main function will skip the printf("x is 1") line and only print "x is 0".
void func(char *str) {
char buffer[24];
int *ret;
ret = buffer + 28; // Supposed to set ret to the return address of func
(*ret) += 32; // Add the offset needed so that func will skip over printf("x is 1")
strcpy(buffer, str);
}
int main(int argc, char **argv) {
int x;
x = 0;
func(argv[1]);
x = 1;
printf("x is 1");
printf("x is 0");
getchar();
}
As the comments imply, the ret pointer needs to first be set to the return address of the function. I then need to add on an offset that will push it over the line I want to skip. I am running this code on a Linux system with 2 x Intel(R) Xeon(TM) CPU 3.20GHz. I am using gcc version 4.7.2 (Debian 4.7.2-5) to compile. I'm also trying to use example3.c from this (http://insecure.org/stf/smashstack.html) link for reference. Here is a disassembly of the main function using gdb:
Dump of assembler code for function main:
0x0000000000400641 <+0>: push %rbp
0x0000000000400642 <+1>: mov %rsp,%rbp
0x0000000000400645 <+4>: sub $0x20,%rsp
0x0000000000400649 <+8>: mov %edi,-0x14(%rbp)
0x000000000040064c <+11>: mov %rsi,-0x20(%rbp)
0x0000000000400650 <+15>: movl $0x0,-0x4(%rbp)
0x0000000000400657 <+22>: mov -0x20(%rbp),%rax
0x000000000040065b <+26>: add $0x8,%rax
0x000000000040065f <+30>: mov (%rax),%rax
0x0000000000400662 <+33>: mov %rax,%rdi
0x0000000000400665 <+36>: callq 0x4005ac <func>
0x000000000040066a <+41>: movl $0x1,-0x4(%rbp)
0x0000000000400671 <+48>: mov $0x40075b,%edi
0x0000000000400676 <+53>: mov $0x0,%eax
0x000000000040067b <+58>: callq 0x400470 <printf#plt>
0x0000000000400680 <+63>: mov $0x400762,%edi
0x0000000000400685 <+68>: mov $0x0,%eax
0x000000000040068a <+73>: callq 0x400470 <printf#plt>
0x000000000040068f <+78>: callq 0x400490 <getchar#plt>
0x0000000000400694 <+83>: leaveq
0x0000000000400695 <+84>: retq
End of assembler dump.
Using what I've read from the example, my buffer is 24 bytes long and I should add an extra 4 bytes for the SFP size. This would mean I add 28 bytes to get to the return address of <+41>. It then looks like I want to jump to the last printf call at <+73>. This should be an offset of 32. However, when I execute the code, "x is 1" is still printed. I can't seem to find out why. Is there something wrong with my math or assumptions?
This looks to be an ideal time to get experience with gdb and verify that your expectations regarding the stack and the function return address locations are correct!
I will, however suggest that your modified return address should probably be at <+63>, not <+73>, as you need to run the function setup code (to pass the argument, etc).