HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > БЕЗОПАСНОСТЬ И УЯЗВИМОСТИ > Электроника и Фрикинг
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 16.08.2021, 15:31
Marylin
Постоянный
Регистрация: 01.09.2019
Сообщений: 378
С нами: 3526561

Репутация: 0
По умолчанию

Современные процессоры имеют весьма внушительный набор команд. Общее их кол-во можно подсчитать открыв том(2) Интеловского мануала"Instruction Set Referense". На различных площадках народ сильно преувеличивает утверждая, будто число опкодов давно перевалило за тысячу. Чтобы-уж наверняка и ради собственного интереса, я скопировал из оглавления указанной доки в блокнот весь перечень, и к своему удивлению на выходе получил всего 632 строки текста, в каждой из которых одна инструкция. Судя по-всему, на данный момент именно такой объём полностью удовлетворяет нашим требованиям, иначе монархи из Intel не упустили-бы возможности записать на свой счёт очередной новый опкод. Из всего этого листа, в данной статье предлагаю рассмотреть одну из интересных инструкций под названием CPUID, которая призвана идентифицировать бортовой процессор CPU.

Под капотом..


1. Основная идея;
2. Стандартные функции CPUID;
3. Расширенные функции;
4. Практика;
5. Постфикс.
--------------------------------------------------------

1. Основная идея

При включении машины, системный BIOS/EFI должен каким-либо образом опознать установленный на материнской плате CPU, чтобы настроить под него чипсет и всё остальное. Нужно сказать, что на первом витке эволюции вплоть до древних 16-битных процессоров i80286 в этом не было необходимости, поскольку архитектура компьютера была примитивной, с довольно устойчивым состоянием. Но тут пришёл 32-битный процессор нового поколения i80386, который мог работать как в 16, так и в 32-битном защищённом режиме. Здесь-то и всплыла проблема. Инженеры Intel с присущим им оптимизмом решили её просто – они жёстко запрограммировали проц, чтобы на старте он возвращал свою "половую" принадлежность в регистровой паре EСX:EDX. Но дальше-больше..

Инструкцию CPUID принёс с собой следующий процессор i80486, когда благодаря механизму PAE (Physical Address Extension, расширение физического адреса) 32-битному CPU стало доступно сразу три ступени памяти, разрядностью 16, 32 и 36-бит. Теперь, биосу требовался паспорт процессора, иначе он просто не мог должным образом настроить ответственные за распределение ОЗУ регистры чипсета. Так возникла необходимость в идентификации CPU, что вынудило Intel включить в набор команд инструкцию CPUID. Первая его спецификация от Intel датируется 1993-годом.

Если говорить об инструкциях в целом, то каждая из них является совокупностью микрокоманд. Когда инструкция простая (типа INC, ADD, SUB и т.д.), для неё достаточно одной микрокоманды, а если сложная – она собирается из нескольких таких команд. На этапе изготовления процессора, производитель зашивает в специально предназначенную для этих целей встроенную ROM-память весь поддерживаемый набор микрокоманд, из которых в конечном счёте и собираются разнообразные инструкции. Блок процессора под названием "микро-секвенсер" (microsequencer) считывает микрокоманды из хранилища ROM, и по-требованию передаёт их в декодер инструкций. Таким образом, чтобы включить в набор команд процессора какую-нибудь новую инструкцию, в большинстве случаях производителю достаточно добавить лишь пару ключевых микрокоманд.

На этапе производства процессора отследить все его ошибки невозможно, что влечёт за собой различные хард-глюки. По этой причине, производитель предусматривает обновление микро-кодов уже при эксплуатации готового продукта. Обновы всегда кратны 2Кб и их можно найти на сайтах Intel и AMD. Включённый в пакет прошивальщик сначала идентифицирует бортовой CPU, и если его модель/степпинг/ревизия совпадает с прошивкой, то заливает обновлённые микро-коды не в ROM процессора, а в системный BIOS, от куда проц потом их и стягивает. Intel давно уже практикует рассылку своих обновлений всем производителям биосов, чтобы они включали их в свой код. В конечном итоге, поддержка биосами современных процессоров определяется в первую очередь наличием соответствующей прошивки.

