Domanda:
Che cos'è l'offuscamento delle "istruzioni sovrapposte"?
perror
2013-04-03 13:11:29 UTC
view on stackexchange narkive permalink

Ho analizzato alcuni binari in x86 / x86-64 usando alcuni trucchi di offuscamento. Uno era chiamato istruzioni sovrapposte . Qualcuno può spiegare come funziona questo offuscamento e come aggirarlo?

Mi sono chiesto quale sia il termine corretto per questo. Ho sentito parlare di "scissione delle istruzioni" abbastanza frequentemente.
Incontro diversi modi di parlare di questa tecnica. Ma è vero che nessuno è stato ampiamente adottato dalla comunità. "_Istruzioni sovrapposte_" è stata la più utilizzata che ho trovato sinora. Ma potrei aver letto solo una piccola parte della documentazione esistente su di esso.
Quattro risposte:
#1
+28
Remko
2013-04-03 13:25:43 UTC
view on stackexchange narkive permalink

Il documento Analisi statica degli eseguibili x86 spiega abbastanza bene le istruzioni sovrapposte. Il seguente esempio è preso da esso (pagina 28):

  0000: B8 00 03 C1 BB mov eax, 0xBBC103000005: B9 00 00 00 05 mov ecx, 0x05000000000A: 03 C1 add eax, ecx000C : EB F4 jmp $ -10000E: 03 C3 add eax, ebx0010: C3 ret  

Osservando il codice, non è evidente quale sarà il valore di eax nell'istruzione return ( o che l'istruzione di ritorno sia mai raggiunta, del resto). Ciò è dovuto al salto da 000C a 0002, un indirizzo che non è esplicitamente presente nell'elenco (jmp $ -10 denota un salto relativo dal valore corrente del contatore del programma, che è 0xC e 0xC10 = 2). Questo salto trasferisce il controllo al terzo byte dell'istruzione di movimento lunga cinque byte all'indirizzo 0000. L'esecuzione della sequenza di byte che inizia all'indirizzo 0002 apre un flusso di istruzioni completamente nuovo:

  0000: B8 00 03 C1 BB mov eax, 0xBBC103000005: B9 00 00 00 05 mov ecx, 0x05000000000A: 03 C1 aggiungi eax, ecx000C: EB F4 jmp $ -100002: 03 C1 aggiungi eax, ecx0004: BB B9 00 00 00 mov ebx, 0xB90009: 05 03 C1 EB F4 add eax, 0xF4EBC103000E: 03 C3 add eax, ebx0010: C3 ret  

Sarebbe interessante sapere se / come Ida Pro e specialmente il plugin Hex Rays gestiscono questo. Forse @IgorSkochinsky può commentare questo ...

#2
+16
Ange
2013-04-08 19:47:54 UTC
view on stackexchange narkive permalink

È anche noto come il trucco del "salto nel mezzo".

spiegazione

regole di esecuzione

  • la maggior parte delle istruzioni richiede più di un byte per essere codificati
    • possono richiedere fino a 15 byte sulle moderne CPU
  • l'esecuzione può iniziare in qualsiasi posizione purché i permessi siano validi

in modo che ogni byte che segue il primo di un'istruzione possa essere riutilizzato per avviare un'altra istruzione.

disassemblatori abusivi

  • disassemblatori diretti avviano il istruzione successiva subito dopo la fine dell'ultima.

così tali disassemblatori (che non seguono il flusso) nasconderanno l'istruzione che si trova nel mezzo di uno visibile .

esempi

banale

  00: EB 01 jmp 302: 68 c3 90 90 90 push 0x909090c3  

verrà effettivamente eseguito come

  00: EB 01 jmp 303: C3 retn ...  

poiché il primo jmp salta il primo byte 68 (che codifica un push immediato) della seguente istruzione.

più sovrapposizioni

da questo esempio, 69 84 definisce un'istruzione imul che può richiedere fino a 11 byte. In questo modo puoi inserire diverse righe di istruzioni nei suoi operandi "falsi".

  00: EB02 jmp 402: 69846A40682C104000EB02 imul eax, [edx + ebp * 2 + 0102C6840], 0x002EB00400D: ... .  

verrà effettivamente eseguito come

  00: EB02 jmp 404: 6A40 push 04006: 682C104000 push 0x40102C0B: EB02 jmp 0xF0F: ...  codice> 

