「hell dll」を回避するために、Microsoftは、通常のバージョンとライブラリ名に加えて、特定のアセンブリが特定の開発者からではなく「特定の」開発者から「来た」ことを保証する公開鍵を持つアセンブリに署名する機会を与えられたことを知っています別の。 したがって、何らかの完全な理由で既存のライブラリのコードを変更して他のプロセスにロードし、公開キーが同じままにしたい場合、成功しません。 署名できないため、秘密キーはありません。
私たちの小さな目標は、プログラムがコンソールにテキストを表示することです。
まず、このサイクルのHabréに投稿された記事の全リスト
要約:
はじめに
今日は、GACとNACにあるアセンブリについて説明します。 .NET FrameworkはGAC構造に非常に敏感であることがわかりました。 彼は彼女を非常に信頼しており、実際にバージョン番号を確認せずにそこからアセンブリをロードします。 したがって、置換を行うために、それほど必要はありません。特定のファイルへの呼び出しを別の場所にリダイレクトするために、カーネルレベルのドライバーを記述する必要があります。 ファイルシステムフィルターの可能性を利用しました。 一番下の行は簡単です。ドライバー自体に多くのコードを記述しないために、カーネル空間とユーザー空間の間にプロトコルが記述されています。 サービスアプリケーション-ユーザースペース側のウィンドウから立っています。 ドライバーと通信し、リダイレクト先をコマンドに渡します。 そして、それは順番にリダイレクトされます。 mscorlibなどの既存のライブラリのコードを変更するには、ReflexilまたはMono :: Cecilを使用できます。
手続き
- mscorlibアセンブリのアセンブリ(ReflexilまたはMono :: Cecil)を変更し、変更されたアセンブリを取得します。 たとえば、ログインを埋め込みます。
- 結果をtmpディレクトリに入れます。
- リダイレクト「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 };
ここではすべてが簡単です。 フィルタードライバーを登録するための構造を紹介します。 フィルターはすべてのボリュームで機能し、CreateFile操作をインターセプトします(ファイルを作成/開く)
ドライバーの初期化も、経験のある人に質問をしてはいけません。 ここで何が起こるかをリストします。
- ドライバーはシステムに登録されています
- システム内の新しいプロセスを通知する通知が作成されます
- 通信ポートが上昇し、ユーザーレベル(保護の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; }
次。 次に、何をどこにリダイレクトするかを知る必要があります。 どのファイルと場所。 これを行うために、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の助けを借りてお待ちしています。 このメカニズムでは、I / Oが完了するのを待っている間にいくつかのスレッドがポートでハングすることが必要です(一般に、理解できる理由です)。 データが到着すると、タスクの1つが起動し、このデータを処理できます。 このタスクでは、リダイレクトのリストを送信します。
コードは少し汚いかもしれませんが、まあまあです。 主なことは、必要なことを行うことです。
- FilterConnectCommunicationPortはドライバーをドライバーに接続します
- CreateIoCompletionPortは、I / O完了ポートを作成します。これについて読む場所のすべてのリンクはコード内にあります
- MessagingManagerThread-ドライバーからのメッセージ処理のフローを制御するクラス
- Stopは、io完了ポートを介してすべてのスレッドに完了コマンドを送信します。そうしないと、すべてがハングします
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<string,string> redirections) { uint hr = WinApi.FilterConnectCommunicationPort(FilterCommunicationPortName, 0, IntPtr.Zero, 0, IntPtr.Zero, out m_filterPortHandle); if(hr != 0x0) { 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<m_threads.Length; i++) { m_threads[i] = new MessagingManagerThread(m_ioCompletionPortHandle, m_cancellationToken.Token, m_filterPortHandle, redirections); unsafe { m_buffers[i] = Marshal.AllocHGlobal( sizeof(F2U.IncomingMessagePacket) ); WinApi.RtlZeroMemory( m_buffers[i], sizeof(F2U.IncomingMessagePacket) ); var buffer = (F2U.IncomingMessagePacket *) m_buffers[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<string,string> m_redirections; private const int timeoutCompletionStatus = 5000; /// <summary> /// Builds MessagingManagerThread object /// </summary> /// <param name="handle">I/O Completion Port Handle (Win32)</param> public MessagingManagerThread(IntPtr ioCompletionPortHandle, CancellationToken token, IntPtr filterPortHandle, Dictionary<string,string> 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");
つまり、GACでmscorlib.dllアセンブリを呼び出すと、32ビットシステムの「C:\ temp \ GAC32 \ mscorlib.dll」フォルダーにリダイレクトされます。 64ビットの場合、同じことを行いますが、異なるフォルダーに対して行います。
mscorlib.dllを変更する
mscorlibを変更するには、.Net Reflector + Reflexilの古いフリーウェアバージョンを使用できます。
それらの中で、バージョン番号が「5.0.40930.0」になるようにSystem.Environment.get_Version()を置き換えました
結果を確認する
テストでは、.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!
注:
システムライブラリのコードを慎重に変更する必要があります。 たとえば、この例では、実行するバージョン番号をチェックするため、ソフトウェアの一部を停止します。 良い例は、現在行っている内部イベントのロガーの作成です
ソース:
- http://fsfilters.blogspot.com/
- DDKの例->ミニフィルター->スキャナー
- msdn.microsoft.com/en-us/library/windows/desktop/aa365198(v = vs. 85).aspx
- www.rsdn.ru/article/baseserv/threadpool.xml