[TYPES]
Type [0]: Pointer
Type [1]: U32
Type [2]: Variant
Type [3]: Unknown 14
Type [4]: Unknown 24
Type [5]: Extended
Type [6]: Double
Type [7]: Single
Type [8]: String
Type [9]: U32
Type [10]: S32
Type [11]: S16
Type [12]: U16
Type [13]: S8
Type [14]: Char
Type [15]: U32
Type [16]: U8 Export: BOOLEAN
Type [17]: U8
Type [18]: U8
Список типов, используемых в коде.
Число в скобках после слова Type - это порядковый номер (индекс) типа во внутреннем списке типов, после двоеточия - тип.
Начало списка практически одинаково у всех дампов, в список сначала заносятся встроенные фундаментальные типы движка, в конце те, что используются в коде пользователя явно и типы промежуточных переменных времени исполнения (результаты функций в выражениях хранятся в временных переменных, вызовы методов экземпляров классов и их свойств и т.д.).
U32 - unsigned integer, 32-бит, беззнаковое целое, (DWORD, Cardinal),
S32 - signed integer, 32-бит, знаковое целое (Integer, Longint),
S16 - SmallInt,
U16 - WORD,
S8 - ShortInt,
U8 - Byte, строка Type [16]:
U8 Export: BOOLEAN говорит, что в коде используется тип Boolean, а не Byte.
[VARS]
Список глобальных переменных. Пуст, глобальных переменных нет.
[PROCS]
Список процедур/функций.
Число в скобках после слова Proc - индекс процедуры/функции во внутреннем списке процедур.
Cлово Export - пользовательская процедура/функция.
Слова External Decl - встроенная процедура/функция.
После двоеточия: имя процедуры/функции, за которым следуют список типов параметров и их модификаторы. Если описывается процедура, то первым в списке будет написано -1, а если функция, то первым будет тип результата, следом идут параметры (если есть) вида "модификатор типпараметра", где модификатор @ - параметр передан по значению или с модификатором const, ! - параметр передан по ссылке с модификатором var или out. Например: @12 !14 - передали 2 параметра с типами под номерами из TYPES 12 и 14, второй параметр по ссылке, т.е. (var1: WORD; var var2: Char)
Proc [0] Export: !MAIN -1
[0] RET
С внутренней процедуры !MAIN начинается любой код.
Любая процедура/функция заканчивается инструкцией RET (от слова Return, выход из процедуры).
Число в скобках перед каждой инструкцией - смещение адреса инструкции относительно точки входа в процедуру/функцию.
Proc [1] Export: ISX64 16
Декларация пользовательской функции
ISX64 без параметров, возвращающей
Boolean.
А вот и первая пользовательская функция. Функция потому, что первый параметр (он же неявный параметр
Result) в списке не -1, а индекс типа, в секции
TYPES индекс
16 соответствует типу
Boolean. После
16 ничего нет, значит никакие параметры явно в функцию не передаются. Смело пишем:
function IsX64: Boolean; begin end;
[0] PUSHTYPE 18(U8) // 1
Заносим на стек указатель на тип внутренней переменной (PUSHTYPE), с которой будем работать ниже. Тут заносится инфа о типе U8 (Byte, Boolean).
При операциях со стеком после комментария // число указывает на индекс элемента на вершине стека (оно же количество элементов на стеке).
С PUSHTYPE может начинаться не только работа с внутренней переменной в блоке begin-end (тело), а еще и декларация локальных переменных (var). Как узнать где мы - уже в теле или объявляем локальные переменные? Очень просто. Перед RET инструкция POP не выталкивает из стека PUSHTYPE'ы локальных переменных, поэтому, например, POP // 2 перед RET будет означать, что первые 2 PUSHTYPE в самом начале объявляют 2 локальные переменные, а дальше уже тело идет.
[5] PUSHVAR Base[1] // 2
Инициализируем и заносим (PUSHVAR) на стек переменную (Base[1]), тип которой занесли предыдущей инструкцией. Число в скобках после Base - индекс элемента с инфой о типе на стеке. 1 элемент занесен предыдущей инструкцией - PUSHTYPE 18(U8) // 1.
[11] CALL 2
Вызов (
CALL) процедуры с индексом 2 (
Proc [2] соответствует вызову внутренней функции
PROCESSORARCHITECTURE, см. ниже).
Теперь понятна возня с помещением на стек указателя на тип переменной и самой переменной. Перед вызовом какой-либо процедуры/функции на стек сначала заносятся все параметры.
PROCESSORARCHITECTURE, как и любая другая функция, имеет 1 неявный параметр
Result, с ним и работали предыдущие
PUSHTYPE/PUSHVAR. Итак, временно пишем в теле:
ProcessorArchitecture
Пока еще не знаем, что с
ProcessorArchitecture (он же
Base[1], он же
Result функции
ProcessorArchitecture) делать.
[16] POP // 1
Выталкиваем из стека Result функции ProcessorArchitecture.
[17] COMPARE into Base[-1]: Base[1] = [2]
Операция сравнения (
COMPARE), результат сравнения (
Base[1] = [2]) заносится (
into) в переменную
Base[-1].
Видим, что внутренняя переменная
Base в скобках имеет отрицательный индекс
-1. Так вот по отрицательным индексам лежат переменные, передаваемые в пользовательскую процедуру/функцию. У функций, в том числе и нашей
IsX64, если вы не забыли, всегда есть по крайней мере 1 параметр, это
Result и он неявно по ссылке передается в функцию. Так вот
Base[-1] соответствует
Result,
Base[-2] - первому явному параметру,
Base[-3] - второму и т.д. Для процедур аналогично, кроме
Result, там нет неявных параметров, поэтому
Base[-1] - первый параметр,
Base[-2] - второй.
Итак, понятно, что головному
Result'у присваивается результат сравнения
Base[1] = [2].
Base[1] мы уже знаем, что это результат функции
ProcessorArchitecture,
= [2] сравнивается с константой 2. Глянем в хелп и найдем что
ProcessorArchitecture возвращает и с чем его можно сравнить. Результат типа
TSetupProcessorArchitecture, константа
2 этого перечисляемого типа соответствует
paX64. Итак, временно пишем:
Result := ProcessorArchitecture = paX64;
[35] POP // 0
Выталкиваем из стека инфу о типе.
[36] RET
Конец функции, выход из нее, больше с
Result ничего не делается, поэтому пишем уже на чистовик:
function IsX64: Boolean;
begin
Result := ProcessorArchitecture = paX64;
end;
Proc [2]: External Decl: \01 PROCESSORARCHITECTURE
Proc [3] Export: ISIA64 16
Декларация пользовательской функции IsIA64 без параметров, возвращающей Boolean.
[0] PUSHTYPE 18(U8) // 1
[5] PUSHVAR Base[1] // 2
На стеке тип Boolean и переменная Base[1] этого типа. Локальных переменных нет, т.к. последний POP указывает на 0.
[11] CALL 2
Вызов PROCESSORARCHITECTURE, результат в Base[1]
[16] POP // 1
Выталкиваем из стека Result функции ProcessorArchitecture.
[17] COMPARE into Base[-1]: Base[1] = [3]
Знакомая по предыдущей функции строка, только сравнение с константой
3. Пишем:
Result := ProcessorArchitecture = paIA64;
[35] POP // 0
Выталкиваем из стека инфу о типе.
[36] RET
Выход из функции. Пишем:
function IsIA64: Boolean;
begin
Result := ProcessorArchitecture = paIA64;
end;
Proc [4] Export: ISWIN2K 16
Декларация пользовательской функции IsWin2K без параметров, возвращающей Boolean.
[0] PUSHTYPE 9(U32) // 1
[5] PUSHVAR Base[1] // 2
На стеке тип Cardinal и переменная Base[1] этого типа. Локальных переменных нет, т.к. последний POP указывает на 0.
[11] CALL 5
Вызов GETWINDOWSVERSION, результат в Base[1]
[16] POP // 1
Выталкиваем из стека
Result функции
GetWindowsVersion. Временно пишем:
GetWindowsVersion
[17] CALC Base[1] SHR [24]
Операция с числами (
CALC). Биты числа
Base[1] (он же
GetWindowsVersion) сдвигаются вправо (
SHR) на
24 (
[24]) позиции, результат идет в
Base[1]. Временно пишем:
GetWindowsVersion shr 24
[33] COMPARE into Base[-1]: Base[1] >= [5]
Уже знаем. Временно пишем:
Result := GetWindowsVersion shr 24 >= 5;
[54] POP // 0
Выталкиваем из стека инфу о типе.
[55] RET
Выход из функции. Пишем:
function IsWin2K: Boolean;
begin
Result := GetWindowsVersion shr 24 >= 5;
end;
Proc [5]: External Decl: \01 GETWINDOWSVERSION
Proc [6] Export: ISOTHERARCH 16
Декларация пользовательской функции IsOtherArch без параметров, возвращающей Boolean.
[0] PUSHVAR Base[-1] // 1
Сразу работа с головным Result.
[6] CALL 1
Вызов
IsX64, результат в
Result. Временно пишем:
Result := IsX64;
[11] POP // 0
Выталкиваем из стека Result.
[12] BNOT Base[-1]
Логическая операция
not (
BNOT), применительно к нашему
Result. Временно пишем:
Result := not IsX64;
[18] COND_NOT_GOTO currpos + 36 Base[-1] [64]
Условие и переход по метке при невыпонении условия. Тут если Result равен False, то переход по смещению [64], там тоже условный переход по смещению [104], по смещению [104] инструкция RET.
[28] PUSHTYPE 16(U8) // 1
[33] PUSHVAR Base[1] // 2
На стеке тип Boolean и переменная Base[1] этого типа.
[39] CALL 3
Вызов
IsIA64, результат в
Base[1]. Временно пишем:
Result := not IsX64;
IsIA64
[44] POP // 1
Выталкиваем из стека Base[1].
[45] BNOT Base[1]
Логическая операция
not (
BNOT), применительно к нашему
Base[1]. Временно пишем:
Result := not IsX64;
not IsIA64
[51] CALC Base[-1] AND Base[1]
Опаньки,
Result скрестили с
Base[1] (
not IsIA64). Временно пишем:
Result := not IsX64;
Result := Result and not IsIA64;
Объединяем:
Result := not IsX64 and not IsIA64;
Теперь становится понятным почему 2 условных перехода. Сначала в выражении вычисляется
not IsX64 и если результат равен
False, то смысла в вычислении остальных членов логического выражения нет и происходит переход по первой метке на метку условного перехода от второго члена выражения
not IsIA64.
[63] POP // 0
Выталкиваем из стека инфу о типе.
[64] COND_NOT_GOTO currpos + 30 Base[-1] [104]
Если вторая часть выражения not IsIA64 равна False, то выход (переход на [104], где лежит RET).
[74] PUSHTYPE 16(U8) // 1
[79] PUSHVAR Base[1] // 2
На стеке тип Boolean и переменная Base[1] этого типа.
[85] CALL 4
Вызов
IsWin2K, результат в
Base[1]. Временно пишем:
Result := not IsX64 and not IsIA64;
IsWin2K
[90] POP // 1
Выталкиваем из стека Base[1].
[91] CALC Base[-1] AND Base[1]
Опять
Result скрестили с
Base[1] (
IsWin2K). Временно пишем:
Result := not IsX64 and not IsIA64;
Result := Result and IsWin2K;
Объединяем:
Result := not IsX64 and not IsIA64 and IsWin2K;
[103] POP // 0
Выталкиваем из стека инфу о типе.
[104] RET
Выход из функции. Пишем:
function IsOtherArch: Boolean;
begin
Result := not IsX64 and not IsIA64 and IsWin2K;
end;
Proc [7] Export: NOTX64 16
Декларация пользовательской функции NotX64 без параметров, возвращающей Boolean.
[0] PUSHVAR Base[-1] // 1
На стеке Result.
[6] CALL 1
Вызов
IsX64, результат в
Result. Временно пишем:
Result := IsX64;
[11] POP // 0
Выталкиваем из стека Result.
[12] BNOT Base[-1]
Логическая операция
not (
BNOT), применительно к нашему
Result. Временно пишем:
Result := not IsX64;
[18] COND_NOT_GOTO currpos + 36 Base[-1] [64]
На выход, если Result в False. Опять выражение?
[28] PUSHTYPE 16(U8) // 1
[33] PUSHVAR Base[1] // 2
На стеке тип Boolean и переменная Base[1] этого типа.
[39] CALL 3
Вызов
IsIA64, результат в
Base[1]. Временно пишем:
Result := not IsX64;
IsIA64
[44] POP // 1
Выталкиваем из стека Base[1].
[45] BNOT Base[1]
Логическая операция
not (
BNOT), применительно к нашему
Base[1]. Временно пишем:
Result := not IsX64;
not IsIA64
[51] CALC Base[-1] AND Base[1]
Result скрестили с
Base[1] (
not IsIA64). Временно пишем:
Result := not IsX64;
Result := Result and not IsIA64;
Объединяем:
Result := not IsX64 and not IsIA64;
[63] POP // 0
Выталкиваем из стека инфу о типе.
[64] RET
Выход из функции. Пишем:
function NotX64: Boolean;
begin
Result := not IsX64 and not IsIA64;
end;
Proc [8]: External Decl: \01\00 DIREXISTS
Дамп выложили неполностью, DIREXISTS нигде выше не используется.