воскресенье, 21 июля 2019 г.

Обход Patch-Guard

Забавную штуку я обнаружил сегодня. То, что мы с Hex'ом придумали больше 7ми лет назад внезапно всплыло в виде библиотеки на гитхаб: https://github.com/everdox/InfinityHook

Если кратко, то через подмену указателя в структуре, которую не контролирует Patch-Guard можно перехватывать довольно много всего в системе(начиная с vista). В моем случае технология использовалась в одном продукте, которому уже несколько лет. И перехватывались именно сисколы(как и в InfinityHook). И хотя в либе используется "Circular Kernel Context Logger", а у меня в коде "NT Kernel Logger" совпадение крайне забавное. Дойти до подобной идеи, как мне кажется, не тривиально. В коде библиотеки есть несколько грубых ошибок, которые будут приводить к потере сисколов, и только это позволяет мне думать, что человек дошел до идеи сам, а не получил эту информацию от бывших сотрудников, которые имели доступ к этой либе в svn на моей работе :)

среда, 15 мая 2019 г.

Транзакции в windows. Часть 1.

Что такое транзакция?


Транзакция это группа операций, которая удовлетворяет следующим свойствам:

* атомарность(atomic),
* согласованность (consistent),
* изолированность(isolated)
* долговечность(durable) - ACID.

Под атомарностью подразумевается то, что все операции в рамках транзакции либо выполнятся успешно, либо не выполнятся вообще и откатятся(rollback) в исходное состояние.

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

Под изолированностью понимается то, что к примеру, процесс обновления системы не виден для человека, который ей пользуется в данный момент.

Под долговечностью понимается сохранность данных при каком-либо сбое. Применительно к windows это может означать внезапную остановку системы или отключение питания системы. Данные транзакции при этом будут сохранены.

Как принцип ACID реализуется в windows ?


ACID реализуется через механизм транзакций в windows(начиная с vista), поддерживается два типа транзакционных операций: реестр(registry) и файловые операции на NTFS томе.

Создание транзации => выполнение действий в рамках транзакции => подтверждение изменений ( commit ) или откат изменений ( rollback ) обеспечивает атомарность и согласованность действий в рамках транзакции.

Изолированность заложена в сам процесс транзаций, к примеру, созданный ключ реестра в рамках транзации не виден для обычных реестровых api, он виден только для транзакционных api работы с ключем.

Долговечность достигается за счет компонента, под названием Common Log File System (CLFS). Это высокопроизводительная, обще-целевая подсистема логирования, которая к тому же устойчива к сбоям системы.  

Как транзакции используются в windows ?

 

Самый наглядный пример - это атомарное обновление набора файлов и ключей реестра, этим занимаются разные инсталляторы, windows installer в частности.

На примере работы обновления windows попробуем получить имена создаваемых ключей реестра и создаваемых файлов при коммите транзакции. 

Для этого, нужно немного разобрать внутренности функции коммита, это будет сделано в другой статье. Пока же, достаточно упомянуть точку, где происходит непосредственно запись. Это делается в функции NTSTATUS CmpTransMgrCommitUoW( CM_KCB_UOW *unitOfWork, PLARGE_INTEGER currentTime ), внутри которой есть switch ( unitOfWork->ActionType ).

Нас интересует unitOfWork->ActionType == UoWAddThisKey, который приводит нас к функции NTSTATUS CmpCommitAddKeyUoW( CM_KCB_UOW *uow, PLARGE_INTEGER lastWriteTime ).

Из структуры CM_KCB_UOW можно получить имя создаваемого ключа:

typedef struct _CM_KCB_UOW
{
...
     CM_KEY_CONTROL_BLOCK *KeyControlBlock;
...
} CM_KCB_UOW;

typedef struct _CM_KEY_CONTROL_BLOCK
{
    ...
    PCM_NAME_CONTROL_BLOCK NameBlock;
    ...
} CM_KEY_CONTROL_BLOCK;

