В інтерв’ю талановитий словак розповів нам, як йому вдалося запустити додаток C # на старій Windows 3.11.

словацького

Сьогодні Windows 3.0 святкує рівно 30 років з моменту свого запуску. Як свідчить номер версії, це було третє, але восьме видання найпопулярнішої на сьогоднішній день операційної системи для настільних ПК.

Насправді на початку 1990-х років це ще не була повноцінною операційною системою - Windows була "просто" графічним середовищем, яке все ще базувалося на MS-DOS.

Це не змінилося в наступних версіях. MS-DOS склав основу для Windows на найближчі кілька років, включаючи версію Windows 3.11 з кінця 1993 року. Ми згадуємо про це завдяки успіху словацького програміста Міхала Стреховського.

На майже 30-річній Windows 3.11 йому вдалося запустити додаток, написане мовою програмування C #. Це цікаво, оскільки ця мова виникла приблизно через 7 років. На той час Microsoft вже мала справу з версією Windows 2000 або легендарною Windows XP. В обох випадках це були окремі операційні системи без бази на MS-DOS.

Крім того, C # виник у часи 32-розрядних операційних систем. Він ніколи не був розроблений для запуску своїх програм на 16-розрядних операційних системах. І це саме те, що є Windows 3.11.

В інтерв’ю ви прочитаєте, що надихнуло Міхала Стреховського, яку мотивацію він мав і що йому довелося подолати на роботі.

Міхал Стреховський Він працює в Microsoft майже 10 років, але за час роботи не досяг успіху, а як хобі у вільний час. Вивчав інформатику в університеті імені Масарика. Раніше він писав для Živé.sk - він писав для нас унікальна серія захисту від тріщин - а до цього він також займався книгами. У минулому він стояв за програмним забезпеченням для зміни прихованих налаштувань Windows під назвою SysTuner, яке розробляв до 2004 року.

Почнемо з того, як ти здобув свої знання. Ви з дитинства цікавилися програмуванням?

Я програмував через свого батька. Його батько був інженером-механіком і на той час використовував для своєї роботи комп'ютер Sinclair ZX Spectrum. Іноді він приносив його додому з роботи, і, крім ігор, він також показував мені коротку програму, яку він написав, де я давав математичні приклади рішень. Однак, більше ніж вирішення прикладів, мене цікавило, як працює програма.

Я багато чого навчився сам у вільний час. Мені було просто цікаво і я хотів знати, як працюють комп’ютери. Ніхто не наказав мені їх вивчати, і він не давав мені ресурсів для них.

Крім того, доступ до Інтернету в минулому був сильно обмежений. Батьки дозволяли мені максимум годину на тиждень. Тож під час уроку я знайшов декілька матеріалів з цієї теми, а потім вивчав їх увесь тиждень поряд із школою.

Мене також надихнули дві книги про злом, які вийшли в той час.

Точно так?

Потрібно подивитися інструкції процесора і зрозуміти, що вони роблять. Він також повинен сприймати, як програма взаємодіє "зі світом" та операційною системою, на якій вона працює. Мені це подобається.

Останні 7 років я провів програмування віртуальної машини під управлінням .NET. За цей час людина теж чомусь навчиться. Тож я поєднав своє хобі з тим, чим займаюсь, або я працював як робота в Microsoft.

Як власний проект я створив бібліотеку, яку розробник міг додати до свого додатку і таким чином отримати певний рівень захисту від розтріскування.

Отже, ви намагалися впоратися з самим розтріскуванням?

Я намагався пограти з цим у старшій школі. Як власний проект я створив бібліотеку, яку розробник міг додати до свого додатку і таким чином отримати певний рівень захисту від розтріскування.

Наприклад, деякі його функції вдалося виявити, коли користувач намагався запустити програму в налагоджувачі. Налагоджувач - це інструмент, який програмісти, а також зломщики та хакери використовують для аналізу програм та відстеження даних в пам'яті або інструкцій, які вони надсилають процесору. Зломщики можуть використовувати його для виявлення вразливостей та обходу захисту, реалізованої розробником програми.