На уровне-же микроархитектуры CPUID устроена так, что помимо прочего, в постоянную память ROM производитель закладывает порядка 30-ти основных листов с детальной информацией о своём продукте, и некоторое кол-во (под)листов. В документации эти листы назвали "Leaf", а подлисты "Subleaf". Перед тем-как вызвать инструкцию CPUID, мы должны предварительно указать номер конкретного листа в регистре EAX, и если этот лист имеет вложенный (под)лист, то его номер занести ещё и в регистр ECX. На программном уровне, запрос каждого из листов представляет собой отдельную функцию, о чём прямым текстом и сообщается в спецификации CPUID.

В таблице ниже я перечислил все доступные мне функции и их назначения. К сожалению, в наличии оказалась только спека от 2012-года, и более свежих данных мне так и не удалось выудить из сети (если кто поделится, буду благодарен). Причём действительна представленная табличка только для процессоров Intel, поскольку у AMD нумерация функций совсем иная. Как по мне, так двум этим гигантам пора-бы уже не выносить нам мозг, а "выпить за мировую", чтобы согласовывать между собой хотя-бы такие элементарные вещи. Ан-нет.. нужно чтобы всё было через известное место. В силу того, что на обеих моих машинах (бук и стационар) установлены процессоры Intel, всё нижесказанное будет относиться исключительно к продуктам Intel.






Обратите внимание, что вложенные подлисты имеют только функции EAX=04,07,0В и 0Dh (выделены красным). Например если мы запросим инструкцию
Код:
CPUID.EAX=4:ECX=0
, то в регистрах EAX,EBX,ECX,EDX процессор вернёт нам информацию о своём кэше L1. Теперь, чтобы получить характеристики кэша L2, мы должны повторно вызвать
Код:
CPUID.EAX=4
, только на этот раз сменить номер (под)листа на ECX=1. Соответственно для кэша L3 будет уже
Код:
EAX=4:ECX=2
, и т.д. в том-же духе. Вне зависимости от разрядности процессора 32 или 64-бит, CPUID всегда возвращает информацию в первые четыре регистра общего назначения EAX,EBX,ECX,EDX. Надеюсь суть вы уловили, и вооружившись спекой можно теперь смело пробираться вглубь.

Спецификация CPUID для процессоров INTEL (2012 год)
Спецификация CPUID для процессоров AMD (2010 год)


2. Стандартные функции

Начнём по-порядку и рассмотрим на конкретных примерах, какого рода информацию возвращают все функции CPUID. В некоторых случаях, это поможет осуществить направленные на конкретный процессор атаки, ну или просто идентифицировать его, чтобы пустить код по нужной ветке. К примеру если мы написали приложение с использованием современных инструкций SSE4 или AVX, то это приложение может попасть на платформу, процессор которой вообще не поддерживает данный тип инструкций. Значит перед запуском софта нужно обязательно позвать CPUID и проверить в нём нужные биты. В любом случае знания всегда идут на шаг впереди их отсутствия, а потому профит по-любому будет.

2.0. Функция EAX=0. (Vendor-ID, and Largest Standard Function)

Данная функция возвращает в EAX общее число поддерживаемых процессором стандартных функций, и в остальные три регистра сбрасывает строку с именем производителя. Для Intel и AMD эта строка будет иметь вид "GenuineIntel" и "AuthenticAMD" соответственно. На процессоре своего стационара я получил всего 14 указанных в таблице выше функций, при этом
Код:
0x08
и
Код:
0x0C
находятся в резерве. А вот на буке с 64-битным процессором счётчик показывает уже 25 поддерживаемых функций, но как говорилось выше спеки на него нет. Пример вызова функции выглядит так:


C-подобный:


Код:
xor     eax
,
eax
;
//
,
eax
,
buff




2.1. ФункцияEAX=1. (Feature Information)

Очередная функция возвращает характерные особенности бортового процессора, где можно будет найти его: Family/Model/Stepping, идентификатор текущего контролёра прерываний APIC, макс.кол-во поддерживаемых ядер (не путать с реальным кол-вом), размер линейки кэш-памяти, и в 64-битах регистровой пары ECX:EDX разнообразные свойства, типа поддержка инструкций SSE4/AVX, гипер-трейдинг HT, x2APIC, RDRAND и многое другое (см.спецификацию). Чтобы вывести все эти 64 типа информации на консоль, придётся чекать их биты по отдельности например инструкцией
Код:
BT
(bit-test), как в примере ниже. Если тестируемый бит взведён, инструкция взводит флаг процессора(CF):


