How gdb locate the address of local variable? - c

there is short program, the code is just as follow:
/* init.c */
#include <syscall.h>
#include <stdio.h>
int main()
{
int pid, exitstatus;
char shell[] = "shell";
char * args[] = {shell, 0};
while(1) {
pid = fork();
if (!pid)
exec(shell, args);
while (pid != wait(&exitstatus));
printf("Shell exited with status %d; starting it back up...", exitstatus);
}
}
compiled using gcc:
gcc -nostdinc -fno-strict-aliasing -fno-builtin -Wall -gstabs -Werror -O0 -m32 -c -o init.o init.c
using objdump to check the gstabs information of variable pid:
objdump -G init.o | grep pid
and the output of objdump:
37 LSYM 0 0 00000018 777 pid:(0,1)
When I try to print the memory address of pid within gdb:
print &pid
the result shows that &pid == $ebp + 0x18, but the assembly code is actually operate at memory address $esp + 0x18 as the assembly code will show below.
part of the disassemble output of init.o:
20 2c: c7 44 24 20 00 00 00 movl $0x0,0x20(%esp)
21 33: 00
22 34: e8 fc ff ff ff call 35 <main+0x35>
23 39: 89 44 24 18 mov %eax,0x18(%esp)
24 3d: 83 7c 24 18 00 cmpl $0x0,0x18(%esp)
25 42: 75 16 jne 5a <main+0x5a>
26 44: 8d 44 24 1c lea 0x1c(%esp),%eax
27 48: 89 44 24 04 mov %eax,0x4(%esp)
28 4c: 8d 44 24 26 lea 0x26(%esp),%eax
here at line 23:
mov %eax,0x18(%esp)
I think that what this instruction does is storing the result of fork() to variable pid, and it's using the address $esp + 0x18 not $ebp + 0x18 as gdb shows, as result, I can not get the desired result when I execute print pid in gdb (so as other local variables). So, how could this happen?

Related

fpic and O3 optimization flags

