ExGenRandom — Генератор псевдослучайных чисел ядра Windows NT ОС: Windows 11 Build 26100 (ARM64) Отладчик: WinDbg Preview (Kernel Debugging via Serial) Целевая архитектура: ARM64 (Qualcomm Snapdragon) Экспорт nt!ExGenRandom — это фундаментальный генератор псевдослучайных чисел (PRNG) ядра Windows NT. На нём строятся все основные API генерации случайных чисел как в kernel-mode (RtlRandom, RtlRandomEx), так и косвенно в user-mode через криптографические примитивы. В этом исследовании разберём алгоритм целиком — от спинлока до финального XOR. Содержание Цепочка вызовов Дизассемблирование ExGenRandom Алгоритм LFG (Lagged Fibonacci Generator) Глобальные переменные и структура PRNG Обёртки: RtlRandom и RtlRandomEx BCrypt Pipeline и SmCrGenRandom Глобальные seed-переменные Спинлок и синхронизация Ветвь KeBugCheckEx Сводные таблицы Сравнение PRNG функций Windows Практические выводы 1. Цепочка вызовов Код (Text): ntdll!RtlRandom └─► nt!RtlRandom └─► nt!ExGenRandom (w0=1) ├─► nt!KfRaiseIrql (DISPATCH_LEVEL) ├─► nt!KiAcquireSpinLockInstrumented / SWPA ├─► [LFG Algorithm: sub table[i], table[j] → result] ├─► nt!KiReleaseSpinLockInstrumented / STLR ├─► nt!KfLowerIrql └─► XOR nt!ExpRNGAuxiliarySeed → return ntdll!RtlRandomEx └─► nt!RtlRandomEx └─► nt!ExGenRandom (w0=1) nt!BCryptGenRandom ├─► nt!ExGetExtensionTable (CNG) ├─► nt!KscpCfgCheckUserCallTargetEs (CFG) └─► [CNG Provider callback] nt!SmCrGenRandom └─► nt!BCryptGenRandom (tail call) 2. Дизассемблирование ExGenRandom Адрес: Код (Text): nt!ExGenRandom = 0xFFFFF801'B4582828 Прототип: Код (Text): ULONG ExGenRandom(ULONG GeneratorIndex); Параметр w0 (GeneratorIndex) определяет, какой из двух генераторов использовать: - w0 = 0 — Генератор #0 (общесистемный) - w0 = 1 — Генератор #1 (используется RtlRandom/RtlRandomEx) Полная дизассембликация: Код (Text): nt!ExGenRandom: ; ═══════════════════════════════════════════ ; Пролог: PAC + сохранение регистров ; ═══════════════════════════════════════════ fffff801`b4582828 pacibsp ; PAC signing fffff801`b458282c stp x19,x20,[sp,#-0x30]! ; Сохранить callee-saved fffff801`b4582830 stp x21,x22,[sp,#0x10] fffff801`b4582834 str x23,[sp,#0x20] fffff801`b4582838 stp fp,lr,[sp,#-0x10]! fffff801`b458283c mov fp,sp ; ═══════════════════════════════════════════ ; Загрузка адреса спинлока + выравнивание ; ═══════════════════════════════════════════ fffff801`b4582840 adrp x8,nt!WheapSelBuffer+0xc00 fffff801`b4582844 add x20,x8,#0xC68 ; x20 = SpinLock fffff801`b4582848 tst x20,#7 ; Проверка выравнивания fffff801`b458284c mov w19,w0 ; w19 = GeneratorIndex fffff801`b4582850 bne +0x120 ; → KeBugCheckEx если не выровнен ; ═══════════════════════════════════════════ ; Повышение IRQL → DISPATCH_LEVEL (2) ; ═══════════════════════════════════════════ fffff801`b4582854 mov w0,#2 fffff801`b4582858 bl nt!KfRaiseIrql ; IRQL = DISPATCH_LEVEL fffff801`b458285c adrp x8,nt!PpmPolicyConfigTable+0x140 fffff801`b4582860 add x21,x8,#0xB00 ; x21 = PpmPolicyConfigTable+0xb04 fffff801`b4582864 ldr w9,[x21,#4] ; Флаги instrumentation fffff801`b4582868 mov w8,#0x210000 fffff801`b458286c uxtb w23,w0 ; Сохранить старый IRQL в w23 fffff801`b4582870 adrp x22,nt!PopStateTransitonBlameStack+0x3a0 fffff801`b4582874 tst w9,w8 ; Проверка флага 0x210000 fffff801`b4582878 bne +0x10c ; → Instrumented path если флаг установлен ; ═══════════════════════════════════════════ ; Быстрый путь: SWPA (atomic spinlock) ; ═══════════════════════════════════════════ fffff801`b458287c mov x8,#1 fffff801`b4582880 swpa x8,x8,[x20] ; Atomic swap → spinlock fffff801`b4582884 cbz x8,+0x10 ; Если 0 → acquired, прыгаем fffff801`b4582888 mov x0,x20 ; Иначе → ждать fffff801`b458288c bl nt!KxWaitForSpinLockAndAcquire ; ═══════════════════════════════════════════ ; Проверка: GeneratorIndex == 1 ? ; ═══════════════════════════════════════════ fffff801`b4582890 cmp w19,#1 fffff801`b4582894 bne +0x18 ; Если != 1 → основной LFG path ; ═══════════════════════════════════════════ ; Ветвь для Generator #1: проверка pending reseed ; ═══════════════════════════════════════════ fffff801`b4582898 adrp x10,nt!WheapSelBuffer+0xc00 fffff801`b458289c ldr w8,[x10,#0xC70] ; PendingReseedCounter fffff801`b45828a0 cbnz w8,+0x144 ; Если != 0 → reseed path ; ═══════════════════════════════════════════ ; LFG Core: адресация таблицы генератора ; ═══════════════════════════════════════════ fffff801`b45828a4 adrp x8,nt!WheapSelBuffer+0xc00 fffff801`b45828a8 add x8,x8,#0xAA0 ; База PRNG таблиц fffff801`b45828ac mov x9,#0xE4 ; Размер одного генератора fffff801`b45828b0 smaddl x12,w19,w9,x8 ; x12 = base + index * 0xE4 ; Загрузка индексов i и j fffff801`b45828b4 ldp w9,w8,[x12,#0xDC] ; w9 = index_i, w8 = index_j fffff801`b45828b8 cmp w9,#0x36 ; Проверка: i < 54? fffff801`b45828bc csinceq w11,wzr,w9 ; w11 = i (если i==54 → сброс в 0) fffff801`b45828c0 cmp w8,#0x36 ; Проверка: j < 54? fffff801`b45828c4 ldr w9,[x12,w11 uxtw #2] ; w9 = table[i] fffff801`b45828c8 csinceq w10,wzr,w8 ; w10 = j (если j==54 → сброс в 0) fffff801`b45828cc ldr w8,[x12,w10 uxtw #2] ; w8 = table[j] ; ═══════════════════════════════════════════ ; Вычисление: result = table[i] - table[j] ; ═══════════════════════════════════════════ fffff801`b45828d0 sub w19,w9,w8 ; w19 = table[i] - table[j] fffff801`b45828d4 str w19,[x12,w11 uxtw #2] ; table[i] = result fffff801`b45828d8 stp w11,w10,[x12,#0xDC] ; Сохранить новые i, j ; ═══════════════════════════════════════════ ; Освобождение спинлока ; ═══════════════════════════════════════════ fffff801`b45828dc ldr w8,[x21,#4] ; Флаги instrumentation fffff801`b45828e0 tbnz w8,#0x10,+0x38 ; Если bit 4 → instrumented release ; Быстрый путь: STLR (store-release) fffff801`b45828e4 stlr xzr,[x20] ; SpinLock = 0 (release) ; ═══════════════════════════════════════════ ; Восстановление IRQL ; ═══════════════════════════════════════════ fffff801`b45828e8 mov w0,w23 ; Старый IRQL fffff801`b45828ec bl nt!KfLowerIrql ; IRQL restored ; ═══════════════════════════════════════════ ; Финальный XOR с auxiliary seed ; ═══════════════════════════════════════════ fffff801`b45828f0 adrp x8,nt!WheapSelBuffer+0xc00 fffff801`b45828f4 ldr w8,[x8,#0xC74] ; ExpRNGAuxiliarySeed (XOR mask) fffff801`b45828f8 eor w0,w8,w19 ; result ^= AuxiliarySeed ; ═══════════════════════════════════════════ ; Эпилог: AUTIBSP + RET ; ═══════════════════════════════════════════ fffff801`b45828fc ldp fp,lr,[sp],#0x10 fffff801`b4582900 ldr x23,[sp,#0x20] fffff801`b4582904 ldp x21,x22,[sp,#0x10] fffff801`b4582908 ldp x19,x20,[sp],#0x30 fffff801`b458290c autibsp ; PAC verify fffff801`b4582910 ret ; ═══════════════════════════════════════════ ; Instrumented spinlock release ; ═══════════════════════════════════════════ fffff801`b4582914 ldr w8,[x22,#0x930] ; Instrumentation enabled? fffff801`b4582918 cbnz w8,-0x58 ; Да → обычный STLR release fffff801`b458291c ldr lr,[sp,#8] fffff801`b4582920 mov x0,x20 fffff801`b4582924 xpaclri fffff801`b4582928 mov x1,lr fffff801`b458292c bl nt!KiReleaseSpinLockInstrumented fffff801`b4582930 b -0x54 ; → restore IRQL ; ═══════════════════════════════════════════ ; Instrumented spinlock acquire ; ═══════════════════════════════════════════ fffff801`b4582934 ldr w8,[x22,#0x930] fffff801`b4582938 cbnz w8,-0x64 ; → SWPA path fffff801`b458293c mov x0,x20 fffff801`b4582940 bl nt!KiAcquireSpinLockInstrumented fffff801`b4582944 b -0x58 ; → GeneratorIndex check ; ═══════════════════════════════════════════ ; KeBugCheckEx: невалидное выравнивание ; ═══════════════════════════════════════════ fffff801`b4582948 adrp x8,nt!PpmPolicyConfigTable+0x140 fffff801`b458294c ldr w8,[x8,#0xD60] fffff801`b4582950 tbnz w8,#0,-0x08 ; Retry если флаг установлен fffff801`b4582954 mov x4,#0 ; P4 = 0 fffff801`b4582958 mov x3,#8 ; P3 = 8 (alignment size) fffff801`b458295c mov x2,x20 ; P2 = SpinLock address fffff801`b4582960 mov x1,#0 ; P1 = 0 fffff801`b4582964 mov w0,#0x1F6 ; BugCheck 0x1F6 = INVALID_CALLBACK fffff801`b4582968 bl nt!KeBugCheckEx ; BSOD ; ═══════════════════════════════════════════ ; Reseed path: вытягивание seed из extra pool ; ═══════════════════════════════════════════ fffff801`b458296c sub w9,w8,#1 ; Decrement counter fffff801`b4582970 adrp x8,nt!WheapSelBuffer+0xc00 fffff801`b4582974 add x8,x8,#0x760 ; Extra seed pool base fffff801`b4582978 str w9,[x10,#0xC70] ; Store new counter fffff801`b458297c add x8,x8,w9,uxtw #2 ; x8 = pool[counter-1] fffff801`b4582980 ldr w19,[x8] ; w19 = seed value fffff801`b4582984 strb wzr,[x8] ; Zero out seed[0] fffff801`b4582988 strb wzr,[x8,#1] ; Zero out seed[1] fffff801`b458298c strb wzr,[x8,#2] ; Zero out seed[2] fffff801`b4582980 strb wzr,[x8,#3] ; Zero out seed[3] fffff801`b4582994 b -0xC4 ; → Release spinlock 3. Алгоритм LFG (Lagged Fibonacci Generator) Ядро ExGenRandom реализует модифицированный алгоритм Lagged Fibonacci Generator (LFG) с параметром taп = 55 (таблица из 55 DWORD значений). Математическая модель: Код (Text): table[i] = (table[i] - table[j]) mod 2^32 result = table[i] i = (i + 1) mod 55 j = (j + 1) mod 55 final = result XOR AuxiliarySeed Структура генератора (0xE4 байта): СмещениеРазмерОписание+0x000xDC (55 DWORD)State table — массив из 55 значений+0xDC4 байта (DWORD)Index i — текущая позиция первого лага+0xE04 байта (DWORD)Index j — текущая позиция второго лагаСброс индексов: Когда индекс достигает 0x36 (54), он сбрасывается в 0 инструкцией Код (Text): csinceq wN, wzr, wN — это реализация модульной арифметики Код (Text): index mod 55 . Два генератора: ГенераторИндекс (w0)Адрес базыИспользование#00WheapSelBuffer+0xAA0Общесистемный (kernel internal)#11WheapSelBuffer+0xB84RtlRandom / RtlRandomEx 4. Глобальные переменные и структура PRNG СимволАдресОписаниеPRNG Table Basent!WheapSelBuffer+0xAA0База массива из 2-х LFG генераторов (по 0xE4 байта)SpinLocknt!WheapSelBuffer+0xC68Спинлок для синхронизации PRNG (8 байт)PendingReseedCounternt!WheapSelBuffer+0xC70Счётчик отложенных reseed операций для генератора #1ExpRNGAuxiliarySeednt!WheapSelBuffer+0xC74XOR-маска для финального смешивания результатаExtra Seed Poolnt!WheapSelBuffer+0x760Дополнительный пул seed-значений (DWORD массив)Instrumentation Flagsnt!PpmPolicyConfigTable+0xB04Флаги ETW instrumented spinlock (0x210000)Instrumentation Datant!PopStateTransitonBlameStack+0x930Указатель на ETW instrumentation контекстBugCheck Flagnt!PpmPolicyConfigTable+0xD60Флаг для retry при невалидном выравниванииДамп AuxiliarySeed (текущее значение): Код (Text): nt!WheapSelBuffer+0xC74 (ExpRNGAuxiliarySeed): 0x00000000 5. Обёртки: RtlRandom и RtlRandomEx Обе функции идентичны — они являются обёртками, вызывающими Код (Text): ExGenRandom(1) и маскирующими результат до 31 бита. nt!RtlRandom = 0xFFFFF801'B4B1BD90 Код (Text): nt!RtlRandom: fffff801`b4b1bd90 pacibsp fffff801`b4b1bd94 str x19,[sp,#-0x10]! fffff801`b4b1bd98 stp fp,lr,[sp,#-0x10]! fffff801`b4b1bd9c mov fp,sp fffff801`b4b1bda0 mov x19,x0 ; x19 = &Seed fffff801`b4b1bda4 mov w0,#1 ; GeneratorIndex = 1 fffff801`b4b1bda8 bl nt!ExGenRandom ; Вызов LFG PRNG fffff801`b4b1bdac and w0,w0,#0x7FFFFFFF ; result &= 0x7FFFFFFF (31 бит) fffff801`b4b1bdb0 str w0,[x19] ; *Seed = result fffff801`b4b1bdb4 ldp fp,lr,[sp],#0x10 fffff801`b4b1bdb8 ldr x19,[sp],#0x10 fffff801`b4b1bdbc autibsp fffff801`b4b1bdc0 ret nt!RtlRandomEx = 0xFFFFF801'B450FBF0 Код (Text): nt!RtlRandomEx: fffff801`b450fbf0 pacibsp fffff801`b450fbf4 str x19,[sp,#-0x10]! fffff801`b450fbf8 stp fp,lr,[sp,#-0x10]! fffff801`b450fbfc mov fp,sp fffff801`b450fc00 mov x19,x0 ; x19 = &Seed fffff801`b450fc04 mov w0,#1 ; GeneratorIndex = 1 fffff801`b450fc08 bl nt!ExGenRandom ; Вызов LFG PRNG fffff801`b450fc0c and w0,w0,#0x7FFFFFFF ; result &= 0x7FFFFFFF (31 бит) fffff801`b450fc10 str w0,[x19] ; *Seed = result fffff801`b450fc14 ldp fp,lr,[sp],#0x10 fffff801`b450fc18 ldr x19,[sp],#0x10 fffff801`b450fc1c autibsp fffff801`b450fc20 ret Ключевое наблюдение: Дизассембликация RtlRandom и RtlRandomEx полностью идентична — это одни и те же инструкции по разным адресам. Обе вызывают Код (Text): ExGenRandom(1) и маскируют результат до 31 бита Код (Text): & 0x7FFFFFFF . Это означает, что RtlRandomEx не предоставляет криптографически более стойкий результат по сравнению с RtlRandom — это просто второй экземпляр той же функции. 6. BCrypt Pipeline и SmCrGenRandom nt!BCryptGenRandom = 0xFFFFF801'B4760B40 Код (Text): nt!BCryptGenRandom: fffff801`b4760b40 pacibsp fffff801`b4760b44 stp x19,x20,[sp,#-0x20]! fffff801`b4760b48 stp x21,x22,[sp,#0x10] fffff801`b4760b4c stp fp,lr,[sp,#-0x10]! fffff801`b4760b50 mov fp,sp fffff801`b4760b54 adrp x19,nt!SepTokenPolicyCounter+0xe0 fffff801`b4760b58 ldr x0,[x19,#0xD0] ; CNG Extension Table fffff801`b4760b5c mov x22,x1 ; Buffer fffff801`b4760b60 mov w21,w2 ; Size fffff801`b4760b64 ldr w20,[pc+0x58] ; Tag fffff801`b4760b68 bl nt!ExGetExtensionTable ; Получить CNG provider fffff801`b4760b6c mov x8,x0 fffff801`b4760b70 cbz x8,+0x34 ; Если NULL → fallback ; Вызов CNG provider через CFG fffff801`b4760b74 ldr x8,[x8,#0xA0] ; Генератор random fffff801`b4760b78 mov w3,#2 ; Flags fffff801`b4760b7c mov w2,w21 ; Size fffff801`b4760b80 mov x1,x22 ; Buffer fffff801`b4760b84 mov x0,#0 ; hAlgorithm = NULL fffff801`b4760b88 mov x15,x8 ; Target function fffff801`b4760b8c bl nt!KscpCfgCheckUserCallTargetEs ; CFG validation fffff801`b4760b90 blr x15 ; Вызов CNG random generator ; Cleanup fffff801`b4760b94 ldr x8,[x19,#0xD0] fffff801`b4760b98 mov w20,w0 fffff801`b4760b9c add x0,x8,#0x58 fffff801`b4760ba0 bl nt!ExReleaseRundownProtection fffff801`b4760ba4 mov w0,w20 ; Return NTSTATUS ... fffff801`b4760bb8 ret nt!SmCrGenRandom = 0xFFFFF801'B476A2C0 Код (Text): nt!SmCrGenRandom: fffff801`b476a2c0 mov w2,w1 ; Size fffff801`b476a2c4 mov x1,x0 ; Buffer fffff801`b476a2c8 b nt!BCryptGenRandom ; Tail call Важное различие: - ExGenRandom → Быстрый, некриптографический PRNG (LFG). Не подходит для криптографии. - BCryptGenRandom → Криптографический PRNG через CNG (Cryptographic Next Generation) provider. Использует проверенный CSPRNG. - SmCrGenRandom → Просто tail-call обёртка над BCryptGenRandom. 7. Глобальные seed-переменные ПеременнаяАдресЗначениеНазначениеExpRNGAuxiliarySeednt!WheapSelBuffer+0xC740x00000000XOR-маска финального смешивания ExGenRandomKiProcessNodeSeednt!KiProcessNodeSeed0x00000000'00000000Seed для NUMA node процессораCcRandomSeednt!CcRandomSeed0x00000003'0000ADADSeed для Cache Manager рандомизацииViRandomSeednt!ViRandomSeed0x00000000'0035CA79Seed для Driver VerifierCarEtwRandomSeednt!CarEtwRandomSeed0x00000000'00000000ETW random seedHvSymcryptSeednt!HvSymcryptSeed—SymCrypt seed для Hyper-VRtlpLowFragHeapRandomDatant!RtlpLowFragHeapRandomData60874C69'1B747204...Seed для Low Fragmentation HeapRtlpSeedGlfsr (GLFSR seed function) — инициализирует seed через аппаратный счётчик: Код (Text): nt!RtlpSeedGlfsr: fffff801`b4827478 mrs x8,PMCCNTR_EL0 ; Аппаратный performance counter fffff801`b482747c uxtw x11,w8 ; Младшие 32 бита fffff801`b4827480 mov x8,#0 ; bit = 0 fffff801`b4827484 mov w10,#0x20 ; 32 итерации loop: fffff801`b4827488 mov x9,#1 fffff801`b482748c lsl x9,x9,x8 ; mask = 1 << bit fffff801`b4827490 and x9,x9,x11 ; test = counter & mask fffff801`b4827494 lsl x9,x9,x8 ; test <<= bit fffff801`b4827498 eor x0,x9,x0 ; seed ^= test fffff801`b482749c add x8,x8,#1 ; bit++ fffff801`b48274a0 sub w10,w10,#1 ; remaining-- fffff801`b48274a4 cbnz w10,loop ; Пока remaining != 0 fffff801`b48274a8 ret Этот код извлекает энтропию из аппаратного performance counter процессора (PMCCNTR_EL0), обрабатывая каждый бит через XOR. Используется при инициализации PRNG для создания начального seed. 8. Спинлок и синхронизация ExGenRandom использует два механизма синхронизации для потокобезопасности: Механизм 1: IRQL elevation Код (Text): KfRaiseIrql(DISPATCH_LEVEL = 2) ; Запретить переключение потоков ... PRNG operation ... KfLowerIrql(oldIrql) ; Восстановить Механизм 2: SpinLock Код (Text): nt!WheapSelBuffer+0xC68 — 8-байтовый спинлок Два пути захвата спинлока: ПутьУсловиеМеханизмБыстрыйФлаг 0x210000 сброшенSWPA (Store Word Pair Atomic) — direct atomic swapInstrumentedФлаг 0x210000 установленKiAcquireSpinLockInstrumented / KiReleaseSpinLockInstrumented — с ETW трассировкойАтомарный захват (SWPA): Код (Text): mov x8,#1 swpa x8,x8,[x20] ; Atomic: if lock==0, set to 1 and return old=0 (acquired) cbz x8,acquired ; Если old==0 → успешно bl KxWaitForSpinLockAndAcquire ; Иначе → spin-wait Освобождение (STLR): Код (Text): stlr xzr,[x20] ; Store-release: SpinLock = 0 9. Ветвь KeBugCheckEx Если адрес спинлока не выровнен на 8 байт ( Код (Text): tst x20, #7 ≠ 0), ядро вызывает BSOD: Код (Text): KeBugCheckEx( BugCheckCode = 0x1F6, // INVALID_CALLBACK P1 = 0, // Reserved P2 = SpinLockAddress, // Неaligned адрес P3 = 8, // Требуемое выравнивание P4 = 0 // Reserved ); BugCheck 0x1F6 в контексте ExGenRandom указывает на повреждение глобальной переменной спинлока — это может произойти при pool corruption или kernel-mode exploit. 10. Сводные таблицы Ключевые адреса функций: ФункцияАдресОписаниеnt!ExGenRandom0xFFFFF801'B4582828Основной PRNG ядра (LFG)nt!RtlRandom0xFFFFF801'B4B1BD90Обёртка ExGenRandom(1) → & 0x7FFFFFFFnt!RtlRandomEx0xFFFFF801'B450FBF0Идентична RtlRandomnt!BCryptGenRandom0xFFFFF801'B4760B40Криптографический PRNG через CNGnt!SmCrGenRandom0xFFFFF801'B476A2C0Tail-call → BCryptGenRandomnt!KfRaiseIrql0xFFFFF801'B4406730Повышение IRQLnt!KfLowerIrql0xFFFFF801'B44065F0Понижение IRQLnt!KiAcquireSpinLockInstrumented0xFFFFF801'B446D420Instrumented spinlock acquirent!KiReleaseSpinLockInstrumented0xFFFFF801'B4709668Instrumented spinlock releasent!KxWaitForSpinLockAndAcquire0xFFFFF801'B446D5E8Spinlock wait-loopnt!RtlpSeedGlfsr0xFFFFF801'B4827470GLFSR seed из performance counternt!KeBugCheckEx0xFFFFF801'B445D770BSOD handlernt!ExGetExtensionTable0xFFFFF801'B457F580CNG extension table loadernt!KscpCfgCheckUserCallTargetEs0xFFFFF801'B4CF4040CFG call target validationnt!ExReleaseRundownProtection0xFFFFF801'B4579210Rundown protection releasent!SymCryptModSetRandom0xFFFFF801'B466E8C0SymCrypt modular randomnt!SymCryptFdefModSetRandomGeneric0xFFFFF801'B467A440Generic FDEF randomГлобальные переменные PRNG: ПеременнаяАдресРазмерНазначениеPRNG Table (Gen #0)WheapSelBuffer+0xAA00xE4 байтаState array + индексы для генератора 0PRNG Table (Gen #1)WheapSelBuffer+0xB840xE4 байтаState array + индексы для генератора 1SpinLockWheapSelBuffer+0xC688 байтГлобальный спинлок PRNGPendingReseedWheapSelBuffer+0xC704 байтаСчётчик отложенных reseedAuxiliarySeedWheapSelBuffer+0xC744 байтаXOR-маска результатаExtra Seed PoolWheapSelBuffer+0x760~0x340 байтПул seed-значений для reseed 11. Сравнение PRNG функций Windows КритерийExGenRandomRtlRandom/RtlRandomExBCryptGenRandomАлгоритмLFG (Lagged Fibonacci)ExGenRandom(1)CNG CSPRNG (AES-CTR DRBG)Криптографическая стойкостьНЕТНЕТДАРежимKernel-mode onlyKernel-mode onlyKernel + User modeРазмер результата32 бита (DWORD)31 бит (& 0x7FFFFFFF)Произвольный (буфер)СинхронизацияSpinLock + IRQL↑Наследует от ExGenRandomRundown ProtectionПроизводительностьВысокая (~20 инструкций)Высокая (~15 инструкций)Ниже (CNG overhead)XOR маскаExpRNGAuxiliarySeedНаследует + & 0x7FFFFFFFN/A (CNG provider)CFG защитаPACIBSP/AUTIBSPPACIBSP/AUTIBSPPAC + KscpCfgCheckUserCallTargetEsReseed поддержкаДа (PendingReseedCounter)НаследуетДа (CNG internal)ПрименениеASLR, hash рандомизация, pool tag randomizationСовместимость с legacy APIГенерация ключей, токенов, nonce 12. Практические выводы ExGenRandom — НЕ криптографический PRNG. Алгоритм LFG (Lagged Fibonacci Generator) с lag=55 предсказуем при достаточном количестве наблюдений. Для криптографии использовать BCryptGenRandom. Два генератора = два контекста. Генератор #0 (w0=0) используется ядром для внутренних нужд (ASLR, рандомизация pool tag и т.д.). Генератор #1 (w0=1) — через RtlRandom/RtlRandomEx — для legacy API. RtlRandom == RtlRandomEx. Полностью идентичные функции. Ex-суффикс не даёт никакого улучшения качества случайности — это просто второй экземпляр того же кода. 31-битный результат RtlRandom. Маска Код (Text): & 0x7FFFFFFF гарантированно делает результат положительным signed LONG — это ограничение совместимости с legacy кодом. SpinLock + DISPATCH_LEVEL = полный запрет вытеснения на текущном процессоре. Это гарантирует атомарность операции с PRNG state, но создаёт latency для всех процессоров, ожидающих спинлок. XOR с AuxiliarySeed добавляет дополнительный уровень энтропии поверх LFG. Этот seed обновляется периодически из Extra Seed Pool. Reseed через PendingReseedCounter позволяет отложенно «подмешивать» новые seed-значения в генератор #1 при каждом следующем вызове, если счётчик отличен от нуля. Seed из аппаратного счётчика (PMCCNTR_EL0). RtlpSeedGlfsr извлекает энтропию из performance counter — это 32 цикла XOR, по одному на каждый бит. PAC (Pointer Authentication) на всех функциях. PACIBSP/AUTIBSP защищают от ROP-атак на return address. KeBugCheckEx при pool corruption. Если спинлок (WheapSelBuffer+0xC68) повреждён и его адрес не выровнен на 8 байт — BSOD 0x1F6. BCryptGenRandom использует CFG (Control Flow Guard) через KscpCfgCheckUserCallTargetEs — это предотвращает атаки на indirect call к CNG provider. Нет Nt/Zw API для ExGenRandom. Функция доступна только в kernel-mode. User-mode получает доступ к PRNG только через BCryptGenRandom или RtlRandom (через ntdll syscall). Один спинлок на оба генератора. Это означает, что вызов ExGenRandom(0) и ExGenRandom(1) на разных процессорах будут конкурировать за один и тот же спинлок — это потенциальное bottleneck на высоконагруженных системах. Сводка адресов (Build 26100 ARM64): Код (Text): nt!ExGenRandom 0xFFFFF801'B4582828 nt!RtlRandom 0xFFFFF801'B4B1BD90 nt!RtlRandomEx 0xFFFFF801'B450FBF0 nt!BCryptGenRandom 0xFFFFF801'B4760B40 nt!SmCrGenRandom 0xFFFFF801'B476A2C0 nt!KfRaiseIrql 0xFFFFF801'B4406730 nt!KfLowerIrql 0xFFFFF801'B44065F0 nt!KiAcquireSpinLockInstrumented 0xFFFFF801'B446D420 nt!KiReleaseSpinLockInstrumented 0xFFFFF801'B4709668 nt!KxWaitForSpinLockAndAcquire 0xFFFFF801'B446D5E8 nt!RtlpSeedGlfsr 0xFFFFF801'B4827470 nt!KeBugCheckEx 0xFFFFF801'B445D770 nt!ExGetExtensionTable 0xFFFFF801'B457F580 nt!KscpCfgCheckUserCallTargetEs 0xFFFFF801'B4CF4040 nt!ExReleaseRundownProtection 0xFFFFF801'B4579210 nt!SymCryptModSetRandom 0xFFFFF801'B466E8C0 nt!SymCryptFdefModSetRandomGeneric 0xFFFFF801'B467A440
Раз уж разговор про криптографию, вашему ии нужно было упоменуть и других провайдеров BCrypt кроме дефолтного "RNG" - это "FIPS186DSARNG" и "DUALECRNG". Оба они являются примитивами для крипто-алгоритмов цифровых ключей DSA/ECDSA, а последний берёт полином от эллиптической кривой "Elliptic Curve". Функцией BCryptEnumAlgorithms() можно перечислить всех поддерживаемых системой крипто-провайдеров - она возвращает длину ключей, шаг их инкремента, а так-же размеры блоков для симметричных (не потоковых) алгоритмов типа AES\DES\RC2. Код (ASM): format pe console entry start include 'win32ax.inc' include 'equates\bcrypt.inc' ;//------------ .data algCount dd 0 operation dd 1 ; начинаем с "BCRYPT_CIPHER_OPERATION = 1" algHndl dd 0 pLen dd 0 objLen dd 0 pcbResult dd 0 align 16 algList BCRYPT_ALGORITHM_IDENTIFIER keyLen BCRYPT_KEY_LENGTHS_STRUCT buff db 0 ;//------------ .code start: invoke SetConsoleTitle,<'*** CNG Enum algo ***',0> cinvoke printf,<10,' Enum CNG Algorithm',\ 10,' ***************************',\ 10,' Symmetric cipher....: ',0> invoke BCryptEnumAlgorithms,[operation],algCount,algList,0 call GetAlgoName shl [operation],1 cinvoke printf,<10,' Hash algorithm......: ',0> invoke BCryptEnumAlgorithms,[operation],algCount,algList,0 call GetAlgoName shl [operation],1 cinvoke printf,<10,' Asymmetric cipher...: ',0> invoke BCryptEnumAlgorithms,[operation],algCount,algList,0 call GetAlgoName shl [operation],1 cinvoke printf,<10,' Secret agreement....: ',0> invoke BCryptEnumAlgorithms,[operation],algCount,algList,0 call GetAlgoName shl [operation],1 cinvoke printf,<10,' Signature...........: ',0> invoke BCryptEnumAlgorithms,[operation],algCount,algList,0 call GetAlgoName shl [operation],1 cinvoke printf,<10,' Random generator....: ',0> invoke BCryptEnumAlgorithms,[operation],algCount,algList,0 call GetAlgoName ;// ********************************************* cinvoke printf,<10,10,10,\ 10,' Signature algo info',\ ;<---- детали алго подписей 10,' ***************************',0> ; invoke BCryptEnumAlgorithms,BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION,algCount,algList,0 ; invoke BCryptEnumAlgorithms,BCRYPT_CIPHER_OPERATION,algCount,algList,0 invoke BCryptEnumAlgorithms,BCRYPT_SIGNATURE_OPERATION,algCount,algList,0 call GetAlgoProperty @exit: cinvoke _getch cinvoke exit,0 ;------------ ;//----- ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ ------------// proc GetAlgoName ;<---- выводит массив имён алгоритмов mov ecx,[algCount] mov esi,[algList.pszName] mov esi,[esi] @@: push esi ecx cinvoke printf,<'%ls, ',0>,esi pop ecx esi @01: lodsw cmp ax,0 jne @01 loop @b ret endp ;//------------ proc GetAlgoProperty ;<---- запрашивает свойства по имени mov ecx,[algCount] mov esi,[algList.pszName] mov esi,[esi] @cycle: push esi ecx esi cinvoke printf,<10,10,' %ls *********',0>,esi ; Открыть алгоритм по имени (указатель на имя лежит в ESI) pop esi invoke BCryptOpenAlgorithmProvider,algHndl,esi,0,0 ; Размер блока шифрования invoke BCryptGetProperty,[algHndl],BCRYPT_BLOCK_LENGTH,pLen,4,pcbResult,0 cinvoke printf,<10,' Block size...: %d byte',0>,[pLen] ; Диапазон размеров ключей (и их шаг) invoke BCryptGetProperty,[algHndl],BCRYPT_KEY_LENGTHS,keyLen,12,pcbResult,0 mov eax,[keyLen.dwMinLen] mov ebx,[keyLen.dwMaxLen] mov ecx,[keyLen.dwIncrement] cinvoke printf,<10,' Key length...: %03d...%03d bit, increment = %d bit',0>,\ eax,ebx,ecx ; Размер временного буфера под данные invoke BCryptGetProperty,[algHndl],BCRYPT_OBJECT_LENGTH,objLen,4,pcbResult,0 cinvoke printf,<10,' Temp object..: %d byte',0>,[objLen] pop ecx esi @@: lodsw cmp ax,0 jne @b dec ecx jnz @f jmp @stop @@: jmp @cycle @stop: ret endp ;------------ section '.idata' import data readable library kernel32,'kernel32.dll',msvcrt,'msvcrt.dll',bcrypt,'bcrypt.dll' include 'api\kernel32.inc' include 'api\msvcrt.inc' include 'api\bcrypt.inc'
этот форум примущественно направлен на архитектуру x86,я не знаю уместны ли тут публикации на тему архитектуры ARM