Post

Decompiler Construction: Chapter 18 Practical Decompilation Results

**This is a decompilation result as of 8/3/2026, information output passed this date is assumed to be outdated. To try the decompiler on your own visit: ** Luramas

To stress-test the process, I have included this program, which includes:

  • State-dependent control flow with continuously mutating variables
  • Mixed arithmetic and bitwise operations (XOR, shifts, rotation patterns)
  • Early loop terminations
  • Pointers
  • Redundant computations that should be simplified

The following input program is compiled and passed through the decompilation process:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdint.h>
volatile int32_t sink;
typedef struct {
    int32_t a; int32_t b; int32_t c;
} Node;
int32_t func(int32_t *arr, const Node *n, const int32_t x) {
	
    int32_t acc = 0;
    int32_t i = 0;
    int32_t state = (x ^ 0x5A) + (n->a << 1);
    while (i < 10) {
        state = state ^ (i * 3);
        if ((state & 1) == 0) { acc += arr[i] + n->b; } else { acc += arr[i] ^ n->c; }
        if (acc > 100) { break; }
        ++i;
        if (!(i % 3)) { continue; }
        state = (state << 1) | (state >> 31);
    }
    int32_t tmp = acc * 2;
    tmp = tmp - acc;
    if (x < 0) { tmp = tmp ^ 0xDEADBEEF; }
    sink = tmp;
    return acc + state;
}
int32_t main(void) {
    int32_t arr = 9;
    for (int32_t t = 0; t < 1000; ++t) { 
        Node n = { .a = t, .b = t ^ 0xAA, .c = t + 3};  func(&arr, &n, 19); }
    return 0;
}

Typically this step you would use DBI but for this example we have already compiled, extracted machine code, and mapped edges.

Assembly, this was compiled with CLANG.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0x0: push rbp
0x1: mov rbp, rsp
0x4: sub rsp, 0x20
0x8: mov dword ptr [rbp - 4], 0
0xf: mov dword ptr [rbp - 8], 9
0x16: mov dword ptr [rbp - 0xc], 0
0x1d: cmp dword ptr [rbp - 0xc], 0x3e8
0x24: jge 0x5d
0x26: mov eax, dword ptr [rbp - 0xc]
0x29: mov dword ptr [rbp - 0x18], eax
0x2c: mov eax, dword ptr [rbp - 0xc]
0x2f: xor eax, 0xaa
0x34: mov dword ptr [rbp - 0x14], eax
0x37: mov eax, dword ptr [rbp - 0xc]
0x3a: add eax, 3
0x3d: mov dword ptr [rbp - 0x10], eax
0x40: lea rdi, [rbp - 8]
0x44: lea rsi, [rbp - 0x18]
0x48: mov edx, 0x13
0x4d: call 0x65
....

Download Full Assembly