C-подобный:


Код:
mov     eax
,
1
cpuid
         mov
[
feature1
]
,
ecx
;
//
,
eax
,
ecx
,
edx
,
esi
,
edi

         bt
[
feature2
]
,
25
;
// проверить бит(25) в регистре EDX
jnc     @f
;
// пропустить, если он сброшен
cinvoke  printf
,

;
// иначе: есть поддержка инструкций SSE
@@
:
bt
[
feature2
]
,
26
;
// проверить на SSE2..
jnc     @f
;
//
cinvoke  printf
,

;
// есть поддержка
@@
:
bt
[
feature1
]
,
0
;
// и т.д..




Как видно из листа "FeatureFlags", мой бородатый проц не знаком с инструкциями SSE4/AVX, а лишь поддерживает макс.SSSE3. Имеет встроенный датчик температуры "TermalMonitor2" (TM2), в режиме энергосбережения может сохранять в специальной область памяти значения всех регистров CPU/FPU (xsave/fxsave соответственно), при переходе в ядро вместо устаревшего
Код:
SYSCALL
использует
Код:
SYSENTER
, способен очищать линейки кэша L1 при помощи инструкции
Код:
CLFLUSH
, и прочее. Описание всех этих аббревиатур имеется в спецификации, так-что курите её.

2.2. ФункцияEAX=2. (Cache Descriptors)

Код:
CPUID.EAX=2
возвращает дескрипторы кэша и TLB. На выходе, младшие 8-бит регистра EAX (AL) содержат значение, которое определяет, сколько раз нужно потянуть за
Код:
CPUID.EAX=2
, чтобы получить полный образ подсистемы кэш процессора. В большинстве случаях процессоры возвращают в AL значение(1), значит достаточно только одного вызова инструкции CPUID.

Каждый дескриптор кэша имеет размер 1-байт, и является закодированным значением определённой строки. На всякий-пожарный, я оформил все эти строки в виде таблички, и сохранил их в инклуд (см.скрепку в подвале статьи). Поскольку результат мы получим в четырёх 4-байтных регистрах EAX,EBX,ECX,EDX, итого получается 4х4=16 байт. Тогда общее кол-во дескрипторов будет 15, без учёта регистра-счётчика(AL). Если дескриптор имеет значение нуль, значит он пустой и не используется.

Теперь, для разбора дескрипторов последовательно перемещаемся от самого старшего байта всех регистров вниз, к младшему их байту (ну или наоборот). Код реализации парсинга дескрипторов и фрагмент таблицы из спецификации представлены ниже:


C-подобный:


Код:
cinvoke  printf
,

@@
:
mov     eax
,
2
cpuid
         dec     al
;
// повторить CPUID.EAX=2 по значению в AL
jnz     @b
         push    ebx ecx edx 

         call    printCpuid2
;
// распечатать дескрипторы из EAX
pop     eax
;
//
,
dword
[
esi
]
jmp     @f
;
// ..и на выход.
@fuck
:
add     esi
,
8
;
// если промах, смещаемся к сл.дескриптору в таблице..
loop    @b
;
//
@@
:
pop     ecx eax
;
//
ret




Проясним немного ситуацию с устройством кэша TLB процессора..

Не секрет, что процессоры заранее кэшируют нужную информацию, как-только им предоставляется малейшая возможность обращения к оперативной памяти ОЗУ. При этом контролёр памяти старается оттяпать из неё как-можно больше данных, для чего предусмотрен пакетный режим чтения. Значение "Burst-Length" (длина пакета) выставляется чипсетом на макс. =8, в результате чего контролёр повторяет операцию чтения по 8-байтной шине памяти 8-раз. Итого за одно обращение к ОЗУ контролёр читает про запас как-минимум пакетами по 64-байта данных, которые помещает в кэш-линейки "64 byte line size" (см.скрин выше). Кэши L1 бывают двух типов – отдельно для инструкций, отдельно для данных. А вот кэши L2 и L3 уже общие, без разделения на инструкции и данные.