typedef struct _CM_NAME_CONTROL_BLOCK
{
...
    union
    {
        CM_NAME_HASH NameHash;
        struct
        {
            ULONG   ConvKey;
            struct _CM_KEY_HASH *NextHash;
            USHORT  NameLength;   
            WCHAR   Name[1] ;      // The actual string value
        };
    };
} CM_NAME_CONTROL_BLOCK;

Имея эту информацию, можно получить список создаваемых ключей при обновлении(ставим брекпойнт и запускаем любое обновление windows):

bp CmpCommitAddKeyUoW "da /c 64 poi(poi(esi+18)+4*7)+e;.echo '---CmpCommitAddKeyUoW---'; g"

Вывод:
a0874016  "PACKAGE_19_FOR_KB935509~31BF3856AD364E35~X86~~6.0.1.9.H@......NtFs....h._.`...."
'---CmpCommitAddKeyUoW---------'
9694cd4e  "X86_MICROSOFT-WINDOWS-B..AGER-PCAT.RESOURCES_31BF3856AD364E35_0.0.0.0_SV-SE_2877C192B6493CCD*"
'---CmpCommitAddKeyUoW---------'
a0a0ba6e  "PACKAGE_20_FOR_KB935509~31BF3856AD364E35~X86~~6.0.1.9.........CMN.."
'---CmpCommitAddKeyUoW---------'
a0b5f28e  "X86_MICROSOFT-WINDOWS-B..AGER-PCAT.RESOURCES_31BF3856AD364E35_0.0.0.0_TR-TR_D1850BD9A5053EBE0"
'---CmpCommitAddKeyUoW---------'
a05c96fe  "PACKAGE_21_FOR_KB935509~31BF3856AD364E35~X86~~6.0.1.9.0.\.....SeTd.."
'---CmpCommitAddKeyUoW---------'
a064d986  "X86_MICROSOFT-WINDOWS-B..AGER-PCAT.RESOURCES_31BF3856AD364E35_0.0.0.0_ZH-CN_A2E229D7553D10DD0"
'---CmpCommitAddKeyUoW---------'
a0791426  "PACKAGE_22_FOR_KB935509~31BF3856AD364E35~X86~~6.0.1.9.X.y.....Ntfo"
'---CmpCommitAddKeyUoW---------'
a058a3a6  "X86_MICROSOFT-WINDOWS-B..AGER-PCAT.RESOURCES_31BF3856AD364E35_0.0.0.0_ZH-HK_A18D22655618836D0.0.X..."
'---CmpCommitAddKeyUoW---------'
9e773b16  "PACKAGE_23_FOR_KB935509~31BF3856AD364E35~X86~~6.0.1.9.H;w.....FMfn..."
'---CmpCommitAddKeyUoW---------'
a0d26696  "X86_MICROSOFT-WINDOWS-B..AGER-PCAT.RESOURCES_31BF3856AD364E35_0.0.0.0_ZH-TW_A6DE672D52ADED4D0.0.0"
'---CmpCommitAddKeyUoW---------'
9eef0f46  "PACKAGE_24_FOR_KB935509~31BF3856AD364E35~X86~~6.0.1.9.x.......FSim."
'---CmpCommitAddKeyUoW---------'
96e7f5e6  "X86_MICROSOFT-WINDOWS-SERVICINGSTACK-MSG_31BF3856AD364E35_0.0.0.0_NONE_62795FA07331A3BC442....IoNm"
'---CmpCommitAddKeyUoW---------'

Примерно также можно получить и значения:

a0c19858  "PendingXmlIdentifier"
'---CmpCommitSetValueKeyUoW----'
a0c198d0  "AdvancedInstallersNeedResolving"
'---CmpCommitSetValueKeyUoW----'
a20f12b8  "InstallClient"
'---CmpCommitSetValueKeyUoW----'
a20f1310  "InstallName"
'---CmpCommitSetValueKeyUoW----'
a20f1358  "InstallLocation"
'---CmpCommitSetValueKeyUoW----'
a20f1430  "CurrentState"
'---CmpCommitSetValueKeyUoW----'
a20f1470  "Visibility"
'---CmpCommitSetValueKeyUoW----'

Для файлов интересна функция:

Ntfs!TxfGetTransactionFromFileObject:
8519e67e 8bff            mov     edi,edi
8519e680 55              push    ebp
8519e681 8bec            mov     ebp,esp
8519e683 56              push    esi
8519e684 ff7508          push    dword ptr [ebp+8]
8519e687 33f6            xor     esi,esi
8519e689 e8fd3ff7ff      call    Ntfs!IoGetTransactionParameterBlock
8519e68e 85c0            test    eax,eax

Прототип не известен, но сразу на входе есть вызов IoGetTransactionParameterBlock( PFILE_OBJECT FileObject ), которая есть в msdn
В аргументах идет нужный нам FILE_OBJECT.

Ставим брекпойнт, чтобы получить список создаваемых и открываемых файлов при коммите транзакции:

bp 8519e689 "!object poi(esp);.echo '---File---'; g"

Получаем:

Object: 831da480  Type: (82b93a80) File
    ObjectHeader: 831da468 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \windows\winsxs\msil_microsoft.web.management_31bf3856ad364e35_6.0.6000.16386_none_c30fe7d58014a975\ {HarddiskVolume1}
'---File---'
Object: 83301c88  Type: (82b93a80) File
    ObjectHeader: 83301c70 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \Windows\winsxs {HarddiskVolume1}
'---File---'
Object: 831da480  Type: (82b93a80) File
    ObjectHeader: 831da468 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \windows\winsxs\msil_microsoft_vsavb_b03f5f7f11d50a3a_6.0.6000.16386_none_6728c2d6cd97e7f4\ {HarddiskVolume1}
'---File---'
Object: 83301c88  Type: (82b93a80) File
    ObjectHeader: 83301c70 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \Windows\winsxs {HarddiskVolume1}
'---File---'
Object: 831da480  Type: (82b93a80) File
    ObjectHeader: 831da468 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \windows\winsxs\msil_miguicontrols.resources_31bf3856ad364e35_6.0.6000.16386_ru-ru_0c31b4feb872e0ff\ {HarddiskVolume1}
'---File---'
Object: 83301c88  Type: (82b93a80) File
    ObjectHeader: 83301c70 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \Windows\winsxs {HarddiskVolume1}
'---File---'
Object: 831da480  Type: (82b93a80) File
    ObjectHeader: 831da468 (old version)
    HandleCount: 0  PointerCount: 1
    Directory Object: 00000000  Name: \windows\winsxs\msil_miguicontrols_31bf3856ad364e35_6.0.6000.16386_none_ac1216923fb00239\ {HarddiskVolume1}

Поддержка транзакций в ядре windows

 

Для поддержки транзакций в ядре существуют четыре объекта ядра:

Transaction ( KTRANSACTION )
Transaction Manager ( KTM )
Resource Manager ( KRESOURCEMANAGER )
Enlistment ( KENLISTMENT )

Жизненный цикл этих объектов, как собственно и любых других в windows состоит из инициализации, создания, работы с объектами и их удаления.

Инициализация

 

Инициализация(заполнение OpenProcedure/CloseProcedure/DeleteProcedure и остальных данных ) и создание(ObCreateObjectTypeEx) этих объектов происходит на этапе инициализации системы:

Phase1Initialization => Phase1InitializationDiscard => TmInitSystem =>
=> TmpTransactionManagerInitialization / TmpTransactionInitialization / TmpResourceManagerInitialization / TmpEnlistmentInitialization
=> инициализация и создания соответствующих объектов.

Пример инициализации KTRANSACTION:

