Skip to main content

/writings / it / patching-sse4x-software-for-older-cpus

IT

Patching di software SSE4.x per CPU meno recenti

Ingegneria inversa
Also in:BGBRENES
あaAuto-translated from English.read original ↗

Introduzione

La maggior parte delle CPU moderne è compatibile perché condivide la stessa ISA1. L'ISA più comune oggi è x86_64, un'estensione a 64 bit della vecchia architettura x86. La necessità di operazioni più complesse e veloci, soprattutto nell'aritmetica in virgola mobile, ha portato a estensioni come SSE2 e AVX3.

Queste estensioni comprendono diverse versioni che permettono al processore di interpretare insiemi differenti di opcode. SSE arriva alla versione 4.2, con una versione 4a esclusiva di AMD. AVX arriva ad AVX-512, ma l'insieme di estensioni più comune nelle CPU COTS4 è AVX-2.

Durante la compilazione del software è possibile impostare un flag che stabilisce se il codice macchina prodotto sarà compatibile con un insieme specifico di estensioni. Questa scelta può offrire prestazioni migliori, perché le estensioni possono eseguire determinate operazioni usando meno cicli della CPU.

Tuttavia, CPU più vecchie, perfettamente in grado di eseguire software moderno, potrebbero essere state introdotte sul mercato prima che le estensioni più recenti fossero disponibili. Di conseguenza, il software compilato con queste istruzioni più nuove non può essere eseguito affatto su di esse.

Rilevare il supporto delle estensioni

La descrizione di queste estensioni è inclusa nei flag CPUID. Il modo più semplice per verificare se la CPU supporta istruzioni specifiche è tramite CPU-Z:

CPU-Z che mostra gli insiemi di istruzioni supportati
CPU-Z che mostra gli insiemi di istruzioni supportati

Puoi anche provare semplicemente a eseguire il software. Potrebbe mostrare un popup che dice che non è compatibile, oppure bloccarsi silenziosamente. Controlla il Visualizzatore eventi di Windows per il codice di errore 0xC000001D, che indica il tentativo di eseguire un'istruzione illegale.

Processo di patching

Descriverò il processo di patching che ho usato per aggirare queste istruzioni nel gioco Days Gone. È il mio metodo e non è necessariamente ottimale, ma funziona. Anche gli acquirenti legittimi del gioco non riuscivano a eseguirlo su processori leggermente più vecchi: eseguendo il patching di alcune istruzioni sono riuscito a renderlo compatibile.

L'esecuzione del gioco provoca un CTD5 immediato e il processo scompare dal Task Manager. Non ci sono avvisi né indicazioni del problema.

Configurare il debugger

Carica il gioco in x64dbg e imposta un breakpoint su EXCEPTION_ILLEGAL_INSTRUCTION. Questa eccezione viene generata quando il processore incontra un opcode sconosciuto:

Impostazione del breakpoint dell'eccezione
Impostazione del breakpoint dell'eccezione

Configurazione del breakpoint
Configurazione del breakpoint

Esegui il gioco finché non viene raggiunto questo breakpoint. Potresti incontrare altri breakpoint: continua finché non arrivi a quello dell'istruzione non valida. Processori diversi possono avere insiemi diversi di estensioni disponibili e quindi essere in grado di interpretare opcode diversi.

Debugger fermo sull'istruzione illegale
Debugger fermo sull'istruzione illegale

Osserva come la barra di stato in basso indichi che l'esecuzione si è fermata sul breakpoint EXCEPTION_ILLEGAL_INSTRUCTION. Ora siamo su un opcode non supportato.

Primo tentativo: NOP

Di solito provo prima ad applicare NOP6 all'istruzione (clic destro → Binary → Fill with NOPs). Può funzionare oppure no, a seconda dell'istruzione e del software. I giochi usano spesso queste istruzioni per decodificare contenuti video, come titoli di coda o filmati.

Tuttavia, applicare semplicemente NOP all'istruzione può compromettere il software. In questo caso dobbiamo sostituire queste istruzioni con equivalenti che usino opcode disponibili sulla nostra CPU.

Trovare istruzioni equivalenti

Una ricerca dell'istruzione pminuw mi ha portato a questa utile risorsa, che fornisce sostituti compatibili con SSE2:

Implementazione PMINUW compatibile con SSE2

;   xmm0 = ingresso|uscita
;   xmm1 = ingresso
;   xmm7 = temporaneo
movdqa xmm7, xmm0    ; Sposta xmm0 nel temporaneo
psubusw xmm7, xmm1   ; Sottrazione con saturazione senza segno
psubw xmm0, xmm7     ; Sottrazione (senza saturazione; qui non ci sarebbe overflow)

Questo significa che possiamo sostituire l'istruzione con queste tre per ottenere compatibilità con SSE2. L'istruzione attuale, però, è di soli 5 byte (66:0F383AD1) e per la sostituzione ci servono 3×4 = 12 byte. Come possiamo farli entrare?

Usare code cave

Possiamo saltare a un'altra posizione che possa contenere queste istruzioni e poi tornare indietro per continuare l'esecuzione. Per prima cosa calcoliamo lo spazio necessario:

movdqa  : 4 byte
psubusw : 4 byte
psubw   : 4 byte
jmp     : ≤5 byte

Ci servono circa 17 byte di spazio libero. Cerca una "code cave", cioè un'area inutilizzata nella sezione eseguibile. Cerca un pattern di 17 byte CC consecutivi oppure scorri per trovare un'area vuota:

Ricerca di code cave
Ricerca di code cave

Apri il primo risultato e scrivi le istruzioni sostitutive. Alla fine, aggiungi un jmp che torni all'istruzione successiva a quella sostituita. Ricordati di adeguare i registri e di verificare che il registro temporaneo non sia usato altrove:

Scrittura del codice sostitutivo nella cave
Scrittura del codice sostitutivo nella cave

Completare la patch

Sostituisci l'istruzione originale con un jmp alla nostra code cave:

Sostituzione dell'istruzione originale con un salto
Sostituzione dell'istruzione originale con un salto

Questo è tutto. Ripeti finché non vengono raggiunti altri breakpoint. Per Days Gone, ho sostituito 12 istruzioni pminuw e 2 pmaxud. Per alcune istruzioni non sono riuscito a trovare sostituti semplici, quindi ho semplicemente applicato NOP: ha funzionato.


Riepilogo dei passaggi

  1. Identifica il problema: controlla CPU-Z per gli insiemi di istruzioni supportati oppure cerca il codice 0xC000001D nel Visualizzatore eventi di Windows
  2. Configura il debugger: carica l'eseguibile in x64dbg e imposta un breakpoint su EXCEPTION_ILLEGAL_INSTRUCTION
  3. Trova le istruzioni non supportate: esegui fino al breakpoint per identificare l'opcode problematico
  4. Prova prima NOP: talvolta è sufficiente riempire l'istruzione con NOP
  5. Trova istruzioni equivalenti: cerca alternative compatibili con SSE2 per le istruzioni SSE4.x
  6. Individua le code cave: cerca spazio vuoto (byte CC) nell'eseguibile
  7. Applica la patch alla code cave: scrivi le istruzioni sostitutive seguite da un salto indietro
  8. Sostituisci l'istruzione originale: modificala affinché salti alla tua code cave
  9. Ripeti: continua finché tutte le istruzioni illegali non sono state patchate

Giochi che richiedono istruzioni SSE4.1/4.2

Elenco compilato da mafia47 (alcuni giochi richiedono anche AVX)

Gioco Stato
Sunset Overdrive Ancora nessuna correzione
Journey Correzione: disabilitare l'interpolazione del tessuto (solo versione più recente)
Metal Slug XX Ancora nessuna correzione
Beyond: Two Souls Giocabile con Intel SDE7
Detroit: Become Human Correzione disponibile
Control Ultimate Edition Ancora nessuna correzione
Yakuza 3, 4, 5, 6 Ancora nessuna correzione
Yakuza: Like a Dragon Ancora nessuna correzione
Horizon Zero Dawn Correzione solo per Steam v1.0.9.3
Days Gone Correzione solo per v1.0
Little Nightmares Giocabile con Intel SDE
Unravel Two Giocabile con Intel SDE
Death Stranding Ancora nessuna correzione
Cyberpunk 2077 Correzione per GOG v1.22 (richiede test)
Red Dead Redemption 2 Correzione solo per la versione crackata
Nioh 2 Ancora nessuna correzione
God Eater 3 Ancora nessuna correzione
Assassin's Creed Origins Correzione per v1.51 (non per CPU Phenom)
Assassin's Creed Odyssey Correzione per v1.5.3 (non per CPU Phenom)
Assassin's Creed Valhalla Ancora nessuna correzione
Immortals Fenyx Rising Ancora nessuna correzione
Far Cry 5 Corretto dagli sviluppatori nell'ultima versione; nessuna correzione per le versioni vecchie
Dead Rising 4 Ancora nessuna correzione
Crash Bandicoot 4 Ancora nessuna correzione
Saints Row The Third Remastered Ancora nessuna correzione
DOOM Eternal Correzione solo per exe senza Denuvo (non per CPU Phenom)
Need for Speed Heat Ancora nessuna correzione
NBA 2K21 Ancora nessuna correzione
Shadow Man Remastered Giocabile con Intel SDE
Biomutant Ancora nessuna correzione
Outriders Ancora nessuna correzione
Puyo Puyo Tetris 2 Corretto dagli sviluppatori
Diablo II Resurrected Ancora nessuna correzione

Considerazioni finali

Sono sicuro che esistano approcci migliori, automatizzando con script oppure usando plugin specializzati per debugger. Sto anche lavorando a un emulatore generico che potrebbe aiutarci a continuare a usare CPU più vecchie ma perfettamente capaci: sarebbe ideale perché l'emulazione JIT non modifica il software originale ed emulerebbe solo le istruzioni necessarie, con un overhead (si spera) minimo.

Footnotes

  1. Instruction Set Architecture è l'insieme di istruzioni che un processore può comprendere ed eseguire. ↩

  2. Streaming SIMD Extensions; SIMD significa Single Instruction, Multiple Data e consente operazioni parallele su più elementi di dati. ↩

  3. Advanced Vector Extension fornisce registri più larghi (a 256 e 512 bit) per una capacità di elaborazione parallela ancora maggiore. ↩

  4. Commercial Off-The-Shelf indica hardware di consumo standard, anziché processori specializzati o personalizzati. ↩

  5. Crash To Desktop: l'applicazione termina bruscamente senza alcuna finestra di errore. ↩

  6. NOP (No Operation) è un'istruzione che non fa nulla. Riempire il codice con NOP ne rimuove di fatto la funzionalità, mantenendo l'allineamento dei byte. ↩

  7. Intel Software Development Emulator può emulare istruzioni mancanti, ma con un notevole overhead prestazionale. ↩