Однако кроме кэша данных, в процессоре имеется ещё и кэш для хранения адресов страниц виртуальной памяти, к которым он обращался последний раз. Этот кэш назвали TLB, или "Translation Lookaside Buffer" (буфер ассоциативной трансляции). Проблема в том, что для доступа к памяти ОЗУ, транслятор контролёра памяти (в процессоре) сначала должен преобразовать текущий адрес из виртуального в физический, на что может уйти до 100-тактов процессора. По современным меркам, это чистой воды расточительство, поэтому один раз преобразовав вирт.адрес, процессор запоминает его в своём кэше TLB. Как показала практика, такой алго даёт существенный прирост скорости при чтении данных из оперативной памяти.

2.3.ФункцияEAX=3. (Processor Serial Number)

Если процессор фирменный и благополучно прошёл все надзорные инстанции, производитель может записать в него серийный номер продукта. Но в наше время это скорее исключение, чем правило, и лично мне такие пока не встречались.

2.4.ФункцияEAX=4. (Deterministic Cache Parameters)

Функция
Код:
CPUID.EAX=4
имеет подлист, поэтому вызывать её нужно с регистром ECX. Как и функция EAX=2, она возвращает информацию о кэшах процессора L1,2,3, только без кэшей TLB. Индекс в регистре ECX указывает, о каком кэше следует возвращать информацию. Чтобы получить информацию о кэшах всех уровней, мы должны вызывать её в цикле, пока в битах EAX[4:0] не получим нуль. Порядок возврата кэшей не регламентируется и может быть изменен по усмотрению Intel. Это означает, что при вызове функции с ECX=0 мы можем получить инфу например о кэше L3 и т.д. Бонусом, индекс(0) в регистре ECX возвращает ещё и кол-во ядер процессора, в старших битах EAX[31:26].

Вызов этой функции, в регистре EBX возвращает информацию об ассоциативности кэшей, кол-ве блоков "Way", и числу записей "Sets". Используя эти данные можно вычислить и размер кэша, который не возвращает функция
Код:
CPUID.EAX=4
в явном виде. Спецификация предлагает нам такую формулу для этих целей:


C-подобный:


Код:
Cache Size
in
Bytes
=
(
Ways
+
1
)
x
(
Partitions
+
1
)
x
(
Line Size
+
1
)
x
(
Sets
+
1
)
=
(
EBX
[
31
:
22
]
+
1
)
x
(
EBX
[
21
:
12
]
+
1
)
x
(
EBX
[
11
:
0
]
+
1
x
(
ECX
+
1
)


Ниже представлен возможный вариант вывода результата этой функции на консоль. Здесь я при первом вызове получаю кол-во ядер, и дальше в цикле зову эту функцию с инкрементом счётчика в ECX, чтобы получить инфу о кэшах всех уровней:


C-подобный:


Код:
mov     eax
,
4
;
// лист(4)
xor     ecx
,
ecx
;
// подлист(0)
cpuid
         shr     eax
,
26
;
//
,
eax
;
//----------------------------
@@
:
mov     eax
,
4
mov     ecx
,
[
count
]
;
// счётчик в переменной
cpuid
         push    ecx ebx ebx ebx eax
         and     eax
,
11111
b
;
// оставить 5 мл.бит в EAX
or      eax
,
eax
;
// проверить на окончание цикла
jz      @stop04
;
// да – на выход
mov     esi
,
cacheType
;
// иначе: ESI линк на таблицу в инклуде
dec     eax
;
// делаем из EAX смещение в таблице
shl     eax
,
2
;
// ^^^^
add     esi
,
eax
;
// ESI = указатель на строку с именем кэша
pop     ebx
;
// EBX = уровень кэша L1,2,3
shr     ebx
,
5
;
//
and     ebx
,
111
b
;
//
cinvoke  printf
,

,
ebx
,
dword
[
esi
]
pop     eax ebx ecx edx
         and     eax
,
0xfff
;
//
,
eax

         inc
[
count
]
;
// ECX +1
jmp     @b
;
// уйти на повтор цикла..
@stop04
:
add     esp
,
4
*
5




Здесь стоит отметить, что для хранения информации о кэшах, современные процессоры 64-бит используют только данную функцию
Код:
CPUID.EAX=4
, полностью игнорируя ранее рассмотренную
Код:
CPUID.EAX=2
, которая возвращает дескрипторы кэшей. В этом случае, при вызове EAX=2 на выходе получаем
Код:
0xFF
вместо дескрипторов, что означает см.функцию EAX=4. Вот фрагмент из описания функции
Код:
CPUID.EAX=2
с дескриптором
Код:
0xFF
:






3. Расширенные функции

Остальные стандартные функции не представляют особого интереса, и при желании вы можете по-экспериментировать с ними сами. А вот рассмотреть набор расширенных функций CPUID вполне себе стоит. Во времена своего младенчества, Intel и AMD разделили инструкцию CPUID пополам. Чтобы хоть как-то отличаться от Intel, инженеры AMD забрали себе старшую половину функций, начиная с 0x80000000. Однако позже всё утряслось и теперь обоим производителям принадлежит весь диапазон, хотя и нумерация по назначению абсолютно разная.

3.0.ФункцияEAX=8000_0000h (Largest Extended Function)

Эта функция особо не напрягается, и возвращает в единственный регистр EAX общее число поддерживаемых расширенных функций. На обоих своих процессорах я получаю значение EAX=8.

3.1.ФункцияEAX=8000_0001h (Extended Feature Bits)

Очередная функция возвращает в регистр EDX флаги расширенных возможностей процессора.


C-подобный:


Код:
mov     eax
,
0x80000001
cpuid
         mov
[
feature1
]
,
edx
;
//
bt
[
feature1
]
,
11
jnc     @f
        cinvoke  printf
,

;
//
@@
:
bt
[
feature1
]
,
26
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
27
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
29
jnc     @f
        cinvoke  printf
,

@@
:




Посмотрим на флаг "XD bit".

В документации AMD этот бит называется NX, от слова "No eXecution", а Intel обозвала его XD – "eXecution Disable". Это самый старший бит(64) в записях виртуальных страниц PTE (Page Table Entry), который предотвращает исполнение данных. Для более детального ознакомления с ним отправляю вас к статье
"DEP и ASLR – игра без правил", где ему была посвящена одна глава. Как видно из скрина, мой древний проц поддерживает и его, и архитектуру Intel-64, хотя в упор не видит гигантских виртуальных страниц размером 1Gb (по-умолчанию 4Кб), и не знаком с современной инструкций генератора рандома
Код:
RDTSCP
. Для перехода в ядро при вызовах API-функций инструкция
Код:
SYSCALL
уже давно не юзается даже процессорами Pentium, и ей на смену пришла свежая
Код:
SYSENTER
.

3.2.ФункцииEAX=8000_0002/3/4 (Processor Brand String)

Цепочка этих трёх функций возвращает имя процессора, как его окрестил производитель. Под строку брэнда производитель резервирует 48 байт во-встроенной памяти ROM, поэтому чтобы получить её полностью, нужно вызвать эти функции последовательно 3-раза. При каждом запросе в регистры EAX,EBX,ECX,EDX будет возвращаться новая информация, поэтому не забываем сохранять её в выделенный буфер. В примере ниже я поместил счётчик цикла в переменную "count", и на каждой итерации увеличиваю номер функции в EAX:


C-подобный:


Код:
mov     edi
,
buff
;
// адрес приёмника для STOSD
mov     eax
,
0x80000002
;
//
,
buff




Видимо хреновый контроль ведёт Intel над своими подчинёнными, о чём свидетельствует оформление строки. Зачем вставлять лишние пробелы в середину строки, когда можно дополнить её до 48-байт этими-же пробелами в конце? Здесь явно что-то не чисто..

3.3.ФункцияEAX=8000_0006h (Extended L2 Cache Features)

Ещё одна функция для сбора информации о кэше, только теперь исключительно об общем L2. Прямым текстом сообщает его размер и длину линейки, и в закодированном виде ассоциативность (мне лень было декодировать). Бьёт без промаха точно в цель на любых процессорах Intel, хоть 32, хоть 64-бит.


C-подобный:


Код:
mov     eax
,
0x80000006
cpuid
         push    ecx
         xchg    eax
,
ecx
         and     eax
,
0xff
;
//
,
ebx
,
eax




3.4.ФункцияEAX=8000_0008h (Virtual & Physical Address Sizes)

Это последняя функция из списка расширенных и возвращает способность процессора адресовать физическую и виртуальную память ОЗУ. Обратите внимание, что хоть процессор и 64-битный, память он адресует максимум 48-бит. Если вогнать это значение в степень двойки (зовите на помощь калькулятор), то получим макс.поддерживаемую современными процессорами память =
Код:
281.474.976.710.656
байт:


C-подобный:


Код:
mov     eax
,
0x80000008
cpuid
         push    eax
         and     eax
,
0xFF
;
//
,
eax
,
ebx




4. Пример кода для сбора информации CPUID

В заключении приведу готовый пример кода, который соберёт всю представленную выше информацию.


C-подобный:


Код:
format   pe console
entry    start
;
//-----------
section
'.inc'
data readable
include
'win32ax.inc'
include
'equates/cpuid.inc'
;
--
--
--
--
-
.
data
cacheType   dd  szInst
,
szData
,
szUni
szInst      db
'Instruction'
,
0
szData      db
'Data'
,
0
szUni       db
'Unified'
,
0
cpu         db
128
dup
(
0
)
count       dd
3
feature1    dd
0
feature2    dd
0
buff        db
0
;
--
--
--
--
-
.
code
start
:
invoke  SetConsoleTitle
,

;
//*************************************
xor     eax
,
eax
        cpuid
        mov     dword
[
buff
+
0
]
,
ebx
;
кидаем производителя в буфер
        mov     dword
[
buff
+
4
]
,
edx
        mov     dword
[
buff
+
8
]
,
ecx
       cinvoke  printf
,

,
eax
,
buff
;
//*************************************
mov     edi
,
buff
        mov     eax
,
0x80000002
@@
:
push    eax
        cpuid
        nop
        stosd
        mov     eax
,
ebx
        stosd
        mov     eax
,
ecx
        stosd
        mov     eax
,
edx
        stosd
        pop     eax
        inc     eax
        dec
[
count
]
jnz     @b
       cinvoke  printf
,

,
buff
;
//*************************************
mov     eax
,
1
cpuid
         mov
[
feature1
]
,
ecx
         mov
[
feature2
]
,
edx

         movzx   ecx
,
bl
;
// Brand index
movzx   edx
,
bh
;
// Cache line size
shl     edx
,
3
;
// ^^^^^
shr     ebx
,
16
;
//
movzx   esi
,
bl
;
//
movzx   edi
,
bh
;
//
cinvoke  printf
,

,
\
                         eax
,
ecx
,
edx
,
esi
,
edi

         bt
[
feature2
]
,
25
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
26
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
0
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
9
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
19
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
20
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
28
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
23
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
28
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
2
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
3
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
5
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
6
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
7
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
8
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
13
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
18
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
21
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
22
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
25
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
26
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
29
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
30
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
1
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
2
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
3
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
4
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
6
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
9
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
11
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
17
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
19
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature2
]
,
24
jnc     @f
        cinvoke  printf