BOOLEAN TmpTransactionInitialization()
{
    OBJECT_TYPE_INITIALIZER     ObjectTypeInitializer;
    UNICODE_STRING                 DestinationString;
    NTSTATUS                     status;

    RtlInitUnicodeString( &DestinationString, L"TmTx" );
   
    TmpTransactionTypeName.Buffer = NULL;
   
    status = RtlDuplicateUnicodeString( 0, &DestinationString, &TmpTransactionTypeName );

    if ( !NT_SUCCESS(status) )   
        return FALSE;
   
    RtlZeroMemory( &ObjectTypeInitializer, sizeof(OBJECT_TYPE_INITIALIZER) );
   
    ObjectTypeInitializer.Length = sizeof(OBJECT_TYPE_INITIALIZER);
    ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
    ObjectTypeInitializer.GenericMapping.GenericRead = TmpTransactionMapping[0];
    ObjectTypeInitializer.GenericMapping.GenericWrite = TmpTransactionMapping[1];
    ObjectTypeInitializer.GenericMapping.GenericExecute = TmpTransactionMapping[2];
    ObjectTypeInitializer.GenericMapping.GenericAll = TmpTransactionMapping[3];
    ObjectTypeInitializer.PoolType = NonPagedPool;
    ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KTRANSACTION);
    ObjectTypeInitializer.ValidAccessMask = TRANSACTION_ALL_ACCESS | TRANSACTION_RIGHT_RESERVED1;
    ObjectTypeInitializer.CloseProcedure = TmpCloseTransaction;
    ObjectTypeInitializer.DeleteProcedure = TmpDeleteTransaction;
   
    status = ObCreateObjectTypeEx( &TmpTransactionTypeName, &ObjectTypeInitializer, 0, &TmTransactionObjectType );

    if ( !NT_SUCCESS(status) )
        return FALSE;
     
    return TRUE;
}

Полностью функция инициализации выглядит так:

BOOLEAN TmInitSystem()
{
    if ( !TmpTransactionManagerInitialization() )
        return FALSE;

    if ( !TmpTransactionInitialization() )
        return FALSE;       

    if ( !TmpResourceManagerInitialization() )
        return FALSE;

    if ( !TmpEnlistmentInitialization() )
        return FALSE;

    ExInitializePagedLookasideList(&TmpLogWriteLookasideList, 0, 0, 0, 0x214, 'lLmT', 0);
    KeInitializeMutex(&TmpAllProtocolsListMutex, 0);
    InitializeListHead(&TmpAllProtocolsList);
    TmpAllProtocolsListCount = 0;
    KeInitializeMutex(&TmpAllPropReqsListMutex, 0);
    InitializeListHead(&TmpAllPropReqsList);
    KeInitializeMutex(&TmpAllCRMListMutex, 0);
    InitializeListHead(&TmpAllCRMList);
    TmpAllCRMListCount = 0;

    TmpNamespaceInitialize( FIELD_OFFSET( KTM, NamespaceLink ), &TmpTmNamespace, FIELD_OFFSET( KTM, TmIdentity ) );
    TmpNamespaceInitialize( FIELD_OFFSET( KTRANSACTION, GlobalNamespaceLink ), &TmpTransactionsNamespace, FIELD_OFFSET( KTRANSACTION, UOW ) );
   
    KeInitializeEvent( &TmpTransactionFreezeCompleteEvent, NotificationEvent, FALSE );
    KeInitializeEvent( &TmpTransactionThawEvent, NotificationEvent, FALSE );
    KeInitializeMutex( &TmpFreezeMutex, 0 );
    KeInitializeEvent( &TmpTransactionFreezeCancelEvent, NotificationEvent, FALSE );
    KeInitializeTimer( &TmpTransactionThawTimer );
    KeInitializeDpc( &TmpTransactionThawDpc, TmpTransactionThawDpcRoutine, NULL );

    if ( !TmpInitializeKtmRmSecurityDescriptor() )
        return FALSE;

    return TRUE;
}

Где хранятся транзакции? Логично было бы предположить, что как и процессы/потоки они хранятся в связанных списках, но нет.
Транзакции хранятся в KTMOBJECT_NAMESPACE. В основе этой структуры лежит AVL дерево. Функции для работы с данной структурой:

TmpNamespaceLock
TmpNamespaceUnlock
TmpNamespaceInitialize
TmpNamespaceEnumerate
TmpNamespaceEnumerateObject
TmpNamespaceForEach
TmpNamespaceLookup
TmpNamespaceReplace
TmpNamespaceInsert
TmpNamespaceRemove
TmpNamespaceRename
TmpNamespaceCompareGuids
TmpNamespaceAllocateEntry
TmpNamespaceFreeEntry

