- Вы можете получить ошибку «Из памяти» из-за ограничения кучи рабочего стола
- Симптомы
- Причина
- Разрешение
- Исправлена ли эта проблема
- Открыть много файловых баз (локально, одновременно)
- Инжект кода средствами CSRSS: Сказ о том, как Windows 7 помогает современным хакерам старыми средствами
- Содержание статьи
- Введение
- LPC, или Local Procedure Calls
- CsrClientCallServer – вот где собака порылась!
- Что дальше?
Вы можете получить ошибку «Из памяти» из-за ограничения кучи рабочего стола
В этой статье помогают устранить ошибку «Из памяти», которая возникает при открываемом многих окнах приложений в Windows.
Оригинальная версия продукта: Windows 7 Пакет обновления 1, Windows Server 2012 R2
Исходный номер КБ: 947246
Симптомы
После открытия многих окон приложений в Windows вы не сможете открыть дополнительные окна. Иногда может открываться окно, но оно не будет содержать ожидаемые компоненты. Кроме того, вы получаете сообщение об ошибке, которое напоминает:
Причина
Эта проблема возникает из-за ограничения кучи рабочего стола. Когда вы закрываете некоторые окна, а затем пытаетесь открыть другие окна, эти окна могут открываться. Однако этот метод не влияет на ограничение кучи рабочего стола.
Разрешение
В этот раздел, описание метода или задачи включены действия, содержащие указания по изменению параметров реестра. Однако неправильное изменение параметров реестра может привести к возникновению серьезных проблем. Поэтому следует в точности выполнять приведенные инструкции. Для дополнительной защиты создайте резервную копию реестра, прежде чем редактировать его. Так вы сможете восстановить реестр, если возникнет проблема. Дополнительные сведения о том, как создать и восстановить реестр, см. в этой информации, как создать и восстановить реестр в Windows.
Чтобы устранить эту проблему, измените размер кучи рабочего стола, следуя следующим шагам:
Нажмите кнопку Начните, введите regedit в поле Начните поиск, а затем выберите regedit.exe в списке Программ.
Если вам предложен пароль администратора или подтверждение, введите пароль или нажмите кнопку Продолжить.
Найдите и выберите HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems подки реестра.
Щелкните правой кнопкой мыши запись Windows, а затем выберите Изменение.
В разделе Значение данных диалоговое окно Изменить строку, найдите запись, а затем увеличить второе значение и третье значение для SharedSection этой записи.
- Второе значение записи реестра — размер кучи рабочего стола для каждого рабочего стола, связанного SharedSection с интерактивной оконной станцией. Куча требуется для каждого рабочего стола, созданного в интерактивной оконной станции (WinSta0). Значение задается в килобайтах (КБ).
- Третье значение — размер кучи рабочего стола для каждого рабочего стола, связанного SharedSection с неинактивной оконной станцией. Значение задается в килобайтах (КБ).
- Мы не рекомендуем задать для второго значения значение более 20480 SharedSection КБ.
По умолчанию запись реестра Windows содержит следующие данные в x86-версии Windows 7 Пакет обновления 1.
%SystemRoot%\system32\csrss.exe
ObjectDirectory=\Windows
SharedSection=1024, 12288 512
Windows=On
SubSystemType=Windows
ServerDll=basesrv,1
ServerDll=winsrv:UserServerDllInitialization,3
ServerDll=winsrv:ConServerDllInitialization,2
ProfileControl=Off
MaxRequestThreads=16
Windows 7 Пакет обновления 1 (64 бита) / Windows Server 2008 R2, R2 2012 (64 бита)
Распределение памяти динамически в более поздних операционных системах. Для выделения памяти нет ограничений. Однако, если вы выделяете слишком много памяти на кучи рабочего стола, может возникнуть отрицательная производительность. Поэтому мы не рекомендуем устанавливать значение более 20480.
Размер кучи рабочего стола не зависит от физической оперативной памяти на компьютере. Нельзя повысить производительность, добавив физическую оперативную память.
Исправлена ли эта проблема
Проверьте, устранена ли проблема. Если проблема не устранена, обратитесь в службу поддержки.
Открыть много файловых баз (локально, одновременно)
Ибо на 5-6 уже начинает либо сразу падать после загрузки, либо зависать со 100% загрузкой проца. Лечится только снятием процесса.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems]
«Debug»=hex(2):00,00
«Kmode»=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,\
00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,77,00,\
69,00,6e,00,33,00,32,00,6b,00,2e,00,73,00,79,00,73,00,00,00
«Optional»=hex(7):50,00,6f,00,73,00,69,00,78,00,00,00,00,00
«Posix»=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,\
00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,70,00,\
73,00,78,00,73,00,73,00,2e,00,65,00,78,00,65,00,00,00
«Required»=hex(7):44,00,65,00,62,00,75,00,67,00,00,00,57,00,69,00,6e,00,64,00,\
6f,00,77,00,73,00,00,00,00,00
«Windows»=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,\
74,00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,63,\
00,73,00,72,00,73,00,73,00,2e,00,65,00,78,00,65,00,20,00,4f,00,62,00,6a,00,\
65,00,63,00,74,00,44,00,69,00,72,00,65,00,63,00,74,00,6f,00,72,00,79,00,3d,\
00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,53,00,68,00,61,00,\
72,00,65,00,64,00,53,00,65,00,63,00,74,00,69,00,6f,00,6e,00,3d,00,35,00,31,\
00,32,00,30,00,2c,00,36,00,34,00,30,00,30,00,2c,00,35,00,31,00,32,00,30,00,\
20,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,3d,00,4f,00,6e,00,20,00,53,\
00,75,00,62,00,53,00,79,00,73,00,74,00,65,00,6d,00,54,00,79,00,70,00,65,00,\
3d,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,53,00,65,00,72,00,76,\
00,65,00,72,00,44,00,6c,00,6c,00,3d,00,62,00,61,00,73,00,65,00,73,00,72,00,\
76,00,2c,00,31,00,20,00,53,00,65,00,72,00,76,00,65,00,72,00,44,00,6c,00,6c,\
00,3d,00,77,00,69,00,6e,00,73,00,72,00,76,00,3a,00,55,00,73,00,65,00,72,00,\
53,00,65,00,72,00,76,00,65,00,72,00,44,00,6c,00,6c,00,49,00,6e,00,69,00,74,\
00,69,00,61,00,6c,00,69,00,7a,00,61,00,74,00,69,00,6f,00,6e,00,2c,00,33,00,\
20,00,53,00,65,00,72,00,76,00,65,00,72,00,44,00,6c,00,6c,00,3d,00,77,00,69,\
00,6e,00,73,00,72,00,76,00,3a,00,43,00,6f,00,6e,00,53,00,65,00,72,00,76,00,\
65,00,72,00,44,00,6c,00,6c,00,49,00,6e,00,69,00,74,00,69,00,61,00,6c,00,69,\
00,7a,00,61,00,74,00,69,00,6f,00,6e,00,2c,00,32,00,20,00,50,00,72,00,6f,00,\
66,00,69,00,6c,00,65,00,43,00,6f,00,6e,00,74,00,72,00,6f,00,6c,00,3d,00,4f,\
00,66,00,66,00,20,00,4d,00,61,00,78,00,52,00,65,00,71,00,75,00,65,00,73,00,\
74,00,54,00,68,00,72,00,65,00,61,00,64,00,73,00,3d,00,31,00,36,00,00,00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems\CSRSS]
«CsrSrvSharedSectionBase»=dword:7f6f0000
Инжект кода средствами CSRSS: Сказ о том, как Windows 7 помогает современным хакерам старыми средствами
Содержание статьи
Существует такая аксиома: чем больше недокументирована какая-нибудь часть Windows, тем больше разработчики Microsoft не хотят, чтобы ее использовали разработчики программного обеспечения. Причина как всегда банальна – недокументированные возможности позволяют вытворять с системой жуткие вещи!
Тем не менее в среде программистов бытует мнение, что, несмотря на полную недокументированность подсистемы ввода-вывода CSRSS, ничего интересного она из себя не представляет, как с точки зрения прикладного кодинга, так и с точки зрения разработки малвари. Да, приходится признать, что даже вирусам и руткитам она неинтересна. За исключением, пожалуй, знаменитого червя Nimda. Хотя, постой, еще существует способ детекта скрытых процессов возможностями CSRSS… В общем, CRRSS не так прост и бесполезен, как кажется некоторым несознательным гражданам. Положим его на наш операционный стол и узнаем, чем же он может быть полезен настоящему хакеру.
Введение
Подсистема CSRSS – client/server run-time subsystem (клиент-серверная подсистема) – это часть исполнительной подсистемы Windows, которая отвечает за консольные приложения, создание/удаление потоков и за 16-битную виртуальную среду MS-DOS. Это мы знаем, но, к сожалению, на этом вся документированность CSRSS заканчивается.
Наверное, это один из самых загадочных кирпичиков Windows. И это не только загадочный, но и чувствительный кирпичик, так как процесс CSRSS.EXE – единственный, которому присваивается эпитет «критичный», и вмешательство в его нормальное исполнение грозит крахом всей системы. Упомяну такую деталь – на весь (!) зоопарк BSOD’ов, который только может сгенерировать ядро Windows, только два багчека: 0x0000004C (FATAL_UNHANDLED_HARD_ERROR) и 0xC000021A (STATUS_SYSTEM_PROCESS_TERMINATED) происходят при крахе его юзермодных процессов – это процессы winlogon.exe и csrss.exe. Без этих двух процессов Windows существовать не может. И самое печальное – во втором случае, если будет установлено, что причиной «синего экрана» стал отказ csrss.exe (как правило, в результате действия малвари), то это будет фатальный случай, приводящий к переустановке системы (или ее аварийному восстановлению).
CSRSS берет свои настройки не из реестра, как может показаться на первый взгляд, а из командной строки, которая выглядит примерно так:
%SystemRoot%system32csrss.exe ObjectDirectory=Windows SharedSection=1024,3072,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16
В общем, на мой взгляд, основная задача CSRSS все-таки заключается в своеобразном контроле над созданием и уничтожением процессов и потоков. А раз так, то нельзя недооценивать те возможности, которые в нем скрыты.
LPC, или Local Procedure Calls
Перед тем, как углубиться в дебри CsrApi, давай посмотрим в сторону механизма LPC, созданного в Windows для реализации межпроцессного взаимодействия. Почему именно на него? Все дело в том, что CSRSS реализует свой собственный протокол поверх LPC. LPC часто называют Local InterProcess Communication. Я, к сожалению, не знаю, как на самом деле расшифровывается LPC, но раз на MSDN-блогах LPC зовут Local Procedure Calls, значит, так тому и быть.
Как уже было сказано выше, LPC – это недокументированный механизм Windows, предназначенный для взаимодействия между процессами и потоками, основанный на приеме/передаче пакетов. Это взаимодействие может происходить как целиком в ядре, так и между юзермодными компонентами.
Суть LPC предельно проста – коммуникация между участниками взаимодействия (клиентом и сервером) осуществляется путем передачи блоков данных (так называемых сообщений LPC) . Клиентом может быть поток или процесс, и исполняться они могут на разных ring-уровнях, как в ядре (r0), так и простым пользовательским приложением (r3).
Основывается он на двух вещах – портах и внутренних LPC-структурах, таких как PORT_MESSAGE. Логически LPC состоит из двух действий – коннекта к определенному порту (NtCreatePort, NtConnectPort, NtListenPort и т.д.) и передаче данных (NtRequestWaitReplyPort и др.).
CsrClientCallServer – вот где собака порылась!
Среди всех используемых CsrApi-функций наиболее часто можно встретить CsrClientCallServer. Перекрестные ссылки на нее можно увидеть в таких функциях, как kernel32!CreateProcess, kernel32!AllocConsole, kernel32!FreeConsole, user32!EndTask и десятках других. Если мы взглянем на нее под микроскопом IDA, то увидим, что каждый раз, когда вызывается CsrClientCallServer, в стек заталкивается какое-то уникальное число, меняющееся от функции к функции:
.text:77E96D55 PUSH 4
.text:77E96D57 PUSH 20225h //
Это загадочное число – не что иное, как индекс указателя специальной функции, определенной в библиотеках, используемых подсистемой CSRSS. То есть специальная процедура, называемая CsrApiRequestThread, исполняется в контексте отдельного потока в csrss.exe, ответственного за прием запросов от пользовательской подсистемы. Она обрабатывает его через соответствующие диспетчерские таблицы CSRSS и возвращает результат.
Что интересного может дать использование функций CsrApi в интересах программиста? Много чего, например, можно организовать прямой распил консоли, так как подсистема CSRSS напрямую отвечает за консоль в Windows.
int main(int argc, char* argv[])
<
NTSTATUS Status;
CSR_API_MSG m;
CONSOLE_TITLE_MSG * consoleTitleMes = &m.u.ConsoleTitle;
CSR_CAPTURE_HEADER * сaptureBuffer;
consoleTitleMes->ConsoleHandle=GetConsoleHandle();
consoleTitleMes->TitleLen=260;
consoleTitleMes->Unicode=0;
CaptureBuffer=(CSR_CAPTURE_HEADER *)CsrAllocateCaptureBuffer(1, consoleTitleMes->TitleLen);
CsrCaptureMessageBuffer(CaptureBuffer, NULL, consoleTitleMes->TitleLen, (PVOID *)&consoleTitleMes->Title);
К сожалению, рамки статьи не позволяют описать все аспекты использования CsrApi-функций в повседневной жизни программиста, поэтому сейчас мы перейдем непосредственно к теме сегодняшней статьи.
Что дальше?
Итак, что же мы нашли при распиле CSRSS? В смысле, с хакерской точки зрения. Ни много, ни мало – инжект кода в Windows 7. Как ты знаешь, разработчики Windows 7 сильно потрудились над безопасностью процессов в системе – теперь просто так выполнить CreateRemoteThread в чужой процесс не удастся. Да, приходится признать, что дяденьки из Microsoft постарались на славу, отрубив мегакулхацкерам любимый способ инжекта кода. При попытке вызова CreateRemoteThread с хэндлом процесса другого пользователя, мы обламываемся, и функция возвращает нам NULL с кодом ошибки ERROR_NOT_ENOUGH_MEMORY.
Но ведь и про старуху бывает порнуха :).
Конечно, существуют в природе способы инжекта с использованием RtlCreateUserThread (подробнее об этом можно прочитать здесь), но мы не будем его рассматривать; желающие могут поэкспериментировать сами. Не зря мы сегодня завели разговор про CSRSS, ведь именно с помощью ее возможностей можно очень даже неплохо вернуть утраченный status quo и получить возможность инжекта кода в чужие процессы. Концепция нашего PoC проста.
Дело в том, что успешность вызова CreateRemoteThread зависит от системной функции CsrClientCallServer, которая фактически обрабатывает этот запрос. Она следит за выполнением, но сама удаленный поток не создает. Вызов CreateRemoteThread сводится к системной функции NtCreateThreadEx, которая создаст поток с флагом CREATE_SUSPENDED, однако дальнейшее развитие ситуации будет зависеть от успешности вызова функции подсистемы CSRSS – CsrClientCallServer. Ничего интересного в голову не приходит? 🙂 Все, что нам нужно – это сделать так, чтобы при создании удаленного потока CsrClientCallServer всегда возвращала успешное значение. И будет тебе счастье.
Смотрим на дизассемблированную kernelbase.dll (это аналог kernel32.dll в Windows 7, если ты не знал):
kernelbase.dll
.text:7597BD24 6A 0C PUSH 0C
.text:7597BD26 68 01000100 PUSH 10001
.text:7597BD2B 53 PUSH EBX
.text:7597BD2C 8D85 F0FDFFFF LEA EAX, DWORD PTR SS:[EBP-210]
.text:7597BD32 50 PUSH EAX
.text:7597BD33 FF15 00129775 CALL NEAR DWORD PTR DS:[ ] ; ntdll.CsrClientCallServer
.text:7597BD39 8B85 10FEFFFF MOV EAX, DWORD PTR SS:[EBP-1F0]
.text:7597BD3F 8985 E8FDFFFF MOV DWORD PTR SS:[EBP-218], EAX
.text:7597BD45 399D E8FDFFFF CMP DWORD PTR SS:[EBP-218], EBX
.text:7597BD4B 0F8C 13D80100 JL KERNELBA.75999564
Нам нужно найти в памяти kernelbase.dll, просканировать таблицу импорта, найти адрес импортируемой функции CsrClientCallServer и подменить его новым указателем на заранее подготовленную функцию CsrClientCallServer, которая всегда будет возвращать «успех». Сделать это легко. Смотрим:
ULONG NewCsrClientCallServer(PVOID Arg1, PVOID Arg2, ULONG Arg3, ULONG Arg4)
<
*( DWORD *)(( BYTE *)Arg1 + 0x20 ) = 0;
return 0;
>
.
DWORD ImportAddress, OriginalCsrClientCallServer, OldProtect;
ImportAddress = GetImportAddressFromIat(GetModuleHandle(«kernelbase.dll»), «CsrClientCallServer»);
VirtualProtect(( VOID ) ImportAddress, sizeof(DWORD), PAGE_EXECUTE_READWRITE, &OldProtect);
OriginalCsrClientCallServer = *(DWORD)ImportAddress;
(DWORD)ImportAddress=(DWORD)NewCsrClientCallServer;
.
И все! Раз, раз, раз – и… мы в дамках! Теперь наша новая функция CsrClientCallServer будет возвращать «success», что, собственно, и нужно для успешного запуска CreateRemoteThread. Кстати, такой подход довольно оригинален: вместо того, чтобы искать способы выполнения своего кода, писать (или покупать) 0day-эксплойты, повышающие права, искать новые уязвимости в системе и т.д., иногда бывает просто нужно переписать один байт. Самое главное – знать, где 🙂
В заключение только добавлю, что для успешного выполнения такого кода нужны дебаг-привилегии, которые подрубаются примерно таким образом:
unsigned long GetDebugPrivileges()
<
TOKEN_PRIVILEGES tokenPrvlgs;
tokenPrvlgs.PrivilegeCount = 1;
tokenPrvlgs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tokenPrvlgs, 0, NULL, NULL))
<
return error;
>
CloseHandle( hToken );
return success;
>
Вот и все, что я хотел донести до тебя. Не так страшна Windows 7, как ее раскрашивают. Код, который делает все вышеизложенное, как всегда, ищи на диске. Удачного компилирования, и да пребудет с тобой Сила!
P.S. Осторожнее в экспериментах с CSRSS! А то систему жалко :).
На компакт-диске ты найдешь реализацию описанных в статье приемов на С.
Не ленись читать MSDN – несмотря на бытующее в определенных кругах пренебрежительное отношение к данному сайту, чтение его статей позволяет устранить до 99% ошибок, возникающих при использовании WinAPI.