,

@@
:
;
//*************************************
cinvoke  printf
,

@@
:
mov     eax
,
2
cpuid
         dec     al
         jnz     @b
         push    ebx ecx edx

         call    printCpuid2
         pop     eax
         call    printCpuid2
         pop     eax
         call    printCpuid2
         pop     eax
         call    printCpuid2
;
//************************************
xor     ecx
,
ecx
         mov     eax
,
4
cpuid
         shr     eax
,
26
inc     eax
        cinvoke  printf
,

,
eax
@@
:
mov     eax
,
4
mov     ecx
,
[
count
]
cpuid
         push    ecx ebx ebx ebx eax
         and     eax
,
11111
b
         or      eax
,
eax
         jz      @stop04

         mov     esi
,
cacheType
         dec     eax
         shl     eax
,
2
add     esi
,
eax

         pop     ebx
         shr     ebx
,
5
and     ebx
,
111
b
        cinvoke  printf
,

,
ebx
,
dword
[
esi
]
pop     eax ebx ecx edx
         and     eax
,
0xfff
;
// line size
inc     eax
         shr     ebx
,
12
and     ebx
,
0x3ff
;
// partition
inc     ebx
         shr     ecx
,
22
and     ecx
,
0x3ff
inc     ecx
         imul    eax
,
ebx
         imul    eax
,
ecx
         inc     edx
         imul    eax
,
edx
         shr     eax
,
10
cinvoke  printf
,

,
eax

         inc
[
count
]
jmp     @b