В TmInitSystem вызывается инициализация глобальных переменных TmpTmNamespace / TmpTransactionsNamespace для хранения менеджеров транзакций и самих транзакций. Кроме того, в KRESOURCEMANAGER / KTM есть локальные namespace'ы. Подробнее про namespace'ы я расскажу в следующей статье.


Создание объектов

 

Создание объектов как и любых других в windows реализуется через NtCreate* функции:

NtCreateTransaction
NtCreateEnlistment
NtCreateResourceManager
NtCreateTransactionManager


Общая реализация также стандартна:

1) try + ProbeForWrite/ProbeForRead
2) проверка аргументов (флаги, длина юникодных строк и так далее)
3) создание объекта через ObCreateObject
4) инициализация созданного объекта (TmInitializeResourceManager/TmpInitializeEnlistment/TmInitializeTransaction/TmInitializeTransactionManager)

Работа с объектами

 

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

NtCommitTransaction
NtRollbackTransaction
NtCommitEnlistment
NtRollbackEnlistment
NtRecoverEnlistment
NtSetInformationEnlistment
NtQueryInformationEnlistment
NtSetInformationTransaction
NtCreateKeyTransacted
...


И так далее, подробнее про функции и механизм commit'a / rollback'a будет рассказано в отдельной статье.

Удаление объектов

 

При удалении вышеупомянутых 4х объектов происходит все тоже самое, что и должно происходить с объектами. При закрытии хендла на объект вызывается CloseProcedure. Когда счетчик ссылок на объект становится равен нулю - вызывается DeleteProcedure. Установка этих функций для каждого объекта происходит на этапе инициализации системы в функции TmInitSystem ( см. выше, раздел Инициализация ).

Рассмотрим на примере объекта транзакции. Скажем, в доке https://docs.microsoft.com/en-us/windows/desktop/ktm/transactions упоминается следующее поведение для транзакций:

"A transaction is an object that defines a logical unit of work. The transaction is alive as long as there is a handle referencing the transaction and it is considered active if the transaction has not yet been committed or rolled back. If a transaction is created and all handles to it have been closed before a commit or rollback occurs, the transaction will be rolled back."

Заглянем в код, CloseProcedure'ой для транзакции является функция TmpCloseTransaction.

VOID TmpCloseTransaction( IN PEPROCESS Process OPTIONAL, IN PVOID Object, IN ULONG GrantedAccess, IN ULONG_PTR ProcessHandleCount, IN ULONG_PTR SystemHandleCount)
{
    if ( SystemHandleCount == 1 || ProcessHandleCount == 1 )
    {
        TmRollbackTransaction( (KTRANSACTION *)Object, 0 );  
    }
}

Msdn не обманул.

Для DeleteProcedure'ы транзакции код выглядит как-то так:

VOID TmpDeleteTransaction( PVOID object )
{
    KTM             *tm = NULL;
    KTRANSACTION     *transaction = (KTRANSACTION*)object;

    SetFlag( transaction->Flags, KTRANSACTION_FLAG_DELETED );
      
    if ( transaction->State == KTransactionUninitialized )
        return;
   
    if ( transaction->TmNamespaceLink.Links.Parent )
    {
        tm = transaction->Tm;
          
        if ( tm )
        {
            TmpNamespaceRemove( (PVOID)transaction, &tm->Transactions, (PVOID)transaction->Tm );
            transaction->Tm = NULL;
        }
    }
      
    if ( transaction->GlobalNamespaceLink.Links.Parent )
        TmpNamespaceRemove( (PVOID)transaction, &TmpTransactionsNamespace, NULL );
      
    if ( transaction->Description.Buffer )
        RtlFreeUnicodeString( &transaction->Description );

    if ( transaction->TreeTx != transaction )
    {            
        ObfDereferenceObject( transaction->TreeTx );
        transaction->TreeTx = NULL;
    }  
}

вторник, 3 апреля 2018 г.

Каким алгоритмом жмется память в win10 при memory compression?

