Изменение кода .net framework библиотек или «утечка» .Net Framework 5.0

Здесь я продемонстрирую возможность, которая по своей сути — самый настоящий хак. Вопрос, зачем это может понадобиться? На самом деле целей для этого может быть огромное множество. Итак наша задача — изменить код библиотеки mscorlib таким образом, чтобы все программы, которые ей пользуются, получили эти изменения. Не рантайм, конечно, а во время старта (для runtime необходимо проделать другие вещи, и тут надо оговориться что изменения эти не должны поломать текущие состояния библиотеки). Mscorlib я взял как пример, потому что она есть у всех на компьютере. Но можно хакать любую другую.Все мы знаем, что для того, чтобы не было «ада dll», Microsoft помимо обычных версий и названия библиотек, дали возможность подписывать сборки ключом, public key которой гарантирует что конкретная сборка «пришла» от конкретного разработчика, а не от какого-то другого. Поэтому, если мы хотим по какой-то вполне добросовестной причине изменить код существующей библиотеки т.о., чтобы она загрузилась в чужой процесс и при этом ключик public key остался тем же, у нас этого не получится. Потому что мы не сможем ее подписать, у нас нет закрытого ключа.

Наша мини цель, чтобы программа вывела на консоль текст:

8c60f0e3d36248ce86ae82a76f5d6997.png

PS: это — исправленная версия старого кода.Краткое содержание:

Введение Разработка драйвера Выводы по разработке драйвера Написание сервиса Windows Изменение кода mscorlib.dll Результаты исследования ВведениеСегодня речь пойдет о сборках, находящихся в GAC и в NAC. Я выяснил, что .NET Framework очень чутко относится к структуре GAC. Он доверяет ей настолько, что загружает оттуда сборки, не особо проверяя номер версии. Потому для того чтобы сделать подмену нам надо не так много: нам необходимо написать драйвер уровня ядра для того чтобы перенаправлять обращения к определенным файлам в другое место. Я для этого воспользовался возможностью filesystem filters. Суть проста: для того чтобы не писать много кода в самом драйвере, пишется протокол между Kernel-space и user-space. Со стороны user-space выступает windows — service приложение. Оно общается с драйвером, дает ему команды, что куда перенаправлять. А тот в свою очередь перенаправляет. Для того чтобы менять код существующей библиотеки, например, mscorlib, можно воспользоваться либо Reflexil, либо Mono: Cecil.Порядок действий:

Меняем сборку (Reflexil или Mono: Cecil) по сборке mscorlib и получаем видоизмененную сборку. Например, встраиваем в нее логгирование Кладем результат в tmp каталог Добавляем redirection «C:\Windows\Microsoft.NET\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll» на, например, «C:\tmp\mscorlib64.dll» — видоизмененная сборка И мы получаем при последующих запусках всех приложений на .net логгирования системной библиотеки. Для порядка, необходимо чтобы драйвер фильтровал также по номерам процессов, кому конкретно давать «левак», а кому — оригинальную библиотеку.Разработка драйвера Для начала устанавливаем VirtualBox. Он нам понадобится чтобы отлаживать драйвер. Нам же не хочется перезапускаться каждый раз, когда наш драйвер будет обращаться не по тем адресам и вылетать с ошибкой (естественно, с BSOD’ом). На виртуалки накатываем образы Windows XP и Windows 7, x86 и x64 и снимаем снапшоты, чтобы было куда откатываться.Далее устанавливаем на девелоперскую машину VisualDDK, WinDDK, и прочую инфраструктуру для разработки драйверов.

Далее пишем сам драйвер. Я не буду выкладывать полные листинги, а только важные их части. Оговорюсь только что пишем мы Minifilter Filesystem Driver.

1) Регистрация фильтра:

const FLT_OPERATION_REGISTRATION Callbacks[] = {

{ IRP_MJ_CREATE, 0, PbPreOperationCreateCallback, PbPostOperationCreateCallback },

{ IRP_MJ_NETWORK_QUERY_OPEN, 0, PbPreOperationNetworkQueryOpenCallback, NULL }, { IRP_MJ_OPERATION_END } };