Builder for Luramas Decompiler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  /* Build assembly */
  {
        /* Build data */
        b.emitd({0x55});                                                                                                     /* push rbp */
        b.emitd({0x48, 0x89, 0xE5});                                                                                         /* mov rbp, rsp */
        b.emitd({0x48, 0x83, 0xEC, 0x20});                                                                                   /* sub rsp, 0x20 */
        b.emitd({0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00});                                                                 /* mov dword ptr [rbp - 0x4], 0x0 */
        b.emitd({0xC7, 0x45, 0xF8, 0x09, 0x00, 0x00, 0x00});                                                                 /* mov dword ptr [rbp - 0x8], 0x9 */
        b.emitd({0xC7, 0x45, 0xF4, 0x00, 0x00, 0x00, 0x00});                                                                 /* mov dword ptr [rbp - 0xc], 0x0 */
        const auto label_1D = *b.emit_label(0x1D);                                                                           /* label_1D: */
        b.emitd({0x81, 0x7D, 0xF4, 0xE8, 0x03, 0x00, 0x00});                                                                 /* cmp dword ptr [rbp - 0xc], 0x3e8 */
        const auto i_jge_5d_rpc = b.emitd({0x7D, 0x37}, edges, JUMP).first;                           /* jge label_5D */
        b.emitd({0x8B, 0x45, 0xF4});                                                                                         /* mov eax, dword ptr [rbp - 0xc] */
        b.emitd({0x89, 0x45, 0xE8});                                                                                         /* mov dword ptr [rbp - 0x18], eax */
        b.emitd({0x8B, 0x45, 0xF4});                                                                                         /* mov eax, dword ptr [rbp - 0xc] */
        b.emitd({0x35, 0xAA, 0x00, 0x00, 0x00});                                                                             /* xor eax, 0xaa */
        b.emitd({0x89, 0x45, 0xEC});                                                                                         /* mov dword ptr [rbp - 0x14], eax */
        b.emitd({0x8B, 0x45, 0xF4});                                                                                         /* mov eax, dword ptr [rbp - 0xc] */
        b.emitd({0x83, 0xC0, 0x03});                                                                                         /* add eax, 0x3 */
        b.emitd({0x89, 0x45, 0xF0});                                                                                         /* mov dword ptr [rbp - 0x10], eax */
        b.emitd({0x48, 0x8D, 0x7D, 0xF8});                                                                                   /* lea rdi, [rbp - 0x8] */
        b.emitd({0x48, 0x8D, 0x75, 0xE8});                                                                                   /* lea rsi, [rbp - 0x18] */	
....

Download Full Builder

We will lift it to IL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
107 SHRN r264, r263, 32 
108 XOR r265, r263, r264 
109 MOVE r263, r265 
110 SHRN r266, r263, 16 
111 XOR r267, r263, r266 
112 MOVE r263, r267 
113 SHRN r268, r263, 8 
114 XOR r269, r263, r268 
115 MOVE r263, r269 
116 SHRN r270, r263, 4 
117 XOR r271, r263, r270 
118 MOVE r263, r271 
119 SHRN r272, r263, 2 
120 XOR r273, r263, r272 
121 MOVE r263, r273 
122 SHRN r274, r263, 1 
123 XOR r275, r263, r274 
124 MOVE r263, r275 
125 ANDN r276, r263, 1 
126 BITNOT r277, r276 
127 ANDN r278, r277, 1 
128 FLAGSET 16, r278 
129 BITCAST r250, r250, 64, 0, false 
130 MOVE r44, r250 
131 NOP 
132 BITCAST r248, r248, 32, 0, false 
133 LOADINT r249, 0 
134 MOVE r248, r249 
135 BITCAST r250, r250, 32, 0, false
.... 

Download Full IL

We will then lift the IL to IR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
r260 = memread<int32_t>(r250)
r259 = (r260 - r255)
r259 = int32_t(r259)
r262 = memread<int32_t>(r250)
r261 = (r262 & 15)
r261 = int32_t(r261)
r263 = (r253 & 15)
r263 = int32_t(r263)
r264 = (r261 + r263)
r264 = int32_t(r264)
r265 = (r264 > 15)
r265 = int32_t(r265)
r266 = (r259 & 15)
r266 = int32_t(r266)
r268 = memread<int32_t>(r250)
r267 = (r268 & 15)
r267 = int32_t(r267)
r269 = (r266 < r267)
r269 = int32_t(r269)
r270 = (r265 | r269)
r270 = int32_t(r270)
r270 = int32_t(r270)
....

Download Full IR

Generated CFG: UNOPTCFG Graph

Download GraphViz Example

The IR currently is unsimplified. We will use various passes to simplify it, here are the most common ones:

  • Constant folding
  • Page constructions
  • Dead code elimination
  • Jump threading
  • Loop reconstruction
  • Expression canonicalization
  • Loop simplification
  • Constant Propagation
  • Dead store elimination We will not be doing any memory inferance for safety.

Again this was ran on 8/3/2026 with Luramas assume it is outdated. You can use the builder above to test with Luramas

Completely Safe IR:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DEFINITION([ARGS]{}, [UPVALUES]{}, [FLAGS]{}, [CASTS]{}, [RESULTS]{})
page_function_goto<100>(a36, a40, a44)
page_function_start (98) {
[PK] DEFINITION([ARGS]{a36, a44}, [UPVALUES]{}, [FLAGS]{}, [CASTS]{int32_t(a44), int64_t(a36)}, [RESULTS]{})
memset<int32_t>(331) = memread<int32_t>((a36 - 36))
local r35 = memread<int32_t>((a36 - 24))
a36 = memread<int32_t>(a44)
a44 = (a44 + 4)
[PK] [RT=a44] return ; 98
} page_function_end(98)
page_function_start (100) {
[PK] DEFINITION([ARGS]{a36, a40, a44}, [UPVALUES]{}, [FLAGS]{}, [CASTS]{int64_t(a36), int32_t(a44), int64_t(r44)}, [RESULTS]{})
memset<int32_t>((a44 - 4)) = a36
a36 = a44
a44 = (a44 - 32)
memset<int32_t>((a36 - 4)) = 0
....

Download Full Optimized IR

Cpp Code Generation uses Luramas-Helpers For Cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__forceinline void pf98(std::int32_t val, std::int64_t val_bs);
__forceinline void pf100(std::int64_t dyn_val_bs, std::int32_t a_z, std::int64_t val_bs);


lura_page_function_goto<pf100>(a, a_c, a_e);
__forceinline void pf98(std::int32_t val, std::int64_t val_bs) {
   lura_memset<std::int32_t>(331) = lura_memread<std::int32_t>((val - 36));
   lura_deadv(auto mem = lura_memread<std::int32_t>((val - 24)));
   val = lura_memread<std::int32_t>(val_bs);
   val_bs += 4;
   return;
}

__forceinline void pf100(std::int64_t dyn_val_bs, std::int32_t a_z, std::int64_t val_bs) {
   lura_memset<std::int32_t>(val_bs - 4) = dyn_val_bs;
   dyn_val_bs = val_bs;
   val_bs -= 32;
....

Download Full CPP Code-Generation


Prev Chapter: Chapter 17 - Lowering IR to Readable and Executable Code

This post is licensed under CC BY 4.0 by the author.