Таким странным вопросом я озадачился, просматривая видео про memory compression https://channel9.msdn.com/Blogs/Seth-Juarez/Memory-Compression-in-Windows-10-RTM.

Любопытство не порок, а вполне себе мотиватор. Поэтому я, недолго думая, взял отладчик в руки и углубился в ядро десятки.
Чтобы минимизировать потерю времени на анализ, я подумал над тем, о чем говорилось в видео.

Запись в store с сжатыми страничками идет асинхронно, то есть искать это можно, но это долго и не эффективно.
А вот получение странички из store идет синхронно при hard fault'е.

Соответственно, искать стоит не то место, где страничка сжимается, а место, где она разжимается в памяти.
Страничные фолты обрабатываются через прерывания, имя функции обработчика page fault'a я не помнил, но ничего мешает сдампить idt и вспомнить:

kd> !idt

Dumping IDT:

...
0c:    fffff8030be0b900 nt!KiStackFault
0d:    fffff8030be0ba40 nt!KiGeneralProtectionFault
0e:    fffff8030be0bb40 nt!KiPageFault <================ обработчик
10:    fffff8030be0c140 nt!KiFloatingErrorFault
11:    fffff8030be0c2c0 nt!KiAlignmentFault
...

После небольшого анализа в IDA получаем цепочку KiPageFault => MiIssueHardFault => MiIssueHardFaultIo => SmPageRead.

Префикс Sm в функции SmPageRead для меня был новым, вспоминая видео и слово store, можно предположить что префикс Sm означает store manager или что-то вроде того.

Смотрим тело функции и видим, что это просто обертка над store manager'ом, который и рулит всеми операциями связанными с имплементацией memory compression фичи:

__int64 SmPageRead(union _MM_STORE_KEY *a1, unsigned __int64 a2)
{
...
  SmKeyConvert(a1, (union _SM_PAGE_KEY *)&v7);
  return SMKM_STORE_MGR<SM_TRAITS>::SmPageRead(v3, &v7, v2, v5, v4);
}

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

В итоге поиск привел к ST_STORE<SM_TRAITS>::StDmSinglePageCopy, которая вызывала RtlDecompressBufferEx. То есть использовалась стандартная функция.

Первый аргумент у неё CompressionFormat, осталось лишь поставить брекпойнт и выяснить значение в отладчике.
Бряк сработал, на х64 первые 4 аргумента передаются в регистрах, смотрим чему равен rcx, он равен трем.

Это соответствует флагам COMPRESSION_FORMAT_DEFAULT | COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_STANDARD.
То есть алгоритм сжатия - LZ compression. На этом моё любопытство было удовлетворено.

вторник, 31 октября 2017 г.

Long road to launch boot driver

Понадобилось мне как-то запустить проприетарный boot драйвер на x86 windows.
Зарегистрировал его в System Bus Extender группе, запустил, и поймал startup repair на старте ОС.
Что же, BSOD'a нет - уже хорошо, подумал я тогда.

Проверил внешние зависимости - они были.
Вот только были они от системных драйверов(NDIS и прочие), никаких других сторонних компонентов не было.
Несмотря на то, что LoadOrder показывал, что тот же NDIS стартовал как boot, но с тагом N/A, то есть позже, все равно при ресолве импорта он был бы подгружен системой. Дело было в чем-то другом.

Я предположил, что в структуре файла.
Проверил CRC в хедере - все в норме, security directory пуста(подписи нет), разве что peverify выдал нечто вроде: Warning: cannot find address of 'ExReleaseFastMutex' in 'HAL.dll',
но это лишь варнинг, что сам верифай и подтвердил: Everything is OK. File is correct and can be loaded by system loader.
То есть, со структурой файла было всё в порядке.

Не придумав ничего другого, как пробежать момент загрузки в отладчике( бряк на IopLoadDriver ), я дошел в windbg до точки входа и ... драйвер загрузился. Это было как-то совсем странно, обычно бывает наоборот, под отладчиком программы не грузятся из-за антиотладки, а у меня случилось vise versa.

