rust_code_obfuscator 0.3.1

A Rust library to easily obfuscate strings and control-flow using cryptify lib
Documentation
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
<!DOCTYPE html>
<html lang="it">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Code Obfuscation - appunti pratici</title>
  <meta name="description" content="Appunti pratici sull'offuscamento del codice: limiti, tecniche utili, esempi e criteri di valutazione." />
  <style>
    :root {
      --bg: #fbfaf7;
      --fg: #171717;
      --muted: #5f5a52;
      --line: #ded8ce;
      --panel: #f1eee8;
      --code: #ece7dd;
      --accent: #8a4b24;
      --accent-soft: #ead8c8;
    }

    * { box-sizing: border-box; }

    body {
      margin: 0;
      background: var(--bg);
      color: var(--fg);
      font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
      line-height: 1.65;
    }

    .page {
      max-width: 920px;
      margin: 0 auto;
      padding: 48px 22px 72px;
    }

    header {
      display: grid;
      gap: 22px;
      padding-bottom: 30px;
      border-bottom: 1px solid var(--line);
    }

    .topbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 16px;
      flex-wrap: wrap;
    }

    .eyebrow {
      margin: 0;
      color: var(--accent);
      font: 700 0.78rem/1.2 ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
      letter-spacing: .08em;
      text-transform: uppercase;
    }

    h1, h2, h3 { line-height: 1.18; }

    h1 {
      max-width: 760px;
      margin: 0;
      font-size: clamp(2.2rem, 6vw, 4.8rem);
      font-weight: 500;
      letter-spacing: -0.04em;
    }

    h2 {
      margin: 56px 0 14px;
      font-size: clamp(1.55rem, 3vw, 2.2rem);
      font-weight: 500;
      letter-spacing: -0.025em;
    }

    h3 {
      margin: 34px 0 10px;
      font-size: 1.15rem;
      font-weight: 700;
    }

    p { margin: 0 0 14px; }

    .lead {
      max-width: 760px;
      margin: 0;
      color: var(--muted);
      font-size: clamp(1.15rem, 2vw, 1.45rem);
    }

    .note {
      margin: 24px 0;
      padding: 18px 20px;
      background: var(--panel);
      border-left: 3px solid var(--accent);
      color: #342d28;
    }

    .muted { color: var(--muted); }

    .grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 14px;
      margin: 22px 0;
    }

    .card {
      padding: 18px;
      background: var(--panel);
      border: 1px solid var(--line);
      border-radius: 14px;
    }

    .card strong {
      display: block;
      margin-bottom: 6px;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
      font-size: .9rem;
    }

    .card p {
      margin: 0;
      color: var(--muted);
      font-size: .96rem;
    }

    pre {
      margin: 18px 0 8px;
      padding: 18px;
      overflow-x: auto;
      background: var(--code);
      border: 1px solid var(--line);
      border-radius: 14px;
    }

    code {
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
      font-size: .88rem;
    }

    p code, li code {
      background: var(--code);
      border-radius: 5px;
      padding: .12em .35em;
    }

    ul {
      padding-left: 1.2rem;
      color: var(--muted);
    }

    li + li { margin-top: 7px; }

    .langbar {
      display: flex;
      gap: 8px;
      align-items: center;
      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
    }

    .langbtn {
      border: 1px solid var(--line);
      background: transparent;
      color: var(--muted);
      padding: 7px 11px;
      border-radius: 999px;
      cursor: pointer;
      font-weight: 650;
    }

    .langbtn[aria-pressed="true"],
    .langbtn:hover {
      background: var(--accent);
      border-color: var(--accent);
      color: white;
    }

    .footer {
      margin-top: 56px;
      padding-top: 22px;
      border-top: 1px solid var(--line);
      color: var(--muted);
      font-size: .95rem;
    }

    a { color: var(--accent); text-underline-offset: 3px; }

    .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0,0,0,0);
      white-space: nowrap;
      border: 0;
    }

    noscript {
      display: block;
      margin: 20px 0;
      padding: 14px 16px;
      background: var(--accent-soft);
      border-radius: 10px;
    }

    @media (max-width: 760px) {
      .page { padding-top: 32px; }
      .grid { grid-template-columns: 1fr; }
    }
  </style>
