вторник, 28 февраля 2012 г.

Баг PsSetCreateThreadNotifyRoutine на WinXp

Забавный баг существует на Windows XP, в нотификаторе на создание потока нельзя получить объект потока по приходящему в него tid'у.

А все потому, что:

NTSTATUS PspCreateThread(...)
{
...
   ExGetCallBackBlockRoutine - вызов колбека на XP
...
   ObInsertObject - а добавление объекта в таблицу хендлов идет уже после
...
}

На Windows Vista баг пофикшен:

NTSTATUS PspInsertThread(...)
{
...
     call    ObInsertObject - сначала объект добавляется в таблицу хендлов
...
     mov     [ebp+arg_14], offset _PspCreateProcessNotifyRoutine
     push    [ebp+arg_14]
     call    _ExReferenceCallBackBlock
...
     call    dword ptr [edi+4] - а уже потом идет вызов колбека
...
     call    _ExDereferenceCallBackBlock
...
}

четверг, 23 февраля 2012 г.

Branch Tracing в ядре

Оказывается, в ядре windows есть и поддержка Branch Tracing(начиная с висты).
Этой функциональностью пользуется driver verifier:

На этапе инициализации:

IoInitSystem(...)
{
...
    if ( ViVerifierEnabled )
        VfNotifyVerifierOfEvent(0); // initialize branch tracing
...
}

Phase1InitializationDiscard(...):
{
...
    push    5
    pop     eax
    call    VfNotifyVerifierOfEvent(x) // start branch tracing
...
}


VOID VfNotifyVerifierOfEvent( ULONG eventId )
{
    switch ( eventId )
    {
        case 0:                      
             VfInitializeBranchTracing();
        break;
 ...
        case 5:
             VfStartBranchTracing();
        break; 

    }
} 

Включение branch tracing:

VfStartBranchTracing
{
...
    push    0
    push    0C0h
    push    1D9h // IA32_DEBUGCTL
    WRMSR(x,x,x)
...
}

К сожалению, чаще всего драйвера тестируются на виртуалках, а там branch tracing не поддерживается, поэтому полноценно поиграться с ним не выйдет.

пятница, 27 января 2012 г.

Bound import

О bound import'e писали неоднократно, поэтому подробно останавливаться на нем не вижу смысла, напомню лишь что это, в общих чертах.

Bound import позволяет сэкономить время загрузки модуля, за счет кеширования адресов ф-ций в импорте.
То есть, системный загрузчик не делает получение адресов ф-ций из их имен, если выполнены некоторые условия для этого.

Условия следующие:

* Виртуальный адрес у директории bound import'a не должен быть равен нулю
* временные штампы в IMAGE_BOUND_IMPORT_DESCRIPTOR и в хедере импортируемого модуля совпадают
* Для dll: модуль не должен быть ребазирован (незнаю правда, насколько эта информация справедлива для систем с aslr)

Стандартные приложения windows используют bound import очень активно.

Мне стало интересно, много ли инструкций экономит эта фича.

Проверялось число инструкций для notepad.exe ( win xp sp3 ), с apc диспатчера, то есть с первых инструкций(в UM) после старта процесса и до entry point приложения.

Для notepad без bound import'a число инструкций = 2280899
Для notepad с bound import'ом  число инструкций = 2268677

Разница 12222, т.е. всего пол процента от общего числа инструкций ( до точки входа ).

Однако стоит помнить, что обход импорта это рекурсивная процедура:

LdrpWalkImportDescriptor => LdrpLoadImportModule => LdrpWalkImportDescriptor
LdrpWalkImportDescriptor => LdrLoadDll => LdrpWalkImportDescriptor

То есть прирост в производительности от использования bound import'a может быть заметным, если задействовано большое количество импортируемых модулей.

среда, 25 января 2012 г.

Когда postmortem analysis бесполезен

Чаще всего при BSOD'ах помогает анализ креш дампа, но бывают случаи, когда такой анализ бесполезен.

Такие ситуации могут быть, когда существует системный поток или воркитем, которые по каким-то причинам ( ошибки в проектировании )
продолжают работать даже тогда, когда драйвер выгружен, что приводит к ошибкам доступа к выгруженной памяти и соответственно к багчеку:

DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS (ce)
A driver unloaded without cancelling timers, DPCs, worker threads, etc.
The broken driver's name is displayed on the screen.
Arguments:
Arg1: f7e7f81a, memory referenced
Arg2: 00000008, value 0 = read operation, 1 = write operation
Arg3: f7e7f81a, If non-zero, the instruction address which referenced the bad memory
    address.
Arg4: 00000000, Mm internal code.

Debugging Details:
------------------

WRITE_ADDRESS:  f7e7f81a

FAULTING_IP:
Template+181a
f7e7f81a ??              ???
...
PROCESS_NAME:  System
...

STACK_TEXT: 
f7f5a84c 804f7bad 00000003 f7e7f81a 00000000 nt!RtlpBreakWithStatusInstruction
f7f5a898 804f879a 00000003 00000000 c07bf3f8 nt!KiBugCheckDebugBreak+0x19
f7f5ac78 804f8cc5 00000050 f7e7f81a 00000008 nt!KeBugCheck2+0x574
f7f5ac98 8051cc5f 00000050 f7e7f81a 00000008 nt!KeBugCheckEx+0x1b
f7f5acf8 8054052c 00000008 f7e7f81a 00000000 nt!MmAccessFault+0x8e7
f7f5acf8 f7e7f81a 00000008 f7e7f81a 00000000 nt!KiTrap0E+0xcc
WARNING: Frame IP not in any known module. Following frames may be wrong.
f7f5ad80 00000000 8162dd80 00000000 00000000 <Unloaded_Template.sys>+0x181a