Природно, я також намагався продати цю бібліотеку, мабуть, встановив ціну в 20 доларів. Декілька людей навіть купили його, але я навіть мінімум не заробив за подання податкової декларації.

Однак старшокласнику без контактів буде важко впоратися. Але я отримав від цього цінний досвід. Я також майже впевнений, що це забезпечило мені мою нинішню роботу. Коли роботодавець побачив, чим я займаюся у вільний час, я автоматично йому був цікавіший.

Давайте розглянемо, як ви запускали програму C # у старій Windows 3.11. Це було якось пов’язано з тим, що ви працюєте в Microsoft?

Це одна з речей, які я граю на вихідних. Це жодним чином не було пов’язано з моєю роботою в Microsoft, це було виключно моє дозвілля. Я думав про те, щоб дістати C # туди, куди він ще не потрапляв, і ніхто його не використовує. У мене було кілька експериментів, найвідоміший з яких, мабуть, той, коли мені вдалося отримати C # у Windows 3.11.

Ця цікава продуктивність пов’язана з тим, що Windows 3.11 був випущений в 1991 році. Однак перша версія C # з’явилася приблизно через 10 років. Навіть розробники C # не думали, що ця мова програмування одного разу потрапить у і без того застарілу і невикористовувану систему.

Що робить C # специфічним?

Цьому передувало кілька кроків. С # працює в принципі інакше, ніж мови програмування С або С ++. Ці мови програмування є "традиційними" в тому сенсі, що написаний в них код перекладається безпосередньо в машинний код для цільової архітектури процесора та операційної системи під час обробки. Тому, якщо ви створюєте програму для x86 Windows із коду на C ++, ви не запускатимете її на телефоні Android ARM64.

C # в цьому відношенні більше схожий на Java або Kotlina. Коли ви компілюєте код на C #, ви не отримуєте інструкцій щодо "розуміння" якогось фізичного процесора. Для їх запуску потрібен віртуальний процесор. У магазині її теж не купиш. Віртуальний процесор - це середовище виконання, за допомогою якого ваш конкретний процесор та операційна система можуть виконувати семантику програм на C #.

Які операційні системи C # вони підтримують?

Якщо у вас Windows Vista або новіша версія, ви вже маєте цей віртуальний процесор. Це частина операційної системи.

У 2014 році Microsoft оголосила про наступника .NET Framework із відкритим кодом, який називається .NET Core. У 2016 році він розширив «офіційний» .NET за допомогою інших підтримуваних платформ: Linux та macOS. У той же час відкриття вихідного коду дозволило будь-кому додати підтримку для інших операційних систем та процесорів.

Microsoft також заснувала .NET Foundation. Це не контролюється корпорацією Майкрософт. Проекти, пов'язані з .NET Core, були перенесені під нього, щоб спільнота більше не сприймала платформу як закриту та належала одній компанії. .NET хотів потрапити на якомога більше пристроїв і платформ.

Що стосується мого експерименту з C # та Windows 3.11, я використовував існуючу технологію .NET Core з відкритим кодом. Але мені довелося змінити їх до форми, яка також підтримується в Windows 3.11. Це не те, що має велике застосування на практиці. Я зробив багато ярликів у прототипі, тому це точно не все .NET у Windows 3.11.

Моєю метою було з'ясувати справді мінімальні вимоги до запуску коду C #.

Як ви спеціально потрапили до Windows 3.11? Ви сприйняли це як виклик?

На початку Windows 3.11 не була метою. Я почав із спроби визначити мінімальні вимоги до запуску коду C #. Відразу було зрозуміло, що мінімальні вимоги визначаються середовищем виконання. Навіть найпростіші та найпримітивніші програми, які друкують лише число або рядок, потребують цієї віртуальної машини, щоб зрозуміти їхні інструкції.

На цьому етапі я допоміг собі експериментальний проект Microsoft, також опублікований за ліцензією з відкритим кодом. Це альтернативне середовище виконання. Не те, з чим офіційно працює .NET Core.