</head>
<body>
  <main class="page">
    <header>
      <div class="topbar">
        <p class="eyebrow" data-i18n="eyebrow">Reverse engineering / software protection</p>
        <nav class="langbar" aria-label="Selettore lingua">
          <span class="sr-only" id="langStatus" aria-live="polite"></span>
          <button class="langbtn" data-lang="it" aria-pressed="true">IT</button>
          <button class="langbtn" data-lang="en" aria-pressed="false">EN</button>
          <button class="langbtn" data-lang="pl" aria-pressed="false">PL</button>
        </nav>
      </div>

      <h1 data-i18n="title">Offuscare codice non significa renderlo inviolabile.</h1>
      <p class="lead" data-i18n="lead">Significa aumentare il costo dell’analisi. A volte basta far perdere tempo. A volte serve proteggere una parte precisa del flusso. In ogni caso, va progettato con un modello di minaccia reale, non come decorazione.</p>
    </header>

    <noscript data-i18n="noscript">Il cambio lingua richiede JavaScript. La pagina resta leggibile in italiano.</noscript>

    <section>
      <h2 data-i18n="what_h2">Il punto di partenza</h2>
      <p class="muted" data-i18n="what_p1">Un obfuscator prende un programma e ne produce uno equivalente dal punto di vista osservabile, ma più difficile da capire, modificare o ricostruire. Questo non crea sicurezza assoluta: se un segreto deve comparire in memoria, un attaccante abbastanza motivato può arrivarci.</p>
      <p class="muted" data-i18n="what_p2">Il valore pratico sta nel compromesso: rendere costosa l’analisi statica, disturbare quella dinamica e ridurre la probabilità che un attaccante scelga proprio quel binario come bersaglio facile.</p>

      <div class="note" data-i18n="note1">Una buona offuscazione non si misura da quanto “strano” sembra il codice, ma da quanto tempo richiede estrarre ciò che volevi proteggere.</div>
    </section>

    <section>
      <h2 data-i18n="threat_h2">Prima domanda: contro chi?</h2>
      <div class="grid">
        <article class="card">
          <strong data-i18n="threat1_h">Solo file binario</strong>
          <p data-i18n="threat1_p">L’attaccante analizza il file con strumenti statici. Qui contano nomi, stringhe, grafo di controllo e pattern riconoscibili.</p>
        </article>
        <article class="card">
          <strong data-i18n="threat2_h">Runtime libero</strong>
          <p data-i18n="threat2_p">L’attaccante può eseguire, debuggare, tracciare, usare hook o emulatori. L’offuscamento diventa solo uno strato.</p>
        </article>
        <article class="card">
          <strong data-i18n="threat3_h">Tempo limitato</strong>
          <p data-i18n="threat3_p">È lo scenario più comune: non devi fermare tutti, devi rendere poco conveniente l’analisi rispetto al valore ottenuto.</p>
        </article>
      </div>
    </section>

    <section>
      <h2 data-i18n="tech_h2">Tecniche che hanno senso</h2>

      <h3 data-i18n="t1_h3">1. Stringhe e dati immediatamente riconoscibili</h3>
      <p class="muted" data-i18n="t1_p">URL, nomi di endpoint, messaggi interni e token non dovrebbero stare in chiaro nel binario. Codificarli e decodificarli solo al bisogno è una protezione semplice, ma utile contro analisi superficiali.</p>
      <pre><code class="language-rust">fn decrypt_xor(data: &[u8], key: u8) -> String {
    let bytes: Vec&lt;u8&gt; = data.iter().map(|b| b ^ key).collect();
    String::from_utf8_lossy(&bytes).into_owned()
}