istruzione che si sovrappone a se stessa

L'istruzione salta nel secondo byte di se stessa:

  00: EBFF jmp 102: C0C300 rol bl, 0  

verrà effettivamente eseguito come

  00: EBFF jmp 101: FFC0 inc eax03: C3 retn  

diverse modalità di CPU

questo offuscamento può essere esteso al passaggio allo stesso EIP ma in diverse modalità di CPU:

  • Le CPU 64b supportano ancora l'istruzione 32b
  • La modalità 64b utilizza 0x33 per cs
  • alcune istruzioni sono disponibile solo in una modalità particolare:
    • arpl in modalità 32b
    • movsxd in modalità 64b

in modo da poter passare allo stesso EIP ma con un CS diverso e ottenere istruzioni diverse.

In questo esempio, questo codice viene eseguito prima in modalità 32b:

  00: 63D8 arpl ax, bx02: 48 dec eax03: 01C0 add eax, eax05: CB retf  

e quindi rieseguito in modalità 64 bit come:

  00: 63D8 movsxd rbx, eax02: 4801C0 add rax, rax05: CB retf  

In questo caso, le istruzioni si sovrappongono, non a causa di un EIP diverso, ma perché la CPU è cambiata temporaneamente dalla modalità 32b a 64b.

È possibile passare dalla modalità a 32 a quella a 64 bit come programma in esecuzione nello spazio utente su qualsiasi sistema operativo principale?
@Dougall sì. Su Windows è fatto da `X86SwitchTo64BitMode ()` (o manualmente con una chiamata / salto lontano con il selettore di segmento 33). Tuttavia sono abbastanza certo che questo sia specifico per l'implementazione di Windows WOW64 e non applicabile in un altro sistema operativo.
@Ange, puoi aggiornare il link https://code.google.com/p/corkami/source/browse/trunk/src/CoST/CoST.asm?r=1593#2247? Grazie!
#3
+8
joxeankoret
2013-04-03 14:19:39 UTC
view on stackexchange narkive permalink

Quasi tutte le istruzioni multibyte possono essere utilizzate come istruzioni sovrapposte in x86 / x86_64. Il motivo è molto semplice: i set di istruzioni x86 e x86_64 sono CISC. Il che significa, tra le altre cose, che le istruzioni non hanno una lunghezza fissa. Quindi, poiché le istruzioni sono di lunghezza variabile, scrivendo attentamente quel codice macchina, ogni istruzione è suscettibile di nascondere istruzioni sovrapposte.

Ad esempio, dato il codice seguente:

  [ 0x00408210: 0x00a31e10] > b0x000050f5 (01) 56 PUSH ESI 0x000050f6 (04) 8b742408 MOV ESI, [ESP + 0x8] 0x000050fa (01) 57 PUSH EDI 0x000050fb (03) ED0x400 c1 ESI + 0x40a058] 0x00005104 (01) 57 PUSH EDI 0x00005105 (06) ff15f4804000 CALL 0x004080f4; 1 KERNEL32.dll! GetModuleHandleA0x0000510b (02) 85c0 TEST EAX, EAX 0x0000510d (02) 750b JNZ 0x0000511a; 2 

Supponiamo che da qualche parte dopo l'ultima istruzione ci sia un salto nel mezzo di qualche istruzione nel codice visualizzato come, ad esempio, al 2 ° byte nel MOV ESI ... istruzioni:

  [0x000050f7: 0x00405cf7] > c0x000050f7 (02) 7424 JZ 0x0000511d; 1 0x000050f7 ------------------------------------------------ ---------------------- 0x000050f9 (03) 0857c1 OPPURE [EDI-0x3f], DL 0x000050fc (02) e603 OUT 0x3, AL  

Si scopre che questa istruzione cambia in una JZ. Che è valido. Salto al 3 ° byte ...

  [0x000050f7: 0x00405cf7] > s +1 [0x000050f8: 0x00405cf8] > c0x000050f8 (02) 2408 AND AL, 0x8 0x00000050H (01) (03) c1e603 SHL ESI, 0x3 0x000050fe (06) 8bbe58a04000 MOV EDI, [ESI + 0x40a058]  

Passaggio al 2 ° byte dell'istruzione CALL:

  [0x000050f5: 0x00405cf5] > s 0x5106 [0x00005106: 0x00405d06] > c0x00005106 (05) 15f4000 EA4000; '\ x8e \ x91'0x0000510b (02) 85c0 TEST EAX, EAX 0x0000510d (02) 750b JNZ 0x0000511a; 1 

