Середина ночі, developer.apple.com працює повільно і з затримками, і вони не знають чому. Але я все одно спробую Swift.
> swift --version
Swift version 6.0.3 (swift-6.0.3-RELEASE)
Target: x86_64-unknown-windows-msvc
Перше враження
В деяких мовах, які вважаються низькорівневими, якщо ви зможете викликати MessageBox
, ви зрозумієте Матрицю. У Swift, виявляється, все, що потрібно, це просто import WinSDK
:
import WinSDK
func DllMain(
hinstDLL: HINSTANCE,
fdwReason: DWORD,
lpvReserved: LPVOID
) -> Bool {
return false
}
Компілірувати це також просто:
swiftc SwiftDll.swift -emit-library -O
Немає необхідності оголошувати нічого вручну, ніяких префіксів, нічого. Однак DllMain
в SwiftDll.dll
залишається за замовчуванням, викликаючи DisableThreadLibraryCalls
і повертаючи 1. Я знайшов таке рішення: зробити його public func DllMain
і позначити як @_silgen_name("DllMain")
. З якоїсь причини @_cdecl("DllMain")
змушує його з'являтися двічі з різними іменами — розфасований $s8SwiftDll0B4Main8hinstDLL9fdwReason11lpvReservedSbSpySo11HINSTANCE_VGs6UInt32VSvtF і очікуваний DllMain
. Це працює, але це буквально той самий код, який повторюється двічі.
Новина: У Swift немає статичних змінних, крім статичних членів struct
/class
. Отже, якщо я хочу зареєструвати обробник виключень, мені потрібно зберігати результат AddVectoredExceptionHandler
у глобальній змінній. Якого типу..?
5 | var exceptionHandler: PVOID = NULL
| `- error: cannot convert value of type '()'
to specified type 'PVOID' (aka 'UnsafeMutableRawPointer')
ChatGPT згадує UnsafeMutableRawPointer?
і nil
. Додавання ?
до PVOID
та зміна NULL
на nil
вирішують проблему.
Тест
Через деякий час обробник виключень виглядає ось так:
public func handleException(
exceptionInfo: PEXCEPTION_POINTERS?
) -> LONG {
let exceptionCode =
exceptionInfo!.pointee.ExceptionRecord!.pointee.ExceptionCode
let address =
exceptionInfo!.pointee.ExceptionRecord!.pointee.ExceptionInformation.1
let context =
exceptionInfo!.pointee.ContextRecord
if (exceptionCode == EXCEPTION_ACCESS_VIOLATION) {
addRecord(address, context!.pointee.Rip)
savedAddress = address
var oldProtect: DWORD = 0
VirtualProtect(
LPVOID(bitPattern: UInt(address)),
1,
DWORD(PAGE_READWRITE),
&oldProtect
)
context!.pointee.EFlags |= trapFlag
}
if (exceptionCode == EXCEPTION_SINGLE_STEP) {
var oldProtect: DWORD = 0
VirtualProtect(
LPVOID(bitPattern: UInt(savedAddress)),
1,
DWORD(PAGE_NOACCESS),
&oldProtect
)
}
return EXCEPTION_CONTINUE_EXECUTION
}
При порушенні доступу (код виключення EXCEPTION_ACCESS_VIOLATION
), буде виконано наступне:
- Логування адреси помилкової інструкції та адреси даних, до яких намагається звернутися інструкція
- Зміна захисту сторінки пам'яті, що містить дані, на
PAGE_READWRITE
, щоб ці дані були доступні для читання та запису - Повторне виконання помилкової інструкції
Після повторного виконання інструкції (код виключення EXCEPTION_SINGLE_STEP
), обробник змінює захист сторінки назад на PAGE_NOACCESS
.
Тепер ідея — ввести SwiftDll.dll
в динамічну гру і встановити захист деякої часто використовуваної сторінки на PAGE_NOACCESS
— наприклад, сторінки, що містить здоров'я гравця, його позицію в світі тощо. Cheat Engine підходить для знаходження таких речей. Process Hacker можна використовувати для ін'єкції DLL.
Нехай це буде Counter-Strike 2. Виявляється, його потрібно запустити з прапором -insecure
, щоб ін'єкція DLL працювала. Чи не дозволяє Process Hacker змінювати захист конкретної сторінки? Що ж, виклик VirtualProtect
безпосередньо в DllMain
спрацює.
Нехай адреса буде адресою значення здоров'я.
Отже, після ін'єкції, чи стала гра повільною? Так. Чи повільніша вона зараз, ніж лівий панель на developer.apple.com? Може, так, а може й ні.
У мене також є CppDll.dll
, яка є точно таким самим, але реалізована на C/C++. Як вона працює порівняно з SwiftDll.dll
? Мені потрібно було б увімкнути лічильник FPS, щоб побачити різницю. Насправді, увімкнення його показує, що в обох випадках це одні й ті ж 5-7 FPS.
Висновок
Сучасний C++ не такий "безкоштовний", якщо тільки ваш компілятор не Clang. Якщо це так, ви можете просто написати новий код на Swift. Проект Embedded Swift також вражає. І останнє, але не менш важливе, компіляція займає багато часу.
Swift для C++ практиків, Частина 1: Вступ і типи значень
Ліва панель дуже повільна · Проблема #919
Перекладено з: Swift for Systems Programming