Далее я решил позагружать драйвер вручную, через kmdmanager, он драйвер не загрузил и выдал что-то типа error number not found. Wtf???
Пришлось загружать через свою тулзу. Наконец, я получил код ошибки:

ERROR_INVALID_IMAGE_HASH

Windows cannot verify the digital signature for this file.
A recent hardware or software change might have installed a file that is signed incorrectly or damaged, or that might be malicious software from an unknown source.


Тут я повторно сказал - WTF! Какие подписи на x86? Пробую запустить свой драйвер пустышку без всяких подписей - запускается.

После перелопаченного гугла решение паззла нашлось - https://msdn.microsoft.com/en-us/library/windows/hardware/dn653559(v=vs.85).aspx

В частности, в доке сказано, что:

• Boot-start drivers should contain an embedded signature.

Именно она и находилась в моем драйвере( но невалидная или поврежденная ).

Также, в доке есть другой полезный раздел:

How to Disable Signature Enforcement during Development

During the early stages of development, developers can disable enforcement in Windows so that driver signing is unnecessary.
The following options are available for developers to temporarily disable kernel-mode code-signing enforcement so that Windows Vista will load an unsigned driver.

•    Attaching a kernel debugger.
    Attaching an active kernel debugger to the target computer disables the enforcement of kernel-mode signatures in Windows Vista and allows the driver to load.
•    Using the F8 option.
    An F8 Advanced Boot Option introduced with Windows Vista — “Disable Driver Signature Enforcement”
    — is available to disable the kernel-signing enforcement only for the current boot session. This setting does not persist across boot sessions.

Собственно, поэтому при активном отладчике драйвер у меня замечательно загрузился. А после “Disable Driver Signature Enforcement” на старте, наконец запустился и исходный драйвер.

вторник, 25 апреля 2017 г.

Как заставить powershell загрузить CLR последней версии

Потребовалось мне получить некоторые имена функций из powershell, казалось бы качай pdb и дело в шляпе, ан нет.

Powershell это по сути фронтенд .NET Framework, со всеми вытекающими ввиде JIT и предварительно скомпилированными через Native Image Generator сборками. Сборки естественно генерятся под целевую платформу и логично, что символьные сервера микрософт никаким образом не могут содержать pdb для NI файлов.

Однако, в 4й версии фреймворка NGEN обзавелся параметром createpdb, и символы можно получить примерно так:

ngen.exe createpdb "C:\Windows\assembly\...\mscorlib.ni.dll" "C:\SymbolCache"

Соответственно, после получения символов задача должна быть решена. Но powershell подложил свинью, в виде загружаемого рантайма версии 2.0. А NGEN этой версии не имеет параметра для создания pdb. Далее пришлось раскапывать, как powershell узнает, какую версию рантайма грузить.

Недолгие поиски привели к реестру:

wmain => GetRegistryInfo(&NETversion) => RegQueryREG_SZValue(L"RuntimeVersion")

В реестре была видна версия 2.0:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine]
"ApplicationBase"="C:\\Windows\\System32\\WindowsPowerShell\\v1.0"
"PSCompatibleVersion"="1.0, 2.0"
"RuntimeVersion"="v2.0.50727"
"ConsoleHostAssemblyName"="Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil"
"ConsoleHostModuleName"="C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Microsoft.PowerShell.ConsoleHost.dll"
"PowerShellVersion"="2.0"

Ради любопытства я посмотрел как именно грузится рантайм:

wmain => LaunchManagedMonad => CorBindToRuntimeEx(NETversion)

Ну и все, что оставалось сделать, это внести последнюю версию рантайма(v4.0.30319), и после небольшой возни с доступами к защищенной ветке powershell'а дело было сделано.

Меняем и сравниваем:

Было:  LoadLibraryExW(C:\Windows\assembly\NativeImages_v2.0.50727_32\mscorlib\fe70d777535c215f4fe9f9def2b4c815\mscorlib.ni.dll)
Стало: LoadLibraryExW(C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\b7a12c4c0032847fcc6b9c710460456f\mscorlib.ni.dll)

Все имена функций прекрасно распознались:

Было:  0x792F123C (mscorlib.ni.dll)
Стало: System.AppDomain.SetupDomain(0x79A61212) (mscorlib.ni.dll)

четверг, 20 апреля 2017 г.

Another one EOP mitigation

В win10(10.0.15063) прикрыли еще одну возможность повышения привилегий, про первую я писал тут: http://kitrap08.blogspot.ru/2016/10/win10-securitydescriptor.html

Вторая заключается в обнулении не SecurityDescriptor'а, а флага Enabled в SEP_TOKEN_PRIVILEGES:

kd> dt nt!_TOKEN -r1
   +0x000 TokenSource      : _TOKEN_SOURCE
      +0x000 SourceName       : [8] Char
      +0x008 SourceIdentifier : _LUID
   +0x010 TokenId          : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x018 AuthenticationId : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x020 ParentTokenId    : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x028 ExpirationTime   : _LARGE_INTEGER
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
      +0x000 u                : <unnamed-tag>
      +0x000 QuadPart         : Int8B
   +0x030 TokenLock        : Ptr32 _ERESOURCE
      +0x000 SystemResourcesList : _LIST_ENTRY
      +0x008 OwnerTable       : Ptr32 _OWNER_ENTRY
      +0x00c ActiveCount      : Int2B
      +0x00e Flag             : Uint2B
      +0x010 SharedWaiters    : Ptr32 _KSEMAPHORE
      +0x014 ExclusiveWaiters : Ptr32 _KEVENT
      +0x018 OwnerEntry       : _OWNER_ENTRY
      +0x020 ActiveEntries    : Uint4B
      +0x024 ContentionCount  : Uint4B
      +0x028 NumberOfSharedWaiters : Uint4B
      +0x02c NumberOfExclusiveWaiters : Uint4B
      +0x030 Address          : Ptr32 Void
      +0x030 CreatorBackTraceIndex : Uint4B
      +0x034 SpinLock         : Uint4B
   +0x034 ModifiedId       : _LUID
      +0x000 LowPart          : Uint4B
      +0x004 HighPart         : Int4B
   +0x040 Privileges       : _SEP_TOKEN_PRIVILEGES
      +0x000 Present          : Uint8B
      +0x008 Enabled          : Uint8B <===================== обнуляем, получаем полный доступ к процессу(EOP)
      +0x010 EnabledByDefault : Uint8B

Более подробный анализ тут: http://anti-reversing.com/Downloads/Sec_Research/ntoskrnl_v10.0.15063_nt!_SEP_TOKEN_PRIVILEGES-Single_Write_EoP_Protect.pdf

вторник, 31 января 2017 г.

Можно ли скрыть ключ в реестре изменив 1 бит?

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

Как вообще скрывают ключи в реестре? Хуками, фильтрами и ... и похоже на этом моя фантазия исчерпалась. Но все это требует довольно много кода. Как это коррелирует с заголовком топика? Да никак.

Тем не менее, изменив 1 бит можно скрыть любой ключ реестра. Как это возможно?

Это возможно, если установить CM_KEY_CONTROL_BLOCK *keyCtrlBlock->TransKCBOwner не равным нулю, то есть достаточно установить 1 бит, чтобы сделать ключ реестра невидимым для regedit и системы в целом.

Как такое возможно? Все дело в транзакциях, именно так они работают.

NtOpenKey => CmpBuildHashStackAndLookupCache => CmpCacheLookup => CmRmIsKCBVisible => if (CM_TRANS *transOwner = keyCtrlBlock->TransKCBOwner ) != NULL ).

Собственно, в этом и кроется ответ, достаточно изменить один бит в keyCtrlBlock->TransKCBOwner и ключ реестра станет невидимым для любых существующих антивирусов и антируткитов.

Как это ловить? Да очень просто, перечисляем транзации, сравниваем их хендлы с keyCtrlBlock->TransKCBOwner, если не равно - руткит активность!

А можно ли обойти то, что описано выше? Естественно! Просто создаем валидную транзакцию, которая скрывает ключ реестра, но восстанавливает/удаляет ее когда нужно!

Что такое транзакции и как они работают я постараюсь описать в следующих постах.