CONST FLT_REGISTRATION FilterRegistration = {

sizeof (FLT_REGISTRATION), // Size FLT_REGISTRATION_VERSION, // Version 0, // Flags

NULL, // Context Callbacks, // Operation callbacks

PtFilterUnload, // FilterUnload

PtInstanceSetup, // InstanceSetup PtInstanceQueryTeardown, // InstanceQueryTeardown PtInstanceTeardownStart, // InstanceTeardownStart PtInstanceTeardownComplete, // InstanceTeardownComplete

PtGenerateFileName, // GenerateFileName PtNormalizeNameComponent // NormalizeNameComponent }; Тут все просто. Вводим структуры для регистрации драйвера фильтра. Фильтр будет работать на всех volumes, и перехватывать операцию CreateFile (создание/открытие файла)

Инициализация драйвера также не должна вызывать вопросов у опытных людей. Просто перечислю, что тут происходит:

драйвер регистрируется в системе делается нотификация, которая будет нас уведомлять о новых процессах в системе поднимается Communitation Port для общения с пользовательским уровнем (3 кольцо защиты, приложения Windows), где нас ожидает сервис Windows, который будет описан ниже CPP_DRIVER_ENTRY ( __in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING RegistryPath )

{ /* locals */ NTSTATUS status; OBJECT_ATTRIBUTES attr; UNICODE_STRING portName; PSECURITY_DESCRIPTOR securityDescriptor;

/* unused */ UNREFERENCED_PARAMETER (RegistryPath);

/* code */ __try {

// Set DRIVER_DATA to zeroes memset (&DRIVER_DATA, 0, sizeof (DRIVER_DATA));

/* Setup process creation callback */ status = Xu (&PsProcessNotify, FALSE); if (! NT_SUCCESS (status)) __leave;

SetFlag (DRIVER_DATA.initialized, FILTER_SUBSYSTEM_PROCESS);

// Get exported OS functions DECLARE_CONST_UNICODE_STRING (Func1, L«IoReplaceFileObjectName»); // Win7+ DRIVER_DATA.pfnIoReplaceFileObjectName = (PIOREPLACEFILEOBJECTNAME) MmGetSystemRoutineAddress ((PUNICODE_STRING) &Func1);

// Register filter status = FltRegisterFilter (DriverObject, &FilterRegistration, &DRIVER_DATA.fltHandle); if (! NT_SUCCESS (status)) __leave;

SetFlag (DRIVER_DATA.initialized, FILTER_SUBSYSTEM_DRIVER);

FltInitializePushLock (&DRIVER_DATA.Sync); SetFlag (DRIVER_DATA.initialized, FILTER_SUBSYSTEM_LOCK);

// Setup security descriptor status = FltBuildDefaultSecurityDescriptor (&securityDescriptor, FLT_PORT_ALL_ACCESS); if (! NT_SUCCESS (status)) __leave;

RtlInitUnicodeString (&portName, PbCommPortName);

InitializeObjectAttributes (&attr, &portName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, securityDescriptor); status = FltCreateCommunicationPort (DRIVER_DATA.fltHandle, &DRIVER_DATA.Connection.ServerPort, &attr, NULL, CommPortConnect, CommPortDisconnect, CommPortMessageNotify, 1); if (! NT_SUCCESS (status)) __leave;

SetFlag (DRIVER_DATA.initialized, FILTER_SUBSYSTEM_COMPORT);

// Free the security descriptor in all cases. It is not needed once the call to FltCreateCommunicationPort () is made. FltFreeSecurityDescriptor (securityDescriptor); if (! NT_SUCCESS (status)) __leave; // Start filtering i/o status = FltStartFiltering (DRIVER_DATA.fltHandle); if (! NT_SUCCESS (status)) __leave; ASSERT (NT_SUCCESS (status));

DRIVER_DATA.State = DRIVER_STATE_STARTED;

} __finally { if (! NT_SUCCESS (status)) { DeregisterFilter (); } }

return status; } Выход из фильтра также прост: Скажу только что DeregisterFilter занимается освобождением всех ресурсов. основываясь на выставленных флагах в DRIVER_DATA.Initialized (см. код выше)

NTSTATUS PtFilterUnload ( __in FLT_FILTER_UNLOAD_FLAGS Flags ) { UNREFERENCED_PARAMETER (Flags);

PAGED_CODE ();

DeregisterFilter (); DbgPrint («PoliciesSandbox! PtFilterUnload: Entered\n»);

return STATUS_SUCCESS; } Далее. Теперь нам необходимо знать что и куда перенаправлять. Какой файл и куда. Для этого я сделал небольшой протокольчик между kernel-mode и user-mode по communication port, оторый мы подняли в DriverEntry

Определяем набор команд:

typedef enum {

// From verson = v1.0 // From User-space to Kernel-space GetVersion, GetFilesystemRedirections, AddFilesystemByPIDRedirection, RemoveFilesystemByPIDRedirection, GetRegistryRedirections, AddRegistryByPIDRedirection, RemoveRegistryByPIDRedirection, // From Kernel-space to User-space ProcessAttached, CancelIOCompletionPort

// Version v2.0 is here } COMM_COMMAND; Определяем структуру базы всех команд:

typedef struct { COMM_COMMAND Command; USHORT Data[]; } COMM_MESSAGE, *PCOMM_MESSAGE; Определяем структуру, посылаемую сервису Windows для уведомления о новом процессе в системе:

typedef struct { ULONG Pid; } COMM_PROCESS_ATTACHED, *PCOMM_PROCESS_ATTACHED; Опеделяем структуру-ответ:

typedef struct { USHORT NeedToMonitor; USHORT IsCompleted; // true, if this packet contains all redirections, needed by driver. Otherwice, driver needs to ask more USHORT PairsCount; // redirections count. Actually, count of null-terminated strings in Data struct { // positions of redirections to make searching fast USHORT From; USHORT To; } Positions[64];

WCHAR Data[]; } COMM_FS_REDIRECTIONS, *PCOMM_FS_REDIRECTIONS; Также вводим структуры для хранения данных в драйвере:

typedef struct _MAPPING_ENTRY {

UNICODE_STRING OldName;

UNICODE_STRING NewName;

_MAPPING_ENTRY *Next;

} MAPPING_ENTRY, *PMAPPING_ENTRY;

typedef struct _PROCESSES_MAP_ENTRY { ULONG Pid; PMAPPING_ENTRY entries; _PROCESSES_MAP_ENTRY *Prev, *Next; } PROCESSES_MAP_ENTRY, *PPROCESSES_MAP_ENTRY; Теперь, когда все определено, необходимо при старте нового процесса (а мы уже его перехватываем) оповестить об этом сервис, который в качестве ответа отдаст нам набор правил редиректа файлов и папок в зависимости от этого процесса.

Я не буду включать код функций, очищающих память, т.к. это не так интересно.При вызове функции ей передается создавший процесс, созданный и флаг, создан ли процесс либо он умирает. Т.е. функция оповещает нас и о создании и о смерти процесса.

Внутри ее, если процесс создается, мы оповещаем об этом сервис Windows, передавая PID процесса. По этому PID сервис находит у себя список правил редиректа и отдает их нам в качестве ответа:

VOID PsProcessNotify ( IN HANDLE ParentId, IN HANDLE ProcessId, IN BOOLEAN Create ) { NTSTATUS status;

if (HandleToULong (ProcessId) <= 4) return;

/* Check exsisting data */

if (Create) { LARGE_INTEGER liTimeout; DbgPrint («Process created: %x», ProcessId); liTimeout.QuadPart = -((LONGLONG)1×10 * 1000×1000); ULONG SenderBufferLength = MESSAGE_BUFFER_SIZE; ULONG ReplyLength = MESSAGE_BUFFER_SIZE; PCOMM_MESSAGE message = (PCOMM_MESSAGE)myNonPagedAlloc (MESSAGE_BUFFER_SIZE); message→Command = ProcessAttached; ((PCOMM_PROCESS_ATTACHED)(&message→Data[0]))→Pid = HandleToULong (ProcessId); status = FltSendMessage (DRIVER_DATA.fltHandle, &DRIVER_DATA.Connection.ClientPort, message, SenderBufferLength, message, &ReplyLength, &liTimeout);

if (ReplyLength > 0) { PCOMM_FS_REDIRECTIONS Redirections = (PCOMM_FS_REDIRECTIONS)&message→Data[0]; DbgPrint («Recieved reply from user-mode: NeedToMonitor = %s\n», Redirections→NeedToMonitor? «TRUE» : «FALSE»); if (Redirections→NeedToMonitor) { PbRepInitializeMapping (HandleToULong (ProcessId), Redirections); } else { DbgPrint («Thread %d not needed to be monitored. Skipping.», HandleToULong (ProcessId)); } } if (! message) myFree (message); } else { DbgPrint («Process destroyed: %x\n», ProcessId);

PPROCESSES_MAP_ENTRY entry = DRIVER_DATA.Mapping; while (entry!= NULL) { if (entry→Pid == HandleToULong (ProcessId)) { PbRepDeleteMapping (entry); break; } } } } Код, представленный ниже просто инициализирует внутренние структуры относительно тех данных, которые пришли от сервиса Windows:

NTSTATUS PbRepInitializeMapping (__in ULONG pid, __in PCOMM_FS_REDIRECTIONS Redirections) { NTSTATUS status = STATUS_SUCCESS;

FltAcquirePushLockExclusive (&DRIVER_DATA.Sync);

__try { DbgPrint («PlociesSandbox! PbRepInitializeMapping: redirections count: %d\n», Redirections→PairsCount);

PMAPPING_ENTRY current = NULL;

// Lookup PID in map PPROCESSES_MAP_ENTRY currentProcess = DRIVER_DATA.Mapping; while (currentProcess!= NULL) { if (currentProcess→Pid == pid) { DbgPrint («PlociesSandbox! PbRepInitializeMapping: Already initialized; skipping»); return STATUS_SUCCESS; } currentProcess = currentProcess→Next; }

currentProcess = (PPROCESSES_MAP_ENTRY)myNonPagedAlloc (sizeof (PROCESSES_MAP_ENTRY)); currentProcess→Pid = pid; currentProcess→Next = DRIVER_DATA.Mapping; currentProcess→Prev = NULL; if (DRIVER_DATA.Mapping!= NULL) DRIVER_DATA.Mapping→Prev = currentProcess; DRIVER_DATA.Mapping = currentProcess;

for (int i=0; i < Redirections->PairsCount; i++) { // Copying a pair of pathes From→To to internal mapping structure int FromLen = wcslen (&Redirections→Data[Redirections→Positions[i].From]); int ToLen = wcslen (&Redirections→Data[Redirections→Positions[i].To]); PMAPPING_ENTRY mappingEntry = (PMAPPING_ENTRY)myAlloc (NonPagedPool, sizeof (MAPPING_ENTRY));

mappingEntry→OldName.Buffer = (WCHAR*)myAlloc (NonPagedPool, (FromLen + 1) * sizeof (WCHAR)); wcscpy (mappingEntry→OldName.Buffer, &Redirections→Data[Redirections→Positions[i].From]); mappingEntry→OldName.Length = mappingEntry→OldName.MaximumLength = wcslen (mappingEntry→OldName.Buffer) * sizeof (WCHAR);

mappingEntry→NewName.Buffer = (WCHAR*)myAlloc (NonPagedPool, (ToLen + 1) * sizeof (WCHAR)); wcscpy (mappingEntry→NewName.Buffer, &Redirections→Data[Redirections→Positions[i].To]); mappingEntry→NewName.Length = mappingEntry→NewName.MaximumLength = wcslen (mappingEntry→NewName.Buffer) * sizeof (WCHAR);

if (current == NULL) { current = mappingEntry; currentProcess→entries = current; } else { current→Next = mappingEntry; current = mappingEntry; }

current→Next = NULL; }

} __finally {

FltReleasePushLock (&DRIVER_DATA.Sync);

} DbgPrint («PlociesSandbox! PbRepInitializeMapping: done\n»);

return status; } И, последнее, функция поиска правила редиректа. Если правило найдено, возвращает STATUS_SUCCESS и видоизмененный путь:

bool PbIsFolder (PUNICODE_STRING path) { return path→Buffer[path→Length/2 — 1] == L'\\'; }

NTSTATUS PbLookupRedirection (__in PUNICODE_STRING FilePath, __out PUNICODE_STRING *FileFound) { // Possible redirections: // Full change: \Device\HarddiskVolume2\InternalPath\Path\To\Some\File.Ext → \Device\.\Temporary\File.ext // Partial change: \Device\HarddiskVolume2\InternalPath\ → \Device\HarddiskVolume2\Temporary\ // in this case all pathes, starts with …\InternalPath should be changed. For ex.: // \Device\HarddiskVolume2\InternalPath\Some\Path\To\File.Ext → \Device\HarddiskVolume2\Temporary\Some\Path\To\File.Ext ULONG Pid = HandleToULong (PsGetCurrentProcessId ()); FltAcquirePushLockShared (&DRIVER_DATA.Sync); __try { PPROCESSES_MAP_ENTRY currentProcess = DRIVER_DATA.Mapping; while (currentProcess!= NULL) { if (currentProcess→Pid == Pid) { PMAPPING_ENTRY current = currentProcess→entries; while (current!= NULL) { if (PbIsFolder (¤t→OldName)) { // Folders prefixes are identical, please note that all lengthes are double-sized if (wcsncmp (current→OldName.Buffer, FilePath→Buffer, current→OldName.Length / 2) == NULL) { int newlength = (FilePath→Length — current→OldName.Length) + current→NewName.Length; PUNICODE_STRING ret = PbAllocUnicodeString (newlength + 2); RtlCopyUnicodeString (ret, ¤t→NewName); RtlCopyMemory (Add2Ptr (ret→Buffer, ret→Length), Add2Ptr (FilePath→Buffer, current→OldName.Length), (FilePath→Length — current→OldName.Length) + 2); ret→Length = wcslen (ret→Buffer) * 2; *FileFound = ret; return STATUS_SUCCESS; } } else { if (wcscmp (current→OldName.Buffer, FilePath→Buffer) == NULL) { PUNICODE_STRING ret = PbAllocUnicodeString (current→NewName.Length + 2); RtlCopyUnicodeString (ret, ¤t→NewName); *FileFound = ret; return STATUS_SUCCESS; } } current = current→Next; } } currentProcess = currentProcess→Next; } return STATUS_NOT_FOUND; } __finally { FltReleasePushLock (&DRIVER_DATA.Sync); } }

Выводы по разработке драйвера У нас есть драйвер, который оповещает внешний сервис о создании новых процессов в операционной системе. Сервис получая информацию о процессе принимает решение о том, включать на нем мониторинг или нет. Если да, передает также список правил редиректов на файловой системе.

Написание сервиса Windows Опять же, я не буду вдаваться в особые подробности. Это обычный сервис. Единственная особенность заключается в том что он должен ожидать от драйвера команд и отвечать на них. Ожидать мы будем при помощи IoCompletionPort. Этот механизм требует (в общем понятно для чего), чтобы на ожидании завершения ввода/вывода по порту висело несколько потоков. Когда придут данные, одна из задач просыпается и может эти данные обработать. Мы в этой задаче будем отсылать список редиректов.Код может быть немного нечистый, ну да ладно. Главное делает что надо:

FilterConnectCommunicationPort соединяет нас с драйвером CreateIoCompletionPort создает i/o completion port, все ссылки, где о нем почитать есть в коде MessagingManagerThread — класс, управляющий потоком обработки сообщений от драйвера Остановка (Stop) посылает всем потокам команду завершения через io completion port, иначе все будет висеть public unsafe class MessagingManager { private const string FilterCommunicationPortName = »\\PoliciesInjectorCom0»; private IntPtr m_filterPortHandle; private IntPtr m_ioCompletionPortHandle; private IntPtr[] m_buffers; private MessagingManagerThread[] m_threads; private CancellationTokenSource m_cancellationToken; public unsafe MessagingManager (Dictionary redirections) { uint hr = WinApi.FilterConnectCommunicationPort (FilterCommunicationPortName, 0, IntPtr.Zero, 0, IntPtr.Zero, out m_filterPortHandle); if (hr!= 0×0) { throw WinApi.CreateWin32Exception (String.Format («Cannot connect to driver via '{0}' port», FilterCommunicationPortName)); } Console.WriteLine («Connected to {0}», FilterCommunicationPortName); // For more info, ENG: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v=vs.85).aspx // For more info, RUS: http://www.rsdn.ru/article/baseserv/threadpool.xml int threadsCount = Environment.ProcessorCount * 2; m_ioCompletionPortHandle = WinApi.CreateIoCompletionPort (m_filterPortHandle, IntPtr.Zero, IntPtr.Zero, threadsCount); if (m_ioCompletionPortHandle == IntPtr.Zero) { throw WinApi.CreateWin32Exception («Cannot create I/O Completeon port»); } // Make thread for each processor and threads cancellation token m_threads = new MessagingManagerThread[threadsCount]; m_buffers = new IntPtr[threadsCount]; m_cancellationToken = new CancellationTokenSource (); Console.WriteLine («Number of threads to monitor: {0}», threadsCount); for (int i=0; i

// Make street magic (see DDK Examples→mini filters→scanner) hr = WinApi.FilterGetMessage (m_filterPortHandle, out buffer→Header, Marshal.SizeOf (typeof (F2U.IncomingMessagePacket)), ref buffer→Overlapped);

if (hr!= WinApi.HRESULT_FROM_WIN32(WinApi.ERROR_IO_PENDING)) { throw WinApi.CreateWin32Exception (String.Format («Cannot get filter message. 0x{0: X}», hr)); } } } } public unsafe bool Stop () { bool successfull = true; m_cancellationToken.Cancel ();

Console.WriteLine («Starting to cancel I/O.»); if (! WinApi.CancelIoEx (m_filterPortHandle, IntPtr.Zero)) { var errstr = String.Format («Cannot cancel I/O operations (0x{0: X}).», Marshal.GetLastWin32Error ()); Console.WriteLine (errstr); successfull = false; // throw WinApi.CreateWin32Exception (errstr); } foreach (var thread in m_threads.AsEnumerable ()) { var overlapped = new F2U.IncomingMessagePacket (); IntPtr *completionKey; overlapped.Message.Command = Command.CancelIOCompletionPort; WinApi.PostQueuedCompletionStatus (m_ioCompletionPortHandle, (uint)sizeof (F2U.IncomingMessagePacket), out completionKey, (NativeOverlapped *) &overlapped); } foreach (var thread in m_threads.AsEnumerable ()) { if (! thread.WaitHandle.WaitOne (2000)) { Console.WriteLine («Failed while waiting for thread {0} is stopped», thread.ThreadId); successfull = false; // TODO: kill thread and report confusing bug } } return successfull; } } И, наконец, класс обслуживания потока:

public class MessagingManagerThread { private ManualResetEvent m_resetEvent; private IntPtr m_ioCompletionPortHandle; private IntPtr m_filterPortHandle; private CancellationToken m_cancelToken; private Thread m_thread; private Int32 m_threadId; private Dictionary m_redirections;

private const int timeoutCompletionStatus = 5000; ///

/// Builds MessagingManagerThread object /// /// I/O Completion Port Handle (Win32) public MessagingManagerThread (IntPtr ioCompletionPortHandle, CancellationToken token, IntPtr filterPortHandle, Dictionary redirections) { m_ioCompletionPortHandle = ioCompletionPortHandle; m_filterPortHandle = filterPortHandle; m_cancelToken = token; m_redirections = redirections; m_resetEvent = new ManualResetEvent (false); m_thread = new Thread (Start); m_thread.Start (); } public WaitHandle WaitHandle { get { return m_resetEvent; } } public Int32 ThreadId { get { return m_threadId; } }

public unsafe void Start () { try { // Get current thread id (unmanaged) m_threadId = WinApi.GetCurrentThreadId (); Console.WriteLine («Monitoring thread {0} is started», m_threadId); // Messages processing queue while (true) { // If cancellation requested, we should to leave thread if (m_cancelToken.IsCancellationRequested) { Console.WriteLine («Cancellation on thread {0} is requested», m_threadId); return; } // Otherwise, we should read completion port uint numberOfBytesTransferred; UIntPtr lpCompletionKey; NativeOverlapped* lpOverlapped; Console.WriteLine («Starting waiting for message at {0}…», m_threadId); if (! WinApi.GetQueuedCompletionStatus (m_ioCompletionPortHandle, out numberOfBytesTransferred, out lpCompletionKey, out lpOverlapped, -1)) { // Something wrong happend var error = Marshal.GetLastWin32Error (); if (error == WinApi.ERROR_OPERATION_ABORTED) { return; } if (error == WinApi.WAIT_TIMEOUT) { Console.WriteLine («Time out {0}», m_threadId); continue; } Console.WriteLine («WinApi.GetQueuedCompletionStatus returns error code: {0: X}», error); throw WinApi.CreateWin32Exception («GetQueuedCompletionStatus», (uint)error); } Console.WriteLine («GetQueuedCompletionStatus finished successfully, Message is recieved»); // Message recieved var request = (F2U.IncomingMessagePacket *)lpOverlapped; if (request→Message.Command == Command.ProcessAttached) { Run_ProcessAttached (request); } else if (request→Message.Command == Command.CancelIOCompletionPort) { Console.WriteLine («Thread destroying requested»); } // Queue a new request completion. WinApi.RtlZeroMemory ((IntPtr) request, Marshal.SizeOf (request→GetType ())); uint hr = WinApi.FilterGetMessage (m_filterPortHandle, out request→Header, Marshal.SizeOf (request→GetType ()), ref request→Overlapped);

if (hr!= WinApi.HRESULT_FROM_WIN32(WinApi.ERROR_IO_PENDING)) { throw WinApi.CreateWin32Exception («Cannot get filter message», hr); } } } finally { // Send signal to owner m_resetEvent.Set (); } } private unsafe void Run_ProcessAttached (F2U.IncomingMessagePacket *data) { var pid = ((F2U.ProcessAttached *)data→Message.Data)→ProcessId; Console.WriteLine («Incoming request for PID = {0}», pid); var reply = new IncomingMessagePacketReply (); reply.Header.NtStatus = 0; reply.Header.MessageId = data→Header.MessageId; reply.Message.Command = Command.ProcessAttached; int size= Message.MAX_DATA_SIZE; var pAttachedReply = ((ProcessAttachedReply *)(&reply.Message.Data[0])); pAttachedReply→NeedToMonitor=1; Process process = null; try { process = Process.GetProcessById (pid); Console.WriteLine («Retrieved name by pid: {0}; Managed: ???», process.ProcessName); } catch (ArgumentException ex) { pAttachedReply→NeedToMonitor=0; } if (! process.ProcessName.Contains («testredir»)) pAttachedReply→NeedToMonitor = 0; if (pAttachedReply→NeedToMonitor==1) { int pos = 0, index = 0; Console.WriteLine («Redirections registered: {0}», m_redirections.Count); foreach (var redirection in m_redirections) { Console.WriteLine (» — Trying to add redirection: \n {0}\n {1}», redirection.Key, redirection.Value); unchecked { ((ProcessAttachedReply_Positions *)&pAttachedReply→Positions[index])→From = (ushort)pos; AppendStringToArray (&pAttachedReply→Data, redirection.Key, ref pos); ((ProcessAttachedReply_Positions *)&pAttachedReply→Positions[index])→To = (ushort)pos; AppendStringToArray (&pAttachedReply→Data, redirection.Value, ref pos); index++; } } pAttachedReply→PairsCount = (ushort)m_redirections.Count; } uint status = WinApi.FilterReplyMessage (m_filterPortHandle, ref reply.Header, (int)size); Console.WriteLine («Making reply: Command = ProcessAttached, NeedToMonitor = True, Size = {0}, ReplyStat=0x{1: X}», size, status); } private unsafe void AppendStringToArray (ushort *arr, string str, ref int position) { foreach (var ch in str) { arr[position] = ch; position++; } arr[position]=0; position++; } } Настройка происходит следующим не хитрым образом:

var redir = new RedirectionsManager (); redir.Add (typeof (Environment).Assembly.GetName (),»\\temp\\GAC32\\mscorlib.dll»); Это значит что любое обращение к сборке mscorlib.dll в GAC приведет к редиректу в папку «C:\temp\GAC32\mscorlib.dll» для 32-х разрядной системы. Для 64-х разрядной проделайте тоже самое, но для другой папки.Модифицируем mscorlib.dll Для модификации mscorlib можно воспользоваться .Net Reflector + Reflexil, старыми freeware версиями.В них я заменил System.Environment.get_Version () так, чтобы номер версии был »5.0.40930.0«Проверка результатов Для тестирования создадим .Net Framework 4.0 приложение и назовем его testredir, поскольку наш драйвер ожидает именно такое имя процесса.Текст приложения прост до невозможности

using System;

namespace testredir { public static class testredir { public static void Main (string[] args) { Console.WriteLine («Ver: {0}!», System.Environment.Version); Console.Write («Press any key to continue. . .»); Console.ReadKey (true); } } } Вывод программы: Ver: 5.0.40930.0!

Заметка: Изменять код системных библиотек необходимо аккуратно. Например, наш пример валит часть программного обеспечения, поскольку оно проверяет номер версии, на которой запускается. Хорошим примером может послужить создание логгера внутренних событий, чем я сейчас и занимаюсь

Источники:

© Habrahabr.ru