I had a C program for test: a.c
int a = 0;
static int fa_local()
{
a = 78;
int b;
int c;
}
int fa_global()
{
a = 7777;
fa_local();
}
int test()
{
a = 6666;
fa_global();
}
This is its relocation table after build:
[freeman#centos-7 link_symbol_test]$ gcc -c a.c
[freeman#centos-7 link_symbol_test]$ readelf -r a.o
Relocation section '.rela.text' at offset 0x5d0 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000006 000900000002 R_X86_64_PC32 0000000000000000 a - 8
000000000016 000900000002 R_X86_64_PC32 0000000000000000 a - 8
000000000030 000900000002 R_X86_64_PC32 0000000000000000 a - 8
00000000003e 000a00000002 R_X86_64_PC32 0000000000000010 fa_global - 4
the relocation entry is the funcation call fa_global() in test(), which has offset 00000000003e.
[freeman#centos-7 link_symbol_test]$ objdump -dS a.o:
0000000000000010 <fa_global>:
int fa_global()
{
10: 55 push %rbp
11: 48 89 e5 mov %rsp,%rbp
a = 7777;
14: c7 05 00 00 00 00 61 movl $0x1e61,0x0(%rip) # 1e <fa_global+0xe>
1b: 1e 00 00
fa_local();
1e: b8 00 00 00 00 mov $0x0,%eax
23: e8 d8 ff ff ff callq 0 <fa_local>
}
28: 5d pop %rbp
29: c3 retq
000000000000002a <test>:
int test()
{
2a: 55 push %rbp
2b: 48 89 e5 mov %rsp,%rbp
a = 6666;
2e: c7 05 00 00 00 00 0a movl $0x1a0a,0x0(%rip) # 38 <test+0xe>
35: 1a 00 00
fa_global();
38: b8 00 00 00 00 mov $0x0,%eax
3d: e8 00 00 00 00 callq 42 <test+0x18>
}
42: 5d pop %rbp
43: c3 retq
For fa_global(), it is in the same file.
why would this symbol need to be relocated,
while static symbol fa_local() doesn't?
2017.9.12 update: assembly code after optimization
[freeman#centos-7 relocation_test]$ gcc -fno-inline -O2 -c a.c
[freeman#centos-7 relocation_test]$ objdump -dS a.o
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <fa_local>:
0: c7 05 00 00 00 00 4e movl $0x4e,0x0(%rip) # a <fa_local+0xa>
7: 00 00 00
a: c3 retq
b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000000010 <fa_global>:
10: 31 c0 xor %eax,%eax
12: c7 05 00 00 00 00 61 movl $0x1e61,0x0(%rip) # 1c <fa_global+0xc>
19: 1e 00 00
1c: eb e2 jmp 0 <fa_local>
1e: 66 90 xchg %ax,%ax
0000000000000020 <test>:
20: 31 c0 xor %eax,%eax
22: c7 05 00 00 00 00 0a movl $0x1a0a,0x0(%rip) # 2c <test+0xc>
29: 1a 00 00
2c: e9 00 00 00 00 jmpq 31 <test+0x11>
But I still see the relocation entry:
000000000000002d R_X86_64_PC32 fa_global-0x0000000000000004
fa_local is a function. The compiler can determine its offset from the calling point. it uses a PC relative addressing mode for the call instruction so it does not need the absolute address and can emit the code directly.
Conversely, the a symbol is in a different section of memory, a writable segment whose offset cannot be determined at compile time. The linker does this in the relocation phase.
Right here the function is in the same file, but being non-static it can also be called from other files compiled later.
The compiler cannot know if that will happen, so it has to "prepare for the worst."
Related
I write 2 C programs : main.c and sum.c.
Here is main.c :
int array[2] = {1, 2};
int main() {
int val = sum(array, 2);
return val;
}
Here is sum.c :
int sum(int* a, int n) {
int i, s = 0;
for (i = 0; i < n; i++) {
s += a[i];
}
return s;
}
I use command gcc -c -o main.o main.c and objdump -d -r main.o>main.d and I get :
main.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 48 83 ec 08 sub $0x8,%rsp
8: be 02 00 00 00 mov $0x2,%esi
d: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 14 <main+0x14>
10: R_X86_64_PC32 array-0x4
14: b8 00 00 00 00 mov $0x0,%eax
19: e8 00 00 00 00 callq 1e <main+0x1e>
1a: R_X86_64_PLT32 sum-0x4
1e: 48 83 c4 08 add $0x8,%rsp
22: c3 retq
What does 1e: R_X86_64_PLT32 sum-0x4 mean? Shouldn't it be sum-0x22 because at that time the RIP is 0x22?
And also what does 0x4 in 10: R_X86_64_PC32 array-0x4 stand for?
And one more question : My computer is 64-bit but why the address is 32-bit in the assembly code?
I have a test Linux kernel module which prints the dump_stack(). But it prints an incomplete trace as it does not have the function addresses printed. I use a Ubuntu 16.04.7 LTS with 4.15.0-142-generic kernel and CONFIG_DEBUG_INFO=y is set in /boot/config-4.15.0-142-generic.
My questions:
(1) Why the function's address is not printed? I can dump the symbol table using objdump -t.
(2) Trace shows that dump_stack() is called from module_level_init . However it is module_level_init()->module_level_2()->module_level_3()
Following is the dmesg output:
[ 1347.807370] CPU: 7 PID: 13262 Comm: insmod Tainted: P OE 4.15.0-142-generic #146~16.04.1-Ubuntu
[ 1347.807371] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[29020.752090] Call Trace:
[29020.752096] dump_stack+0x6d/0x8b
[29020.752097] ? 0xffffffffc0730000
[29020.752099] module_level_init+0x1a/0x1000 [kern]
[29020.752102] do_one_initcall+0x55/0x1b0
[29020.752103] ? _cond_resched+0x1a/0x50
[29020.752105] ? kmem_cache_alloc_trace+0x165/0x1c0
[29020.752106] do_init_module+0x5f/0x222
[29020.752108] load_module+0x1894/0x1ea0
[29020.752111] ? ima_post_read_file+0x83/0xa0
[29020.752112] SYSC_finit_module+0xe5/0x120
[29020.752113] ? SYSC_finit_module+0xe5/0x120
[29020.752115] SyS_finit_module+0xe/0x10
[29020.752116] do_syscall_64+0x73/0x130
The kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
int module_level_3(void)
{
dump_stack();
return 0;
}
int module_level_2(void)
{
module_level_3();
return 0;
}
static int __init module_level_init(void)
{
printk(KERN_INFO "Hello, world\n");
module_level_2();
return 0;
}
static void __exit module_level_exit(void)
{
printk(KERN_INFO "Goodbye, world\n");
}
module_init(module_level_init);
module_exit(module_level_exit);
MODULE_LICENSE("GPL");
Makefile:
obj-m += kern.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
objdump
#objdump -Sdlr kern.ko
kern.ko: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <module_level_3>:
module_level_3():
0: e8 00 00 00 00 callq 5 <module_level_3+0x5>
1: R_X86_64_PC32 __fentry__-0x4
5: 55 push %rbp
6: 48 89 e5 mov %rsp,%rbp
9: e8 00 00 00 00 callq e <module_level_3+0xe>
a: R_X86_64_PC32 dump_stack-0x4
e: 31 c0 xor %eax,%eax
10: 5d pop %rbp
11: c3 retq
12: 0f 1f 40 00 nopl 0x0(%rax)
16: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
1d: 00 00 00
0000000000000020 <module_level_2>:
module_level_2():
20: e8 00 00 00 00 callq 25 <module_level_2+0x5>
21: R_X86_64_PC32 __fentry__-0x4
25: 55 push %rbp
26: 48 89 e5 mov %rsp,%rbp
29: e8 00 00 00 00 callq 2e <module_level_2+0xe>
2a: R_X86_64_PC32 dump_stack-0x4
2e: 31 c0 xor %eax,%eax
30: 5d pop %rbp
31: c3 retq
Disassembly of section .init.text:
0000000000000000 <init_module>:
module_level_init():
0: e8 00 00 00 00 callq 5 <init_module+0x5>
1: R_X86_64_PC32 __fentry__-0x4
5: 55 push %rbp
6: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
9: R_X86_64_32S .rodata.str1.1
d: 48 89 e5 mov %rsp,%rbp
10: e8 00 00 00 00 callq 15 <init_module+0x15>
11: R_X86_64_PC32 printk-0x4
15: e8 00 00 00 00 callq 1a <init_module+0x1a>
16: R_X86_64_PC32 dump_stack-0x4
1a: 31 c0 xor %eax,%eax
1c: 5d pop %rbp
1d: c3 retq
Disassembly of section .exit.text:
0000000000000000 <cleanup_module>:
module_level_exit():
0: 55 push %rbp
1: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
4: R_X86_64_32S .rodata.str1.1+0x10
8: 48 89 e5 mov %rsp,%rbp
b: e8 00 00 00 00 callq 10 <cleanup_module+0x10>
c: R_X86_64_PC32 printk-0x4
10: 5d pop %rbp
11: c3 retq
I want to understand AFL's code instrumentation in detail.
Compiling a sample program sample.c
int main(int argc, char **argv) {
int ret = 0;
if(argc > 1) {
ret = 7;
} else {
ret = 12;
}
return ret;
}
with gcc -c -o obj/sample-gcc.o src/sample.c and afl-gcc -c -o obj/sample-afl-gcc.o src/sample.c and disassembling both with objdump -d leads to different Assembly code:
[GCC]
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 89 7d ec mov %edi,-0x14(%rbp)
b: 48 89 75 e0 mov %rsi,-0x20(%rbp)
f: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
16: 83 7d ec 01 cmpl $0x1,-0x14(%rbp)
1a: 7e 09 jle 25 <main+0x25>
1c: c7 45 fc 07 00 00 00 movl $0x7,-0x4(%rbp)
23: eb 07 jmp 2c <main+0x2c>
25: c7 45 fc 0c 00 00 00 movl $0xc,-0x4(%rbp)
2c: 8b 45 fc mov -0x4(%rbp),%eax
2f: 5d pop %rbp
30: c3 retq
[AFL-GCC]
0000000000000000 <main>:
0: 48 8d a4 24 68 ff ff lea -0x98(%rsp),%rsp
7: ff
8: 48 89 14 24 mov %rdx,(%rsp)
c: 48 89 4c 24 08 mov %rcx,0x8(%rsp)
11: 48 89 44 24 10 mov %rax,0x10(%rsp)
16: 48 c7 c1 0e ff 00 00 mov $0xff0e,%rcx
1d: e8 00 00 00 00 callq 22 <main+0x22>
22: 48 8b 44 24 10 mov 0x10(%rsp),%rax
27: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx
2c: 48 8b 14 24 mov (%rsp),%rdx
30: 48 8d a4 24 98 00 00 lea 0x98(%rsp),%rsp
37: 00
38: f3 0f 1e fa endbr64
3c: 31 c0 xor %eax,%eax
3e: 83 ff 01 cmp $0x1,%edi
41: 0f 9e c0 setle %al
44: 8d 44 80 07 lea 0x7(%rax,%rax,4),%eax
48: c3 retq
AFL (usually) adds a trampoline in front of every basic block to track executed paths [https://github.com/mirrorer/afl/blob/master/afl-as.h#L130]
-> Instruction 0x00 lea until 0x30 lea
AFL (usually) adds a main payload to the program (which I excluded due to simplicity) [https://github.com/mirrorer/afl/blob/master/afl-as.h#L381]
AFL claims to use a wrapper for GCC, so I expected everything else to be equal. Why is the if-else-condition still compiled differently?
Bonus question: If a binary without source code available should be instrumented manually without using AFL's QEMU-mode or Unicorn-mode, can this be achieved by (naively) adding the main payload and each trampoline manually to the binary file or are there better approaches?
Re: Why the compilation with gcc and with afl-gcc is different, a short look at the afl-gcc source shows that by default it modifies the compiler parameters, setting -O3 -funroll-loops (as well as defining __AFL_COMPILER and FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION).
According to the documentation (docs/env_variables.txt):
By default, the wrapper appends -O3 to optimize builds. Very rarely,
this will cause problems in programs built with -Werror, simply
because -O3 enables more thorough code analysis and can spew out
additional warnings. To disable optimizations, set AFL_DONT_OPTIMIZE.
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
I want to use my own shellcode for a buffer overflow exploit so for that I have written a script in C language[shellcode script].
I have used the following commands.:
gcc -c file.c -o file.o
objdump -sS -D file.o
root#kali:~/shellcode# cat file.c
#include<stdio.h>
int main()
{
printf("Hi");
}
The above code is of 'file.c'.
I expect the output of the 'objdump -sS -D file.o' to be free from null-bytes, but actually this is my output after typing that command:
file.o: file format elf64-x86-64
Contents of section .text:
0000 554889e5 488d3d00 000000b8 00000000 UH..H.=.........
0010 e8000000 00b80000 00005dc3 ..........].
Contents of section .rodata:
0000 486900 Hi.
Contents of section .comment:
0000 00474343 3a202844 65626961 6e20382e .GCC: (Debian 8.
0010 332e302d 36292038 2e332e30 00 3.0-6) 8.3.0.
Contents of section .eh_frame:
0000 14000000 00000000 017a5200 01781001 .........zR..x..
0010 1b0c0708 90010000 1c000000 1c000000 ................
0020 00000000 1c000000 00410e10 8602430d .........A....C.
0030 06570c07 08000000 .W......
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # b <main+0xb>
b: b8 00 00 00 00 mov $0x0,%eax
10: e8 00 00 00 00 callq 15 <main+0x15>
15: b8 00 00 00 00 mov $0x0,%eax
1a: 5d pop %rbp
1b: c3 retq
Disassembly of section .rodata:
0000000000000000 <.rodata>:
0: 48 rex.W
1: 69 .byte 0x69
...
Disassembly of section .comment:
0000000000000000 <.comment>:
0: 00 47 43 add %al,0x43(%rdi)
3: 43 3a 20 rex.XB cmp (%r8),%spl
6: 28 44 65 62 sub %al,0x62(%rbp,%riz,2)
a: 69 61 6e 20 38 2e 33 imul $0x332e3820,0x6e(%rcx),%esp
11: 2e 30 2d 36 29 20 38 xor %ch,%cs:0x38202936(%rip) # 3820294e <main+0x3820294e>
18: 2e 33 2e xor %cs:(%rsi),%ebp
1b: 30 00 xor %al,(%rax)
Disassembly of section .eh_frame:
0000000000000000 <.eh_frame>:
0: 14 00 adc $0x0,%al
2: 00 00 add %al,(%rax)
4: 00 00 add %al,(%rax)
6: 00 00 add %al,(%rax)
8: 01 7a 52 add %edi,0x52(%rdx)
b: 00 01 add %al,(%rcx)
d: 78 10 js 1f <.eh_frame+0x1f>
f: 01 1b add %ebx,(%rbx)
11: 0c 07 or $0x7,%al
13: 08 90 01 00 00 1c or %dl,0x1c000001(%rax)
19: 00 00 add %al,(%rax)
1b: 00 1c 00 add %bl,(%rax,%rax,1)
1e: 00 00 add %al,(%rax)
20: 00 00 add %al,(%rax)
22: 00 00 add %al,(%rax)
24: 1c 00 sbb $0x0,%al
26: 00 00 add %al,(%rax)
28: 00 41 0e add %al,0xe(%rcx)
2b: 10 86 02 43 0d 06 adc %al,0x60d4302(%rsi)
31: 57 push %rdi
32: 0c 07 or $0x7,%al
34: 08 00 or %al,(%rax)
...
Can somebody please explain me how I can remove null-bytes from this program, or if possible write the output in assembly so that I can learn what to change and how
P.S - I know mov $0x0, $rsp can be done by xor $rsp, $rsp but I don't know about movq, lea, add, sub, etc.
Thank you for your precious time.
Removing nullbytes (\x00) from shellcode is only necessary if you are using functions that depend on a trailing \x00, such as strcpy:
char * strcpy ( char * destination, const char * source );
which copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
However strncpy copies the first num characters of source to destination, padding it with zeros until num characters have been written to destination.
char * strncpy ( char * destination, const char * source, size_t num );
This means if you pass your shellcode size/length to the parameter num, it will copy all characters into the buffer, without the hassle of removing nullbytes as they aren't terminating copying from source to destination.
To get the length of the shellcode:
#include <stdio.h>
#include <string.h>
int main()
{
char* evil="\x90\x83\xc8\xff\xf7\xd0\x50";
printf("%d",strlen(evil));
}
will return:
7