fn main() {
    let encoded = [0x2a, 0x27, 0x30, 0x30];
    let value = decrypt_xor(&encoded, 0x42);
    println!("{}", value);
}</code></pre>
      <p class="muted" data-i18n="t1_note">Da sola non basta: una chiave statica si recupera. Meglio derivare chiavi a runtime, spezzare dati sensibili e ridurre il tempo in cui restano in chiaro.</p>

      <h3 data-i18n="t2_h3">2. Control-flow flattening</h3>
      <p class="muted" data-i18n="t2_p">Il flattening rende meno leggibile il grafo di controllo, trasformando una logica lineare in un dispatcher. È utile contro decompilatori e lettura rapida, ma può peggiorare performance e manutenzione.</p>

      <h3 data-i18n="t3_h3">3. Virtualizzazione</h3>
      <p class="muted" data-i18n="t3_p">La virtualizzazione traduce porzioni di programma in bytecode custom, eseguito da un interprete interno. È più pesante, ma spesso più efficace sulle funzioni davvero sensibili.</p>
      <pre><code class="language-rust">fn exec(code: &[u8]) -> i64 {
    let mut pc = 0usize;
    let mut stack = Vec::new();

    while pc &lt; code.len() {
        match code[pc] {
            0x01 =&gt; { pc += 1; stack.push(code[pc] as i64); }
            0x02 =&gt; {
                let b = stack.pop().unwrap_or(0);
                let a = stack.pop().unwrap_or(0);
                stack.push(a + b);
            }
            0xff =&gt; break,
            _ =&gt; break,
        }
        pc += 1;
    }

    stack.pop().unwrap_or(0)
}</code></pre>

      <h3 data-i18n="t4_h3">4. Controlli anti-debug e anti-tamper</h3>
      <p class="muted" data-i18n="t4_p">Controlli su debugger, timing, checksum delle sezioni eseguibili o integrità dei file possono rallentare l’analisi dinamica. Non vanno trattati come muri, ma come attrito.</p>
      <pre><code class="language-rust">fn being_traced() -> bool {
    let Ok(status) = std::fs::read_to_string("/proc/self/status") else {
        return false;
    };

    status
        .lines()
        .find(|line| line.starts_with("TracerPid:"))
        .and_then(|line| line.split_whitespace().last())
        .is_some_and(|pid| pid != "0")
}</code></pre>
    </section>

    <section>
      <h2 data-i18n="metrics_h2">Come capire se serve davvero</h2>
      <ul>
        <li data-i18n="m1">Misura il tempo necessario a recuperare una stringa, una chiave o una funzione critica.</li>
        <li data-i18n="m2">Confronta output di Ghidra, IDA o altri decompilatori prima e dopo la trasformazione.</li>
        <li data-i18n="m3">Controlla l’impatto su performance, dimensione binaria, crash rate e debugging interno.</li>
      </ul>
      <p class="muted" data-i18n="metrics_p">La baseline non offuscata deve restare disponibile per audit, test e manutenzione. L’offuscamento va applicato alla release, non al modo in cui il team ragiona sul codice.</p>
    </section>

    <section>
      <h2 data-i18n="best_h2">Regole pratiche</h2>
      <ul>
        <li data-i18n="b1">Proteggi poche parti ad alto valore, non tutto il programma senza criterio.</li>
        <li data-i18n="b2">Non spedire segreti statici quando puoi derivarli o recuperarli da un canale controllato.</li>
        <li data-i18n="b3">Automatizza test di equivalenza dopo ogni trasformazione.</li>
        <li data-i18n="b4">Prevedi rotazione, revoca e aggiornamento: prima o poi qualcosa verrà estratto.</li>
      </ul>
    </section>

    <section class="note">
      <strong data-i18n="summary_h">In sintesi</strong>
      <p data-i18n="summary_p">L’offuscamento è utile quando è mirato, misurabile e inserito in una strategia più ampia. Non sostituisce sicurezza, design corretto o gestione seria dei segreti. Aggiunge costo. E in molti casi è esattamente quello che serve.</p>
    </section>

    <footer class="footer">
      <p>- <a href="https://www.linkedin.com/in/gianfranco-iaculo" rel="me">Gianfranco Iaculo</a></p>
    </footer>
  </main>

  <script>
    const I18N = {
      it: {
        eyebrow: "Reverse engineering / software protection",
        title: "Offuscare codice non significa renderlo inviolabile.",
        lead: "Significa aumentare il costo dell’analisi. A volte basta far perdere tempo. A volte serve proteggere una parte precisa del flusso. In ogni caso, va progettato con un modello di minaccia reale, non come decorazione.",
        noscript: "Il cambio lingua richiede JavaScript. La pagina resta leggibile in italiano.",
        what_h2: "Il punto di partenza",
        what_p1: "Un obfuscator prende un programma e ne produce uno equivalente dal punto di vista osservabile, ma più difficile da capire, modificare o ricostruire. Questo non crea sicurezza assoluta: se un segreto deve comparire in memoria, un attaccante abbastanza motivato può arrivarci.",
        what_p2: "Il valore pratico sta nel compromesso: rendere costosa l’analisi statica, disturbare quella dinamica e ridurre la probabilità che un attaccante scelga proprio quel binario come bersaglio facile.",
        note1: "Una buona offuscazione non si misura da quanto “strano” sembra il codice, ma da quanto tempo richiede estrarre ciò che volevi proteggere.",
        threat_h2: "Prima domanda: contro chi?",
        threat1_h: "Solo file binario",
        threat1_p: "L’attaccante analizza il file con strumenti statici. Qui contano nomi, stringhe, grafo di controllo e pattern riconoscibili.",
        threat2_h: "Runtime libero",
        threat2_p: "L’attaccante può eseguire, debuggare, tracciare, usare hook o emulatori. L’offuscamento diventa solo uno strato.",
        threat3_h: "Tempo limitato",
        threat3_p: "È lo scenario più comune: non devi fermare tutti, devi rendere poco conveniente l’analisi rispetto al valore ottenuto.",
        tech_h2: "Tecniche che hanno senso",
        t1_h3: "1. Stringhe e dati immediatamente riconoscibili",
        t1_p: "URL, nomi di endpoint, messaggi interni e token non dovrebbero stare in chiaro nel binario. Codificarli e decodificarli solo al bisogno è una protezione semplice, ma utile contro analisi superficiali.",
        t1_note: "Da sola non basta: una chiave statica si recupera. Meglio derivare chiavi a runtime, spezzare dati sensibili e ridurre il tempo in cui restano in chiaro.",
        t2_h3: "2. Control-flow flattening",
        t2_p: "Il flattening rende meno leggibile il grafo di controllo, trasformando una logica lineare in un dispatcher. È utile contro decompilatori e lettura rapida, ma può peggiorare performance e manutenzione.",
        t3_h3: "3. Virtualizzazione",
        t3_p: "La virtualizzazione traduce porzioni di programma in bytecode custom, eseguito da un interprete interno. È più pesante, ma spesso più efficace sulle funzioni davvero sensibili.",
        t4_h3: "4. Controlli anti-debug e anti-tamper",
        t4_p: "Controlli su debugger, timing, checksum delle sezioni eseguibili o integrità dei file possono rallentare l’analisi dinamica. Non vanno trattati come muri, ma come attrito.",
        metrics_h2: "Come capire se serve davvero",
        m1: "Misura il tempo necessario a recuperare una stringa, una chiave o una funzione critica.",
        m2: "Confronta output di Ghidra, IDA o altri decompilatori prima e dopo la trasformazione.",
        m3: "Controlla l’impatto su performance, dimensione binaria, crash rate e debugging interno.",
        metrics_p: "La baseline non offuscata deve restare disponibile per audit, test e manutenzione. L’offuscamento va applicato alla release, non al modo in cui il team ragiona sul codice.",
        best_h2: "Regole pratiche",
        b1: "Proteggi poche parti ad alto valore, non tutto il programma senza criterio.",
        b2: "Non spedire segreti statici quando puoi derivarli o recuperarli da un canale controllato.",
        b3: "Automatizza test di equivalenza dopo ogni trasformazione.",
        b4: "Prevedi rotazione, revoca e aggiornamento: prima o poi qualcosa verrà estratto.",
        summary_h: "In sintesi",
        summary_p: "L’offuscamento è utile quando è mirato, misurabile e inserito in una strategia più ampia. Non sostituisce sicurezza, design corretto o gestione seria dei segreti. Aggiunge costo. E in molti casi è esattamente quello che serve."
      },
      en: {
        eyebrow: "Reverse engineering / software protection",
        title: "Obfuscating code does not make it unbreakable.",
        lead: "It raises the cost of analysis. Sometimes that means wasting an attacker’s time. Sometimes it means protecting one specific part of the flow. Either way, it needs a real threat model, not decorative complexity.",
        noscript: "Language switching requires JavaScript. The page remains readable in Italian.",
        what_h2: "The starting point",
        what_p1: "An obfuscator takes a program and produces an observably equivalent one that is harder to understand, modify, or reconstruct. This does not create absolute security: if a secret must appear in memory, a motivated attacker can eventually reach it.",
        what_p2: "The practical value is the tradeoff: make static analysis expensive, disrupt dynamic analysis, and reduce the chance that your binary is treated as an easy target.",
        note1: "Good obfuscation is not measured by how strange the code looks, but by how long it takes to extract the thing you wanted to protect.",
        threat_h2: "First question: against whom?",
        threat1_h: "Binary-only access",
        threat1_p: "The attacker analyzes the file with static tools. Names, strings, control-flow graphs and recognizable patterns matter here.",
        threat2_h: "Free runtime access",
        threat2_p: "The attacker can run, debug, trace, hook or emulate the program. Obfuscation becomes one layer, not the whole defense.",
        threat3_h: "Limited time",
        threat3_p: "This is the common case: you do not need to stop everyone, you need to make analysis less convenient than the value it returns.",
        tech_h2: "Techniques that make sense",
        t1_h3: "1. Strings and obvious data",
        t1_p: "URLs, endpoint names, internal messages and tokens should not sit plainly in the binary. Encoding them and decoding only when needed is simple, but useful against shallow analysis.",
        t1_note: "On its own, this is not enough: a static key can be recovered. Runtime derivation, splitting sensitive data, and reducing cleartext lifetime are better.",
        t2_h3: "2. Control-flow flattening",
        t2_p: "Flattening makes the control-flow graph harder to read by turning linear logic into a dispatcher. It helps against decompilers and quick reading, but can hurt performance and maintenance.",
        t3_h3: "3. Virtualization",
        t3_p: "Virtualization translates selected program parts into custom bytecode executed by an internal interpreter. It is heavier, but often more effective for genuinely sensitive functions.",
        t4_h3: "4. Anti-debug and anti-tamper checks",
        t4_p: "Checks for debuggers, timing anomalies, executable section checksums or file integrity can slow dynamic analysis. Treat them as friction, not walls.",
        metrics_h2: "How to know whether it is useful",
        m1: "Measure the time needed to recover a string, key, or critical function.",
        m2: "Compare Ghidra, IDA, or other decompiler output before and after the transform.",
        m3: "Track the impact on performance, binary size, crash rate, and internal debugging.",
        metrics_p: "The unobfuscated baseline should remain available for audits, tests, and maintenance. Obfuscation belongs in the release pipeline, not in the way the team reasons about the code.",
        best_h2: "Practical rules",
        b1: "Protect a few high-value areas, not the whole program blindly.",
        b2: "Do not ship static secrets when you can derive them or fetch them through a controlled channel.",
        b3: "Automate equivalence tests after every transform.",
        b4: "Plan rotation, revocation, and updates: eventually something will be extracted.",
        summary_h: "In short",
        summary_p: "Obfuscation is useful when it is targeted, measurable, and part of a wider strategy. It does not replace security, sound design, or serious secret management. It adds cost. In many cases, that is exactly what you need."
      },
      pl: {
        eyebrow: "Reverse engineering / ochrona oprogramowania",
        title: "Zaciemnianie kodu nie czyni go niezniszczalnym.",
        lead: "Podnosi koszt analizy. Czasem wystarczy zabrać atakującemu czas. Czasem trzeba chronić konkretny fragment przepływu. W każdym przypadku potrzebny jest realny model zagrożeń, a nie dekoracyjna złożoność.",
        noscript: "Przełączanie języka wymaga JavaScript. Strona pozostaje czytelna po włosku.",
        what_h2: "Punkt wyjścia",
        what_p1: "Obfuscator bierze program i tworzy jego obserwowalnie równoważną wersję, trudniejszą do zrozumienia, zmiany lub odtworzenia. To nie daje absolutnego bezpieczeństwa: jeśli sekret musi pojawić się w pamięci, zdeterminowany atakujący w końcu do niego dotrze.",
        what_p2: "Wartość praktyczna polega na kompromisie: utrudnić analizę statyczną, zakłócić analizę dynamiczną i zmniejszyć szansę, że binarka zostanie uznana za łatwy cel.",
        note1: "Dobre zaciemnianie mierzy się nie tym, jak dziwnie wygląda kod, lecz tym, ile czasu zajmuje wydobycie chronionej informacji.",
        threat_h2: "Pierwsze pytanie: przeciw komu?",
        threat1_h: "Tylko plik binarny",
        threat1_p: "Atakujący analizuje plik narzędziami statycznymi. Liczą się nazwy, stringi, graf przepływu sterowania i rozpoznawalne wzorce.",
        threat2_h: "Swobodny runtime",
        threat2_p: "Atakujący może uruchamiać, debugować, śledzić, hookować albo emulować program. Zaciemnianie jest wtedy tylko jedną warstwą.",
        threat3_h: "Ograniczony czas",
        threat3_p: "To najczęstszy przypadek: nie trzeba zatrzymać wszystkich, tylko sprawić, że analiza przestanie się opłacać.",
        tech_h2: "Techniki, które mają sens",
        t1_h3: "1. Stringi i oczywiste dane",
        t1_p: "URL-e, nazwy endpointów, komunikaty wewnętrzne i tokeny nie powinny leżeć jawnie w binarce. Kodowanie ich i dekodowanie tylko w razie potrzeby jest proste, ale użyteczne przeciw płytkiej analizie.",
        t1_note: "Samo to nie wystarczy: statyczny klucz da się odzyskać. Lepsze jest wyprowadzanie kluczy w runtime, dzielenie danych i skracanie czasu życia tekstu jawnego.",
        t2_h3: "2. Spłaszczanie przepływu sterowania",
        t2_p: "Flattening utrudnia czytanie grafu przepływu, zamieniając liniową logikę w dispatcher. Pomaga przeciw dekompilatorom i szybkiej lekturze, ale może pogorszyć wydajność i utrzymanie.",
        t3_h3: "3. Wirtualizacja",
        t3_p: "Wirtualizacja tłumaczy wybrane części programu na własny bytecode wykonywany przez wewnętrzny interpreter. Jest cięższa, ale często skuteczniejsza dla naprawdę wrażliwych funkcji.",
        t4_h3: "4. Anti-debug i anti-tamper",
        t4_p: "Kontrole debuggerów, anomalii czasu, sum kontrolnych sekcji wykonywalnych lub integralności plików mogą spowolnić analizę dynamiczną. Traktuj je jako tarcie, nie mur.",
        metrics_h2: "Jak sprawdzić, czy to działa",
        m1: "Zmierz czas potrzebny na odzyskanie stringa, klucza lub krytycznej funkcji.",
        m2: "Porównaj wynik Ghidry, IDA lub innych dekompilatorów przed i po transformacji.",
        m3: "Śledź wpływ na wydajność, rozmiar binarki, crash rate i debugowanie wewnętrzne.",
        metrics_p: "Wersja bez zaciemniania powinna pozostać dostępna do audytów, testów i utrzymania. Zaciemnianie należy do pipeline’u release, nie do sposobu myślenia zespołu o kodzie.",
        best_h2: "Praktyczne zasady",
        b1: "Chroń kilka miejsc o wysokiej wartości, nie cały program bez kryterium.",
        b2: "Nie dostarczaj statycznych sekretów, jeśli możesz je wyprowadzać albo pobierać kontrolowanym kanałem.",
        b3: "Automatyzuj testy równoważności po każdej transformacji.",
        b4: "Zaplanuj rotację, unieważnianie i aktualizacje: prędzej czy później coś zostanie wyciągnięte.",
        summary_h: "W skrócie",
        summary_p: "Zaciemnianie jest użyteczne, gdy jest celowane, mierzalne i częścią szerszej strategii. Nie zastępuje bezpieczeństwa, dobrego projektu ani poważnego zarządzania sekretami. Dodaje koszt. W wielu przypadkach właśnie o to chodzi."
      }
    };

    const buttons = document.querySelectorAll('.langbtn');
    const status = document.getElementById('langStatus');

    function setLang(lang) {
      const dict = I18N[lang] || I18N.it;
      document.documentElement.lang = lang;

      buttons.forEach(button => {
        button.setAttribute('aria-pressed', String(button.dataset.lang === lang));
      });

      document.querySelectorAll('[data-i18n]').forEach(node => {
        const key = node.dataset.i18n;
        node.textContent = dict[key] || I18N.it[key] || '';
      });

      status.textContent = {
        it: 'Lingua impostata su italiano',
        en: 'Language set to English',
        pl: 'Język ustawiono na polski'
      }[lang] || '';

      try { localStorage.setItem('lang', lang); } catch (_) {}
    }

    buttons.forEach(button => {
      button.addEventListener('click', () => setLang(button.dataset.lang));
    });

    const saved = (() => {
      try { return localStorage.getItem('lang'); } catch (_) { return null; }
    })();

    const browser = (navigator.language || 'it').slice(0, 2);
    setLang(saved && I18N[saved] ? saved : (I18N[browser] ? browser : 'it'));
  </script>
</body>
</html>