В этом случае из дампа невозможно понять кто виноват, т.к. доступа к памяти с драйвером нет, она не валидна:

kd> !pte f7e7f81a
                    VA f7e7f81a
PDE at C0603DF8            PTE at C07BF3F8
contains 000000000101F163  contains 0000000000000000
pfn 101f      -G-DA--KWEV   not valid

Когда системный поток или воркитем один, то логика нас приводит к анализу именно его. Но что делать, когда их несколько?

Очевидно, что тут потребуется live анализ и выявление границ функций системных потоков на рабочем драйвере.

Смотрим в исходниках имя системного потока и имя следующей за ним функции, пусть к примеру это будет SystemThread1 и InitSystemThread1, тоже самое делаем для остальных потоков.

Далее в отладчике на живом запущенном драйвере находим границы ф-ции системных потоков:

kd> ? InitSystemThread1 - SystemThread1
Evaluate expression: 336 = 00000150 - размер ф-ции

Выводим листинг всей ф-ции:

kd> u f77fd6f0 f77fd6f0+150h

f77fd6f0 55              push    ebp
f77fd6f1 8bec            mov     ebp,esp
f77fd6f3 6aff            push    0FFFFFFFFh
f77fd6f5 68d81180f7      push    offset Template!__safe_se_handler_table+0x8 (f78011d8)
f77fd6fa 6808dd7ff7      push    offset Template!except_handler3 (f77fdd08)
f77fd6ff 64a100000000    mov     eax,dword ptr fs:[00000000h]
f77fd705 50              push    eax
...

Делаем тоже самое для остальных потоков, после чего добиваемся воспроизведения BSOD'а.

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

пятница, 30 декабря 2011 г.

Мысли вслух

Подумалось недавно, как много Microsoft делает для обеспечения безопасности своих ОС: куча защитных механизмов против эксплойтов( aslr/dep/safeseh/function pointer obfuscation/sehop/stack cookies и т.д. ), uac, встроенная в ядро система защиты от патчей ( patch guard ) призванная бороться в том числе и с руткитами, скоро появится безопасная загрузка через UEFI, призванная бороться с буткитами(в win8), цикл SDL, призванный делать код безопасным еще на этапе написания, тулзы повышающие безопасность устаревших ОС семейства windows (EMET),  средство удаления вредоносных программ и даже бесплатный антивирус (Microsoft Security Essentials).

Эффективны ли эти меры? Безусловно да, безопасность ОС со временем развивается и улучшается, для того, чтобы обойти все защитные механизмы, малварь должна быть все изощреннее и изощреннее, уровень малварописателей также обязан повышаться. Таким образом, несмотря на то, что практически каждый защитный механизм обходится, в целом, картина имеет положительную динамику. Порог вхождения для малварописателей также с течением времени повышается.

Придет ли момент, когда малварь в принципе исчезнет? Тут можно только гадать. Но в принципе, с течением времени могут появиться механизмы защиты, не обходимые принципиально(как пример, можно взять какой-нибудь патч гвард сидящий на уровне гипервизора, и контролирующий не только изменение кодосекций и системных структур, но и остальные лазейки), или требующие для обхода затрат, превосходящих прибыль малварописателей, т.е. экономически не выгодных.

Пока все выглядит как вечная война щита и меча, кончится ли она когда нибудь? Поживем, увидим.

p.s. Всех с Наступающим!

четверг, 15 декабря 2011 г.

Подарок для отладчико-писателей

Те, кто писал отладчики знают, что существуют определенные трудности с отрисовкой элементов GUI. Сложности были и с выводом в видеопамять на видеокартах, где эта видеопамять фрагментирована, и с копанием в недокументированных структурах драйвера дисплея. Ни о каких стандартах и речи не шло. Но все течет, все меняется, развивается как software, так и hardware. И с появлением UEFI появился и стандарт GOP (Graphic Output Protocol).

GOP дает возможность устанавливать видео режимы и писать из/в фреймбуфер графического контроллера, предоставляя следующие интерфейсы:

QueryMode Returns information for an available graphics mode that the graphics device and the set of active video output devices supports.
SetMode Set the video device into the specified mode and clears the visible portions of the output display to black.
Blt Software abstraction to draw on the video device’s frame buffer.

Так что с использованием GOP можно наваять практически любой GUI для того же отладчика уровня ядра, если конечно вы являетесь счастливым обладателем платы с UEFI( как я например :) ).

Пример того, как выглядит графика выведенная через GOP(собственно весь интерфейс UEFI выведен через него):

пятница, 9 декабря 2011 г.

BURNMEMORY

Есть любопытная опция для загрузчика ОС: http://msdn.microsoft.com/en-us/library/windows/hardware/ff556246%28v=vs.85%29.aspx

На этапе загрузки ядра она обрабатывается следующим образом: получается число страниц, нужное для представления указанного в опциях количества MB, затем эти страницы включаются в список BadPageList, напомню, списков всего 8:

typedef enum _MMLISTS
{
    ZeroedPageList,
    FreePageList,
    StandbyPageList,
    ModifiedPageList,
    ModifiedNoWritePageList,
    BadPageList,
    ActiveAndValid,
    TransitionPage
} MMLISTS;

Также, данная память не учитывается в MmNumberOfPhysicalPages, то есть невидима для системы.

Но вот практический смысл данной опции для меня как-то ускользает.
Кто-нибудь знает, зачем нужна опция BURNMEMORY ?