I'm trying to compile main.c which uses libnothing.so. Here is the source code:
main.c
#include "nothing.h"
int main(void)
{
doAlmostNothing();
return 0;
}
nothing.c
#include "nothing.h"
void doNothingStatic(void) {
volatile int x = 45;
x++;
}
void doNothing(void) {}
void doAlmostNothing(void)
{
doNothingStatic();
doNothing();
}
nothing.h
void doAlmostNothing(void);
First I compile nothing.c like this without fpic: gcc -c nothing.c I'll get this error: /usr/bin/ld: nothing.o: relocation R_X86_64_PC32 against symbol doNothing can not be used when making a shared object; recompile with -fPIC when building the .so gcc -shared nothing.o -o libnothing.so
But if I compile it using O3 gcc -c -O3 nothing.c I don't get the relocation error anymore.
Is -O3 adding fpic by default ?
EDIT
I changed a bit the code by adding void as suggested in the comments, removed static from doNothingStatic and add some dummy work in it.
Here is the console output when running the commands:
bil#bil-VirtualBox:~/Documents/test/linking$ gcc-7 -c nothing.c
bil#bil-VirtualBox:~/Documents/test/linking$ gcc-7 -shared nothing.o -o nothing.so
/usr/bin/ld: nothing.o: relocation R_X86_64_PC32 against symbol `doNothingStatic' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
bil#bil-VirtualBox:~/Documents/test/linking$ gcc-7 -c -O3 nothing.c
bil#bil-VirtualBox:~/Documents/test/linking$ gcc-7 -shared nothing.o -o libnothing.so
bil#bil-VirtualBox:~/Documents/test/linking$ ls
libnothing.so main main.c main.o nothing.c nothing.h nothing.o libnothing.so
I also looked on the assembly that objdump provides:
without O3:
nothing.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <doNothingStatic>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: c7 45 fc 2d 00 00 00 movl $0x2d,-0x4(%rbp)
b: 8b 45 fc mov -0x4(%rbp),%eax
e: 83 c0 01 add $0x1,%eax
11: 89 45 fc mov %eax,-0x4(%rbp)
14: 90 nop
15: 5d pop %rbp
16: c3 retq
0000000000000017 <doNothing>:
17: 55 push %rbp
18: 48 89 e5 mov %rsp,%rbp
1b: 90 nop
1c: 5d pop %rbp
1d: c3 retq
000000000000001e <doAlmostNothing>:
1e: 55 push %rbp
1f: 48 89 e5 mov %rsp,%rbp
22: e8 00 00 00 00 callq 27 <doAlmostNothing+0x9>
27: e8 00 00 00 00 callq 2c <doAlmostNothing+0xe>
2c: 90 nop
2d: 5d pop %rbp
2e: c3 retq
with O3
nothing.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <doNothingStatic>:
0: c7 44 24 fc 2d 00 00 movl $0x2d,-0x4(%rsp)
7: 00
8: 8b 44 24 fc mov -0x4(%rsp),%eax
c: 83 c0 01 add $0x1,%eax
f: 89 44 24 fc mov %eax,-0x4(%rsp)
13: c3 retq
14: 66 90 xchg %ax,%ax
16: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
1d: 00 00 00
0000000000000020 <doNothing>:
20: f3 c3 repz retq
22: 0f 1f 40 00 nopl 0x0(%rax)
26: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
2d: 00 00 00
0000000000000030 <doAlmostNothing>:
30: c7 44 24 fc 2d 00 00 movl $0x2d,-0x4(%rsp)
37: 00
38: 8b 44 24 fc mov -0x4(%rsp),%eax
3c: 83 c0 01 add $0x1,%eax
3f: 89 44 24 fc mov %eax,-0x4(%rsp)
43: c3 retq
Indeed it seems the functions are inlined when using -O3
No, it is just that the function doNothing was inlined and thus there were no intra-module function calls left.
The relocation type means an absolute function or data access using a sign-extended 32-bit pointer, i.e. basically something within the first 2 GiB of virtual memory. When compiled with -O3 all function calls were inlined and therefore the calls using the relocations are not needed.
No, -O3 does not turn on -fPIC.
Here is the a list of flags turned on by the different optimization levels.
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

Get Shellcode from Object Dump the right way

I have read several tutorials on basic shellcoding and i have a question. I have written a little function in c
#include <stdio.h>
#include<string.h>
int main() {
char cmd[] = "net user /add jango Jango01$";
system(cmd);
}
and have build an object file of it using
gcc -c exec.c -o exec.o
Now i want to turn it into shellcode by using objdump.
c:\>objdump -d -M intel exec.o
: file format pe-i386
Disassembly of section .text:
00000000 <_main>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 e4 f0 and esp,0xfffffff0
6: 83 ec 30 sub esp,0x30
9: e8 00 00 00 00 call e <_main+0xe>
============= SNIP =============
5c: c9 leave
5d: c3 ret
5e: 90 nop
5f: 90 nop
When i bundle the bytes to something like
char code[] = "\x55\x89\xe5\x83\xe4\xf0\x83\xec\x30\xe8\x00\x00\x00\x00 == SNIPPET == \xc9\xc3\x90\x90";
is this the correct way? I am not sure if the hexdump is the final shellcode. My english is not so good and i am not sure if i understood all right.
Thank you

gdb breakpoint in shared library not working

So, I have the following c program:
#include <stdio.h>
#include <string.h>
int main(){
char arr[20];
//this is line 6
strcpy(arr,"Hello, world!\n");
printf(arr);
}
I compiled it using the following command:
gcc -g t2.c -o a2.out
After that I loaded it in gdb and tried setting breakpoints at line 6, at the strcpy function and at line 8. Sure enough, when setting the breakpoint at strcpy I got the following message : "Make breakpoint pending on future shared library load? (y or [n])". I answered "y" and got "Breakpoint 2 (strcpy) pending.".
After answering yes, and running through the program, Breakpoint 2 is never resolved, and the debugger jumps straight to Breakpoint 3 at printf.
I am using Intel syntax in my debugger. Other than that no custom settings. Can anyone tell why the Breakpoint at strcpy is never resolved?
Compilers such as gcc are deeply familiar with the semantics of string functions such as strcpy.
On x86-64 with your example, gcc 9 is generating inline assembly rather than a strcpy call even at
-O0. The breakpoint should work for most other functions.
x86-64 disassembly generated with gcc-9 (no strcpy call):
0000000000000000 <main>:
0: 48 83 ec 28 sub rsp,0x28
4: 48 b8 48 65 6c 6c 6f 2c 20 77 movabs rax,0x77202c6f6c6c6548
e: bf 01 00 00 00 mov edi,0x1
13: 48 89 04 24 mov QWORD PTR [rsp],rax
17: b8 21 0a 00 00 mov eax,0xa21
1c: 48 89 e6 mov rsi,rsp
1f: 66 89 44 24 0c mov WORD PTR [rsp+0xc],ax
24: 31 c0 xor eax,eax
26: c7 44 24 08 6f 72 6c 64 mov DWORD PTR [rsp+0x8],0x646c726f
2e: c6 44 24 0e 00 mov BYTE PTR [rsp+0xe],0x0
33: e8 00 00 00 00 call 38 <main+0x38> 34: R_X86_64_PLT32 __printf_chk-0x4
38: 31 c0 xor eax,eax
3a: 48 83 c4 28 add rsp,0x28
3e: c3 ret

What numeric values defines in dissembled of C code?

I'm understanding the assembly and C code.
I have following C program , compiled to generate Object file only.
#include <stdio.h>
int main()
{
int i = 10;
int j = 22 + i;
return 0;
}
I executed following command
objdump -S myprogram.o
Output of above command is:
objdump -S testelf.o
testelf.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
#include <stdio.h>
int main()
{
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 10 sub $0x10,%esp
int i = 10;
6: c7 45 f8 0a 00 00 00 movl $0xa,-0x8(%ebp)
int j = 22 + i;
d: 8b 45 f8 mov -0x8(%ebp),%eax
10: 83 c0 16 add $0x16,%eax
13: 89 45 fc mov %eax,-0x4(%ebp)
return 0;
16: b8 00 00 00 00 mov $0x0,%eax
}
1b: c9 leave
1c: c3 ret
What is meant by number numeric before the mnemonic commands
i.e. "83 ec 10 " before "sub" command or
"c7 45 f8 0a 00 00 00" before "movl" command
I'm using following platform to compile this code:
$ lscpu
Architecture: i686
CPU op-mode(s): 32-bit
Byte Order: Little Endian
CPU(s): 1
On-line CPU(s) list: 0
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 1
Vendor ID: GenuineIntel
Those are x86 opcodes. A detailed reference, other than the ones listed in the comments above is available here.
For example the c7 45 f8 0a 00 00 00 before the movl $0xa,-0x8(%ebp) are hexadecimal values for the opcode bytes. They tell the CPU to move the immediate value of 10 decimal (as a 4-byte value) into the address located on the current stack 8-bytes above the stack frame base pointer. That is where the variable i from your C source code is located when your code is running. The top of the stack is at a lower memory address than the bottom of the stack, so moving a negative direction from the base is moving up the stack.
The c7 45 f8 opcodes mean to mov data and clear the arithmetic carry flag in the EFLAGS register. See the reference for more detail.
The remainder of the codes are an immediate value. Since you are using a little endian system, the least significant byte of a number is listed first, such that 10 decimal which is 0x0a in hexadecimal and has a 4-byte value of 0x0000000a is stored as 0a 00 00 00.

objdump of binary with debug info produces mangled output

I often notice severely mangled output with mixed assembly and C instructions in the output of objdump -S. This seems to happen only for binaries built with debug info. Is there any way to fix this?
To illustrate the issue i have written a simple program :
/* test.c */
#include <stdio.h>
int main()
{
static int i = 0;
while(i < 0x1000000) {
i++;
}
return 0;
}
The above program was built with/without debug info as follows :
$ gcc test.c -o test-release
$ gcc test.c -g -o test-debug
Disassembling the test-release binary works fine.
$ objdump -S test-release
produces the following clear and concise snippet for the main() function.
080483b4 <main>:
80483b4: 55 push %ebp
80483b5: 89 e5 mov %esp,%ebp
80483b7: eb 0d jmp 80483c6 <main+0x12>
80483b9: a1 18 a0 04 08 mov 0x804a018,%eax
80483be: 83 c0 01 add $0x1,%eax
80483c1: a3 18 a0 04 08 mov %eax,0x804a018
80483c6: a1 18 a0 04 08 mov 0x804a018,%eax
80483cb: 3d ff ff ff 00 cmp $0xffffff,%eax
80483d0: 7e e7 jle 80483b9 <main+0x5>
80483d2: b8 00 00 00 00 mov $0x0,%eax
80483d7: 5d pop %ebp
80483d8: c3 ret
But $ objdump -S test-debug
produces the following mangled snippet for the same main() function.
080483b4 <main>:
#include <stdio.h>
int main()
{
80483b4: 55 push %ebp
80483b5: 89 e5 mov %esp,%ebp
static int i = 0;
while(i < 0x1000000) {
80483b7: eb 0d jmp 80483c6 <main+0x12>
i++;
80483b9: a1 18 a0 04 08 mov 0x804a018,%eax
80483be: 83 c0 01 add $0x1,%eax
80483c1: a3 18 a0 04 08 mov %eax,0x804a018
int main()
{
static int i = 0;
while(i < 0x1000000) {
80483c6: a1 18 a0 04 08 mov 0x804a018,%eax
80483cb: 3d ff ff ff 00 cmp $0xffffff,%eax
80483d0: 7e e7 jle 80483b9 <main+0x5>
i++;
}
return 0;
80483d2: b8 00 00 00 00 mov $0x0,%eax
}
80483d7: 5d pop %ebp
80483d8: c3 ret
I do understand that as the debug binary contains additional symbol info, the C code is displayed interlaced with the assembly instructions. But this makes it a tad difficult to follow the flow of code.
Is there any way to instruct objdump to output pure assembly and not interlace debug symbols into the output even if encountered in a binary?
Use -d instead of -S. objdump is doing exactly what you are telling it to. The -S option implies -d but also displays the C source if debugging information is available.

Resources