Його перевага полягає в тому, що на додаток до віртуальної машини він містить ще один компілятор. Він може перекласти код C # безпосередньо в машинний код, "зрозумілий" для обраної апаратної платформи та операційної системи. Подібно до загальноперекладеного вихідного коду мовами C або C.++.

Потім код, написаний на C #, перекладається двічі. Виконавець виконує інструкції для віртуального процесора з рядків програми на C #, написаних програмістом. Інший компілятор перетворює ці віртуальні інструкції на машинний код для певної обраної платформи, наприклад x86 Windows. Кажучи простою мовою, він перекладає це на "мову", зрозумілу фізичному процесору в комп'ютері.

І сам .NET Core цього не знає?

Також у .NET Core можна створити "автономну програму". Він реалізує реалізацію віртуального процесора для конкретної операційної системи і "несе процесор". Тому він більше не вимагає спеціальної підтримки .NET в операційній системі. Однак реалізація цього віртуального процесора є відносно великою.

Експериментальний проект, про який я говорю, навпаки опускає це середовище при складанні. Як я вже сказав, замість цього він перекладає написану програму безпосередньо у власний код для обраного процесора.

Опускаючи середовище виконання .NET, мова втрачає одну з переваг перед статично перекладеними мовами, такими як C та C ++. Можна генерувати новий програмний код під час запуску програми. Ну, я бачив це як дуже хороший початковий крок. Мені вдалося виключити перший "непотрібний" компонент із усього процесу, тобто умову використання віртуальної машини та середовища виконання.

Однак цього було недостатньо?

Ні. Для того, щоб мінімізувати вимоги, я далі зосередився на окремих функціях мови C # та середовищі її виконання. Перевага згаданого експериментального проекту полягає саме в тому, що окремі компоненти середовища виконання не є обов’язковими. Програміст може "натиснути" те, що він хоче виключити. Це не дуже підтримується, але воно є і працює.

Тож я зробив абсолютно просту гру з такою змією. Я розробив його таким чином, щоб він залежав від якомога меншої кількості компонентів середовища виконання, і міг замінити будь-які непотрібні.

Спочатку я почав займатися "збирачем сміття", тобто автоматизованим управлінням пам'яттю. Знову ж таки, це область, де C # ближче до Java, ніж традиційний C.

У звичайному C перед використанням змінної програміст повинен запросити для неї простір пам'яті, виділивши його. Навпаки, коли йому це більше не потрібно, він не повинен забувати звільнити пам’ять. Для великих проектів це величезна проблема, і розробник може легко загубитися в управлінні пам'яттю. Потім програма використовує без потреби багато ресурсів, аварійно завершує роботу або іншим чином не працює належним чином.

Тож як працює C #?

У C # програмісту не потрібно запитувати вручну та звільняти пам’ять. Середовище виконання (віртуальний процесор) виконує ці завдання розумно та за потреби. Однак розробник має менший контроль над системними ресурсами. Однак сам код набагато менш складний і легший для діагностики у разі проблем.

«Збирач сміття» - це відносно складний фрагмент коду. Відстежує час останнього доступу програми до пам’яті тощо. Але я був зачарований, бо теоретично його можна було "викинути". Мені просто доведеться знову керувати пам’яттю.

Я також зробив це із системою введення та виведення з програми, яку замінив власною невеликою реалізацією. Поступово я замінив все на простіші реалізації, які мають менше залежностей від операційної системи.

В результаті я отримав додаток із змією, написаною на C #, розміром всього 8 кілобайт і абсолютно незалежною від середовища виконання.

Який був результат?

Ось так я кілька разів на вихідних «бавився» з нею. У підсумку я отримав гру з цією змією, написаною на C #, розміром всього 8 кілобайт і була абсолютно незалежною від робочого середовища. Вся віртуальна машина стала непотрібною. Кожна команда та байт з мого вихідного коду були безпосередньо перекладені та простежені у отриманому файлі .EXE.