Come puoi vedere, virtualmente qualsiasi istruzione multibyte è suscettibile di essere utilizzata come istruzione sovrapposta.

Questo trucco anti-inversione è abbastanza spesso usato con predicati opachi per f ** k il grafo di flusso.

Quindi, vuoi dire che non c'è modo di costruire tale elenco? Un altro punto che mi sorprende molto degli opcode x86 / x86-64, è la sua capacità di risincronizzarsi dopo un po 'con il flusso di istruzioni originale. Questa proprietà aiuta molto anche a sovrapporre le istruzioni. Tuttavia, non ho idea del perché la risincronizzazione funzioni così bene.
#4
+3
Dougall
2013-04-08 20:16:04 UTC
view on stackexchange narkive permalink

Poiché le istruzioni x86 possono essere di qualsiasi lunghezza e non devono essere allineate, il valore immediato di un'istruzione può essere del tutto un'altra istruzione. Ad esempio:

  00000000 0531C0EB01 add eax, 0x1ebc03100000005 055090EB01 add eax, 0x1eb90500000000A 05B010EB01 add eax, 0x1eb10b00000000F EBF0 jmp short 0x1  

Questo fa esattamente ciò che dice , fino al salto. Quando salta, il valore immediato che viene aggiunto a eax diventa un'istruzione, quindi il codice avrà il seguente aspetto:

  00000000 05 db 500000001 31C0 xor ax, ax xor ax, ax00000003 EB01 jmp short 0x600000005 05 db 500000006 50 push ax push ax00000007 90 nop00000008 EB01 jmp short 0xb0000000A 05 db 50000000B B010 mov al, 0x10 mov al, 0x10 ....  

Le istruzioni che sono effettivamente significative sono mostrate a destra -colonna a mano. In questo esempio, vengono utilizzate brevi istruzioni di salto per saltare la parte add eax dell'istruzione ( 05 ). Va notato che questo potrebbe essere fatto in modo più efficace utilizzando un singolo byte per mangiare i 05 , come 3C05 che è cmp al, 0x5 , e sarebbe innocuo nel codice che non si preoccupa dei flag.

Nello schema sopra, puoi facilmente sostituire tutti i 05 con 90 (nop) per visualizzare lo smontaggio corretto. Ciò può essere reso più complicato utilizzando gli 05 come valori immediati per il codice nascosto (da cui dipende l'esecuzione). In realtà, la persona che offusca il codice probabilmente non userebbe add eax più e più volte e potrebbe cambiare l'ordine di esecuzione per rendere più complicato il tracciamento.

Ho preparato un campione utilizzando il modello sopra. Questo è un file ELF Linux a 32 bit in base64. L'effetto del codice nascosto è l'esecuzione di execve ("// usr / bin / python", 0, 0) . Ti suggerisco di non eseguire binari casuali da risposte SE. Puoi, tuttavia, usarlo per testare i tuoi disassemblatori. IDA, Hopper e objdump tutti falliscono miseramente a prima vista, anche se immagino che si può ottenere IDA farlo correttamente in qualche modo.

  f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAYIAECDQAAAAoAQAAAAAAADQAIAABACgAAwACAAEAAAAAAAAAAIAECACABAgUAQAAFAEAAAUAAAAAEAAAAAAAAAAAAAAAAAAABTHA6wEFUJDrAQWwEOsBBffg6wEF9 + DrAQWJw + sBBbRu6wEFsG / rAQX34 + sBBbRo6wEFsHTrAQVQkOsBBbR56wEFsHDrAQX34 + sBBbQv6wEFsG7rAQVQkOsBBbRp6wEFsGLrAQX34 + sBBbQv6wEFsHLrAQVQkOsBBbRz6wEFsHXrAQX34 + sBBbQv6wEFsC / rAQVQkOsBBTHJ6wEF9 + HrAQWJ4 + sBBbAL6wEFzYDrAelN //// AC5zaHN0cnRhYgAudGV4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAEAAAAGAAAAYIAECGAAAAC0AAAAAAAAAAAAAAAQAAAAAAAAAAEAAAADAAAAAAAAAAAAAAAUAQAAEQAAAAAAAAAAAAAAAQAAAAAAAAA =  


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...