gdb-multiarch: debugging x64 program on aarch64 does not recognize dynamic functions - c

I am working on an arm64 machine and trying to debug a x64 program. I installed gdb, gdb-multiarch, the x64 compiler, the x64 libc. I have a basic program:
#include <stdio.h>
int main() {
printf("hello from qemu\n");
}
Compiled with x86_64-linux-gnu-gcc -o main main.c
I run it using qemu-x86_64 -g 1234 main then start gdb-multiarch with
file main
target remote localhost:1234
The main function looks like this:
gef➤ disas main
Dump of assembler code for function main:
0x0000004000001135 <+0>: push rbp
0x0000004000001136 <+1>: mov rbp,rsp
0x0000004000001139 <+4>: lea rdi,[rip+0xec4] # 0x4000002004
0x0000004000001140 <+11>: call 0x4000001030
0x0000004000001145 <+16>: mov eax,0x0
0x000000400000114a <+21>: pop rbp
0x000000400000114b <+22>: ret
End of assembler dump.
The printf function is not recognized, and most gef commands do not work, for example got or any heap command:
gef➤ got
[!] Command 'got' failed to execute properly, reason: min() arg is an empty sequence

Related

Inserting gdb breakpoints fail

I'm learning about buffer overflow in c.
For that purpose, I'm following this simple example.
I have the following gcc version:
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 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.
And this simple c file:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
char buf[256];
strcpy(buf, argv[1]);
printf("%s,", buf);
return 0;
}
I then compile this file with $ gcc buf.c -o buf.
I then open in gdb by $ gdb ./buf
I call disas and get the result assembly:
(gdb) disas main
Dump of assembler code for function main:
0x0000000000001189 <+0>: endbr64
0x000000000000118d <+4>: push %rbp
0x000000000000118e <+5>: mov %rsp,%rbp
0x0000000000001191 <+8>: sub $0x120,%rsp
0x0000000000001198 <+15>: mov %edi,-0x114(%rbp)
0x000000000000119e <+21>: mov %rsi,-0x120(%rbp)
0x00000000000011a5 <+28>: mov %fs:0x28,%rax
0x00000000000011ae <+37>: mov %rax,-0x8(%rbp)
0x00000000000011b2 <+41>: xor %eax,%eax
0x00000000000011b4 <+43>: mov -0x120(%rbp),%rax
0x00000000000011bb <+50>: add $0x8,%rax
0x00000000000011bf <+54>: mov (%rax),%rdx
0x00000000000011c2 <+57>: lea -0x110(%rbp),%rax
0x00000000000011c9 <+64>: mov %rdx,%rsi
0x00000000000011cc <+67>: mov %rax,%rdi
0x00000000000011cf <+70>: callq 0x1070 <strcpy#plt>
--Type <RET> for more, q to quit, c to continue without paging--
0x00000000000011d4 <+75>: lea -0x110(%rbp),%rax
0x00000000000011db <+82>: mov %rax,%rsi
0x00000000000011de <+85>: lea 0xe1f(%rip),%rdi # 0x2004
0x00000000000011e5 <+92>: mov $0x0,%eax
0x00000000000011ea <+97>: callq 0x1090 <printf#plt>
0x00000000000011ef <+102>: mov $0x0,%eax
0x00000000000011f4 <+107>: mov -0x8(%rbp),%rcx
0x00000000000011f8 <+111>: xor %fs:0x28,%rcx
0x0000000000001201 <+120>: je 0x1208 <main+127>
0x0000000000001203 <+122>: callq 0x1080 <__stack_chk_fail#plt>
0x0000000000001208 <+127>: leaveq
0x0000000000001209 <+128>: retq
End of assembler dump.
With some really low memory adresses.
I then want to see what happens if I input a big string of A's into the program, I therefore place a breakpoint at 0x00000000000011db
I then run it:
(gdb) run $(python3 -c "print('A'*256)"
Starting program: /home/ask/Notes/ctf/bufoverflow/code/buf $(python3 -c "print('A'*256)"
/bin/bash: -c: line 0: unexpected EOF while looking for matching `)'
/bin/bash: -c: line 1: syntax error: unexpected end of file
During startup program exited with code 1.
(gdb) run $(python3 -c "print('A'*256)")
Starting program: /home/ask/Notes/ctf/bufoverflow/code/buf $(python3 -c "print('A'*256)")
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x11db
Ok, so something with the memory adresses is a bit funky.
I google the issue and find this post where I find that this is because Position-Independent Executable (PIE) was probably enabled, and the memory adresses would be changed when the program is actually run.
I can confirm this by running disas after running the program, and seeing that the memory adresses are in a lot higher ranges.
This all makes sense, but it makes me wonder, iif the adresses change every time I run it, then how can I then place a breakpoint at a memory adress before the program runs?
iif the adresses change every time I run it, then how can I then place a breakpoint at a memory adress before the program runs?
This happens because GDB by default disables address randomization (to make debugging easier).
If you re-enable ASLR with (gdb) set disable-randomization off, then you wouldn't be able to set the breakpoint on an address.
You would still be able to set breakpoint on e.g. main -- in that case GDB will wait until the executable has been relocated, and will set the breakpoint on the actual runtime instruction (the address will change on every run).

What controls whether code stored in the data section can run or not?

https://www.exploit-db.com/exploits/42179
#include <stdio.h>
unsigned char shellcode[] = "\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05";
int main()
{
int (*ret)() = (int(*)())shellcode;
ret();
}
According to the comment in the code, gcc -fno-stack-protector -z execstack shell.c -o shell is supposed to compiled the code on #1 SMP Debian 4.9.18-1 (2017-03-30) x86_64 GNU/Linux.
I get the following error when I try the above code. How to make it work? What has been changed in the OS so that it does not work any more?
$ uname -a
Linux kali 5.10.0-kali4-amd64 #1 SMP Debian 5.10.19-1kali1 (2021-03-03) x86_64 GNU/Linux
$ gcc -fno-stack-protector -z execstack shell.c -o shell
$ ./shell
Segmentation fault
EDIT: It seems the problem is related with kali linux. The same binary runs on Ubuntu 64bit. I tried to step through the binary with gdb on kali.
The segmentation fault on kali is generated when the shellcode is run.
$ gdb -q shell
Reading symbols from shell...
(No debugging symbols found in shell)
(gdb) b main
Breakpoint 1 at 0x1129
(gdb) start
Temporary breakpoint 2 at 0x1129
Starting program: /tmp/shell
Breakpoint 1, 0x0000555555555129 in main ()
(gdb) disassemble main
Dump of assembler code for function main:
0x0000555555555125 <+0>: push %rbp
0x0000555555555126 <+1>: mov %rsp,%rbp
=> 0x0000555555555129 <+4>: sub $0x10,%rsp
0x000055555555512d <+8>: lea 0x2efc(%rip),%rax # 0x555555558030 <shellcode>
0x0000555555555134 <+15>: mov %rax,-0x8(%rbp)
0x0000555555555138 <+19>: mov -0x8(%rbp),%rdx
0x000055555555513c <+23>: mov $0x0,%eax
0x0000555555555141 <+28>: call *%rdx
0x0000555555555143 <+30>: mov $0x0,%eax
0x0000555555555148 <+35>: leave
0x0000555555555149 <+36>: ret
End of assembler dump.
(gdb) si 5
0x0000555555555141 in main ()
(gdb) disassemble main
Dump of assembler code for function main:
0x0000555555555125 <+0>: push %rbp
0x0000555555555126 <+1>: mov %rsp,%rbp
0x0000555555555129 <+4>: sub $0x10,%rsp
0x000055555555512d <+8>: lea 0x2efc(%rip),%rax # 0x555555558030 <shellcode>
0x0000555555555134 <+15>: mov %rax,-0x8(%rbp)
0x0000555555555138 <+19>: mov -0x8(%rbp),%rdx
0x000055555555513c <+23>: mov $0x0,%eax
=> 0x0000555555555141 <+28>: call *%rdx
0x0000555555555143 <+30>: mov $0x0,%eax
0x0000555555555148 <+35>: leave
0x0000555555555149 <+36>: ret
End of assembler dump.
(gdb) si
0x0000555555558030 in shellcode ()
(gdb) si
Program received signal SIGSEGV, Segmentation fault.
0x0000555555558030 in shellcode ()
(gdb) x/16bx 0x0000555555558030
0x555555558030 <shellcode>: 0x50 0x48 0x31 0xd2 0x48 0x31 0xf6 0x48
0x555555558038 <shellcode+8>: 0xbb 0x2f 0x62 0x69 0x6e 0x2f 0x2f 0x73
Using the same binary on Ubuntu, the shellcode runs correctly.
When I modify the code by putting the shellcode in the stack, then it can run on kali. So the problem is related with whether code in data can be run or not. What controls this behavior?
$ cat shell.c
#include <stdio.h>
int main() {
unsigned char shellcode[] = "\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05";
int (*ret)() = (int(*)())shellcode;
ret();
}
What has been changed in the OS so that it does not work any more?
It used to be that readonly-data (.rodata) was put into the read-execute segment, together with .text.
To put shellcode into .rodata, you would need to make it const:
unsigned const char shellcode[] = ...
I don't think the example without const ever worked.
Once you put it into .rodata, it will work when linking with -Wl,-z,noseparate-code on newer systems (if your linker doesn't support noseparate-code, then it is probably old enough that the example will work without any special flags).

GDB skips shared library breakpoint

Im using a debugger with a simple C program, im trying to set a breakpoint with a shared library, but GDB skips this breakpoint entirely.
Im trying to use GDB with a simple C program to learn about GDB. I set 3 breakpoints, 1 at line 7, one at the strcpy function, and one at line 8. I try to set a breakpoint in my program involving a shared library (specifically "break strcpy"), but every time I run the program and press "c", the program skips breakpoint 2 entirely
#include <stdio.h>
#include <string.h>
int main() {
char str_a[20];
strcpy(str_a, "Hello, world!\n");
printf(str_a);
}
Whenever I run the program in the debugger, it stops normally at breakpoint 1, which is expected, but then whenever I press "c" to continue to breakpoint 2, it skips breakpoint 2 entirely and just shows the output breakpoint 3 is supposed to have. Is this something to do with GDB's handling of shared libraries?
EDIT: Here is the disassembly
0x0000555555555145 <+0>: push rbp
0x0000555555555146 <+1>: mov rbp,rsp
0x0000555555555149 <+4>: sub rsp,0x20
0x000055555555514d <+8>: lea rax,[rbp-0x20]
0x0000555555555151 <+12>: lea rsi,[rip+0xeac] # 0x555555556004
0x0000555555555158 <+19>: mov rdi,rax
0x000055555555515b <+22>: call 0x555555555030 <strcpy#plt>
0x0000555555555160 <+27>: lea rax,[rbp-0x20]
0x0000555555555164 <+31>: mov rdi,rax
0x0000555555555167 <+34>: mov eax,0x0
0x000055555555516c <+39>: call 0x555555555040 <printf#plt>
0x0000555555555171 <+44>: mov eax,0x0
0x0000555555555176 <+49>: leave
0x0000555555555177 <+50>: ret
You didn't specify your platform. I suspect it's Linux with GLIBC.
The reason GDB behaves this way is that strcpy is not a normal function, but a GNU IFUNC.
Try setting breakpoint on __strcpy_sse2_unaligned and see this answer.
Update:
the debugger spits out this error whenever it reaches breakpoint 2, "../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.
That isn't an error.
The fact that it reaches that breakpoint confirms that this answer is correct.
You can simply treat __strcpy_sse2_unaligned as an alias to strcpy. Setting a breakpoint there is (on your system) equivalent to setting it on strcpy.

(GDB) Breakpoints and Disassemble

I've been recently interested in reading books and articles about hacking and I found out that Hacking:The art of exploitation is just a must read title. I am following the basic tutorials on how to work with standard Linux tools and analyze your code (Programming chapter). I am not a beginner in programming but working with Linux terminal is quite new for me. I am using the latest release of Kali Linux.
Right now my simple program below should be used to analyze how stack segment works.
int main(){
void stack_func(int a,int b, int c, int d){
char first;
int second;
first = 'c';
second = 220;
}
stack_func(1,2,3,4);
return 0;
}
The first problem is I cannot add any breakpoints for internal functions. Neither mine functions like stack_func() nor functions from libraries like strcpy etc. According to the book the pending breakpoint should resolve. Mine is just ignored and program finishes.
root#root:~/Folder# gdb -q ./stack
Reading symbols from ./stack...done.
(gdb) b stack_func
Function "stack_func" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (stack_func) pending.
(gdb) run
Starting program: /root/Folder/stack
[Inferior 1 (process 20421) exited normally]
(gdb)
The second problem is that disassemble also doesn't work for my function. Again according to the book I should be able to see assembler code for my function stack_func() but the result is below.
(gdb) disass stack_func()
No symbol "stack_func" in current context.
(gdb)
I appologize for any grammatical errors in text. :)
The problem is that you defined stack_func inside another function. This is called nested function and it is gcc extension in GNU C. This function has a bit other symbol name than you expect. To find out it's exact symbol name you can use nm tool:
[ tmp]$ nm a.out |grep stack_func
00000000004004a6 t stack_func.1761
And set breakpoint and disassemble in gdb:
[ tmp]$ gdb -q ./a.out
Reading symbols from ./a.out...done.
(gdb) b 'stack_func.1761'
Breakpoint 1 at 0x4004ba: file 111.c, line 6.
(gdb) disassemble 'stack_func.1761'
Dump of assembler code for function stack_func:
0x00000000004004a6 <+0>: push %rbp
0x00000000004004a7 <+1>: mov %rsp,%rbp
0x00000000004004aa <+4>: mov %edi,-0x14(%rbp)
0x00000000004004ad <+7>: mov %esi,-0x18(%rbp)
0x00000000004004b0 <+10>: mov %edx,-0x1c(%rbp)
0x00000000004004b3 <+13>: mov %ecx,-0x20(%rbp)
0x00000000004004b6 <+16>: mov %r10,-0x28(%rbp)
0x00000000004004ba <+20>: movb $0x63,-0x1(%rbp)
0x00000000004004be <+24>: movl $0xdc,-0x8(%rbp)
0x00000000004004c5 <+31>: nop
0x00000000004004c6 <+32>: pop %rbp
0x00000000004004c7 <+33>: retq
End of assembler dump.
(gdb)

Can't step into string.h function with GDB

Having trouble stepping into string.h in GDB 7.5. Here's a simple example program:
Source code:
#include <stdio.h>
#include <string.h>
int main() {
char str1[20];
strcpy(str1, "STEP INTO ME\n");
printf(str1);
}
Compiled: ~$ gcc -g foo.c
Invoked: ~$ gdb -q ./a.out
GDB:
(gdb) break 5
Breakpoint 1 at 0x8048471: file foo.c, line 6.
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) run
Starting program: /home/user/a.out
Breakpoint 1, main () at foo.c:6
6 strcpy(str_a, "Hello, world!\n");
(gdb) step
7 printf(str_a);
Shouldn't I be in the string library at this point? Instead it continues to the printf().
EDIT:
Scott's suggestion "worked", but not in the expected manner.
Breakpoint 1, main () at foo.c:6
6 strcpy(str_a, "Hello, world!\n");
(gdb) i r $eip
eip 0x80484a1 0x80484a1 <main+21>
(gdb) step
Breakpoint 2, __strcpy_ssse3 () at ../sysdeps/i386/i686/multiarch/strcpy-ssse3.S:78
78 ../sysdeps/i386/i686/multiarch/strcpy-ssse3.S: No such file or directory.
(gdb) i r $eip
eip 0xb7e9c820 0xb7e9c820 <__strcpy_ssse3>
I am surprised at the directory in 78... expected something like: /lib/.../cmov/libc.so.6. And the claim that there is no such file or directory.
Recompile your code with gcc -fno-builtin -g foo.c and the gdb step command will work. (See -fno-builtin documentation). Otherwise small strcpy(), memcpy() calls would often be translated into open coded data movement instructions, e.g. on x86-64:
4 int main() {
0x000000000040052c <+0>: push %rbp
0x000000000040052d <+1>: mov %rsp,%rbp
0x0000000000400530 <+4>: sub $0x20,%rsp
5 char str1[20];
6 strcpy(str1, "STEP INTO ME\n");
0x0000000000400534 <+8>: lea -0x20(%rbp),%rax
0x0000000000400538 <+12>: movl $0x50455453,(%rax)
0x000000000040053e <+18>: movl $0x544e4920,0x4(%rax)
0x0000000000400545 <+25>: movl $0x454d204f,0x8(%rax)
0x000000000040054c <+32>: movw $0xa,0xc(%rax)
7 printf(str1);
0x0000000000400552 <+38>: lea -0x20(%rbp),%rax
0x0000000000400556 <+42>: mov %rax,%rdi
0x0000000000400559 <+45>: mov $0x0,%eax
0x000000000040055e <+50>: callq 0x400410 <printf#plt>
8 }
0x0000000000400563 <+55>: leaveq
0x0000000000400564 <+56>: retq
You can see the strpcy() call being compiled into multiple MOV instructions.
gcc -fno-builtin compiles the same program into:
4 int main() {
0x000000000040057c <+0>: push %rbp
0x000000000040057d <+1>: mov %rsp,%rbp
0x0000000000400580 <+4>: sub $0x20,%rsp
5 char str1[20];
6 strcpy(str1, "STEP INTO ME\n");
0x0000000000400584 <+8>: lea -0x20(%rbp),%rax
0x0000000000400588 <+12>: mov $0x400660,%esi
0x000000000040058d <+17>: mov %rax,%rdi
0x0000000000400590 <+20>: callq 0x400450 <strcpy#plt>
7 printf(str1);
0x0000000000400595 <+25>: lea -0x20(%rbp),%rax
0x0000000000400599 <+29>: mov %rax,%rdi
0x000000000040059c <+32>: mov $0x0,%eax
0x00000000004005a1 <+37>: callq 0x400460 <printf#plt>
8 }
0x00000000004005a6 <+42>: leaveq
0x00000000004005a7 <+43>: retq
and you can see the call to <strcpy#plt>.
Assuming you wanted to step into strcpy() to study its implementation, you'd want to have debug info for libc.so installed. Unfortunately the way to get debug info differs between Linux distros. On Fedora it's as simple as debuginfo-install glibc. It takes more steps on Ubuntu and Debian. This RPM DPKG Rosetta Stone page have links to instructions for Fedora, Ubuntu and Debian (search for debuginfo).
Since you're on Ubuntu 12.10 and actually want to see the strcpy() assembly source code:
$ sudo apt-get install libc6-dbg
$ sudo apt-get source libc6-dev
$ gdb ./a.out
(gdb) directory eglibc-2.15/sysdeps
Source directories searched: /home/scottt/eglibc-2.15/sysdeps:$cdir:$cwd
(gdb) break strcpy
Breakpoint 1 at 0x400450
(gdb) run
Starting program: /home/scottt/a.out
Breakpoint 1, __strcpy_sse2 () at ../sysdeps/x86_64/multiarch/../strcpy.S:32
32 movq %rsi, %rcx /* Source register. */
You tried to set a breakpoint for a function defined in the string library usually part of the standard C library - libc.so
And as gdb informs you:
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
the library is not loaded yet.
But the real problem is, even when the library is loaded, if the library i.e. libc.so does not have debug symbols in it, you would not be able to step through the code within the library using gdb.
You could enable verbose mode to see which symbols, gdb is able to load:
(gdb) b main
Breakpoint 1 at 0x400914: file test.cpp, line 7.
(gdb) set verbose on
(gdb) run
Starting program: /home/agururaghave/.scratch/gdb-test/test
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from system-supplied DSO at 0x7ffff7ffb000...(no debugging symbols found)...done.
Reading symbols from /usr/lib64/libstdc++.so.6...(no debugging symbols found)...done.
Registering libstdc++-v6 pretty-printer for /usr/lib64/libstdc++.so.6 ...
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Breakpoint 1, main () at test.cpp:7
7 bool result = myObj1 < myObj2;
This line for example tells you whether it was able to get the symbols for libc.so:
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
You could then figure out where the debug symbols are picked up from using show debug-file-directory:
(gdb) show debug-file-directory
The directory where separate debug symbols are searched for is "/usr/lib/debug".
As you see /usr/lib/debug here does not contain the full .so with debug symbols. Instead it only has the debug info without any .text or .data sections of the actual libc.so which the program uses for execution.
The solution to install the debug info for libraries would be distro specific.
I think the package is called libc6-dbg on the debian based distros. On my openSUSE machine, it seems to be called glibc-debuginfo
BTW, +1 on scottt's suggestion of using -fno-builtin so that gcc does not use its built-in methods for functions like strcpy and other standard ones defined as part of C standard.
You probably don't have any symbols for your C library. Try stepi, but be prepared to see only assembly instructions.

Resources