@stop04
:
add     esp
,
4
*
5
;
//*************************************
mov     eax
,
0x80000000
cpuid
         sub     eax
,
0x80000000
cinvoke  printf
,

,
eax

         mov     eax
,
0x80000001
cpuid
         mov
[
feature1
]
,
edx
        cinvoke  printf
,

bt
[
feature1
]
,
11
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
20
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
26
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
27
jnc     @f
        cinvoke  printf
,

@@
:
bt
[
feature1
]
,
29
jnc     @f
        cinvoke  printf
,

@@
:
;
//************************************
mov     eax
,
0x80000006
cpuid
         push    ecx
         xchg    eax
,
ecx
         and     eax
,
0xff
;
// line
pop     ebx
         shr     ebx
,
16
cinvoke  printf
,

,
ebx
,
eax
;
//************************************
mov     eax
,
0x80000008
cpuid
         push    eax
         and     eax
,
0xFF
pop     ebx
         shr     ebx
,
8
cinvoke  printf
,

,
eax
,
ebx

@exit
:
cinvoke  _getch
        cinvoke  exit
,
0
;
//---------------------
printCpuid2
:
mov     ecx
,
4
@next
:
xor     ebx
,
ebx
         mov     bl
,
al
         shr     eax
,
8
or      ebx
,
ebx
         jz      @f
         call    printCacheDescriptor
@@
:
loop    @next
ret

printCacheDescriptor
:
push    eax ecx
         mov     ecx
,
[
tSize
]
mov     esi
,
cacheTable
@@
:
cmp
[
esi
]
,
ebx
         jnz     @fuck
         add     esi
,
4
cinvoke  printf
,

,
dword
[
esi
]
jmp     @f
@fuck
:
add     esi
,
8
loop    @b
@@
:
pop     ecx eax
         ret
;
//---------------------
section
'.idata'
import data readable
library  msvcrt
,
'msvcrt.dll'
,
kernel32
,
'kernel32.dll'
include
'api\msvcrt.inc'
include
'api\kernel32.inc'




5. Постфикс

Опыты выше показали, что CPUID изначально не способен возвратить некоторую информацию. К примеру в нём нет данных типа: кодовое имя ядра, порог рабочих напряжений, тип сокета, потребляемая мощность, и значение производственного тех.процесса. Судя по всему, софт на подобии CPU-Z хранит это всё в своих таблицах, которые программисты скурпулёзно собирают ручками. Базы подобного рода пишутся исходя из значений "Family/Model/Stepping", предоставляемые нам функцией
Код:
CPUID.EAX=1
. В любом случае, теперь мы знаем, на что способен "зверь" под названием CPUID, и будем делать соответствующие выводы.

В скрепке можно найти инклуд с описанием дескрипторов кэшей, и исполняемый файл для тестов. Всем удачи, пока!
 
Ответить с цитированием

  #2  
Старый 16.08.2021, 21:10
DeathDay
Участник форума
Регистрация: 28.04.2019
Сообщений: 168
С нами: 3707869

Репутация: 0
По умолчанию

Очень познавательно. Спасибо.
 
Ответить с цитированием

  #3  
Старый 17.08.2021, 01:42
rusrst
Новичок
Регистрация: 18.04.2021
Сообщений: 0
С нами: 2670034

Репутация: 0
По умолчанию

Как всегда браво. Надо extern функцию для СИ собрать, для посмотреть на досуге.
 
Ответить с цитированием

  #4  
Старый 17.08.2021, 05:08
Mikl___
Новичок
Регистрация: 14.12.2019
Сообщений: 0
С нами: 3377258

Репутация: 0
По умолчанию

Спасибо, Marylin! Коротко, классно и познавательно!
 
Ответить с цитированием

  #5  
Старый 18.08.2021, 18:20
Crazy Jack
Новичок
Регистрация: 08.07.2017
Сообщений: 0
С нами: 4657944

Репутация: 0
По умолчанию

Респект и уважуха Marylin.Как обычно, коротко и ясно.
 
Ответить с цитированием

  #6  
Старый 18.08.2021, 19:38
Александр Васильев
Новичок
Регистрация: 17.08.2021
Сообщений: 0
С нами: 2495447

Репутация: 0
По умолчанию

Кратко, четко и по факту!
 
Ответить с цитированием
Ответ





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.