First things first : forget about headers. allAll information about headers and separate C file areis gone.
You're missing some crucial step, imhoIMHO.
- Compilation create one or multiple object file (.o) then the linker create an executable.
Compilation creates one or multiple object files (.o), then the linker creates an executable.
- You should work from disassembled code. Disassembler works pretty well with some exception (self modifying code, self extracting executable, various obfuscation techniques) and will take care of a lot of work for you : identifying various sections, finding functions, guessing (fairly accurately) the calling convention.
You should work from disassembled code. The disassembler works pretty well with some exceptions (self-modifying code, self-extracting executable, various obfuscation techniques) and will take care of a lot of work for you: identifying various sections, finding functions, guessing (fairly accurately) the calling convention.
- then compiler optimization will mess up your code in a very clever way and some part of your original code will never ever be seen again (hey, look, your 200 lines of bugged code always return 0 anyway so i'll just replace them with "xor eax, eax").
Then the compiler optimization will mess up your code in a very clever way and some part of your original code will never ever be seen again (hey, look, your 200 lines of bugged code always return 0 anyway so I'll just replace them with "xor eax, eax").
- sometimes it's fine, and sometimes it produce unreadable C code (vectorization that have no C equivalent and will be decompiled into hundreds of line of intrinsic instead of a fine readable "for" loop).
Sometimes, it's fine, and sometimes it produces unreadable C code (vectorizations that have no C equivalent and will be decompiled into hundreds of lines of intrinsic instead of a fine readable "for" loop).
- i'm not done yet. you also have exception and interrupts, structures, union, function pointers, function inlining, threading, system call and signals, loop unrolling, ...
I'm not done yet. You also have exceptions and interrupts, structures, union, function pointers, function inlining, threading, system call and signals, loop unrolling, etc.
goingGoing down (from human readable-readable to binary) is fairlyrelatively easy compared to going up (decompilation) because so much information is lost during the compilation process.
myMy best bet would be to have a bunch of disassembled function produced by a disassemberdisassembler and produce an LLVM intermediate representation using your IAAI, then compare it with the LLVM IR produced by Clang (clang -S -emit-llvm foo.cclang -S -emit-llvm foo.c
).
An infinite quantity of C code can produce the exact same code. Therefore, iI think it's meaningless to make an AI read C code for the purpose of decompilation : the information is lost forever.
Commercial and Open/Free decompilerdecompilers do not produce C code either, they produce some kind of pseudo-C full of errorerrors, missing code, or code even less readable than the ASM.
produceproduces this :
andAnd this is the disassmbleddisassembled version :
plusPlus a few thousands linelines of assembly that are unrelated to your code but are needed to make the program work.
youYou can't go back and you have no way of knowing this is the exact same code unless 1) you can do static analysis (very easy in this case but absurdly difficult in the real world) 2) or compare the IR or ASM produced by both code with the same compiler with the same options on the same architecture and operating system.
you can do static analysis (very easy in this case, but absurdly difficult in the real world)
or compare the IR or ASM produced by both code with the same compiler with the same options on the same architecture and operating system.