Доброго времени суток форумчане. Возник такой вопрос, каким образом перехватить ввод/вывод консольного приложения на уровне ядра. Суть в чем, есть консольная прога, которая выводит например отладочные выводы или еще что-то в консоль, каким образом этот вывод перехватить из драйвера? Как выловить что программа загружается я в курсе. Можно конечно решить проблему с инжектом длл в процесс, но что-то мне кажется это не особо по феншую. Пошерстил по форуму, толкового ничего не нашел (хз, может плохо искал). Подскажите плиз...
Console IO это пайп, но только не именнованный .. Остается вопрос как достать хендлы GetStdHandle Код (Text): .text:7D85BBC1 push ebp .text:7D85BBC2 mov ebp, esp .text:7D85BBC4 cmp [ebp+nStdHandle], 0FFFFFFF4h .text:7D85BBC8 push esi .text:7D85BBC9 jz short loc_7D85BC2C .text:7D85BBCB cmp [ebp+nStdHandle], 0FFFFFFF5h .text:7D85BBCF jz short loc_7D85BC06 .text:7D85BBD1 cmp [ebp+nStdHandle], 0FFFFFFF6h .text:7D85BBD5 jz short loc_7D85BBDC .text:7D85BBD7 or esi, 0FFFFFFFFh .text:7D85BBDA jmp short loc_7D85BC40 .text:7D85BBDC ; --------------------------------------------------------------------------- .text:7D85BBDC .text:7D85BBDC loc_7D85BBDC: ; CODE XREF: GetStdHandle(x)+16j .text:7D85BBDC mov eax, large fs:18h .text:7D85BBE2 mov eax, [eax+30h] .text:7D85BBE5 mov eax, [eax+10h] .text:7D85BBE8 test dword ptr [eax+68h], 200h .text:7D85BBEF jz short loc_7D85BBF5 .text:7D85BBF1 .text:7D85BBF1 loc_7D85BBF1: ; CODE XREF: GetStdHandle(x)+5Aj .text:7D85BBF1 xor esi, esi .text:7D85BBF3 jmp short loc_7D85BC4A .text:7D85BBF5 ; --------------------------------------------------------------------------- .text:7D85BBF5 .text:7D85BBF5 loc_7D85BBF5: ; CODE XREF: GetStdHandle(x)+30j .text:7D85BBF5 mov eax, large fs:18h .text:7D85BBFB mov eax, [eax+30h] .text:7D85BBFE mov eax, [eax+10h] .text:7D85BC01 mov esi, [eax+18h] .text:7D85BC04 jmp short loc_7D85BC3B .text:7D85BC06 ; --------------------------------------------------------------------------- .text:7D85BC06 .text:7D85BC06 loc_7D85BC06: ; CODE XREF: GetStdHandle(x)+10j .text:7D85BC06 mov eax, large fs:18h .text:7D85BC0C mov eax, [eax+30h] .text:7D85BC0F mov eax, [eax+10h] .text:7D85BC12 test dword ptr [eax+68h], 400h .text:7D85BC19 jnz short loc_7D85BBF1 .text:7D85BC1B mov eax, large fs:18h .text:7D85BC21 mov eax, [eax+30h] .text:7D85BC24 mov eax, [eax+10h] .text:7D85BC27 mov esi, [eax+1Ch] .text:7D85BC2A jmp short loc_7D85BC3B Код (Text): #define STD_INPUT_HANDLE ((DWORD)-10) #define STD_OUTPUT_HANDLE ((DWORD)-11) #define STD_ERROR_HANDLE ((DWORD)-12) Ну думаю теперь понятно ...
Угу, в принципе понятно, спасибо за пояснение, сейчас буду пробовать перехватить это все добро и перенаправлять на себя...
Или лыжи не едут или я, без пяти минут академик. Ну хендлы вроде как я в свое дровере достал. Только прикол в том что там ничего нет.... Мож я что-то не так делаю... Вот кодес как я достаю хендлы i/o Код (Text): ntStatus = ZwQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation, &pi, sizeof(PROCESS_BASIC_INFORMATION), &retLen); if (!NT_SUCCESS(ntStatus)) { DPRINT("ZwQueryInformationProcess was failed\n"); return ntStatus; } hStdin = pi.PebBaseAddress->ProcessParameters->StandardInput; hStdout = pi.PebBaseAddress->ProcessParameters->StandardOutput; Ну и смотрю в отладчике - все нормально, достаются хендлы... Только вот теперь каким образом мне вытянуть что именно печатается на консоли и что-то туда написать?
Ну прекрастно раз достал, теперь надо создать свои и подсунуть(пайпы соедененные с пайп сервером из драйвера) и когда по ним предут данные(которые вы подсунули), обработать их у себя и отправить дальше. (Потребуется делать СsrCall ) Что бы их обработать надо будет транслейт им сделать. Да уж ... получилось довольно таки сложно ... Можно конечно перехватить СsrCall, но если был редирект к файлам(или по пайпам) то соответсвенно вызова не будет, так что много предется перехватывать(СsrCall,Read,Write как минимум, ну и конечно смотреть что это за хендлы).
shchetinin Спасибо большое за консультации... Что-то возникли проблемы с созданием пайпа на уровне ядра(или лыжи не едут или я без пяти минут академик...), уже попытался использовать несколько методов (ядро оказывается не экспортирует эту ф-ю а с IoCreateFile вылазят проблемы, ну и пайпа не создается). Решил попробовать дернуть эту ф-ю через ssdt, но покамесь не знаю чем это все дело закончиться. Есть ли какой-то еще метод создание пайпы в kernel mode? Уже несколько ночей морочусь. Заранее спасибо за помощь.
Через IoCreateFile реализовываеш NtCreateNamedPipe. Кому давно кидал ипл: Код (Text): typedef struct _NAMED_PIPE_CREATE_PARAMETERS { ULONG NamedPipeType; ULONG ReadMode; ULONG CompletionMode; ULONG MaximumInstances; ULONG InboundQuota; ULONG OutboundQuota; LARGE_INTEGER DefaultTimeout; UCHAR TimeoutSpecified; } NAMED_PIPE_CREATE_PARAMETERS, *PNAMED_PIPE_CREATE_PARAMETERS; NTSTATUS NtCreateNamedPipeFile ( OUT PHANDLE FileHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN ULONG NamedPipeType, IN ULONG ReadMode, IN ULONG CompletionMode, IN ULONG MaximumInstances, IN ULONG InboundQuota, IN ULONG OutboundQuota, IN PLARGE_INTEGER DefaultTimeout OPTIONAL ) { NAMED_PIPE_CREATE_PARAMETERS NamedPipeParms; NTSTATUS Status; __try { if ( DefaultTimeout ) { NamedPipeParms.TimeoutSpecified = TRUE; NamedPipeParms.DefaultTimeout.QuadPart = DefaultTimeout->QuadPart; } else { NamedPipeParms.TimeoutSpecified = FALSE; } NamedPipeParms.NamedPipeType = NamedPipeType; NamedPipeParms.ReadMode = ReadMode; NamedPipeParms.CompletionMode = CompletionMode; NamedPipeParms.MaximumInstances = MaximumInstances; NamedPipeParms.InboundQuota = InboundQuota; NamedPipeParms.OutboundQuota = OutboundQuota; Status = IoCreateFile ( FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, NULL, 0, ShareAccess, CreateDisposition, CreateOptions, NULL, 0, CreateFileTypeNamedPipe, &NamedPipeParms, 0 ); return Status; } __except (EXCEPTION_EXECUTE_HANDLER) { KdPrint ((«NtCreateNamedPipeFile: Exception occured.\n»)); return STATUS_UNSUCCESSFUL; } } Ну и пример вызова: Код (Text): Status = NtCreateNamedPipeFile( &PipeHandle, // Out pipe handle SYNCHRONIZE | GENERIC_WRITE | GENERIC_READ, // Desired access mask to pipe instance &ObjectAttributes, // Attributes of the pipe instance &Iosb, // Status of the IO Block FILE_SHARE_WRITE | FILE_SHARE_READ, // Share access mask to pipe instance FILE_OPEN_IF, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE, FILE_PIPE_QUEUE_OPERATION, 255, //max instance 800, 800, &DefaultTimeOut ); Атрибуты для пайпа, очень важно так как не все приложения будут иметь большие права их нужно занулить Код (Text): Status = RtlCreateSecurityDescriptor ( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); Status = RtlSetDaclSecurityDescriptor ( &SecurityDescriptor, TRUE, NULL, FALSE ); InitializeObjectAttributes( &ObjectAttributes, &NamedPipeName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, &SecurityDescriptor );
2 shchetinin Большое спасибо за консультацию. Пробовал я такой метод. Ну даже еще раз решил уже тупо скопипастить с примерчика... Короче говоря постоянно IoCreateFile возвращает статус -1073741773. Уже блин не знаю что делать. По разному пробовал... Ну тему дернуть из SSDT что-то тоже макросы не помогают. Мож я что-то прощелкал? Вот как я юзаю это все дело: Код (Text): RtlInitUnicodeString(&usStdinName, L"\\??\\device\\NamedPipe\\PipeStdin"); UNICODE_STRING usStdoutName = RTL_CONSTANT_STRING(L"\\??\\pipe\\PipeStdout"); LARGE_INTEGER DefaultTimeOut = {0}; //for pipe SECURITY_DESCRIPTOR SecurityDescriptor; DriverObject->DriverUnload = UnloadDriver; status = RtlCreateSecurityDescriptor ( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); status = RtlSetDaclSecurityDescriptor ( &SecurityDescriptor, TRUE, NULL, FALSE ); InitializeObjectAttributes(&oaAttributes,&usStdinName,OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, &SecurityDescriptor); status = CreateNamedPipeFile(&hNewStdin, // Out pipe handle SYNCHRONIZE | GENERIC_WRITE | GENERIC_READ, // Desired access mask to pipe instance &oaAttributes, &iosbStatus, FILE_SHARE_READ | FILE_SHARE_WRITE , FILE_OPEN_IF, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE, FILE_PIPE_QUEUE_OPERATION, 255, //max instance 800, 800, &DefaultTimeOut); if (!NT_SUCCESS(status)) { DPRINT("Can not open pipe stdin\n"); return status; } Ну а сама ф-я просто скопипащена... Проверяю на ХР... Помогите плиз, уже хз сколько времени убил, кажется на вроде как простенькую задачу...
Error = STATUS_OBJECT_NAME_INVALID L"\\??\\device\\NamedPipe\\PipeStdin" не правильно Переделайте на L"\\??\\pipe\\PipeStdin" Должно работать.
В общем прикол в том что я так пробовал. По большому счету L"\\??\\pipe\\PipeStdin" это просто сслка на \\??\\device\\NamedPipe. В общем выдает ошибку STATUS_OBJECT_NAME_NOT_FOUND... Это я пытался делать из DriverEntry. Если попытаться создать именнованный пайп в процессе(когда я вытягиваю хендлы), то вылизит STATUS_ACCESS_VIOLATION...
По ходу где то с параметрами ошибся смотрите : http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtCreateNamedPipeFile.html Это какой пайп создаешь? Новый или пытаешся добратся до существующего?
Ну планирую создать новый и подсунуть дескриптор нового пайпа заместо существующего стандартного. Ну например StandardInput
red_mould Проверяйте входные параметры и этот хендел должен остатся в ядре, а вот к нему который должен присоединится уйтед в приложения.
shchetinin Еще раз большое спасибо за консультации и за помощь, разобрался я как создать пайп в ядре =). И уже нормально создается. Там проблемы были с параметрами...
Всем форумчанам доброго времени суток. У меня опять или лыжи не едут или сам без 5 минут академик. В общем проблема в следующем. Создаю пайп в DriverEntry Код (Text): UNICODE_STRING usStdinName; RtlInitUnicodeString(&usStdinName, L"\\??\\PIPE\\StdinPipe"); LARGE_INTEGER DefaultTimeOut; //for pipe SECURITY_DESCRIPTOR SecurityDescriptor = {SECURITY_DESCRIPTOR_REVISION,0,SE_DACL_PRESENT}; DriverObject->DriverUnload = UnloadDriver; BPOINT; KeInitializeEvent(&evKill, SynchronizationEvent, FALSE); KeInitializeEvent(&evGetHandle, SynchronizationEvent, FALSE); DefaultTimeOut.QuadPart=-1; InitializeObjectAttributes(&oaAttributes,&usStdinName, OBJ_CASE_INSENSITIVE|/*OBJ_INHERIT|*/OBJ_KERNEL_HANDLE, NULL, &SecurityDescriptor); ntStatus = NtCreateNamedPipeFile(&hNewStdin, // Out pipe handle FILE_READ_ATTRIBUTES|FILE_READ_DATA|/*FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA|*/FILE_CREATE_PIPE_INSTANCE, &oaAttributes, &iosbStatus, FILE_SHARE_READ/*|FILE_SHARE_WRITE*/, FILE_OPEN_IF, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE, FILE_PIPE_QUEUE_OPERATION, 255, 800,//PAGE_SIZE, 800,//PAGE_SIZE, &DefaultTimeOut); if (!NT_SUCCESS(ntStatus)) { DPRINT("Can not open pipe stdin\n"); return ntStatus; } ntStatus = ZwFsControlFile(hNewStdin, NULL, NULL, NULL, &iosbStatus, FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0); if (ntStatus == STATUS_PENDING) { if (!NT_SUCCESS(ZwWaitForSingleObject(hNewStdin, FALSE, NULL))) { DPRINT("ZwWaitForSingleObject was failed\n"); } } else if (!NT_SUCCESS(ntStatus)) { DPRINT("ZwFsControlFile was failed\n"); return ntStatus; } Все бы хорошо, хендел создается, возправщается STATUS_PENDING и когда начинаю ожидать пайп при помощи ZwWaitForSingleObject, начинает ждать и можно так проторчать немерянно времени. Ну ясен пень если не подписываться на прослушку пайпа тоже толку от этого не будет. Подскажите плиз, мож есть еще какой-то метод, бо уже перелопатил дофига доков, но там в основном вода или ring 3
Ну мысля такая, создать пайп, и дескриптор подсунуть в консольную приложуху. Ну и чтоб контролировать ввод и вывод на консоль. Дескрипторы процесса я вытягиваю из PEB этого процесса.