Many of the previous answers have noted that this is impossible because the compiler will always leave artifacts behind. I decided to do a little case study using a program on Linux, although the idea is transferable.
For example, I created a small "Hello World!" file in C:
#include <stdio.h>
int main(void) {
puts("Hello World!");
return 0;
}
Then I compiled it and used hexdump -C
on the results. The following except pretty clearly identifies the compiler!
00001020 47 43 43 3a 20 28 55 62 75 6e 74 75 2f 4c 69 6e |GCC: (Ubuntu/Lin|
00001030 61 72 6f 20 34 2e 36 2e 33 2d 31 75 62 75 6e 74 |aro 4.6.3-1ubunt|
00001040 75 35 29 20 34 2e 36 2e 33 00 00 2e 73 79 6d 74 |u5) 4.6.3...symt|
00001050 61 62 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 |ab..strtab..shst|
00001060 72 74 61 62 00 2e 69 6e 74 65 72 70 00 2e 6e 6f |rtab..interp..no|
00001070 74 65 2e 41 42 49 2d 74 61 67 00 2e 6e 6f 74 65 |te.ABI-tag..note|
00001080 2e 67 6e 75 2e 62 75 69 6c 64 2d 69 64 00 2e 67 |.gnu.build-id..g|
00001090 6e 75 2e 68 61 73 68 00 2e 64 79 6e 73 79 6d 00 |nu.hash..dynsym.|
000010a0 2e 64 79 6e 73 74 72 00 2e 67 6e 75 2e 76 65 72 |.dynstr..gnu.ver|
000010b0 73 69 6f 6e 00 2e 67 6e 75 2e 76 65 72 73 69 6f |sion..gnu.versio|
000010c0 6e 5f 72 00 2e 72 65 6c 61 2e 64 79 6e 00 2e 72 |n_r..rela.dyn..r|
000010d0 65 6c 61 2e 70 6c 74 00 2e 69 6e 69 74 00 2e 74 |ela.plt..init..t|
000010e0 65 78 74 00 2e 66 69 6e 69 00 2e 72 6f 64 61 74 |ext..fini..rodat|
000010f0 61 00 2e 65 68 5f 66 72 61 6d 65 5f 68 64 72 00 |a..eh_frame_hdr.|
00001100 2e 65 68 5f 66 72 61 6d 65 00 2e 63 74 6f 72 73 |.eh_frame..ctors|
00001110 00 2e 64 74 6f 72 73 00 2e 6a 63 72 00 2e 64 79 |..dtors..jcr..dy|
00001120 6e 61 6d 69 63 00 2e 67 6f 74 00 2e 67 6f 74 2e |namic..got..got.|
00001130 70 6c 74 00 2e 64 61 74 61 00 2e 62 73 73 00 2e |plt..data..bss..|
00001140 63 6f 6d 6d 65 6e 74 00 00 00 00 00 00 00 00 00 |comment.........|
Running strip -R .comment
partially helps, and removes explicit mention of GCC completely, but there are still some tell-tale signs:
00000260 47 4e 55 00 00 00 00 00 02 00 00 00 06 00 00 00 |GNU.............|
00000270 18 00 00 00 04 00 00 00 14 00 00 00 03 00 00 00 |................|
00000280 47 4e 55 00 5f 8a 1b 97 01 5a ac d7 93 fb 96 29 |GNU._....Z.....)|
* * *
00000310 00 00 00 00 00 00 00 00 00 5f 5f 67 6d 6f 6e 5f |.........__gmon_|
00000320 73 74 61 72 74 5f 5f 00 6c 69 62 63 2e 73 6f 2e |start__.libc.so.|
00000330 36 00 70 75 74 73 00 5f 5f 6c 69 62 63 5f 73 74 |6.puts.__libc_st|
00000340 61 72 74 5f 6d 61 69 6e 00 47 4c 49 42 43 5f 32 |art_main.GLIBC_2|
00000350 2e 32 2e 35 00 00 00 00 02 00 02 00 00 00 00 00 |.2.5............|
* * *
00001020 00 2e 73 68 73 74 72 74 61 62 00 2e 69 6e 74 65 |..shstrtab..inte|
00001030 72 70 00 2e 6e 6f 74 65 2e 41 42 49 2d 74 61 67 |rp..note.ABI-tag|
00001040 00 2e 6e 6f 74 65 2e 67 6e 75 2e 62 75 69 6c 64 |..note.gnu.build|
00001050 2d 69 64 00 2e 67 6e 75 2e 68 61 73 68 00 2e 64 |-id..gnu.hash..d|
00001060 79 6e 73 79 6d 00 2e 64 79 6e 73 74 72 00 2e 67 |ynsym..dynstr..g|
00001070 6e 75 2e 76 65 72 73 69 6f 6e 00 2e 67 6e 75 2e |nu.version..gnu.|
00001080 76 65 72 73 69 6f 6e 5f 72 00 2e 72 65 6c 61 2e |version_r..rela.|
00001090 64 79 6e 00 2e 72 65 6c 61 2e 70 6c 74 00 2e 69 |dyn..rela.plt..i|
000010a0 6e 69 74 00 2e 74 65 78 74 00 2e 66 69 6e 69 00 |nit..text..fini.|
000010b0 2e 72 6f 64 61 74 61 00 2e 65 68 5f 66 72 61 6d |.rodata..eh_fram|
000010c0 65 5f 68 64 72 00 2e 65 68 5f 66 72 61 6d 65 00 |e_hdr..eh_frame.|
000010d0 2e 63 74 6f 72 73 00 2e 64 74 6f 72 73 00 2e 6a |.ctors..dtors..j|
000010e0 63 72 00 2e 64 79 6e 61 6d 69 63 00 2e 67 6f 74 |cr..dynamic..got|
000010f0 00 2e 67 6f 74 2e 70 6c 74 00 2e 64 61 74 61 00 |..got.plt..data.|
00001100 2e 62 73 73 00 00 00 00 00 00 00 00 00 00 00 00 |.bss............|
I decided to see what would happen if I replaced the last portion of this code with null bytes. The program still worked fine, but this decreases portability to other operating systems.
If you try to replace GNU
with another compiler, say, clang
, it still works fine. Although it may confuse the reverse engineer, it certainly isn't that difficult to notice.
Finally, I tried to see what happens if I removed any trace of GLIBC
...
./a.out: ./a.out: no version information available (required by ./a.out)
./a.out: relocation error: ./a.out: symbol , version not defined in file with link time reference