пятница, 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'а.

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