Я зробив зміїну гру для текстового режиму («консольний додаток»). Це було не складно, оскільки більшість операційних систем, включаючи Windows, мають функції читання та запису на консолі. Їх також можна викликати з C #. Проблема, однак, полягала в тому, що ці функції були додані до Windows ще довго після Windows 3.11.

Тому мені довелося коригувати цілі для Windows 3.11. Замість того, щоб пограти зі змією, я спробував відобразити лише просте текстове повідомлення двома кнопками. Однак я використав для цього деякі зміїні будівельні блоки. На останній Windows 10 така програма працювала без проблем відразу, але зі старою Windows 3.11 мені все-таки не пощастило.

Не було проблем із перекладом програми на рідний код?

Мені довелося мати справу з обмеженнями експериментального проекту, з яким я працював з самого початку. Він працює лише в 64-розрядних операційних системах. Він не буде працювати на 32- або навіть 16-бітовій системі.

Тут я натрапив на історичну цікавість. Windows 3.11 була 16-розрядною операційною системою, але з введенням наступної 32-розрядної Windows 95, Microsoft зробила невелику надбудову під назвою Win32 для цієї старої системи.

Win32s дозволив деяким легким і невеликим 32-розрядним програмам працювати на 16-розрядної Windows 3.11. Тому мені не потрібно було мати справу з переходом від 64 бітів до 16, а "лише" 32. Win32s послужили дуже хорошим фундаментом, оскільки на той час він підтримувався та інтегрувався в різні середовища розробки.

Перш ніж перейти до останнього кроку, варто згадати, як працює переклад програм у власний код. Власний компілятор коду генерує так звані "об'єктні файли". Ці файли містять власний код програми, але вони не є виконуваними, оскільки вони не є повними. Вони містять посилання на інші символи, визначені в інших об’єктних файлах.

Для створення виконуваного файлу використовується інструмент, який називається компонувальник, який поєднує кілька об’єктних файлів в один виконуваний блок.

Я скомпілював свій код за допомогою компілятора 2020 у об’єктні файли. Потім я з'єднав їх за допомогою лінкера з 1994 року.

Як ви використали цей "проміжний крок"?

Я переклав свою програму в об’єктні файли за допомогою згаданого експериментального проекту. Отримати 32-розрядні результати від експериментального компілятора не було такою проблемою, оскільки .NET Core підтримує 32-розрядні операційні системи. Експериментальний проект, що використовується, базується на .NET Core. Я програмував решту невеликих деталей на експериментальний компілятор. Я також інтегрував їх назад у проект, щоб зробити їх доступними для інших.

Згодом я використовував link.exe з 1994 р. Як компонувальник. Моя програма була примітивною і не мала ніяких залежностей, але все одно мене здивувало, що вона сумісна зі старим компонувальником. Однак формат файлу з тих пір не сильно змінився, тому компоновщик не мав з ним ні найменшої проблеми. І оскільки він з’явився в той самий час, що і Windows 3.11, він пов’язав мій код з іншими об’єктними файлами, які надійшли в потрібний час.

Отримана програма все ще працює і може працювати в Windows 10. Але вона також працює в Windows 3.11, якій зараз більше 26 років.

Пізніше мені навіть вдалося запустити гру на MS-DOS 5.

Ви не намагалися піти "далі"?

Пізніше мені навіть вдалося запустити гру на MS-DOS 5. Але зараз у мене немає часу на подібні вечірки. Тоді я був у батьківській відпустці, тож мав трохи більше часу. Зараз, крім дитини, мені ще доводиться керувати роботою.

Інші 16-розрядні системи або платформи, старші за Windows 3.11, потребують більше праці, ніж я готовий інвестувати. У Windows 3.11 перевагою став інструмент Win32s. В іншому випадку мені довелося б почати справу з самим генератором власного коду, наприклад, і це було б набагато більше роботи. Це було б неможливо, просто дуже трудомістко.

Тим часом один із авторів експериментального проекту .NET взяв мою гру та переробив її для роботи без будь-якої операційної системи. Команда насправді довела, що C # також можна використовувати для створення самих операційних систем.