Таким образом, защита не сможет обнаружить изменений ни в файле, ни в памяти (при попытке определения имени текущего файла операционная система будет возвращать то имя файла, какое он имел на момент запуска, игнорируя факт его «онлайнового» переименования). Но это слишком громоздкий и навороченный алгоритм, к тому же активной защите ничего не стоит перехватить SetTimer и запретить установку таймера внутри «своего» процесса до завершения распаковки/передачи управления на OEP.
Забавно, но многие защиты забывают о функции SetWindowsHookEx, позволяющей внедрять свою DLL в адресное пространство чужого процесса. Впрочем, даже если бы они помнили о ней, осуществить корректный перехват весьма не просто.
Многие легальные приложения (например, мультимедийные клавиатуры или мыши с дополнительными кнопками по бокам) используют SetWindowsHookEx для расширения функциональности системы. Не существует никакого способа отличить «честное» приложение от дампера. Защита может распознать факт внедрения чужой DLL в адресное пространство охраняемого ее процесса, но откуда ей знать, что эта DLL делает?! Можно, конечно, просто выгрузить ее из памяти (или воспрепятствовать загрузке), но какому пользователю понравится, что легально приобретенная программа конфликтует с его крутой клавиатурой, мышью или другим устройством? Так что SetWindowsHookEx при всей своей незатейливости – довольно неплохой выбор для хакера!
Самый радикальный способ внедрения в чужое адресное пространство – это правка системных библиотек, таких как KERNEL32.DLL или USER32.DLL. Править можно как на диске, так и в памяти, однако в последнем случае защита может легко разоблачить факт вторжения простым сравнением системных библиотек с их образом.
Внедрившись в системную библиотеку, не забудьте скорректировать контрольную сумму в PE-заголовке, иначе NT откажется ее загружать. Сделать это можно как с помощью PE-TOOLS, так и утилитой rebuild.exe, входящей в состав SDK. Внедряться лучше всего в API-функции, вызываемые стартовым кодом оригинального приложения (GetVersion, GetModuleHandleA и т. д.), определяя «свой» процесс функцией GetCurrentProcessId или по содержимому файла (последнее – надежнее, т. к. GetCurrentProcessId может быть перехвачена защитой, которая очень сильно «удивится», если API-функция GetVersion неожиданно заинтересуется идентификатором текущего процесса). Во избежание побочных эффектов запускать такой дампер следует на «выделенной» операционной системе, специально предназначенной для варварских экспериментов и обычно работающей под виртуальной машиной типа BOCHS или VMWare.
Правильно спроектированная и должным образом реализованная защита должна препятствовать нелегальному использованию программы, но не имеет ни морального, ни юридического права мешать честным пользователям и уж тем более вторгаться в операционную систему, производя никем не санкционированные изменения. Последние версии протекторов Themida и Software Passport вплотную приближаются к rootkit. Еще немного и они превратятся в настоящие вирусы, создание которых преследуется по закону.
«Нечестные» защитные приемы
Нормальные упаковщики (UPX, PKLITE, PECOMPACT) сжимают исполняемый файл без потерь, и после завершения распаковки он возвращается к своему первоначальному виду, что делает процесс снятия дампа тривиальной задачей. Протекторы в стремлении усилить защиту зачастую идут на довольно рискованный шаг – они слегка «корежат» обрабатываемый файл с таким расчетом, чтобы он мог работать только под протектором, а после освобождения от него становится нежизнеспособным. Наиболее популярные способы подобной «нечестной» защиты рассмотрены ниже.
Кража байт с OEP. Самая простая и широко распространенная подлянка, используемая даже в таких безобидных протекторах как, например, ASProtect.
Суть ее заключается в том, что упаковщик «крадет» несколько инструкций из оригинальной точки входа, сохраняет их в потайном месте (возможно, в замаскированном или зашифрованном виде), а после завершения распаковки эмулирует выполнение краденых байт. Чаще всего для этой цели используется стек (тогда краденые байты обычно становятся операндами инструкций PUSH), реже – прямое воздействие на регистры и память (при этом краденые инструкции трансформируются в псевдокод и в явном виде нигде не сохраняются).
Суть в том, что в точке входа распакованного образа оригинальных байт уже не оказывается и снятый дамп становится неработоспособным. К нашему счастью, подавляющее большинство программ начинается со стартового кода, который является частью библиотеки времени исполнения (RTL), поставляемой вместе с компиляторами.
Используя оставшийся «хвост» стартового кода, мы легко отождествим компилятор и восстановим краденые байты из его библиотеки. Если же данного компилятора в нашем распоряжении не окажется, первые несколько байт стартового кода в 9 из 10 случаев вполне предсказуемы, и зачастую их удается восстановить самостоятельно (естественно, для этого необходимо иметь опыт работы с различными RTL). Кстати, IDA Pro распознает компилятор именно по первым байтам стартового кода, и, если они отсутствуют или искажены, механизм FLIRT работать не будет. Это значит, что мы останемся без имен библиотечных функций и процесс дизассемблирования займет намного больше времени.
Полиморфный мусор в OEP. Вместо того чтобы красть байты с OEP, некоторые протекторы предпочитают модифицировать стартовый код, разбавляя значимые инструкции бессмысленным полиморфным мусором. Это никак не влияет на работоспособность снятого дампа, но ослепляет «FLIRT», вынуждая нас либо вычищать полиморфный мусор, либо определять версию компилятора «на глазок», загружая сигнатуры вручную (IDA Pro это позволяет).
Переходники в таблице импорта к куче. Протектор Themida использует следующий прием, серьезно затрудняющий восстановление таблицы импорта. Непосредственные адреса API-функций заменяются переходниками на область памяти, выделенную VirtualAlloc (т.е. кучу), которая по умолчанию в дамп не попадает, поэтому восстанавливать импорт приходится вручную.
Это несложно, но утомительно – ищем вызовы API-функций, ведущие к куче (то, что это именно куча, а не что-то другое, можно определить по карте), дампим соответствующий регион памяти на диск, удаляем переходники, заменяя их действительными адресами, после чего запускаем Import Reconstructor или другую утилиту аналогичного назначения и. нет, это еще не все! Это только начало! Помимо создания переходников некоторые функции копируются протектором целиком! Подробнее об этом приеме можно прочитать в статье «Точки останова на win32 API и противодействие им» – раздел «Копирование API – функций целиком», которая находится на http://kpnc.opennet.ru/adt.zip.
Замена jx с последующей эмуляцией. При «отвязке» программ от протектора Armadillo самое сложное – это восстановление оригинального кода программы. Защита дизассемблирует обрабатываемый файл, находит в нем условные и безусловные переходы, записывает поверх них команду INT 03h, а сам переход сохраняется в своей внутренней таблице переходов.
Процесс-сервер перехватывает исключение, возбуждаемое инструкцией INT 03, «смотрит», откуда оно пришло, извлекает из таблицы соответствующий этому адресу переход и эмулирует его выполнение с помощью арифметических манипуляций с регистром флагов (то есть в явном виде переходы нигде не хранятся!). Вот три главных минуса такого решения: во-первых, нет никакой гарантии, что защита правильно дизассемблирует обрабатываемую программу и не спутает переход с другой командой; во-вторых, эмуляция требует времени, существенно снижая производительность и, наконец, втретьих, от взлома это все равно не спасает! Дизассемблировав эмулятор переходов (а дизассемблировать его несложно) и обнаружив таблицу переходов, хакер в считанные минуты напишет скрипт для IDA Pro или OllyDbg, удаляющий все INT 03 и восстанавливающий оригинальные переходы. Существует даже полуавтоматический взломщик Armadillo, написанный двумя богами распаковки – infern0 и dragon (http://www.wasm.ru/baixado.php?mode=tool http://www.wasm.ru/article.php? article=packers2 (вторая часть).
Снимаем защиту от записи на примере чипа GD25Q64 и программатора CH341A
Программатор CH341A Pro — Тестирование и снятие дампа
Источник: samag.ru
ProcDump версии 11.0
Скачать ProcDump(714 КБ)
Создано с помощью ZoomIt
Введение
ProcDump — это служебная программа командной строки, основной целью которой является мониторинг пиков ЦП в приложении и создание аварийных дампов во время пикового скачка, который администратор или разработчик может использовать для определения причины пикового скачка. ProcDump также включает мониторинг зависания окна (с использованием того же определения зависания окна, что и Windows и диспетчер задач), необработанное наблюдение за исключениями и может создавать дампы на основе значений счетчиков производительности системы. Он также может служить служебной программой дампа общего процесса, которую можно внедрить в другие скрипты.
Использование ProcDump
Использование записи:
procdump.exe [-mm] [-ma] [-mt] [-mp] [-mc ] [-md ] [-mk] [-n ] [-s ] [-c|-cl [-u]] [-m|-ml ] [-p|-pl ] [-h] [-e [1] [-g] [-b] [-ld] [-ud] [-ct] [-et]] [-l] [-t] [-f , . ] [-fx , . ] [-dc ] [-o] [-r [1..5] [-a]] [-at ] [-wer] [-64] < | | > [ | ]> | <-x [Argument, . ]> >
Установка использования:
procdump.exe -i [Dump_Folder] [-mm] [-ma] [-mt] [-mp] [-mc ] [-md ] [-mk] [-r] [-at ] [-k] [-wer]
Удаление использования.
procdump.exe -u
Типы дампа:
-Мм | Запись файла дампа Mini. (по умолчанию). — включает прямо или косвенно упоминаемую память (стеки и то, что они ссылаются). — включает все метаданные (процесс, поток, модуль, дескриптор, адресное пространство и т. д.). |
-Ма | Создайте полный файл дампа. — включает всю память (image, сопоставленная и частная). — включает все метаданные (процесс, поток, модуль, дескриптор, адресное пространство и т. д.). |
-Mt | Запись файла дампа «Triage». — включает память (стеки), на которые ссылается напрямую ссылка. — включает ограниченные метаданные (процесс, поток, модуль и дескриптор). — Попытка удаления конфиденциальной информации выполняется, но не гарантируется. |
-Mp | Запись файла дампа MiniPlus. — включает всю частную память, а также всю память для чтения и записи или сопоставленной памяти. — включает все метаданные (процесс, поток, модуль, дескриптор, адресное пространство и т. д.). — Чтобы свести к минимуму размер, исключается самая большая частная область памяти размером более 512 МБ. Область памяти определяется как сумма выделения памяти одинакового размера. Дамп является таким же подробным, как полный дамп, но 10%-75% от размера. — Примечание. Из-за ограничений отладки процессы CLR создаются как полные (-ma). |
-Mc | Запись файла дампа «Пользовательский». — включает память и метаданные, определенные указанной MINIDUMP_TYPE маской (шестнадцатеричной). |
-Md | Запись файла дампа Callback. — включает память, определенную подпрограммой MiniDumpWriteDump MiniDumpCallbackRoutine обратного вызова указанной библиотеки DLL. — включает все метаданные (процесс, поток, модуль, дескриптор, адресное пространство и т. д.). |
-Mk | Кроме того, запишите файл дампа Kernel. — включает стеки ядра потоков в процессе. — ОС не поддерживает дамп ядра ( -mk ) при использовании клона ( -r ). — При использовании нескольких размеров дампа создается дамп ядра для каждого размера дампа. |
Условия:
-a | Избегайте простоя. Требует использования -r . Если триггер приведет к длительной приостановке целевого объекта из-за превышения лимита одновременного дампа, триггер будет пропущен. |
-В | Избегайте простоя во время ожидания. Отмена сбора триггера через секунды N . |
-b | Рассматривайте точки останова отладки как исключения (в противном случае игнорируйте их). |
-c | Пороговое значение ЦП, превышающее создание дампа процесса. |
-Cl | Порог ЦП, ниже которого создается дамп процесса. |
-Dc | Добавьте указанную строку в созданный комментарий дампа. |
-e | Создание дампа, когда процесс сталкивается с необработанным исключением. Включите , 1 чтобы создать дамп при первом исключении. Добавьте -ld для создания дампа при загрузке библиотеки DLL (модуль) (применяется фильтрация). Добавьте -ud для создания дампа при выгрузке библиотеки DLL (модуль) (применяется фильтрация). Добавьте -ct для создания дампа при создании потока. Добавьте -et , чтобы создать дамп при выходе из потока. |
-f | Фильтрация (в том числе) по содержимому исключений, журналу отладки и имени файла при загрузке и выгрузке DLL. Поддерживаются подстановочные знаки (*). |
-Fx | Фильтрация (исключение) по содержимому исключений, журналу отладки и имени файла при загрузке или выгрузке DLL. Поддерживаются подстановочные знаки (*). |
-g | Запуск как собственного отладчика в управляемом процессе (без взаимодействия). |
-h | Создание дампа, если в процессе завислое окно (не отвечает на сообщения окна в течение по крайней мере 5 секунд). |
-k | Завершите процесс после клонирования ( -r ) или в конце сбора дампа. |
-l | Отображение журнала отладки процесса. |
-m | Пороговое значение фиксации памяти в МБ для создания дампа. |
-Мл | Активируется, когда фиксация памяти падает ниже указанного значения МБ. |
-n | Количество дампов для записи перед выходом. |
-o | Перезаписать существующий файл дампа. |
-p | Активируется, когда счетчик производительности превышает указанное пороговое значение. Некоторые счетчики и (или) имена экземпляров могут быть чувствительны к регистру. |
-Pl | Активируется, когда счетчик производительности опускается ниже указанного порогового значения. |
-r | Дамп с помощью клона. Ограничение количества одновременных операций является необязательным (по умолчанию 1, максимум 5). ОС не поддерживает дамп ядра ( -mk ) при использовании клона ( -r ). ВНИМАНИЕ! Высокое значение параллелизма может повлиять на производительность системы. — Windows 7: использует отражение. ОС не поддерживает -e . — Windows 8.0: использует отражение. ОС не поддерживает -e . — Windows 8.1+: использует PSS. Поддерживаются все типы триггеров. |
-s | Последовательные секунды перед записью дампа (по умолчанию — 10). |
-t | Создание дампа при завершении процесса. |
-u | Использование ЦП относительно одного ядра (используется с -c ). |
-v | ТОЛЬКО ОТЛАДКА: Подробные выходные данные. |
-w | Дождитесь запуска указанного процесса, если он не запущен. |
-Wer | Поставить в очередь (самый большой) дамп, чтобы отчеты об ошибках Windows. |
-x | Запустите указанное изображение с необязательными аргументами. Если это приложение магазина или пакет, ProcDump запустится при следующей активации (только). |
-Y | СКРЫТЫЕ: Активация приложения Store. |
-64 | По умолчанию ProcDump записывает 32-разрядный дамп 32-разрядного процесса при работе в 64-разрядной версии Windows. Этот параметр переопределяет создание 64-разрядного дампа. Используется только для отладки подсистемы WOW64. |
Лицензионное соглашение:
Используйте параметр командной -accepteula строки, чтобы автоматически принять условия лицензионного соглашения Sysinternals.
Автоматическое завершение.
Использование этого параметра или задание события с именем ProcDump- аналогично нажатию клавиш CTRL+C для корректного завершения ProcDump. Корректное завершение гарантирует возобновление процесса, если запись активна. Отмена применяется ко всем экземплярам ProcDump, которые отслеживают процесс.
Имени файла:
Имя файла дампа по умолчанию: PROCESSNAME_YYMMDD_HHMMSS.dmp
Поддерживаются следующие подстановки:
PROCESSNAME | Имя процесса |
ИД процесса | ИД процесса |
EXCEPTIONCODE | Код исключения |
ГГММДД | Год,месяц/день |
ЧЧММСС | Час в минуту в секунду |
Примеры
- Создайте мини-дамп процесса с именем «Блокнот» (может существовать только одно совпадение):
C:>procdump notepad
C:>procdump -ma 4572
Источник: learn.microsoft.com
Снимаем дамп объектов с памяти .Net приложения
Продолжаем тему интересного на .Net, от чего мир Java будет посмеиваться (хотя у них это также возможно сделать), а приверженцы С++ говорить: «чего они только не сделают чтобы не учить C++».
В данной заметке мы напишем по сути – простенькое ядрышко профилировщика памяти для платформы .Net, который будет снимать дамп с SOH кучи (а в перспективе и с LOH).
Для написания статьи нам понадобится код из статьи Получение указателя на объект .Net и Ручное клонирование потока (измерение размера объектов).
- Научиться итерировать кучу .Net
- Научиться находить начало кучи .Net
- Попробовать сытерировать все объекты чужого домена.
Ссылка на проект в GitHub: DotNetEx
Для начала, полный список выложенных на Хабре статей данного цикла
-
Найдем начало кучи в .Net
Как мы, наверное, знаем, в .Net существует два вида куч. Это куча для маленьких объектов и куча для больших объектов (> 85K). Они отличаются прежде всего организацией объектов внутри себя. Если в SOH объекты выделяются друг за другом, то в LOH все основано на связанных списках и таблице свободных промежутков между занятыми участками. Это то, что мы знаем. Но правда заключается в другом.
Во-первых SOH не может быть непрерывной ввиду возможности наткнуться на занятый участок виртуальной памяти при ее расширении. Второе – на самом деле то, что объекты выделяются непрерывно друг за другом – красивое допущение, поскольку есть pinned объекты, которые нельзя двигать, а значит при сжатии кучи возникнут пустые промежутки. А это значит что SOH также содержит таблицу пустот. И по итогу это должно означать конкретно для нас что: (а) в памяти может быть несколько зон виртуальной памяти, которые выделены под кучу, (б) вероятнее всего они выделены через VirtualAlloc, (в) объекты находятся не непрерывно, а значит наш алгоритм не может на это полагаться.
Для упрощения примера, давайте будем снимать дамп только с объектов своей кучи. Для того чтобы это сделать, мы возмем указатель на любой объект и при помощи WinApi функции VirtualQuery попробуем получить регион виртуальной памяти, которая была выделена под эту кучу
public static void GetManagedHeap(out IntPtr heapsOffset, out IntPtr lastHeapByte) < // получаем указатель на любой объект. Для этого просто выделим его var offset = EntityPtr.ToPointer(new object()); var memoryBasicInformation = new WinApi.MEMORY_BASIC_INFORMATION(); unsafe < WinApi.VirtualQuery(offset, ref memoryBasicInformation, (IntPtr)Marshal.SizeOf(memoryBasicInformation)); heapsOffset = (IntPtr)memoryBasicInformation.AllocationBase; lastHeapByte = (IntPtr)((long)offset + (long)memoryBasicInformation.RegionSize); >>
[StructLayout(LayoutKind.Explicit)] public unsafe struct EntityInfo < [FieldOffset(0)] public int SyncBlockIndex; [FieldOffset(4)] public MethodTableInfo *MethodTable; >[StructLayout(LayoutKind.Explicit)] public struct RefTypeInfo
Далее посмотрим, как выглядит MethodTableInfo:
[StructLayout(LayoutKind.Explicit)] public unsafe struct MethodTableInfo
Здесь — только часть всей информации с этой структуры. На самом деле она более обширная. Нам тут важно поле EEClass, которое ведет на структуру описания типа. Его я практически не изучал. Выглядит содержимое примерно так:
[StructLayout(LayoutKind.Explicit)] public unsafe struct ObjectTypeInfo
Поскольку для нас важно только поле MethodsTable, я только его и нашел. Остальное — пропустил. Зачем оно нам? Это — обратная ссылка на MethodsTable, который своим полем EEClass ссылается сюда.
Таким вот не хитрым образом мы нашли несколько не точный, однако прекрасно работающий метод детектирования .Net объекта. Выглядит он так:
private static unsafe bool IsCorrectMethodsTable(IntPtr mt) < if (mt == IntPtr.Zero) return false; if (PointsToAllocated(mt)) if (PointsToAllocated((IntPtr) ((MethodTableInfo*) mt)->EEClass)) if (PointsToAllocated((IntPtr) ((MethodTableInfo*) mt)->EEClass->MethodsTable)) return ((IntPtr) ((MethodTableInfo*) mt)->EEClass->MethodsTable == mt) || ((IntPtr) ((MethodTableInfo*) mt)->ModuleInfo == MscorlibModule); return false; > private static bool PointsToAllocated(IntPtr ptr)
Для каждого указателя проверяется, указывает ли он на выделенный участок памяти. Это первый барьер. Второй барьер — если у теоретического объекта первое поле указывает на нечто, что мы изначально интерпретируем как MethodsTable. Проверяем что поле EEClass указывает на выделенный участок памяти и интерпретируем этот указатель как указатель на структуру ObjectTypeInfo, после чего проверяем, равен ли указатель на MethodsTable нашему найденному указателю (есть ли обратная ссылка?) Если все норм, объект найден.
Осталось только пройтись по всем участкам памяти и попробовать распознать там объекты. Я не буду выкладывать эту простыню, поскольку там также есть код для регистрации найденного и вывода на экран. Скажу только что задача решена, ссылка вывода из программы — ниже.
Вывод нашей программы, дамп памяти
00606 : System.String 00583 : System.Object 00277 : System.RuntimeType 00072 : System.Array+SZArrayEnumerator 00046 : System.Char[] 00041 : System.Int32[] 00033 : System.String[] 00032 : System.Object[] 00030 : System.Version 00029 : System.Byte[] 00024 : System.Text.StringBuilder 00023 : System.Collections.Hashtable+bucket[] 00020 : System.Security.PermissionSet 00020 : System.Collections.Hashtable 00020 : System.Reflection.AssemblyName 00014 : System.Reflection.RuntimeAssembly 00014 : System.Security.Permissions.EnvironmentPermission 00013 : System.Collections.Hashtable+SyncHashtable 00012 : System.Globalization.CompareInfo 00012 : System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib, Ve rsion=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Securit y.Policy.EvidenceTypeDescriptor, mscorlib, Version=4.0.0.0, Culture=neutral, Pub licKeyToken=b77a5c561934e089]][] 00011 : System.Globalization.CultureInfo 00010 : System.Collections.ArrayList 00010 : System.Int32 00010 : System.Threading.ThreadStart 00010 : System.Internal.HandleCollector+HandleType 00010 : System.Internal.HandleCollector+HandleType 00009 : System.Security.Permissions.SecurityPermission 00009 : System.EventHandler 00009 : Microsoft.Win32.SafeHandles.SafeRegistryHandle 00009 : Microsoft.Win32.RegistryKey 00008 : System.Threading.Thread 00008 : Microsoft.Win32.SafeHandles.SafeWaitHandle 00008 : System.Security.Policy.EvidenceTypeDescriptor 00008 : System.Runtime.InteropServices.HandleRef 00008 : System.UInt16 00006 : System.Runtime.Remoting.Metadata.SoapTypeAttribute[] 00005 : System.Type[] 00005 : System.Threading.ReaderWriterLock 00005 : System.Reflection.CustomAttributeRecord[] 00005 : System.Globalization.TextInfo 00005 : System.Security.Permissions.EnvironmentStringExpressionSet 00005 : System.WeakReference 00005 : System.Threading.ThreadHelper 00004 : System.Security.FrameSecurityDescriptor 00004 : System.Reflection.RuntimeModule 00004 : System.Reflection.RuntimeConstructorInfo 00004 : System.Guid 00004 : Microsoft.Win32.SafeHandles.SafePEFileHandle 00004 : System.Security.Policy.Evidence 00004 : System.Collections.Generic.Dictionary`2[[System.Type, mscorlib, Version= 4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Security.Poli cy.EvidenceTypeDescriptor, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKey Token=b77a5c561934e089]] 00004 : System.Security.Policy.AssemblyEvidenceFactory 00004 : System.Security.Policy.Evidence+EvidenceUpgradeLockHolder 00003 : System.Security.Util.TokenBasedSet 00003 : System.Globalization.CultureData 00003 : System.Reflection.MemberFilter 00003 : System.Reflection.MethodBase[] 00003 : System.RuntimeType[] 00003 : System.Runtime.Remoting.Lifetime.LeaseLifeTimeServiceProperty 00003 : System.Attribute[] 00003 : System.Threading.ManualResetEvent 00003 : System.IO.PathHelper 00003 : System.Security.Permissions.UIPermission 00002 : System.AppDomainSetup 00002 : System.Security.PermissionToken 00002 : System.Runtime.Remoting.Contexts.IContextProperty[] 00002 : System.Reflection.TypeFilter 00002 : System.Collections.Queue 00002 : System.WeakReference[] 00002 : System.Char 00002 : System.Security.Policy.StrongName[] 00002 : System.Reflection.RuntimeMethodInfo 00002 : System.Threading.SynchronizationContext 00002 : System.Internal.HandleCollector+HandleType[] 00002 : System.Globalization.NumberFormatInfo 00002 : Microsoft.Win32.SystemEvents+SystemEventInvokeInfo[] 00002 : System.Text.EncoderReplacementFallback 00002 : System.IO.UnmanagedMemoryStream 00002 : System.Security.Permissions.FileIOAccess 00002 : System.Security.Util.StringExpressionSet 00001 : System.Exception 00001 : System.OutOfMemoryException 00001 : System.StackOverflowException 00001 : System.ExecutionEngineException 00001 : System.AppDomain 00001 : System.Security.PermissionTokenFactory 00001 : System.Security.PermissionToken[] 00001 : System.Globalization.CalendarData[] 00001 : System.Globalization.CalendarData 00001 : System.__Filters 00001 : System.DefaultBinder 00001 : System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection .RuntimeConstructorInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyTo ken=b77a5c561934e089]] 00001 : System.Reflection.RuntimeConstructorInfo[] 00001 : System.Reflection.ConstructorInfo[] 00001 : System.Collections.Generic.List`1[[System.Reflection.MethodBase, mscorli b, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Signature 00001 : System.Reflection.ParameterInfo[] 00001 : System.Int32[][] 00001 : System.Runtime.Remoting.Proxies.ProxyAttribute 00001 : System.Runtime.Remoting.DomainSpecificRemotingData 00001 : System.Runtime.Remoting.Channels.ChannelServicesData 00001 : System.Runtime.Remoting.Activation.LocalActivator 00001 : System.Runtime.Remoting.Activation.ActivationListener 00001 : System.Runtime.Remoting.Contexts.ContextAttribute[] 00001 : System.Runtime.Remoting.Contexts.Context 00001 : System.Runtime.Remoting.Messaging.ConstructorCallMessage 00001 : System.Runtime.Remoting.Metadata.RemotingTypeCachedData 00001 : System.Collections.Generic.Dictionary`2[[System.RuntimeType, mscorlib, V ersion=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Runtim eType, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e0 89]] 00001 : System.Collections.Generic.Dictionary`2+Entry[[System.RuntimeType, mscor lib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System. RuntimeType, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c56 1934e089]][] 00001 : System.AttributeUsageAttribute 00001 : System.Runtime.Remoting.Metadata.SoapTypeAttribute 00001 : System.Runtime.Remoting.Activation.ConstructionLevelActivator 00001 : System.Runtime.Remoting.RemotingConfigHandler+RemotingConfigInfo 00001 : System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Versio n=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Globalizati on.CultureData, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5 c561934e089]] 00001 : System.Runtime.Remoting.ObjectHandle 00001 : System.Diagnostics.TraceSwitch 00001 : System.Collections.Generic.Dictionary`2[[System.Int16, mscorlib, Version =4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.IntPtr, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Collections.Generic.GenericEqualityComparer`1[[System.Int16, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Collections.Generic.ObjectEqualityComparer`1[[System.IntPtr, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Threading.Mutex 00001 : System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode 00001 : System.Threading.Mutex+MutexCleanupInfo 00001 : System.Threading.Mutex+MutexTryCodeHelper 00001 : System.Threading.EventWaitHandle 00001 : System.Threading.HostExecutionContextManager 00001 : System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, mscorl ib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Security.Policy.ApplicationTrust 00001 : System.Security.Policy.PolicyStatement 00001 : System.Collections.Generic.List`1[[System.Security.Policy.StrongName, ms corlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.SZArrayHelper+SZGenericArrayEnumerator`1[[System.Security.Policy. StrongName, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561 934e089]] 00001 : System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Security.Pol icy.StrongName, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5 c561934e089]] 00001 : Microsoft.Win32.Win32Native+OSVERSIONINFO 00001 : Microsoft.Win32.Win32Native+OSVERSIONINFOEX 00001 : System.OperatingSystem 00001 : System.__ComObject 00001 : System.Collections.Queue+SynchronizedQueue 00001 : System.Threading.AutoResetEvent 00001 : System.Threading.ContextCallback 00001 : System.RuntimeMethodInfoStub 00001 : System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection .RuntimeMethodInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b 77a5c561934e089]] 00001 : System.Reflection.RuntimeMethodInfo[] 00001 : System.Collections.Generic.List`1[[System.Attribute, mscorlib, Version=4 .0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Drawing.SizeF 00001 : System.Drawing.Point 00001 : System.Windows.Forms.Application+ThreadContext 00001 : System.Windows.Forms.WindowsFormsSynchronizationContext 00001 : System.EventArgs 00001 : System.Windows.Forms.NativeMethods+WNDCLASS_D 00001 : Microsoft.Win32.UserPreferenceChangedEventHandler 00001 : System.Random 00001 : System.Collections.Generic.ObjectEqualityComparer`1[[System.Object, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Versio n=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object[], m scorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : Microsoft.Win32.SystemEvents 00001 : System.Runtime.Remoting.Messaging.LogicalCallContext 00001 : System.Text.UTF8Encoding 00001 : Microsoft.Win32.NativeMethods+WNDCLASS 00001 : System.Internal.HandleCollector+HandleType[] 00001 : System.IntPtr[] 00001 : System.Security.Util.URLString 00001 : System.Security.Permissions.FileIOPermission 00001 : System.Security.Util.LocalSiteString 00001 : System.Security.Util.DirectoryString 00001 : Microsoft.Win32.SafeHandles.SafeFileHandle 00001 : System.Text.SBCSCodePageEncoding 00001 : System.Text.InternalEncoderBestFitFallback 00001 : System.Text.InternalDecoderBestFitFallback 00001 : Microsoft.Win32.SafeHandles.SafeViewOfFileHandle 00001 : Microsoft.Win32.SafeHandles.SafeFileMappingHandle 00001 : System.IO.__ConsoleStream 00001 : System.Text.EncoderNLS 00001 : System.IO.StreamWriter+MdaHelper 00001 : System.IO.TextWriter+SyncTextWriter 00001 : System.Diagnostics.Stopwatch 00001 : System.Predicate`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neu tral, PublicKeyToken=b77a5c561934e089]] 00001 : System.DBNull Objects total: 2294. Time taken: 437
Конечно! Ведь мы смотрим на виртуальную память. Это раз… А второе… между доменами нет границ, объекты выделяются друг за другом даже при пересечении границы доменов. Разница — в коде. Потому можно, например, передать между доменами объект без сериализации.
Источник: habr.com