Consider the following C source file:
#include <stdio.h>
void printff() {
printf("this is called funtion printff");
}
int main(int argc, char *argv[]) {
printff();
printf("This is helo analysis");
if (argc == 2) {
printf("The argument supplied is %s\n", argv[1]);
} else if (argc > 2) {
printf("Too many arguments supplied.\n");
} else {
printf("One argument expected.\n");
}
return 0;
}
Compile it with GCC:
gcc hello.c -o a.exe
Running the binary yields expected output:
> a.exe hello
this is called funtion printffThis is helo analysisThe argument supplied is hello
To decompile a.exe, use RetDec:
retdec-decompiler.exe a.exe
This generates five output files:
a.exe.bc: LLVM bytecodea.exe.ll: Human-readable LLVM IRa.exe.dsm: Disassembled machine codea.exe.config.json: Configuration metadataa.exe.c: Reconstructed C source
The decompiled C file appears as follows:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int32_t ___do_global_ctors(void);
int32_t ___main(void);
int32_t _printff(void);
int32_t g1 = -1;
int32_t g2 = 0;
int32_t _printff(void) {
return printf("this is called funtion printff");
}
int main(int argc, char **argv) {
___main();
_printff();
printf("This is helo analysis");
if (argc == 2) {
int32_t arg_ptr = *(int32_t *)((int32_t)argv + 4);
printf("The argument supplied is %s\n", (char *)arg_ptr);
return 0;
}
if (argc < 3) {
puts("One argument expected.");
} else {
puts("Too many arguments supplied.");
}
return 0;
}
int32_t ___do_global_ctors(void) {
int32_t i = 0;
int32_t j = i + 1;
while (*(int32_t *)(4 * j + (int32_t)&g1) != 0) {
i = j;
j = i + 1;
}
if (i == 0) {
return atexit((void (*)())0x401990);
}
int32_t k = i;
while (k != 1) {
k--;
}
return atexit((void (*)())0x401990);
}
int32_t ___main(void) {
int32_t result = g2;
if (result != 0) {
return result;
}
g2 = 1;
return ___do_global_ctors();
}
Recompiling this decompiled source works for simple cases:
gcc a.exe.c -o a2.exe
Executing the recompiled binary reproduces the original behavior:
> a2.exe hello
this is called funtion printffThis is helo analysisThe argument supplied is hello
While straightforward programs like this can be cleanly reconstructed and recompiled, complex binaries often produce decompiled code that fails to compile directly. In such scenarios, manual analysis and rewriting—guided by reverse engineering expertise—are necsesary to recover functional equivalents.