20. Decompilare istruzioni switch

switch (expr) {         MOV R1, expr         R1 = expr
   case 1:                 CMP R1, #1           if (R1 == 1) {
                           JNE L_100
        stmt1;                ...                   ...
        break;             JMP L_break          } else
   case 100:         L_100:
                           CMP R1, #100         if (R1 == 100) {
                           JNE L_1000
        stmt2;                ...                   ...
        break;             JMP L_break          } else
   case 1000:        L_1000:
                           CMP R1, #1000        if (R1 == 1000) {
                           JNE L_default
        stmt3;                ...                   ...
        break;             JMP L_break          } else {
   default:          L_default:
        stmt3;                ...                   ...
        break;
   }                 L_break:                   }
switch (expr) {         MOV R1, expr             R1 = expr
                           CMP R1, #1               if (R1 < 1)
                           JLT L_default                goto L_default;
                           CMP R1, #3               if (R1 > 3)
                           JGT L_default                goto L_default;
                           SUB R1, #1
                           SHL R1, #2
                           MOV R2, jmpTable[R1]     R2 = JmpTable[(R1 - 1)]
                           JMP [R2]                 goto *R2;   // ?

   case 1:            L_1:                       L_1?
        stmt1;                ...                       ...
        break;             JMP L_break              goto L_break;
   case 2:            L_2:                       L_2?
        stmt2;                ...                       ...
        break;             JMP L_break              goto L_break;
   case 3:            L_3:                       L_3?
        stmt3;                ...                       ...
        break;             JMP L_break              goto L_break;
   default:           L_default:                 L_default:
        stmt3;                ...                       ...
        break;
   }                  L_break:                   L_break?

Qui abbiamo una serie di problemi, tutti causati dal salto indiretto JMP [R2]. Questa istruzione non può essere mappata direttamente a un linguaggio di alto livello, quindi il ? accanto al goto *R2; dichiarazione. Questa istruzione è illegale e deve essere convertita in qualcos’altro per generare codice ricompilabile. Sfortunatamente, l’unico modo per convertire tale istruzione in un’istruzione legale è riconoscere le istruzioni precedenti come parte di un’istruzione switch. Questo non è così ovvio, perché dobbiamo analizzare più di un blocco. Potremmo ancora lasciare le 2 istruzioni if che controllano i limiti inferiore e superiore della tabella dei valori case e analizzare solo l’accesso alla tabella di salto.

Vale la pena notare che il codice precedente è solo una delle possibili sequenze di codice che i compilatori generano per un’istruzione switch. Esistono diverse varianti che cospirano contro l’implementazione di un algoritmo generico per le istruzioni switch. Alcune di queste variazioni includono la presenza di una tabella di salto degli offset invece di indirizzi diretti o una tabella di coppie valore-indirizzo che possono essere scansionate da un loop.

Queste variazioni suggeriscono che il rilevamento dell’istruzione switch sia implementato in un modulo dipendente dal processore o dal compilatore, dove le diverse sequenze di codice possono essere rilevate meglio.

Anche il rilevamento delle istruzioni switch è molto importante per migliorare la separazione codice/dati. In effetti, se ricordi, l’ algoritmo di rilevamento del codice/dati si basava sul fatto che le chiamate e i salti possono essere seguiti fino alle loro destinazioni. Ma qui non possiamo essere sicuri della destinazione del salto indiretto, perché la destinazione dipende dal valore di un’espressione calcolata. Sicuramente sappiamo dove si trova la tabella di salto (quando viene utilizzata una tabella di salto) ma non sapremo quanti valori ci sono nella tabella a meno che non analizziamo le istruzioni if che controllano i limiti dell’espressione.

Nota qui qualcosa di molto importante: non possiamo riconoscere le istruzioni if se non siamo in grado di costruire i blocchi di base per la procedura. Ma non possiamo costruire tutti i blocchi di base della procedura a meno che non riconosciamo le istruzioni if che controllano i limiti dei valori maiuscole e minuscole!

Come risolviamo questo problema?

Biografia

Sono uno specialista in materia di sicurezza informatica, scrittore, contributore per il progetto Monero, una criptovaluta che è focalizzata nel proteggere le informazioni sulle transazioni. Il libro che ho pubblicato Mastering Monero è diventato una delle migliori risorse per padroneggiare Monero. Più informazioni su di me

Seguimi su Twitter o scrivimi una e-mail. Le donazioni sono molto apprezzate, mi permettono di continuare a lavorare e a scrivere.

Mastering Monero Book