Въведение
Повечето съвременни процесори са съвместими, защото споделят една и съща ISA1. Най-разпространената ISA днес е x86_64 — 64-битово разширение на по-старата архитектура x86. Нуждата от по-сложни и по-бързи операции, особено при аритметика с плаваща запетая, води до разширения като SSE2 и AVX3.
Тези разширения имат няколко версии, които позволяват на процесора да интерпретира различни набори от опкодове. SSE достига до версия 4.2, както и до ексклузивната за AMD версия 4a. AVX достига до AVX-512, но по-често срещаният набор от разширения при COTS4 процесорите е AVX-2.
При компилиране на софтуер може да се зададе флаг, който определя дали полученият машинен код ще е съвместим с конкретен набор от разширения. Този избор може да подобри производителността, тъй като разширенията изпълняват някои операции с по-малко процесорни цикли.
По-старите процесори обаче, макар напълно способни да изпълняват съвременен софтуер, може да са били пуснати на пазара преди да станат налични най-новите разширения. Затова софтуер, компилиран с тези по-нови инструкции, изобщо не може да се изпълни на тях.
Откриване на поддръжката на разширения
Информацията за тези разширения се съдържа във флаговете на CPUID. Най-лесният начин да проверите дали процесорът ви поддържа конкретни инструкции е чрез CPU-Z:
Можете и просто да опитате да стартирате софтуера. Той може да покаже прозорец, че не е съвместим, или просто да се срине без съобщение. Проверете Windows Event Viewer за код на грешка 0xC000001D, който означава, че е направен опит за изпълнение на невалидна инструкция.
Процесът на пачване
Ще опиша процеса на пачване, който използвах, за да заобиколя тези инструкции в играта Days Gone. Това е моят метод и не е непременно оптимален, но работи. Имайте предвид, че дори легитимните притежатели на играта не можеха да я стартират на малко по-стари процесори — чрез пачване на няколко инструкции успях да я направя съвместима.
Стартирането на играта води до незабавен CTD5, като процесът изчезва от Task Manager. Няма предупреждения или индикации за проблема.
Настройване на дебъгера
Заредете играта в x64dbg и задайте breakpoint на EXCEPTION_ILLEGAL_INSTRUCTION. Това изключение се вдига, когато процесорът срещне непознат опкод:
Стартирайте играта, докато не бъде достигнат този breakpoint. Може да срещнете други breakpoints — продължавайте, докато не стигнете до този за невалидната инструкция. Имайте предвид, че различните процесори може да имат различни налични разширения и съответно да могат да интерпретират различни опкодове.
Обърнете внимание, че долната лента за състоянието показва, че изпълнението е спряно на breakpoint EXCEPTION_ILLEGAL_INSTRUCTION. Вече сме на неподдържан опкод.
Първи опит: NOP-ване
Обикновено първо опитвам да NOP-на6 инструкцията (десен бутон → Binary → Fill with NOPs). Това може да проработи или не, в зависимост от инструкцията и софтуера. Игрите често използват тези инструкции за декодиране на видеосъдържание, като финални надписи или кътсцени.
Но простото NOP-ване на инструкцията може да счупи софтуера. В такъв случай трябва да заменим тези инструкции с еквивалентни, използващи опкодове, налични на нашия процесор.
Намиране на еквивалентни инструкции
Търсенето на инструкцията pminuw ме отведе до този полезен ресурс, който предлага съвместими със SSE2 заместители:
Реализация на PMINUW, съвместима със SSE2
; xmm0 = вход|изход ; xmm1 = вход ; xmm7 = временен регистър movdqa xmm7, xmm0 ; Преместване на xmm0 във временния регистър psubusw xmm7, xmm1 ; Изваждане с насищане без знак psubw xmm0, xmm7 ; Изваждане (без насищане; тук не би имало препълване)
Това означава, че можем да заменим инструкцията си с тези 3 инструкции, за да постигнем SSE2 съвместимост. Текущата ни инструкция обаче е само 5 байта (66:0F383AD1), а за замяната са ни нужни 3×4 = 12 байта. Как да ги поберем?
Използване на code caves
Можем да скочим на друго място, което побира тези инструкции, след което да скочим обратно, за да продължим изпълнението. Първо да изчислим нужното място:
movdqa : 4 байта
psubusw : 4 байта
psubw : 4 байта
jmp : ≤5 байта
Нуждаем се от приблизително 17 байта свободно място. Потърсете „code cave“ — неизползвана област в изпълнимата секция. Направете търсене по шаблон за 17 последователни байта CC или прегледайте наоколо за празна област:
Отворете първия резултат и напишете заместващите инструкции. В края добавете jmp обратно към инструкцията след заменената. Не забравяйте съответно да коригирате регистрите и да проверите дали временният регистър не се използва другаде:
Завършване на пача
Заменете оригиналната инструкция с jmp към нашия code cave:
Това е. Повтаряйте, докато не се задействат повече breakpoints. За Days Gone замених 12× инструкции pminuw и 2× pmaxud. За някои инструкции не успях да намеря лесни заместители, затова просто ги NOP-нах — и проработи.
Обобщение на стъпките
- Установете проблема: Проверете CPU-Z за поддържаните набори от инструкции или потърсете кода
0xC000001Dв Windows Event Viewer - Настройте дебъгера: Заредете изпълнимия файл в x64dbg и задайте breakpoint на
EXCEPTION_ILLEGAL_INSTRUCTION - Намерете неподдържаните инструкции: Стартирайте, докато breakpoint-ът се задейства, за да откриете проблемния опкод
- Първо опитайте NOP: Понякога простото запълване на инструкцията с NOP-ове работи
- Намерете еквивалентни инструкции: Потърсете съвместими със SSE2 алтернативи за SSE4.x инструкциите
- Открийте code caves: Потърсете празно място (байтове
CC) в изпълнимия файл - Пачнете code cave: Напишете заместващите инструкции, последвани от скок обратно
- Заменете оригиналната инструкция: Променете я така, че да скача към вашия code cave
- Повторете: Продължете, докато всички невалидни инструкции бъдат пачнати
Игри, изискващи SSE4.1/4.2 инструкции
Списък, съставен от mafia47 (някои игри изискват и AVX)
| Игра | Състояние |
|---|---|
| Sunset Overdrive | Все още няма решение |
| Journey | Решение: изключете интерполацията на плата (само за най-новата версия) |
| Metal Slug XX | Все още няма решение |
| Beyond: Two Souls | Може да се играе с Intel SDE7 |
| Detroit: Become Human | Налично е решение |
| Control Ultimate Edition | Все още няма решение |
| Yakuza 3, 4, 5, 6 | Все още няма решение |
| Yakuza: Like a Dragon | Все още няма решение |
| Horizon Zero Dawn | Решение само за Steam v1.0.9.3 |
| Days Gone | Решение само за v1.0 |
| Little Nightmares | Може да се играе с Intel SDE |
| Unravel Two | Може да се играе с Intel SDE |
| Death Stranding | Все още няма решение |
| Cyberpunk 2077 | Решение за GOG v1.22 (изисква тестване) |
| Red Dead Redemption 2 | Решение само за crack-ната версия |
| Nioh 2 | Все още няма решение |
| God Eater 3 | Все още няма решение |
| Assassin's Creed Origins | Решение за v1.51 (не за процесори Phenom) |
| Assassin's Creed Odyssey | Решение за v1.5.3 (не за процесори Phenom) |
| Assassin's Creed Valhalla | Все още няма решение |
| Immortals Fenyx Rising | Все още няма решение |
| Far Cry 5 | Поправено от разработчиците в най-новата версия; няма решение за по-стари версии |
| Dead Rising 4 | Все още няма решение |
| Crash Bandicoot 4 | Все още няма решение |
| Saints Row The Third Remastered | Все още няма решение |
| DOOM Eternal | Решение само за exe без Denuvo (не за процесори Phenom) |
| Need for Speed Heat | Все още няма решение |
| NBA 2K21 | Все още няма решение |
| Shadow Man Remastered | Може да се играе с Intel SDE |
| Biomutant | Все още няма решение |
| Outriders | Все още няма решение |
| Puyo Puyo Tetris 2 | Поправено от разработчиците |
| Diablo II Resurrected | Все още няма решение |
Заключителни мисли
Сигурен съм, че има по-добри подходи — например автоматизиране чрез скриптове или използване на специализирани плъгини за дебъгери. Работя и по универсален емулатор, който би могъл да ни помогне да продължим да използваме по-стари, но напълно способни процесори: това би било идеално, защото JIT емулацията не променя оригиналния софтуер и би емулирала само необходимите инструкции, осигурявайки (надявам се) минимален overhead.
Footnotes
-
Архитектура на набора от инструкции — наборът от инструкции, които процесорът може да разбира и изпълнява. ↩
-
Streaming SIMD Extensions; SIMD означава Single Instruction, Multiple Data и позволява паралелни операции върху множество точки от данни. ↩
-
Advanced Vector Extension предоставя по-широки регистри (256-битови и 512-битови) за още повече паралелна обработка. ↩
-
Commercial Off-The-Shelf означава стандартен потребителски хардуер, за разлика от специализирани или персонализирани процесори. ↩
-
Crash To Desktop: приложението се прекратява внезапно, без диалогов прозорец за грешка. ↩
-
NOP (No Operation) е инструкция, която не прави нищо. Запълването на код с NOP-ове на практика премахва функционалността му, като запазва подравняването на байтовете. ↩
-
Intel Software Development Emulator може да емулира липсващи инструкции, но със значителен спад в производителността. ↩